home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 June / PCpro_2006_06.ISO / files / firefox / calendar_windows_latest.xpi / components / calItemBase.js < prev    next >
Encoding:
Text File  |  2006-01-16  |  21.7 KB  |  681 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.  *   Mike Shaver <shaver@off.net>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the MPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the MPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. //
  41. // calItemBase.js
  42. //
  43.  
  44. const ICAL = Components.interfaces.calIIcalComponent;
  45. const kHashPropertyBagContractID = "@mozilla.org/hash-property-bag;1";
  46. const kIWritablePropertyBag = Components.interfaces.nsIWritablePropertyBag;
  47. const HashPropertyBag = new Components.Constructor(kHashPropertyBagContractID, kIWritablePropertyBag);
  48.  
  49. function NewCalDateTime(aJSDate) {
  50.     var c = new CalDateTime();
  51.     if (aJSDate)
  52.         c.jsDate = aJSDate;
  53.     return c;
  54. }
  55.  
  56. function calItemBase() {
  57. }
  58.  
  59. calItemBase.prototype = {
  60.     mIsProxy: false,
  61.  
  62.     QueryInterface: function (aIID) {
  63.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  64.             !aIID.equals(Components.interfaces.calIItemBase))
  65.         {
  66.             throw Components.results.NS_ERROR_NO_INTERFACE;
  67.         }
  68.  
  69.         return this;
  70.     },
  71.  
  72.     mParentItem: null,
  73.     get parentItem() {
  74.         if (this.mParentItem)
  75.             return this.mParentItem;
  76.         else
  77.             return this;
  78.     },
  79.     set parentItem(value) {
  80.         if (this.mImmutable)
  81.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  82.         this.mIsProxy = true;
  83.         this.mParentItem = value;
  84.     },
  85.  
  86.     initializeProxy: function (aParentItem) {
  87.         if (this.mImmutable)
  88.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  89.  
  90.         if (this.mParentItem != null)
  91.             throw Components.results.NS_ERROR_FAILURE;
  92.  
  93.         this.mParentItem = aParentItem;
  94.         this.mCalendar = aParentItem.mCalendar;
  95.         this.mIsProxy = true;
  96.     },
  97.  
  98.     //
  99.     // calIItemBase
  100.     //
  101.     mImmutable: false,
  102.     get isMutable() { return !this.mImmutable; },
  103.  
  104.     mDirty: false,
  105.     modify: function() {
  106.         if (this.mImmutable)
  107.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  108.         this.mDirty = true;
  109.     },
  110.  
  111.     ensureNotDirty: function() {
  112.         if (!this.mDirty)
  113.             return;
  114.  
  115.         if (this.mImmutable) {
  116.             dump ("### Something tried to undirty a dirty immutable event!\n");
  117.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  118.         }
  119.  
  120.         this.setProperty("LAST-MODIFIED", NewCalDateTime(new Date()));
  121.         this.mDirty = false;
  122.     },
  123.  
  124.     makeItemBaseImmutable: function() {
  125.         if (this.mImmutable)
  126.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  127.  
  128.         // make all our components immutable
  129.         if (this.mRecurrenceInfo)
  130.             this.mRecurrenceInfo.makeImmutable();
  131.         if (this.mAlarmTime)
  132.             this.mAlarmTime.makeImmutable();
  133.  
  134.         if (this.mOrganizer)
  135.             this.mOrganizer.makeImmutable();
  136.         for (var i = 0; i < this.mAttendees.length; i++)
  137.             this.mAttendees[i].makeImmutable();
  138.  
  139.         var e = this.mProperties.enumerator;
  140.         while (e.hasMoreElements()) {
  141.             var prop = e.getNext().QueryInterface(Components.interfaces.nsIProperty);
  142.             var val = prop.value;
  143.  
  144.             if (prop.value instanceof Components.interfaces.calIDateTime) {
  145.                 if (prop.value.isMutable)
  146.                     prop.value.makeImmutable();
  147.             }
  148.         }
  149.  
  150.         this.ensureNotDirty();
  151.         this.mImmutable = true;
  152.     },
  153.  
  154.     // initialize this class's members
  155.     initItemBase: function () {
  156.         var now = new Date();
  157.  
  158.         this.mProperties = new HashPropertyBag();
  159.  
  160.         this.setProperty("CREATED", NewCalDateTime(now));
  161.         this.setProperty("LAST-MODIFIED", NewCalDateTime(now));
  162.         this.setProperty("DTSTAMP", NewCalDateTime(now));
  163.  
  164.         this.mAttendees = [];
  165.  
  166.         this.mRecurrenceInfo = null;
  167.  
  168.         this.mAttachments = null;
  169.     },
  170.  
  171.     // for subclasses to use; copies the ItemBase's values
  172.     // into m. aNewParent is optional
  173.     cloneItemBaseInto: function (m, aNewParent) {
  174.         this.ensureNotDirty();
  175.  
  176.         m.mImmutable = false;
  177.         m.mIsProxy = this.mIsProxy;
  178.         m.mParentItem = aNewParent || this.mParentItem;
  179.  
  180.         m.mCalendar = this.mCalendar;
  181.         if (this.mRecurrenceInfo) {
  182.             m.mRecurrenceInfo = this.mRecurrenceInfo.clone();
  183.             m.mRecurrenceInfo.item = m;
  184.         }
  185.  
  186.         if (this.mOrganizer) {
  187.             m.mOrganizer = this.mOrganizer.clone();
  188.         }
  189.  
  190.         m.mAttendees = [];
  191.         for (var i = 0; i < this.mAttendees.length; i++)
  192.             m.mAttendees[i] = this.mAttendees[i].clone();
  193.  
  194.         m.mProperties = Components.classes["@mozilla.org/hash-property-bag;1"].
  195.                         createInstance(Components.interfaces.nsIWritablePropertyBag);
  196.  
  197.         var e = this.mProperties.enumerator;
  198.         while (e.hasMoreElements()) {
  199.             var prop = e.getNext().QueryInterface(Components.interfaces.nsIProperty);
  200.             var val = prop.value;
  201.  
  202.             if (prop.value instanceof Components.interfaces.calIDateTime)
  203.                 val = prop.value.clone();
  204.  
  205.             m.mProperties.setProperty (prop.name, val);
  206.         }
  207.  
  208.         m.mDirty = false;
  209.  
  210.         // these need fixing
  211.         m.mAttachments = this.mAttachments;
  212.         return m;
  213.     },
  214.  
  215.     get lastModifiedTime() {
  216.         this.ensureNotDirty();
  217.         return this.getProperty("LAST-MODIFIED");
  218.     },
  219.  
  220.     get stampTime() {
  221.         var prop = this.getProperty("DTSTAMP");
  222.         if (prop && prop.isValid)
  223.             return prop;
  224.         return this.getProperty("LAST-MODIFIED");
  225.     },
  226.  
  227.     updateStampTime: function() {
  228.         // can't update the stamp time on an immutable event
  229.         if (this.mImmutable)
  230.             return;
  231.  
  232.         this.modify();
  233.         this.setProperty("DTSTAMP", NewCalDateTime(new Date()));
  234.     },
  235.  
  236.     get unproxiedPropertyEnumerator() {
  237.         return this.mProperties.enumerator;
  238.     },
  239.  
  240.     get propertyEnumerator() {
  241.         if (this.mIsProxy) {
  242.             // nsISimpleEnumerator sucks.  It really, really sucks.
  243.             // The interface is badly defined, it's not clear
  244.             // what happens if you just keep calling getNext() without
  245.             // calling hasMoreElements in between, which seems like more
  246.             // of an informational thing.  An interface with
  247.             // "advance()" which returns true or false, and with "item()",
  248.             // which returns the item the enumerator is pointing to, makes
  249.             // far more sense.  Right now we have getNext() doing both
  250.             // item returning and enumerator advancing, which makes
  251.             // no sense.
  252.             return {
  253.                 firstEnumerator: this.mProperties.eumerator,
  254.                 secondEnumerator: this.mParentItem.propertyEnumerator,
  255.                 handledProperties: { },
  256.  
  257.                 currentItem: null,
  258.  
  259.                 QueryInterface: function(aIID) {
  260.                     if (!aIID.equals(Components.interfaces.nsISimpleEnumerator) ||
  261.                         !aIID.equals(Components.interfaces.nsISupports))
  262.                     {
  263.                         throw Components.results.NS_ERROR_NO_INTERFACE;
  264.                     }
  265.                     return this;
  266.                 },
  267.  
  268.                 hasMoreElements: function() {
  269.                     if (!this.secondEnumerator)
  270.                         return false;
  271.  
  272.                     if (this.firstEnumerator) {
  273.                         var moreFirst = this.firstEnumerator.hasMoreElements();
  274.                         if (moreFirst) {
  275.                             this.currentItem = this.firstEnumerator.getNext();
  276.                             this.handledProperties[this.currentItem.name] = true;
  277.                             return true;
  278.                         }
  279.                         this.firstEnumerator = null;
  280.                     }
  281.  
  282.                     var moreSecond = this.secondEnumerator.hasMoreElements();
  283.                     if (moreSecond) {
  284.                         while (this.currentItem.name in this.handledProperties &&
  285.                                this.secondEnumerator.hasMoreElements())
  286.                         do {
  287.                             this.currentItem = this.secondEnumerator.getNext();
  288.                         } while (this.currentItem.name in this.handledProperties &&
  289.                                  ((this.currentItem = null) == null) && // hack
  290.                                  this.secondEnumerator.hasMoreElements());
  291.  
  292.                         if (!this.currentItem)
  293.                             return false;
  294.  
  295.                         return true;
  296.                     }
  297.  
  298.                     this.secondEnumerator = null;
  299.  
  300.                     return false;
  301.                 },
  302.  
  303.                 getNext: function() {
  304.                     if (!currentItem)
  305.                         throw Components.results.NS_ERROR_UNEXPECTED;
  306.  
  307.                     var rval = this.currentItem;
  308.                     this.currentItem = null;
  309.                     return rval;
  310.                 }
  311.             };
  312.         } else {
  313.             return this.mProperties.enumerator;
  314.         }
  315.     },
  316.  
  317.     getProperty: function (aName) {
  318.         try {
  319.             return this.mProperties.getProperty(aName);
  320.         } catch (e) {
  321.             try {
  322.                 if (this.mIsProxy) {
  323.                     return this.mParentItem.getProperty(aName);
  324.                 }
  325.             } catch (e) {}
  326.  
  327.             return null;
  328.         }
  329.     },
  330.  
  331.     getUnproxiedProperty: function (aName) {
  332.         try {
  333.             return this.mProperties.getProperty(aName);
  334.         } catch (e) { }
  335.         return null;
  336.     },
  337.  
  338.     hasProperty: function (aName) {
  339.         return (this.getProperty(aName) != null);
  340.     },
  341.  
  342.     setProperty: function (aName, aValue) {
  343.         this.modify();
  344.         this.mProperties.setProperty(aName, aValue);
  345.     },
  346.  
  347.     deleteProperty: function (aName) {
  348.         this.modify();
  349.         try {
  350.             this.mProperties.deleteProperty(aName);
  351.         } catch (e) { }
  352.     },
  353.  
  354.     getAttendees: function (countObj) {
  355.         countObj.value = this.mAttendees.length;
  356.         return this.mAttendees.concat([]); // clone
  357.     },
  358.  
  359.     getAttendeeById: function (id) {
  360.         for (var i = 0; i < this.mAttendees.length; i++)
  361.             if (this.mAttendees[i].id == id)
  362.                 return this.mAttendees[i];
  363.         return null;
  364.     },
  365.  
  366.     removeAttendee: function (attendee) {
  367.         this.modify();
  368.         var found = false, newAttendees = [];
  369.         for (var i = 0; i < this.mAttendees.length; i++) {
  370.             if (this.mAttendees[i] != attendee)
  371.                 newAttendees.push(this.mAttendees[i]);
  372.             else
  373.                 found = true;
  374.         }
  375.         if (found)
  376.             this.mAttendees = newAttendees;
  377.         else
  378.             throw Component.results.NS_ERROR_INVALID_ARG;
  379.     },
  380.  
  381.     removeAllAttendees: function() {
  382.         this.modify();
  383.         this.mAttendees = [];
  384.     },
  385.  
  386.     addAttendee: function (attendee) {
  387.         this.modify();
  388.         this.mAttendees.push(attendee);
  389.     },
  390.  
  391.     get calendar () {
  392.         return this.mCalendar;
  393.     },
  394.  
  395.     set calendar (v) {
  396.         if (this.mImmutable)
  397.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  398.         this.mCalendar = v;
  399.     },
  400.  
  401.     mOrganizer: null,
  402.     get organizer() {
  403.         return this.mOrganizer;
  404.     },
  405.  
  406.     set organizer(v) {
  407.         this.modify();
  408.         this.mOrganizer = v;
  409.     },
  410.  
  411.     /* MEMBER_ATTR(mIcalString, "", icalString), */
  412.     get icalString() {
  413.         throw Components.results.NS_NOT_IMPLEMENTED;
  414.     },
  415.  
  416.     set icalString() {
  417.         throw Components.results.NS_NOT_IMPLEMENTED;
  418.     },
  419.  
  420.     itemBasePromotedProps: {
  421.         "CREATED": true,
  422.         "UID": true,
  423.         "LAST-MODIFIED": true,
  424.         "SUMMARY": true,
  425.         "PRIORITY": true,
  426.         "METHOD": true,
  427.         "STATUS": true,
  428.         "CLASS": true,
  429.         "DTSTAMP": true,
  430.         "X-MOZILLA-GENERATION": true,
  431.         "RRULE": true,
  432.         "EXDATE": true,
  433.         "RDATE": true,
  434.         "ATTENDEE": true,
  435.         "ORGANIZER": true,
  436.         "RECURRENCE-ID": true,
  437.         "ALARMTIME": true,
  438.     },
  439.  
  440.     icsBasePropMap: [
  441.     { cal: "CREATED", ics: "createdTime" },
  442.     { cal: "LAST-MODIFIED", ics: "lastModified" },
  443.     { cal: "DTSTAMP", ics: "stampTime" },
  444.     { cal: "UID", ics: "uid" },
  445.     { cal: "SUMMARY", ics: "summary" },
  446.     { cal: "PRIORITY", ics: "priority" },
  447.     { cal: "STATUS", ics: "status" },
  448.     { cal: "CLASS", ics: "icalClass" } ],
  449.  
  450.     mapPropsFromICS: function(icalcomp, propmap) {
  451.         for (var i = 0; i < propmap.length; i++) {
  452.             var prop = propmap[i];
  453.             var val = icalcomp[prop.ics];
  454.             if (val != null && val != ICAL.INVALID_VALUE)
  455.                 this.setProperty(prop.cal, val);
  456.         }
  457.     },
  458.  
  459.     mapPropsToICS: function(icalcomp, propmap) {
  460.         for (var i = 0; i < propmap.length; i++) {
  461.             var prop = propmap[i];
  462.             var val = this.getProperty(prop.cal);
  463.             if (val != null && val != ICAL.INVALID_VALUE)
  464.                 icalcomp[prop.ics] = val;
  465.         }
  466.     },
  467.  
  468.     setItemBaseFromICS: function (icalcomp) {
  469.         this.modify();
  470.  
  471.         this.mapPropsFromICS(icalcomp, this.icsBasePropMap);
  472.  
  473.         for (var attprop = icalcomp.getFirstProperty("ATTENDEE");
  474.              attprop;
  475.              attprop = icalcomp.getNextProperty("ATTENDEE")) {
  476.             
  477.             var att = new CalAttendee();
  478.             att.icalProperty = attprop;
  479.             this.addAttendee(att);
  480.         }
  481.  
  482.         var orgprop = icalcomp.getFirstProperty("ORGANIZER");
  483.         if (orgprop) {
  484.             var org = new CalAttendee();
  485.             org.icalProperty = orgprop;
  486.             this.mOrganizer = org;
  487.         }
  488.         
  489.         var gen = icalcomp.getFirstProperty("X-MOZILLA-GENERATION");
  490.         if (gen)
  491.             this.mGeneration = parseInt(gen.value);
  492.  
  493.         // find recurrence properties
  494.         var rec = null;
  495.         for (var recprop = icalcomp.getFirstProperty("ANY");
  496.              recprop;
  497.              recprop = icalcomp.getNextProperty("ANY"))
  498.         {
  499.             var ritem = null;
  500.             if (recprop.propertyName == "RRULE" ||
  501.                 recprop.propertyName == "EXRULE")
  502.             {
  503.                 ritem = new CalRecurrenceRule();
  504.             } else if (recprop.propertyName == "RDATE" ||
  505.                        recprop.propertyName == "EXDATE")
  506.             {
  507.                 ritem = new CalRecurrenceDate();
  508.             } else {
  509.                 continue;
  510.             }
  511.  
  512.             ritem.icalProperty = recprop;
  513.  
  514.             if (!rec) {
  515.                 rec = new CalRecurrenceInfo();
  516.                 rec.item = this;
  517.             }
  518.  
  519.             rec.appendRecurrenceItem(ritem);
  520.         }
  521.         this.mRecurrenceInfo = rec;
  522.  
  523.         var alarmComp = icalcomp.getFirstSubcomponent("VALARM");
  524.         if (alarmComp) {
  525.             var triggerProp = alarmComp.getFirstProperty("TRIGGER");
  526.             var duration = Components.classes["@mozilla.org/calendar/duration;1"]
  527.                                      .createInstance(Components.interfaces.calIDuration);
  528.             duration.icalString = triggerProp.valueAsIcalString;
  529.  
  530.             if (duration.minutes) {
  531.                 this.setProperty("alarmLength", duration.minutes);
  532.                 this.setProperty("alarmUnits", "minutes");
  533.             } else if (duration.hours) {
  534.                 this.setProperty("alarmLength", duration.hours);
  535.                 this.setProperty("alarmUnits", "hours");
  536.             } else if (duration.days) {
  537.                 this.setProperty("alarmLength", duration.days);
  538.                 this.setProperty("alarmUnits", "days");
  539.             }
  540.  
  541.             var related = triggerProp.getParameter("RELATED");
  542.             if (related && related == "END")
  543.                 this.setProperty("alarmRelated", "END");
  544.             else
  545.                 this.setProperty("alarmRelated", "START");
  546.  
  547.             var email = alarmComp.getFirstProperty("X-EMAILADDRESS");
  548.             if (email)
  549.                 this.setProperty("alarmEmailAddress", email);
  550.         }
  551.     },
  552.  
  553.     importUnpromotedProperties: function (icalcomp, promoted) {
  554.         for (var prop = icalcomp.getFirstProperty("ANY");
  555.              prop;
  556.              prop = icalcomp.getNextProperty("ANY")) {
  557.             if (!promoted[prop.propertyName]) {
  558.                 // XXX keep parameters around, sigh
  559.                 this.setProperty(prop.propertyName, prop.value);
  560.             }
  561.         }
  562.     },
  563.  
  564.     isPropertyPromoted: function (name) {
  565.         return (this.itemBasePromotedProps[name]);
  566.     },
  567.  
  568.     get icalComponent() {
  569.         throw Components.results.NS_NOT_IMPLEMENTED;
  570.     },
  571.  
  572.     fillIcalComponentFromBase: function (icalcomp) {
  573.         // Make sure that the LMT and ST are updated
  574.         this.updateStampTime();
  575.         this.ensureNotDirty();
  576.  
  577.         this.mapPropsToICS(icalcomp, this.icsBasePropMap);
  578.  
  579.         if (this.mOrganizer)
  580.             icalcomp.addProperty(this.mOrganizer.icalProperty);
  581.         for (var i = 0; i < this.mAttendees.length; i++)
  582.             icalcomp.addProperty(this.mAttendees[i].icalProperty);
  583.  
  584.         if (this.mGeneration) {
  585.             var genprop = icalProp("X-MOZILLA-GENERATION");
  586.             genprop.value = String(this.mGeneration);
  587.             icalcomp.addProperty(genprop);
  588.         }
  589.  
  590.         if (this.mRecurrenceInfo) {
  591.             var ritems = this.mRecurrenceInfo.getRecurrenceItems({});
  592.             for (i in ritems) {
  593.                 icalcomp.addProperty(ritems[i].icalProperty);
  594.             }
  595.         }
  596.         
  597.         if (this.alarmTime) {
  598.             const icssvc = Components.classes["@mozilla.org/calendar/ics-service;1"]
  599.                                      .getService(Components.interfaces.calIICSService);
  600.             var alarmComp = icssvc.createIcalComponent("VALARM");
  601.  
  602.             var duration = Components.classes["@mozilla.org/calendar/duration;1"]
  603.                                      .createInstance(Components.interfaces.calIDuration);
  604.             duration.isNegative = true;
  605.             duration[this.getProperty("alarmUnits")] = this.getProperty("alarmLength");
  606.  
  607.             var triggerProp = icssvc.createIcalProperty("TRIGGER");
  608.             triggerProp.valueAsIcalString = duration.icalString;
  609.  
  610.             if (this.getProperty("alarmRelated") == "END") 
  611.                 triggerProp.setParameter("RELATED", "END");
  612.  
  613.             alarmComp.addProperty(triggerProp);
  614.  
  615.             if (this.getProperty("alarmEmailAddress")) {
  616.                 var emailProp = icssvc.createIcalProperty("X-EMAILADDRESS");
  617.                 emailProp.value = this.getProperty("alarmEmailAddress");
  618.                 alarmComp.addProperty(emailProp);
  619.             }
  620.  
  621.             icalcomp.addSubcomponent(alarmComp);
  622.         }
  623.     },
  624.     
  625.     getOccurrencesBetween: function(aStartDate, aEndDate, aCount) {
  626.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  627.     }
  628. };
  629.  
  630. makeMemberAttr(calItemBase, "X-MOZILLA-GENERATION", 0, "generation", true);
  631. makeMemberAttr(calItemBase, "CREATED", null, "creationDate", true);
  632. makeMemberAttr(calItemBase, "UID", null, "id", true);
  633. makeMemberAttr(calItemBase, "SUMMARY", null, "title", true);
  634. makeMemberAttr(calItemBase, "PRIORITY", 0, "priority", true);
  635. makeMemberAttr(calItemBase, "CLASS", "PUBLIC", "privacy", true);
  636. makeMemberAttr(calItemBase, "STATUS", null, "status", true);
  637. makeMemberAttr(calItemBase, "ALARMTIME", null, "alarmTime", true);
  638. makeMemberAttr(calItemBase, "RECURRENCE-ID", null, "recurrenceId", true);
  639.  
  640. makeMemberAttr(calItemBase, "mRecurrenceInfo", null, "recurrenceInfo");
  641. makeMemberAttr(calItemBase, "mAttachments", null, "attachments");
  642. makeMemberAttr(calItemBase, "mProperties", null, "properties");
  643.  
  644. function makeMemberAttr(ctor, varname, dflt, attr, asProperty)
  645. {
  646.     // XXX handle defaults!
  647.     var getter = function () {
  648.         if (asProperty)
  649.             return this.getProperty(varname);
  650.         else
  651.             return this[varname];
  652.     };
  653.     var setter = function (v) {
  654.         this.modify();
  655.         if (asProperty)
  656.             this.setProperty(varname, v);
  657.         else
  658.             this[varname] = v;
  659.     };
  660.     ctor.prototype.__defineGetter__(attr, getter);
  661.     ctor.prototype.__defineSetter__(attr, setter);
  662. }
  663.  
  664. //
  665. // helper functions
  666. //
  667.  
  668. function icalFromString(str)
  669. {
  670.     const icssvc = Components.classes["@mozilla.org/calendar/ics-service;1"].
  671.         getService(Components.interfaces.calIICSService);
  672.     return icssvc.parseICS(str);
  673. }
  674.  
  675. function icalProp(kind)
  676. {
  677.     const icssvc = Components.classes["@mozilla.org/calendar/ics-service;1"].
  678.         getService(Components.interfaces.calIICSService);
  679.     return icssvc.createIcalProperty(kind);
  680. }
  681.