diff options
Diffstat (limited to 'comm/calendar/base/content/calendar-chrome-startup.js')
-rw-r--r-- | comm/calendar/base/content/calendar-chrome-startup.js | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/comm/calendar/base/content/calendar-chrome-startup.js b/comm/calendar/base/content/calendar-chrome-startup.js new file mode 100644 index 0000000000..8a902923f9 --- /dev/null +++ b/comm/calendar/base/content/calendar-chrome-startup.js @@ -0,0 +1,438 @@ +/* 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 calendarOnToolbarsPopupShowing, customizeMailToolbarForTabType, + * initViewCalendarPaneMenu, loadCalendarComponent, + */ + +/* globals loadCalendarManager, injectCalendarCommandController, getViewBox, + observeViewDaySelect, getViewBox, calendarController, calendarUpdateNewItemsCommand, + TodayPane, setUpInvitationsManager, changeMode, + prepareCalendarUnifinder, taskViewOnLoad, taskEdit, tearDownInvitationsManager, + unloadCalendarManager, removeCalendarCommandController, finishCalendarUnifinder, + PanelUI, changeMenuForTask, setupDeleteMenuitem, getMinimonth, currentView, + refreshEventTree, gCurrentMode, InitMessageMenu, onViewToolbarsPopupShowing, + onCommandCustomize, CustomizeMailToolbar */ + +var { AddonManager } = ChromeUtils.importESModule("resource://gre/modules/AddonManager.sys.mjs"); +var { AppConstants } = ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs"); +var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); +var { calendarDeactivator } = ChromeUtils.import( + "resource:///modules/calendar/calCalendarDeactivator.jsm" +); + +ChromeUtils.defineModuleGetter(this, "CalMetronome", "resource:///modules/CalMetronome.jsm"); + +/** + * Does calendar initialization steps for a given chrome window. Called at + * startup as the application window is loaded, before tabs are restored. + */ +async function loadCalendarComponent() { + if (loadCalendarComponent.hasBeenCalled) { + cal.ERROR("loadCalendarComponent was called more than once for a single window"); + return; + } + loadCalendarComponent.hasBeenCalled = true; + + if (cal.manager.wrappedJSObject.mCache) { + cal.ASSERT( + [...Services.wm.getEnumerator("mail:3pane")].length > 1, + "Calendar manager initialised calendars before loadCalendarComponent ran on the first " + + "3pane window. This should not happen." + ); + } + + await uninstallLightningAddon(); + + // load locale specific default values for preferences + setLocaleDefaultPreferences(); + + // Move around toolbarbuttons and whatever is needed in the UI. + migrateCalendarUI(); + + // Load the Calendar Manager + await loadCalendarManager(); + + CalMetronome.on("day", doMidnightUpdate); + CalMetronome.on("minute", updateTimeIndicatorPosition); + + // Set up the command controller from calendar-command-controller.js + injectCalendarCommandController(); + + // Set up calendar deactivation for this window. + calendarDeactivator.registerWindow(window); + + // Set up item and day selection listeners + getViewBox().addEventListener("dayselect", observeViewDaySelect); + getViewBox().addEventListener("itemselect", calendarController.onSelectionChanged, true); + + // Start alarm service + Cc["@mozilla.org/calendar/alarm-service;1"].getService(Ci.calIAlarmService).startup(); + document.getElementById("calsidebar_splitter").addEventListener("command", () => { + window.dispatchEvent(new CustomEvent("viewresize")); + }); + document.getElementById("calendar-view-splitter").addEventListener("command", () => { + window.dispatchEvent(new CustomEvent("viewresize")); + }); + window.addEventListener("resize", event => { + if (event.target == window) { + window.dispatchEvent(new CustomEvent("viewresize")); + } + }); + + // Set calendar color CSS on this window + cal.view.colorTracker.registerWindow(window); + + /* Ensure the new items commands state can be setup properly even when no + * calendar support refreshes (i.e. the "onLoad" notification) or when none + * are active. In specific cases such as for file-based ICS calendars can + * happen, the initial "onLoad" will already have been triggered at this + * point (see bug 714431 comment 29). We thus unconditionally invoke + * calendarUpdateNewItemsCommand until somebody writes code that enables the + * checking of the calendar readiness (getProperty("ready") ?). + */ + calendarUpdateNewItemsCommand(); + + // Prepare the Today Pane, and if it is ready, display it. + await TodayPane.onLoad(); + + // Add an unload function to the window so we don't leak any listeners. + window.addEventListener("unload", unloadCalendarComponent); + + setUpInvitationsManager(); + + let filter = document.getElementById("task-tree-filtergroup"); + filter.value = filter.value || "all"; + + // Set up mode-switching menu items and mode[v]box elements for the initial mode. + // At this point no tabs have been restored, so the only reason we wouldn't be + // in "mail" mode is if a content tab has opened to display the account set-up. + let tabmail = document.getElementById("tabmail"); + if (tabmail.currentTabInfo.mode.name == "contentTab") { + changeMode("special"); + } else { + changeMode("mail"); + } + + updateTodayPaneButton(); + + prepareCalendarUnifinder(); + + taskViewOnLoad(); + taskEdit.onLoad(); + + document.getElementById("calSidebar").style.width = `${document + .getElementById("calSidebar") + .getAttribute("width")}px`; + + Services.obs.notifyObservers(window, "calendar-startup-done"); +} + +/** + * Does unload steps for a given calendar chrome window. + */ +function unloadCalendarComponent() { + tearDownInvitationsManager(); + + // Unload the calendar manager + unloadCalendarManager(); + + // Remove the command controller + removeCalendarCommandController(); + + finishCalendarUnifinder(); + + taskEdit.onUnload(); + + CalMetronome.off("minute", updateTimeIndicatorPosition); + CalMetronome.off("day", doMidnightUpdate); +} + +/** + * Uninstall the Lightning calendar addon, now that calendar is in Thunderbird. + */ +async function uninstallLightningAddon() { + try { + let addon = await AddonManager.getAddonByID("{e2fda1a4-762b-4020-b5ad-a41df1933103}"); + if (addon) { + await addon.uninstall(); + } + } catch (err) { + console.error("Error while attempting to uninstall Lightning addon:", err); + } +} +/** + * Migrate calendar UI. This function is called at each startup and can be used + * to change UI items that require js code intervention + */ +function migrateCalendarUI() { + const UI_VERSION = 3; + let currentUIVersion = Services.prefs.getIntPref("calendar.ui.version", 0); + if (currentUIVersion >= UI_VERSION) { + return; + } + + try { + if (currentUIVersion < 2) { + // If the user has customized the event/task window dialog toolbar, + // we copy that custom set of toolbar items to the event/task tab + // toolbar and add the app menu button and a spring for alignment. + let xulStore = Services.xulStore; + let uri = "chrome://calendar/content/calendar-event-dialog.xhtml"; + + if (xulStore.hasValue(uri, "event-toolbar", "currentset")) { + let windowSet = xulStore.getValue(uri, "event-toolbar", "currentset"); + let items = ""; + if (!windowSet.includes("spring")) { + items = "spring"; + } + let previousSet = windowSet == "__empty" ? "" : windowSet + ","; + let tabSet = previousSet + items; + let tabBar = document.getElementById("event-tab-toolbar"); + + tabBar.currentSet = tabSet; + // For some reason we also have to do the following, + // presumably because the toolbar has already been + // loaded into the DOM so the toolbar's currentset + // attribute does not yet match the new currentSet. + tabBar.setAttribute("currentset", tabSet); + } + } + if (currentUIVersion < 3) { + // Rename toolbar button id "button-save" to + // "button-saveandclose" in customized toolbars + let xulStore = Services.xulStore; + let windowUri = "chrome://calendar/content/calendar-event-dialog.xhtml"; + let tabUri = "chrome://messenger/content/messenger.xhtml"; + + if (xulStore.hasValue(windowUri, "event-toolbar", "currentset")) { + let windowSet = xulStore.getValue(windowUri, "event-toolbar", "currentset"); + let newSet = windowSet.replace("button-save", "button-saveandclose"); + xulStore.setValue(windowUri, "event-toolbar", "currentset", newSet); + } + if (xulStore.hasValue(tabUri, "event-tab-toolbar", "currentset")) { + let tabSet = xulStore.getValue(tabUri, "event-tab-toolbar", "currentset"); + let newSet = tabSet.replace("button-save", "button-saveandclose"); + xulStore.setValue(tabUri, "event-tab-toolbar", "currentset", newSet); + + let tabBar = document.getElementById("event-tab-toolbar"); + tabBar.currentSet = newSet; + tabBar.setAttribute("currentset", newSet); + } + } + Services.prefs.setIntPref("calendar.ui.version", UI_VERSION); + } catch (e) { + cal.ERROR("Error upgrading UI from " + currentUIVersion + " to " + UI_VERSION + ": " + e); + } +} + +function setLocaleDefaultPreferences() { + function setDefaultLocaleValue(aName) { + // Shift encoded days from 1=Monday ... 7=Sunday to 0=Sunday ... 6=Saturday + let startDefault = calendarInfo.firstDayOfWeek % 7; + + if (aName == "calendar.categories.names" && defaultBranch.getStringPref(aName) == "") { + cal.category.setupDefaultCategories(); + } else if (aName == "calendar.week.start" && defaultBranch.getIntPref(aName) != startDefault) { + defaultBranch.setIntPref(aName, startDefault); + } else if (aName.startsWith("calendar.week.d")) { + let dayNumber = parseInt(aName[15], 10); + if (dayNumber == 0) { + dayNumber = 7; + } + defaultBranch.setBoolPref(aName, calendarInfo.weekend.includes(dayNumber)); + } + } + + cal.LOG("Start loading of locale dependent preference default values..."); + + let defaultBranch = Services.prefs.getDefaultBranch(""); + let calendarInfo = cal.l10n.calendarInfo(); + + let prefDefaults = [ + "calendar.week.start", + "calendar.week.d0sundaysoff", + "calendar.week.d1mondaysoff", + "calendar.week.d2tuesdaysoff", + "calendar.week.d3wednesdaysoff", + "calendar.week.d4thursdaysoff", + "calendar.week.d5fridaysoff", + "calendar.week.d6saturdaysoff", + "calendar.categories.names", + ]; + for (let prefDefault of prefDefaults) { + setDefaultLocaleValue(prefDefault); + } + + cal.LOG("Loading of locale sensitive preference default values completed."); +} + +/** + * Called at midnight to tell us to redraw date-specific widgets. + */ +function doMidnightUpdate() { + try { + getMinimonth().refreshDisplay(); + + // Refresh the current view and just allow the refresh for the others + // views when will be displayed. + let currView = currentView(); + currView.goToDay(); + let views = ["day-view", "week-view", "multiweek-view", "month-view"]; + for (let view of views) { + if (view != currView.id) { + document.getElementById(view).mToggleStatus = -1; + } + } + + if (!TodayPane.showsToday()) { + TodayPane.setDay(cal.dtz.now()); + } + + // Update the unifinder. + refreshEventTree(); + + // Update today's date on todaypane button. + updateTodayPaneButtonDate(); + } catch (exc) { + cal.ASSERT(false, exc); + } +} + +/** + * Update the position of the current view's indicator of the current time, if + * any. + */ +function updateTimeIndicatorPosition() { + const view = currentView(); + if (!view?.isInitialized) { + // Ensure that we don't attempt to update a view that isn't ready. Calendar + // chrome is always loaded at startup, but the view isn't initialized until + // the user switches to the calendar tab. + return; + } + + view.updateTimeIndicatorPosition(); +} + +/** + * Updates button structure to enable images on both sides of the label. + */ +function updateTodayPaneButton() { + let todaypane = document.getElementById("calendar-status-todaypane-button"); + + let iconStack = document.createXULElement("stack"); + iconStack.setAttribute("pack", "center"); + iconStack.setAttribute("align", "end"); + + let iconBegin = document.createElement("img"); + iconBegin.setAttribute("alt", ""); + iconBegin.setAttribute("src", "chrome://messenger/skin/icons/new/calendar-empty.svg"); + iconBegin.classList.add("toolbarbutton-icon-begin"); + + let iconLabel = document.createXULElement("label"); + iconLabel.classList.add("toolbarbutton-day-text"); + + let dayNumber = cal.l10n.getDateFmtString(`day.${cal.dtz.now().day}.number`); + iconLabel.textContent = dayNumber; + + iconStack.appendChild(iconBegin); + iconStack.appendChild(iconLabel); + + let iconEnd = document.createElement("img"); + iconEnd.setAttribute("alt", ""); + iconEnd.setAttribute("src", "chrome://messenger/skin/icons/new/nav-up-sm.svg"); + iconEnd.classList.add("toolbarbutton-icon-end"); + + let oldImage = todaypane.querySelector(".toolbarbutton-icon"); + todaypane.replaceChild(iconStack, oldImage); + todaypane.appendChild(iconEnd); + + let calSidebar = document.getElementById("calSidebar"); + todaypane.setAttribute("checked", !calSidebar.collapsed); +} + +/** + * Updates the date number in the calendar icon of the todaypane button. + */ +function updateTodayPaneButtonDate() { + let todaypane = document.getElementById("calendar-status-todaypane-button"); + + let dayNumber = cal.l10n.getDateFmtString(`day.${cal.dtz.now().day}.number`); + todaypane.querySelector(".toolbarbutton-day-text").textContent = dayNumber; +} + +/** + * Get the toolbox id for the current tab type. + * + * @returns {string} A toolbox id. + */ +function getToolboxIdForCurrentTabType() { + // A mapping from calendar tab types to toolbox ids. + const calendarToolboxIds = { + calendar: null, + tasks: null, + calendarEvent: "event-toolbox", + calendarTask: "event-toolbox", + }; + let tabmail = document.getElementById("tabmail"); + if (!tabmail) { + return "mail-toolbox"; // Standalone message window. + } + let tabType = tabmail.currentTabInfo.mode.type; + + return calendarToolboxIds[tabType] || null; +} + +/** + * Modify the contents of the "Toolbars" context menu for the current + * tab type. Menu items are inserted before (appear above) aInsertPoint. + * + * @param {MouseEvent} aEvent - The popupshowing event + * @param {nsIDOMXULElement} aInsertPoint - (optional) menuitem node + */ +function calendarOnToolbarsPopupShowing(aEvent, aInsertPoint) { + if (onViewToolbarsPopupShowing.length < 3) { + // SeaMonkey + onViewToolbarsPopupShowing(aEvent); + return; + } + + let toolboxes = ["navigation-toolbox"]; + let toolboxId = getToolboxIdForCurrentTabType(); + + if (toolboxId) { + toolboxes.push(toolboxId); + } + + onViewToolbarsPopupShowing(aEvent, toolboxes, aInsertPoint); +} + +/** + * Open the customize dialog for the toolbar for the current tab type. + */ +function customizeMailToolbarForTabType() { + let toolboxId = getToolboxIdForCurrentTabType(); + if (!toolboxId) { + return; + } + if (toolboxId == "event-toolbox") { + onCommandCustomize(); + } else { + CustomizeMailToolbar(toolboxId, "CustomizeMailToolbar"); + } +} + +/** + * Initialize the calendar sidebar menu state. + */ +function initViewCalendarPaneMenu() { + let calSidebar = document.getElementById("calSidebar"); + + document.getElementById("calViewCalendarPane").setAttribute("checked", !calSidebar.collapsed); + + if (document.getElementById("appmenu_calViewCalendarPane")) { + document.getElementById("appmenu_calViewCalendarPane").checked = !calSidebar.collapsed; + } +} |