From 7a43bc130ad3eb7beb0dc20a495ca178268a2667 Mon Sep 17 00:00:00 2001 From: James Lal Date: Wed, 1 Aug 2012 10:19:14 -0700 Subject: update ical.js fix 'use strict' errors in workers --- caldav.js | 3711 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 1959 insertions(+), 1752 deletions(-) (limited to 'caldav.js') diff --git a/caldav.js b/caldav.js index 69e360c..e3befea 100644 --- a/caldav.js +++ b/caldav.js @@ -3,7 +3,11 @@ /* 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/. */ -var ICAL = ICAL || {}; +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; + +/** + * Helper functions used in various places within ical.js + */ ICAL.helpers = { initState: function initState(aLine, aLineNr) { return { @@ -24,12 +28,12 @@ ICAL.helpers = { }; }, - dumpn: function () { + dumpn: function() { if (!ICAL.debug) { - return; + return null; } - if(typeof (console) !== 'undefined' && 'log' in console) { + if (typeof (console) !== 'undefined' && 'log' in console) { ICAL.helpers.dumpn = function consoleDumpn(input) { return console.log(input); } @@ -42,9 +46,9 @@ ICAL.helpers = { return ICAL.helpers.dumpn(arguments[0]); }, - mixin: function (obj, data) { - if(data) { - for(var k in data) { + mixin: function(obj, data) { + if (data) { + for (var k in data) { obj[k] = data[k]; } } @@ -60,13 +64,13 @@ ICAL.helpers = { var moreLines = true; var line = ""; - while(moreLines) { + while (moreLines) { moreLines = false; var pos = aState.buffer.search(/\r?\n/); - if(pos > -1) { + if (pos > -1) { var len = (aState.buffer[pos] == "\r" ? 2 : 1); - var nextChar = aState.buffer.substr(pos + len, 1) - if(nextChar.match(/^[ \t]$/)) { + 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); @@ -87,46 +91,46 @@ ICAL.helpers = { var result = ""; var line = aLine || ""; - while(line.length) { + 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)) { + ensureKeyExists: function(obj, key, defvalue) { + if (!(key in obj)) { obj[key] = defvalue; } }, - hasKey: function (obj, key) { - return(obj && key in obj && obj[key]); + hasKey: function(obj, key) { + return (obj && key in obj && obj[key]); }, pad2: function pad(data) { - return("00" + data).substr(-2); + return ("00" + data).substr(-2); }, trunc: function trunc(number) { - return(number < 0 ? Math.ceil(number) : Math.floor(number)); + return (number < 0 ? Math.ceil(number) : Math.floor(number)); } }; -var ICAL = ICAL || {}; +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; -(function () { +(function() { ICAL.serializer = { - serializeToIcal: function (obj, name, isParam) { - if(obj && obj.icalclass) { + serializeToIcal: function(obj, name, isParam) { + if (obj && obj.icalclass) { return obj.toString(); } var str = ""; - if(obj.type == "COMPONENT") { + if (obj.type == "COMPONENT") { str = "BEGIN:" + obj.name + ICAL.newLineChar; - for each(var sub in obj.value) { - str += this.serializeToIcal(sub) + ICAL.newLineChar; + for (var subkey in obj.value) { + str += this.serializeToIcal(obj.value[subkey]) + ICAL.newLineChar; } str += "END:" + obj.name; } else { @@ -147,8 +151,8 @@ var ICAL = ICAL || {}; // TODO enforce uppercase when parsing // TODO optionally preserve value types that are default but explicitly set // TODO floating timezone -var ICAL = ICAL || {}; -(function () { +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; +(function() { /* NOTE: I'm not sure this is the latest syntax... { @@ -176,18 +180,21 @@ var ICAL = ICAL || {}; 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 + ":" : ""); + 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) { + if ("buffer" in aState) { + if (aState.buffer) { message += " before '" + aState.buffer + "'"; } else { message += " at end of line"; } } - if("line" in aState) { + if ("line" in aState) { message += " in '" + aState.line + "'"; } this.message = message; @@ -198,15 +205,17 @@ var ICAL = ICAL || {}; // create stack try { throw new Error(); - } catch(e) { + } catch (e) { var split = e.stack.split('\n'); split.shift(); this.stack = split.join('\n'); } } - ParserError.prototype = Object.create(Error.prototype); - ParserError.prototype.constructor = ParserError; + ParserError.prototype = { + __proto__: Error.prototype, + constructor: ParserError + }; var parser = {}; ICAL.icalparser = parser; @@ -221,9 +230,9 @@ var ICAL = ICAL || {}; lineData.name = parser.lexName(aState); // Read Paramaters, if there are any. - if(aState.buffer.substr(0, 1) == ";") { + if (aState.buffer.substr(0, 1) == ";") { lineData.parameters = {}; - while(aState.buffer.substr(0, 1) == ";") { + while (aState.buffer.substr(0, 1) == ";") { aState.buffer = aState.buffer.substr(1); var param = parser.lexParam(aState); lineData.parameters[param.name] = param.value; @@ -233,16 +242,14 @@ var ICAL = ICAL || {}; // Read the value parser.expectRE(aState, /^:/, "Expected ':'"); lineData.value = parser.lexValue(aState); - //FIXME:? There may be some cases where this is needed - //but its perfectly possible that this line is blank. - 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"); + var match = parser.expectRE(aState, /^([A-Za-z0-9-]+)/, + "Expected IANA Token"); return match[1]; } @@ -252,7 +259,7 @@ var ICAL = ICAL || {}; var match = parser.expectRE(aState, /^X-/, error); // Vendor ID - if(match = parser.expectOptionalRE(aState, /^([A-Za-z0-9]+-)/, error)) { + if ((match = parser.expectOptionalRE(aState, /^([A-Za-z0-9]+-)/, error))) { value += match[1]; } @@ -303,7 +310,8 @@ var ICAL = ICAL || {}; // 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"); + var match = parser.expectRE(aState, /^([^"\x00-\x08\x0A-\x1F\x7F]*)/, + "Invalid Param Value"); parser.expectRE(aState, /^"/, "Expecting Quote Character"); return match[1]; } @@ -311,7 +319,8 @@ var ICAL = ICAL || {}; 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"); + var match = parser.expectRE(aState, /^([^";:,\x00-\x08\x0A-\x1F\x7F]*)/, + "Invalid Param Value"); return match[1]; } @@ -320,10 +329,10 @@ var ICAL = ICAL || {}; parser.parseContentLine = function parseContentLine(aState, aLineData) { - switch(aLineData.name) { + switch (aLineData.name) { case "BEGIN": var newdata = ICAL.helpers.initComponentData(aLineData.value); - if(aState.currentData) { + 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. @@ -334,28 +343,29 @@ var ICAL = ICAL || {}; 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.currentData.name != aLineData.value) { + throw new ParserError(aState, "Unexpected END:" + aLineData.value + + ", expected END:" + aState.currentData.name); } - if(aState.parentData.length) { + if (aState.parentData.length) { aState.currentData = aState.parentData.pop(); } break; default: - ICAL.helpers.dumpn("parse " + aLineData.toSource()); + ICAL.helpers.dumpn("parse " + aLineData.toString()); parser.detectParameterType(aLineData); parser.detectValueType(aLineData); - ICAL.helpers.dumpn("parse " + aLineData.toSource()); + ICAL.helpers.dumpn("parse " + aLineData.toString()); aState.currentData.value.push(aLineData); break; } }, parser.detectParameterType = function detectParameterType(aLineData) { - for(var name in aLineData.parameters) { + for (var name in aLineData.parameters) { var paramType = "TEXT"; - if(name in ICAL.design.param && "valueType" in ICAL.design.param[name]) { + if (name in ICAL.design.param && "valueType" in ICAL.design.param[name]) { paramType = ICAL.design.param[name].valueType; } var paramData = { @@ -370,16 +380,17 @@ var ICAL = ICAL || {}; 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]) { + 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.toSource()); + 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)) { + if (!(valueType in ICAL.design.value)) { throw new ParserError(aLineData, "Invalid VALUE Type '" + valueType); } @@ -398,10 +409,10 @@ var ICAL = ICAL || {}; return values; } - if(aLineData.name in ICAL.design.property) { - if(ICAL.design.property[aLineData.name].multiValue) { + 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) { + } else if (ICAL.design.property[aLineData.name].structuredValue) { aLineData.value = unwrapMultiValue(aLineData.value, ";"); } else { aLineData.value = [aLineData.value]; @@ -410,9 +421,9 @@ var ICAL = ICAL || {}; aLineData.value = [aLineData.value]; } - if("unescape" in ICAL.design.value[valueType]) { + if ("unescape" in ICAL.design.value[valueType]) { var unescaper = ICAL.design.value[valueType].unescape; - for(var idx in aLineData.value) { + for (var idx in aLineData.value) { aLineData.value[idx] = unescaper(aLineData.value[idx], aLineData.name); } } @@ -420,39 +431,48 @@ var ICAL = ICAL || {}; return aLineData; } - parser.validateValue = function validateValue(aLineData, aValueType, aValue, aCheckParams) { + 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 + // 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.toSource()); + if (!aValue.match) { + ICAL.helpers.dumpn("MAAA: " + aValue + " ? " + aValue.toString()); } - if(valueData.matches) { + if (valueData.matches) { // Test against regex - if(!aValue.match(valueData.matches)) { - throw new ParserError(aLineData, "Value '" + aValue + "' for " + aLineData.name + " is not " + aValueType); + if (!aValue.match(valueData.matches)) { + throw new ParserError(aLineData, "Value '" + aValue + "' for " + + aLineData.name + " is not " + aValueType); } - } else if("validate" in valueData) { + } 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) { + } 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 (valueData.values.indexOf(aValue) < 0) { + throw new ParserError(aLineData, "Value for " + aLineData.name + + " is not a " + aValueType); } } - if(aCheckParams && "requireParam" in valueData) { - for(var param in valueData.requireParam) { - if(!("parameters" in aLineData) || !(param in aLineData.parameters) || aLineData.parameters[param].value != valueData.requireParam[param]) { - throw new ParserError(aLineData, "Value requires " + param + "=" + valueData.requireParam[param]); + 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]); } } } @@ -463,12 +483,12 @@ var ICAL = ICAL || {}; 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]) { + 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); @@ -476,10 +496,10 @@ var ICAL = ICAL || {}; }; parser.stringifyProperty = function stringifyProperty(aLineData) { - ICAL.helpers.dumpn("Stringify: " + aLineData.toSource()); + ICAL.helpers.dumpn("Stringify: " + aLineData.toString()); var str = aLineData.name; - if(aLineData.parameters) { - for(var key in aLineData.parameters) { + if (aLineData.parameters) { + for (var key in aLineData.parameters) { str += ";" + key + "=" + aLineData.parameters[key].value; } } @@ -492,21 +512,23 @@ var ICAL = ICAL || {}; parser.stringifyValue = function stringifyValue(aLineData) { function arrayStringMap(arr, func) { var newArr = []; - for(var idx in arr) { + for (var idx in arr) { newArr[idx] = func(arr[idx].toString()); } return newArr; } - if(aLineData) { + if (aLineData) { var values = aLineData.value; - if(aLineData.type in ICAL.design.value && "escape" in ICAL.design.value[aLineData.type]) { + 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) { + if (aLineData.name in ICAL.design.property && + ICAL.design.property[aLineData.name].structuredValue) { separator = ";"; } @@ -519,11 +541,11 @@ var ICAL = ICAL || {}; parser.parseDateOrDateTime = function parseDateOrDateTime(aState) { var data = parser.parseDate(aState); - if(parser.expectOptionalRE(aState, /^T/)) { + if (parser.expectOptionalRE(aState, /^T/)) { // This has a time component, parse it var time = parser.parseTime(aState); - if(parser.expectOptionalRE(aState, /^Z/)) { + if (parser.expectOptionalRE(aState, /^Z/)) { data.timezone = "Z"; } ICAL.helpers.mixin(data, time); @@ -537,7 +559,7 @@ var ICAL = ICAL || {}; var time = parser.parseTime(aState); - if(parser.expectOptionalRE(aState, /^Z/)) { + if (parser.expectOptionalRE(aState, /^Z/)) { data.timezone = "Z"; } @@ -546,7 +568,8 @@ var ICAL = ICAL || {}; }; parser.parseDate = function parseDate(aState) { - var match = parser.expectRE(aState, /^((\d{4})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01]))/, "Expected YYYYMMDD Date"); + 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), @@ -556,7 +579,8 @@ var ICAL = ICAL || {}; }; parser.parseTime = function parseTime(aState) { - var match = parser.expectRE(aState, /^(([01][0-9]|2[0-3])([0-5][0-9])([0-5][0-9]|60))/, "Expected HHMMSS Time"); + 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), @@ -568,8 +592,9 @@ var ICAL = ICAL || {}; var error = "Expected Duration Value"; function parseDurSecond(aState) { + var secMatch = parser.expectRE(aState, /^((\d+)S)/, "Expected Seconds"); return { - seconds: parseInt(parser.expectRE(aState, /^((\d+)S)/, "Expected Seconds")[2], 10) + seconds: parseInt(secMatch[2], 10) }; } @@ -578,9 +603,9 @@ var ICAL = ICAL || {}; var minutes = parser.expectRE(aState, /^((\d+)M)/, "Expected Minutes"); try { data = parseDurSecond(aState); - } catch(e) { + } catch (e) { // seconds are optional, its ok - if(!(e instanceof ParserError)) { + if (!(e instanceof ParserError)) { throw e; } } @@ -593,9 +618,9 @@ var ICAL = ICAL || {}; var hours = parser.expectRE(aState, /^((\d+)H)/, "Expected Hours"); try { data = parseDurMinute(aState); - } catch(e) { + } catch (e) { // seconds are optional, its ok - if(!(e instanceof ParserError)) { + if (!(e instanceof ParserError)) { throw e; } } @@ -612,7 +637,8 @@ var ICAL = ICAL || {}; function parseDurTime(aState) { parser.expectRE(aState, /^T/, "Expected Time Value"); - return parser.parseAlternative(aState, parseDurHour, parseDurMinute, parseDurSecond); + return parser.parseAlternative(aState, parseDurHour, + parseDurMinute, parseDurSecond); } function parseDurDate(aState) { @@ -621,14 +647,14 @@ var ICAL = ICAL || {}; try { data = parseDurTime(aState); - } catch(e) { + } catch (e) { // Its ok if this fails - if(!(e instanceof ParserError)) { + if (!(e instanceof ParserError)) { throw e; } } - if(data) { + if (data) { data.days = days[2]; } else { data = { @@ -640,7 +666,8 @@ var ICAL = ICAL || {}; var factor = parser.expectRE(aState, /^([+-]?P)/, error); - var durData = parser.parseAlternative(aState, parseDurDate, parseDurTime, parseDurWeek); + var durData = parser.parseAlternative(aState, parseDurDate, + parseDurTime, parseDurWeek); parser.expectEnd(aState, "Junk at end of DURATION value"); durData.factor = (factor[1] == "-P" ? -1 : 1); @@ -651,11 +678,12 @@ var ICAL = ICAL || {}; var dtime = parser.parseDateTime(aState); parser.expectRE(aState, /\//, "Expected '/'"); - var dtdur = parser.parseAlternative(aState, parser.parseDateTime, parser.parseDuration); + var dtdur = parser.parseAlternative(aState, parser.parseDateTime, + parser.parseDuration); var data = { start: dtime - } - if("factor" in dtdur) { + }; + if ("factor" in dtdur) { data.duration = dtdur; } else { data.end = dtdur; @@ -667,7 +695,8 @@ var ICAL = ICAL || {}; // TODO this function is quite cludgy, maybe it should be done differently function parseFreq(aState) { parser.expectRE(aState, /^FREQ=/, "Expected Frequency"); - var match = parser.expectRE(aState, /^(SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY)/, "Exepected Frequency Value"); + var ruleRE = /^(SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY)/; + var match = parser.expectRE(aState, ruleRE, "Exepected Frequency Value"); return { "FREQ": match[1] }; @@ -699,7 +728,8 @@ var ICAL = ICAL || {}; function parseBySecond(aState) { function parseSecond(aState) { - var value = parser.expectRE(aState, /^(60|[1-5][0-9]|[0-9])/, "Expected Second")[1]; + 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"); @@ -711,7 +741,8 @@ var ICAL = ICAL || {}; function parseByMinute(aState) { function parseMinute(aState) { - var value = parser.expectRE(aState, /^([1-5][0-9]|[0-9])/, "Expected Minute")[1]; + 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"); @@ -723,7 +754,8 @@ var ICAL = ICAL || {}; function parseByHour(aState) { function parseHour(aState) { - var value = parser.expectRE(aState, /^(2[0-3]|1[0-9]|[0-9])/, "Expected Hour")[1]; + 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"); @@ -737,16 +769,17 @@ var ICAL = ICAL || {}; function parseWkDayNum(aState) { var value = ""; var match = parser.expectOptionalRE(aState, /^([+-])/); - if(match) { - value += match[1] + if (match) { + value += match[1]; } match = parser.expectOptionalRE(aState, /^(5[0-3]|[1-4][0-9]|[1-9])/); - if(match) { - value += match[1] + if (match) { + value += match[1]; } - match = parser.expectRE(aState, /^(SU|MO|TU|WE|TH|FR|SA)/, "Expected Week Ordinals"); + var wkDayRE = /^(SU|MO|TU|WE|TH|FR|SA)/; + match = parser.expectRE(aState, wkDayRE, "Expected Week Ordinals"); value += match[1]; return value; } @@ -761,8 +794,8 @@ var ICAL = ICAL || {}; function parseMoDayNum(aState) { var value = ""; var match = parser.expectOptionalRE(aState, /^([+-])/); - if(match) { - value += match[1] + if (match) { + value += match[1]; } match = parser.expectRE(aState, /^(3[01]|[12][0-9]|[1-9])/); @@ -780,11 +813,12 @@ var ICAL = ICAL || {}; function parseYearDayNum(aState) { var value = ""; var match = parser.expectOptionalRE(aState, /^([+-])/); - if(match) { - value += match[1] + if (match) { + value += match[1]; } - match = parser.expectRE(aState, /^(36[0-6]|3[0-5][0-9]|[12][0-9][0-9]|[1-9][0-9]|[1-9])/); + 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); } @@ -799,8 +833,8 @@ var ICAL = ICAL || {}; function parseWeekNum(aState) { var value = ""; var match = parser.expectOptionalRE(aState, /^([+-])/); - if(match) { - value += match[1] + if (match) { + value += match[1]; } match = parser.expectRE(aState, /^(5[0-3]|[1-4][0-9]|[1-9])/); @@ -816,7 +850,8 @@ var ICAL = ICAL || {}; function parseByMonth(aState) { function parseMonthNum(aState) { - var match = parser.expectRE(aState, /^(1[012]|[1-9])/, "Expected Month number"); + 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"); @@ -828,7 +863,8 @@ var ICAL = ICAL || {}; function parseBySetPos(aState) { function parseSpList(aState) { - var value = parser.expectRE(aState, /^(36[0-6]|3[0-5][0-9]|[12][0-9][0-9]|[1-9][0-9]|[1-9])/)[1]; + 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); } @@ -841,7 +877,8 @@ var ICAL = ICAL || {}; function parseWkst(aState) { parser.expectRE(aState, /^WKST=/, "Expected WKST"); - var match = parser.expectRE(aState, /^(SU|MO|TU|WE|TH|FR|SA)/, "Expected Weekday Name"); + var wkstRE = /^(SU|MO|TU|WE|TH|FR|SA)/; + var match = parser.expectRE(aState, wkstRE, "Expected Weekday Name"); return { "WKST": match[1] }; @@ -858,8 +895,8 @@ var ICAL = ICAL || {}; // One or more rule parts var value = parser.parseList(aState, parseRulePart, ";"); var data = {}; - for each(var mbr in value) { - ICAL.helpers.mixin(data, mbr); + for (var key in value) { + ICAL.helpers.mixin(data, value[key]); } // Make sure there's no junk at the end @@ -868,10 +905,12 @@ var ICAL = ICAL || {}; }; parser.parseUtcOffset = function parseUtcOffset(aState) { - if(aState.buffer == "-0000" || aState.buffer == "-000000") { - throw new ParserError(aState, "Invalid value for utc offset: " + aState.buffer); + if (aState.buffer == "-0000" || aState.buffer == "-000000") { + throw new ParserError(aState, + "Invalid value for utc offset: " + aState.buffer); } - var match = parser.expectRE(aState, /^(([+-])([01][0-9]|2[0-3])([0-5][0-9])([0-5][0-9])?)$/, "Expected valid utc offset"); + 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), @@ -879,18 +918,18 @@ var ICAL = ICAL || {}; }; }; - parser.parseAlternative = function parseAlternative(aState /* parserFunc, ... */ ) { + parser.parseAlternative = function parseAlternative(aState /*, parserFunc, ... */) { var tokens = null; - var args = Array.slice(arguments); + var args = Array.prototype.slice.call(arguments); var parser; args.shift(); var errors = []; - while(!tokens && (parser = args.shift())) { + while (!tokens && (parser = args.shift())) { try { tokens = parser(aState); - } catch(e) { - if(e instanceof ParserError) { + } catch (e) { + if (e instanceof ParserError) { errors.push(e); tokens = null; } else { @@ -899,7 +938,7 @@ var ICAL = ICAL || {}; } } - if(!tokens) { + if (!tokens) { var message = errors.join("\nOR ") || "No Tokens found"; throw new ParserError(aState, message); } @@ -912,7 +951,7 @@ var ICAL = ICAL || {}; listvals.push(aElementFunc(aState)); var re = new RegExp("^" + aSeparator + ""); - while(parser.expectOptionalRE(aState, re)) { + while (parser.expectOptionalRE(aState, re)) { listvals.push(aElementFunc(aState)); } return listvals; @@ -920,7 +959,7 @@ var ICAL = ICAL || {}; parser.expectOptionalRE = function expectOptionalRE(aState, aRegex) { var match = aState.buffer.match(aRegex); - if(match) { + if (match) { var count = ("1" in match ? match[1].length : match[0].length); aState.buffer = aState.buffer.substr(count); aState.character += count; @@ -930,14 +969,14 @@ var ICAL = ICAL || {}; parser.expectRE = function expectRE(aState, aRegex, aErrorMessage) { var match = parser.expectOptionalRE(aState, aRegex); - if(!match) { + if (!match) { throw new ParserError(aState, aErrorMessage); } return match; }; parser.expectEnd = function expectEnd(aState, aErrorMessage) { - if(aState.buffer.length > 0) { + if (aState.buffer.length > 0) { throw new ParserError(aState, aErrorMessage); } } @@ -981,7 +1020,8 @@ var ICAL = ICAL || {}; { category: [{ type: "text" - value: "multi1,multi2,multi3" // have icalcomponent take apart the multivalues + // have icalcomponent take apart the multivalues + value: "multi1,multi2,multi3" },{ type "text" value: "otherprop1" @@ -996,7 +1036,11 @@ var ICAL = ICAL || {}; /* 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/. */ -var ICAL = ICAL || {}; +(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 @@ -1037,7 +1081,8 @@ ICAL.design = { }, "PARTSTAT": { // TODO These values are actually different per-component - values: ["NEEDS-ACTION", "ACCEPTED", "DECLINED", "TENTATIVE", "DELEGATED", "COMPLETED", "IN-PROCESS"], + values: ["NEEDS-ACTION", "ACCEPTED", "DECLINED", "TENTATIVE", + "DELEGATED", "COMPLETED", "IN-PROCESS"], allowXName: true, allowIanaToken: true }, @@ -1053,7 +1098,8 @@ ICAL.design = { allowIanaToken: true }, "ROLE": { - values: ["REQ-PARTICIPANT", "CHAIR", "OPT-PARTICIPANT", "NON-PARTICIPANT"], + values: ["REQ-PARTICIPANT", "CHAIR", + "OPT-PARTICIPANT", "NON-PARTICIPANT"], allowXName: true, allowIanaToken: true }, @@ -1067,10 +1113,12 @@ ICAL.design = { matches: /^\// }, "VALUE": { - values: ["BINARY", "BOOLEAN", "CAL-ADDRESS", "DATE", "DATE-TIME", "DURATION", "FLOAT", "INTEGER", "PERIOD", "RECUR", "TEXT", "TIME", "URI", "UTC-OFFSET"], + 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! @@ -1081,13 +1129,13 @@ ICAL.design = { requireParam: { "ENCODING": "BASE64" }, - decorate: function (aString) { + decorate: function(aString) { return ICAL.icalbinary.fromString(aString); } }, "BOOLEAN": { values: ["TRUE", "FALSE"], - decorate: function (aValue) { + decorate: function(aValue) { return ICAL.icalvalue.fromString(aValue, "BOOLEAN"); } }, @@ -1095,7 +1143,7 @@ ICAL.design = { // needs to be an uri }, "DATE": { - validate: function (aValue) { + validate: function(aValue) { var state = { buffer: aValue }; @@ -1103,12 +1151,12 @@ ICAL.design = { ICAL.icalparser.expectEnd(state, "Junk at end of DATE value"); return data; }, - decorate: function (aValue) { + decorate: function(aValue) { return ICAL.icaltime.fromString(aValue); } }, "DATE-TIME": { - validate: function (aValue) { + validate: function(aValue) { var state = { buffer: aValue }; @@ -1117,12 +1165,12 @@ ICAL.design = { return data; }, - decorate: function (aValue) { + decorate: function(aValue) { return ICAL.icaltime.fromString(aValue); } }, "DURATION": { - validate: function (aValue) { + validate: function(aValue) { var state = { buffer: aValue }; @@ -1130,24 +1178,24 @@ ICAL.design = { ICAL.icalparser.expectEnd(state, "Junk at end of DURATION value"); return data; }, - decorate: function (aValue) { + decorate: function(aValue) { return ICAL.icalduration.fromString(aValue); } }, "FLOAT": { matches: /^[+-]?\d+\.\d+$/, - decorate: function (aValue) { + decorate: function(aValue) { return ICAL.icalvalue.fromString(aValue, "FLOAT"); } }, "INTEGER": { matches: /^[+-]?\d+$/, - decorate: function (aValue) { + decorate: function(aValue) { return ICAL.icalvalue.fromString(aValue, "INTEGER"); } }, "PERIOD": { - validate: function (aValue) { + validate: function(aValue) { var state = { buffer: aValue }; @@ -1156,12 +1204,12 @@ ICAL.design = { return data; }, - decorate: function (aValue) { + decorate: function(aValue) { return ICAL.icalperiod.fromString(aValue); } }, "RECUR": { - validate: function (aValue) { + validate: function(aValue) { var state = { buffer: aValue }; @@ -1172,17 +1220,17 @@ ICAL.design = { decorate: function decorate(aValue) { return ICAL.icalrecur.fromString(aValue); - }, + } }, "TEXT": { matches: /.*/, - decorate: function (aValue) { + decorate: function(aValue) { return ICAL.icalvalue.fromString(aValue, "TEXT"); }, - unescape: function (aValue, aName) { - return aValue.replace(/\\\\|\\;|\\,|\\[Nn]/g, function (str) { - switch(str) { + unescape: function(aValue, aName) { + return aValue.replace(/\\\\|\\;|\\,|\\[Nn]/g, function(str) { + switch (str) { case "\\\\": return "\\"; case "\\;": @@ -1199,8 +1247,8 @@ ICAL.design = { }, escape: function escape(aValue, aName) { - return aValue.replace(/\\|;|,|\n/g, function (str) { - switch(str) { + return aValue.replace(/\\|;|,|\n/g, function(str) { + switch (str) { case "\\": return "\\\\"; case ";": @@ -1213,11 +1261,11 @@ ICAL.design = { return str; } }); - }, + } }, "TIME": { - validate: function (aValue) { + validate: function(aValue) { var state = { buffer: aValue }; @@ -1233,7 +1281,7 @@ ICAL.design = { }, "UTC-OFFSET": { - validate: function (aValue) { + validate: function(aValue) { var state = { buffer: aValue }; @@ -1242,7 +1290,7 @@ ICAL.design = { return data; }, - decorate: function (aValue) { + decorate: function(aValue) { return ICAL.icalutcoffset.fromString(aValue); } } @@ -1353,7 +1401,7 @@ ICAL.design = { }, "URL": { defaultType: "URI" - }, + } }, component: { @@ -1361,16 +1409,16 @@ ICAL.design = { 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/. */ -"use strict"; -var ICAL = ICAL || {}; -(function () { + +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; +(function() { ICAL.icalcomponent = function icalcomponent(data, parent) { this.wrappedJSObject = this; this.parent = parent; @@ -1391,7 +1439,7 @@ var ICAL = ICAL || {}; }, fromData: function fromData(data) { - if(!data) { + if (!data) { data = ICAL.helpers.initComponentData(null); } this.data = data; @@ -1407,9 +1455,9 @@ var ICAL = ICAL || {}; var value = this.data.value; - for(var key in value) { + for (var key in value) { var keyname = value[key].name; - if(value[key].type == "COMPONENT") { + 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]); @@ -1423,8 +1471,8 @@ var ICAL = ICAL || {}; undecorate: function undecorate() { var newdata = []; - for each(var data in this.data.value) { - newdata.push(data.undecorate()); + for (var key in this.data.value) { + newdata.push(this.data.value[key].undecorate()); } return { name: this.name, @@ -1435,13 +1483,15 @@ var ICAL = ICAL || {}; getFirstSubcomponent: function getFirstSubcomponent(aType) { var comp = null; - if(aType) { + if (aType) { var ucType = aType.toUpperCase(); - if(ucType in this.components && this.components[ucType] && this.components[ucType].length > 0) { + 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) { + for (var thiscomp in this.components) { comp = this.components[thiscomp][0]; break; } @@ -1451,16 +1501,16 @@ var ICAL = ICAL || {}; getAllSubcomponents: function getAllSubcomponents(aType) { var comps = []; - if(aType && aType != "ANY") { + if (aType && aType != "ANY") { var ucType = aType.toUpperCase(); - if(ucType in this.components) { - for(var compKey in this.components[ucType]) { + 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]) { + for (var compName in this.components) { + for (var compKey in this.components[compName]) { comps.push(this.components[compName][compKey]); } } @@ -1471,7 +1521,7 @@ var ICAL = ICAL || {}; addSubcomponent: function addSubcomponent(aComp, aCompName) { var ucName, comp; var comp; - if(aComp.icalclass == "icalcomponent") { + if (aComp.icalclass == "icalcomponent") { ucName = aComp.name; comp = aComp.clone(); comp.parent = this; @@ -1487,9 +1537,9 @@ var ICAL = ICAL || {}; removeSubcomponent: function removeSubComponent(aName) { var ucName = aName.toUpperCase(); - for each(var comp in this.components[ucName]) { - var pos = this.data.value.indexOf(comp); - if(pos > -1) { + 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); } } @@ -1499,19 +1549,19 @@ var ICAL = ICAL || {}; hasProperty: function hasProperty(aName) { var ucName = aName.toUpperCase(); - return(ucName in this.properties); + return (ucName in this.properties); }, getFirstProperty: function getFirstProperty(aName) { var prop = null; - if(aName) { + if (aName) { var ucName = aName.toUpperCase(); - if(ucName in this.properties && this.properties[ucName]) { + if (ucName in this.properties && this.properties[ucName]) { prop = this.properties[ucName][0]; } } else { - for each(var p in this.properties) { - prop = p; + for (var p in this.properties) { + prop = this.properties[p]; break; } } @@ -1526,13 +1576,13 @@ var ICAL = ICAL || {}; getAllProperties: function getAllProperties(aName) { var props = []; - if(aName && aName != "ANY") { + if (aName && aName != "ANY") { var ucType = aName.toUpperCase(); - if(ucType in this.properties) { + if (ucType in this.properties) { props = this.properties[ucType].concat([]); } } else { - for(var propName in this.properties) { + for (var propName in this.properties) { props = props.concat(this.properties[propName]); } } @@ -1553,23 +1603,23 @@ var ICAL = ICAL || {}; addProperty: function addProperty(aProp) { var prop = aProp; - if(aProp.parent) { + 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.toSource()); + 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 each(var prop in this.properties[ucName]) { - var pos = this.data.value.indexOf(prop); - if(pos > -1) { + 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); } } @@ -1578,8 +1628,8 @@ var ICAL = ICAL || {}; clearAllProperties: function clearAllProperties() { this.properties = {}; - for(var i = this.data.value.length - 1; i >= 0; i--) { - if(this.data.value[i].type != "COMPONENT") { + for (var i = this.data.value.length - 1; i >= 0; i--) { + if (this.data.value[i].type != "COMPONENT") { delete this.data.value[i]; } } @@ -1631,7 +1681,7 @@ var ICAL = ICAL || {}; toString: function toString() { var str = ICAL.helpers.foldline("BEGIN:" + this.name) + ICAL.newLineChar; - for(var key in this.data.value) { + for (var key in this.data.value) { str += this.data.value[key].toString() + ICAL.newLineChar; } str += ICAL.helpers.foldline("END:" + this.name); @@ -1651,10 +1701,10 @@ var ICAL = ICAL || {}; * 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/. */ -"use strict"; -var ICAL = ICAL || {}; -(function () { + +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; +(function() { ICAL.icalproperty = function icalproperty(data, parent) { this.wrappedJSObject = this; this.parent = parent; @@ -1672,8 +1722,8 @@ var ICAL = ICAL || {}; }, fromData: function fromData(aData) { - if(!aData.name) { - ICAL.helpers.dumpn("Missing name: " + aData.toSource()); + if (!aData.name) { + ICAL.helpers.dumpn("Missing name: " + aData.toString()); } this.name = aData.name; this.data = aData; @@ -1681,10 +1731,11 @@ var ICAL = ICAL || {}; delete this.data.name; }, - undecorate: function () { - var values = [] - for each(var val in this.data.value) { - if("undecorate" in val) { + 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); @@ -1695,7 +1746,7 @@ var ICAL = ICAL || {}; type: this.data.type, value: values }; - if(this.data.parameters) { + if (this.data.parameters) { obj.parameters = this.data.parameters; } return obj; @@ -1706,7 +1757,7 @@ var ICAL = ICAL || {}; name: this.name, type: this.data.type, value: this.data.value, - parameters: this.data.parameters, + parameters: this.data.parameters }); }, @@ -1721,11 +1772,11 @@ var ICAL = ICAL || {}; }, getFirstValue: function getValue() { - return(this.data.value ? this.data.value[0] : null); + return (this.data.value ? this.data.value[0] : null); }, getValues: function getValues() { - return(this.data.value ? this.data.value : []); + return (this.data.value ? this.data.value : []); }, setValue: function setValue(aValue, aType) { @@ -1735,9 +1786,10 @@ var ICAL = ICAL || {}; setValues: function setValues(aValues, aType) { var newValues = []; var newType = null; - for each(var value in aValues) { - if(value.icalclass && value.icaltype) { - if(newType && newType != value.icaltype) { + 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; @@ -1745,20 +1797,20 @@ var ICAL = ICAL || {}; newValues.push(value); } else { var type; - if(aType) { + if (aType) { type = aType; - } else if(typeof value == "string") { + } else if (typeof value == "string") { type = "TEXT"; - } else if(typeof value == "number") { + } else if (typeof value == "number") { type = (Math.floor(value) == value ? "INTEGER" : "FLOAT"); - } else if(typeof value == "boolean") { + } else if (typeof value == "boolean") { type = "BOOLEAN"; value = (value ? "TRUE" : "FALSE"); } else { throw new ParserError(null, "Invalid value: " + value); } - if(newType && newType != type) { + if (newType && newType != type) { throw new Error("All values must be of the same type!"); } else { newType = type; @@ -1784,7 +1836,7 @@ var ICAL = ICAL || {}; getParameterValue: function getParameter(aName) { var value = null; var ucName = aName.toUpperCase(); - if(ICAL.helpers.hasKey(this.data.parameters, ucName)) { + if (ICAL.helpers.hasKey(this.data.parameters, ucName)) { value = this.data.parameters[ucName].value; } return value; @@ -1793,7 +1845,7 @@ var ICAL = ICAL || {}; getParameterType: function getParameterType(aName) { var type = null; var ucName = aName.toUpperCase(); - if(ICAL.helpers.hasKey(this.data.parameters, ucName)) { + if (ICAL.helpers.hasKey(this.data.parameters, ucName)) { type = this.data.parameters[ucName].type; } return type; @@ -1808,25 +1860,27 @@ var ICAL = ICAL || {}; value: aValue }; - if(aName == "VALUE") { + if (aName == "VALUE") { this.data.type = aValue; // TODO revalidate value } }, countParameters: function countParmeters() { - return(this.data.parameters ? Object.keys(this.data.parameters).length : 0); + // 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)) { + if (ICAL.helpers.hasKey(this.data.parameters, ucName)) { delete this.data.parameters[ucName]; } } }; - ICAL.icalproperty.fromData = function (aData) { + ICAL.icalproperty.fromData = function(aData) { return new ICAL.icalproperty(aData); }; })(); @@ -1834,10 +1888,10 @@ var ICAL = ICAL || {}; * 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/. */ -"use strict"; -var ICAL = ICAL || {}; -(function () { + +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; +(function() { ICAL.icalvalue = function icalvalue(aData, aParent, aType) { this.parent = aParent; this.fromData(aData, aType); @@ -1853,8 +1907,8 @@ var ICAL = ICAL || {}; var type = (aType || (aData && aData.type) || this.icaltype); this.icaltype = type; - if(aData && type) { - aData.type = type + if (aData && type) { + aData.type = type; } this.data = aData; @@ -1869,7 +1923,7 @@ var ICAL = ICAL || {}; return this.toString(); }, - toString: function () { + toString: function() { return this.data.value.toString(); } }; @@ -1922,13 +1976,14 @@ var ICAL = ICAL || {}; //if (typeof this.window['atob'] == 'function') { // return atob(data); //} - var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz0123456789+/="; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc = "", tmp_arr = []; - if(!data) { + if (!data) { return data; } @@ -1952,7 +2007,7 @@ var ICAL = ICAL || {}; var r = data.length % 3; - return(r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); + return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); }, @@ -1974,13 +2029,14 @@ var ICAL = ICAL || {}; //if (typeof this.window['btoa'] == 'function') { // return btoa(data); //} - var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz0123456789+/="; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, dec = "", tmp_arr = []; - if(!data) { + if (!data) { return data; } @@ -1998,9 +2054,9 @@ var ICAL = ICAL || {}; o2 = bits >> 8 & 0xff; o3 = bits & 0xff; - if(h3 == 64) { + if (h3 == 64) { tmp_arr[ac++] = String.fromCharCode(o1); - } else if(h4 == 64) { + } else if (h4 == 64) { tmp_arr[ac++] = String.fromCharCode(o1, o2); } else { tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); @@ -2029,7 +2085,7 @@ var ICAL = ICAL || {}; icaltype: "UTC-OFFSET", fromData: function fromData(aData) { - if(aData) { + if (aData) { this.hours = aData.hours; this.minutes = aData.minutes; this.factor = aData.factor; @@ -2037,7 +2093,9 @@ var ICAL = ICAL || {}; }, toString: function toString() { - return(this.factor == 1 ? "+" : "-") + ICAL.helpers.pad2(this.hours) + ICAL.helpers.pad2(this.minutes); + return (this.factor == 1 ? "+" : "-") + + ICAL.helpers.pad2(this.hours) + + ICAL.helpers.pad2(this.minutes); } }; ICAL.icalvalue._createFromString(ICAL.icalutcoffset); @@ -2046,10 +2104,10 @@ var ICAL = ICAL || {}; * 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/. */ -"use strict"; -var ICAL = ICAL || {}; -(function () { + +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; +(function() { ICAL.icalperiod = function icalperiod(aData) { this.wrappedJSObject = this; this.fromData(aData); @@ -2064,7 +2122,7 @@ var ICAL = ICAL || {}; icaltype: "PERIOD", getDuration: function duration() { - if(this.duration) { + if (this.duration) { return this.duration; } else { return this.end.subtractDate(this.start); @@ -2076,7 +2134,7 @@ var ICAL = ICAL || {}; }, fromData: function fromData(data) { - if(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); @@ -2096,10 +2154,10 @@ var ICAL = ICAL || {}; * 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/. */ -"use strict"; -var ICAL = ICAL || {}; -(function () { + +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; +(function() { ICAL.icalduration = function icalduration(data) { this.wrappedJSObject = this; this.fromData(data); @@ -2121,8 +2179,9 @@ var ICAL = ICAL || {}; }, 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); + 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) { @@ -2132,7 +2191,7 @@ var ICAL = ICAL || {}; this.days = ICAL.helpers.trunc(secs / 86400); // If we have a flat number of weeks, use them. - if(this.days % 7 == 0) { + if (this.days % 7 == 0) { this.weeks = this.days / 7; this.days = 0; } else { @@ -2152,16 +2211,18 @@ var ICAL = ICAL || {}; }, fromData: function fromData(aData) { - const propsToCopy = ["weeks", "days", "hours", "minutes", "seconds", "isNegative"]; - for each(var key in propsToCopy) { - if(aData && key in aData) { - this[key] = aData[key]; + 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[key] = 0; + this[prop] = 0; } } - if(aData && "factor" in aData) { + if (aData && "factor" in aData) { this.isNegative = (aData.factor == "-1"); } }, @@ -2178,7 +2239,7 @@ var ICAL = ICAL || {}; compare: function compare(aOther) { var thisSeconds = this.toSeconds(); var otherSeconds = aOther.toSeconds(); - return(thisSeconds > otherSeconds) - (thisSeconds < otherSeconds); + return (thisSeconds > otherSeconds) - (thisSeconds < otherSeconds); }, normalize: function normalize() { @@ -2187,20 +2248,20 @@ var ICAL = ICAL || {}; }, toString: function toString() { - if(this.toSeconds() == 0) { + if (this.toSeconds() == 0) { return "PT0S"; } else { var str = ""; - if(this.isNegative) str += "-"; + if (this.isNegative) str += "-"; str += "P"; - if(this.weeks) str += this.weeks + "W"; - if(this.days) str += this.days + "D"; + if (this.weeks) str += this.weeks + "W"; + if (this.days) str += this.days + "D"; - if(this.hours || this.minutes || this.seconds) { + 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"; + if (this.hours) str += this.hours + "H"; + if (this.minutes) str += this.minutes + "M"; + if (this.seconds) str += this.seconds + "S"; } return str; } @@ -2208,7 +2269,7 @@ var ICAL = ICAL || {}; }; ICAL.icalduration.fromSeconds = function icalduration_from_seconds(aSeconds) { - return(new ICAL.icalduration()).fromSeconds(); + return (new ICAL.icalduration()).fromSeconds(); }; ICAL.icalduration.fromString = function icalduration_from_string(aStr) { @@ -2224,10 +2285,10 @@ var ICAL = ICAL || {}; * 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/. */ -"use strict"; -var ICAL = ICAL || {}; -(function () { + +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; +(function() { ICAL.icaltimezone = function icaltimezone(data) { this.wrappedJSObject = this; this.fromData(data); @@ -2251,21 +2312,23 @@ var ICAL = ICAL || {}; icalclass: "icaltimezone", fromData: function fromData(aData) { - const propsToCopy = ["tzid", "location", "tznames", "latitude", "longitude"]; - for each(var key in propsToCopy) { - if(aData && key in aData) { - this[key] = aData[key]; + 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[key] = 0; + this[prop] = 0; } } this.expand_end_year = 0; this.expand_start_year = 0; - if(aData && "component" in aData) { - if(typeof aData.component == "string") { + if (aData && "component" in aData) { + if (typeof aData.component == "string") { this.component = this.componentFromString(aData.component); - } else if(aData.component.icalclass == "icalcomponent") { + } else if (aData.component.icalclass == "icalcomponent") { this.component = aData.component.clone(); } else { this.component = eval(aData.component.toSource()); @@ -2282,13 +2345,13 @@ var ICAL = ICAL || {}; }, utc_offset: function utc_offset(tt) { - if(this == ICAL.icaltimezone.utc_timezone || this == ICAL.icaltimezone.local_timezone) { + 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) { + if (!this.changes || this.changes.length == 0) { return 0; } @@ -2305,36 +2368,39 @@ var ICAL = ICAL || {}; var change_num_to_use = -1; var step = 1; - for(;;) { + for (;;) { + //TODO: we should not use eval anywhere and + // this only works in gecko (toSource) var change = eval(this.changes[change_num].toSource()); // TODO clone - if(change.utc_offset < change.prev_utc_offset) { + 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); + 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.toSource()); + ICAL.helpers.dumpn("Compare" + cmp + " / " + change.toString()); - if(cmp >= 0) { + if (cmp >= 0) { change_num_to_use = change_num; } else { step = -1; } - if(step == -1 && change_num_to_use != -1) { + if (step == -1 && change_num_to_use != -1) { break; } change_num += step; - if(change_num < 0) { + if (change_num < 0) { return 0; } - if(change_num >= this.changes.length) { + if (change_num >= this.changes.length) { break; } } @@ -2342,16 +2408,20 @@ var ICAL = ICAL || {}; 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) { + if (utc_offset_change < 0 && change_num_to_use > 0) { + //TODO: we should not use eval anywhere and + // this only works in gecko (toSource) var tmp_change = eval(zone_change.toSource()); // TODO copy - ICAL.icaltimezone.adjust_change(tmp_change, 0, 0, 0, tmp_change.prev_utc_offset); + 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) { + 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) { + if (zone_change.is_daylight != want_daylight && + prev_zone_change.is_daylight == want_daylight) { zone_change = prev_zone_change; } } @@ -2366,13 +2436,13 @@ var ICAL = ICAL || {}; middle = 0; var upper = this.changes.length; - while(lower < upper) { + 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) { + if (cmp == 0) { break; - } else if(cmp > 0) { + } else if (cmp > 0) { upper = middle; } else { lower = middle; @@ -2383,34 +2453,35 @@ var ICAL = ICAL || {}; }, ensure_coverage: function ensure_coverage(aYear) { - if(ICAL.icaltimezone._minimum_expansion_year == -1) { + 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) { + 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) { + if (changes_end_year > ICAL.icaltimezone.MAX_YEAR) { changes_end_year = ICAL.icaltimezone.MAX_YEAR; } - if(!this.changes || this.expand_end_year < aYear) { + 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. - for each(var comp in this.component.getAllSubcomponents()) { - this.expand_vtimezone(comp, aYear, 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 || []); @@ -2421,8 +2492,10 @@ var ICAL = ICAL || {}; }, expand_vtimezone: function expand_vtimezone(aComponent, aYear, changes) { - if(!aComponent.hasProperty("DTSTART") || !aComponent.hasProperty("TZOFFSETTO") || !aComponent.hasProperty("TZOFFSETFROM")) { - return; + if (!aComponent.hasProperty("DTSTART") || + !aComponent.hasProperty("TZOFFSETTO") || + !aComponent.hasProperty("TZOFFSETFROM")) { + return null; } var dtstart = aComponent.getFirstProperty("DTSTART").getFirstValue(); @@ -2439,7 +2512,7 @@ var ICAL = ICAL || {}; return changebase; } - if(!aComponent.hasProperty("RRULE") && !aComponent.hasProperty("RDATE")) { + if (!aComponent.hasProperty("RRULE") && !aComponent.hasProperty("RDATE")) { var change = init_changes(); change.year = dtstart.year; change.month = dtstart.month; @@ -2448,16 +2521,19 @@ var ICAL = ICAL || {}; change.minute = dtstart.minute; change.second = dtstart.second; - ICAL.icaltimezone.adjust_change(change, 0, 0, 0, - change.prev_utc_offset); + ICAL.icaltimezone.adjust_change(change, 0, 0, 0, + -change.prev_utc_offset); changes.push(change); } else { - for each(var rdate in aComponent.getAllProperties("RDATE")) { + 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) { + if (rdate.time.isDate) { change.hour = dtstart.hour; change.minute = dtstart.minute; change.second = dtstart.second; @@ -2466,8 +2542,9 @@ var ICAL = ICAL || {}; 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); + if (rdate.time.zone == ICAL.icaltimezone.utc_timezone) { + ICAL.icaltimezone.adjust_change(change, 0, 0, 0, + -change.prev_utc_offset); } } @@ -2479,7 +2556,7 @@ var ICAL = ICAL || {}; var change = init_changes(); - if(rrule.until && rrule.until.zone == ICAL.icaltimezone.utc_timezone) { + 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; } @@ -2487,9 +2564,9 @@ var ICAL = ICAL || {}; var iterator = rrule.iterator(dtstart); var occ; - while((occ = iterator.next())) { + while ((occ = iterator.next())) { var change = init_changes(); - if(occ.year > aYear || !occ) { + if (occ.year > aYear || !occ) { break; } @@ -2501,7 +2578,8 @@ var ICAL = ICAL || {}; change.second = occ.second; change.isDate = occ.isDate; - ICAL.icaltimezone.adjust_change(change, 0, 0, 0, - change.prev_utc_offset); + ICAL.icaltimezone.adjust_change(change, 0, 0, 0, + -change.prev_utc_offset); changes.push(change); } } @@ -2510,35 +2588,38 @@ var ICAL = ICAL || {}; }, toString: function toString() { - return(this.tznames ? this.tznames : this.tzid); + 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.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.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.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.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.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; + 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) { + 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; } @@ -2548,6 +2629,8 @@ var ICAL = ICAL || {}; utc_offset = to_zone.utc_offset(tt); tt.adjust(0, 0, 0, utc_offset); + + return null; }; ICAL.icaltimezone.fromData = function icaltimezone_fromData(aData) { @@ -2574,1777 +2657,1885 @@ var ICAL = ICAL || {}; * 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/. */ -"use strict"; -var ICAL = ICAL || {}; -(function () { - ICAL.icalrecur = function icalrecur(data) { + +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; +(function() { + ICAL.icaltime = function icaltime(data) { this.wrappedJSObject = this; - this.parts = {}; this.fromData(data); }; - ICAL.icalrecur.prototype = { - - parts: null, + ICAL.icaltime.prototype = { - interval: 1, - wkst: 1, - until: null, - count: null, - freq: null, - icalclass: "icalrecur", - icaltype: "RECUR", + year: 0, + month: 1, + day: 1, - iterator: function (aStart) { - return new icalrecur_iterator(this, aStart); - }, + hour: 0, + minute: 0, + second: 0, - clone: function clone() { - return ICAL.icalrecur.fromData(this); - //return ICAL.icalrecur.fromIcalProperty(this.toIcalProperty()); - }, + isDate: false, + zone: null, - is_finite: function isfinite() { - return(this.count || this.until); - }, + auto_normalize: false, + icalclass: "icaltime", + icaltype: "DATE-TIME", - is_by_count: function isbycount() { - return(this.count && !this.until); + clone: function icaltime_clone() { + return new ICAL.icaltime(this); }, - addComponent: function addPart(aType, aValue) { - if(!(aType in this.parts)) { - this.parts[aType] = [aValue]; - } else { - this.parts[aType].push(aValue); - } + reset: function icaltime_reset() { + this.fromData(ICAL.icaltime.epoch_time); + this.zone = ICAL.icaltimezone.utc_timezone; }, - setComponent: function setComponent(aType, aValues) { - this.parts[aType] = aValues; + 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 + }); }, - 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; + 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); }, - 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; + 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 next; + return this; }, fromData: function fromData(aData) { - const propsToCopy = ["freq", "count", "wkst", "interval"]; - for each(var key in propsToCopy) { - if(aData && key.toUpperCase() in aData) { - this[key] = aData[key.toUpperCase()]; - // TODO casing sucks, fix the parser! - } else if(aData && key in 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]; - // TODO casing sucks, fix the parser! + } 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 && "until" in aData && aData.until) { - this.until = aData.until.clone(); + if (aData && "timezone" in aData && aData.timezone == "Z") { + this.zone = ICAL.icaltimezone.utc_timezone; + } + if (aData && "zone" in aData) { + this.zone = aData.zone; } - const partsToCopy = ["BYSECOND", "BYMINUTE", "BYHOUR", "BYDAY", "BYMONTHDAY", "BYYEARDAY", "BYWEEKNO", "BYMONTH", "BYSETPOS"] - this.parts = {}; - if(aData) { - for each(var key in partsToCopy) { - if(key in aData) { - this.parts[key] = aData[key]; - // TODO casing sucks, fix the parser! - } - } - // TODO oh god, make it go away! - if(aData.parts) { - for each(var key in partsToCopy) { - if(key in aData.parts) { - this.parts[key] = aData.parts[key]; - // TODO casing sucks, fix the parser! - } - } - } + if (!this.zone) { + this.zone = ICAL.icaltimezone.local_timezone; } - return this; - }, - toString: function icalrecur_toString() { - // TODO retain order - var str = "FREQ=" + this.freq; - if(this.count) { - str += ";COUNT=" + this.count; + if (aData && "auto_normalize" in aData) { + this.auto_normalize = aData.auto_normalize; + } else { + this.auto_normalize = old_auto_normalize; } - if(this.interval != 1) { - str += ";INTERVAL=" + this.interval; + if (this.auto_normalize) { + this.normalize(); } - str += [";" + k + "=" + this.parts[k] - for(k in this.parts)].join(""); - return str; + return this; }, - 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 = eval(propval.parts.toSource()); - if(aProp.name == "EXRULE") { - this.isNegative = true; - } else if(aProp.name == "RRULE") { - this.isNegative = false; + 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 { - throw new Error("Invalid Property " + aProp.name + " passed"); + h += 5; } - }, - }; - ICAL.icalrecur.fromData = function icalrecur_fromData(data) { - return(new ICAL.icalrecur(data)); - } + // Normalize to 1 = sunday + h = ((h + 6) % 7) + 1; + return h; + }, - ICAL.icalrecur.fromString = function icalrecur_fromString(str) { - var data = ICAL.icalparser.parseValue(str, "RECUR"); - return ICAL.icalrecur.fromData(data); - }; + 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; + }, - ICAL.icalrecur.fromIcalProperty = function icalrecur_fromIcalProperty(prop) { - var recur = new ICAL.icalrecur(); - recur.fromIcalProperty(prop); - return recur; - }; + start_of_week: function start_of_week() { + var result = this.clone(); + result.day -= this.day_of_week() - 1; + return result.normalize(); + }, - function icalrecur_iterator(aRule, aStart) { - this.rule = aRule; - this.dtstart = aStart; - this.by_data = eval(aRule.parts.toSource()); - this.days = []; - this.init(); - } + end_of_week: function end_of_week() { + var result = this.clone(); + result.day += 7 - this.day_of_week(); + return result.normalize(); + }, - icalrecur_iterator.prototype = { + 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; + }, - 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 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"); - } - } + 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; + }, - // 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"); - } + 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; + }, - // 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"); - } + 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; + }, - // 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"); - } + 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; + }, - // 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"); - } + 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; - 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); + var otherday = this.clone(); - if(this.rule.freq == "WEEKLY") { - if("BYDAY" in parts) { - var [pos, rule_dow] = this.rule_day_of_week(parts.BYDAY[0]); - 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 { - parts.BYDAY = [icalrecur_iterator._wkdayMap[this.dtstart.day_of_week()]]; - } - } + if (pos >= 0) { + otherday.day = 1; + var start_dow = otherday.day_of_week(); - 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); + if (pos != 0) { + pos--; } - var next = ICAL.icaltime.from_day_of_year(this.days[0], this.last.year); + weekday = aDayOfWeek - start_dow + 1; - this.last.day = next.day; - this.last.month = next.month; - } + if (weekday <= 0) { + weekday += 7; + } + } else { + otherday.day = days_in_month; + var end_dow = otherday.day_of_week(); - if(this.rule.freq == "MONTHLY" && this.has_by_data("BYDAY")) { - var [pos, dow] = this.rule_day_of_week(this.by_data.BYDAY[this.by_indices.BYDAY]); + pos++; - var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); - var poscount = 0; + weekday = (end_dow - dow); - 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 (weekday < 0) { + weekday += 7; } - if(this.last.day > days_in_month || this.last.day == 0) { - throw new Error("Malformed values in BYDAY part"); - } + weekday = days_in_month - weekday; + } - } 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; - } + weekday += pos * 7; - this.last.normalize(); - } + return weekday; }, - next: function icalrecur_iterator_next() { - var before = (this.last ? this.last.clone() : null); + 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; - if((this.rule.count && this.occurrence_number >= this.rule.count) || (this.rule.until && this.last.compare(this.rule.until) > 0)) { - return null; - } + var dt = this.clone(); + dt.isDate = true; + var first_dow = dt.day_of_week(); + var isoyear = this.year; - 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; + 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); + } } - do { - var valid = 1; + var daysBetween = (dt.subtractDate(week1).toSeconds() / 86400); + return ICAL.helpers.trunc(daysBetween / 7) + 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; + addDuration: function icaltime_add(aDuration) { + var mult = (aDuration.isNegative ? -1 : 1); - case "WEEKLY": - this.next_week(); - break; - case "MONTHLY": - valid = this.next_month(); - break; - case "YEARLY": - this.next_year(); - break; + 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; - default: - return null; - } - } while (!this.check_contracting_rules() || this.last.compare(this.dtstart) < 0 || !valid); + this.normalize(); + }, - // TODO is this valid? - if(this.last.compare(before) == 0) { - throw new Error("Same occurrence found twice, protecting " + " you from death by recursion"); + 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); } - if(this.rule.until && this.last.compare(this.rule.until) > 0) { - return null; - } else { - this.occurrence_number++; - return this.last; + 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(); - next_second: function next_second() { - return this.next_generic("BYSECOND", "SECONDLY", "second", "minute"); - }, + dur.seconds = this.second - aDate.second; + dur.minutes = this.minute - aDate.minute; + dur.hours = this.hour - aDate.hour; - increment_second: function increment_second(inc) { - return this.increment_generic(inc, "second", 60, "minute"); - }, + 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(); - next_minute: function next_minute() { - return this.next_generic("BYMINUTE", "MINUTELY", "minute", "hour", "next_second"); - }, + 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(); - increment_minute: function increment_minute(inc) { - return this.increment_generic(inc, "minute", 60, "hour"); - }, + 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); + } - next_hour: function next_hour() { - return this.next_generic("BYHOUR", "HOURLY", "hour", "monthday", "next_minute"); + return dur.normalize(); }, - increment_hour: function increment_hour(inc) { - this.increment_generic(inc, "hour", 24, "monthday"); - }, + compare: function icaltime_compare(other) { + function cmp(attr) { + return ICAL.icaltime._cmp_attr(a, b, attr); + } - next_day: function next_day() { - var has_by_day = ("BYDAY" in this.by_data); - var this_freq = (this.rule.freq == "DAILY"); + if (!other) return 0; - if(this.next_hour() == 0) { - return 0; + if (this.isDate || other.isDate) { + return this.compare_date_only_tz(other, this.zone); } - if(this_freq) { - this.increment_monthday(this.rule.interval); + var target_zone; + if (this.zone == ICAL.icaltimezone.local_timezone || + other.zone == ICAL.icaltimezone.local_timezone) { + target_zone = ICAL.icaltimezone.local_timezone; } else { - this.increment_monthday(1); + target_zone = ICAL.icaltimezone.utc_timezone; } - return 0; - }, + var a = this.convert_to_zone(target_zone); + var b = other.convert_to_zone(target_zone); + var rc = 0; - next_week: function next_week() { - var end_of_data = 0; + if ((rc = cmp("year")) != 0) return rc; + if ((rc = cmp("month")) != 0) return rc; + if ((rc = cmp("day")) != 0) return rc; - if(this.next_weekday_by_week() == 0) { - return end_of_data; + 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(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]; + if ((rc = cmp("hour")) != 0) return rc; + if ((rc = cmp("minute")) != 0) return rc; + if ((rc = cmp("second")) != 0) return rc; - this.last.day += 7 * week_no; - this.last.normalize(); + // Now rc is 0 and the dates are equal + return rc; + }, - if(end_of_data) { - this.increment_year(1) - } - } else { - // Jump to the next week - this.increment_monthday(7 * this.rule.interval); + 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; - return end_of_data; + if ((rc = cmp("year")) != 0) return rc; + if ((rc = cmp("month")) != 0) return rc; + if ((rc = cmp("day")) != 0) return rc; + + return rc; }, - next_month: function next_month() { - var this_freq = (this.rule.freq == "MONTHLY"); - var data_valid = 1; + convert_to_zone: function convert_to_zone(zone) { + var copy = this.clone(); + var zone_equals = (this.zone.tzid == zone.tzid); - if(this.next_hour() == 0) { - return data_valid; + if (!this.isDate && !zone_equals) { + ICAL.icaltimezone.convert_time(copy, this.zone, zone); } - 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; + copy.zone = zone; + return copy; + }, - 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 [pos, dow] = this.rule_day_of_week(this.by_data.BYDAY[dayIdx]); - var mday = this.by_data.BYMONTHDAY[mdIdx]; + 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); + } + }, - this.last.day = day; - var this_dow = this.last.day_of_week(); + 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" : "") + ); + }, - if((pos == 0 && dow == this_dow && mday == day) || (this.last.nth_weekday(dow, pos))) { - notFound = false; - } - } - } + 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); } - if(day > days_in_month) { - this.last.day = 1; - this.increment_month(); - this.last.day--; - data_valid = 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); } + } + }, - } 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; + normalize: function icaltime_normalize() { + if (this.isDate) { + this.hour = 0; + this.minute = 0; + this.second = 0; + } + this.icaltype = (this.isDate ? "DATE" : "DATE-TIME"); - 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; - } + this.adjust(0, 0, 0, 0); + return this; + }, - for(var day = this.last.day + 1; day <= days_in_month; day++) { - this.last.day = day; + 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.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; - } - } + 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--; } - 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; - } + 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--; } - } 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(); + 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--; } + } - var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); + // 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; + } - var day = this.by_data.BYMONTHDAY[this.by_indices.BYMONTHDAY]; + this.year += years_overflow; + this.month -= 12 * years_overflow; - if(day < 0) { - day = days_in_month + day + 1; - } + // 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; + } - if(day > days_in_month) { - this.last.day = 1; - data_valid = this.is_day_in_byday(this.last); - } + this.month++; + if (this.month > 12) { + this.year++; + this.month = 1; + } - this.last.day = day; + day -= days_in_month; + } } 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); + 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); + } } - return data_valid; + this.day = day; + return this; }, - 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; - } + 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; + }, - //this.sort_byday_rules(this.by_data.BYDAY, this.rule.wkst); + toUnixTime: function toUnixTime() { + var dur = this.subtractDate(ICAL.icaltime.epoch_time); + return dur.toSeconds(); + } + }; - for(;;) { - var tt = new ICAL.icaltime(); - tt.auto_normalize = false; - this.by_indices.BYDAY++; + (function setupNormalizeAttributes() { + // This needs to run before any instances are created! + function addAutoNormalizeAttribute(attr, mattr) { + ICAL.icaltime.prototype[mattr] = ICAL.icaltime.prototype[attr]; - if(this.by_indices.BYDAY == this.by_data.BYDAY.length) { - this.by_indices.BYDAY = 0; - end_of_data = 1; + 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; } + }); - var [, dow] = this.rule_day_of_week(this.by_data.BYDAY[this.by_indices.BYDAY]); - dow -= this.rule.wkst; - if(dow < 0) { - dow += 7; - } + } - tt.year = this.last.year; - tt.month = this.last.month; - tt.day = this.last.day; + 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"); - var start_of_week = tt.start_doy_week(this.rule.wkst); + ICAL.icaltime.prototype.auto_normalize = true; + } + })(); - if(dow + start_of_week < 1) { - // The selected date is in the previous year - if(!end_of_data) { - continue; - } - } + 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; - var next = ICAL.icaltime.from_day_of_year(start_of_week + dow, this.last.year); + if (month < 1 || month > 12) return days; - this.last.day = next.day; - this.last.month = next.month; - this.last.year = next.year; + days = _days_in_month[month]; - return end_of_data; - } - }, + if (month == 2) { + days += ICAL.icaltime.is_leap_year(year); + } - next_year: function next_year() { + return days; + }; - if(this.next_hour() == 0) { - return 0; - } + 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)); + } + }; - 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); + 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; } + } - var next = ICAL.icaltime.from_day_of_year(this.days[this.days_index], this.last.year); + tt.auto_normalize = true; + return tt; + }; - this.last.day = next.day; - this.last.month = next.month; + ICAL.icaltime.fromString = function fromString(str) { + var tt = new ICAL.icaltime(); + return tt.fromString(str); + }; - return 1; - }, + ICAL.icaltime.fromJSDate = function fromJSDate(aDate, useUTC) { + var tt = new ICAL.icaltime(); + return tt.fromJSDate(aDate, useUTC); + }; - rule_day_of_week: function rule_day_of_week(dow) { - const 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)/); - return(matches ? [parseInt(matches[1] || 0, 10), dowMap[matches[2]]] || 0 : [0, 0]); - }, + ICAL.icaltime.fromData = function fromData(aData) { + var t = new ICAL.icaltime(); + return t.fromData(aData); + }; - 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; + ICAL.icaltime.now = function icaltime_now() { + return ICAL.icaltime.fromJSDate(new Date(), false); + }; - if(aPreviousIncr && this[aPreviousIncr]() == 0) { - return end_of_data; - } + ICAL.icaltime.week_one_starts = function week_one_starts(aYear, aWeekStart) { + var t = ICAL.icaltime.fromData({ + year: aYear, + month: 1, + day: 4, + isDate: true + }); - if(has_by_rule) { - this.by_indices[aRuleType]++; - var idx = this.by_indices[aRuleType]; + var fourth_dow = t.day_of_week(); + t.day += (1 - fourth_dow) + ((aWeekStart || ICAL.icaltime.SUNDAY) - 1); + return t; + }; - if(this.by_indices[aRuleType] == this.by_data[aRuleType].length) { - this.by_indices[aRuleType] = 0; - end_of_data = 1; - } - this.last[aDateAttr] = this.by_data[aRuleType][this.by_indices[aRuleType]]; - } else if(this_freq) { - this["increment_" + aDateAttr](this.rule.interval); - } + ICAL.icaltime.epoch_time = ICAL.icaltime.fromData({ + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + isDate: false, + timezone: "Z" + }); - if(has_by_rule && end_of_data && this_freq) { - this["increment_" + aFollowingAttr](1); - } + 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; + }; - return end_of_data; - }, + 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] + ]; - 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++; + 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/. */ - 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); - } +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; +(function() { + ICAL.icalrecur = function icalrecur(data) { + this.wrappedJSObject = this; + this.parts = {}; + this.fromData(data); + }; - 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++; - } + ICAL.icalrecur.prototype = { - this.last.month--; - var years = ICAL.helpers.trunc(this.last.month / 12); - this.last.month %= 12; - this.last.month++; + parts: null, - if(years != 0) { - this.increment_year(years); - } - } + 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); }, - increment_year: function increment_year(inc) { - this.last.year += inc; + clone: function clone() { + return ICAL.icalrecur.fromData(this); + //return ICAL.icalrecur.fromIcalProperty(this.toIcalProperty()); }, - 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); + 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); } }, - has_by_data: function has_by_data(aRuleType) { - return(aRuleType in this.rule.parts); + setComponent: function setComponent(aType, aValues) { + this.parts[aType] = aValues; }, - expand_year_days: function expand_year_days(aYear) { - var t = new ICAL.icaltime(); - this.days = []; + getComponent: function getComponent(aType, aCount) { + var ucName = aType.toUpperCase(); + var components = (ucName in this.parts ? this.parts[ucName] : []); - // We need our own copy with a few keys set - var parts = {}; - for each(var p in ["BYDAY", "BYWEEKNO", "BYMONTHDAY", "BYMONTH", "BYYEARDAY"]) { - if(p in this.rule.parts) { - parts[p] = this.rule.parts[p]; - } + 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; } - if("BYMONTH" in parts && "BYWEEKNO" in parts) { - var valid = 1; - var validWeeks = {}; - t.year = aYear; - t.isDate = true; + return next; + }, - 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; - } + 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! } + } - 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 (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! } } - - if(valid) { - delete parts.BYMONTH; - } else { - delete parts.BYWEEKNO; + // 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; + }, - var partCount = Object.keys(parts).length; + 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; + }, - 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 each(var month in this.by_data.BYMONTH) { - var t2 = this.dtstart.clone(); - t2.year = aYear; - t2.month = month; - t2.isDate = true; - this.days.push(t2.day_of_year()); - } - } else if(partCount == 1 && "BYMONTHDAY" in parts) { - for each(var month_day in this.by_data.BYMONTHDAY) { - var t2 = this.dtstart.clone(); - t2.day = month_day; - t2.year = aYear; - t2.isDate = true; - this.days.push(t2.day_of_year()); - } - } else if(partCount == 2 && "BYMONTHDAY" in parts && "BYMONTH" in parts) { - for each(var month in this.by_data.BYMONTH) { - for each(var monthDay in this.by_data.BYMONTHDAY) { - t.day = monthDay; - t.month = month; - 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 each(var month in this.by_data.BYMONTH) { - var days_in_month = ICAL.icaltime.days_in_month(month, aYear); - - t.year = aYear; - t.month = month; - 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 each(var day_coded in this.by_data.BYDAY) { - var [dow, pos] = this.rule_day_of_week(day_coded); - - 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 each(var day in expandedDays) { - 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 each(var day in expandedDays) { - 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 each(var day in expandedDays) { - 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 = []; + 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); + return null; } - return 0; }, + fromIcalProperty: function fromIcalProperty(aProp) { + var propval = aProp.getFirstValue(); + this.fromData(propval); + //TODO: we should not use eval anywhere and + // this only works in gecko (toSource) + this.parts = eval(propval.parts.toSource()); + if (aProp.name == "EXRULE") { + this.isNegative = true; + } else if (aProp.name == "RRULE") { + this.isNegative = false; + } else { + throw new Error("Invalid Property " + aProp.name + " passed"); + } + } + }; - 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; + ICAL.icalrecur.fromData = function icalrecur_fromData(data) { + return (new ICAL.icalrecur(data)); + } - var start_dow = tmp.day_of_week(); + ICAL.icalrecur.fromString = function icalrecur_fromString(str) { + var data = ICAL.icalparser.parseValue(str, "RECUR"); + return ICAL.icalrecur.fromData(data); + }; - tmp.month = 12; - tmp.day = 31; - tmp.isDate = true; + ICAL.icalrecur.fromIcalProperty = function icalrecur_fromIcalProperty(prop) { + var recur = new ICAL.icalrecur(); + recur.fromIcalProperty(prop); + return recur; + }; - var end_dow = tmp.day_of_week(); - var end_year_day = tmp.day_of_year(); + function icalrecur_iterator(aRule, aStart) { + this.rule = aRule; + this.dtstart = aStart; + //TODO: we should not use eval anywhere and + // this only works in gecko (toSource) + this.by_data = eval(aRule.parts.toSource()); + this.days = []; + this.init(); + } - for each(var day in this.by_data.BYDAY) { - var [pos, dow] = this.rule_day_of_week(day); + icalrecur_iterator.prototype = { - if(pos == 0) { - var tmp_start_doy = ((dow + 7 - start_dow) % 7) + 1; + rule: null, + dtstart: null, + last: null, + occurrence_number: 0, + by_indices: null, + by_data: null, - for(var doy = tmp_start_doy; doy <= end_year_day; doy += 7) { - days_list.push(doy); - } + days: null, + days_index: 0, - } else if(pos > 0) { - var first; - if(dow >= start_dow) { - first = dow - start_dow + 1; - } else { - first = dow - start_dow + 8; - } + init: function icalrecur_iterator_init() { + this.last = this.dtstart.clone(); + var parts = this.by_data; - days_list.push(first + (pos - 1) * 7); - } else { - var last; - pos = -pos; + this.by_indices = { + "BYSECOND": 0, + "BYMINUTE": 0, + "BYHOUR": 0, + "BYDAY": 0, + "BYMONTH": 0, + "BYWEEKNO": 0, + "BYMONTHDAY": 0 + }; - if(dow <= end_dow) { - last = end_year_day - end_dow + dow; - } else { - last = end_year_day - end_dow + dow - 7; - } + 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); + } - days_list.push(last - (pos - 1) * 7); + // 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"); } } - return days_list; - }, - is_day_in_byday: function is_day_in_byday(tt) { - for each(var day in this.by_data.BYDAY) { - var [pos, dow] = this.rule_day_of_week(day); - var this_dow = tt.day_of_week(); + // 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"); + } - if((pos == 0 && dow == this_dow) || (tt.nth_weekday(dow, pos) == tt.day)) { - return 1; - } + // 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"); } - return 0; - }, + // 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"); + } - check_set_position: function check_set_position(aPos) { - return("BYSETPOS" in this.by_data && this.by_data.BYSETPOS.indexOf(aPos)); - }, + // 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"); + } - 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]); - var [, two] = this.rule_day_of_week(aRules[i]); - one -= aWeekStart; - two -= aWeekStart; - if(one < 0) one += 7; - if(two < 0) two += 7; + 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(one > two) { - var tmp = aRules[i]; - aRules[i] = aRules[j]; - aRules[j] = tmp; + if (this.rule.freq == "WEEKLY") { + if ("BYDAY" in parts) { + var parts = this.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]; } } - }, - 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 each(var bydata in this.by_data[aRuleType]) { - if(bydata == v) { - pass = true; + 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); } - } 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]; + var next = ICAL.icaltime.from_day_of_year(this.days[0], this.last.year); - 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]; - } + this.last.day = next.day; + this.last.month = next.month; } - 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/. */ -"use strict"; + 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 ICAL = 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, + var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.year); + var poscount = 0; - isDate: false, - zone: null, + 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; + } + } + } + } - auto_normalize: false, - icalclass: "icaltime", - icaltype: "DATE-TIME", + if (this.last.day > days_in_month || this.last.day == 0) { + throw new Error("Malformed values in BYDAY part"); + } - clone: function icaltime_clone() { - return new ICAL.icaltime(this); - }, + } 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; + } - reset: function icaltime_reset() { - this.fromData(ICAL.icaltime.epoch_time); - this.zone = ICAL.icaltimezone.utc_timezone; + this.last.normalize(); + } }, - 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 - }); - }, + next: function icalrecur_iterator_next() { + var before = (this.last ? this.last.clone() : null); - 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; + if ((this.rule.count && this.occurrence_number >= this.rule.count) || + (this.rule.until && this.last.compare(this.rule.until) > 0)) { + return null; } - 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(); - } + 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; } - 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; + do { + var valid = 1; - const 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; - } + 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; - if(aData && "timezone" in aData && aData.timezone == "Z") { - this.zone = ICAL.icaltimezone.utc_timezone; - } - if(aData && "zone" in aData) { - this.zone = aData.zone; - } + case "WEEKLY": + this.next_week(); + break; + case "MONTHLY": + valid = this.next_month(); + break; + case "YEARLY": + this.next_year(); + break; - if(!this.zone) { - this.zone = ICAL.icaltimezone.local_timezone; - } + default: + return null; + } + } while (!this.check_contracting_rules() || + this.last.compare(this.dtstart) < 0 || + !valid); - this.auto_normalize = old_auto_normalize; - if(this.auto_normalize) { - this.normalize(); + // TODO is this valid? + if (this.last.compare(before) == 0) { + throw new Error("Same occurrence found twice, protecting " + + "you from death by recursion"); } - 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); + if (this.rule.until && this.last.compare(this.rule.until) > 0) { + return null; } else { - h += 5; + this.occurrence_number++; + return this.last; } - - // 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); - return ICAL.icaltime._days_in_year_passed_month[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(); + next_second: function next_second() { + return this.next_generic("BYSECOND", "SECONDLY", "second", "minute"); }, - 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; + increment_second: function increment_second(inc) { + return this.increment_generic(inc, "second", 60, "minute"); }, - 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; + next_minute: function next_minute() { + return this.next_generic("BYMINUTE", "MINUTELY", + "minute", "hour", "next_second"); }, - 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; + increment_minute: function increment_minute(inc) { + return this.increment_generic(inc, "minute", 60, "hour"); }, - 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; + next_hour: function next_hour() { + return this.next_generic("BYHOUR", "HOURLY", "hour", + "monthday", "next_minute"); }, - 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; + increment_hour: function increment_hour(inc) { + this.increment_generic(inc, "hour", 24, "monthday"); }, - 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; + next_day: function next_day() { + var has_by_day = ("BYDAY" in this.by_data); + var this_freq = (this.rule.freq == "DAILY"); - var otherday = this.clone(); + if (this.next_hour() == 0) { + return 0; + } - if(pos >= 0) { - otherday.day = 1; - var start_dow = otherday.day_of_week(); + if (this_freq) { + this.increment_monthday(this.rule.interval); + } else { + this.increment_monthday(1); + } - if(pos != 0) { - pos--; - } + return 0; + }, - weekday = aDayOfWeek - start_dow + 1; + next_week: function next_week() { + var end_of_data = 0; - if(weekday <= 0) { - weekday += 7; + 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; } - } else { - otherday.day = days_in_month; - var end_dow = otherday.day_of_week(); - pos++; + // HACK should be first month of the year + this.last.month = 1; + this.last.day = 1; - weekday = (end_dow - dow); + var week_no = this.by_data.BYWEEKNO[this.by_indices.BYWEEKNO]; - if(weekday < 0) { - weekday += 7; - } + this.last.day += 7 * week_no; + this.last.normalize(); - weekday = days_in_month - weekday; + if (end_of_data) { + this.increment_year(1); + } + } else { + // Jump to the next week + this.increment_monthday(7 * this.rule.interval); } - weekday += pos * 7; - - return weekday; + return end_of_data; }, - 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; + next_month: function next_month() { + var this_freq = (this.rule.freq == "MONTHLY"); + var data_valid = 1; - var dt = this.clone(); - dt.isDate = true; - var first_dow = dt.day_of_week(); - var isoyear = this.year; + if (this.next_hour() == 0) { + return data_valid; + } - 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++; + 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; + } + } + } } - } else { - week1 = ICAL.icaltime.week_one_starts(isoyear, aWeekStart); - if(dt.compare(week1) < 0) { - week1 = ICAL.icaltime.week_one_starts(--isoyear, aWeekStart); + if (day > days_in_month) { + this.last.day = 1; + this.increment_month(); + this.last.day--; + data_valid = 0; } - } - var daysBetween = (dt.subtractDate(week1).toSeconds() / 86400); - return ICAL.helpers.trunc(daysBetween / 7) + 1; - }, + } 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; - addDuration: function icaltime_add(aDuration) { - var mult = (aDuration.isNegative ? -1 : 1); + 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; + } - 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; + for (var day = this.last.day + 1; day <= days_in_month; day++) { + this.last.day = day; - this.normalize(); - }, + 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; + } + } + } - 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); - } + data_valid = found; - function leap_years_between(aStart, aEnd) { - if(aStart >= aEnd) { - return 0; - } else { - return leap_years_until(aEnd - 1) - leap_years_until(aStart); + 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; + } } - } - var dur = new ICAL.icalduration(); + } else if (this.has_by_data("BYMONTHDAY")) { + this.by_indices.BYMONTHDAY++; - dur.seconds = this.second - aDate.second; - dur.minutes = this.minute - aDate.minute; - dur.hours = this.hour - aDate.hour; + if (this.by_indices.BYMONTHDAY >= this.by_data.BYMONTHDAY.length) { + this.by_indices.BYMONTHDAY = 0; + this.increment_month(); + } - 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(); + var days_in_month = ICAL.icaltime.days_in_month(this.last.month, this.last.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(); + var day = this.by_data.BYMONTHDAY[this.by_indices.BYMONTHDAY]; - 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); - } + if (day < 0) { + day = days_in_month + day + 1; + } - return dur.normalize(); - }, + if (day > days_in_month) { + this.last.day = 1; + data_valid = this.is_day_in_byday(this.last); + } - compare: function icaltime_compare(other) { - function cmp(attr) { - return ICAL.icaltime._cmp_attr(a, b, attr); + 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); } - if(!other) return 0; + return data_valid; + }, - if(this.isDate || other.isDate) { - return this.compare_date_only_tz(other, this.zone); + next_weekday_by_week: function next_weekday_by_week() { + var end_of_data = 0; + + if (this.next_hour() == 0) { + return end_of_data; } - 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; + if (!this.has_by_data("BYDAY")) { + return 1; } - var a = this.convert_to_zone(target_zone); - var b = other.convert_to_zone(target_zone); - var rc = 0; + 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; - if((rc = cmp("year")) != 0) return rc; - if((rc = cmp("month")) != 0) return rc; - if((rc = cmp("day")) != 0) return rc; + return end_of_data; + } + }, - if(a.isDate && b.isDate) { - // If both are dates, we are done + next_year: function next_year() { + + if (this.next_hour() == 0) { 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; + 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); - // Now rc is 0 and the dates are equal - return rc; + this.last.day = next.day; + this.last.month = next.month; + + return 1; }, - compare_date_only_tz: function icaltime_compare_date_only_tz(other, tz) { - function cmp(attr) { - return ICAL.icaltime._cmp_attr(a, b, attr); + 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]; } - 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; + 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; - return rc; - }, + if (aPreviousIncr && this[aPreviousIncr]() == 0) { + return end_of_data; + } - convert_to_zone: function convert_to_zone(zone) { - var copy = this.clone(); - var zone_equals = (this.zone.tzid == zone.tzid); + 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(!this.isDate && !zone_equals) { - ICAL.icaltimezone.convert_time(copy, this.zone, zone) + if (has_by_rule && end_of_data && this_freq) { + this["increment_" + aFollowingAttr](1); } - copy.zone = zone; - return copy; + return end_of_data; }, - 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); + 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(); + } } }, - 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" : "")); - }, + increment_month: function increment_month() { + if (this.has_by_data("BYMONTH")) { + this.by_indices.BYMONTH++; - 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); + 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 utcDate = this.convert_to_zone(ICAL.icaltimezone.utc_timezone); - if(this.isDate) { - return Date.UTC(this.year, this.month - 1, this.day); + var inc; + if (this.rule.freq == "MONTHLY") { + this.last.month += this.rule.interval; } else { - return Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second, 0); + 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); } } }, - normalize: function icaltime_normalize() { - if(this.isDate) { - this.hour = 0; - this.minute = 0; - this.second = 0; + 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); } - this.icaltype = (this.isDate ? "DATE" : "DATE-TIME"); + }, - this.adjust(0, 0, 0, 0); - return this; + has_by_data: function has_by_data(aRuleType) { + return (aRuleType in this.rule.parts); }, - 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; + expand_year_days: function expand_year_days(aYear) { + var t = new ICAL.icaltime(); + this.days = []; - 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--; + // 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]; } + } - 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--; + 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; + } } - 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--; + 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; + } } - } - // 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; + if (valid) { + delete parts.BYMONTH; + } else { + delete parts.BYWEEKNO; + } } - this.year += years_overflow; - this.month -= 12 * years_overflow; + var partCount = Object.keys(parts).length; - // 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; + 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); - this.month++; - if(this.month > 12) { - this.year++; - this.month = 1; + 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); - day -= days_in_month; + 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 { - while(day <= 0) { - if(this.month == 1) { - this.year--; - this.month = 12; - } else { - this.month--; + } 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); - day += ICAL.icaltime.days_in_month(this.month, this.year); + 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 = []; } - - this.day = day; - return this; + return 0; }, - 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; - }, + expand_by_day: function expand_by_day(aYear) { - toUnixTime: function toUnixTime() { - var dur = this.subtractDate(ICAL.icaltime.epoch_time); - return dur.toSeconds(); - } - }; + var days_list = []; + var tmp = this.last.clone(); - (function setupNormalizeAttributes() { - // This needs to run before any instances are created! - function addAutoNormalizeAttribute(attr, mattr) { - ICAL.icaltime.prototype[mattr] = ICAL.icaltime.prototype[attr]; + tmp.year = aYear; + tmp.month = 1; + tmp.day = 1; + tmp.isDate = true; - 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; - } - }); + var start_dow = tmp.day_of_week(); - } + tmp.month = 12; + tmp.day = 31; + tmp.isDate = true; - 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"); + var end_dow = tmp.day_of_week(); + var end_year_day = tmp.day_of_year(); - ICAL.icaltime.prototype.auto_normalize = true; - } - })(); + 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]; - ICAL.icaltime.days_in_month = function icaltime_days_in_month(month, year) { - const _days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + if (pos == 0) { + var tmp_start_doy = ((dow + 7 - start_dow) % 7) + 1; - var days = 30; + for (var doy = tmp_start_doy; doy <= end_year_day; doy += 7) { + days_list.push(doy); + } - if(month < 1 || month > 12) return days; + } else if (pos > 0) { + var first; + if (dow >= start_dow) { + first = dow - start_dow + 1; + } else { + first = dow - start_dow + 8; + } - days = _days_in_month[month]; + days_list.push(first + (pos - 1) * 7); + } else { + var last; + pos = -pos; - if(month == 2) { - days += ICAL.icaltime.is_leap_year(year); - } + if (dow <= end_dow) { + last = end_year_day - end_dow + dow; + } else { + last = end_year_day - end_dow + dow - 7; + } - return days; - }; + days_list.push(last - (pos - 1) * 7); + } + } + return days_list; + }, - 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)); - } - }; + 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(); - 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 ((pos == 0 && dow == this_dow) || + (tt.nth_weekday(dow, pos) == tt.day)) { + return 1; + } + } - 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++; - } + return 0; + }, - tt.year = year; - tt.isDate = true; + check_set_position: function check_set_position(aPos) { + return ("BYSETPOS" in this.by_data && + this.by_data.BYSETPOS.indexOf(aPos)); + }, - 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; - } - } + 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; - tt.auto_normalize = true; - return tt; - }; + if (one > two) { + var tmp = aRules[i]; + aRules[i] = aRules[j]; + aRules[j] = tmp; + } + } + } + }, - ICAL.icaltime.fromString = function fromString(str) { - var tt = new ICAL.icaltime(); - return tt.fromString(str); - }; + 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; - ICAL.icaltime.fromJSDate = function fromJSDate(aDate, useUTC) { - var tt = new ICAL.icaltime(); - return tt.fromJSDate(aDate, useUTC); - }; + 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; + }, - ICAL.icaltime.fromData = function fromData(aData) { - var t = new ICAL.icaltime(); - return t.fromData(aData); - }; + 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(); - ICAL.icaltime.now = function icaltime_now() { - return ICAL.icaltime.fromJSDate(new Date(), false); - }; + 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)); + }, - ICAL.icaltime.week_one_starts = function week_one_starts(aYear, aWeekStart) { - var t = ICAL.icaltime.fromData({ - year: aYear, - month: 1, - day: 4, - isDate: true - }); + setup_defaults: function setup_defaults(aRuleType, req, deftime) { + var indexMapValue = icalrecur_iterator._indexMap[aRuleType]; + var ruleMapValue = icalrecur_iterator._expandMap[this.rule.freq][indexMapValue]; - var fourth_dow = t.day_of_week(); - t.day += (1 - fourth_dow) + ((aWeekStart || ICAL.icaltime.SUNDAY) - 1); - return t; + 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; + } }; - ICAL.icaltime.epoch_time = ICAL.icaltime.fromData({ - year: 1970, - month: 1, - day: 1, - hour: 0, - minute: 0, - second: 0, - isDate: false, - timezone: "Z" - }); + icalrecur_iterator._wkdayMap = ["", "SU", "MO", "TU", "WE", "TH", "FR", "SA"]; - 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; + icalrecur_iterator._indexMap = { + "BYSECOND": 0, + "BYMINUTE": 1, + "BYHOUR": 2, + "BYDAY": 3, + "BYMONTHDAY": 4, + "BYYEARDAY": 5, + "BYWEEKNO": 6, + "BYMONTH": 7, + "BYSETPOS": 8 }; - 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; + 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/. */ -"use strict"; -var ICAL = ICAL || {}; -(function () { +(typeof(ICAL) === 'undefined')? ICAL = {} : ''; + +(function() { ICAL.foldLength = 75; ICAL.newLineChar = "\n"; @@ -4364,14 +4555,18 @@ var ICAL = ICAL || {}; * @param {String|Object} ical ical string or pre-parsed object. * @param {Boolean} decorate when true decorates object data types. * - * @return {Object|ICAL.icalcomponent} + * @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) { + 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++; @@ -4379,7 +4574,8 @@ var ICAL = ICAL || {}; return state.currentData; }; -}());/* sax js - LICENSE: https://github.com/isaacs/sax-js/blob/master/LICENSE */ +}()); +/* sax js - LICENSE: https://github.com/isaacs/sax-js/blob/master/LICENSE */ // wrapper for non-node envs ;(function (sax) { @@ -4452,6 +4648,8 @@ function SAXParser (strict, opt) { // mostly just for error reporting parser.position = parser.line = parser.column = 0 emit(parser, "onready") + + return this; } function checkBufferLength (parser) { @@ -4551,11 +4749,14 @@ function SAXStream (strict, opt) { return me._parser["on"+ev] = h } me.on(ev, h) + return null; }, enumerable: true, configurable: false }) }) + + return this; } SAXStream.prototype = Object.create(Stream.prototype, @@ -4954,7 +5155,7 @@ function write (chunk) { "Cannot write after close. Assign an onready handler.") if (chunk === null) return end(parser) var i = 0, c = "" - while (parser.c = c = chunk.charAt(i++)) { + while ((parser.c = c = chunk.charAt(i++))) { parser.position ++ if (c === "\n") { parser.line ++ @@ -5410,7 +5611,11 @@ function write (chunk) { if (typeof(window) === 'undefined') { return require(require('path').join(__dirname, path)); } else { - return paths[path]; + if (path in paths) { + return paths[path]; + } else { + return null; + } } }, @@ -6700,7 +6905,7 @@ function write (chunk) { var match = data.match(HTTP_STATUS); if (match) { - var handler = this.handler; + handler = this.handler; this.current[this.currentTag[handler.tagField]] = match[1]; } else { this._super.ontext.call(this, data, handler); @@ -6711,8 +6916,6 @@ function write (chunk) { var PrivilegeSet = Base.create({ name: 'PrivilegeSet', - name: 'href', - onopentag: function(data) { if (this.currentTag.handler === this.handler) { this.stack.push(this.current); @@ -6726,7 +6929,6 @@ function write (chunk) { onclosetag: function(data) { var current = this.currentTag; - var data; if (current.handler === this.handler) { data = this.current; @@ -6805,6 +7007,7 @@ function write (chunk) { } handler._super.onopentag.call(this, data, handler); + return null; }, oncomplete: function() { @@ -6842,6 +7045,7 @@ function write (chunk) { } handler._super.onopentag.call(this, data, handler._super); + return null; }, oncomplete: function() { @@ -7149,7 +7353,8 @@ function write (chunk) { var principal; if (err) { - return callback(err); + callback(err); + return; } principal = findProperty('current-user-principal', data, true); @@ -7172,7 +7377,8 @@ function write (chunk) { find.send(function(err, data) { if (err) { - return callback(err); + callback(err); + return; } details = { @@ -7194,7 +7400,8 @@ function write (chunk) { self._findPrincipal(self.url, function(err, url) { if (!url) { - return callback(new Error('Cannot resolve principal url')); + callback(new Error('Cannot resolve principal url')); + return; } self._findCalendarHome(url, function(err, details) { -- cgit