1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is Lightning code.
15 *
16 * The Initial Developer of the Original Code is Oracle Corporation
17 * Portions created by the Initial Developer are Copyright (C) 2005
18 * the Initial Developer. All Rights Reserved.
19 *
20 * Contributor(s):
21 * Mike Shaver <shaver@mozilla.org>
22 * Vladimir Vukicevic <vladimir@pobox.com>
23 * Stuart Parmenter <stuart.parmenter@oracle.com>
24 * Dan Mosedale <dmose@mozilla.org>
25 * Joey Minta <jminta@gmail.com>
26 * Simon Paquet <bugzilla@babylonsounds.com>
27 * Stefan Sitter <ssitter@googlemail.com>
28 * Thomas Benisch <thomas.benisch@sun.com>
29 * Michael Buettner <michael.buettner@sun.com>
30 * Philipp Kewisch <mozilla@kewis.ch>
31 * Berend Cornelius <berend.cornelius@sun.com>
32 *
33 * Alternatively, the contents of this file may be used under the terms of
34 * either the GNU General Public License Version 2 or later (the "GPL"), or
35 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
36 * in which case the provisions of the GPL or the LGPL are applicable instead
37 * of those above. If you wish to allow use of your version of this file only
38 * under the terms of either the GPL or the LGPL, and not to allow others to
39 * use your version of this file under the terms of the MPL, indicate your
40 * decision by deleting the provisions above and replace them with the notice
41 * and other provisions required by the GPL or the LGPL. If you do not delete
42 * the provisions above, a recipient may use your version of this file under
43 * the terms of any one of the MPL, the GPL or the LGPL.
44 *
45 * ***** END LICENSE BLOCK ***** */
46
47 var gLastShownCalendarView = null;
48
49 function yesterday()
50 {
51 var d = now();
52 d.day--;
53 return d;
54 }
55
56 function nextMonth(dt)
57 {
58 var d = new Date(dt);
59 d.setDate(1); // make sure we avoid "June 31" when we bump the month
60
61 var mo = d.getMonth();
62 if (mo == 11) {
63 d.setMonth(0);
64 d.setYear(d.getYear() + 1);
65 } else {
66 d.setMonth(mo + 1);
67 }
68
69 return d;
70 }
71
72 var gMiniMonthLoading = false;
73 function ltnMinimonthPick(minimonth)
74 {
75 if (gMiniMonthLoading)
76 return;
77
78 var jsDate = minimonth.value;
79 document.getElementById("ltnDateTextPicker").value = jsDate;
80 var cdt = jsDateToDateTime(jsDate);
81
82 if (document.getElementById("displayDeck").selectedPanel !=
83 document.getElementById("calendar-view-box")) {
84 ltnShowCalendarView(gLastShownCalendarView);
85 }
86
87 cdt = cdt.getInTimezone(currentView().timezone);
88 cdt.isDate = true;
89 currentView().goToDay(cdt);
90 }
91
92 function ltnGoToDate()
93 {
94 var goToDate = document.getElementById("ltnDateTextPicker");
95 if (goToDate.value) {
96 ltnMinimonthPick(goToDate);
97 }
98 }
99
100 function ltnOnLoad(event)
101 {
102 // Load the Calendar Manager
103 loadCalendarManager();
104
105 // take the existing folderPaneBox (that's what thunderbird displays
106 // at the left side of the application window) and stuff that inside
107 // of the deck we're introducing with the contentPanel. this essentially
108 // rearranges the DOM tree and allows us to switch between content that
109 // lives inside of the left pane.
110 var folderPaneBox = document.getElementById("folderPaneBox");
111 var contentPanel = document.getElementById("contentPanel");
112 contentPanel.insertBefore(folderPaneBox, contentPanel.firstChild);
113
114 // we're taking care of the mode toolbar (that's the small toolbar on
115 // the lower left with the 'mail', 'calendar', 'task' buttons on it).
116 // since we want to have this particular toolbar displayed at the
117 // top or bottom inside the folderPaneBox (basically on top or bottom
118 // of the window) we need to go to great length in order to get this
119 // damned thing working. i decided to dynamically place the toolbox
120 // inside the DOM tree, that appears to be the most clean solution to
121 // the problem. unfortunately, it didn't work out that easy. as soon
122 // as we call insertBefore() to place the node somewhere different, the
123 // constructor of the appropriate binding gets called again. this has
124 // the nasty side-effect that the toolbar get a bit confused, since it
125 // thinks it is customized, which just isn't the case. that's why we need
126 // to carry some internal toolbar properties over. and that's what those
127 // functions retrieveToolbarProperties() and restoreToolbarProperties()
128 // are all about.
129 var retrieveToolbarProperties = function(toolbox)
130 {
131 var toolbars = {};
132 var toolbar = toolbox.firstChild;
133 while (toolbar) {
134 if (toolbar.localName == "toolbar") {
135 if (toolbar.getAttribute("customizable") == "true") {
136 if (!toolbar.hasAttribute("customindex")) {
137 var propertybag = {};
138 propertybag.firstPermanentChild = toolbar.firstPermanentChild;
139 propertybag.lastPermanentChild = toolbar.lastPermanentChild;
140 toolbars[toolbar.id] = propertybag;
141 }
142 }
143 }
144 toolbar = toolbar.nextSibling;
145 }
146 return toolbars;
147 }
148
149 var restoreToolbarProperties = function(toolbox,toolbars)
150 {
151 var toolbar = toolbox.firstChild;
152 while (toolbar) {
153 if (toolbar.localName == "toolbar") {
154 if (toolbar.getAttribute("customizable") == "true") {
155 if (!toolbar.hasAttribute("customindex")) {
156 var propertybag = toolbars[toolbar.id];
157 toolbar.firstPermanentChild = propertybag.firstPermanentChild;
158 toolbar.lastPermanentChild = propertybag.lastPermanentChild;
159 }
160 }
161 }
162 toolbar = toolbar.nextSibling;
163 }
164 }
165
166 // DOMAttrModified handler that listens on the toolbox element
167 var onModified = function(aEvent) {
168 if(aEvent.attrName == "location") {
169 var modeToolbox = document.getElementById("mode-toolbox");
170 modeToolbox.removeEventListener("DOMAttrModified", onModified, false);
171 var onLocationHandler = function() {
172 var contentPanel = document.getElementById("contentPanel");
173 var palette = modeToolbox.palette;
174 var bag = retrieveToolbarProperties(modeToolbox);
175 if(aEvent.newValue == "top" && !aEvent.prevValue || aEvent.prevValue == "bottom") {
176 // place the mode toolbox at the top of the left pane
177 modeToolbox = contentPanel.parentNode.insertBefore(modeToolbox, contentPanel);
178 } else if(aEvent.newValue == "bottom" && aEvent.prevValue == "top") {
179 // place the mode toolbox at the bottom of the left pane
180 modeToolbox = contentPanel.parentNode.appendChild(modeToolbox);
181 }
182 restoreToolbarProperties(modeToolbox,bag);
183 modeToolbox.addEventListener("DOMAttrModified", onModified, false);
184 modeToolbox.palette = palette;
185 }
186
187 setTimeout(onLocationHandler,1);
188 }
189 }
190
191 // install the handler that listens for modified 'location' attribute
192 // on the toolbox. the value is changed by the toolbar customize dialog.
193 var modeToolbox = document.getElementById("mode-toolbox");
194 if(modeToolbox.getAttribute("location") != "bottom") {
195 var palette = modeToolbox.palette;
196 var bag = retrieveToolbarProperties(modeToolbox);
197 modeToolbox = contentPanel.parentNode.insertBefore(modeToolbox, contentPanel);
198 modeToolbox.palette = palette;
199 restoreToolbarProperties(modeToolbox,bag);
200 }
201 modeToolbox.addEventListener("DOMAttrModified", onModified, false);
202
203 // To make sure the folder pane doesn't disappear without any possibility
204 // to get it back, we need to reveal it when the mode box is collapsed.
205 function modeBoxAttrModified(event) {
206 if (event.attrName == "collapsed") {
207 document.getElementById("folderPaneBox")
208 .removeAttribute("collapsed");
209 }
210 }
211
212 document.getElementById("ltnModeBox").addEventListener("DOMAttrModified",
213 modeBoxAttrModified,
214 true);
215
216 // Set up the views
217 initializeViews();
218
219 // Initialize Minimonth
220 gMiniMonthLoading = true;
221
222 var today = new Date();
223 var nextmo = nextMonth(today);
224
225 document.getElementById("ltnMinimonth").value = today;
226 document.getElementById("ltnDateTextPicker").value = today;
227
228 gMiniMonthLoading = false;
229
230 // nuke the onload, or we get called every time there's
231 // any load that occurs
232 document.removeEventListener("load", ltnOnLoad, true);
233
234 // Hide the calendar view so it doesn't push the status-bar offscreen
235 collapseElement(document.getElementById("calendar-view-box"));
236
237 // fire up the alarm service
238 var alarmSvc = Components.classes["@mozilla.org/calendar/alarm-service;1"]
239 .getService(Components.interfaces.calIAlarmService);
240 alarmSvc.timezone = calendarDefaultTimezone();
241 alarmSvc.startup();
242
243 // Add an unload function to the window so we don't leak any listeners
244 document.getElementById("messengerWindow")
245 .addEventListener("unload", ltnFinish, false);
246
247 document.getElementById("displayDeck")
248 .addEventListener("dayselect", observeViewDaySelect, false);
249
250 prepareCalendarToDoUnifinder();
251
252 // Make sure we update ourselves if the program stays open over midnight
253 scheduleMidnightUpdate(refreshUIBits);
254
255 scheduleInvitationsUpdate(FIRST_DELAY_STARTUP, REPEAT_DELAY);
256 getCalendarManager().addObserver(gInvitationsCalendarManagerObserver);
257
258 // Set up the command controller from calendar-common-sets.js
259 injectCalendarCommandController();
260
261 getViewDeck().addEventListener("itemselect", onSelectionChanged, true);
262
263 var filter = document.getElementById("task-tree-filter");
264 filter.value = filter.value || "all";
265 }
266
267 function onSelectionChanged(aEvent) {
268 var selectedItems = aEvent.detail;
269
270 calendarController.item_selected = (selectedItems.length > 0);
271 var selected_events_readonly = 0;
272 var selected_events_requires_network = 0;
273
274 for each (var item in selectedItems) {
275 if (item.calendar.readOnly) {
276 selected_events_readonly++;
277 }
278 if (item.calendar.getProperty("requiresNetwork") !== false) {
279 selected_events_requires_network++;
280 }
281 }
282
283 calendarController.selected_events_readonly =
284 (selected_events_readonly == selectedItems.length);
285
286 calendarController.selected_events_requires_network =
287 (selected_events_requires_network == selectedItems.length);
288
289 document.commandDispatcher.updateCommands('mail-toolbar');
290 document.commandDispatcher.updateCommands('calendar_commands');
291 }
292
293 /* Called at midnight to tell us to redraw date-specific widgets. Do NOT call
294 * this for normal refresh, since it also calls scheduleMidnightRefresh.
295 */
296 function refreshUIBits() {
297 document.getElementById("ltnMinimonth").refreshDisplay();
298
299 // refresh the current view, if it has ever been shown
300 var cView = currentView();
301 if (cView.initialized) {
302 cView.goToDay(cView.selectedDay);
303 }
304
305 if (TodayPane.showsYesterday()) {
306 TodayPane.setDay(now());
307 }
308
309 // update the unifinder
310 refreshEventTree();
311
312 // schedule our next update...
313 scheduleMidnightUpdate(refreshUIBits);
314 }
315
316 /**
317 * Select the calendar view in the background, not switching to calendar mode if
318 * in mail mode.
319 */
320 function ltnSelectCalendarView(type) {
321 gLastShownCalendarView = type;
322
323 // Sunbird/Lightning Common view switching code
324 switchToView(type);
325
326 // Set the labels for the context-menu
327 var nextCommand = document.getElementById("context_next");
328 nextCommand.setAttribute("label", nextCommand.getAttribute("label-"+type));
329 var previousCommand = document.getElementById("context_previous")
330 previousCommand.setAttribute("label", previousCommand.getAttribute("label-"+type));
331
332 }
333
334 function toggleControlDisplay(aCommandId, aControlId) {
335 var control = document.getElementById(aControlId);
336 var command = document.getElementById(aCommandId);
337 if (control.getAttribute("collapsedinMode") == "false") {
338 if (control.hasAttribute("collapsed")) {
339 control.removeAttribute("collapsed");
340 command.setAttribute("checked", "true");
341 return;
342 }
343 }
344 command.setAttribute("checked", "false");
345 }
346
347 function toggleControlinMode(aCommandId, aControlId) {
348 var control = document.getElementById(aControlId);
349 var command = document.getElementById(aCommandId);
350 if (control.hasAttribute("collapsed")) {
351 control.removeAttribute("collapsed");
352 control.setAttribute("collapsedinMode", "false");
353 command.setAttribute("checked","true");
354 }
355 else {
356 control.setAttribute("collapsed", "true");
357 control.setAttribute("collapsedinMode", "true");
358 command.setAttribute("checked", "false");
359 }
360 }
361
362 function toggleToolbar(aCommandId, aToolbarId) {
363 var toolBar = document.getElementById(aToolbarId);
364 var command = document.getElementById(aCommandId);
365 if (toolBar.hasAttribute("collapsed")) {
366 toolBar.removeAttribute("collapsed");
367 command.setAttribute("checked", "true");
368 }
369 else {
370 toolBar.setAttribute("collapsed", "true");
371 command.setAttribute("checked", "false");
372 }
373 }
374
375
376
377 /**
378 * Show the calendar view, also switching to calendar mode if in mail mode
379 */
380 function ltnShowCalendarView(type)
381 {
382 gLastShownCalendarView = type;
383
384 if (gCurrentMode != 'calendar') {
385 // This function in turn calls showCalendarView(), so return afterwards.
386 ltnSwitch2Calendar();
387 return;
388 }
389
390 ltnSelectCalendarView(type);
391 }
392
393 function toggleTodayPaneinMailMode()
394 {
395 var oTodayPane = document.getElementById("today-pane-panel");
396 var todayPaneCommand = document.getElementById('cmd_toggleTodayPane');
397 if (oTodayPane.hasAttribute("collapsed")) {
398 oTodayPane.removeAttribute("collapsed");
399 oTodayPane.removeAttribute("collapsedinMailMode");
400 todayPaneCommand.setAttribute("checked","true");
401 document.getElementById("today-closer").setAttribute("checked", "false");
402 }
403 else {
404 oTodayPane.setAttribute("collapsed", true);
405 oTodayPane.setAttribute("collapsedinMailMode", "true");
406 todayPaneCommand.setAttribute("checked", "false");
407 }
408 }
409
410
411 /**
412 * This function has the sole responsibility to switch back to
413 * mail mode (by calling ltnSwitch2Mail()) if we are getting
414 * notifications from other panels (besides the calendar views)
415 * but find out that we're not in mail mode. This situation can
416 * for example happen if we're in calendar mode but the 'new mail'
417 * slider gets clicked and wants to display the appropriate mail.
418 * All necessary logic for switching between the different modes
419 * should live inside of the corresponding functions:
420 * - ltnSwitch2Mail()
421 * - ltnSwitch2Calendar()
422 * - ltnSwitch2Task()
423 */
424 function LtnObserveDisplayDeckChange(event) {
425 var deck = event.target;
426
427 // Bug 309505: The 'select' event also fires when we change the selected
428 // panel of calendar-view-box. Workaround with this check.
429 if (deck.id != "displayDeck") {
430 return;
431 }
432
433 var id = null;
434 try { id = deck.selectedPanel.id } catch (e) { }
435
436 // Switch back to mail mode in case we find that this
437 // notification has been fired but we're still in calendar or task mode.
438 // Specifically, switch back if we're *not* in mail mode but the notification
439 // did *not* come from either the "calendar-view-box" or the "calendar-task-box".
440 if (gCurrentMode != 'mail') {
441 if (id != "calendar-view-box" && id != "calendar-task-box") {
442 ltnSwitch2Mail();
443 }
444 }
445 }
446
447 function ltnFinish() {
448 getCalendarManager().removeObserver(gInvitationsCalendarManagerObserver);
449 getCompositeCalendar().removeObserver(agendaTreeView.calendarObserver);
450
451 unloadCalendarManager();
452
453 removeCalendarCommandController();
454 }
455
456 // After 1.5 was released, the search box was moved into an optional toolbar
457 // item, with a different ID. This function keeps us compatible with both.
458 function findMailSearchBox() {
459 var tb15Box = document.getElementById("searchBox");
460 if (tb15Box) {
461 return tb15Box;
462 }
463
464 var tb2Box = document.getElementById("searchInput");
465 if (tb2Box) {
466 return tb2Box;
467 }
468
469 // In later versions, it's possible that a user removed the search box from
470 // the toolbar.
471 return null;
472 }
473
474 var gSelectFolder = SelectFolder;
475 var gSelectMessage = SelectMessage;
476
477 SelectFolder = function(folderUri) {
478 document.getElementById("switch2mail").doCommand();
479 gSelectFolder(folderUri);
480 }
481
482 var calendarpopuplist = new Array();
483 var taskpopuplist = new Array();
484 var mailpopuplist = new Array();
485 var menulist = new Array();
486
487 function ltnInitializeMenus(){
488 copyPopupMenus();
489 ltnRemoveMailOnlyItems(calendarpopuplist, "calendar");
490 ltnRemoveMailOnlyItems(taskpopuplist, "task");
491 var modeToolbar = document.getElementById("mode-toolbar");
492 var visible = !modeToolbar.hasAttribute("collapsed");
493 document.getElementById("modeBroadcaster").setAttribute("checked", visible);
494 document.getElementById("calendar-toolbar").setAttribute("collapsed", gCurrentMode!="calendar");
495 document.getElementById("task-toolbar").setAttribute("collapsed", gCurrentMode!="task");
496
497 var taskProgressButton = document.getElementById("task-progress-button");
498 if (taskProgressButton) {
499 taskProgressButton.appendChild(clonePopupMenu("calendar.context.progress-menu",
500 "toolbar-progress-menu",
501 "tb-"));
502 }
503
504 var taskPriorityButton = document.getElementById("task-priority-button");
505 if (taskPriorityButton) {
506 taskPriorityButton.appendChild(clonePopupMenu("calendar.context.priority-menu",
507 "toolbar-priority-menu",
508 "tb-"));
509 }
510 }
511
512 function getMenuElementById(aElementId, aMenuPopup) {
513 var element = null;
514 var elements = aMenuPopup.getElementsByAttribute("id", aElementId);
515 if (elements.length > 0) {
516 element = elements[0];
517 }
518 return element;
519 }
520
521 /** removes all succeedingmenu elements of a container up to the next
522 * menuseparator that thus denotes the end of the section. Elements with the
523 * attribute mode == 'calendar' are ignored
524 */
525 function removeMenuElementsInSection(aElement, aExcludeMode) {
526 var element = aElement;
527 if (element) {
528 var bleaveloop = false;
529 while (!bleaveloop) {
530 var ignore = false;
531 bleaveloop = element.localName == "menuseparator";
532 if (bleaveloop) {
533 // we delete the menuseparator only if it's the last element
534 // within its container
535 bleaveloop = (element.nextSibling != null);
536 }
537 if (element.hasAttribute("mode")) {
538 ignore = element.getAttribute("mode") == aExcludeMode ||
539 element.getAttribute("mode") == "calendar,task";
540 }
541 var nextMenuElement = element.nextSibling;
542 if (!ignore) {
543 try {
544 element.parentNode.removeChild(element);
545 } catch (e) {
546 dump("Element '" + element.getAttribute("id") + "' could not be removed\n");
547 }
548 }
549 if (!bleaveloop) {
550 element = nextMenuElement;
551 bleaveloop = (element == null);
552 }
553 }
554 }
555 }
556
557 function removeElements(aElementList) {
558 aElementList.forEach(function(element) {
559 try {
560 if (element) {
561 element.parentNode.removeChild(element);
562 }
563 } catch (e) {
564 dump("Element '" + element.getAttribute("id") + "' could not be removed\n");
565 }
566 });
567 }
568
569 function addToPopupList(aMenuElement, aNewPopupMenu, aPopupList, aExcludedModes, aClone, aRemovePopupShowing) {
570 var child = aMenuElement.firstChild;
571 if (child) {
572 if (child.localName == "menupopup") {
573 if (aNewPopupMenu) {
574 var newPopupMenu = aNewPopupMenu;
575 } else {
576 var newPopupMenu = child;
577 }
578 if (aClone) {
579 newPopupMenu = newPopupMenu.cloneNode(true);
580 if (aRemovePopupShowing) {
581 newPopupMenu.removeAttribute("onpopupshowing");
582 }
583 }
584 removeMenuElements(newPopupMenu, aExcludedModes);
585 aPopupList.push(newPopupMenu);
586 }
587 }
588 }
589
590 function copyPopupMenus() {
591 // define menuList...
592 menulist.push(document.getElementById("menu_File"));
593 menulist.push(document.getElementById("menu_Edit"));
594 var menuView = document.getElementById("menu_View");
595 menulist.push(menuView);
596 menulist.push(menuView.nextSibling); // id-less menu_Go
597 menulist.push(document.getElementById("messageMenu"));
598 menulist.push(document.getElementById("tasksMenu"));
599
600 // define PopupMenus for calendar mode...
601 var excludeList = new Array("task");
602 addToPopupList(menulist[0], null, calendarpopuplist, excludeList, true, true);
603 addToPopupList(menulist[1], null, calendarpopuplist, excludeList, true, false);
604 addToPopupList(menulist[2], null, calendarpopuplist, excludeList, true, true);
605 addToPopupList(menulist[3], document.getElementById("calendar-GoPopupMenu"), calendarpopuplist, excludeList, true, false);
606 addToPopupList(menulist[4], document.getElementById("calendarCalendarPopupMenu"), calendarpopuplist, excludeList, true, false);
607 addToPopupList(menulist[5], null, calendarpopuplist, excludeList, true, false);
608
609 // define PopupMenus for task mode...
610 var excludeList = new Array("calendar");
611 addToPopupList(menulist[0], null, taskpopuplist, excludeList, true, true);
612 addToPopupList(menulist[1], null, taskpopuplist, excludeList, true, false);
613 addToPopupList(menulist[2], null, taskpopuplist, excludeList, true, true);
614 addToPopupList(menulist[3], document.getElementById("calendar-GoPopupMenu"), taskpopuplist, excludeList, true, false);
615 var tasksViewMenuPopup = clonePopupMenu("taskitem-context-menu", "taskitem-menu", "menu-");
616 tasksViewMenuPopup.removeChild(getMenuElementById("menu-" + "task-context-menu-modify", tasksViewMenuPopup));
617 tasksViewMenuPopup.removeChild(getMenuElementById("menu-" + "task-context-menu-delete", tasksViewMenuPopup));
618 addToPopupList(menulist[4], tasksViewMenuPopup, taskpopuplist, excludeList, false, false);
619 addToPopupList(menulist[5], null, taskpopuplist, excludeList, true, true);
620
621 // define PopupMenus for mail mode...
622 var excludeList = new Array("calendar", "task", "calendar,task");
623 addToPopupList(menulist[0], null, mailpopuplist, excludeList, false, false);
624 addToPopupList(menulist[1], null, mailpopuplist, excludeList, false, false);
625 addToPopupList(menulist[2], null, mailpopuplist, excludeList, false, false);
626 // copy calendar-GoPopupMenu into Thunderbird's GoPopupMenu to switch modes
627 var tbGoPopupMenu = menulist[3].lastChild;
628 var calGoPopupMenu = document.getElementById("calendar-GoPopupMenu").cloneNode(true);
629 var calGoItem;
630 while ((calGoItem = calGoPopupMenu.firstChild)) {
631 tbGoPopupMenu.appendChild(calGoPopupMenu.removeChild(calGoItem));
632 }
633 addToPopupList(menulist[3], null, mailpopuplist, excludeList, false, false);
634 addToPopupList(menulist[4], null, mailpopuplist, excludeList, false, false);
635 addToPopupList(menulist[5], null, mailpopuplist, excludeList, false, false);
636 }
637
638 function removeNeedlessSeparators(aMenuPopupList) {
639 aMenuPopupList.forEach(function(aMenuPopup) {
640 var child = aMenuPopup.firstChild;
641 if (child) {
642 if (child.localName == "menuseparator") {
643 try {
644 aMenuPopup.removeChild(child)
645 } catch (e) {
646 dump("Element '" + child.getAttribute("id") + "' could not be removed\n");
647 }
648 }
649 }
650 child = aMenuPopup.lastChild;
651 if (child) {
652 if (child.localName == "menuseparator") {
653 try {
654 aMenuPopup.removeChild(child)
655 } catch (e) {
656 dump("Element '" + child.getAttribute("id") + "' could not be removed\n");
657 }
658 }
659 }
660 });
661 }
662
663 function ltnRemoveMailOnlyItems(aMenuPopupList, aExcludeMode) {
664 removeElements(
665 // "File" - menu
666 [getMenuElementById("openMessageFileMenuitem", aMenuPopupList[0]),
667 getMenuElementById("newAccountMenuItem", aMenuPopupList[0]),
668 getMenuElementById("fileAttachmentMenu", aMenuPopupList[0]),
669 getAdjacentSibling(getMenuElementById("menu_saveAs", aMenuPopupList[0]), 2),
670
671 // "Edit" - menu
672 getMenuElementById("menu_find", aMenuPopupList[1]),
673 getMenuElementById("menu_favoriteFolder", aMenuPopupList[1]),
674 getMenuElementById("menu_properties", aMenuPopupList[1]),
675 getMenuElementById("menu_accountmgr", aMenuPopupList[1]),
676
677 // "View"-menu
678 getMenuElementById("menu_showMessengerToolbar", aMenuPopupList[2]),
679
680 // "Tools"-menu
681 getMenuElementById("tasksMenuMail", aMenuPopupList[5]),
682 getMenuElementById("menu_import", aMenuPopupList[5])]);
683
684 removeNeedlessSeparators(aMenuPopupList);
685
686 // "File" - menu
687 [getMenuElementById("menu_newFolder", aMenuPopupList[0]),
688 getMenuElementById("menu_saveAs", aMenuPopupList[0]),
689 getMenuElementById("menu_getnextnmsg", aMenuPopupList[0]),
690 getMenuElementById("menu_renameFolder", aMenuPopupList[0]),
691 // getMenuElementById("offlineMenuItem", aMenuPopupList[0]),
692
693 // "Edit" - menu
694 getMenuElementById("menu_delete", aMenuPopupList[1]),
695 getMenuElementById("menu_select", aMenuPopupList[1]),
696
697 // "View"-menu
698 getMenuElementById("menu_MessagePaneLayout", aMenuPopupList[2]),
699 getMenuElementById("viewSortMenu", aMenuPopupList[2]),
700 getMenuElementById("viewheadersmenu", aMenuPopupList[2]),
701 getMenuElementById("viewTextSizeMenu", aMenuPopupList[2]),
702 getMenuElementById("pageSourceMenuItem", aMenuPopupList[2]),
703
704 // "Tools"-menu
705 getMenuElementById("filtersCmd", aMenuPopupList[5]),
706 getMenuElementById("runJunkControls", aMenuPopupList[5])].forEach(function(element){
707 removeMenuElementsInSection(element, aExcludeMode);
708 });
709 }
710
711 function swapPopupMenus() {
712 var showStatusbar = document.getElementById("menu_showTaskbar").getAttribute("checked");
713 var newmenupopuplist = null;
714 if (gCurrentMode == "mail") {
715 newmenupopuplist = mailpopuplist;
716 } else if (gCurrentMode == "calendar") {
717 newmenupopuplist = calendarpopuplist;
718 } else if (gCurrentMode == "task") {
719 newmenupopuplist = taskpopuplist;
720 }
721 for (var i = 0; i < menulist.length; i++) {
722 var menu = menulist[i];
723 var oldmenupopup = menu.firstChild;
724 if (oldmenupopup) {
725 menu.replaceChild(newmenupopuplist[i], oldmenupopup);
726 }
727 }
728 document.getElementById("menu_showTaskbar").setAttribute("checked", showStatusbar);
729 var messageMenu = document.getElementById("messageMenu");
730 if (gCurrentMode == "mail") {
731 messageMenu.setAttribute("label", messagemenulabel);
732 messageMenu.setAttribute("accesskey", messagemenuaccesskey);
733 } else if (gCurrentMode == "calendar"){
734 messageMenu.setAttribute("label", calendarmenulabel);
735 messageMenu.setAttribute("accesskey", calendarmenuaccesskey);
736 } else if (gCurrentMode == "task"){
737 messageMenu.setAttribute("label", tasksmenulabel);
738 messageMenu.setAttribute("accesskey", tasksmenuaccesskey);
739 }
740 }
741
742 function removeMenuElements(aRoot, aModeValue) {
743 for (var n = 0; n < aModeValue.length; n++) {
744 var modeElements = aRoot.getElementsByAttribute("mode", aModeValue[n]);
745 if (modeElements.length > 0) {
746 for (var i = modeElements.length-1; i >=0; i--) {
747 var element = modeElements[i];
748 if (element) {
749 var localName = element.localName;
750 if (localName =="menuitem" || localName == "menuseparator" || localName == "menu"){
751 element.parentNode.removeChild(element);
752 }
753 }
754 }
755 }
756 }
757 }
758
759 // == invitations link
760 const FIRST_DELAY_STARTUP = 100;
761 const FIRST_DELAY_RESCHEDULE = 100;
762 const FIRST_DELAY_REGISTER = 10000;
763 const FIRST_DELAY_UNREGISTER = 0;
764 const REPEAT_DELAY = 180000;
765
766 var gInvitationsOperationListener = {
767 onOperationComplete: function sBOL_onOperationComplete(aCalendar,
768 aStatus,
769 aOperationType,
770 aId,
771 aDetail) {
772 if (!Components.isSuccessCode(aStatus)) {
773 var invitationsBox = document.getElementById("invitations");
774 invitationsBox.setAttribute("hidden", "true");
775 }
776 },
777
778 onGetResult: function sBOL_onGetResult(aCalendar,
779 aStatus,
780 aItemType,
781 aDetail,
782 aCount,
783 aItems) {
784 if (!Components.isSuccessCode(aStatus)) {
785 return;
786 }
787 var invitationsBox = document.getElementById("invitations");
788 var value = invitationsLabel + " (" + aCount + ")";
789 invitationsBox.setAttribute("value", value);
790 invitationsBox.removeAttribute("hidden");
791 }
792 };
793
794 var gInvitationsCalendarManagerObserver = {
795 mSideBar: this,
796
797 onCalendarRegistered: function cMO_onCalendarRegistered(aCalendar) {
798 this.mSideBar.rescheduleInvitationsUpdate(FIRST_DELAY_REGISTER,
799 REPEAT_DELAY);
800 },
801
802 onCalendarUnregistering: function cMO_onCalendarUnregistering(aCalendar) {
803 this.mSideBar.rescheduleInvitationsUpdate(FIRST_DELAY_UNREGISTER,
804 REPEAT_DELAY);
805 },
806
807 onCalendarDeleting: function cMO_onCalendarDeleting(aCalendar) {
808 }
809 };
810
811 function scheduleInvitationsUpdate(firstDelay, repeatDelay) {
812 getInvitationsManager().scheduleInvitationsUpdate(firstDelay,
813 repeatDelay,
814 gInvitationsOperationListener);
815 }
816
817 function rescheduleInvitationsUpdate(firstDelay, repeatDelay) {
818 getInvitationsManager().cancelInvitationsUpdate();
819 scheduleInvitationsUpdate(firstDelay, repeatDelay);
820 }
821
822 function openInvitationsDialog() {
823 getInvitationsManager().cancelInvitationsUpdate();
824 getInvitationsManager().openInvitationsDialog(
825 gInvitationsOperationListener,
826 function oiD_callback() {
827 scheduleInvitationsUpdate(FIRST_DELAY_RESCHEDULE,
828 REPEAT_DELAY);
829 });
830 }
831
832 SelectMessage = function(messageUri) {
833 document.getElementById("switch2mail").doCommand();
834 gSelectMessage(messageUri);
835 }
836
837 document.getElementById("displayDeck").
838 addEventListener("select", LtnObserveDisplayDeckChange, true);
839
840 document.addEventListener("load", ltnOnLoad, true);