diff options
Diffstat (limited to 'caldav.js')
-rw-r--r-- | caldav.js | 4812 |
1 files changed, 46 insertions, 4766 deletions
@@ -1,4612 +1,3 @@ -/* ical.js - https://github.com/kewisch/ical.js */ - -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -if (typeof(ICAL) === 'undefined') - (typeof(window) !== 'undefined') ? this.ICAL = {} : ICAL = {}; - -/** - * Helper functions used in various places within ical.js - */ -ICAL.helpers = { - initState: function initState(aLine, aLineNr) { - return { - buffer: aLine, - line: aLine, - lineNr: aLineNr, - character: 0, - currentData: null, - parentData: [] - }; - }, - - initComponentData: function initComponentData(aName) { - return { - name: aName, - type: "COMPONENT", - value: [] - }; - }, - - dumpn: function() { - if (!ICAL.debug) { - return; - } - - if (typeof (console) !== 'undefined' && 'log' in console) { - ICAL.helpers.dumpn = function consoleDumpn(input) { - return console.log(input); - } - } else { - ICAL.helpers.dumpn = function geckoDumpn(input) { - dump(input + '\n'); - } - } - - return ICAL.helpers.dumpn(arguments[0]); - }, - - mixin: function(obj, data) { - if (data) { - for (var k in data) { - obj[k] = data[k]; - } - } - return obj; - }, - - isArray: function(o) { - return o && (o instanceof Array || typeof o == "array"); - }, - - clone: function(aSrc, aDeep) { - if (!aSrc || typeof aSrc != "object") { - return aSrc; - } else if (aSrc instanceof Date) { - return new Date(aSrc.getTime()); - } else if ("clone" in aSrc) { - return aSrc.clone(); - } else if (ICAL.helpers.isArray(aSrc)) { - var result = []; - for (var i = 0; i < aSrc.length; i++) { - result.push(aDeep ? ICAL.helpers.clone(aSrc[i], true) : aSrc[i]); - } - return result; - } else { - var result = {}; - for (var name in aSrc) { - if (aSrc.hasOwnProperty(name)) { - dump("Cloning " + name + "\n"); - if (aDeep) { - result[name] = ICAL.helpers.clone(aSrc[name], true); - } else { - result[name] = aSrc[name]; - } - } - } - return result; - } - }, - - unfoldline: function unfoldline(aState) { - // Section 3.1 - // if the line ends with a CRLF - // and the next line starts with a LINEAR WHITESPACE (space, htab, ...) - - // then remove the CRLF and the whitespace to unsplit the line - var moreLines = true; - var line = ""; - - while (moreLines) { - moreLines = false; - var pos = aState.buffer.search(/\r?\n/); - if (pos > -1) { - var len = (aState.buffer[pos] == "\r" ? 2 : 1); - var nextChar = aState.buffer.substr(pos + len, 1); - if (nextChar.match(/^[ \t]$/)) { - moreLines = true; - line += aState.buffer.substr(0, pos); - aState.buffer = aState.buffer.substr(pos + len + 1); - } else { - // We're at the end of the line, copy the found chunk - line += aState.buffer.substr(0, pos); - aState.buffer = aState.buffer.substr(pos + len); - } - } else { - line += aState.buffer; - aState.buffer = ""; - } - } - return line; - }, - - foldline: function foldline(aLine) { - var result = ""; - var line = aLine || ""; - - while (line.length) { - result += ICAL.newLineChar + " " + line.substr(0, ICAL.foldLength); - line = line.substr(ICAL.foldLength); - } - return result.substr(ICAL.newLineChar.length + 1); - }, - - ensureKeyExists: function(obj, key, defvalue) { - if (!(key in obj)) { - obj[key] = defvalue; - } - }, - - hasKey: function(obj, key) { - return (obj && key in obj && obj[key]); - }, - - pad2: function pad(data) { - return ("00" + data).substr(-2); - }, - - trunc: function trunc(number) { - return (number < 0 ? Math.ceil(number) : Math.floor(number)); - } -}; -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; - -(function() { - ICAL.serializer = { - serializeToIcal: function(obj, name, isParam) { - if (obj && obj.icalclass) { - return obj.toString(); - } - - var str = ""; - - if (obj.type == "COMPONENT") { - str = "BEGIN:" + obj.name + ICAL.newLineChar; - for (var subkey in obj.value) { - str += this.serializeToIcal(obj.value[subkey]) + ICAL.newLineChar; - } - str += "END:" + obj.name; - } else { - str += ICAL.icalparser.stringifyProperty(obj); - } - return str; - } - }; -}()); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -// TODO validate known parameters -// TODO make sure all known types don't contain junk -// TODO tests for parsers -// TODO SAX type parser -// TODO structure data in components -// TODO enforce uppercase when parsing -// TODO optionally preserve value types that are default but explicitly set -// TODO floating timezone -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function() { - /* NOTE: I'm not sure this is the latest syntax... - - { - X-WR-CALNAME: "test", - components: { - VTIMEZONE: { ... }, - VEVENT: { - "uuid1": { - UID: "uuid1", - ... - components: { - VALARM: [ - ... - ] - } - } - }, - VTODO: { ... } - } - } - */ - - // Exports - - function ParserError(aState, aMessage) { - this.mState = aState; - this.name = "ParserError"; - if (aState) { - var lineNrData = ("lineNr" in aState ? aState.lineNr + ":" : "") + - ("character" in aState && !isNaN(aState.character) ? - aState.character + ":" : - ""); - - var message = lineNrData + aMessage; - if ("buffer" in aState) { - if (aState.buffer) { - message += " before '" + aState.buffer + "'"; - } else { - message += " at end of line"; - } - } - if ("line" in aState) { - message += " in '" + aState.line + "'"; - } - this.message = message; - } else { - this.message = aMessage; - } - - // create stack - try { - throw new Error(); - } catch (e) { - var split = e.stack.split('\n'); - split.shift(); - this.stack = split.join('\n'); - } - } - - ParserError.prototype = { - __proto__: Error.prototype, - constructor: ParserError - }; - - var parser = {}; - ICAL.icalparser = parser; - - parser.lexContentLine = function lexContentLine(aState) { - // contentline = name *(";" param ) ":" value CRLF - // The corresponding json object will be: - // { name: "name", parameters: { key: "value" }, value: "value" } - var lineData = {}; - - // Parse the name - lineData.name = parser.lexName(aState); - - // Read Paramaters, if there are any. - if (aState.buffer.substr(0, 1) == ";") { - lineData.parameters = {}; - while (aState.buffer.substr(0, 1) == ";") { - aState.buffer = aState.buffer.substr(1); - var param = parser.lexParam(aState); - lineData.parameters[param.name] = param.value; - } - } - - // Read the value - parser.expectRE(aState, /^:/, "Expected ':'"); - lineData.value = parser.lexValue(aState); - parser.expectEnd(aState, "Junk at End of Line"); - return lineData; - }; - - parser.lexName = function lexName(aState) { - function parseIanaToken(aState) { - var match = parser.expectRE(aState, /^([A-Za-z0-9-]+)/, - "Expected IANA Token"); - return match[1]; - } - - function parseXName(aState) { - var error = "Expected XName"; - var value = "X-"; - var match = parser.expectRE(aState, /^X-/, error); - - // Vendor ID - if (match = parser.expectOptionalRE(aState, /^([A-Za-z0-9]+-)/, error)) { - value += match[1]; - } - - // Remaining part - match = parser.expectRE(aState, /^([A-Za-z0-9-]+)/, error); - value += match[1]; - - return value; - } - return parser.parseAlternative(aState, parseXName, parseIanaToken); - }; - - parser.lexValue = function lexValue(aState) { - // VALUE-CHAR = WSP / %x21-7E / NON-US-ASCII - // ; Any textual character - - if (aState.buffer.length === 0) { - return aState.buffer; - } - - // TODO the unicode range might be wrong! - var match = parser.expectRE(aState, - /* WSP|%x21-7E|NON-US-ASCII */ - /^([ \t\x21-\x7E\u00C2-\uF400]+)/, - "Invalid Character in value"); - - return match[1]; - }; - - parser.lexParam = function lexParam(aState) { - // read param name - var name = parser.lexName(aState); - parser.expectRE(aState, /^=/, "Expected '='"); - - // read param value - var values = parser.parseList(aState, parser.lexParamValue, ","); - return { - name: name, - value: (values.length == 1 ? values[0] : values) - }; - }; - - parser.lexParamValue = function lexParamValue(aState) { - // CONTROL = %x00-08 / %x0A-1F / %x7F - // ; All the controls except HTAB - function parseQuotedString(aState) { - parser.expectRE(aState, /^"/, "Expecting Quote Character"); - // QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII - // ; Any character except CONTROL and DQUOTE - - var match = parser.expectRE(aState, /^([^"\x00-\x08\x0A-\x1F\x7F]*)/, - "Invalid Param Value"); - parser.expectRE(aState, /^"/, "Expecting Quote Character"); - return match[1]; - } - - function lexParamText(aState) { - // SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-US-ASCII - // ; Any character except CONTROL, DQUOTE, ";", ":", "," - var match = parser.expectRE(aState, /^([^";:,\x00-\x08\x0A-\x1F\x7F]*)/, - "Invalid Param Value"); - return match[1]; - } - - return parser.parseAlternative(aState, parseQuotedString, lexParamText); - }; - - parser.parseContentLine = function parseContentLine(aState, aLineData) { - - switch (aLineData.name) { - case "BEGIN": - var newdata = ICAL.helpers.initComponentData(aLineData.value); - if (aState.currentData) { - // If there is already data (i.e this is not the top level - // component), then push the new data to its values and - // stack the parent data. - aState.currentData.value.push(newdata); - aState.parentData.push(aState.currentData); - } - - aState.currentData = newdata; // set the new data array - break; - case "END": - if (aState.currentData.name != aLineData.value) { - throw new ParserError(aState, "Unexpected END:" + aLineData.value + - ", expected END:" + aState.currentData.name); - } - if (aState.parentData.length) { - aState.currentData = aState.parentData.pop(); - } - break; - default: - ICAL.helpers.dumpn("parse " + aLineData.toString()); - parser.detectParameterType(aLineData); - parser.detectValueType(aLineData); - ICAL.helpers.dumpn("parse " + aLineData.toString()); - aState.currentData.value.push(aLineData); - break; - } - }, - - parser.detectParameterType = function detectParameterType(aLineData) { - for (var name in aLineData.parameters) { - var paramType = "TEXT"; - - if (name in ICAL.design.param && "valueType" in ICAL.design.param[name]) { - paramType = ICAL.design.param[name].valueType; - } - var paramData = { - value: aLineData.parameters[name], - type: paramType - }; - - aLineData.parameters[name] = paramData; - } - }; - - parser.detectValueType = function detectValueType(aLineData) { - var valueType = "TEXT"; - var defaultType = null; - if (aLineData.name in ICAL.design.property && - "defaultType" in ICAL.design.property[aLineData.name]) { - valueType = ICAL.design.property[aLineData.name].defaultType; - } - - if ("parameters" in aLineData && "VALUE" in aLineData.parameters) { - ICAL.helpers.dumpn("VAAAA: " + aLineData.parameters.VALUE.toString()); - valueType = aLineData.parameters.VALUE.value.toUpperCase(); - } - - if (!(valueType in ICAL.design.value)) { - throw new ParserError(aLineData, "Invalid VALUE Type '" + valueType); - } - - aLineData.type = valueType; - - // It could be a multi-value value, we have to take that apart first - function unwrapMultiValue(x, separator) { - var values = []; - - function replacer(s, a) { - values.push(a); - return ""; - } - var re = new RegExp("(.*?[^\\\\])" + separator, "g"); - values.push(x.replace(re, replacer)); - return values; - } - - if (aLineData.name in ICAL.design.property) { - if (ICAL.design.property[aLineData.name].multiValue) { - aLineData.value = unwrapMultiValue(aLineData.value, ","); - } else if (ICAL.design.property[aLineData.name].structuredValue) { - aLineData.value = unwrapMultiValue(aLineData.value, ";"); - } else { - aLineData.value = [aLineData.value]; - } - } else { - aLineData.value = [aLineData.value]; - } - - if ("unescape" in ICAL.design.value[valueType]) { - var unescaper = ICAL.design.value[valueType].unescape; - for (var idx in aLineData.value) { - aLineData.value[idx] = unescaper(aLineData.value[idx], aLineData.name); - } - } - - return aLineData; - } - - parser.validateValue = function validateValue(aLineData, aValueType, - aValue, aCheckParams) { - var propertyData = ICAL.design.property[aLineData.name]; - var valueData = ICAL.design.value[aValueType]; - - // TODO either make validators just consume the value, then check for end - // here (possibly requires returning remainder or renaming buffer<->value - // in the states) validators don't really need the whole linedata - - if (!aValue.match) { - ICAL.helpers.dumpn("MAAA: " + aValue + " ? " + aValue.toString()); - } - - if (valueData.matches) { - // Test against regex - if (!aValue.match(valueData.matches)) { - throw new ParserError(aLineData, "Value '" + aValue + "' for " + - aLineData.name + " is not " + aValueType); - } - } else if ("validate" in valueData) { - // Validator throws an error itself if needed - var objData = valueData.validate(aValue); - - // Merge in extra value data, if it exists - ICAL.helpers.mixin(aLineData, objData); - } else if ("values" in valueData) { - // Fixed list of values - if (valueData.values.indexOf(aValue) < 0) { - throw new ParserError(aLineData, "Value for " + aLineData.name + - " is not a " + aValueType); - } - } - - if (aCheckParams && "requireParam" in valueData) { - var reqParam = valueData.requireParam; - for (var param in reqParam) { - if (!("parameters" in aLineData) || - !(param in aLineData.parameters) || - aLineData.parameters[param].value != reqParam[param]) { - - throw new ParserError(aLineData, "Value requires " + param + "=" + - valueData.requireParam[param]); - } - } - } - - return aLineData; - }; - - parser.parseValue = function parseValue(aStr, aType) { - var lineData = { - value: [aStr] - }; - return parser.validateValue(lineData, aType, aStr, false); - }; - - parser.decorateValue = function decorateValue(aType, aValue) { - if (aType in ICAL.design.value && "decorate" in ICAL.design.value[aType]) { - return ICAL.design.value[aType].decorate(aValue); - } else { - return ICAL.design.value.TEXT.decorate(aValue); - } - }; - - parser.stringifyProperty = function stringifyProperty(aLineData) { - ICAL.helpers.dumpn("Stringify: " + aLineData.toString()); - var str = aLineData.name; - if (aLineData.parameters) { - for (var key in aLineData.parameters) { - str += ";" + key + "=" + aLineData.parameters[key].value; - } - } - - str += ":" + parser.stringifyValue(aLineData); - - return ICAL.helpers.foldline(str); - }; - - parser.stringifyValue = function stringifyValue(aLineData) { - function arrayStringMap(arr, func) { - var newArr = []; - for (var idx in arr) { - newArr[idx] = func(arr[idx].toString()); - } - return newArr; - } - - if (aLineData) { - var values = aLineData.value; - if (aLineData.type in ICAL.design.value && - "escape" in ICAL.design.value[aLineData.type]) { - var escaper = ICAL.design.value[aLineData.type].escape; - values = arrayStringMap(values, escaper); - } - - var separator = ","; - if (aLineData.name in ICAL.design.property && - ICAL.design.property[aLineData.name].structuredValue) { - separator = ";"; - } - - return values.join(separator); - } else { - return null; - } - }; - - parser.parseDateOrDateTime = function parseDateOrDateTime(aState) { - var data = parser.parseDate(aState); - - if (parser.expectOptionalRE(aState, /^T/)) { - // This has a time component, parse it - var time = parser.parseTime(aState); - - if (parser.expectOptionalRE(aState, /^Z/)) { - data.timezone = "Z"; - } - ICAL.helpers.mixin(data, time); - } - return data; - }; - - parser.parseDateTime = function parseDateTime(aState) { - var data = parser.parseDate(aState); - parser.expectRE(aState, /^T/, "Expected 'T'"); - - var time = parser.parseTime(aState); - - if (parser.expectOptionalRE(aState, /^Z/)) { - data.timezone = "Z"; - } - - ICAL.helpers.mixin(data, time); - return data; - }; - - parser.parseDate = function parseDate(aState) { - var dateRE = /^((\d{4})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01]))/; - var match = parser.expectRE(aState, dateRE, "Expected YYYYMMDD Date"); - return { - year: parseInt(match[2], 10), - month: parseInt(match[3], 10), - day: parseInt(match[4], 10) - }; - // TODO timezone? - }; - - parser.parseTime = function parseTime(aState) { - var timeRE = /^(([01][0-9]|2[0-3])([0-5][0-9])([0-5][0-9]|60))/; - var match = parser.expectRE(aState, timeRE, "Expected HHMMSS Time"); - return { - hour: parseInt(match[2], 10), - minute: parseInt(match[3], 10), - second: parseInt(match[4], 10) - }; - }; - - parser.parseDuration = function parseDuration(aState) { - var error = "Expected Duration Value"; - - function parseDurSecond(aState) { - var secMatch = parser.expectRE(aState, /^((\d+)S)/, "Expected Seconds"); - return { - seconds: parseInt(secMatch[2], 10) - }; - } - - function parseDurMinute(aState) { - var data = {}; - var minutes = parser.expectRE(aState, /^((\d+)M)/, "Expected Minutes"); - try { - data = parseDurSecond(aState); - } catch (e) { - // seconds are optional, its ok - if (!(e instanceof ParserError)) { - throw e; - } - } - data.minutes = parseInt(minutes[2], 10); - return data; - } - - function parseDurHour(aState) { - var data = {}; - var hours = parser.expectRE(aState, /^((\d+)H)/, "Expected Hours"); - try { - data = parseDurMinute(aState); - } catch (e) { - // seconds are optional, its ok - if (!(e instanceof ParserError)) { - throw e; - } - } - - data.hours = parseInt(hours[2], 10); - return data; - } - - function parseDurWeek(aState) { - return { - weeks: parser.expectRE(aState, /^((\d+)W)/, "Expected Weeks")[2] - }; - } - - function parseDurTime(aState) { - parser.expectRE(aState, /^T/, "Expected Time Value"); - return parser.parseAlternative(aState, parseDurHour, - parseDurMinute, parseDurSecond); - } - - function parseDurDate(aState) { - var days = parser.expectRE(aState, /^((\d+)D)/, "Expected Days"); - var data; - - try { - data = parseDurTime(aState); - } catch (e) { - // Its ok if this fails - if (!(e instanceof ParserError)) { - throw e; - } - } - - if (data) { - data.days = days[2]; - } else { - data = { - days: parseInt(days[2], 10) - }; - } - return data; - } - - var factor = parser.expectRE(aState, /^([+-]?P)/, error); - - var durData = parser.parseAlternative(aState, parseDurDate, - parseDurTime, parseDurWeek); - parser.expectEnd(aState, "Junk at end of DURATION value"); - - durData.factor = (factor[1] == "-P" ? -1 : 1); - return durData; - }; - - parser.parsePeriod = function parsePeriod(aState) { - var dtime = parser.parseDateTime(aState); - parser.expectRE(aState, /\//, "Expected '/'"); - - var dtdur = parser.parseAlternative(aState, parser.parseDateTime, - parser.parseDuration); - var data = { - start: dtime - }; - if ("factor" in dtdur) { - data.duration = dtdur; - } else { - data.end = dtdur; - } - return data; - }, - - parser.parseRecur = function parseRecur(aState) { - // TODO this function is quite cludgy, maybe it should be done differently - function parseFreq(aState) { - parser.expectRE(aState, /^FREQ=/, "Expected Frequency"); - var ruleRE = /^(SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY)/; - var match = parser.expectRE(aState, ruleRE, "Exepected Frequency Value"); - return { - "FREQ": match[1] - }; - } - - function parseUntil(aState) { - parser.expectRE(aState, /^UNTIL=/, "Expected Frequency"); - var untilDate = parser.parseDateOrDateTime(aState); - return { - "UNTIL": untilDate - }; - } - - function parseCount(aState) { - parser.expectRE(aState, /^COUNT=/, "Expected Count"); - var match = parser.expectRE(aState, /^(\d+)/, "Expected Digit(s)"); - return { - "COUNT": parseInt(match[1], 10) - }; - } - - function parseInterval(aState) { - parser.expectRE(aState, /^INTERVAL=/, "Expected Interval"); - var match = parser.expectRE(aState, /^(\d+)/, "Expected Digit(s)"); - return { - "INTERVAL": parseInt(match[1], 10) - }; - } - - function parseBySecond(aState) { - function parseSecond(aState) { - var secondRE = /^(60|[1-5][0-9]|[0-9])/; - var value = parser.expectRE(aState, secondRE, "Expected Second")[1]; - return parseInt(value, 10); - } - parser.expectRE(aState, /^BYSECOND=/, "Expected BYSECOND"); - var seconds = parser.parseList(aState, parseSecond, ","); - return { - "BYSECOND": seconds - }; - } - - function parseByMinute(aState) { - function parseMinute(aState) { - var minuteRE = /^([1-5][0-9]|[0-9])/; - var value = parser.expectRE(aState, minuteRE, "Expected Minute")[1]; - return parseInt(value, 10); - } - parser.expectRE(aState, /^BYMINUTE=/, "Expected BYMINUTE"); - var minutes = parser.parseList(aState, parseMinute, ","); - return { - "BYMINUTE": minutes - }; - } - - function parseByHour(aState) { - function parseHour(aState) { - var hourRE = /^(2[0-3]|1[0-9]|[0-9])/; - var value = parser.expectRE(aState, hourRE, "Expected Hour")[1]; - return parseInt(value, 10); - } - parser.expectRE(aState, /^BYHOUR=/, "Expected BYHOUR"); - var hours = parser.parseList(aState, parseHour, ","); - return { - "BYHOUR": hours - }; - } - - function parseByDay(aState) { - function parseWkDayNum(aState) { - var value = ""; - var match = parser.expectOptionalRE(aState, /^([+-])/); - if (match) { - value += match[1]; - } - - match = parser.expectOptionalRE(aState, /^(5[0-3]|[1-4][0-9]|[1-9])/); - if (match) { - value += match[1]; - } - - var wkDayRE = /^(SU|MO|TU|WE|TH|FR|SA)/; - match = parser.expectRE(aState, wkDayRE, "Expected Week Ordinals"); - value += match[1]; - return value; - } - parser.expectRE(aState, /^BYDAY=/, "Expected BYDAY Rule"); - var wkdays = parser.parseList(aState, parseWkDayNum, ","); - return { - "BYDAY": wkdays - }; - } - - function parseByMonthDay(aState) { - function parseMoDayNum(aState) { - var value = ""; - var match = parser.expectOptionalRE(aState, /^([+-])/); - if (match) { - value += match[1]; - } - - match = parser.expectRE(aState, /^(3[01]|[12][0-9]|[1-9])/); - value += match[1]; - return parseInt(value, 10); - } - parser.expectRE(aState, /^BYMONTHDAY=/, "Expected BYMONTHDAY Rule"); - var modays = parser.parseList(aState, parseMoDayNum, ","); - return { - "BYMONTHDAY": modays - }; - } - - function parseByYearDay(aState) { - function parseYearDayNum(aState) { - var value = ""; - var match = parser.expectOptionalRE(aState, /^([+-])/); - if (match) { - value += match[1]; - } - - var yrDayRE = /^(36[0-6]|3[0-5][0-9]|[12][0-9][0-9]|[1-9][0-9]|[1-9])/; - match = parser.expectRE(aState, yrDayRE); - value += match[1]; - return parseInt(value, 10); - } - parser.expectRE(aState, /^BYYEARDAY=/, "Expected BYYEARDAY Rule"); - var yrdays = parser.parseList(aState, parseYearDayNum, ","); - return { - "BYYEARDAY": yrdays - }; - } - - function parseByWeekNo(aState) { - function parseWeekNum(aState) { - var value = ""; - var match = parser.expectOptionalRE(aState, /^([+-])/); - if (match) { - value += match[1]; - } - - match = parser.expectRE(aState, /^(5[0-3]|[1-4][0-9]|[1-9])/); - value += match[1]; - return parseInt(value, 10); - } - parser.expectRE(aState, /^BYWEEKNO=/, "Expected BYWEEKNO Rule"); - var weeknos = parser.parseList(aState, parseWeekNum, ","); - return { - "BYWEEKNO": weeknos - }; - } - - function parseByMonth(aState) { - function parseMonthNum(aState) { - var moNumRE = /^(1[012]|[1-9])/; - var match = parser.expectRE(aState, moNumRE, "Expected Month number"); - return parseInt(match[1], 10); - } - parser.expectRE(aState, /^BYMONTH=/, "Expected BYMONTH Rule"); - var monums = parser.parseList(aState, parseMonthNum, ","); - return { - "BYMONTH": monums - }; - } - - function parseBySetPos(aState) { - function parseSpList(aState) { - var spRE = /^(36[0-6]|3[0-5][0-9]|[12][0-9][0-9]|[1-9][0-9]|[1-9])/; - var value = parser.expectRE(aState, spRE)[1]; - - return parseInt(value, 10); - } - parser.expectRE(aState, /^BYSETPOS=/, "Expected BYSETPOS Rule"); - var spnums = parser.parseList(aState, parseSpList, ","); - return { - "BYSETPOS": spnums - }; - } - - function parseWkst(aState) { - parser.expectRE(aState, /^WKST=/, "Expected WKST"); - var wkstRE = /^(SU|MO|TU|WE|TH|FR|SA)/; - var match = parser.expectRE(aState, wkstRE, "Expected Weekday Name"); - return { - "WKST": match[1] - }; - } - - function parseRulePart(aState) { - return parser.parseAlternative(aState, - parseFreq, parseUntil, parseCount, parseInterval, - parseBySecond, parseByMinute, parseByHour, parseByDay, - parseByMonthDay, parseByYearDay, parseByWeekNo, - parseByMonth, parseBySetPos, parseWkst); - } - - // One or more rule parts - var value = parser.parseList(aState, parseRulePart, ";"); - var data = {}; - for (var key in value) { - ICAL.helpers.mixin(data, value[key]); - } - - // Make sure there's no junk at the end - parser.expectEnd(aState, "Junk at end of RECUR value"); - return data; - }; - - parser.parseUtcOffset = function parseUtcOffset(aState) { - var utcRE = /^(([+-])([01][0-9]|2[0-3])([0-5][0-9])([0-5][0-9])?)$/; - var match = parser.expectRE(aState, utcRE, "Expected valid utc offset"); - return { - factor: (match[2] == "-" ? -1 : 1), - hours: parseInt(match[3], 10), - minutes: parseInt(match[4], 10) - }; - }; - - parser.parseAlternative = function parseAlternative(aState /*, parserFunc, ... */) { - var tokens = null; - var args = Array.prototype.slice.call(arguments); - var parser; - args.shift(); - var errors = []; - - while (!tokens && (parser = args.shift())) { - try { - tokens = parser(aState); - } catch (e) { - if (e instanceof ParserError) { - errors.push(e); - tokens = null; - } else { - throw e; - } - } - } - - if (!tokens) { - var message = errors.join("\nOR ") || "No Tokens found"; - throw new ParserError(aState, message); - } - - return tokens; - }, - - parser.parseList = function parseList(aState, aElementFunc, aSeparator) { - var listvals = []; - - listvals.push(aElementFunc(aState)); - var re = new RegExp("^" + aSeparator + ""); - while (parser.expectOptionalRE(aState, re)) { - listvals.push(aElementFunc(aState)); - } - return listvals; - }; - - parser.expectOptionalRE = function expectOptionalRE(aState, aRegex) { - var match = aState.buffer.match(aRegex); - if (match) { - var count = ("1" in match ? match[1].length : match[0].length); - aState.buffer = aState.buffer.substr(count); - aState.character += count; - } - return match; - }; - - parser.expectRE = function expectRE(aState, aRegex, aErrorMessage) { - var match = parser.expectOptionalRE(aState, aRegex); - if (!match) { - throw new ParserError(aState, aErrorMessage); - } - return match; - }; - - parser.expectEnd = function expectEnd(aState, aErrorMessage) { - if (aState.buffer.length > 0) { - throw new ParserError(aState, aErrorMessage); - } - } - - /* Possible shortening: - - pro: retains order - - con: datatypes not obvious - - pro: not so many objects created - - { - "begin:vcalendar": [ - { - prodid: "-//Example Inc.//Example Client//EN", - version: "2.0" - "begin:vtimezone": [ - { - "last-modified": [{ - type: "date-time", - value: "2004-01-10T03:28:45Z" - }], - tzid: "US/Eastern" - "begin:daylight": [ - { - dtstart: { - type: "date-time", - value: "2000-04-04T02:00:00" - } - rrule: { - type: "recur", - value: { - freq: "YEARLY", - byday: ["1SU"], - bymonth: ["4"], - } - } - } - ] - } - ], - "begin:vevent": [ - { - category: [{ - type: "text" - // have icalcomponent take apart the multivalues - value: "multi1,multi2,multi3" - },{ - type "text" - value: "otherprop1" - }] - } - ] - } - ] - } - */ -})(); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; - -/** - * Design data used by the parser to decide if data is semantically correct - */ -ICAL.design = { - param: { - // Although the syntax is DQUOTE uri DQUOTE, I don't think we should - // enfoce anything aside from it being a valid content line. - // "ALTREP": { ... }, - - // CN just wants a param-value - // "CN": { ... } - - "CUTYPE": { - values: ["INDIVIDUAL", "GROUP", "RESOURCE", "ROOM", "UNKNOWN"], - allowXName: true, - allowIanaToken: true - }, - - "DELEGATED-FROM": { - valueType: "CAL-ADDRESS", - multiValue: true - }, - "DELEGATED-TO": { - valueType: "CAL-ADDRESS", - multiValue: true - }, - // "DIR": { ... }, // See ALTREP - "ENCODING": { - values: ["8BIT", "BASE64"] - }, - // "FMTTYPE": { ... }, // See ALTREP - "FBTYPE": { - values: ["FREE", "BUSY", "BUSY-UNAVAILABLE", "BUSY-TENTATIVE"], - allowXName: true, - allowIanaToken: true - }, - // "LANGUAGE": { ... }, // See ALTREP - "MEMBER": { - valueType: "CAL-ADDRESS", - multiValue: true - }, - "PARTSTAT": { - // TODO These values are actually different per-component - values: ["NEEDS-ACTION", "ACCEPTED", "DECLINED", "TENTATIVE", - "DELEGATED", "COMPLETED", "IN-PROCESS"], - allowXName: true, - allowIanaToken: true - }, - "RANGE": { - values: ["THISANDFUTURE"] - }, - "RELATED": { - values: ["START", "END"] - }, - "RELTYPE": { - values: ["PARENT", "CHILD", "SIBLING"], - allowXName: true, - allowIanaToken: true - }, - "ROLE": { - values: ["REQ-PARTICIPANT", "CHAIR", - "OPT-PARTICIPANT", "NON-PARTICIPANT"], - allowXName: true, - allowIanaToken: true - }, - "RSVP": { - valueType: "BOOLEAN" - }, - "SENT-BY": { - valueType: "CAL-ADDRESS" - }, - "TZID": { - matches: /^\// - }, - "VALUE": { - values: ["BINARY", "BOOLEAN", "CAL-ADDRESS", "DATE", "DATE-TIME", - "DURATION", "FLOAT", "INTEGER", "PERIOD", "RECUR", "TEXT", - "TIME", "URI", "UTC-OFFSET"], - allowXName: true, - allowIanaToken: true - } - }, - - // When adding a value here, be sure to add it to the parameter types! - value: { - - "BINARY": { - matches: /^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/, - requireParam: { - "ENCODING": "BASE64" - }, - decorate: function(aString) { - return ICAL.icalbinary.fromString(aString); - } - }, - "BOOLEAN": { - values: ["TRUE", "FALSE"], - decorate: function(aValue) { - return ICAL.icalvalue.fromString(aValue, "BOOLEAN"); - } - }, - "CAL-ADDRESS": { - // needs to be an uri - }, - "DATE": { - validate: function(aValue) { - var state = { - buffer: aValue - }; - var data = ICAL.icalparser.parseDate(state); - ICAL.icalparser.expectEnd(state, "Junk at end of DATE value"); - return data; - }, - decorate: function(aValue) { - return ICAL.icaltime.fromString(aValue); - } - }, - "DATE-TIME": { - validate: function(aValue) { - var state = { - buffer: aValue - }; - var data = ICAL.icalparser.parseDateTime(state); - ICAL.icalparser.expectEnd(state, "Junk at end of DATE-TIME value"); - return data; - }, - - decorate: function(aValue) { - return ICAL.icaltime.fromString(aValue); - } - }, - "DURATION": { - validate: function(aValue) { - var state = { - buffer: aValue - }; - var data = ICAL.icalparser.parseDuration(state); - ICAL.icalparser.expectEnd(state, "Junk at end of DURATION value"); - return data; - }, - decorate: function(aValue) { - return ICAL.icalduration.fromString(aValue); - } - }, - "FLOAT": { - matches: /^[+-]?\d+\.\d+$/, - decorate: function(aValue) { - return ICAL.icalvalue.fromString(aValue, "FLOAT"); - } - }, - "INTEGER": { - matches: /^[+-]?\d+$/, - decorate: function(aValue) { - return ICAL.icalvalue.fromString(aValue, "INTEGER"); - } - }, - "PERIOD": { - validate: function(aValue) { - var state = { - buffer: aValue - }; - var data = ICAL.icalparser.parsePeriod(state); - ICAL.icalparser.expectEnd(state, "Junk at end of PERIOD value"); - return data; - }, - - decorate: function(aValue) { - return ICAL.icalperiod.fromString(aValue); - } - }, - "RECUR": { - validate: function(aValue) { - var state = { - buffer: aValue - }; - var data = ICAL.icalparser.parseRecur(state); - ICAL.icalparser.expectEnd(state, "Junk at end of RECUR value"); - return data; - }, - - decorate: function decorate(aValue) { - return ICAL.icalrecur.fromString(aValue); - } - }, - - "TEXT": { - matches: /.*/, - decorate: function(aValue) { - return ICAL.icalvalue.fromString(aValue, "TEXT"); - }, - unescape: function(aValue, aName) { - return aValue.replace(/\\\\|\\;|\\,|\\[Nn]/g, function(str) { - switch (str) { - case "\\\\": - return "\\"; - case "\\;": - return ";"; - case "\\,": - return ","; - case "\\n": - case "\\N": - return "\n"; - default: - return str; - } - }); - }, - - escape: function escape(aValue, aName) { - return aValue.replace(/\\|;|,|\n/g, function(str) { - switch (str) { - case "\\": - return "\\\\"; - case ";": - return "\\;"; - case ",": - return "\\,"; - case "\n": - return "\\n"; - default: - return str; - } - }); - } - }, - - "TIME": { - validate: function(aValue) { - var state = { - buffer: aValue - }; - var data = ICAL.icalparser.parseTime(state); - ICAL.icalparser.expectEnd(state, "Junk at end of TIME value"); - return data; - } - }, - - "URI": { - // TODO - /* ... */ - }, - - "UTC-OFFSET": { - validate: function(aValue) { - var state = { - buffer: aValue - }; - var data = ICAL.icalparser.parseUtcOffset(state); - ICAL.icalparser.expectEnd(state, "Junk at end of UTC-OFFSET value"); - return data; - }, - - decorate: function(aValue) { - return ICAL.icalutcoffset.fromString(aValue); - } - } - }, - - property: { - decorate: function decorate(aData, aParent) { - return new ICAL.icalproperty(aData, aParent); - }, - "ATTACH": { - defaultType: "URI" - }, - "ATTENDEE": { - defaultType: "CAL-ADDRESS" - }, - "CATEGORIES": { - defaultType: "TEXT", - multiValue: true - }, - "COMPLETED": { - defaultType: "DATE-TIME" - }, - "CREATED": { - defaultType: "DATE-TIME" - }, - "DTEND": { - defaultType: "DATE-TIME", - allowedTypes: ["DATE-TIME", "DATE"] - }, - "DTSTAMP": { - defaultType: "DATE-TIME" - }, - "DTSTART": { - defaultType: "DATE-TIME", - allowedTypes: ["DATE-TIME", "DATE"] - }, - "DUE": { - defaultType: "DATE-TIME", - allowedTypes: ["DATE-TIME", "DATE"] - }, - "DURATION": { - defaultType: "DURATION" - }, - "EXDATE": { - defaultType: "DATE-TIME", - allowedTypes: ["DATE-TIME", "DATE"] - }, - "EXRULE": { - defaultType: "RECUR" - }, - "FREEBUSY": { - defaultType: "PERIOD", - multiValue: true - }, - "GEO": { - defaultType: "FLOAT", - structuredValue: true - }, - /* TODO exactly 2 values */"LAST-MODIFIED": { - defaultType: "DATE-TIME" - }, - "ORGANIZER": { - defaultType: "CAL-ADDRESS" - }, - "PERCENT-COMPLETE": { - defaultType: "INTEGER" - }, - "REPEAT": { - defaultType: "INTEGER" - }, - "RDATE": { - defaultType: "DATE-TIME", - allowedTypes: ["DATE-TIME", "DATE", "PERIOD"] - }, - "RECURRENCE-ID": { - defaultType: "DATE-TIME", - allowedTypes: ["DATE-TIME", "DATE"] - }, - "RESOURCES": { - defaultType: "TEXT", - multiValue: true - }, - "REQUEST-STATUS": { - defaultType: "TEXT", - structuredValue: true - }, - "PRIORITY": { - defaultType: "INTEGER" - }, - "RRULE": { - defaultType: "RECUR" - }, - "SEQUENCE": { - defaultType: "INTEGER" - }, - "TRIGGER": { - defaultType: "DURATION", - allowedTypes: ["DURATION", "DATE-TIME"] - }, - "TZOFFSETFROM": { - defaultType: "UTC-OFFSET" - }, - "TZOFFSETTO": { - defaultType: "UTC-OFFSET" - }, - "TZURL": { - defaultType: "URI" - }, - "URL": { - defaultType: "URI" - } - }, - - component: { - decorate: function decorate(aData, aParent) { - return new ICAL.icalcomponent(aData, aParent); - }, - "VEVENT": {} - } -}; -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -"use strict"; - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function() { - ICAL.icalcomponent = function icalcomponent(data, parent) { - this.wrappedJSObject = this; - this.parent = parent; - this.fromData(data); - } - - ICAL.icalcomponent.prototype = { - - data: null, - name: "", - components: null, - properties: null, - - icalclass: "icalcomponent", - - clone: function clone() { - return new ICAL.icalcomponent(this.undecorate(), this.parent); - }, - - fromData: function fromData(data) { - if (!data) { - data = ICAL.helpers.initComponentData(null); - } - this.data = data; - this.data.value = this.data.value || []; - this.data.type = this.data.type || "COMPONENT"; - this.components = {}; - this.properties = {}; - - // Save the name directly on the object, as we want this accessed - // from the outside. - this.name = this.data.name; - delete this.data.name; - - var value = this.data.value; - - for (var key in value) { - var keyname = value[key].name; - if (value[key].type == "COMPONENT") { - value[key] = new ICAL.icalcomponent(value[key], this); - ICAL.helpers.ensureKeyExists(this.components, keyname, []); - this.components[keyname].push(value[key]); - } else { - value[key] = new ICAL.icalproperty(value[key], this); - ICAL.helpers.ensureKeyExists(this.properties, keyname, []); - this.properties[keyname].push(value[key]); - } - } - }, - - undecorate: function undecorate() { - var newdata = []; - for (var key in this.data.value) { - newdata.push(this.data.value[key].undecorate()); - } - return { - name: this.name, - type: "COMPONENT", - value: newdata - }; - }, - - getFirstSubcomponent: function getFirstSubcomponent(aType) { - var comp = null; - if (aType) { - var ucType = aType.toUpperCase(); - if (ucType in this.components && - this.components[ucType] && - this.components[ucType].length > 0) { - comp = this.components[ucType][0]; - } - } else { - for (var thiscomp in this.components) { - comp = this.components[thiscomp][0]; - break; - } - } - return comp; - }, - - getAllSubcomponents: function getAllSubcomponents(aType) { - var comps = []; - if (aType && aType != "ANY") { - var ucType = aType.toUpperCase(); - if (ucType in this.components) { - for (var compKey in this.components[ucType]) { - comps.push(this.components[ucType][compKey]); - } - } - } else { - for (var compName in this.components) { - for (var compKey in this.components[compName]) { - comps.push(this.components[compName][compKey]); - } - } - } - return comps; - }, - - addSubcomponent: function addSubcomponent(aComp, aCompName) { - var ucName, comp; - var comp; - if (aComp.icalclass == "icalcomponent") { - ucName = aComp.name; - comp = aComp.clone(); - comp.parent = this; - } else { - ucName = aCompName.toUpperCase(); - comp = new ICAL.icalcomponent(aComp, ucName, this); - } - - this.data.value.push(comp); - ICAL.helpers.ensureKeyExists(this.components, ucName, []); - this.components[ucName].push(comp); - }, - - removeSubcomponent: function removeSubComponent(aName) { - var ucName = aName.toUpperCase(); - for (var key in this.components[ucName]) { - var pos = this.data.value.indexOf(this.components[ucName][key]); - if (pos > -1) { - this.data.value.splice(pos, 1); - } - } - - delete this.components[ucName]; - }, - - hasProperty: function hasProperty(aName) { - var ucName = aName.toUpperCase(); - return (ucName in this.properties); - }, - - getFirstProperty: function getFirstProperty(aName) { - var prop = null; - if (aName) { - var ucName = aName.toUpperCase(); - if (ucName in this.properties && this.properties[ucName]) { - prop = this.properties[ucName][0]; - } - } else { - for (var p in this.properties) { - prop = this.properties[p]; - break; - } - } - return prop; - }, - - getFirstPropertyValue: function getFirstPropertyValue(aName) { - // TODO string value? - var prop = this.getFirstProperty(aName); - return (prop ? prop.getFirstValue() : null); - }, - - getAllProperties: function getAllProperties(aName) { - var props = []; - if (aName && aName != "ANY") { - var ucType = aName.toUpperCase(); - if (ucType in this.properties) { - props = this.properties[ucType].concat([]); - } - } else { - for (var propName in this.properties) { - props = props.concat(this.properties[propName]); - } - } - return props; - }, - - addPropertyWithValue: function addStringProperty(aName, aValue) { - var ucName = aName.toUpperCase(); - var lineData = ICAL.icalparser.detectValueType({ - name: ucName, - value: aValue - }); - - var prop = ICAL.icalproperty.fromData(lineData); - ICAL.helpers.dumpn("Adding property " + ucName + "=" + aValue); - return this.addProperty(prop); - }, - - addProperty: function addProperty(aProp) { - var prop = aProp; - if (aProp.parent) { - prop = aProp.clone(); - } - aProp.parent = this; - - ICAL.helpers.ensureKeyExists(this.properties, aProp.name, []); - this.properties[aProp.name].push(aProp); - ICAL.helpers.dumpn("DATA IS: " + this.data.toString()); - this.data.value.push(aProp); - ICAL.helpers.dumpn("Adding property " + aProp); - }, - - removeProperty: function removeProperty(aName) { - var ucName = aName.toUpperCase(); - for (var key in this.properties[ucName]) { - var pos = this.data.value.indexOf(this.properties[ucName][key]); - if (pos > -1) { - this.data.value.splice(pos, 1); - } - } - delete this.properties[ucName]; - }, - - clearAllProperties: function clearAllProperties() { - this.properties = {}; - for (var i = this.data.value.length - 1; i >= 0; i--) { - if (this.data.value[i].type != "COMPONENT") { - delete this.data.value[i]; - } - } - }, - - _valueToJSON: function(value) { - if (value && value.icaltype) { - return value.toString(); - } - - if (typeof(value) === 'object') { - return this._undecorateJSON(value); - } - - return value; - }, - - _undecorateJSON: function(object) { - if (object instanceof Array) { - var result = []; - var len = object.length; - - for (var i = 0; i < len; i++) { - result.push(this._valueToJSON(object[i])); - } - - } else { - var result = {}; - var key; - - for (key in object) { - if (object.hasOwnProperty(key)) { - result[key] = this._valueToJSON(object[key]); - } - } - } - - return result; - }, - - /** - * Exports the components values to a json friendly - * object. You can use JSON.stringify directly on - * components as a result. - */ - toJSON: function toJSON() { - return this._undecorateJSON(this.undecorate()); - }, - - toString: function toString() { - var str = ICAL.helpers.foldline("BEGIN:" + this.name) + ICAL.newLineChar; - for (var key in this.data.value) { - str += this.data.value[key].toString() + ICAL.newLineChar; - } - str += ICAL.helpers.foldline("END:" + this.name); - return str; - } - }; - - ICAL.icalcomponent.fromString = function icalcomponent_from_string(str) { - return ICAL.toJSON(str, true); - }; - - ICAL.icalcomponent.fromData = function icalcomponent_from_data(aData) { - return new ICAL.icalcomponent(aData); - }; -})(); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -"use strict"; - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function() { - ICAL.icalproperty = function icalproperty(data, parent) { - this.wrappedJSObject = this; - this.parent = parent; - this.fromData(data); - } - - ICAL.icalproperty.prototype = { - parent: null, - data: null, - name: null, - icalclass: "icalproperty", - - clone: function clone() { - return new ICAL.icalproperty(this.undecorate(), this.parent); - }, - - fromData: function fromData(aData) { - if (!aData.name) { - ICAL.helpers.dumpn("Missing name: " + aData.toString()); - } - this.name = aData.name; - this.data = aData; - this.setValues(this.data.value, this.data.type); - delete this.data.name; - }, - - undecorate: function() { - var values = []; - for (var key in this.data.value) { - var val = this.data.value[key]; - if ("undecorate" in val) { - values.push(val.undecorate()); - } else { - values.push(val); - } - } - var obj = { - name: this.name, - type: this.data.type, - value: values - }; - if (this.data.parameters) { - obj.parameters = this.data.parameters; - } - return obj; - }, - - toString: function toString() { - return ICAL.icalparser.stringifyProperty({ - name: this.name, - type: this.data.type, - value: this.data.value, - parameters: this.data.parameters - }); - }, - - getStringValue: function getStringValue() { - ICAL.helpers.dumpn("GV: " + ICAL.icalparser.stringifyValue(this.data)); - return ICAL.icalparser.stringifyValue(this.data); - }, - - setStringValue: function setStringValue(val) { - this.setValue(val, this.data.type); - // TODO force TEXT or rename method to something like setParseValue() - }, - - getFirstValue: function getValue() { - return (this.data.value ? this.data.value[0] : null); - }, - - getValues: function getValues() { - return (this.data.value ? this.data.value : []); - }, - - setValue: function setValue(aValue, aType) { - return this.setValues([aValue], aType); - }, - - setValues: function setValues(aValues, aType) { - var newValues = []; - var newType = null; - for (var key in aValues) { - var value = aValues[key]; - if (value.icalclass && value.icaltype) { - if (newType && newType != value.icaltype) { - throw new Error("All values must be of the same type!"); - } else { - newType = value.icaltype; - } - newValues.push(value); - } else { - var type; - if (aType) { - type = aType; - } else if (typeof value == "string") { - type = "TEXT"; - } else if (typeof value == "number") { - type = (Math.floor(value) == value ? "INTEGER" : "FLOAT"); - } else if (typeof value == "boolean") { - type = "BOOLEAN"; - value = (value ? "TRUE" : "FALSE"); - } else { - throw new ParserError(null, "Invalid value: " + value); - } - - if (newType && newType != type) { - throw new Error("All values must be of the same type!"); - } else { - newType = type; - } - ICAL.icalparser.validateValue(this.data, type, "" + value, true); - newValues.push(ICAL.icalparser.decorateValue(type, "" + value)); - } - } - - this.data.value = newValues; - this.data.type = newType; - return aValues; - }, - - getValueType: function getValueType() { - return this.data.type; - }, - - getName: function getName() { - return this.name; - }, - - getParameterValue: function getParameter(aName) { - var value = null; - var ucName = aName.toUpperCase(); - if (ICAL.helpers.hasKey(this.data.parameters, ucName)) { - value = this.data.parameters[ucName].value; - } - return value; - }, - - getParameterType: function getParameterType(aName) { - var type = null; - var ucName = aName.toUpperCase(); - if (ICAL.helpers.hasKey(this.data.parameters, ucName)) { - type = this.data.parameters[ucName].type; - } - return type; - }, - - setParameter: function setParameter(aName, aValue, aType) { - // TODO autodetect type by name - var ucName = aName.toUpperCase(); - ICAL.helpers.ensureKeyExists(this.data, "parameters", {}); - this.data.parameters[ucName] = { - type: aType || "TEXT", - value: aValue - }; - - if (aName == "VALUE") { - this.data.type = aValue; - // TODO revalidate value - } - }, - - countParameters: function countParmeters() { - // TODO Object.keys compatibility? - var dp = this.data.parameters; - return (dp ? Object.keys(dp).length : 0); - }, - - removeParameter: function removeParameter(aName) { - var ucName = aName.toUpperCase(); - if (ICAL.helpers.hasKey(this.data.parameters, ucName)) { - delete this.data.parameters[ucName]; - } - } - }; - - ICAL.icalproperty.fromData = function(aData) { - return new ICAL.icalproperty(aData); - }; -})(); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -"use strict"; - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function() { - ICAL.icalvalue = function icalvalue(aData, aParent, aType) { - this.parent = aParent; - this.fromData(aData, aType); - }; - - ICAL.icalvalue.prototype = { - - data: null, - parent: null, - icaltype: null, - - fromData: function icalvalue_fromData(aData, aType) { - var type = (aType || (aData && aData.type) || this.icaltype); - this.icaltype = type; - - if (aData && type) { - aData.type = type; - } - - this.data = aData; - }, - - fromString: function icalvalue_fromString(aString, aType) { - var type = aType || this.icaltype; - this.fromData(ICAL.icalparser.parseValue(aString, type), type); - }, - - undecorate: function icalvalue_undecorate() { - return this.toString(); - }, - - toString: function() { - return this.data.value.toString(); - } - }; - - ICAL.icalvalue.fromString = function icalvalue_fromString(aString, aType) { - var val = new ICAL.icalvalue(); - val.fromString(aString, aType); - return val; - }; - - ICAL.icalvalue._createFromString = function icalvalue__createFromString(ctor) { - ctor.fromString = function icalvalue_derived_fromString(aStr) { - var val = new ctor(); - val.fromString(aStr); - return val; - }; - }; - - ICAL.icalbinary = function icalbinary(aData, aParent) { - ICAL.icalvalue.call(this, aData, aParent, "BINARY"); - }; - - ICAL.icalbinary.prototype = { - - __proto__: ICAL.icalvalue.prototype, - - icaltype: "BINARY", - - decodeValue: function decodeValue() { - return this._b64_decode(this.data.value); - }, - - setEncodedValue: function setEncodedValue(val) { - this.data.value = this._b64_encode(val); - }, - - _b64_encode: function base64_encode(data) { - // http://kevin.vanzonneveld.net - // + original by: Tyler Akins (http://rumkin.com) - // + improved by: Bayron Guevara - // + improved by: Thunder.m - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + bugfixed by: Pellentesque Malesuada - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + improved by: Rafał Kukawski (http://kukawski.pl) - // * example 1: base64_encode('Kevin van Zonneveld'); - // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' - // mozilla has this native - // - but breaks in 2.0.0.12! - //if (typeof this.window['atob'] == 'function') { - // return atob(data); - //} - var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "abcdefghijklmnopqrstuvwxyz0123456789+/="; - var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, - ac = 0, - enc = "", - tmp_arr = []; - - if (!data) { - return data; - } - - do { // pack three octets into four hexets - o1 = data.charCodeAt(i++); - o2 = data.charCodeAt(i++); - o3 = data.charCodeAt(i++); - - bits = o1 << 16 | o2 << 8 | o3; - - h1 = bits >> 18 & 0x3f; - h2 = bits >> 12 & 0x3f; - h3 = bits >> 6 & 0x3f; - h4 = bits & 0x3f; - - // use hexets to index into b64, and append result to encoded string - tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); - } while (i < data.length); - - enc = tmp_arr.join(''); - - var r = data.length % 3; - - return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); - - }, - - _b64_decode: function base64_decode(data) { - // http://kevin.vanzonneveld.net - // + original by: Tyler Akins (http://rumkin.com) - // + improved by: Thunder.m - // + input by: Aman Gupta - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + bugfixed by: Onno Marsman - // + bugfixed by: Pellentesque Malesuada - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + input by: Brett Zamir (http://brett-zamir.me) - // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA=='); - // * returns 1: 'Kevin van Zonneveld' - // mozilla has this native - // - but breaks in 2.0.0.12! - //if (typeof this.window['btoa'] == 'function') { - // return btoa(data); - //} - var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "abcdefghijklmnopqrstuvwxyz0123456789+/="; - var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, - ac = 0, - dec = "", - tmp_arr = []; - - if (!data) { - return data; - } - - data += ''; - - do { // unpack four hexets into three octets using index points in b64 - h1 = b64.indexOf(data.charAt(i++)); - h2 = b64.indexOf(data.charAt(i++)); - h3 = b64.indexOf(data.charAt(i++)); - h4 = b64.indexOf(data.charAt(i++)); - - bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; - - o1 = bits >> 16 & 0xff; - o2 = bits >> 8 & 0xff; - o3 = bits & 0xff; - - if (h3 == 64) { - tmp_arr[ac++] = String.fromCharCode(o1); - } else if (h4 == 64) { - tmp_arr[ac++] = String.fromCharCode(o1, o2); - } else { - tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); - } - } while (i < data.length); - - dec = tmp_arr.join(''); - - return dec; - } - }; - ICAL.icalvalue._createFromString(ICAL.icalbinary); - - ICAL.icalutcoffset = function icalutcoffset(aData, aParent) { - ICAL.icalvalue.call(this, aData, aParent, "UTC-OFFSET"); - }; - - ICAL.icalutcoffset.prototype = { - - __proto__: ICAL.icalvalue.prototype, - - hours: null, - minutes: null, - factor: null, - - icaltype: "UTC-OFFSET", - - fromData: function fromData(aData) { - if (aData) { - this.hours = aData.hours; - this.minutes = aData.minutes; - this.factor = aData.factor; - } - }, - - toString: function toString() { - return (this.factor == 1 ? "+" : "-") + - ICAL.helpers.pad2(this.hours) + - ICAL.helpers.pad2(this.minutes); - } - }; - ICAL.icalvalue._createFromString(ICAL.icalutcoffset); -})(); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -"use strict"; - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function() { - ICAL.icalperiod = function icalperiod(aData) { - this.wrappedJSObject = this; - this.fromData(aData); - }; - - ICAL.icalperiod.prototype = { - - start: null, - end: null, - duration: null, - icalclass: "icalperiod", - icaltype: "PERIOD", - - getDuration: function duration() { - if (this.duration) { - return this.duration; - } else { - return this.end.subtractDate(this.start); - } - }, - - toString: function toString() { - return this.start + "/" + (this.end || this.duration); - }, - - fromData: function fromData(data) { - if (data) { - this.start = ("start" in data ? new ICAL.icaltime(data.start) : null); - this.end = ("end" in data ? new ICAL.icaltime(data.end) : null); - this.duration = ("duration" in data ? new ICAL.icalduration(data.duration) : null); - } - } - }; - - ICAL.icalperiod.fromString = function fromString(str) { - var data = ICAL.icalparser.parseValue(str, "PERIOD"); - return ICAL.icalperiod.fromData(data); - }; - ICAL.icalperiod.fromData = function fromData(aData) { - return new ICAL.icalperiod(aData); - }; -})(); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -"use strict"; - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function() { - ICAL.icalduration = function icalduration(data) { - this.wrappedJSObject = this; - this.fromData(data); - }; - - ICAL.icalduration.prototype = { - - weeks: 0, - days: 0, - hours: 0, - minutes: 0, - seconds: 0, - isNegative: false, - icalclass: "icalduration", - icaltype: "DURATION", - - clone: function clone() { - return ICAL.icalduration.fromData(this); - }, - - toSeconds: function toSeconds() { - var seconds = this.seconds + 60 * this.minutes + 3600 * this.hours + - 86400 * this.days + 7 * 86400 * this.weeks; - return (this.isNegative ? -seconds : seconds); - }, - - fromSeconds: function fromSeconds(aSeconds) { - var secs = Math.abs(aSeconds); - - this.isNegative = (aSeconds < 0); - this.days = ICAL.helpers.trunc(secs / 86400); - - // If we have a flat number of weeks, use them. - if (this.days % 7 == 0) { - this.weeks = this.days / 7; - this.days = 0; - } else { - this.weeks = 0; - } - - secs -= (this.days + 7 * this.weeks) * 86400; - - this.hours = ICAL.helpers.trunc(secs / 3600); - secs -= this.hours * 3600; - - this.minutes = ICAL.helpers.trunc(secs / 60); - secs -= this.minutes * 60; - - this.seconds = secs; - return this; - }, - - fromData: function fromData(aData) { - var propsToCopy = ["weeks", "days", "hours", - "minutes", "seconds", "isNegative"]; - for (var key in propsToCopy) { - var prop = propsToCopy[key]; - if (aData && prop in aData) { - this[prop] = aData[prop]; - } else { - this[prop] = 0; - } - } - - if (aData && "factor" in aData) { - this.isNegative = (aData.factor == "-1"); - } - }, - - reset: function reset() { - this.isNegative = false; - this.weeks = 0; - this.days = 0; - this.hours = 0; - this.minutes = 0; - this.seconds = 0; - }, - - compare: function compare(aOther) { - var thisSeconds = this.toSeconds(); - var otherSeconds = aOther.toSeconds(); - return (thisSeconds > otherSeconds) - (thisSeconds < otherSeconds); - }, - - normalize: function normalize() { - this.fromSeconds(this.toSeconds()); - return this; - }, - - toString: function toString() { - if (this.toSeconds() == 0) { - return "PT0S"; - } else { - var str = ""; - if (this.isNegative) str += "-"; - str += "P"; - if (this.weeks) str += this.weeks + "W"; - if (this.days) str += this.days + "D"; - - if (this.hours || this.minutes || this.seconds) { - str += "T"; - if (this.hours) str += this.hours + "H"; - if (this.minutes) str += this.minutes + "M"; - if (this.seconds) str += this.seconds + "S"; - } - return str; - } - } - }; - - ICAL.icalduration.fromSeconds = function icalduration_from_seconds(aSeconds) { - return (new ICAL.icalduration()).fromSeconds(); - }; - - ICAL.icalduration.fromString = function icalduration_from_string(aStr) { - var data = ICAL.icalparser.parseValue(aStr, "DURATION"); - return ICAL.icalduration.fromData(data); - }; - - ICAL.icalduration.fromData = function icalduration_from_data(aData) { - return new ICAL.icalduration(aData); - }; -})(); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -"use strict"; - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function() { - ICAL.icaltimezone = function icaltimezone(data) { - this.wrappedJSObject = this; - this.fromData(data); - }; - - ICAL.icaltimezone.prototype = { - - tzid: "", - location: "", - tznames: "", - - latitude: 0.0, - longitude: 0.0, - - component: null, - - expand_end_year: 0, - expand_start_year: 0, - - changes: null, - icalclass: "icaltimezone", - - fromData: function fromData(aData) { - var propsToCopy = ["tzid", "location", "tznames", - "latitude", "longitude"]; - for (var key in propsToCopy) { - var prop = propsToCopy[key]; - if (aData && prop in aData) { - this[prop] = aData[prop]; - } else { - this[prop] = 0; - } - } - - this.expand_end_year = 0; - this.expand_start_year = 0; - if (aData && "component" in aData) { - if (typeof aData.component == "string") { - this.component = this.componentFromString(aData.component); - } else { - this.component = ICAL.helpers.clone(aData.component, true); - } - } else { - this.component = null; - } - return this; - }, - - componentFromString: function componentFromString(str) { - this.component = ICAL.toJSON(str, true); - return this.component; - }, - - utc_offset: function utc_offset(tt) { - if (this == ICAL.icaltimezone.utc_timezone || this == ICAL.icaltimezone.local_timezone) { - return 0; - } - - this.ensure_coverage(tt.year); - - if (!this.changes || this.changes.length == 0) { - return 0; - } - - var tt_change = { - year: tt.year, - month: tt.month, - day: tt.day, - hour: tt.hour, - minute: tt.minute, - second: tt.second - }; - - var change_num = this.find_nearby_change(tt_change); - var change_num_to_use = -1; - var step = 1; - - for (;;) { - var change = ICAL.helpers.clone(this.changes[change_num], true); - if (change.utc_offset < change.prev_utc_offset) { - ICAL.helpers.dumpn("Adjusting " + change.utc_offset); - ICAL.icaltimezone.adjust_change(change, 0, 0, 0, change.utc_offset); - } else { - ICAL.helpers.dumpn("Adjusting prev " + change.prev_utc_offset); - ICAL.icaltimezone.adjust_change(change, 0, 0, 0, - change.prev_utc_offset); - } - - var cmp = ICAL.icaltimezone._compare_change_fn(tt_change, change); - ICAL.helpers.dumpn("Compare" + cmp + " / " + change.toString()); - - if (cmp >= 0) { - change_num_to_use = change_num; - } else { - step = -1; - } - - if (step == -1 && change_num_to_use != -1) { - break; - } - - change_num += step; - - if (change_num < 0) { - return 0; - } - - if (change_num >= this.changes.length) { - break; - } - } - - var zone_change = this.changes[change_num_to_use]; - var utc_offset_change = zone_change.utc_offset - zone_change.prev_utc_offset; - - if (utc_offset_change < 0 && change_num_to_use > 0) { - var tmp_change = ICAL.helpers.clone(zone_change, true); - ICAL.icaltimezone.adjust_change(tmp_change, 0, 0, 0, - tmp_change.prev_utc_offset); - - if (ICAL.icaltimezone._compare_change_fn(tt_change, tmp_change) < 0) { - var prev_zone_change = this.changes[change_num_to_use - 1]; - - var want_daylight = false; // TODO - - if (zone_change.is_daylight != want_daylight && - prev_zone_change.is_daylight == want_daylight) { - zone_change = prev_zone_change; - } - } - } - - // TODO return is_daylight? - return zone_change.utc_offset; - }, - - find_nearby_change: function icaltimezone_find_nearby_change(change) { - var lower = 0, - middle = 0; - var upper = this.changes.length; - - while (lower < upper) { - middle = ICAL.helpers.trunc(lower + upper / 2); - var zone_change = this.changes[middle]; - var cmp = ICAL.icaltimezone._compare_change_fn(change, zone_change); - if (cmp == 0) { - break; - } else if (cmp > 0) { - upper = middle; - } else { - lower = middle; - } - } - - return middle; - }, - - ensure_coverage: function ensure_coverage(aYear) { - if (ICAL.icaltimezone._minimum_expansion_year == -1) { - var today = ICAL.icaltime.now(); - ICAL.icaltimezone._minimum_expansion_year = today.year; - } - - var changes_end_year = aYear; - if (changes_end_year < ICAL.icaltimezone._minimum_expansion_year) { - changes_end_year = ICAL.icaltimezone._minimum_expansion_year; - } - - changes_end_year += ICAL.icaltimezone.EXTRA_COVERAGE; - - if (changes_end_year > ICAL.icaltimezone.MAX_YEAR) { - changes_end_year = ICAL.icaltimezone.MAX_YEAR; - } - - if (!this.changes || this.expand_end_year < aYear) { - this.expand_changes(changes_end_year); - } - }, - - expand_changes: function expand_changes(aYear) { - var changes = []; - if (this.component) { - // HACK checking for component only needed for floating - // tz, which is not in core libical. - var subcomps = this.component.getAllSubcomponents(); - for (var compkey in subcomps) { - this.expand_vtimezone(subcomps[compkey], aYear, changes); - } - - this.changes = changes.concat(this.changes || []); - this.changes.sort(ICAL.icaltimezone._compare_change_fn); - } - - this.change_end_year = aYear; - }, - - expand_vtimezone: function expand_vtimezone(aComponent, aYear, changes) { - if (!aComponent.hasProperty("DTSTART") || - !aComponent.hasProperty("TZOFFSETTO") || - !aComponent.hasProperty("TZOFFSETFROM")) { - return; - } - - var dtstart = aComponent.getFirstProperty("DTSTART").getFirstValue(); - - function convert_tzoffset(offset) { - return offset.factor * (offset.hours * 3600 + offset.minutes * 60); - } - - function init_changes() { - var changebase = {}; - changebase.is_daylight = (aComponent.name == "DAYLIGHT"); - changebase.utc_offset = convert_tzoffset(aComponent.getFirstProperty("TZOFFSETTO").data); - changebase.prev_utc_offset = convert_tzoffset(aComponent.getFirstProperty("TZOFFSETFROM").data); - return changebase; - } - - if (!aComponent.hasProperty("RRULE") && !aComponent.hasProperty("RDATE")) { - var change = init_changes(); - change.year = dtstart.year; - change.month = dtstart.month; - change.day = dtstart.day; - change.hour = dtstart.hour; - change.minute = dtstart.minute; - change.second = dtstart.second; - - ICAL.icaltimezone.adjust_change(change, 0, 0, 0, - -change.prev_utc_offset); - changes.push(change); - } else { - var props = aComponent.getAllProperties("RDATE"); - for (var rdatekey in props) { - var rdate = props[rdatekey]; - var change = init_changes(); - change.year = rdate.time.year; - change.month = rdate.time.month; - change.day = rdate.time.day; - - if (rdate.time.isDate) { - change.hour = dtstart.hour; - change.minute = dtstart.minute; - change.second = dtstart.second; - } else { - change.hour = rdate.time.hour; - change.minute = rdate.time.minute; - change.second = rdate.time.second; - - if (rdate.time.zone == ICAL.icaltimezone.utc_timezone) { - ICAL.icaltimezone.adjust_change(change, 0, 0, 0, - -change.prev_utc_offset); - } - } - - changes.push(change); - } - - var rrule = aComponent.getFirstProperty("RRULE").getFirstValue(); - // TODO multiple rrules? - - var change = init_changes(); - - if (rrule.until && rrule.until.zone == ICAL.icaltimezone.utc_timezone) { - rrule.until.adjust(0, 0, 0, change.prev_utc_offset); - rrule.until.zone = ICAL.icaltimezone.local_timezone; - } - - var iterator = rrule.iterator(dtstart); - - var occ; - while ((occ = iterator.next())) { - var change = init_changes(); - if (occ.year > aYear || !occ) { - break; - } - - change.year = occ.year; - change.month = occ.month; - change.day = occ.day; - change.hour = occ.hour; - change.minute = occ.minute; - change.second = occ.second; - change.isDate = occ.isDate; - - ICAL.icaltimezone.adjust_change(change, 0, 0, 0, - -change.prev_utc_offset); - changes.push(change); - } - } - - return changes; - }, - - toString: function toString() { - return (this.tznames ? this.tznames : this.tzid); - } - - }; - - ICAL.icaltimezone._compare_change_fn = function icaltimezone_compare_change_fn(a, b) { - if (a.year < b.year) return -1; - else if (a.year > b.year) return 1; - - if (a.month < b.month) return -1; - else if (a.month > b.month) return 1; - - if (a.day < b.day) return -1; - else if (a.day > b.day) return 1; - - if (a.hour < b.hour) return -1; - else if (a.hour > b.hour) return 1; - - if (a.minute < b.minute) return -1; - else if (a.minute > b.minute) return 1; - - if (a.second < b.second) return -1; - else if (a.second > b.second) return 1; - - return 0; - }; - - ICAL.icaltimezone.convert_time = function icaltimezone_convert_time(tt, from_zone, to_zone) { - if (tt.isDate || - from_zone.tzid == to_zone.tzid || - from_zone == ICAL.icaltimezone.local_timezone || - to_zone == ICAL.icaltimezone.local_timezone) { - tt.zone = to_zone; - return tt; - } - - var utc_offset = from_zone.utc_offset(tt); - tt.adjust(0, 0, 0, - utc_offset); - - utc_offset = to_zone.utc_offset(tt); - tt.adjust(0, 0, 0, utc_offset); - }; - - ICAL.icaltimezone.fromData = function icaltimezone_fromData(aData) { - var tt = new ICAL.icaltimezone(); - return tt.fromData(aData); - }; - - ICAL.icaltimezone.utc_timezone = ICAL.icaltimezone.fromData({ - tzid: "UTC" - }); - ICAL.icaltimezone.local_timezone = ICAL.icaltimezone.fromData({ - tzid: "floating" - }); - - ICAL.icaltimezone.adjust_change = function icaltimezone_adjust_change(change, days, hours, minutes, seconds) { - return ICAL.icaltime.prototype.adjust.call(change, days, hours, minutes, seconds); - }; - - ICAL.icaltimezone._minimum_expansion_year = -1; - ICAL.icaltimezone.MAX_YEAR = 2035; // TODO this is because of time_t, which we don't need. Still usefull? - ICAL.icaltimezone.EXTRA_COVERAGE = 5; -})(); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -"use strict"; - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function() { - ICAL.icaltime = function icaltime(data) { - this.wrappedJSObject = this; - this.fromData(data); - }; - - ICAL.icaltime.prototype = { - - year: 0, - month: 1, - day: 1, - - hour: 0, - minute: 0, - second: 0, - - isDate: false, - zone: null, - - auto_normalize: false, - icalclass: "icaltime", - icaltype: "DATE-TIME", - - clone: function icaltime_clone() { - return new ICAL.icaltime(this); - }, - - reset: function icaltime_reset() { - this.fromData(ICAL.icaltime.epoch_time); - this.zone = ICAL.icaltimezone.utc_timezone; - }, - - resetTo: function icaltime_resetTo(year, month, day, - hour, minute, second, timezone) { - this.fromData({ - year: year, - month: month, - day: day, - hour: hour, - minute: minute, - second: second, - zone: timezone - }); - }, - - fromString: function icaltime_fromString(str) { - var data; - try { - data = ICAL.icalparser.parseValue(str, "DATE"); - data.isDate = true; - } catch (e) { - data = ICAL.icalparser.parseValue(str, "DATE-TIME"); - data.isDate = false; - } - return this.fromData(data); - }, - - fromJSDate: function icaltime_fromJSDate(aDate, useUTC) { - if (!aDate) { - this.reset(); - } else { - if (useUTC) { - this.zone = ICAL.icaltimzone.utc_timezone; - this.year = aDate.getUTCFullYear(); - this.month = aDate.getUTCMonth() + 1; - this.day = aDate.getUTCDate(); - this.hour = aDate.getUTCHours(); - this.minute = aDate.getUTCMinutes(); - this.second = aDate.getUTCSeconds(); - } else { - this.zone = ICAL.icaltimezone.local_timezone; - this.year = aDate.getFullYear(); - this.month = aDate.getMonth() + 1; - this.day = aDate.getDate(); - this.hour = aDate.getHours(); - this.minute = aDate.getMinutes(); - this.second = aDate.getSeconds(); - } - } - return this; - }, - - fromData: function fromData(aData) { - // TODO given we're switching formats, this may not be needed - var old_auto_normalize = this.auto_normalize; - this.auto_normalize = false; - - var propsToCopy = { - year: 0, - month: 1, - day: 1, - hour: 0, - minute: 0, - second: 0 - }; - for (var key in propsToCopy) { - if (aData && key in aData) { - this[key] = aData[key]; - } else { - this[key] = propsToCopy[key]; - } - } - if (aData && !("isDate" in aData)) { - this.isDate = !("hour" in aData); - } else if (aData && ("isDate" in aData)) { - this.isDate = aData.isDate; - } - - if (aData && "timezone" in aData && aData.timezone == "Z") { - this.zone = ICAL.icaltimezone.utc_timezone; - } - if (aData && "zone" in aData) { - this.zone = aData.zone; - } - - if (!this.zone) { - this.zone = ICAL.icaltimezone.local_timezone; - } - - if (aData && "auto_normalize" in aData) { - this.auto_normalize = aData.auto_normalize; - } else { - this.auto_normalize = old_auto_normalize; - } - if (this.auto_normalize) { - this.normalize(); - } - return this; - }, - - day_of_week: function icaltime_day_of_week() { - // Using Zeller's algorithm - var q = this.day; - var m = this.month + (this.month < 3 ? 12 : 0); - var Y = this.year - (this.month < 3 ? 1 : 0); - - var h = (q + Y + ICAL.helpers.trunc(((m + 1) * 26) / 10) + ICAL.helpers.trunc(Y / 4)); - if (true /* gregorian */) { - h += ICAL.helpers.trunc(Y / 100) * 6 + ICAL.helpers.trunc(Y / 400); - } else { - h += 5; - } - - // Normalize to 1 = sunday - h = ((h + 6) % 7) + 1; - return h; - }, - - day_of_year: function icaltime_day_of_year() { - var is_leap = (ICAL.icaltime.is_leap_year(this.year) ? 1 : 0); - var diypm = ICAL.icaltime._days_in_year_passed_month; - return diypm[is_leap][this.month - 1] + this.day; - }, - - start_of_week: function start_of_week() { - var result = this.clone(); - result.day -= this.day_of_week() - 1; - return result.normalize(); - }, - - end_of_week: function end_of_week() { - var result = this.clone(); - result.day += 7 - this.day_of_week(); - return result.normalize(); - }, - - start_of_month: function start_of_month() { - var result = this.clone(); - result.day = 1; - result.isDate = true; - result.hour = 0; - result.minute = 0; - result.second = 0; - return result; - }, - - end_of_month: function end_of_month() { - var result = this.clone(); - result.day = ICAL.icaltime.days_in_month(result.month, result.year); - result.isDate = true; - result.hour = 0; - result.minute = 0; - result.second = 0; - return result; - }, - - start_of_year: function start_of_year() { - var result = this.clone(); - result.day = 1; - result.month = 1; - result.isDate = true; - result.hour = 0; - result.minute = 0; - result.second = 0; - return result; - }, - - end_of_year: function end_of_year() { - var result = this.clone(); - result.day = 31; - result.month = 12; - result.isDate = true; - result.hour = 0; - result.minute = 0; - result.second = 0; - return result; - }, - - start_doy_week: function start_doy_week(aFirstDayOfWeek) { - var firstDow = aFirstDayOfWeek || ICAL.icaltime.SUNDAY; - var delta = this.day_of_week() - firstDow; - if (delta < 0) delta += 7; - return this.day_of_year() - delta; - }, - - nth_weekday: function icaltime_nth_weekday(aDayOfWeek, aPos) { - var days_in_month = ICAL.icaltime.days_in_month(this.month, this.year); - var weekday; - var pos = aPos; - - var otherday = this.clone(); - - if (pos >= 0) { - otherday.day = 1; - var start_dow = otherday.day_of_week(); - - if (pos != 0) { - pos--; - } - - weekday = aDayOfWeek - start_dow + 1; - - if (weekday <= 0) { - weekday += 7; - } - } else { - otherday.day = days_in_month; - var end_dow = otherday.day_of_week(); - - pos++; - - weekday = (end_dow - dow); - - if (weekday < 0) { - weekday += 7; - } - - weekday = days_in_month - weekday; - } - - weekday += pos * 7; - - return weekday; - }, - - week_number: function week_number(aWeekStart) { - // This function courtesty of Julian Bucknall, published under the MIT license - // http://www.boyet.com/articles/publishedarticles/calculatingtheisoweeknumb.html - var doy = this.day_of_year(); - var dow = this.day_of_week(); - var year = this.year; - var week1; - - var dt = this.clone(); - dt.isDate = true; - var first_dow = dt.day_of_week(); - var isoyear = this.year; - - if (dt.month == 12 && dt.day > 28) { - week1 = ICAL.icaltime.week_one_starts(isoyear + 1, aWeekStart); - if (dt.compare(week1) < 0) { - week1 = ICAL.icaltime.week_one_starts(isoyear, aWeekStart); - } else { - isoyear++; - } - } else { - week1 = ICAL.icaltime.week_one_starts(isoyear, aWeekStart); - if (dt.compare(week1) < 0) { - week1 = ICAL.icaltime.week_one_starts(--isoyear, aWeekStart); - } - } - - var daysBetween = (dt.subtractDate(week1).toSeconds() / 86400); - return ICAL.helpers.trunc(daysBetween / 7) + 1; - }, - - addDuration: function icaltime_add(aDuration) { - var mult = (aDuration.isNegative ? -1 : 1); - - this.second += mult * aDuration.seconds; - this.minute += mult * aDuration.minutes; - this.hour += mult * aDuration.hours; - this.day += mult * aDuration.days; - this.day += mult * 7 * aDuration.weeks; - - this.normalize(); - }, - - subtractDate: function icaltime_subtract(aDate) { - function leap_years_until(aYear) { - return ICAL.helpers.trunc(aYear / 4) - - ICAL.helpers.trunc(aYear / 100) + - ICAL.helpers.trunc(aYear / 400); - } - - function leap_years_between(aStart, aEnd) { - if (aStart >= aEnd) { - return 0; - } else { - return leap_years_until(aEnd - 1) - leap_years_until(aStart); - } - } - var dur = new ICAL.icalduration(); - - dur.seconds = this.second - aDate.second; - dur.minutes = this.minute - aDate.minute; - dur.hours = this.hour - aDate.hour; - - if (this.year == aDate.year) { - var this_doy = this.day_of_year(); - var that_doy = aDate.day_of_year(); - dur.days = this_doy - that_doy; - } else if (this.year < aDate.year) { - var days_left_thisyear = 365 + - (ICAL.icaltime.is_leap_year(this.year) ? 1 : 0) - - this.day_of_year(); - - dur.days -= days_left_thisyear + aDate.day_of_year(); - dur.days -= leap_years_between(this.year + 1, aDate.year); - dur.days -= 365 * (aDate.year - this.year - 1); - } else { - var days_left_thatyear = 365 + - (ICAL.icaltime.is_leap_year(aDate.year) ? 1 : 0) - - aDate.day_of_year(); - - dur.days += days_left_thatyear + this.day_of_year(); - dur.days += leap_years_between(aDate.year + 1, this.year); - dur.days += 365 * (this.year - aDate.year - 1); - } - - return dur.normalize(); - }, - - compare: function icaltime_compare(other) { - function cmp(attr) { - return ICAL.icaltime._cmp_attr(a, b, attr); - } - - if (!other) return 0; - - if (this.isDate || other.isDate) { - return this.compare_date_only_tz(other, this.zone); - } - - var target_zone; - if (this.zone == ICAL.icaltimezone.local_timezone || - other.zone == ICAL.icaltimezone.local_timezone) { - target_zone = ICAL.icaltimezone.local_timezone; - } else { - target_zone = ICAL.icaltimezone.utc_timezone; - } - - var a = this.convert_to_zone(target_zone); - var b = other.convert_to_zone(target_zone); - var rc = 0; - - if ((rc = cmp("year")) != 0) return rc; - if ((rc = cmp("month")) != 0) return rc; - if ((rc = cmp("day")) != 0) return rc; - - if (a.isDate && b.isDate) { - // If both are dates, we are done - return 0; - } else if (b.isDate) { - // If b is a date, then a is greater - return 1; - } else if (a.isDate) { - // If a is a date, then b is greater - return -1; - } - - if ((rc = cmp("hour")) != 0) return rc; - if ((rc = cmp("minute")) != 0) return rc; - if ((rc = cmp("second")) != 0) return rc; - - // Now rc is 0 and the dates are equal - return rc; - }, - - compare_date_only_tz: function icaltime_compare_date_only_tz(other, tz) { - function cmp(attr) { - return ICAL.icaltime._cmp_attr(a, b, attr); - } - var a = this.convert_to_zone(tz); - var b = other.convert_to_zone(tz); - var rc = 0; - - if ((rc = cmp("year")) != 0) return rc; - if ((rc = cmp("month")) != 0) return rc; - if ((rc = cmp("day")) != 0) return rc; - - return rc; - }, - - convert_to_zone: function convert_to_zone(zone) { - var copy = this.clone(); - var zone_equals = (this.zone.tzid == zone.tzid); - - if (!this.isDate && !zone_equals) { - ICAL.icaltimezone.convert_time(copy, this.zone, zone); - } - - copy.zone = zone; - return copy; - }, - - utc_offset: function utc_offset() { - if (this.zone == ICAL.icaltimezone.local_timezone || - this.zone == ICAL.icaltimezone.utc_timezone) { - return 0; - } else { - return this.zone.utc_offset(this); - } - }, - - toString: function toString() { - return ("0000" + this.year).substr(-4) + - ("00" + this.month).substr(-2) + - ("00" + this.day).substr(-2) + - (this.isDate ? "" : - "T" + - ("00" + this.hour).substr(-2) + - ("00" + this.minute).substr(-2) + - ("00" + this.second).substr(-2) + - (this.zone && this.zone.tzid == "UTC" ? "Z" : "") - ); - }, - - toJSDate: function toJSDate() { - if (this.zone == ICAL.icaltimezone.local_timezone) { - if (this.isDate) { - return new Date(this.year, this.month - 1, this.day); - } else { - return new Date(this.year, this.month - 1, this.day, - this.hour, this.minute, this.second, 0); - } - } else { - var utcDate = this.convert_to_zone(ICAL.icaltimezone.utc_timezone); - if (this.isDate) { - return Date.UTC(this.year, this.month - 1, this.day); - } else { - return Date.UTC(this.year, this.month - 1, this.day, - this.hour, this.minute, this.second, 0); - } - } - }, - - normalize: function icaltime_normalize() { - if (this.isDate) { - this.hour = 0; - this.minute = 0; - this.second = 0; - } - this.icaltype = (this.isDate ? "DATE" : "DATE-TIME"); - - this.adjust(0, 0, 0, 0); - return this; - }, - - adjust: function icaltime_adjust(aExtraDays, aExtraHours, - aExtraMinutes, aExtraSeconds) { - var second, minute, hour, day; - var minutes_overflow, hours_overflow, days_overflow = 0, - years_overflow = 0; - var days_in_month; - - if (!this.isDate) { - second = this.second + aExtraSeconds; - this.second = second % 60; - minutes_overflow = ICAL.helpers.trunc(second / 60); - if (this.second < 0) { - this.second += 60; - minutes_overflow--; - } - - minute = this.minute + aExtraMinutes + minutes_overflow; - this.minute = minute % 60; - hours_overflow = ICAL.helpers.trunc(minute / 60); - if (this.minute < 0) { - this.minute += 60; - hours_overflow--; - } - - hour = this.hour + aExtraHours + hours_overflow; - this.hour = hour % 24; - days_overflow = ICAL.helpers.trunc(hour / 24); - if (this.hour < 0) { - this.hour += 24; - days_overflow--; - } - } - - // Adjust month and year first, because we need to know what month the day - // is in before adjusting it. - if (this.month > 12) { - years_overflow = ICAL.helpers.trunc((this.month - 1) / 12); - } else if (this.month < 1) { - years_overflow = ICAL.helpers.trunc(this.month / 12) - 1; - } - - this.year += years_overflow; - this.month -= 12 * years_overflow; - - // Now take care of the days (and adjust month if needed) - day = this.day + aExtraDays + days_overflow; - if (day > 0) { - for (;;) { - var days_in_month = ICAL.icaltime.days_in_month(this.month, this.year); - if (day <= days_in_month) { - break; - } - - this.month++; - if (this.month > 12) { - this.year++; - this.month = 1; - } - - day -= days_in_month; - } - } else { - while (day <= 0) { - if (this.month == 1) { - this.year--; - this.month = 12; - } else { - this.month--; - } - - day += ICAL.icaltime.days_in_month(this.month, this.year); - } - } - - this.day = day; - return this; - }, - - fromUnixTime: function fromUnixTime(seconds) { - var epoch = ICAL.icaltime.epoch_time.clone(); - epoch.adjust(0, 0, 0, seconds); - this.fromData(epoch); - this.zone = ICAL.icaltimezone.utc_timezone; - }, - - toUnixTime: function toUnixTime() { - var dur = this.subtractDate(ICAL.icaltime.epoch_time); - return dur.toSeconds(); - } - }; - - (function setupNormalizeAttributes() { - // This needs to run before any instances are created! - function addAutoNormalizeAttribute(attr, mattr) { - ICAL.icaltime.prototype[mattr] = ICAL.icaltime.prototype[attr]; - - Object.defineProperty(ICAL.icaltime.prototype, attr, { - get: function() { - return this[mattr]; - }, - set: function(val) { - this[mattr] = val; - if (this.auto_normalize) { - var old_normalize = this.auto_normalize; - this.auto_normalize = false; - this.normalize(); - this.auto_normalize = old_normalize; - } - return val; - } - }); - - } - - if ("defineProperty" in Object) { - addAutoNormalizeAttribute("year", "mYear"); - addAutoNormalizeAttribute("month", "mMonth"); - addAutoNormalizeAttribute("day", "mDay"); - addAutoNormalizeAttribute("hour", "mHour"); - addAutoNormalizeAttribute("minute", "mMinute"); - addAutoNormalizeAttribute("second", "mSecond"); - addAutoNormalizeAttribute("isDate", "mIsDate"); - - ICAL.icaltime.prototype.auto_normalize = true; - } - })(); - - ICAL.icaltime.days_in_month = function icaltime_days_in_month(month, year) { - var _days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - var days = 30; - - if (month < 1 || month > 12) return days; - - days = _days_in_month[month]; - - if (month == 2) { - days += ICAL.icaltime.is_leap_year(year); - } - - return days; - }; - - ICAL.icaltime.is_leap_year = function icaltime_is_leap_year(year) { - if (year <= 1752) { - return ((year % 4) == 0); - } else { - return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)); - } - }; - - ICAL.icaltime.from_day_of_year = function icaltime_from_day_of_year(aDayOfYear, aYear) { - var year = aYear; - var doy = aDayOfYear; - var tt = new ICAL.icaltime(); - tt.auto_normalize = false; - var is_leap = (ICAL.icaltime.is_leap_year(year) ? 1 : 0); - - if (doy < 1) { - year--; - is_leap = (ICAL.icaltime.is_leap_year(year) ? 1 : 0); - doy += ICAL.icaltime._days_in_year_passed_month[is_leap][12]; - } else if (doy > ICAL.icaltime._days_in_year_passed_month[is_leap][12]) { - is_leap = (ICAL.icaltime.is_leap_year(year) ? 1 : 0); - doy -= ICAL.icaltime._days_in_year_passed_month[is_leap][12]; - year++; - } - - tt.year = year; - tt.isDate = true; - - for (var month = 11; month >= 0; month--) { - if (doy > ICAL.icaltime._days_in_year_passed_month[is_leap][month]) { - tt.month = month + 1; - tt.day = doy - ICAL.icaltime._days_in_year_passed_month[is_leap][month]; - break; - } - } - - tt.auto_normalize = true; - return tt; - }; - - ICAL.icaltime.fromString = function fromString(str) { - var tt = new ICAL.icaltime(); - return tt.fromString(str); - }; - - ICAL.icaltime.fromJSDate = function fromJSDate(aDate, useUTC) { - var tt = new ICAL.icaltime(); - return tt.fromJSDate(aDate, useUTC); - }; - - ICAL.icaltime.fromData = function fromData(aData) { - var t = new ICAL.icaltime(); - return t.fromData(aData); - }; - - ICAL.icaltime.now = function icaltime_now() { - return ICAL.icaltime.fromJSDate(new Date(), false); - }; - - ICAL.icaltime.week_one_starts = function week_one_starts(aYear, aWeekStart) { - var t = ICAL.icaltime.fromData({ - year: aYear, - month: 1, - day: 4, - isDate: true - }); - - var fourth_dow = t.day_of_week(); - t.day += (1 - fourth_dow) + ((aWeekStart || ICAL.icaltime.SUNDAY) - 1); - return t; - }; - - ICAL.icaltime.epoch_time = ICAL.icaltime.fromData({ - year: 1970, - month: 1, - day: 1, - hour: 0, - minute: 0, - second: 0, - isDate: false, - timezone: "Z" - }); - - ICAL.icaltime._cmp_attr = function _cmp_attr(a, b, attr) { - if (a[attr] > b[attr]) return 1; - if (a[attr] < b[attr]) return -1; - return 0; - }; - - ICAL.icaltime._days_in_year_passed_month = [ - [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365], - [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366] - ]; - - ICAL.icaltime.SUNDAY = 1; - ICAL.icaltime.MONDAY = 2; - ICAL.icaltime.TUESDAY = 3; - ICAL.icaltime.WEDNESDAY = 4; - ICAL.icaltime.THURSDAY = 5; - ICAL.icaltime.FRIDAY = 6; - ICAL.icaltime.SATURDAY = 7; -})(); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -"use strict"; - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function() { - ICAL.icalrecur = function icalrecur(data) { - this.wrappedJSObject = this; - this.parts = {}; - this.fromData(data); - }; - - ICAL.icalrecur.prototype = { - - parts: null, - - interval: 1, - wkst: ICAL.icaltime.MONDAY, - until: null, - count: null, - freq: null, - icalclass: "icalrecur", - icaltype: "RECUR", - - iterator: function(aStart) { - return new icalrecur_iterator(this, aStart); - }, - - clone: function clone() { - return ICAL.icalrecur.fromData(this); - //return ICAL.icalrecur.fromIcalProperty(this.toIcalProperty()); - }, - - is_finite: function isfinite() { - return (this.count || this.until); - }, - - is_by_count: function isbycount() { - return (this.count && !this.until); - }, - - addComponent: function addPart(aType, aValue) { - if (!(aType in this.parts)) { - this.parts[aType] = [aValue]; - } else { - this.parts[aType].push(aValue); - } - }, - - setComponent: function setComponent(aType, aValues) { - this.parts[aType] = aValues; - }, - - getComponent: function getComponent(aType, aCount) { - var ucName = aType.toUpperCase(); - var components = (ucName in this.parts ? this.parts[ucName] : []); - - if (aCount) aCount.value = components.length; - return components; - }, - - getNextOccurrence: function getNextOccurrence(aStartTime, aRecurrenceId) { - ICAL.helpers.dumpn("GNO: " + aRecurrenceId + " / " + aStartTime); - var iter = this.iterator(aStartTime); - var next, cdt; - - do { - next = iter.next(); - ICAL.helpers.dumpn("Checking " + next + " <= " + aRecurrenceId); - } while (next && next.compare(aRecurrenceId) <= 0); - - if (next && aRecurrenceId.zone) { - next.zone = aRecurrenceId.zone; - } - - return next; - }, - - fromData: function fromData(aData) { - var propsToCopy = ["freq", "count", "wkst", "interval"]; - for (var key in propsToCopy) { - var prop = propsToCopy[key]; - if (aData && prop.toUpperCase() in aData) { - this[prop] = aData[prop.toUpperCase()]; - // TODO casing sucks, fix the parser! - } else if (aData && prop in aData) { - this[prop] = aData[prop]; - // TODO casing sucks, fix the parser! - } - } - - if (aData && "until" in aData && aData.until) { - this.until = aData.until.clone(); - } - - var partsToCopy = ["BYSECOND", "BYMINUTE", "BYHOUR", "BYDAY", - "BYMONTHDAY", "BYYEARDAY", "BYWEEKNO", - "BYMONTH", "BYSETPOS"]; - this.parts = {}; - if (aData) { - for (var key in partsToCopy) { - var prop = partsToCopy[key]; - if (prop in aData) { - this.parts[prop] = aData[prop]; - // TODO casing sucks, fix the parser! - } - } - // TODO oh god, make it go away! - if (aData.parts) { - for (var key in partsToCopy) { - var prop = partsToCopy[key]; - if (prop in aData.parts) { - this.parts[prop] = aData.parts[prop]; - // TODO casing sucks, fix the parser! - } - } - } - } - return this; - }, - - toString: function icalrecur_toString() { - // TODO retain order - var str = "FREQ=" + this.freq; - if (this.count) { - str += ";COUNT=" + this.count; - } - if (this.interval != 1) { - str += ";INTERVAL=" + this.interval; - } - for (var k in this.parts) { - str += ";" + k + "=" + this.parts[k]; - } - return str; - }, - - toIcalProperty: function toIcalProperty() { - try { - var valueData = { - name: this.isNegative ? "EXRULE" : "RRULE", - type: "RECUR", - value: [this.toString()] - // TODO more props? - }; - return ICAL.icalproperty.fromData(valueData); - } catch (e) { - ICAL.helpers.dumpn("EICALPROP: " + this.toString() + "//" + e); - ICAL.helpers.dumpn(e.stack); - } - }, - fromIcalProperty: function fromIcalProperty(aProp) { - var propval = aProp.getFirstValue(); - this.fromData(propval); - this.parts = ICAL.helpers.clone(propval.parts, true); - if (aProp.name == "EXRULE") { - this.isNegative = true; - } else if (aProp.name == "RRULE") { - this.isNegative = false; - } else { - throw new Error("Invalid Property " + aProp.name + " passed"); - } - } - }; - - ICAL.icalrecur.fromData = function icalrecur_fromData(data) { - return (new ICAL.icalrecur(data)); - } - - ICAL.icalrecur.fromString = function icalrecur_fromString(str) { - var data = ICAL.icalparser.parseValue(str, "RECUR"); - return ICAL.icalrecur.fromData(data); - }; - - ICAL.icalrecur.fromIcalProperty = function icalrecur_fromIcalProperty(prop) { - var recur = new ICAL.icalrecur(); - recur.fromIcalProperty(prop); - return recur; - }; - - function icalrecur_iterator(aRule, aStart) { - this.rule = aRule; - this.dtstart = aStart; - this.by_data = ICAL.helpers.clone(aRule.parts, true); - this.days = []; - this.init(); - } - - icalrecur_iterator.prototype = { - - rule: null, - dtstart: null, - last: null, - occurrence_number: 0, - by_indices: null, - by_data: null, - - days: null, - days_index: 0, - - init: function icalrecur_iterator_init() { - this.last = this.dtstart.clone(); - var parts = this.by_data; - - this.by_indices = { - "BYSECOND": 0, - "BYMINUTE": 0, - "BYHOUR": 0, - "BYDAY": 0, - "BYMONTH": 0, - "BYWEEKNO": 0, - "BYMONTHDAY": 0 - }; - - if ("BYDAY" in parts) { - // libical does this earlier when the rule is loaded, but we postpone to - // now so we can preserve the original order. - this.sort_byday_rules(parts.BYDAY, this.rule.wkst); - } - - // If the BYYEARDAY appares, no other date rule part may appear - if ("BYYEARDAY" in parts) { - if ("BYMONTH" in parts || "BYWEEKNO" in parts || - "BYMONTHDAY" in parts || "BYDAY" in parts) { - throw new Error("Invalid BYYEARDAY rule"); - } - } - - // BYWEEKNO and BYMONTHDAY rule parts may not both appear - if ("BYWEEKNO" in parts && "BYMONTHDAY" in parts) { - throw new Error("BYWEEKNO does not fit to BYMONTHDAY"); - } - - // For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor - // BYWEEKNO may appear. - if (this.rule.freq == "MONTHLY" && - ("BYYEARDAY" in parts || "BYWEEKNO" in parts)) { - throw new Error("For MONTHLY recurrences neither BYYEARDAY nor BYWEEKNO may appear"); - } - - // For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor - // BYYEARDAY may appear. - if (this.rule.freq == "WEEKLY" && - ("BYYEARDAY" in parts || "BYMONTHDAY" in parts)) { - throw new Error("For WEEKLY recurrences neither BYMONTHDAY nor BYYEARDAY may appear"); - } - - // BYYEARDAY may only appear in YEARLY rules - if (this.rule.freq != "YEARLY" && "BYYEARDAY" in parts) { - throw new Error("BYYEARDAY may only appear in YEARLY rules"); - } - - this.last.second = this.setup_defaults("BYSECOND", "SECONDLY", this.dtstart.second); - this.last.minute = this.setup_defaults("BYMINUTE", "MINUTELY", this.dtstart.minute); - this.last.hour = this.setup_defaults("BYHOUR", "HOURLY", this.dtstart.hour); - this.last.day = this.setup_defaults("BYMONTHDAY", "DAILY", this.dtstart.day); - this.last.month = this.setup_defaults("BYMONTH", "MONTHLY", this.dtstart.month); - - if (this.rule.freq == "WEEKLY") { - if ("BYDAY" in parts) { - var parts = this.rule_day_of_week(parts.BYDAY[0]); - var pos = parts[0]; - var rule_dow = parts[1]; - var dow = rule_dow - this.last.day_of_week(); - if ((this.last.day_of_week() < rule_dow && dow >= 0) || dow < 0) { - // Initial time is after first day of BYDAY data - this.last.day += dow; - this.last.normalize(); - } - } else { - var wkMap = icalrecur_iterator._wkdayMap[this.dtstart.day_of_week()]; - parts.BYDAY = [wkMap]; - } - } - - if (this.rule.freq == "YEARLY") { - for (;;) { - this.expand_year_days(this.last.year); - if (this.days.length > 0) { - break; - } - this.increment_year(this.rule.interval); - } - - var next = ICAL.icaltime.from_day_of_year(this.days[0], this.last.year); - - this.last.day = next.day; - this.last.month = next.month; - } - - if (this.rule.freq == "MONTHLY" && this.has_by_data("BYDAY")) { - var coded_day = this.by_data.BYDAY[this.by_indices.BYDAY]; - var parts = this.rule_day_of_week(coded_day); - var pos = parts[0]; - var dow = parts[1]; - - var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); - var poscount = 0; - - if (pos >= 0) { - for (this.last.day = 1; this.last.day <= days_in_month; this.last.day++) { - if (this.last.day_of_week() == dow) { - if (++poscount == pos || pos == 0) { - break; - } - } - } - } else { - pos = -pos; - for (this.last.day = days_in_month; this.last.day != 0; this.last.day--) { - if (this.last.day_of_week() == dow) { - if (++poscount == pos) { - break; - } - } - } - } - - if (this.last.day > days_in_month || this.last.day == 0) { - throw new Error("Malformed values in BYDAY part"); - } - - } else if (this.has_by_data("BYMONTHDAY")) { - if (this.last.day < 0) { - var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); - this.last.day = days_in_month + this.last.day + 1; - } - - this.last.normalize(); - } - }, - - next: function icalrecur_iterator_next() { - var before = (this.last ? this.last.clone() : null); - - if ((this.rule.count && this.occurrence_number >= this.rule.count) || - (this.rule.until && this.last.compare(this.rule.until) > 0)) { - return null; - } - - if (this.occurrence_number == 0 && this.last.compare(this.dtstart) >= 0) { - // First of all, give the instance that was initialized - this.occurrence_number++; - return this.last; - } - - do { - var valid = 1; - - switch (this.rule.freq) { - case "SECONDLY": - this.next_second(); - break; - case "MINUTELY": - this.next_minute(); - break; - case "HOURLY": - this.next_hour(); - break; - case "DAILY": - this.next_day(); - break; - - case "WEEKLY": - this.next_week(); - break; - case "MONTHLY": - valid = this.next_month(); - break; - case "YEARLY": - this.next_year(); - break; - - default: - return null; - } - } while (!this.check_contracting_rules() || - this.last.compare(this.dtstart) < 0 || - !valid); - - // TODO is this valid? - if (this.last.compare(before) == 0) { - throw new Error("Same occurrence found twice, protecting " + - "you from death by recursion"); - } - - if (this.rule.until && this.last.compare(this.rule.until) > 0) { - return null; - } else { - this.occurrence_number++; - return this.last; - } - }, - - next_second: function next_second() { - return this.next_generic("BYSECOND", "SECONDLY", "second", "minute"); - }, - - increment_second: function increment_second(inc) { - return this.increment_generic(inc, "second", 60, "minute"); - }, - - next_minute: function next_minute() { - return this.next_generic("BYMINUTE", "MINUTELY", - "minute", "hour", "next_second"); - }, - - increment_minute: function increment_minute(inc) { - return this.increment_generic(inc, "minute", 60, "hour"); - }, - - next_hour: function next_hour() { - return this.next_generic("BYHOUR", "HOURLY", "hour", - "monthday", "next_minute"); - }, - - increment_hour: function increment_hour(inc) { - this.increment_generic(inc, "hour", 24, "monthday"); - }, - - next_day: function next_day() { - var has_by_day = ("BYDAY" in this.by_data); - var this_freq = (this.rule.freq == "DAILY"); - - if (this.next_hour() == 0) { - return 0; - } - - if (this_freq) { - this.increment_monthday(this.rule.interval); - } else { - this.increment_monthday(1); - } - - return 0; - }, - - next_week: function next_week() { - var end_of_data = 0; - - if (this.next_weekday_by_week() == 0) { - return end_of_data; - } - - if (this.has_by_data("BYWEEKNO")) { - var idx = ++this.by_indices.BYWEEKNO; - - if (this.by_indices.BYWEEKNO == this.by_data.BYWEEKNO.length) { - this.by_indices.BYWEEKNO = 0; - end_of_data = 1; - } - - // HACK should be first month of the year - this.last.month = 1; - this.last.day = 1; - - var week_no = this.by_data.BYWEEKNO[this.by_indices.BYWEEKNO]; - - this.last.day += 7 * week_no; - this.last.normalize(); - - if (end_of_data) { - this.increment_year(1); - } - } else { - // Jump to the next week - this.increment_monthday(7 * this.rule.interval); - } - - return end_of_data; - }, - - next_month: function next_month() { - var this_freq = (this.rule.freq == "MONTHLY"); - var data_valid = 1; - - if (this.next_hour() == 0) { - return data_valid; - } - - if (this.has_by_data("BYDAY") && this.has_by_data("BYMONTHDAY")) { - var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); - var notFound = true; - var day; - - for (day = last.day + 1; notFound && day <= days_in_month; day++) { - for (var dayIdx = 0; dayIdx < this.by_data.BYDAY.length; dayIdx++) { - for (var mdIdx = 0; mdIdx < this.by_data.BYMONTHDAY.length; mdIdx++) { - var parts = this.rule_day_of_week(this.by_data.BYDAY[dayIdx]); - var pos = parts[0]; - var dow = parts[1]; - var mday = this.by_data.BYMONTHDAY[mdIdx]; - - this.last.day = day; - var this_dow = this.last.day_of_week(); - - if ((pos == 0 && dow == this_dow && mday == day) || - (this.last.nth_weekday(dow, pos))) { - notFound = false; - } - } - } - } - if (day > days_in_month) { - this.last.day = 1; - this.increment_month(); - this.last.day--; - data_valid = 0; - } - - } else if (this.has_by_data("BYDAY")) { - var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); - var setpos = 0; - - if (this.has_by_data("BYSETPOS")) { - var lastday = this.last.day; - for (var day = 1; day <= days_in_month; day++) { - this.last.day = day; - if (this.is_day_in_byday(this.last) && day <= last_day) { - setpos++; - } - } - this.last.day = last_day; - } - - for (var day = this.last.day + 1; day <= days_in_month; day++) { - this.last.day = day; - - if (this.is_day_in_byday(this.last)) { - if (!this.has_by_data("BYSETPOS") || - this.check_set_position(++setpos) || - this.check_set_position(setpos - this.by_data.BYSETPOS.length - 1)) { - found = 1; - break; - } - } - } - - data_valid = found; - - if (day > days_in_month) { - this.last.day = 1; - this.increment_month(); - - if (this.is_day_in_byday(this.last)) { - if (!this.has_by_data("BYSETPOS") || this.check_set_position(1)) { - data_valid = 1; - } - } else { - data_valid = 0; - } - } - } else if (this.has_by_data("BYMONTHDAY")) { - this.by_indices.BYMONTHDAY++; - - if (this.by_indices.BYMONTHDAY >= this.by_data.BYMONTHDAY.length) { - this.by_indices.BYMONTHDAY = 0; - this.increment_month(); - } - - var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); - - var day = this.by_data.BYMONTHDAY[this.by_indices.BYMONTHDAY]; - - if (day < 0) { - day = days_in_month + day + 1; - } - - if (day > days_in_month) { - this.last.day = 1; - data_valid = this.is_day_in_byday(this.last); - } - - this.last.day = day; - } else { - this.last.day = this.by_data.BYMONTHDAY[0]; - this.increment_month(); - var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); - this.last.day = Math.min(this.last.day, days_in_month); - } - - return data_valid; - }, - - next_weekday_by_week: function next_weekday_by_week() { - var end_of_data = 0; - - if (this.next_hour() == 0) { - return end_of_data; - } - - if (!this.has_by_data("BYDAY")) { - return 1; - } - - for (;;) { - var tt = new ICAL.icaltime(); - tt.auto_normalize = false; - this.by_indices.BYDAY++; - - if (this.by_indices.BYDAY == this.by_data.BYDAY.length) { - this.by_indices.BYDAY = 0; - end_of_data = 1; - } - - var coded_day = this.by_data.BYDAY[this.by_indices.BYDAY]; - var parts = this.rule_day_of_week(coded_day); - var dow = parts[1]; - - dow -= this.rule.wkst; - if (dow < 0) { - dow += 7; - } - - tt.year = this.last.year; - tt.month = this.last.month; - tt.day = this.last.day; - - var start_of_week = tt.start_doy_week(this.rule.wkst); - - if (dow + start_of_week < 1) { - // The selected date is in the previous year - if (!end_of_data) { - continue; - } - } - - var next = ICAL.icaltime.from_day_of_year(start_of_week + dow, - this.last.year); - - this.last.day = next.day; - this.last.month = next.month; - this.last.year = next.year; - - return end_of_data; - } - }, - - next_year: function next_year() { - - if (this.next_hour() == 0) { - return 0; - } - - if (++this.days_index == this.days.length) { - this.days_index = 0; - do { - this.increment_year(this.rule.interval); - this.expand_year_days(this.last.year); - } while (this.days.length == 0); - } - - var next = ICAL.icaltime.from_day_of_year(this.days[this.days_index], - this.last.year); - - this.last.day = next.day; - this.last.month = next.month; - - return 1; - }, - - rule_day_of_week: function rule_day_of_week(dow) { - var dowMap = { - SU: 1, - MO: 2, - TU: 3, - WE: 4, - TH: 5, - FR: 6, - SA: 7 - }; - var matches = dow.match(/([+-]?[0-9])?(MO|TU|WE|TH|FR|SA|SU)/); - if (matches) { - return [parseInt(matches[1] || 0, 10), dowMap[matches[2]]] || 0; - } else { - return [0, 0]; - } - }, - - next_generic: function next_generic(aRuleType, aInterval, aDateAttr, - aFollowingAttr, aPreviousIncr) { - var has_by_rule = (aRuleType in this.by_data); - var this_freq = (this.rule.freq == aInterval); - var end_of_data = 0; - - if (aPreviousIncr && this[aPreviousIncr]() == 0) { - return end_of_data; - } - - if (has_by_rule) { - this.by_indices[aRuleType]++; - var idx = this.by_indices[aRuleType]; - var dta = this.by_data[aRuleType]; - - if (this.by_indices[aRuleType] == dta.length) { - this.by_indices[aRuleType] = 0; - end_of_data = 1; - } - this.last[aDateAttr] = dta[this.by_indices[aRuleType]]; - } else if (this_freq) { - this["increment_" + aDateAttr](this.rule.interval); - } - - if (has_by_rule && end_of_data && this_freq) { - this["increment_" + aFollowingAttr](1); - } - - return end_of_data; - }, - - increment_monthday: function increment_monthday(inc) { - for (var i = 0; i < inc; i++) { - var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); - this.last.day++; - - if (this.last.day > days_in_month) { - this.last.day -= days_in_month; - this.increment_month(); - } - } - }, - - increment_month: function increment_month() { - if (this.has_by_data("BYMONTH")) { - this.by_indices.BYMONTH++; - - if (this.by_indices.BYMONTH == this.by_data.BYMONTH.length) { - this.by_indices.BYMONTH = 0; - this.increment_year(1); - } - - this.last.month = this.by_data.BYMONTH[this.by_indices.BYMONTH]; - } else { - var inc; - if (this.rule.freq == "MONTHLY") { - this.last.month += this.rule.interval; - } else { - this.last.month++; - } - - this.last.month--; - var years = ICAL.helpers.trunc(this.last.month / 12); - this.last.month %= 12; - this.last.month++; - - if (years != 0) { - this.increment_year(years); - } - } - }, - - increment_year: function increment_year(inc) { - this.last.year += inc; - }, - - increment_generic: function increment_generic(inc, aDateAttr, - aFactor, aNextIncrement) { - this.last[aDateAttr] += inc; - var nextunit = ICAL.helpers.trunc(this.last[aDateAttr] / aFactor); - this.last[aDateAttr] %= aFactor; - if (nextunit != 0) { - this["increment_" + aNextIncrement](nextunit); - } - }, - - has_by_data: function has_by_data(aRuleType) { - return (aRuleType in this.rule.parts); - }, - - expand_year_days: function expand_year_days(aYear) { - var t = new ICAL.icaltime(); - this.days = []; - - // We need our own copy with a few keys set - var parts = {}; - var rules = ["BYDAY", "BYWEEKNO", "BYMONTHDAY", "BYMONTH", "BYYEARDAY"]; - for (var p in rules) { - var part = rules[p]; - if (part in this.rule.parts) { - parts[part] = this.rule.parts[part]; - } - } - - if ("BYMONTH" in parts && "BYWEEKNO" in parts) { - var valid = 1; - var validWeeks = {}; - t.year = aYear; - t.isDate = true; - - for (var monthIdx = 0; monthIdx < this.by_data.BYMONTH.length; monthIdx++) { - var month = this.by_data.BYMONTH[monthIdx]; - t.month = month; - t.day = 1; - var first_week = t.week_number(this.rule.wkst); - t.day = ICAL.icaltime.days_in_month(month, aYear); - var last_week = t.week_number(this.rule.wkst); - for (monthIdx = first_week; monthIdx < last_week; monthIdx++) { - validWeeks[monthIdx] = 1; - } - } - - for (var weekIdx = 0; weekIdx < this.by_data.BYWEEKNO.length && valid; weekIdx++) { - var weekno = this.by_data.BYWEEKNO[weekIdx]; - if (weekno < 52) { - valid &= validWeeks[weekIdx]; - } else { - valid = 0; - } - } - - if (valid) { - delete parts.BYMONTH; - } else { - delete parts.BYWEEKNO; - } - } - - var partCount = Object.keys(parts).length; - - if (partCount == 0) { - var t = this.dtstart.clone(); - t.year = this.last.year; - this.days.push(t.day_of_year()); - } else if (partCount == 1 && "BYMONTH" in parts) { - for (var monthkey in this.by_data.BYMONTH) { - var t2 = this.dtstart.clone(); - t2.year = aYear; - t2.month = this.by_data.BYMONTH[monthkey]; - t2.isDate = true; - this.days.push(t2.day_of_year()); - } - } else if (partCount == 1 && "BYMONTHDAY" in parts) { - for (var monthdaykey in this.by_data.BYMONTHDAY) { - var t2 = this.dtstart.clone(); - t2.day = this.by_data.BYMONTHDAY[monthdaykey]; - t2.year = aYear; - t2.isDate = true; - this.days.push(t2.day_of_year()); - } - } else if (partCount == 2 && - "BYMONTHDAY" in parts && - "BYMONTH" in parts) { - for (var monthkey in this.by_data.BYMONTH) { - for (var monthdaykey in this.by_data.BYMONTHDAY) { - t.day = this.by_data.BYMONTHDAY[monthdaykey]; - t.month = this.by_data.BYMONTH[monthkey]; - t.year = aYear; - t.isDate = true; - - this.days.push(t.day_of_year()); - } - } - } else if (partCount == 1 && "BYWEEKNO" in parts) { - // TODO unimplemented in libical - } else if (partCount == 2 && - "BYWEEKNO" in parts && - "BYMONTHDAY" in parts) { - // TODO unimplemented in libical - } else if (partCount == 1 && "BYDAY" in parts) { - this.days = this.days.concat(this.expand_by_day(aYear)); - } else if (partCount == 2 && "BYDAY" in parts && "BYMONTH" in parts) { - for (var monthkey in this.by_data.BYMONTH) { - var days_in_month = ICAL.icaltime.days_in_month(month, aYear); - - t.year = aYear; - t.month = this.by_data.BYMONTH[monthkey]; - t.day = 1; - t.isDate = true; - - var first_dow = t.day_of_week(); - var doy_offset = t.day_of_year() - 1; - - t.day = days_in_month; - var last_dow = t.day_of_week(); - - if (this.has_by_data("BYSETPOS")) { - var set_pos_counter = 0; - var by_month_day = []; - for (var day = 1; day <= days_in_month; day++) { - t.day = day; - if (this.is_day_in_byday(t)) { - by_month_day.push(day); - } - } - - for (var spIndex = 0; spIndex < by_month_day.length; spIndex++) { - if (this.check_set_position(spIndex + 1) || - this.check_set_position(spIndex - by_month_day.length)) { - this.days.push(doy_offset + by_month_day[spIndex]); - } - } - } else { - for (var daycodedkey in this.by_data.BYDAY) { - var coded_day = this.by_data.BYDAY[daycodedkey]; - var parts = this.rule_day_of_week(coded_day); - var dow = parts[0]; - var pos = parts[1]; - - var first_matching_day = ((dow + 7 - first_dow) % 7) + 1; - var last_matching_day = days_in_month - ((last_dow + 7 - dow) % 7); - - if (pos == 0) { - for (var day = first_matching_day; day <= days_in_month; day += 7) { - this.days.push(doy_offset + day); - } - } else if (pos > 0) { - month_day = first_matching_day + (pos - 1) * 7; - - if (month_day <= days_in_month) { - this.days.push(doy_offset + month_day); - } - } else { - month_day = last_matching_day + (pos + 1) * 7; - - if (month_day > 0) { - this.days.push(doy_offset + month_day); - } - } - } - } - } - } else if (partCount == 2 && "BYDAY" in parts && "BYMONTHDAY" in parts) { - var expandedDays = this.expand_by_day(aYear); - - for (var daykey in expandedDays) { - var day = expandedDays[daykey]; - var tt = ICAL.icaltime.from_day_of_year(day, aYear); - if (this.by_data.BYMONTHDAY.indexOf(tt.day) >= 0) { - this.days.push(day); - } - } - } else if (partCount == 3 && - "BYDAY" in parts && - "BYMONTHDAY" in parts && - "BYMONTH" in parts) { - var expandedDays = this.expand_by_day(aYear); - - for (var daykey in expandedDays) { - var day = expandedDays[daykey]; - var tt = ICAL.icaltime.from_day_of_year(day, aYear); - - if (this.by_data.BYMONTH.indexOf(tt.month) >= 0 && - this.by_data.BYMONTHDAY.indexOf(tt.day) >= 0) { - this.days.push(day); - } - } - } else if (partCount == 2 && "BYDAY" in parts && "BYWEEKNO" in parts) { - var expandedDays = this.expand_by_day(aYear); - - for (var daykey in expandedDays) { - var day = expandedDays[daykey]; - var tt = ICAL.icaltime.from_day_of_year(day, aYear); - var weekno = tt.week_number(this.rule.wkst); - - if (this.by_data.BYWEEKNO.indexOf(weekno)) { - this.days.push(day); - } - } - } else if (partCount == 3 && - "BYDAY" in parts && - "BYWEEKNO" in parts && - "BYMONTHDAY" in parts) { - // TODO unimplemted in libical - } else if (partCount == 1 && "BYYEARDAY" in parts) { - this.days = this.days.concat(this.by_data.BYYEARDAY); - } else { - this.days = []; - } - return 0; - }, - - expand_by_day: function expand_by_day(aYear) { - - var days_list = []; - var tmp = this.last.clone(); - - tmp.year = aYear; - tmp.month = 1; - tmp.day = 1; - tmp.isDate = true; - - var start_dow = tmp.day_of_week(); - - tmp.month = 12; - tmp.day = 31; - tmp.isDate = true; - - var end_dow = tmp.day_of_week(); - var end_year_day = tmp.day_of_year(); - - for (var daykey in this.by_data.BYDAY) { - var day = this.by_data.BYDAY[daykey]; - var parts = this.rule_day_of_week(day); - var pos = parts[0]; - var dow = parts[1]; - - if (pos == 0) { - var tmp_start_doy = ((dow + 7 - start_dow) % 7) + 1; - - for (var doy = tmp_start_doy; doy <= end_year_day; doy += 7) { - days_list.push(doy); - } - - } else if (pos > 0) { - var first; - if (dow >= start_dow) { - first = dow - start_dow + 1; - } else { - first = dow - start_dow + 8; - } - - days_list.push(first + (pos - 1) * 7); - } else { - var last; - pos = -pos; - - if (dow <= end_dow) { - last = end_year_day - end_dow + dow; - } else { - last = end_year_day - end_dow + dow - 7; - } - - days_list.push(last - (pos - 1) * 7); - } - } - return days_list; - }, - - is_day_in_byday: function is_day_in_byday(tt) { - for (var daykey in this.by_data.BYDAY) { - var day = this.by_data.BYDAY[daykey]; - var parts = this.rule_day_of_week(day); - var pos = parts[0]; - var dow = parts[1]; - var this_dow = tt.day_of_week(); - - if ((pos == 0 && dow == this_dow) || - (tt.nth_weekday(dow, pos) == tt.day)) { - return 1; - } - } - - return 0; - }, - - check_set_position: function check_set_position(aPos) { - return ("BYSETPOS" in this.by_data && - this.by_data.BYSETPOS.indexOf(aPos)); - }, - - sort_byday_rules: function icalrecur_sort_byday_rules(aRules, aWeekStart) { - for (var i = 0; i < aRules.length; i++) { - for (var j = 0; j < i; j++) { - var one = this.rule_day_of_week(aRules[j])[1]; - var two = this.rule_day_of_week(aRules[i])[1]; - one -= aWeekStart; - two -= aWeekStart; - if (one < 0) one += 7; - if (two < 0) two += 7; - - if (one > two) { - var tmp = aRules[i]; - aRules[i] = aRules[j]; - aRules[j] = tmp; - } - } - } - }, - - check_contract_restriction: function check_contract_restriction(aRuleType, v) { - var indexMapValue = icalrecur_iterator._indexMap[aRuleType]; - var ruleMapValue = icalrecur_iterator._expandMap[this.rule.freq][indexMapValue]; - var pass = false; - - if (aRuleType in this.by_data && - ruleMapValue == icalrecur_iterator.CONTRACT) { - for (var bydatakey in this.by_data[aRuleType]) { - if (this.by_data[aRuleType][bydatakey] == v) { - pass = true; - break; - } - } - } else { - // Not a contracting byrule or has no data, test passes - pass = true; - } - return pass; - }, - - check_contracting_rules: function check_contracting_rules() { - var dow = this.last.day_of_week(); - var weekNo = this.last.week_number(this.rule.wkst); - var doy = this.last.day_of_year(); - - return (this.check_contract_restriction("BYSECOND", this.last.second) && - this.check_contract_restriction("BYMINUTE", this.last.minute) && - this.check_contract_restriction("BYHOUR", this.last.hour) && - this.check_contract_restriction("BYDAY", dow) && - this.check_contract_restriction("BYWEEKNO", weekNo) && - this.check_contract_restriction("BYMONTHDAY", this.last.day) && - this.check_contract_restriction("BYMONTH", this.last.month) && - this.check_contract_restriction("BYYEARDAY", doy)); - }, - - setup_defaults: function setup_defaults(aRuleType, req, deftime) { - var indexMapValue = icalrecur_iterator._indexMap[aRuleType]; - var ruleMapValue = icalrecur_iterator._expandMap[this.rule.freq][indexMapValue]; - - if (ruleMapValue != icalrecur_iterator.CONTRACT) { - if (!(aRuleType in this.by_data)) { - this.by_data[aRuleType] = [deftime]; - } - if (this.rule.freq != req) { - return this.by_data[aRuleType][0]; - } - } - return deftime; - } - }; - - icalrecur_iterator._wkdayMap = ["", "SU", "MO", "TU", "WE", "TH", "FR", "SA"]; - - icalrecur_iterator._indexMap = { - "BYSECOND": 0, - "BYMINUTE": 1, - "BYHOUR": 2, - "BYDAY": 3, - "BYMONTHDAY": 4, - "BYYEARDAY": 5, - "BYWEEKNO": 6, - "BYMONTH": 7, - "BYSETPOS": 8 - }; - - icalrecur_iterator._expandMap = { - "SECONDLY": [1, 1, 1, 1, 1, 1, 1, 1], - "MINUTELY": [2, 1, 1, 1, 1, 1, 1, 1], - "HOURLY": [2, 2, 1, 1, 1, 1, 1, 1], - "DAILY": [2, 2, 2, 1, 1, 1, 1, 1], - "WEEKLY": [2, 2, 2, 2, 3, 3, 1, 1], - "MONTHLY": [2, 2, 2, 2, 2, 3, 3, 1], - "YEARLY": [2, 2, 2, 2, 2, 2, 2, 2] - }; - icalrecur_iterator.UNKNOWN = 0; - icalrecur_iterator.CONTRACT = 1; - icalrecur_iterator.EXPAND = 2; - icalrecur_iterator.ILLEGAL = 3; -})(); -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ - -"use strict"; - -(typeof(ICAL) === 'undefined')? ICAL = {} : ''; - -(function() { - ICAL.foldLength = 75; - ICAL.newLineChar = "\n"; - - /** - * Return a parsed ICAL object to the ICAL format. - * - * @param {Object} object parsed ical string. - * @return {String} ICAL string. - */ - ICAL.stringify = function ICALStringify(object) { - return ICAL.serializer.serializeToIcal(object); - }; - - /** - * Parse an ICAL object or string. - * - * @param {String|Object} ical ical string or pre-parsed object. - * @param {Boolean} decorate when true decorates object data types. - * - * @return {Object|ICAL.icalcomponent} The raw data or decorated icalcomponent. - */ - ICAL.parse = function ICALParse(ical) { - var state = ICAL.helpers.initState(ical, 0); - - while (state.buffer.length) { - var line = ICAL.helpers.unfoldline(state); - var lexState = ICAL.helpers.initState(line, state.lineNr); - if (line.match(/^\s*$/) && state.buffer.match(/^\s*$/)) { - break; - } - - var lineData = ICAL.icalparser.lexContentLine(lexState); - ICAL.icalparser.parseContentLine(state, lineData); - state.lineNr++; - } - - return state.currentData; - }; -}()); /* sax js - LICENSE: https://github.com/isaacs/sax-js/blob/master/LICENSE */ // wrapper for non-node envs ;(function (sax) { @@ -5629,6 +1020,16 @@ function write (chunk) { Exports.prototype = { /** + * Associate and register module. + * + * @param {String} path uri associated with module. + * @param {Object} object module object. + */ + register: function(path, object) { + paths[path] = object; + }, + + /** * Unified require between browser/node. * Path is relative to this file so you * will want to use it like this from any depth. @@ -5655,7 +1056,7 @@ function write (chunk) { * Maps exports to a file path. */ set exports(val) { - return paths[this.path] = val; + this.register(this.path, val); }, get exports() { @@ -5678,11 +1079,11 @@ function write (chunk) { return new Exports(path); } + Module.register = Exports.prototype.register; Module.require = Exports.prototype.require; Module.exports = Module; Module._paths = paths; - /** * Reference self as exports * which also happens to be the constructor @@ -5712,145 +1113,6 @@ function write (chunk) { module )); -(function(module, ns) { - // Credit: Andreas Gal - I removed the callback / xhr logic - - // Iterate over all entries if x is an array, otherwise just call fn on x. - - /* Pattern for an individual entry: name:value */ - var ENTRY = /^([A-Za-z0-9-]+)((?:;[A-Za-z0-9-]+=(?:"[^"]+"|[^";:,]+)(?:,(?:"[^"]+"|[^";:,]+))*)*):(.*)$/; - - /* Pattern for an individual parameter: name=value[,value] */ - var PARAM = /;([A-Za-z0-9-]+)=((?:"[^"]+"|[^";:,]+)(?:,(?:"[^"]+"|[^";:,]+))*)/g; - - /* Pattern for an individual parameter value: value | "value" */ - var PARAM_VALUE = /,?("[^"]+"|[^";:,]+)/g; - - // Parse a calendar in iCal format. - function ParseICal(text) { - // Parse the text into an object graph - var lines = text.replace(/\r/g, '').split('\n'); - var tos = Object.create(null); - var stack = [tos]; - - // Parse parameters for an entry. Foramt: <param>=<pvalue>[;...] - function parseParams(params) { - var map = Object.create(null); - var param = PARAM.exec(params); - while (param) { - var values = []; - var value = PARAM_VALUE.exec(param[2]); - while (value) { - values.push(value[1].replace(/^"(.*)"$/, '$1')); - value = PARAM_VALUE.exec(param[2]); - } - map[param[1].toLowerCase()] = (values.length > 1 ? values : values[0]); - param = PARAM.exec(params); - } - return map; - } - - // Add a property to the current object. If a property with the same name - // already exists, turn it into an array. - function add(prop, value, params) { - if (params) - value = { parameters: parseParams(params), value: value }; - if (prop in tos) { - var previous = tos[prop]; - if (previous instanceof Array) { - previous.push(value); - return; - } - value = [previous, value]; - } - tos[prop] = value; - } - - for (var n = 0; n < lines.length; ++n) { - var line = lines[n]; - // check whether the line continues (next line stats with space or tab) - var nextLine; - while ((nextLine = lines[n+1]) && (nextLine[0] === ' ' || nextLine[0] === '\t')) { - line += nextLine.substr(1); - ++n; - continue; - } - // parse the entry, format is 'PROPERTY:VALUE' - var matches = ENTRY.exec(line); - - if (!matches) { - throw new Error('invalid format'); - } - - var prop = matches[1].toLowerCase(); - var params = matches[2]; - var value = matches[3]; - switch (prop) { - case 'begin': - var obj = Object.create(null); - add(value.toLowerCase(), obj); - stack.push(tos = obj); - break; - case 'end': - stack.pop(); - tos = stack[stack.length - 1]; - if (stack.length == 1) { - var cal = stack[0]; - if (typeof cal.vcalendar !== 'object' || cal.vcalendar instanceof Array) { - throw new Error('single vcalendar object expected'); - } - - return cal.vcalendar; - } - break; - default: - add(prop, value, params); - break; - } - } - throw new Error('unexpected end of file'); - } - - function Value(v) { - return (typeof v !== 'object') ? v : v.value; - } - - function Parameter(v, name) { - if (typeof v !== 'object') - return undefined; - return v.parameters[name]; - } - - // Parse a time specification. - function ParseDateTime(v) { - var dt = Value(v); - if (Parameter(v, 'VALUE') === 'DATE') { - // 20081202 - return new Date(dt.substr(0, 4), dt.substr(4, 2), dt.substr(6, 2)); - } - v = Value(v); - // 20120426T130000Z - var year = dt.substr(0, 4); - var month = dt.substr(4, 2) - 1; - var day = dt.substr(6, 2); - var hour = dt.substr(9, 2); - var min = dt.substr(11, 2); - var sec = dt.substr(13, 2); - if (dt[15] == 'Z') { - return new Date(Date.UTC(year, month, day, hour, min, sec)); - } - return new Date(year, month, day, hour, min, sec); - } - - module.exports = ParseICal; - -}.apply( - this, - (this.Caldav) ? - [Caldav('ical'), Caldav] : - [module, require('./caldav')] -)); - /** @namespace */ @@ -6861,16 +2123,10 @@ function write (chunk) { )); (function(module, ns) { - if (typeof(ICAL) === 'undefined') { - require('../../ical.js'); - } - - var HTTP_STATUS = /([0-9]{3,3})/; - var Base = ns.require('sax/base'); - var TextHandler = Base.create({ - name: 'text', + var CalendarDataHandler = Base.create({ + name: 'calendar data', //don't add text only elements //to the stack as objects @@ -6882,12 +2138,37 @@ function write (chunk) { //and value is the text. ontext: function(data) { var handler = this.handler; - this.current[this.currentTag[handler.tagField]] = data; + this.current[this.currentTag[handler.tagField]] = + CalendarDataHandler.parseICAL(data); } }); - var CalendarDataHandler = Base.create({ - name: 'calendar data', + /** + * Default ical parser handler. + * + * XXX: Feels a little hacky but works... + */ + CalendarDataHandler.parseICAL = function(input) { + return input; + }; + + module.exports = CalendarDataHandler; + +}.apply( + this, + (this.Caldav) ? + [Caldav('sax/calendar_data_handler'), Caldav] : + [module, require('../caldav')] +)); +(function(module, ns) { + + var HTTP_STATUS = /([0-9]{3,3})/; + + var Base = ns.require('sax/base'); + var CalendarDataHandler = ns.require('sax/calendar_data_handler'); + + var TextHandler = Base.create({ + name: 'text', //don't add text only elements //to the stack as objects @@ -6899,11 +2180,10 @@ function write (chunk) { //and value is the text. ontext: function(data) { var handler = this.handler; - this.current[this.currentTag[handler.tagField]] = ICAL.parse(data); + this.current[this.currentTag[handler.tagField]] = data; } }); - var HrefHandler = Base.create({ name: 'href', @@ -7551,8 +2831,9 @@ function write (chunk) { (function(module, ns) { module.exports = { - Abstract: ns.require('sax/abstract'), - CalendarQuery: ns.require('sax/dav_response') + Base: ns.require('sax/base'), + CalendarDataHandler: ns.require('sax/calendar_data_handler'), + DavResponse: ns.require('sax/dav_response') }; }.apply( @@ -7752,7 +3033,6 @@ function write (chunk) { var exports = module.exports; - exports.Ical = ns.require('ical'); exports.Responder = ns.require('responder'); exports.Sax = ns.require('sax'); exports.Template = ns.require('template'); |