home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 June / PCpro_2006_06.ISO / files / firefox / calendar_windows_latest.xpi / components / calAlarmService.js < prev    next >
Encoding:
Text File  |  2006-01-21  |  13.3 KB  |  402 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Oracle Corporation code.
  16.  *
  17.  * The Initial Developer of the Original Code is Oracle Corporation
  18.  * Portions created by the Initial Developer are Copyright (C) 2005
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Stuart Parmenter <stuart.parmenter@oracle.com>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const kHoursBetweenUpdates = 6;
  39.  
  40. function newTimerWithCallback(callback, delay, repeating)
  41. {
  42.     var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
  43.     
  44.     timer.initWithCallback(callback,
  45.                            delay,
  46.                            (repeating) ? timer.TYPE_REPEATING_PRECISE : timer.TYPE_ONE_SHOT);
  47.     return timer;
  48. }
  49.  
  50. function jsDateToDateTime(date)
  51. {
  52.     var newDate = Components.classes["@mozilla.org/calendar/datetime;1"].createInstance(Components.interfaces.calIDateTime);
  53.     newDate.jsDate = date;
  54.     return newDate;
  55. }
  56.  
  57. function jsDateToFloatingDateTime(date)
  58. {
  59.     var newDate = Components.classes["@mozilla.org/calendar/datetime;1"].createInstance(Components.interfaces.calIDateTime);
  60.     newDate.timezone = "floating";
  61.     newDate.year = date.getFullYear();
  62.     newDate.month = date.getMonth();
  63.     newDate.day = date.getDate();
  64.     newDate.hour = date.getHours();
  65.     newDate.minute = date.getMinutes();
  66.     newDate.second = date.getSeconds();
  67.     newDate.normalize();
  68.     return newDate;
  69. }
  70.  
  71. /*
  72. var testalarmnumber = 0;
  73. function testAlarm(name) {
  74.     this.title = name;
  75.     this.alarmTime = jsDateToDateTime(new Date());
  76.     this.alarmTime.second += 5;
  77.     this.alarmTime.normalize();
  78.     this.calendar = new Object();
  79.     this.calendar.suppressAlarms = false;
  80.     this.id = testalarmnumber++;
  81. }
  82. testAlarm.prototype = {
  83. };
  84. */
  85.  
  86.  
  87. function calAlarmService() {
  88.     this.wrappedJSObject = this;
  89.  
  90.     this.calendarObserver = {
  91.         alarmService: this,
  92.  
  93.         onStartBatch: function() { },
  94.         onEndBatch: function() { },
  95.         onLoad: function() { },
  96.         onAddItem: function(aItem) {
  97.             if (aItem.alarmTime)
  98.                 this.alarmService.addAlarm(aItem, false);
  99.         },
  100.         onModifyItem: function(aNewItem, aOldItem) {
  101.             this.alarmService.removeAlarm(aOldItem);
  102.  
  103.             if (aNewItem.alarmTime)
  104.                 this.alarmService.addAlarm(aNewItem, false);
  105.         },
  106.         onDeleteItem: function(aDeletedItem) {
  107.             this.alarmService.removeAlarm(aDeletedItem);
  108.         },
  109.         onAlarm: function(aAlarmItem) { },
  110.         onError: function(aErrNo, aMessage) { }
  111.     };
  112.  
  113.  
  114.     this.calendarManagerObserver = {
  115.         alarmService: this,
  116.  
  117.         onCalendarRegistered: function(aCalendar) {
  118.             this.alarmService.observeCalendar(aCalendar);
  119.         },
  120.         onCalendarUnregistering: function(aCalendar) {
  121.             this.alarmService.unobserveCalendar(aCalendar);
  122.         },
  123.         onCalendarDeleting: function(aCalendar) {},
  124.         onCalendarPrefSet: function(aCalendar, aName, aValue) {},
  125.         onCalendarPrefDeleting: function(aCalendar, aName) {}
  126.     };
  127. }
  128.  
  129. var calAlarmServiceClassInfo = {
  130.     getInterfaces: function (count) {
  131.         var ifaces = [
  132.             Components.interfaces.nsISupports,
  133.             Components.interfaces.calIAlarmService,
  134.             Components.interfaces.nsIObserver,
  135.             Components.interfaces.nsIClassInfo
  136.         ];
  137.         count.value = ifaces.length;
  138.         return ifaces;
  139.     },
  140.  
  141.     getHelperForLanguage: function (language) {
  142.         return null;
  143.     },
  144.  
  145.     contractID: "@mozilla.org/calendar/alarm-service;1",
  146.     classDescription: "Calendar Alarm Service",
  147.     classID: Components.ID("{7a9200dd-6a64-4fff-a798-c5802186e2cc}"),
  148.     implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
  149.     flags: 0
  150. };
  151.  
  152. calAlarmService.prototype = {
  153.     mRangeStart: null,
  154.     mRangeEnd: null,
  155.     mEvents: {},
  156.     mObservers: [],
  157.     mUpdateTimer: null,
  158.     mStarted: false,
  159.  
  160.     QueryInterface: function (aIID) {
  161.         if (aIID.equals(Components.interfaces.nsIClassInfo))
  162.             return calAlarmServiceClassInfo;
  163.  
  164.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  165.             !aIID.equals(Components.interfaces.calIAlarmService) &&
  166.             !aIID.equals(Components.interfaces.nsIObserver))
  167.         {
  168.             throw Components.results.NS_ERROR_NO_INTERFACE;
  169.         }
  170.  
  171.         return this;
  172.     },
  173.  
  174.  
  175.     /* nsIObserver */
  176.     observe: function (subject, topic, data) {
  177.         observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
  178.  
  179.         if (topic == "app-startup") {
  180.             observerService.addObserver(this, "profile-after-change", false);
  181.             observerService.addObserver(this, "xpcom-shutdown", false);
  182.         }
  183.         if (topic == "profile-after-change") {
  184.             this.startup();
  185.         }
  186.         if (topic == "xpcom-shutdown") {
  187.             this.shutdown();
  188.         }
  189.     },
  190.  
  191.     /* calIAlarmService APIs */
  192.     snoozeEvent: function(event, duration) {
  193.         /* modify the event for a new alarm time */
  194.         var alarmTime = jsDateToDateTime((new Date())).getInTimezone("UTC");
  195.         alarmTime.addDuration(duration);
  196.         newEvent = event.clone();
  197.         newEvent.alarmTime = alarmTime;
  198.         // calling modifyItem will cause us to get the right callback
  199.         // and update the alarm properly
  200.         newEvent.calendar.modifyItem(newEvent, event, null);
  201.     },
  202.  
  203.     addObserver: function(aObserver) {
  204.         dump("observer added\n");
  205.         if (this.mObservers.indexOf(aObserver) != -1)
  206.             return;
  207.  
  208.         this.mObservers.push(aObserver);
  209.     },
  210.  
  211.     removeObserver: function(aObserver) {
  212.         dump("observer removed\n");
  213.         function notThis(v) {
  214.             return v != aObserver;
  215.         }
  216.  
  217.         this.mObservers = this.mObservers.filter(notThis);
  218.     },
  219.  
  220.  
  221.     /* helper functions */
  222.     notifyObservers: function(functionName, args) {
  223.         function notify(obs) {
  224.             try { obs[functionName].apply(obs, args);  }
  225.             catch (e) { }
  226.         }
  227.         this.mObservers.forEach(notify);
  228.     },
  229.  
  230.     startup: function() {
  231.         if (this.mStarted)
  232.             return;
  233.  
  234.         dump("Starting calendar alarm service\n");
  235.         this.mStarted = true;
  236.  
  237.         this.calendarManager = Components.classes["@mozilla.org/calendar/manager;1"].getService(Components.interfaces.calICalendarManager);
  238.         var calendarManager = this.calendarManager;
  239.         calendarManager.addObserver(this.calendarManagerObserver);
  240.  
  241.         var calendars = calendarManager.getCalendars({});
  242.         for each(var calendar in calendars) {
  243.             this.observeCalendar(calendar);
  244.         }
  245.  
  246.         this.findAlarms();
  247.  
  248.         /* set up a timer to update alarms every N hours */
  249.         var timerCallback = {
  250.             alarmService: this,
  251.             notify: function(timer) {
  252.                 this.alarmService.findAlarms();
  253.             }
  254.         };
  255.  
  256.         this.mUpdateTimer = newTimerWithCallback(timerCallback, kHoursBetweenUpdates * 3600000, true);
  257.  
  258.         /* tell people that we're alive so they can start monitoring alarms */
  259.         this.notifier = Components.classes["@mozilla.org/embedcomp/appstartup-notifier;1"].getService(Components.interfaces.nsIObserver);
  260.         var notifier = this.notifier;
  261.         notifier.observe(null, "alarm-service-startup", null);
  262.  
  263.  
  264.         /* Test Code
  265.         this.addAlarm(new testAlarm("Meeting with Mr. T"));
  266.         this.addAlarm(new testAlarm("Blah blah"));
  267.         */
  268.     },
  269.  
  270.     shutdown: function() {
  271.         /* tell people that we're no longer running */
  272.         var notifier = this.notifier;
  273.         notifier.observe(null, "alarm-service-shutdown", null);
  274.  
  275.         if (this.mUpdateTimer) {
  276.             this.mUpdateTimer.cancel();
  277.             this.mUpdateTimer = null;
  278.         }
  279.         
  280.         var calendarManager = this.calendarManager;
  281.         calendarManager.removeObserver(this.calendarManagerObserver);
  282.  
  283.         for each(var timer in this.mEvents) {
  284.             timer.cancel();
  285.         }
  286.         this.mEvents = {};
  287.  
  288.         var calendars = calendarManager.getCalendars({});
  289.         for each(var calendar in calendars) {
  290.             this.unobserveCalendar(calendar);
  291.         }
  292.  
  293.         this.calendarManager = null;
  294.         this.notifier = null;
  295.         this.mRangeStart = null;
  296.         this.mRangeEnd = null;
  297.  
  298.         this.mStarted = false;
  299.     },
  300.  
  301.  
  302.     observeCalendar: function(calendar) {
  303.         calendar.addObserver(this.calendarObserver);
  304.     },
  305.  
  306.     unobserveCalendar: function(calendar) {
  307.         calendar.removeObserver(this.calendarObserver);
  308.     },
  309.  
  310.     addAlarm: function(aItem, skipCheck, alarmTime) {
  311.         // if aItem.alarmTime >= 'now' && aItem.alarmTime <= gAlarmEndTime
  312.         if (!alarmTime)
  313.             alarmTime = aItem.alarmTime.getInTimezone("UTC");
  314.  
  315.         var now;
  316.         // XXX When the item is floating, should use the default timezone
  317.         // from the prefs, instead of the javascript timezone (which is what
  318.         // jsDateToFloatingDateTime uses)
  319.         if (aItem.alarmTime.timezone == "floating")
  320.             now = jsDateToFloatingDateTime((new Date()));
  321.         else
  322.             now = jsDateToDateTime((new Date())).getInTimezone("UTC");
  323.  
  324.         var callbackObj = {
  325.             alarmService: this,
  326.             item: aItem,
  327.             notify: function(timer) {
  328.                 this.alarmService.alarmFired(this.item);
  329.                 delete this.alarmService.mEvents[this.item];
  330.             }
  331.         };
  332.  
  333.         if ((alarmTime.compare(now) >= 0) || skipCheck) {
  334.             // delay is in msec, so don't forget to multiply
  335.             var timeout = alarmTime.subtractDate(now).inSeconds * 1000;
  336.  
  337.             var timeUntilRefresh = this.mRangeEnd.subtractDate(now).inSeconds * 1000;
  338.             if (timeUntilRefresh < timeout) {
  339.                 // we'll get this alarm later.  No sense in keeping an extra timeout
  340.                 return;
  341.             }
  342.  
  343.             this.mEvents[aItem.id] = newTimerWithCallback(callbackObj, timeout, false);
  344.             dump("adding alarm timeout (" + timeout + ") for " + aItem + " at " + aItem.alarmTime + "\n");
  345.         }
  346.     },
  347.  
  348.     removeAlarm: function(aItem) {
  349.         if (aItem.id in this.mEvents) {
  350.             this.mEvents[aItem.id].cancel();
  351.             delete this.mEvents[aItem.id];
  352.         }
  353.     },
  354.  
  355.     findAlarms: function() {
  356.         if (this.mEvents.length > 0) {
  357.             // This may happen if an alarm is snoozed over the time range mark
  358.             var err = new Error();
  359.             var debug = Components.classes["@mozilla.org/xpcom/debug;1"].getService(Components.interfaces.nsIDebug);
  360.             debug.warning("mEvents.length should always be 0 when we enter findAlarms",
  361.                           err.fileName, err.lineNumber);
  362.         }
  363.  
  364.         var getListener = {
  365.             alarmService: this,
  366.             onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) {
  367.             },
  368.             onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
  369.                 for (var i = 0; i < aCount; ++i) {
  370.                     var item = aItems[i];
  371.                     if (item.alarmTime) {
  372.                         this.alarmService.addAlarm(item, false);
  373.                     }
  374.                 }
  375.             }
  376.         };
  377.  
  378.         // figure out the 'now' and 6 hours from now and look for events 
  379.         // between then with alarms
  380.         this.mRangeStart = jsDateToDateTime((new Date())).getInTimezone("UTC");
  381.  
  382.         var until = this.mRangeStart.clone();
  383.         until.hour += kHoursBetweenUpdates;
  384.         until.normalize();
  385.         this.mRangeEnd = until.getInTimezone("UTC");
  386.  
  387.         var calendarManager = this.calendarManager;
  388.         var calendars = calendarManager.getCalendars({});
  389.         for each(var calendar in calendars) {
  390.             calendar.getItems(calendar.ITEM_FILTER_TYPE_EVENT,
  391.                               0, this.mRangeStart, this.mRangeEnd, getListener);
  392.         }
  393.     },
  394.  
  395.     alarmFired: function(event) {
  396.         if (event.calendar.suppressAlarms)
  397.             return;
  398.  
  399.         this.notifyObservers("onAlarm", [event]);
  400.     }
  401. };
  402.