summaryrefslogtreecommitdiffstats
path: root/comm/calendar/base/content/preferences
diff options
context:
space:
mode:
Diffstat (limited to 'comm/calendar/base/content/preferences')
-rw-r--r--comm/calendar/base/content/preferences/alarms.inc.xhtml188
-rw-r--r--comm/calendar/base/content/preferences/alarms.js165
-rw-r--r--comm/calendar/base/content/preferences/calendar-preferences.inc.xhtml55
-rw-r--r--comm/calendar/base/content/preferences/calendar-preferences.js28
-rw-r--r--comm/calendar/base/content/preferences/categories.inc.xhtml32
-rw-r--r--comm/calendar/base/content/preferences/categories.js297
-rw-r--r--comm/calendar/base/content/preferences/editCategory.js112
-rw-r--r--comm/calendar/base/content/preferences/editCategory.xhtml49
-rw-r--r--comm/calendar/base/content/preferences/general.inc.xhtml185
-rw-r--r--comm/calendar/base/content/preferences/general.js148
-rw-r--r--comm/calendar/base/content/preferences/notifications.js24
-rw-r--r--comm/calendar/base/content/preferences/views.inc.xhtml311
-rw-r--r--comm/calendar/base/content/preferences/views.js115
13 files changed, 1709 insertions, 0 deletions
diff --git a/comm/calendar/base/content/preferences/alarms.inc.xhtml b/comm/calendar/base/content/preferences/alarms.inc.xhtml
new file mode 100644
index 0000000000..a2524983d0
--- /dev/null
+++ b/comm/calendar/base/content/preferences/alarms.inc.xhtml
@@ -0,0 +1,188 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at http://mozilla.org/MPL/2.0/.
+ <html:div data-category="paneCalendar">
+ <html:fieldset data-category="paneCalendar">
+ <html:legend data-l10n-id="reminder-legend"></html:legend>
+ <vbox id="alarm-sound-box">
+ <hbox align="center">
+ <checkbox id="alarmSoundCheckbox"
+ preference="calendar.alarms.playsound"
+ data-l10n-id="reminder-play-checkbox"/>
+ <spacer flex="1"/>
+ <button is="highlightable-button" id="calendar.prefs.alarm.sound.play"
+ data-l10n-id="reminder-play-alarm-button"
+ oncommand="gAlarmsPane.previewAlarm()"/>
+ </hbox>
+ <radiogroup id="alarmSoundType"
+ class="indent"
+ orient="vertical"
+ preference="calendar.alarms.soundType"
+ aria-labelledby="alarmSoundCheckbox">
+ <hbox>
+ <radio id="alarmSoundSystem"
+ value="0"
+ data-l10n-id="reminder-default-sound-label"/>
+ </hbox>
+ <hbox>
+ <radio id="alarmSoundCustom"
+ value="1"
+ data-l10n-id="reminder-custom-sound-label"/>
+ </hbox>
+ </radiogroup>
+ <hbox align="center" class="input-container">
+ <html:input id="alarmSoundFileField"
+ type="text"
+ class="input-filefield indent"
+ readonly="readonly"
+ preference="calendar.alarms.soundURL"
+ preference-editable="true"
+ aria-labelledby="alarmSoundCustom"/>
+ <button is="highlightable-button" id="calendar.prefs.alarm.sound.browse"
+ data-l10n-id="reminder-browse-sound-label"
+ oncommand="gAlarmsPane.browseAlarm()"/>
+ </hbox>
+ </vbox>
+ <hbox align="center" flex="1">
+ <checkbox id="alarmshow"
+ preference="calendar.alarms.show"
+ data-l10n-id="reminder-dialog-label"/>
+ </hbox>
+ <hbox align="center" flex="1">
+ <checkbox id="missedalarms"
+ preference="calendar.alarms.showmissed"
+ data-l10n-id="missed-reminder-label"/>
+ </hbox>
+ </html:fieldset>
+ </html:div>
+
+ <html:div data-category="paneCalendar">
+ <html:fieldset data-category="paneCalendar">
+ <html:legend data-l10n-id="reminder-default-legend"></html:legend>
+ <hbox align="center">
+ <label data-l10n-id="default-snooze-label"
+ control="defaultsnoozelength"/>
+ <html:input id="defaultsnoozelength" type="number" class="size3"
+ min="0"
+ preference="calendar.alarms.defaultsnoozelength"
+ onselect="updateUnitLabelPlural('defaultsnoozelength', 'defaultsnoozelengthunit', 'minutes')"
+ oninput="updateUnitLabelPlural('defaultsnoozelength', 'defaultsnoozelengthunit', 'minutes')"/>
+ <label id="defaultsnoozelengthunit"/>
+ </hbox>
+ <hbox>
+ <html:table id="alarm-defaults-table">
+ <html:tr>
+ <html:td>
+ <label data-l10n-id="event-alarm-label"
+ control="eventdefalarm"/>
+ </html:td>
+ <html:td>
+ <hbox>
+ <menulist id="eventdefalarm"
+ crop="none"
+ preference="calendar.alarms.onforevents">
+ <menupopup id="eventdefalarmpopup">
+ <menuitem id="eventdefalarmon"
+ data-l10n-id="alarm-on-label"
+ value="1"/>
+ <menuitem id="eventdefalarmoff"
+ data-l10n-id="alarm-off-label"
+ value="0"
+ selected="true"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </html:td>
+ </html:tr>
+ <html:tr>
+ <html:td>
+ <label data-l10n-id="task-alarm-label"
+ control="tododefalarm"/>
+ </html:td>
+ <html:td>
+ <hbox>
+ <menulist id="tododefalarm"
+ crop="none"
+ preference="calendar.alarms.onfortodos">
+ <menupopup id="tododefalarmpopup">
+ <menuitem id="tododefalarmon"
+ data-l10n-id="alarm-on-label"
+ value="1"/>
+ <menuitem id="tododefalarmoff"
+ data-l10n-id="alarm-off-label"
+ value="0"
+ selected="true"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </html:td>
+ </html:tr>
+ <html:tr>
+ <html:td>
+ <label data-l10n-id="event-alarm-time-label"
+ control="eventdefalarmlen"/>
+ </html:td>
+ <html:td>
+ <hbox class="defaultTimeBox"
+ align="center"
+ flex="1">
+ <html:input id="eventdefalarmlen" type="number" class="size3" min="0"
+ preference="calendar.alarms.eventalarmlen"
+ onselect="updateMenuLabelsPlural('eventdefalarmlen', 'eventdefalarmunit')"
+ oninput="updateMenuLabelsPlural('eventdefalarmlen', 'eventdefalarmunit')"/>
+ <hbox>
+ <menulist id="eventdefalarmunit"
+ flex="1"
+ crop="none"
+ preference="calendar.alarms.eventalarmunit">
+ <menupopup id="eventdefalarmunitpopup">
+ <menuitem id="eventdefalarmunitmin"
+ value="minutes"
+ selected="true"/>
+ <menuitem id="eventdefalarmunithour"
+ value="hours"/>
+ <menuitem id="eventdefalarmunitday"
+ value="days"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </hbox>
+ </html:td>
+ </html:tr>
+ <html:tr>
+ <html:td>
+ <label data-l10n-id="task-alarm-time-label"
+ control="tododefalarmlen"/>
+ </html:td>
+ <html:td>
+ <hbox class="defaultTimeBox"
+ align="center"
+ flex="1">
+ <html:input id="tododefalarmlen" type="number" class="size3" min="0"
+ preference="calendar.alarms.todoalarmlen"
+ onselect="updateMenuLabelsPlural('tododefalarmlen', 'tododefalarmunit')"
+ oninput="updateMenuLabelsPlural('tododefalarmlen', 'tododefalarmunit')"/>
+ <hbox>
+ <menulist id="tododefalarmunit"
+ flex="1"
+ crop="none"
+ preference="calendar.alarms.todoalarmunit">
+ <menupopup id="tododefalarmunitpopup">
+ <menuitem id="tododefalarmunitmin"
+ value="minutes"
+ selected="true"/>
+ <menuitem id="tododefalarmunithour"
+ value="hours"/>
+ <menuitem id="tododefalarmunitday"
+ value="days"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </hbox>
+ </html:td>
+ </html:tr>
+ </html:table>
+ <spacer flex="1"/>
+ </hbox>
+ </html:fieldset>
+ </html:div>
diff --git a/comm/calendar/base/content/preferences/alarms.js b/comm/calendar/base/content/preferences/alarms.js
new file mode 100644
index 0000000000..29b5df30ab
--- /dev/null
+++ b/comm/calendar/base/content/preferences/alarms.js
@@ -0,0 +1,165 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* exported gAlarmsPane */
+
+/* import-globals-from ../calendar-ui-utils.js */
+/* globals Preferences */
+
+Preferences.addAll([
+ { id: "calendar.alarms.playsound", type: "bool" },
+ { id: "calendar.alarms.soundURL", type: "string" },
+ { id: "calendar.alarms.soundType", type: "int" },
+ { id: "calendar.alarms.show", type: "bool" },
+ { id: "calendar.alarms.showmissed", type: "bool" },
+ { id: "calendar.alarms.onforevents", type: "int" },
+ { id: "calendar.alarms.onfortodos", type: "int" },
+ { id: "calendar.alarms.eventalarmlen", type: "int" },
+ { id: "calendar.alarms.eventalarmunit", type: "string" },
+ { id: "calendar.alarms.todoalarmlen", type: "int" },
+ { id: "calendar.alarms.todoalarmunit", type: "string" },
+ { id: "calendar.alarms.defaultsnoozelength", type: "int" },
+]);
+
+/**
+ * Global Object to hold methods for the alarms pref pane
+ */
+var gAlarmsPane = {
+ /**
+ * Initialize the alarms pref pane. Sets up dialog controls to match the
+ * values set in prefs.
+ */
+ init() {
+ // Enable/disable the alarm sound URL box and buttons
+ this.alarmsPlaySoundPrefChanged();
+
+ // Set the correct singular/plural for the time units
+ updateMenuLabelsPlural("eventdefalarmlen", "eventdefalarmunit");
+ updateMenuLabelsPlural("tododefalarmlen", "tododefalarmunit");
+ updateUnitLabelPlural("defaultsnoozelength", "defaultsnoozelengthunit", "minutes");
+
+ Preferences.addSyncFromPrefListener(document.getElementById("alarmSoundFileField"), () =>
+ this.readSoundLocation()
+ );
+ },
+
+ /**
+ * Converts the given file url to a nsIFile
+ *
+ * @param aFileURL A string with a file:// url.
+ * @returns The corresponding nsIFile.
+ */
+ convertURLToLocalFile(aFileURL) {
+ // Convert the file url into a nsIFile
+ if (aFileURL) {
+ let fph = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
+ return fph.getFileFromURLSpec(aFileURL);
+ }
+ return null;
+ },
+
+ /**
+ * Handler function to be called when the calendar.alarms.soundURL pref has
+ * changed. Updates the label in the dialog.
+ */
+ readSoundLocation() {
+ let soundUrl = document.getElementById("alarmSoundFileField");
+ soundUrl.value = Preferences.get("calendar.alarms.soundURL").value;
+ if (soundUrl.value.startsWith("file://")) {
+ soundUrl.label = gAlarmsPane.convertURLToLocalFile(soundUrl.value).leafName;
+ } else {
+ soundUrl.label = soundUrl.value;
+ }
+ soundUrl.style.backgroundImage = "url(moz-icon://" + soundUrl.label + "?size=16)";
+ return undefined;
+ },
+
+ /**
+ * Causes the default sound to be selected in the dialog controls
+ */
+ useDefaultSound() {
+ let defaultSoundUrl = "chrome://calendar/content/sound.wav";
+ Preferences.get("calendar.alarms.soundURL").value = defaultSoundUrl;
+ document.getElementById("alarmSoundCheckbox").checked = true;
+ this.readSoundLocation();
+ },
+
+ /**
+ * Opens a filepicker to open a local sound for the alarm.
+ */
+ browseAlarm() {
+ let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+
+ // If we already have a sound file, then use the path for that sound file
+ // as the initial path in the dialog.
+ let currentValue = Preferences.get("calendar.alarms.soundURL").value;
+ if (currentValue && currentValue.startsWith("file://")) {
+ let localFile = Services.io.newURI(currentValue).QueryInterface(Ci.nsIFileURL).file;
+ picker.displayDirectory = localFile.parent;
+ }
+
+ let title = document.getElementById("bundlePreferences").getString("soundFilePickerTitle");
+
+ picker.init(window, title, Ci.nsIFilePicker.modeOpen);
+ picker.appendFilters(Ci.nsIFilePicker.filterAudio);
+ picker.appendFilters(Ci.nsIFilePicker.filterAll);
+
+ picker.open(rv => {
+ if (rv != Ci.nsIFilePicker.returnOK || !picker.file) {
+ return;
+ }
+ Preferences.get("calendar.alarms.soundURL").value = picker.fileURL.spec;
+ document.getElementById("alarmSoundCheckbox").checked = true;
+ this.readSoundLocation();
+ });
+ },
+
+ /**
+ * Plays the alarm sound currently selected.
+ */
+ previewAlarm() {
+ let soundUrl;
+ if (Preferences.get("calendar.alarms.soundType").value == 0) {
+ soundUrl = "chrome://calendar/content/sound.wav";
+ } else {
+ soundUrl = Preferences.get("calendar.alarms.soundURL").value;
+ }
+ let soundIfc = Cc["@mozilla.org/sound;1"].createInstance(Ci.nsISound);
+ let url;
+ try {
+ soundIfc.init();
+ if (soundUrl && soundUrl.length && soundUrl.length > 0) {
+ url = Services.io.newURI(soundUrl);
+ soundIfc.play(url);
+ } else {
+ soundIfc.beep();
+ }
+ } catch (ex) {
+ dump("alarms.js previewAlarm Exception caught! " + ex + "\n");
+ }
+ },
+
+ /**
+ * Handler function to call when the calendar.alarms.playsound preference
+ * has been changed. Updates the disabled state of fields that depend on
+ * playing a sound.
+ */
+ alarmsPlaySoundPrefChanged() {
+ let alarmsPlaySoundPref = Preferences.get("calendar.alarms.playsound");
+ let alarmsSoundType = Preferences.get("calendar.alarms.soundType");
+
+ for (let item of ["alarmSoundType", "calendar.prefs.alarm.sound.play"]) {
+ document.getElementById(item).disabled = !alarmsPlaySoundPref.value;
+ }
+
+ for (let item of ["alarmSoundFileField", "calendar.prefs.alarm.sound.browse"]) {
+ document.getElementById(item).disabled =
+ alarmsSoundType.value != 1 || !alarmsPlaySoundPref.value;
+ }
+ },
+};
+
+Preferences.get("calendar.alarms.playsound").on("change", gAlarmsPane.alarmsPlaySoundPrefChanged);
+Preferences.get("calendar.alarms.soundType").on("change", gAlarmsPane.alarmsPlaySoundPrefChanged);
+Preferences.get("calendar.alarms.soundURL").on("change", gAlarmsPane.readSoundLocation);
diff --git a/comm/calendar/base/content/preferences/calendar-preferences.inc.xhtml b/comm/calendar/base/content/preferences/calendar-preferences.inc.xhtml
new file mode 100644
index 0000000000..d07f2b812b
--- /dev/null
+++ b/comm/calendar/base/content/preferences/calendar-preferences.inc.xhtml
@@ -0,0 +1,55 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This file requires the following localization files:
+# chrome://lightning/locale/lightning.dtd
+# chrome://calendar/locale/global.dtd
+# chrome://calendar/locale/calendar-event-dialog.dtd
+ <linkset>
+ <html:link rel="localization" href="calendar/preferences.ftl"/>
+ <html:link rel="localization" href="calendar/category-dialog.ftl"/>
+ </linkset>
+ <html:template id="paneCalendar"
+ flex="1"
+ insertbefore="paneChat">
+
+ <hbox id="calendarPaneCategory"
+ class="subcategory"
+ data-category="paneCalendar">
+ <html:h1 data-l10n-id="calendar-title"></html:h1>
+ </hbox>
+
+#include views.inc.xhtml
+#include general.inc.xhtml
+
+ <hbox id="calendarReminderCategory"
+ class="subcategory"
+ data-category="paneCalendar">
+ <html:h1 data-l10n-id="calendar-title-reminder"></html:h1>
+ </hbox>
+
+#include alarms.inc.xhtml
+
+ <hbox id="calendarNotificationCategory"
+ class="subcategory"
+ data-category="paneCalendar">
+ <html:h1 data-l10n-id="calendar-title-notification"></html:h1>
+ </hbox>
+
+ <html:div data-category="paneCalendar">
+ <html:fieldset data-category="paneCalendar">
+ <calendar-notifications-setting id="calendar-notifications-setting"/>
+ <label data-l10n-id="calendar-notifications-customize-label"
+ class="indent tip-caption"></label>
+ </html:fieldset>
+ </html:div>
+
+ <hbox id="calendarCategoriesCategory"
+ class="subcategory"
+ data-category="paneCalendar">
+ <html:h1 data-l10n-id="calendar-title-category"></html:h1>
+ </hbox>
+
+#include categories.inc.xhtml
+ </html:template>
diff --git a/comm/calendar/base/content/preferences/calendar-preferences.js b/comm/calendar/base/content/preferences/calendar-preferences.js
new file mode 100644
index 0000000000..4b42fa2300
--- /dev/null
+++ b/comm/calendar/base/content/preferences/calendar-preferences.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* exported gCalendarPane */
+
+/* import-globals-from alarms.js */
+/* import-globals-from categories.js */
+/* import-globals-from general.js */
+/* import-globals-from notifications.js */
+/* import-globals-from views.js */
+/* globals Preferences */
+
+Preferences.add({ id: "calendar.preferences.lightning.selectedTabIndex", type: "int" });
+
+var gCalendarPane = {
+ init() {
+ let elements = document.querySelectorAll("#paneCalendar preference");
+ for (let element of elements) {
+ element.updateElements();
+ }
+ gCalendarGeneralPane.init();
+ gAlarmsPane.init();
+ gNotificationsPane.init();
+ gCategoriesPane.init();
+ gViewsPane.init();
+ },
+};
diff --git a/comm/calendar/base/content/preferences/categories.inc.xhtml b/comm/calendar/base/content/preferences/categories.inc.xhtml
new file mode 100644
index 0000000000..d6f37be9df
--- /dev/null
+++ b/comm/calendar/base/content/preferences/categories.inc.xhtml
@@ -0,0 +1,32 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at http://mozilla.org/MPL/2.0/.
+ <html:div data-category="paneCalendar">
+ <html:fieldset data-category="paneCalendar">
+ <separator class="thin"/>
+ <hbox>
+ <richlistbox id="categorieslist"
+ flex="1"
+ seltype="multiple"
+ onselect="gCategoriesPane.updateButtons()"
+ ondblclick="gCategoriesPane.listOnDblClick(event)"/>
+ <vbox id="categoriesButtons">
+ <hbox>
+ <button is="highlightable-button" id="newCButton"
+ data-l10n-id="new-tag-button"
+ oncommand="gCategoriesPane.addCategory()"
+ search-l10n-ids="category-name-label,category-color-label.label"/>
+ </hbox>
+ <hbox>
+ <button is="highlightable-button" id="editCButton"
+ data-l10n-id="edit-tag-button"
+ oncommand="gCategoriesPane.editCategory()"
+ search-l10n-ids="category-name-label,category-color-label.label"/>
+ </hbox>
+ <button is="highlightable-button" id="deleteCButton"
+ data-l10n-id="delete-tag-button"
+ oncommand="gCategoriesPane.deleteCategory()"/>
+ </vbox>
+ </hbox>
+ </html:fieldset>
+ </html:div>
diff --git a/comm/calendar/base/content/preferences/categories.js b/comm/calendar/base/content/preferences/categories.js
new file mode 100644
index 0000000000..ba7453b447
--- /dev/null
+++ b/comm/calendar/base/content/preferences/categories.js
@@ -0,0 +1,297 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* exported gCategoriesPane */
+
+/* globals gSubDialog, Preferences */
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+var { AppConstants } = ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs");
+
+Preferences.add({ id: "calendar.categories.names", type: "string" });
+
+var gCategoryList;
+var categoryPrefBranch = Services.prefs.getBranch("calendar.category.color.");
+
+/**
+ * Global Object to hold methods for the categories pref pane
+ */
+var gCategoriesPane = {
+ mCategoryDialog: null,
+ mWinProp: null,
+
+ /**
+ * Initialize the categories pref pane. Sets up dialog controls to show the
+ * categories saved in preferences.
+ */
+ init() {
+ // On non-instant-apply platforms, once this pane has been loaded,
+ // attach our "revert all changes" function to the parent prefwindow's
+ // "ondialogcancel" event.
+ let parentPrefWindow = document.documentElement;
+ if (!parentPrefWindow.instantApply) {
+ let existingOnDialogCancel = parentPrefWindow.getAttribute("ondialogcancel");
+ parentPrefWindow.setAttribute(
+ "ondialogcancel",
+ "gCategoriesPane.panelOnCancel(); " + existingOnDialogCancel
+ );
+ }
+
+ // A list of preferences to be reverted when the dialog is cancelled.
+ // It needs to be a property of the parent to be visible onCancel
+ if (!("backupPrefList" in parent)) {
+ parent.backupPrefList = [];
+ }
+
+ gCategoryList = cal.category.fromPrefs();
+
+ this.updateCategoryList();
+
+ this.mCategoryDialog = "chrome://calendar/content/preferences/editCategory.xhtml";
+
+ // Workaround for Bug 1151440 - the HTML color picker won't work
+ // in linux when opened from modal dialog
+ this.mWinProp = "centerscreen, chrome, resizable=no";
+ if (AppConstants.platform != "linux") {
+ this.mWinProp += ", modal";
+ }
+ },
+
+ /**
+ * Updates the listbox containing the categories from the categories saved
+ * in preferences.
+ */
+
+ updatePrefs() {
+ cal.l10n.sortArrayByLocaleCollator(gCategoryList);
+ Preferences.get("calendar.categories.names").value = cal.category.arrayToString(gCategoryList);
+ },
+
+ updateCategoryList() {
+ this.updatePrefs();
+ let listbox = document.getElementById("categorieslist");
+
+ listbox.clearSelection();
+ this.updateButtons();
+
+ while (listbox.lastElementChild) {
+ listbox.lastChild.remove();
+ }
+
+ for (let i = 0; i < gCategoryList.length; i++) {
+ let newListItem = document.createXULElement("richlistitem");
+ let categoryName = document.createXULElement("label");
+ categoryName.setAttribute("id", gCategoryList[i]);
+ categoryName.setAttribute("flex", "1");
+ categoryName.setAttribute("value", gCategoryList[i]);
+ let categoryNameFix = cal.view.formatStringForCSSRule(gCategoryList[i]);
+
+ let categoryColor = document.createXULElement("box");
+ categoryColor.style.width = "150px";
+ let colorCode = categoryPrefBranch.getCharPref(categoryNameFix, "");
+ if (colorCode) {
+ categoryColor.style.backgroundColor = colorCode;
+ }
+
+ newListItem.appendChild(categoryName);
+ newListItem.appendChild(categoryColor);
+ listbox.appendChild(newListItem);
+ }
+ },
+
+ /**
+ * Adds a category, opening the edit category dialog to prompt the user to
+ * set up the category.
+ */
+ async addCategory() {
+ let listbox = document.getElementById("categorieslist");
+ listbox.clearSelection();
+ this.updateButtons();
+ let params = {
+ title: await document.l10n.formatValue("category-new-label"),
+ category: "",
+ color: null,
+ };
+ gSubDialog.open(this.mCategoryDialog, { features: "resizable=no" }, params);
+ },
+
+ /**
+ * Edits the currently selected category using the edit category dialog.
+ */
+ async editCategory() {
+ let list = document.getElementById("categorieslist");
+ let categoryNameFix = cal.view.formatStringForCSSRule(gCategoryList[list.selectedIndex]);
+ let currentColor = categoryPrefBranch.getCharPref(categoryNameFix, "");
+
+ let params = {
+ title: await document.l10n.formatValue("category-edit-label"),
+ category: gCategoryList[list.selectedIndex],
+ color: currentColor,
+ };
+ if (list.selectedItem) {
+ gSubDialog.open(this.mCategoryDialog, { features: "resizable=no" }, params);
+ }
+ },
+
+ /**
+ * Removes the selected category.
+ */
+ deleteCategory() {
+ let list = document.getElementById("categorieslist");
+ if (list.selectedCount < 1) {
+ return;
+ }
+
+ let categoryNameFix = cal.view.formatStringForCSSRule(gCategoryList[list.selectedIndex]);
+ this.backupData(categoryNameFix);
+ try {
+ categoryPrefBranch.clearUserPref(categoryNameFix);
+ } catch (ex) {
+ // If the pref doesn't exist, don't bail out here.
+ }
+
+ // Remove category entry from listbox and gCategoryList.
+ let newSelection =
+ list.selectedItem.nextElementSibling || list.selectedItem.previousElementSibling;
+ let selectedItems = Array.from(list.selectedItems);
+ for (let i = list.selectedCount - 1; i >= 0; i--) {
+ let item = selectedItems[i];
+ if (item == newSelection) {
+ newSelection = newSelection.nextElementSibling || newSelection.previousElementSibling;
+ }
+ gCategoryList.splice(list.getIndexOfItem(item), 1);
+ item.remove();
+ }
+ list.selectedItem = newSelection;
+ this.updateButtons();
+
+ // Update the prefs from gCategoryList
+ this.updatePrefs();
+ },
+
+ /**
+ * Saves the given category to the preferences.
+ *
+ * @param categoryName The name of the category.
+ * @param categoryColor The color of the category
+ */
+ async saveCategory(categoryName, categoryColor) {
+ let list = document.getElementById("categorieslist");
+ // Check to make sure another category doesn't have the same name
+ let toBeDeleted = -1;
+ for (let i = 0; i < gCategoryList.length; i++) {
+ if (i == list.selectedIndex) {
+ continue;
+ }
+
+ if (categoryName.toLowerCase() == gCategoryList[i].toLowerCase()) {
+ let [title, description] = await document.l10n.formatValues([
+ { id: "category-overwrite-title" },
+ { id: "category-overwrite" },
+ ]);
+
+ if (Services.prompt.confirm(null, title, description)) {
+ if (list.selectedIndex != -1) {
+ // Don't delete the old category yet. It will mess up indices.
+ toBeDeleted = list.selectedIndex;
+ }
+ list.selectedIndex = i;
+ } else {
+ return;
+ }
+ }
+ }
+
+ if (categoryName.length == 0) {
+ let warning = await document.l10n.formatValue("category-blank-warning");
+ Services.prompt.alert(null, null, warning);
+ return;
+ }
+
+ let categoryNameFix = cal.view.formatStringForCSSRule(categoryName);
+ if (list.selectedIndex == -1) {
+ this.backupData(categoryNameFix);
+ gCategoryList.push(categoryName);
+ if (categoryColor) {
+ categoryPrefBranch.setCharPref(categoryNameFix, categoryColor);
+ }
+ } else {
+ this.backupData(categoryNameFix);
+ gCategoryList.splice(list.selectedIndex, 1, categoryName);
+ categoryPrefBranch.setCharPref(categoryNameFix, categoryColor || "");
+ }
+
+ // If 'Overwrite' was chosen, delete category that was being edited
+ if (toBeDeleted != -1) {
+ list.selectedIndex = toBeDeleted;
+ this.deleteCategory();
+ }
+
+ this.updateCategoryList();
+
+ let updatedCategory = gCategoryList.indexOf(categoryName);
+ list.ensureIndexIsVisible(updatedCategory);
+ list.selectedIndex = updatedCategory;
+ },
+
+ /**
+ * Enable the edit and delete category buttons.
+ */
+ updateButtons() {
+ let categoriesList = document.getElementById("categorieslist");
+ document.getElementById("deleteCButton").disabled = categoriesList.selectedCount <= 0;
+ document.getElementById("editCButton").disabled = categoriesList.selectedCount != 1;
+ },
+
+ /**
+ * Backs up the category name in case the dialog is canceled.
+ *
+ * @see formatStringForCSSRule
+ * @param categoryNameFix The formatted category name.
+ */
+ backupData(categoryNameFix) {
+ let currentColor = categoryPrefBranch.getCharPref(categoryNameFix, "##NEW");
+
+ for (let i = 0; i < parent.backupPrefList.length; i++) {
+ if (categoryNameFix == parent.backupPrefList[i].name) {
+ return;
+ }
+ }
+ parent.backupPrefList[parent.backupPrefList.length] = {
+ name: categoryNameFix,
+ color: currentColor,
+ };
+ },
+
+ /**
+ * Event Handler function to be called on doubleclick of the categories
+ * list. If the edit function is enabled and the user doubleclicked on a
+ * list item, then edit the selected category.
+ */
+ listOnDblClick(event) {
+ if (event.target.localName == "listitem" && !document.getElementById("editCButton").disabled) {
+ this.editCategory();
+ }
+ },
+
+ /**
+ * Reverts category preferences in case the cancel button is pressed.
+ */
+ panelOnCancel() {
+ for (let i = 0; i < parent.backupPrefList.length; i++) {
+ if (parent.backupPrefList[i].color == "##NEW") {
+ try {
+ categoryPrefBranch.clearUserPref(parent.backupPrefList[i].name);
+ } catch (ex) {
+ dump("Exception caught in 'panelOnCancel': " + ex + "\n");
+ }
+ } else {
+ categoryPrefBranch.setCharPref(
+ parent.backupPrefList[i].name,
+ parent.backupPrefList[i].color
+ );
+ }
+ }
+ },
+};
diff --git a/comm/calendar/base/content/preferences/editCategory.js b/comm/calendar/base/content/preferences/editCategory.js
new file mode 100644
index 0000000000..c3479b88c0
--- /dev/null
+++ b/comm/calendar/base/content/preferences/editCategory.js
@@ -0,0 +1,112 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* exported editCategoryLoad, categoryNameChanged, clickColor, delay */
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+window.addEventListener("DOMContentLoaded", editCategoryLoad);
+
+// Global variable, set to true if the user has picked a custom color.
+var customColorSelected = false;
+
+/**
+ * Load Handler, called when the edit category dialog is loaded
+ */
+function editCategoryLoad() {
+ let winArg = window.arguments[0];
+ let color = winArg.color || cal.view.hashColor(winArg.category);
+ let hasColor = !!winArg.color;
+ document.getElementById("categoryName").value = winArg.category;
+ document.getElementById("categoryColor").value = color;
+ document.getElementById("useColor").checked = hasColor;
+ customColorSelected = hasColor;
+ document.title = winArg.title;
+
+ toggleColor();
+}
+
+/**
+ * Handler function to be called when the category dialog is accepted and
+ * the opener should further process the selected name and color
+ */
+document.addEventListener("dialogaccept", () => {
+ let color = document.getElementById("useColor").checked
+ ? document.getElementById("categoryColor").value
+ : null;
+
+ let categoryName = document.getElementById("categoryName").value;
+ window.opener.gCategoriesPane.saveCategory(categoryName, color);
+});
+
+/**
+ * Handler function to be called when the category name changed
+ */
+function categoryNameChanged() {
+ let newValue = document.getElementById("categoryName").value;
+
+ // The user removed the category name, assign the color automatically again.
+ if (newValue == "") {
+ customColorSelected = false;
+ }
+
+ if (!customColorSelected && document.getElementById("useColor").checked) {
+ // Color is wanted, choose the color based on the category name's hash.
+ document.getElementById("categoryColor").value = cal.view.hashColor(newValue);
+ }
+}
+
+/**
+ * Handler function to be called when the color picker's color has been changed.
+ */
+function colorPickerChanged() {
+ document.getElementById("useColor").checked = true;
+ customColorSelected = true;
+}
+
+/**
+ * Handler called when the use color checkbox is toggled.
+ */
+function toggleColor() {
+ let useColor = document.getElementById("useColor").checked;
+ let categoryColor = document.getElementById("categoryColor");
+
+ if (useColor) {
+ categoryColor.removeAttribute("disabled");
+ if (toggleColor.lastColor) {
+ categoryColor.value = toggleColor.lastColor;
+ }
+ } else {
+ categoryColor.setAttribute("disabled", "disabled");
+ toggleColor.lastColor = categoryColor.value;
+ categoryColor.value = "";
+ }
+}
+
+/**
+ * Click handler for the color picker. Turns the button back into a colorpicker
+ * when clicked.
+ */
+function clickColor() {
+ let categoryColor = document.getElementById("categoryColor");
+ if (categoryColor.hasAttribute("disabled")) {
+ colorPickerChanged();
+ toggleColor();
+ categoryColor.click();
+ }
+}
+
+/**
+ * Call the function after the given timeout, resetting the timer if delay is
+ * called again with the same function.
+ *
+ * @param timeout The timeout interval.
+ * @param func The function to call after the timeout.
+ */
+function delay(timeout, func) {
+ if (func.timer) {
+ clearTimeout(func.timer);
+ }
+ func.timer = setTimeout(func, timeout);
+}
diff --git a/comm/calendar/base/content/preferences/editCategory.xhtml b/comm/calendar/base/content/preferences/editCategory.xhtml
new file mode 100644
index 0000000000..5128a03249
--- /dev/null
+++ b/comm/calendar/base/content/preferences/editCategory.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<!DOCTYPE html>
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ scrolling="false"
+>
+ <head>
+ <title><!-- category-new-label / category-edit-label --></title>
+ <link rel="localization" href="calendar/category-dialog.ftl" />
+ <script defer="defer" src="chrome://messenger/content/globalOverlay.js"></script>
+ <script defer="defer" src="chrome://global/content/editMenuOverlay.js"></script>
+ <script defer="defer" src="chrome://calendar/content/preferences/editCategory.js"></script>
+ </head>
+ <html:body xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <dialog id="editCategory" buttons="accept,cancel" style="width: 100vw; height: 100vh">
+ <label id="categoryNameLabel" data-l10n-id="category-name-label" control="categoryName" />
+ <html:input
+ id="categoryName"
+ type="text"
+ onchange="categoryNameChanged()"
+ oninput="delay(500, categoryNameChanged)"
+ aria-labelledby="categoryNameLabel"
+ />
+ <hbox id="colorSelectRow">
+ <checkbox
+ id="useColor"
+ data-l10n-id="category-color-label"
+ oncommand="toggleColor(); categoryNameChanged()"
+ />
+ <html:input
+ id="categoryColor"
+ type="color"
+ style="width: 64px; height: 23px"
+ onclick="clickColor()"
+ onchange="colorPickerChanged()"
+ aria-labelledby="useColor"
+ />
+ </hbox>
+ </dialog>
+ </html:body>
+</html>
diff --git a/comm/calendar/base/content/preferences/general.inc.xhtml b/comm/calendar/base/content/preferences/general.inc.xhtml
new file mode 100644
index 0000000000..f6b00ad29c
--- /dev/null
+++ b/comm/calendar/base/content/preferences/general.inc.xhtml
@@ -0,0 +1,185 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at http://mozilla.org/MPL/2.0/.
+ <html:div data-category="paneCalendar">
+ <html:fieldset data-category="paneCalendar">
+ <html:legend data-l10n-id="todaypane-legend"></html:legend>
+ <hbox align="center">
+ <label data-l10n-id="agenda-days"
+ control="agenda-days-menulist"/>
+ <hbox>
+ <menulist id="agenda-days-menulist"
+ preference="calendar.agenda.days">
+ <menupopup id="agenda-days-popup">
+ <menuitem value="1"/>
+ <menuitem value="2"/>
+ <menuitem value="3"/>
+ <menuitem value="4"/>
+ <menuitem value="5"/>
+ <menuitem value="6"/>
+ <menuitem value="7"/>
+ <menuitem value="14"/>
+ <menuitem value="21"/>
+ <menuitem value="28"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </hbox>
+ </html:fieldset>
+ </html:div>
+
+ <html:div data-category="paneCalendar">
+ <html:fieldset id="defaults-itemtype-groupbox" data-category="paneCalendar">
+ <html:legend data-l10n-id="event-task-legend"></html:legend>
+ <vbox id="defaults-itemtype-box">
+ <hbox id="defaults-event-grid-box" align="center">
+ <label id="default-event-length-label"
+ data-l10n-id="default-length-label"
+ control="defaultlength"/>
+ <html:input id="defaultlength" type="number"
+ class="size3"
+ min="0"
+ preference="calendar.event.defaultlength"
+ onselect="updateUnitLabelPlural('defaultlength', 'defaultlengthunit', 'minutes')"
+ oninput="updateUnitLabelPlural('defaultlength', 'defaultlengthunit', 'minutes')"/>
+ <label id="defaultlengthunit"/>
+ </hbox>
+ <html:table id="defaults-task-table">
+ <html:tr id="defaults-task-start-row">
+ <html:td>
+ <label id="default-task-start-label"
+ data-l10n-id="task-start-label"
+ control="default_task_start"/>
+ </html:td>
+ <html:td>
+ <hbox>
+ <menulist id="default_task_start"
+ crop="none"
+ oncommand="gCalendarGeneralPane.updateDefaultTodoDates()"
+ preference="calendar.task.defaultstart">
+ <menupopup id="default_task_start_popup">
+ <menuitem id="default_task_start_none"
+ data-l10n-id="task-start-1-label"
+ value="none"
+ selected="true"/>
+ <menuitem id="default_task_start_start_of_day"
+ data-l10n-id="task-start-2-label"
+ value="startofday"/>
+ <menuitem id="default_task_start_tomorrow"
+ data-l10n-id="task-start-4-label"
+ value="tomorrow"/>
+ <menuitem id="default_task_start_next_week"
+ data-l10n-id="task-start-5-label"
+ value="nextweek"/>
+ <menuitem id="default_task_start_offset_current"
+ data-l10n-id="task-start-6-label"
+ value="offsetcurrent"/>
+ <menuitem id="default_task_start_offset_next_hour"
+ data-l10n-id="task-start-8-label"
+ value="offsetnexthour"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </html:td>
+ <html:td>
+ <hbox id="default_task_start_offset" align="center">
+ <html:input id="default_task_start_offset_text" type="number"
+ class="size3"
+ min="0"
+ preference="calendar.task.defaultstartoffset"
+ onselect="updateMenuLabelsPlural('default_task_start_offset_text', 'default_task_start_offset_units')"
+ oninput="updateMenuLabelsPlural('default_task_start_offset_text', 'default_task_start_offset_units')"/>
+ <hbox>
+ <menulist id="default_task_start_offset_units"
+ crop="none"
+ preference="calendar.task.defaultstartoffsetunits">
+ <menupopup id="default_task_start_offset_units_popup">
+ <menuitem id="default_task_start_offset_units_minutes"
+ value="minutes"
+ selected="true"/>
+ <menuitem id="default_task_start_offset_units_hours"
+ value="hours"/>
+ <menuitem id="default_task_start_offset_units_days"
+ value="days"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </hbox>
+ </html:td>
+ </html:tr>
+ <html:tr id="defaults-task-due-row">
+ <html:td>
+ <label id="default-task-due-label"
+ data-l10n-id="task-due-label"
+ control="default_task_due"/>
+ </html:td>
+ <html:td>
+ <hbox>
+ <menulist id="default_task_due"
+ crop="none"
+ oncommand="gCalendarGeneralPane.updateDefaultTodoDates()"
+ preference="calendar.task.defaultdue">
+ <menupopup id="default_task_due_popup">
+ <menuitem id="default_task_due_none"
+ data-l10n-id="task-start-1-label"
+ value="none"
+ selected="true"/>
+ <menuitem id="default_task_due_end_of_day"
+ data-l10n-id="task-start-3-label"
+ value="endofday"/>
+ <menuitem id="default_task_due_tomorrow"
+ data-l10n-id="task-start-4-label"
+ value="tomorrow"/>
+ <menuitem id="default_task_due_next_week"
+ data-l10n-id="task-start-5-label"
+ value="nextweek"/>
+ <menuitem id="default_task_due_offset_current"
+ data-l10n-id="task-start-7-label"
+ value="offsetcurrent"/>
+ <menuitem id="default_task_due_offset_next_hour"
+ data-l10n-id="task-start-8-label"
+ value="offsetnexthour"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </html:td>
+ <html:td>
+ <hbox id="default_task_due_offset" align="center">
+ <html:input id="default_task_due_offset_text" type="number"
+ class="size3"
+ min="0"
+ preference="calendar.task.defaultdueoffset"
+ onselect="updateMenuLabelsPlural('default_task_due_offset_text', 'default_task_due_offset_units')"
+ oninput="updateMenuLabelsPlural('default_task_due_offset_text', 'default_task_due_offset_units')"/>
+ <hbox>
+ <menulist id="default_task_due_offset_units"
+ crop="none"
+ preference="calendar.task.defaultdueoffsetunits">
+ <menupopup id="default_task_due_offset_units_popup">
+ <menuitem id="default_task_due_offset_units_minutes"
+ value="minutes"
+ selected="true"/>
+ <menuitem id="default_task_due_offset_units_hours"
+ value="hours"/>
+ <menuitem id="default_task_due_offset_units_days"
+ value="days"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </hbox>
+ </html:td>
+ </html:tr>
+ </html:table>
+ </vbox>
+ <hbox align="center">
+ <checkbox id="tabedit" pack="end"
+ data-l10n-id="edit-intab-label"
+ preference="calendar.item.editInTab"/>
+ </hbox>
+ <hbox align="center">
+ <checkbox id="promptDelete" pack="end"
+ data-l10n-id="prompt-delete-label"
+ preference="calendar.item.promptDelete"/>
+ </hbox>
+ </html:fieldset>
+ </html:div>
diff --git a/comm/calendar/base/content/preferences/general.js b/comm/calendar/base/content/preferences/general.js
new file mode 100644
index 0000000000..8991193e05
--- /dev/null
+++ b/comm/calendar/base/content/preferences/general.js
@@ -0,0 +1,148 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* exported gCalendarGeneralPane */
+
+/* import-globals-from ../calendar-ui-utils.js */
+/* globals Preferences */
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+Preferences.addAll([
+ { id: "calendar.date.format", type: "int" },
+ { id: "calendar.event.defaultlength", type: "int" },
+ { id: "calendar.timezone.local", type: "string" },
+ { id: "calendar.timezone.useSystemTimezone", type: "bool" },
+ { id: "calendar.task.defaultstart", type: "string" },
+ { id: "calendar.task.defaultstartoffset", type: "int" },
+ { id: "calendar.task.defaultstartoffsetunits", type: "string" },
+ { id: "calendar.task.defaultdue", type: "string" },
+ { id: "calendar.task.defaultdueoffset", type: "int" },
+ { id: "calendar.task.defaultdueoffsetunits", type: "string" },
+ { id: "calendar.agenda.days", type: "int" },
+ { id: "calendar.item.editInTab", type: "bool" },
+ { id: "calendar.item.promptDelete", type: "bool" },
+]);
+
+/**
+ * Global Object to hold methods for the general pref pane
+ */
+var gCalendarGeneralPane = {
+ /**
+ * Initialize the general pref pane. Sets up dialog controls to match the
+ * values set in prefs.
+ */
+ init() {
+ this.onChangedUseSystemTimezonePref();
+
+ let formatter = cal.dtz.formatter;
+ let dateFormattedLong = formatter.formatDateLong(cal.dtz.now());
+ let dateFormattedShort = formatter.formatDateShort(cal.dtz.now());
+
+ // menu items include examples of current date formats.
+ document.l10n.setAttributes(
+ document.getElementById("dateformat-long-menuitem"),
+ "dateformat-long",
+ { date: dateFormattedLong }
+ );
+
+ document.l10n.setAttributes(
+ document.getElementById("dateformat-short-menuitem"),
+ "dateformat-short",
+ { date: dateFormattedShort }
+ );
+
+ // deselect and reselect to update visible item title
+ updateUnitLabelPlural("defaultlength", "defaultlengthunit", "minutes");
+ this.updateDefaultTodoDates();
+
+ let tzMenuList = document.getElementById("calendar-timezone-menulist");
+ let tzMenuPopup = document.getElementById("calendar-timezone-menupopup");
+
+ let tzids = {};
+ let displayNames = [];
+ // don't rely on what order the timezone-service gives you
+ for (let timezoneId of cal.timezoneService.timezoneIds) {
+ let timezone = cal.timezoneService.getTimezone(timezoneId);
+ if (timezone && !timezone.isFloating && !timezone.isUTC) {
+ let displayName = timezone.displayName;
+ displayNames.push(displayName);
+ tzids[displayName] = timezone.tzid;
+ }
+ }
+ // the display names need to be sorted
+ displayNames.sort((a, b) => a.localeCompare(b));
+ for (let displayName of displayNames) {
+ addMenuItem(tzMenuPopup, displayName, tzids[displayName]);
+ }
+
+ let prefValue = Preferences.get("calendar.timezone.local").value;
+ if (!prefValue) {
+ prefValue = cal.dtz.defaultTimezone.tzid;
+ }
+ tzMenuList.value = prefValue;
+
+ // Set the agenda length menulist.
+ this.initializeTodaypaneMenu();
+ },
+
+ updateDefaultTodoDates() {
+ let defaultDue = document.getElementById("default_task_due").value;
+ let defaultStart = document.getElementById("default_task_start").value;
+ let offsetValues = ["offsetcurrent", "offsetnexthour"];
+
+ document.getElementById("default_task_due_offset").style.visibility = offsetValues.includes(
+ defaultDue
+ )
+ ? ""
+ : "hidden";
+ document.getElementById("default_task_start_offset").style.visibility = offsetValues.includes(
+ defaultStart
+ )
+ ? ""
+ : "hidden";
+
+ updateMenuLabelsPlural("default_task_start_offset_text", "default_task_start_offset_units");
+ updateMenuLabelsPlural("default_task_due_offset_text", "default_task_due_offset_units");
+ },
+
+ initializeTodaypaneMenu() {
+ // Assign the labels for the menuitem
+ let menulist = document.getElementById("agenda-days-menulist");
+ let items = menulist.getElementsByTagName("menuitem");
+ for (let menuItem of items) {
+ let menuitemValue = Number(menuItem.value);
+ if (menuitemValue > 7) {
+ menuItem.label = unitPluralForm(menuitemValue / 7, "weeks");
+ } else {
+ menuItem.label = unitPluralForm(menuitemValue, "days");
+ }
+ }
+
+ let pref = Preferences.get("calendar.agenda.days");
+ let value = pref.value;
+
+ // Check if the preference has been edited with a wrong value.
+ if (value > 0 && value <= 28) {
+ if (value % 7 != 0) {
+ let intValue = Math.floor(value / 7) * 7;
+ value = intValue == 0 ? value : intValue;
+ pref.value = value;
+ }
+ } else {
+ pref.value = 14;
+ }
+ },
+
+ onChangedUseSystemTimezonePref() {
+ let useSystemTimezonePref = Preferences.get("calendar.timezone.useSystemTimezone");
+
+ document.getElementById("calendar-timezone-menulist").disabled = useSystemTimezonePref.value;
+ },
+};
+
+Preferences.get("calendar.timezone.useSystemTimezone").on(
+ "change",
+ gCalendarGeneralPane.onChangedUseSystemTimezonePref
+);
diff --git a/comm/calendar/base/content/preferences/notifications.js b/comm/calendar/base/content/preferences/notifications.js
new file mode 100644
index 0000000000..11725b8309
--- /dev/null
+++ b/comm/calendar/base/content/preferences/notifications.js
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* exported gNotificationsPane */
+/* globals Preferences */
+
+Preferences.add({ id: "calendar.notifications.times", type: "string" });
+
+/**
+ * Global Object to hold methods for the notifications pref pane.
+ */
+var gNotificationsPane = {
+ /**
+ * Initialize <calendar-notifications-setting> and listen to the change event.
+ */
+ init() {
+ var calendarNotificationsSetting = document.getElementById("calendar-notifications-setting");
+ calendarNotificationsSetting.value = Preferences.get("calendar.notifications.times").value;
+ calendarNotificationsSetting.addEventListener("change", () => {
+ Preferences.get("calendar.notifications.times").value = calendarNotificationsSetting.value;
+ });
+ },
+};
diff --git a/comm/calendar/base/content/preferences/views.inc.xhtml b/comm/calendar/base/content/preferences/views.inc.xhtml
new file mode 100644
index 0000000000..d37d183d8b
--- /dev/null
+++ b/comm/calendar/base/content/preferences/views.inc.xhtml
@@ -0,0 +1,311 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at http://mozilla.org/MPL/2.0/.
+ <html:div data-category="paneCalendar">
+ <html:fieldset data-category="paneCalendar">
+ <separator/>
+ <box id="datePrefsBox">
+ <hbox align="center">
+ <label data-l10n-id="dateformat-label"
+ control="dateformat"/>
+ </hbox>
+ <hbox align="center">
+ <menulist id="dateformat" crop="none"
+ flex="1"
+ preference="calendar.date.format">
+ <menupopup id="dateformatpopup">
+ <menuitem id="dateformat-long-menuitem"
+ value="0"/>
+ <menuitem id="dateformat-short-menuitem"
+ value="1"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <spacer/>
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK) && defined(MOZ_ENABLE_DBUS)
+ <radiogroup id="timezone-source-radio-group"
+ orient="vertical"
+ preference="calendar.timezone.useSystemTimezone">
+ <hbox align="center">
+ <radio id="use-system-timezone-radio-button"
+ value="true"
+ data-l10n-id="use-system-timezone-radio-button"/>
+ </hbox>
+ <hbox>
+ <radio id="set-timezone-manually-radio-button"
+ value="false"
+ data-l10n-id="set-timezone-manually-radio-button"/>
+ </hbox>
+ </radiogroup>
+ <spacer/><spacer/>
+ <hbox align="center" class="indent">
+ <label data-l10n-id="timezone-label"
+ control="calendar-timezone-menulist"/>
+ </hbox>
+ <hbox align="center">
+ <menulist id="calendar-timezone-menulist"
+ flex="1"
+ preference="calendar.timezone.local">
+ <menupopup id="calendar-timezone-menupopup"/>
+ </menulist>
+ </hbox>
+#else
+ <hbox align="center" class="indent">
+ <label data-l10n-id="timezone-label"
+ control="calendar-timezone-menulist"/>
+ </hbox>
+ <hbox align="center">
+ <menulist id="calendar-timezone-menulist"
+ flex="1"
+ preference="calendar.timezone.local">
+ <menupopup id="calendar-timezone-menupopup"/>
+ </menulist>
+ </hbox>
+#endif
+ <spacer/>
+ <hbox align="center" flex="1">
+ <label data-l10n-id="weekstart-label"
+ control="weekstarts"/>
+ </hbox>
+ <hbox align="center">
+ <menulist id="weekstarts"
+ flex="1"
+ preference="calendar.week.start"
+ oncommand="gViewsPane.updateViewWorkDayCheckboxes(this.value)">
+ <menupopup id="weekstartspopup">
+ <menuitem data-l10n-id="day-1-name" value="0"/>
+ <menuitem data-l10n-id="day-2-name" value="1"/>
+ <menuitem data-l10n-id="day-3-name" value="2"/>
+ <menuitem data-l10n-id="day-4-name" value="3"/>
+ <menuitem data-l10n-id="day-5-name" value="4"/>
+ <menuitem data-l10n-id="day-6-name" value="5"/>
+ <menuitem data-l10n-id="day-7-name" value="6"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <spacer/>
+ </box>
+ <hbox align="center" flex="1">
+ <checkbox id="weekNumber"
+ crop="end"
+ data-l10n-id="show-weeknumber-label"
+ preference="calendar.view-minimonth.showWeekNumber"/>
+ </hbox>
+ <separator/>
+ <hbox>
+ <vbox>
+ <label data-l10n-id="workdays-label"/>
+ </vbox>
+ <vbox>
+ <hbox>
+ <checkbox id="dayoff0"
+ class="dayOffCheckbox"
+ data-l10n-id="day-1-checkbox"
+ orient="vertical"
+ preference="calendar.week.d0sundaysoff"/>
+ <checkbox id="dayoff1"
+ class="dayOffCheckbox"
+ data-l10n-id="day-2-checkbox"
+ orient="vertical"
+ preference="calendar.week.d1mondaysoff"/>
+ <checkbox id="dayoff2"
+ class="dayOffCheckbox"
+ data-l10n-id="day-3-checkbox"
+ orient="vertical"
+ preference="calendar.week.d2tuesdaysoff"/>
+ <checkbox id="dayoff3"
+ class="dayOffCheckbox"
+ data-l10n-id="day-4-checkbox"
+ orient="vertical"
+ preference="calendar.week.d3wednesdaysoff"/>
+ <checkbox id="dayoff4"
+ class="dayOffCheckbox"
+ data-l10n-id="day-5-checkbox"
+ orient="vertical"
+ preference="calendar.week.d4thursdaysoff"/>
+ <checkbox id="dayoff5"
+ class="dayOffCheckbox"
+ data-l10n-id="day-6-checkbox"
+ orient="vertical"
+ preference="calendar.week.d5fridaysoff"/>
+ <checkbox id="dayoff6"
+ class="dayOffCheckbox"
+ data-l10n-id="day-7-checkbox"
+ orient="vertical"
+ preference="calendar.week.d6saturdaysoff"/>
+ </hbox>
+ </vbox>
+ </hbox>
+ </html:fieldset>
+ </html:div>
+
+ <html:div data-category="paneCalendar">
+ <html:fieldset data-category="paneCalendar">
+ <html:legend data-l10n-id="dayweek-legend"></html:legend>
+ <html:table id="dayAndWeekViewsTable">
+ <html:tr>
+ <html:th>
+ <label data-l10n-id="visible-hours-label"
+ control="visiblehours"/>
+ </html:th>
+ <html:td>
+ <hbox align="center">
+ <hbox>
+ <menulist id="visiblehours"
+ preference="calendar.view.visiblehours">
+ <menupopup id="visiblehourspopup">
+ <menuitem label="1" value="1"/>
+ <menuitem label="2" value="2"/>
+ <menuitem label="3" value="3"/>
+ <menuitem label="4" value="4"/>
+ <menuitem label="5" value="5"/>
+ <menuitem label="6" value="6"/>
+ <menuitem label="7" value="7"/>
+ <menuitem label="8" value="8"/>
+ <menuitem label="9" value="9"/>
+ <menuitem label="10" value="10"/>
+ <menuitem label="11" value="11"/>
+ <menuitem label="12" value="12"/>
+ <menuitem label="13" value="13"/>
+ <menuitem label="14" value="14"/>
+ <menuitem label="15" value="15"/>
+ <menuitem label="16" value="16"/>
+ <menuitem label="17" value="17"/>
+ <menuitem label="18" value="18"/>
+ <menuitem label="19" value="19"/>
+ <menuitem label="20" value="20"/>
+ <menuitem label="21" value="21"/>
+ <menuitem label="22" value="22"/>
+ <menuitem label="23" value="23"/>
+ <menuitem label="24" value="24"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <label data-l10n-id="visible-hours-end-label"/>
+ </hbox>
+ </html:td>
+ </html:tr>
+ <html:tr>
+ <html:th>
+ <label data-l10n-id="day-start-label"
+ control="daystarthour"/>
+ </html:th>
+ <html:td>
+ <hbox>
+ <menulist id="daystarthour"
+ oncommand="gViewsPane.updateViewEndMenu(this.value);"
+ preference="calendar.view.daystarthour">
+ <menupopup id="daystarthourpopup">
+ <menuitem id="timeStart0" value="0" data-l10n-id="midnight-label"/>
+ <menuitem id="timeStart1" value="1"/>
+ <menuitem id="timeStart2" value="2"/>
+ <menuitem id="timeStart3" value="3"/>
+ <menuitem id="timeStart4" value="4"/>
+ <menuitem id="timeStart5" value="5"/>
+ <menuitem id="timeStart6" value="6"/>
+ <menuitem id="timeStart7" value="7"/>
+ <menuitem id="timeStart8" value="8"/>
+ <menuitem id="timeStart9" value="9"/>
+ <menuitem id="timeStart10" value="10"/>
+ <menuitem id="timeStart11" value="11"/>
+ <menuitem id="timeStart12" value="12" data-l10n-id="noon-label"/>
+ <menuitem id="timeStart13" value="13"/>
+ <menuitem id="timeStart14" value="14"/>
+ <menuitem id="timeStart15" value="15"/>
+ <menuitem id="timeStart16" value="16"/>
+ <menuitem id="timeStart17" value="17"/>
+ <menuitem id="timeStart18" value="18"/>
+ <menuitem id="timeStart19" value="19"/>
+ <menuitem id="timeStart20" value="20"/>
+ <menuitem id="timeStart21" value="21"/>
+ <menuitem id="timeStart22" value="22"/>
+ <menuitem id="timeStart23" value="23"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </html:td>
+ </html:tr>
+ <html:tr>
+ <html:th>
+ <label data-l10n-id="day-end-label"
+ control="dayendhour"/>
+ </html:th>
+ <html:td>
+ <hbox>
+ <menulist id="dayendhour"
+ oncommand="gViewsPane.updateViewStartMenu(this.value);"
+ preference="calendar.view.dayendhour">
+ <menupopup id="dayendhourpopup">
+ <menuitem id="timeEnd1" value="1"/>
+ <menuitem id="timeEnd2" value="2"/>
+ <menuitem id="timeEnd3" value="3"/>
+ <menuitem id="timeEnd4" value="4"/>
+ <menuitem id="timeEnd5" value="5"/>
+ <menuitem id="timeEnd6" value="6"/>
+ <menuitem id="timeEnd7" value="7"/>
+ <menuitem id="timeEnd8" value="8"/>
+ <menuitem id="timeEnd9" value="9"/>
+ <menuitem id="timeEnd10" value="10"/>
+ <menuitem id="timeEnd11" value="11"/>
+ <menuitem id="timeEnd12" value="12" data-l10n-id="noon-label"/>
+ <menuitem id="timeEnd13" value="13"/>
+ <menuitem id="timeEnd14" value="14"/>
+ <menuitem id="timeEnd15" value="15"/>
+ <menuitem id="timeEnd16" value="16"/>
+ <menuitem id="timeEnd17" value="17"/>
+ <menuitem id="timeEnd18" value="18"/>
+ <menuitem id="timeEnd19" value="19"/>
+ <menuitem id="timeEnd20" value="20"/>
+ <menuitem id="timeEnd21" value="21"/>
+ <menuitem id="timeEnd22" value="22"/>
+ <menuitem id="timeEnd23" value="23"/>
+ <menuitem id="timeEnd24" value="24" data-l10n-id="midnight-label"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </html:td>
+ </html:tr>
+ </html:table>
+ <checkbox id="showLocation" pack="end"
+ data-l10n-id="location-checkbox"
+ preference="calendar.view.showLocation"/>
+ <spacer/>
+ </html:fieldset>
+ </html:div>
+
+ <html:div data-category="paneCalendar">
+ <html:fieldset id="viewsMultiweekGroupbox" data-category="paneCalendar">
+ <html:legend data-l10n-id="multiweek-legend"></html:legend>
+ <hbox align="center">
+ <label data-l10n-id="number-of-weeks-label"
+ control="viewsMultiweekTotalWeeks"/>
+ <hbox>
+ <menulist id="viewsMultiweekTotalWeeks"
+ preference="calendar.weeks.inview">
+ <menupopup>
+ <menuitem data-l10n-id="week-1-label" value="1"/>
+ <menuitem data-l10n-id="week-2-label" value="2"/>
+ <menuitem data-l10n-id="week-3-label" value="3"/>
+ <menuitem data-l10n-id="week-4-label" value="4"/>
+ <menuitem data-l10n-id="week-5-label" value="5"/>
+ <menuitem data-l10n-id="week-6-label" value="6"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </hbox>
+ <hbox align="center" id="previousWeeksBox">
+ <label data-l10n-id="previous-weeks-label"
+ control="viewsMultiweekPreviousWeeks"/>
+ <hbox>
+ <menulist id="viewsMultiweekPreviousWeeks"
+ preference="calendar.previousweeks.inview">
+ <menupopup>
+ <menuitem data-l10n-id="week-0-label" value="0"/>
+ <menuitem data-l10n-id="week-1-label" value="1"/>
+ <menuitem data-l10n-id="week-2-label" value="2"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </hbox>
+ </html:fieldset>
+ </html:div>
diff --git a/comm/calendar/base/content/preferences/views.js b/comm/calendar/base/content/preferences/views.js
new file mode 100644
index 0000000000..7c1a7384a1
--- /dev/null
+++ b/comm/calendar/base/content/preferences/views.js
@@ -0,0 +1,115 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* exported gViewsPane */
+
+/* globals Preferences */
+
+Preferences.addAll([
+ { id: "calendar.week.start", type: "int" },
+ { id: "calendar.view-minimonth.showWeekNumber", type: "bool" },
+ { id: "calendar.week.d0sundaysoff", type: "bool", inverted: "true" },
+ { id: "calendar.week.d1mondaysoff", type: "bool", inverted: "true" },
+ { id: "calendar.week.d2tuesdaysoff", type: "bool", inverted: "true" },
+ { id: "calendar.week.d3wednesdaysoff", type: "bool", inverted: "true" },
+ { id: "calendar.week.d4thursdaysoff", type: "bool", inverted: "true" },
+ { id: "calendar.week.d5fridaysoff", type: "bool", inverted: "true" },
+ { id: "calendar.week.d6saturdaysoff", type: "bool", inverted: "true" },
+ { id: "calendar.view.daystarthour", type: "int" },
+ { id: "calendar.view.dayendhour", type: "int" },
+ { id: "calendar.view.visiblehours", type: "int" },
+ { id: "calendar.weeks.inview", type: "int" },
+ { id: "calendar.previousweeks.inview", type: "int" },
+ { id: "calendar.view.showLocation", type: "bool" },
+]);
+
+/**
+ * Global Object to hold methods for the views pref pane
+ */
+var gViewsPane = {
+ /**
+ * Initialize the views pref pane. Sets up dialog controls to match the
+ * values set in prefs.
+ */
+ init() {
+ this.updateViewEndMenu(Preferences.get("calendar.view.daystarthour").value);
+ this.updateViewStartMenu(Preferences.get("calendar.view.dayendhour").value);
+ this.updateViewWorkDayCheckboxes(Preferences.get("calendar.week.start").value);
+ this.initializeViewStartEndMenus();
+ },
+
+ /**
+ * Initialize the strings for the "day starts at" and "day ends at"
+ * menulists. This is needed to respect locales that use AM/PM.
+ */
+ initializeViewStartEndMenus() {
+ const { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+ let formatter = cal.dtz.formatter;
+ let calTime = cal.createDateTime();
+ calTime.minute = 0;
+
+ // 1 to 23 instead of 0 to 24 to keep midnight & noon as the localized strings
+ for (let theHour = 1; theHour <= 23; theHour++) {
+ calTime.hour = theHour;
+ let time = formatter.formatTime(calTime);
+
+ let labelIdStart = "timeStart" + theHour;
+ let labelIdEnd = "timeEnd" + theHour;
+ // This if block to keep Noon as the localized string, instead of as a number.
+ if (theHour != 12) {
+ document.getElementById(labelIdStart).setAttribute("label", time);
+ document.getElementById(labelIdEnd).setAttribute("label", time);
+ }
+ }
+ },
+
+ /**
+ * Updates the view end menu to only display hours after the selected view
+ * start.
+ *
+ * @param aStartValue The value selected for view start.
+ */
+ updateViewEndMenu(aStartValue) {
+ let endMenuKids = document.getElementById("dayendhourpopup").children;
+ for (let i = 0; i < endMenuKids.length; i++) {
+ if (Number(endMenuKids[i].value) <= Number(aStartValue)) {
+ endMenuKids[i].setAttribute("hidden", true);
+ } else {
+ endMenuKids[i].removeAttribute("hidden");
+ }
+ }
+ },
+
+ /**
+ * Updates the view start menu to only display hours before the selected view
+ * end.
+ *
+ * @param aEndValue The value selected for view end.
+ */
+ updateViewStartMenu(aEndValue) {
+ let startMenuKids = document.getElementById("daystarthourpopup").children;
+ for (let i = 0; i < startMenuKids.length; i++) {
+ if (Number(startMenuKids[i].value) >= Number(aEndValue)) {
+ startMenuKids[i].setAttribute("hidden", true);
+ } else {
+ startMenuKids[i].removeAttribute("hidden");
+ }
+ }
+ },
+
+ /**
+ * Update the workday checkboxes based on the start of the week.
+ *
+ * @Param weekStart The (0-based) index of the weekday the week
+ * should start at.
+ */
+ updateViewWorkDayCheckboxes(weekStart) {
+ weekStart = Number(weekStart);
+ for (let i = weekStart; i < weekStart + 7; i++) {
+ let checkbox = document.getElementById("dayoff" + (i % 7));
+ checkbox.parentNode.appendChild(checkbox);
+ }
+ },
+};