summaryrefslogtreecommitdiffstats
path: root/comm/calendar/base/content/calendar-tabs.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/calendar/base/content/calendar-tabs.js')
-rw-r--r--comm/calendar/base/content/calendar-tabs.js419
1 files changed, 419 insertions, 0 deletions
diff --git a/comm/calendar/base/content/calendar-tabs.js b/comm/calendar/base/content/calendar-tabs.js
new file mode 100644
index 0000000000..4681c5f6f9
--- /dev/null
+++ b/comm/calendar/base/content/calendar-tabs.js
@@ -0,0 +1,419 @@
+/* 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/. */
+
+/* import-globals-from item-editing/calendar-item-editing.js */
+/* import-globals-from item-editing/calendar-item-panel.js */
+/* import-globals-from calendar-command-controller.js */
+/* import-globals-from calendar-modes.js */
+/* import-globals-from calendar-views-utils.js */
+
+/* globals MozElements */
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+var calendarTabMonitor = {
+ monitorName: "calendarTabMonitor",
+
+ // Unused, but needed functions
+ onTabTitleChanged() {},
+ onTabOpened() {},
+ onTabClosing() {},
+ onTabPersist() {},
+ onTabRestored() {},
+
+ onTabSwitched(aNewTab, aOldTab) {
+ // Unfortunately, tabmail doesn't provide a hideTab function on the tab
+ // type definitions. To make sure the commands are correctly disabled,
+ // we want to update calendar/task commands when switching away from
+ // those tabs.
+ if (aOldTab?.mode.name == "calendar" || aOldTab?.mode.name == "task") {
+ calendarController.updateCommands();
+ calendarController2.updateCommands();
+ }
+ // we reset the save menu controls when moving away (includes closing)
+ // from an event or task editor tab
+ if (aNewTab.mode.name == "calendarEvent" || aNewTab.mode.name == "calendarTask") {
+ sendMessage({ command: "triggerUpdateSaveControls" });
+ } else if (window.calItemSaveControls) {
+ // we need to reset the labels of the menu controls for saving if we
+ // are not switching to an item tab and displayed an item tab before
+ let saveMenu = document.getElementById("calendar-save-menuitem");
+ let saveandcloseMenu = document.getElementById("calendar-save-and-close-menuitem");
+ saveMenu.label = window.calItemSaveControls.saveMenu.label;
+ saveandcloseMenu.label = window.calItemSaveControls.saveandcloseMenu.label;
+ }
+
+ // Change the mode (gCurrentMode) to match the new tab.
+ switch (aNewTab.mode.name) {
+ case "calendar":
+ calSwitchToCalendarMode();
+ break;
+ case "tasks":
+ calSwitchToTaskMode();
+ break;
+ case "chat":
+ case "calendarEvent":
+ case "calendarTask":
+ calSwitchToMode(aNewTab.mode.name);
+ break;
+ case "addressBookTab":
+ case "preferencesTab":
+ case "contentTab":
+ calSwitchToMode("special");
+ break;
+ default:
+ calSwitchToMode("mail");
+ break;
+ }
+ },
+};
+
+var calendarTabType = {
+ name: "calendar",
+ panelId: "calendarTabPanel",
+ modes: {
+ calendar: {
+ type: "calendar",
+ maxTabs: 1,
+ openTab(tab) {
+ tab.tabNode.setIcon("chrome://messenger/skin/icons/new/compact/calendar.svg");
+ gLastShownCalendarView.get();
+ tab.title = cal.l10n.getLtnString("tabTitleCalendar");
+ },
+ showTab(tab) {},
+ closeTab(tab) {},
+
+ persistTab(tab) {
+ let tabmail = document.getElementById("tabmail");
+ return {
+ // Since we do strange tab switching logic in calSwitchToCalendarMode,
+ // we should store the current tab state ourselves.
+ background: tab != tabmail.currentTabInfo,
+ };
+ },
+
+ restoreTab(tabmail, state) {
+ tabmail.openTab("calendar", state);
+ },
+
+ onTitleChanged(tab) {
+ tab.title = cal.l10n.getLtnString("tabTitleCalendar");
+ },
+
+ supportsCommand: (aCommand, aTab) => calendarController2.supportsCommand(aCommand),
+ isCommandEnabled: (aCommand, aTab) => calendarController2.isCommandEnabled(aCommand),
+ doCommand: (aCommand, aTab) => calendarController2.doCommand(aCommand),
+ onEvent: (aEvent, aTab) => calendarController2.onEvent(aEvent),
+ },
+
+ tasks: {
+ type: "tasks",
+ maxTabs: 1,
+ openTab(tab) {
+ tab.tabNode.setIcon("chrome://messenger/skin/icons/new/compact/tasks.svg");
+ tab.title = cal.l10n.getLtnString("tabTitleTasks");
+ },
+ showTab(tab) {},
+ closeTab(tab) {},
+
+ persistTab(tab) {
+ let tabmail = document.getElementById("tabmail");
+ return {
+ // Since we do strange tab switching logic in calSwitchToTaskMode,
+ // we should store the current tab state ourselves.
+ background: tab != tabmail.currentTabInfo,
+ };
+ },
+
+ restoreTab(tabmail, state) {
+ tabmail.openTab("tasks", state);
+ },
+
+ onTitleChanged(tab) {
+ tab.title = cal.l10n.getLtnString("tabTitleTasks");
+ },
+
+ supportsCommand: (aCommand, aTab) => calendarController2.supportsCommand(aCommand),
+ isCommandEnabled: (aCommand, aTab) => calendarController2.isCommandEnabled(aCommand),
+ doCommand: (aCommand, aTab) => calendarController2.doCommand(aCommand),
+ onEvent: (aEvent, aTab) => calendarController2.onEvent(aEvent),
+ },
+ },
+
+ saveTabState(tab) {},
+};
+
+XPCOMUtils.defineLazyGetter(calendarTabType.modes.calendar, "notificationbox", () => {
+ return new MozElements.NotificationBox(element => {
+ document.getElementById("calendar-deactivated-notification-location-events").append(element);
+ });
+});
+
+XPCOMUtils.defineLazyGetter(calendarTabType.modes.tasks, "notificationbox", () => {
+ return new MozElements.NotificationBox(element => {
+ document.getElementById("calendar-deactivated-notification-location-tasks").append(element);
+ });
+});
+
+/**
+ * For details about tab info objects and the tabmail interface see:
+ * comm/mail/base/content/mailTabs.js
+ * comm/mail/base/content/tabmail.js
+ */
+var calendarItemTabType = {
+ name: "calendarItem",
+ perTabPanel: "vbox",
+ idNumber: 0,
+ modes: {
+ calendarEvent: { type: "calendarEvent" },
+ calendarTask: { type: "calendarTask" },
+ },
+ /**
+ * Opens an event tab or a task tab.
+ *
+ * @param {object} aTab - A tab info object
+ * @param {object} aArgs - Contains data about the event/task
+ */
+ openTab(aTab, aArgs) {
+ // Create a clone to use for this tab. Remove the cloned toolbox
+ // and move the original toolbox into its place. There is only
+ // one toolbox/toolbar so its settings are the same for all item tabs.
+ let original = document.getElementById("calendarItemPanel").firstElementChild;
+ let clone = original.cloneNode(true);
+
+ clone.querySelector("toolbox").remove();
+ moveEventToolbox(clone);
+ clone.setAttribute("id", "calendarItemTab" + this.idNumber);
+
+ if (aTab.mode.type == "calendarTask") {
+ // For task tabs, css class hides event-specific toolbar buttons.
+ clone.setAttribute("class", "calendar-task-dialog-tab");
+ }
+
+ aTab.panel.setAttribute("id", "calendarItemTabWrapper" + this.idNumber);
+ aTab.panel.appendChild(clone);
+
+ // Set up the iframe and store the iframe's id. The iframe's
+ // src is set in onLoadCalendarItemPanel() that is called below.
+ aTab.iframe = aTab.panel.querySelector("iframe");
+ let iframeId = "calendarItemTabIframe" + this.idNumber;
+ aTab.iframe.setAttribute("id", iframeId);
+ gItemTabIds.push(iframeId);
+
+ // Generate and set the tab title.
+ let strName;
+ if (aTab.mode.type == "calendarEvent") {
+ strName = aArgs.calendarEvent.title ? "editEventDialog" : "newEventDialog";
+ aTab.tabNode.setIcon("chrome://messenger/skin/icons/new/compact/calendar.svg");
+ } else if (aTab.mode.type == "calendarTask") {
+ strName = aArgs.calendarEvent.title ? "editTaskDialog" : "newTaskDialog";
+ aTab.tabNode.setIcon("chrome://messenger/skin/icons/new/compact/tasks.svg");
+ } else {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+ // name is "New Event", "Edit Task", etc.
+ let name = cal.l10n.getCalString(strName);
+ aTab.title = name + ": " + (aArgs.calendarEvent.title || name);
+
+ // allowTabClose prevents the tab from being closed until we ask
+ // the user if they want to save any unsaved changes.
+ aTab.allowTabClose = false;
+
+ // Put the arguments where they can be accessed easily
+ // from the iframe. (window.arguments[0])
+ aTab.iframe.contentWindow.arguments = [aArgs];
+
+ // activate or de-activate 'Events and Tasks' menu items
+ document.commandDispatcher.updateCommands("calendar_commands");
+
+ onLoadCalendarItemPanel(iframeId, aArgs.url);
+
+ this.idNumber += 1;
+ },
+ /**
+ * Saves a tab's state when it is deactivated / hidden. The opposite of showTab.
+ *
+ * @param {object} aTab - A tab info object
+ */
+ saveTabState(aTab) {
+ // save state
+ aTab.itemTabConfig = {};
+ Object.assign(aTab.itemTabConfig, gConfig);
+
+ // clear statusbar
+ let statusbar = document.getElementById("status-bar");
+ let items = statusbar.getElementsByClassName("event-dialog");
+ for (let item of items) {
+ item.setAttribute("collapsed", true);
+ }
+ // move toolbox to the place where it can be accessed later
+ let to = document.getElementById("calendarItemPanel").firstElementChild;
+ moveEventToolbox(to);
+ },
+ /**
+ * Called when a tab is activated / shown. The opposite of saveTabState.
+ *
+ * @param {object} aTab - A tab info object
+ */
+ showTab(aTab) {
+ // move toolbox into place then load state
+ moveEventToolbox(aTab.panel.firstElementChild);
+ Object.assign(gConfig, aTab.itemTabConfig);
+ updateItemTabState(gConfig);
+
+ // activate or de-activate 'Events and Tasks' menu items
+ document.commandDispatcher.updateCommands("calendar_commands");
+ },
+ /**
+ * Called when there is a request to close a tab. Using aTab.allowTabClose
+ * we first prevent the tab from closing so we can prompt the user
+ * about saving changes, then we allow the tab to close.
+ *
+ * @param {object} aTab - A tab info object
+ */
+ tryCloseTab(aTab) {
+ if (aTab.allowTabClose) {
+ return true;
+ }
+ onCancel(aTab.iframe.id);
+ return false;
+ },
+ /**
+ * Closes a tab.
+ *
+ * @param {object} aTab - A tab info object
+ */
+ closeTab(aTab) {
+ // Remove the iframe id from the array where they are stored.
+ let index = gItemTabIds.indexOf(aTab.iframe.id);
+ if (index != -1) {
+ gItemTabIds.splice(index, 1);
+ }
+ aTab.itemTabConfig = null;
+
+ // If this is the last item tab that is closing, then delete
+ // window.calItemSaveControls, so mochitests won't complain.
+ let tabmail = document.getElementById("tabmail");
+ let calendarItemTabCount =
+ tabmail.tabModes.calendarEvent.tabs.length + tabmail.tabModes.calendarTask.tabs.length;
+ if (calendarItemTabCount == 1) {
+ delete window.calItemSaveControls;
+ }
+ },
+ /**
+ * Called when quitting the application (and/or closing the window).
+ * Saves an open tab's state to be able to restore it later.
+ *
+ * @param {object} aTab - A tab info object
+ */
+ persistTab(aTab) {
+ let args = aTab.iframe.contentWindow.arguments[0];
+ // Serialize args, with manual handling of some properties.
+ // persistTab is called even for new events/tasks in tabs that
+ // were closed and never saved (for 'undo close tab'
+ // functionality), thus we confirm we have the expected values.
+ if (
+ !args ||
+ !args.calendar ||
+ !args.calendar.id ||
+ !args.calendarEvent ||
+ !args.calendarEvent.id
+ ) {
+ return {};
+ }
+
+ let calendarId = args.calendar.id;
+ let itemId = args.calendarEvent.id;
+ // Handle null args.initialStartDateValue, just for good measure.
+ // Note that this is not the start date for the event or task.
+ let hasDateValue = args.initialStartDateValue && args.initialStartDateValue.icalString;
+ let initialStartDate = hasDateValue ? args.initialStartDateValue.icalString : null;
+
+ args.calendar = null;
+ args.calendarEvent = null;
+ args.initialStartDateValue = null;
+
+ return {
+ calendarId,
+ itemId,
+ initialStartDate,
+ args,
+ tabType: aTab.mode.type,
+ };
+ },
+ /**
+ * Called when starting the application (and/or opening the window).
+ * Restores a tab that was open when the application was quit previously.
+ *
+ * @param {object} aTabmail - The tabmail interface
+ * @param {object} aState - The state of the tab to restore
+ */
+ restoreTab(aTabmail, aState) {
+ // Sometimes restoreTab is called for tabs that were never saved
+ // and never meant to be persisted or restored. See persistTab.
+ if (aState.args && aState.calendarId && aState.itemId) {
+ aState.args.initialStartDateValue = aState.initialStartDate
+ ? cal.createDateTime(aState.initialStartDate)
+ : cal.dtz.getDefaultStartDate();
+
+ aState.args.onOk = doTransaction.bind(null, "modify");
+
+ aState.args.calendar = cal.manager.getCalendarById(aState.calendarId);
+ if (aState.args.calendar) {
+ aState.args.calendar.getItem(aState.itemId).then(item => {
+ if (item) {
+ aState.args.calendarEvent = item;
+ aTabmail.openTab(aState.tabType, aState.args);
+ }
+ });
+ }
+ }
+ },
+};
+
+window.addEventListener("load", e => {
+ let tabmail = document.getElementById("tabmail");
+ tabmail.registerTabType(calendarTabType);
+ tabmail.registerTabType(calendarItemTabType);
+ tabmail.registerTabMonitor(calendarTabMonitor);
+});
+
+/**
+ * Switch the calendar view, and optionally switch to calendar mode.
+ *
+ * @param aType The type of view to select.
+ * @param aShow If true, the mode will be switched to calendar if not
+ * already there.
+ */
+function switchCalendarView(aType, aShow) {
+ gLastShownCalendarView.set(aType);
+
+ if (aShow && gCurrentMode != "calendar") {
+ // This function in turn calls switchToView(), so return afterwards.
+ calSwitchToCalendarMode();
+ return;
+ }
+ document
+ .querySelector(`.calview-toggle-item[aria-selected="true"]`)
+ ?.setAttribute("aria-selected", false);
+ document
+ .querySelector(`.calview-toggle-item[aria-controls="${aType}-view"]`)
+ ?.setAttribute("aria-selected", true);
+ switchToView(aType);
+}
+
+/**
+ * Move the event toolbox, containing the toolbar, into view for a tab
+ * or back to its hiding place where it is accessed again for other tabs.
+ *
+ * @param {Node} aDestination - Destination where the toolbox will be moved
+ */
+function moveEventToolbox(aDestination) {
+ let toolbox = document.getElementById("event-toolbox");
+ // the <toolbarpalette> has to be copied manually
+ let palette = toolbox.palette;
+ let iframe = aDestination.querySelector("iframe");
+ aDestination.insertBefore(toolbox, iframe);
+ toolbox.palette = palette;
+}