diff options
Diffstat (limited to '')
-rw-r--r-- | comm/calendar/base/content/calendar-tabs.js | 419 |
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; +} |