home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 June / PCpro_2006_06.ISO / files / firefox / calendar_windows_latest.xpi / components / calMemoryCalendar.js < prev    next >
Encoding:
JavaScript  |  2006-01-21  |  20.3 KB  |  550 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
  18.  *  Oracle Corporation
  19.  * Portions created by the Initial Developer are Copyright (C) 2004
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. //
  40. // calMemoryCalendar.js
  41. //
  42.  
  43. const calCalendarManagerContractID = "@mozilla.org/calendar/manager;1";
  44. const calICalendarManager = Components.interfaces.calICalendarManager;
  45.  
  46. const USECS_PER_SECOND = 1000000;
  47.  
  48. var activeCalendarManager = null;
  49. function getCalendarManager()
  50. {
  51.     if (!activeCalendarManager) {
  52.         activeCalendarManager = 
  53.             Components.classes[calCalendarManagerContractID].getService(calICalendarManager);
  54.     }
  55.     return activeCalendarManager;
  56. }
  57.  
  58. function calMemoryCalendar() {
  59.     this.wrappedJSObject = this;
  60.     this.calendarToReturn = this,
  61.     this.initMemoryCalendar();
  62. }
  63.  
  64. // END_OF_TIME needs to be the max value a PRTime can be
  65. const START_OF_TIME = -0x7fffffffffffffff;
  66. const END_OF_TIME = 0x7fffffffffffffff;
  67.  
  68. calMemoryCalendar.prototype = {
  69.     // This will be returned from getItems as the calendar. The ics
  70.     // calendar overwrites this.
  71.     calendarToReturn: null,
  72.  
  73.     //
  74.     // nsISupports interface
  75.     // 
  76.     QueryInterface: function (aIID) {
  77.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  78.             !aIID.equals(Components.interfaces.calICalendar)) {
  79.             throw Components.results.NS_ERROR_NO_INTERFACE;
  80.         }
  81.  
  82.         return this;
  83.     },
  84.  
  85.     initMemoryCalendar: function() {
  86.         this.mObservers = Array();
  87.         this.mItems = { };
  88.     },
  89.  
  90.     //
  91.     // nsICalendar interface
  92.     //
  93.  
  94.     // attribute AUTF8String name;
  95.     get name() {
  96.         return getCalendarManager().getCalendarPref(this, "NAME");
  97.     },
  98.     set name(name) {
  99.         getCalendarManager().setCalendarPref(this, "NAME", name);
  100.     },
  101.  
  102.     // readonly attribute AUTF8String type;
  103.     get type() { return "memory"; },
  104.  
  105.     mReadOnly: false,
  106.  
  107.     // Most of the time you can just let the ics calendar handle this
  108.     get readOnly() { 
  109.         return this.mReadOnly;
  110.     },
  111.     set readOnly(bool) {
  112.         this.mReadOnly = bool;
  113.     },
  114.  
  115.     // attribute nsIURI uri;
  116.     mUri: null,
  117.     get uri() { return this.mUri; },
  118.     set uri(aURI) { this.mUri = aURI; },
  119.  
  120.  
  121.     refresh: function() {
  122.         // no-op
  123.     },
  124.  
  125.     // attribute boolean suppressAlarms;
  126.     get suppressAlarms() { return false; },
  127.     set suppressAlarms(aSuppressAlarms) { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; },
  128.  
  129.     // void addObserver( in calIObserver observer );
  130.     addObserver: function (aObserver, aItemFilter) {
  131.         if (this.mObservers.some(function(o) { return (o == aObserver); }))
  132.             return;
  133.  
  134.         this.mObservers.push(aObserver);
  135.     },
  136.  
  137.     // void removeObserver( in calIObserver observer );
  138.     removeObserver: function (aObserver) {
  139.         var newObservers = this.mObservers.filter(function(o) { return (o != aObserver); });
  140.         this.mObservers = newObservers;
  141.     },
  142.  
  143.     // void addItem( in calIItemBase aItem, in calIOperationListener aListener );
  144.     addItem: function (aItem, aListener) {
  145.         var newItem = aItem.clone();
  146.         return this.adoptItem(newItem, aListener);
  147.     },
  148.     
  149.     // void adoptItem( in calIItemBase aItem, in calIOperationListener aListener );
  150.     adoptItem: function (aItem, aListener) {
  151.         if (this.readOnly) 
  152.             throw Components.interfaces.calIErrors.CAL_IS_READONLY;
  153.         if (aItem.id == null && aItem.isMutable)
  154.             aItem.id = "uuid" + (new Date()).getTime();
  155.  
  156.         if (aItem.id == null) {
  157.             if (aListener)
  158.                 aListener.onOperationComplete (this.calendarToReturn,
  159.                                                Components.results.NS_ERROR_FAILURE,
  160.                                                aListener.ADD,
  161.                                                aItem.id,
  162.                                                "Can't set ID on non-mutable item to addItem");
  163.             return;
  164.         }
  165.  
  166.         if (this.mItems[aItem.id] != null) {
  167.             // is this an error?
  168.             if (aListener)
  169.                 aListener.onOperationComplete (this.calendarToReturn,
  170.                                                Components.results.NS_ERROR_FAILURE,
  171.                                                aListener.ADD,
  172.                                                aItem.id,
  173.                                                "ID already exists for addItem");
  174.             return;
  175.         }
  176.  
  177.         aItem.calendar = this.calendarToReturn;
  178.         aItem.generation = 1;
  179.         aItem.makeImmutable();
  180.         this.mItems[aItem.id] = aItem;
  181.  
  182.         // notify the listener
  183.         if (aListener)
  184.             aListener.onOperationComplete (this.calendarToReturn,
  185.                                            Components.results.NS_OK,
  186.                                            aListener.ADD,
  187.                                            aItem.id,
  188.                                            aItem);
  189.         // notify observers
  190.         this.observeAddItem(aItem);
  191.     },
  192.  
  193.     // void modifyItem( in calIItemBase aNewItem, in calIItemBase aOldItem, in calIOperationListener aListener );
  194.     modifyItem: function (aNewItem, aOldItem, aListener) {
  195.         if (this.readOnly) 
  196.             throw Components.interfaces.calIErrors.CAL_IS_READONLY;
  197.         if (!aNewItem) {
  198.             throw Components.results.NS_ERROR_FAILURE;
  199.         }
  200.         if (aNewItem.id == null || this.mItems[aNewItem.id] == null) {
  201.             // this is definitely an error
  202.             if (aListener)
  203.                 aListener.onOperationComplete (this.calendarToReturn,
  204.                                                Components.results.NS_ERROR_FAILURE,
  205.                                                aListener.MODIFY,
  206.                                                aNewItem.id,
  207.                                                "ID for modifyItem doesn't exist, is null, or is from different calendar");
  208.             return;
  209.         }
  210.  
  211.         // do the old and new items match?
  212.         if (aOldItem.id != aNewItem.id) {
  213.             if (aListener)
  214.                 aListener.onOperationComplete (this.calendarToReturn,
  215.                                                Components.results.NS_ERROR_FAILURE,
  216.                                                aListener.MODIFY,
  217.                                                aNewItem.id,
  218.                                                "item ID mismatch between old and new items");
  219.             return;
  220.         }
  221.  
  222.         if (aOldItem != this.mItems[aOldItem.id]) {
  223.             if (aListener)
  224.                 aListener.onOperationComplete (this.calendarToReturn,
  225.                                                Components.results.NS_ERROR_FAILURE,
  226.                                                aListener.MODIFY,
  227.                                                aNewItem.id,
  228.                                                "old item mismatch in modifyItem");
  229.             return;
  230.         }
  231.  
  232.         if (aOldItem.generation != aNewItem.generation) {
  233.             if (aListener)
  234.                 aListener.onOperationComplete (this.calendarToReturn,
  235.                                                Components.results.NS_ERROR_FAILURE,
  236.                                                aListener.MODIFY,
  237.                                                aNewItem.id,
  238.                                                "generation mismatch in modifyItem");
  239.             return;
  240.         }
  241.  
  242.         var modifiedItem = aNewItem.clone();
  243.         modifiedItem.generation += 1;
  244.         modifiedItem.makeImmutable();
  245.         this.mItems[aNewItem.id] = modifiedItem;
  246.  
  247.         if (aListener)
  248.             aListener.onOperationComplete (this.calendarToReturn,
  249.                                            Components.results.NS_OK,
  250.                                            aListener.MODIFY,
  251.                                            modifiedItem.id,
  252.                                            modifiedItem);
  253.         // notify observers
  254.         this.observeModifyItem(modifiedItem, aOldItem);
  255.     },
  256.  
  257.     // void deleteItem( in calIItemBase aItem, in calIOperationListener aListener );
  258.     deleteItem: function (aItem, aListener) {
  259.         if (this.readOnly) 
  260.             throw Components.interfaces.calIErrors.CAL_IS_READONLY;
  261.         if (aItem.id == null || this.mItems[aItem.id] == null) {
  262.             if (aListener)
  263.                 aListener.onOperationComplete (this.calendarToReturn,
  264.                                                Components.results.NS_ERROR_FAILURE,
  265.                                                aListener.DELETE,
  266.                                                aItem.id,
  267.                                                "ID is null or is from different calendar in deleteItem");
  268.             return;
  269.         }
  270.  
  271.         var oldItem = this.mItems[aItem.id];
  272.         if (oldItem.generation != aItem.generation) {
  273.             if (aListener)
  274.                 aListener.onOperationComplete (this.calendarToReturn,
  275.                                                Components.results.NS_ERROR_FAILURE,
  276.                                                aListener.DELETE,
  277.                                                aItem.id,
  278.                                                "generation mismatch in deleteItem");
  279.             return;
  280.         }
  281.  
  282.         delete this.mItems[aItem.id];
  283.  
  284.         if (aListener)
  285.             aListener.onOperationComplete (this.calendarToReturn,
  286.                                            Components.results.NS_OK,
  287.                                            aListener.DELETE,
  288.                                            aItem.id,
  289.                                            null);
  290.         // notify observers
  291.         this.observeDeleteItem(oldItem);
  292.     },
  293.  
  294.     // void getItem( in string id, in calIOperationListener aListener );
  295.     getItem: function (aId, aListener) {
  296.         if (!aListener)
  297.             return;
  298.  
  299.         if (aId == null ||
  300.             this.mItems[aId] == null) {
  301.             aListener.onOperationComplete(this.calendarToReturn,
  302.                                           Components.results.NS_ERROR_FAILURE,
  303.                                           aListener.GET,
  304.                                           null,
  305.                                           "IID doesn't exist for getItem");
  306.             return;
  307.         }
  308.  
  309.         var item = this.mItems[aId];
  310.         var iid = null;
  311.  
  312.         if (item instanceof Components.interfaces.calIEvent) {
  313.             iid = Components.interfaces.calIEvent;
  314.         } else if (item instanceof Components.interfaces.calITodo) {
  315.             iid = Components.interfaces.calITodo;
  316.         } else {
  317.             aListener.onOperationComplete (this.calendarToReturn,
  318.                                            Components.results.NS_ERROR_FAILURE,
  319.                                            aListener.GET,
  320.                                            aId,
  321.                                            "Can't deduce item type based on QI");
  322.             return;
  323.         }
  324.  
  325.         aListener.onGetResult (this.calendarToReturn,
  326.                                Components.results.NS_OK,
  327.                                iid,
  328.                                null, 1, [item]);
  329.  
  330.         aListener.onOperationComplete (this.calendarToReturn,
  331.                                        Components.results.NS_OK,
  332.                                        aListener.GET,
  333.                                        aId,
  334.                                        null);
  335.  
  336.     },
  337.  
  338.     // void getItems( in unsigned long aItemFilter, in unsigned long aCount, 
  339.     //                in calIDateTime aRangeStart, in calIDateTime aRangeEnd,
  340.     //                in calIOperationListener aListener );
  341.     getItems: function (aItemFilter, aCount,
  342.                         aRangeStart, aRangeEnd, aListener)
  343.     {
  344.         if (!aListener)
  345.             return;
  346.  
  347.         const calICalendar = Components.interfaces.calICalendar;
  348.         const calIItemBase = Components.interfaces.calIItemBase;
  349.         const calIEvent = Components.interfaces.calIEvent;
  350.         const calITodo = Components.interfaces.calITodo;
  351.         const calIItemOccurrence = Components.interfaces.calIItemOccurrence;
  352.         const calIRecurrenceInfo = Components.interfaces.calIRecurrenceInfo;
  353.  
  354.         var itemsFound = Array();
  355.         var startTime = START_OF_TIME;
  356.         var endTime = END_OF_TIME;
  357.         if (aRangeStart)
  358.             startTime = aRangeStart.nativeTime;
  359.         if (aRangeEnd)
  360.             endTime = aRangeEnd.nativeTime;
  361.  
  362.         //
  363.         // filters
  364.         //
  365.  
  366.         // item base type
  367.         var wantEvents = ((aItemFilter & calICalendar.ITEM_FILTER_TYPE_EVENT) != 0);
  368.         var wantTodos = ((aItemFilter & calICalendar.ITEM_FILTER_TYPE_TODO) != 0);
  369.         if(!wantEvents && !wantTodos) {
  370.             // bail.
  371.             aListener.onOperationComplete (this.calendarToReturn,
  372.                                            Components.results.NS_ERROR_FAILURE,
  373.                                            aListener.GET,
  374.                                            null,
  375.                                            "Bad aItemFilter passed to getItems");
  376.             return;
  377.         }
  378.  
  379.         // completed?
  380.         var itemCompletedFilter = ((aItemFilter & calICalendar.ITEM_FILTER_COMPLETED_YES) != 0);
  381.         var itemNotCompletedFilter = ((aItemFilter & calICalendar.ITEM_FILTER_COMPLETED_NO) != 0);
  382.  
  383.         // return occurrences?
  384.         var itemReturnOccurrences = ((aItemFilter & calICalendar.ITEM_FILTER_CLASS_OCCURRENCES) != 0);
  385.  
  386.         // figure out the return interface type
  387.         var typeIID = null;
  388.         if (itemReturnOccurrences) {
  389.             typeIID = calIItemBase;
  390.         } else {
  391.             if (wantEvents && wantTodos) {
  392.                 typeIID = calIItemBase;
  393.             } else if (wantEvents) {
  394.                 typeIID = calIEvent;
  395.             } else if (wantTodos) {
  396.                 typeIID = calITodo;
  397.             }
  398.         }
  399.  
  400.         //  if aCount != 0, we don't attempt to sort anything, and
  401.         //  instead return the first aCount items that match.
  402.  
  403.         for (var itemIndex in this.mItems) {
  404.             var item = this.mItems[itemIndex];
  405.             var itemtoadd = null;
  406.  
  407.             var itemStartTime = 0;
  408.             var itemEndTime = 0;
  409.  
  410.             var tmpitem = item;
  411.             if (wantEvents && (item instanceof calIEvent)) {
  412.                 tmpitem = item.QueryInterface(calIEvent);
  413.                 itemStartTime = (item.startDate
  414.                                  ? item.startDate.nativeTime
  415.                                  : START_OF_TIME);
  416.                 itemEndTime = (item.endDate
  417.                                ? item.endDate.nativeTime
  418.                                : END_OF_TIME);
  419.             } else if (wantTodos && (item instanceof calITodo)) {
  420.                 // if it's a todo, also filter based on completeness
  421.                 if (item.percentComplete == 100 && !itemCompletedFilter)
  422.                     continue;
  423.                 else if (item.percentComplete < 100 && !itemNotCompletedFilter)
  424.                     continue;
  425.  
  426.                 itemEndTime = itemStartTime = 
  427.                     item.entryDate ? item.entryDate.nativeTime : 0;
  428.             } else {
  429.                 // XXX unknown item type, wth do we do?
  430.                 continue;
  431.             }
  432.  
  433.             // Correct for floating
  434.             if (aRangeStart && item.startDate && item.startDate.timezone == 'floating')
  435.                 itemStartTime -= aRangeStart.timezoneOffset * USECS_PER_SECOND;
  436.             if (aRangeEnd && item.endDate && item.endDate.timezone == 'floating')
  437.                 itemEndTime -= aRangeEnd.timezoneOffset * USECS_PER_SECOND;
  438.  
  439.             if (itemStartTime < endTime) {
  440.                 // figure out if there are recurrences here we care about
  441.                 if (itemReturnOccurrences && item.recurrenceInfo)
  442.                 {
  443.                     // there might be some recurrences here that we need to handle
  444.                     var recs = item.recurrenceInfo.getOccurrences (aRangeStart, aRangeEnd, 0, {});
  445.                     itemsFound = itemsFound.concat(recs);
  446.                 } else if (itemEndTime >= startTime) {
  447.                     itemsFound.push(item);
  448.                 }
  449.             }
  450.  
  451.             if (aCount && itemsFound.length >= aCount)
  452.                 break;
  453.         }
  454.  
  455.         aListener.onGetResult (this.calendarToReturn,
  456.                                Components.results.NS_OK,
  457.                                typeIID,
  458.                                null,
  459.                                itemsFound.length,
  460.                                itemsFound);
  461.  
  462.         aListener.onOperationComplete (this.calendarToReturn,
  463.                                        Components.results.NS_OK,
  464.                                        aListener.GET,
  465.                                        null,
  466.                                        null);
  467.     },
  468.  
  469.     startBatch: function ()
  470.     {
  471.         this.observeBatchChange(true);
  472.     },
  473.     endBatch: function ()
  474.     {
  475.         this.observeBatchChange(false);
  476.     },
  477.  
  478.     //
  479.     // Helper functions
  480.     //
  481.     observeBatchChange: function (aNewBatchMode) {
  482.         for each (obs in this.mObservers) {
  483.             if (aNewBatchMode)
  484.                 obs.onStartBatch ();
  485.             else
  486.                 obs.onEndBatch ();
  487.         }
  488.     },
  489.  
  490.     observeAddItem: function (aItem) {
  491.         for each (obs in this.mObservers)
  492.             obs.onAddItem (aItem);
  493.     },
  494.  
  495.     observeModifyItem: function (aOldItem, aNewItem) {
  496.         for each (obs in this.mObservers)
  497.             obs.onModifyItem (aOldItem, aNewItem);
  498.     },
  499.  
  500.     observeDeleteItem: function (aDeletedItem) {
  501.         for each (obs in this.mObservers)
  502.             obs.onDeleteItem (aDeletedItem);
  503.     },
  504. }
  505.  
  506. /****
  507.  **** module registration
  508.  ****/
  509.  
  510. var calMemoryCalendarModule = {
  511.     mCID: Components.ID("{bda0dd7f-0a2f-4fcf-ba08-5517e6fbf133}"),
  512.     mContractID: "@mozilla.org/calendar/calendar;1?type=memory",
  513.     
  514.     registerSelf: function (compMgr, fileSpec, location, type) {
  515.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  516.         compMgr.registerFactoryLocation(this.mCID,
  517.                                         "Calendar in-memory back-end",
  518.                                         this.mContractID,
  519.                                         fileSpec,
  520.                                         location,
  521.                                         type);
  522.     },
  523.  
  524.     getClassObject: function (compMgr, cid, iid) {
  525.         if (!cid.equals(this.mCID))
  526.             throw Components.results.NS_ERROR_NO_INTERFACE;
  527.  
  528.         if (!iid.equals(Components.interfaces.nsIFactory))
  529.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  530.  
  531.         return this.mFactory;
  532.     },
  533.  
  534.     mFactory: {
  535.         createInstance: function (outer, iid) {
  536.             if (outer != null)
  537.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  538.             return (new calMemoryCalendar()).QueryInterface(iid);
  539.         }
  540.     },
  541.  
  542.     canUnload: function(compMgr) {
  543.         return true;
  544.     }
  545. };
  546.  
  547. function NSGetModule(compMgr, fileSpec) {
  548.     return calMemoryCalendarModule;
  549. }
  550.