UNPKG

ical.js-one.com

Version:

[![Build Status](https://secure.travis-ci.org/mozilla-comm/ical.js.png?branch=master)](http://travis-ci.org/mozilla-comm/ical.js)

312 lines (257 loc) 8.26 kB
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * Portions Copyright (C) Philipp Kewisch, 2011-2012 */ "use strict"; (typeof(ICAL) === 'undefined')? ICAL = {} : ''; (function() { var DOW_MAP = { SU: ICAL.Time.SUNDAY, MO: ICAL.Time.MONDAY, TU: ICAL.Time.TUESDAY, WE: ICAL.Time.WEDNESDAY, TH: ICAL.Time.THURSDAY, FR: ICAL.Time.FRIDAY, SA: ICAL.Time.SATURDAY }; var REVERSE_DOW_MAP = {}; for (var key in DOW_MAP) { REVERSE_DOW_MAP[DOW_MAP[key]] = key; } var COPY_PARTS = ["BYSECOND", "BYMINUTE", "BYHOUR", "BYDAY", "BYMONTHDAY", "BYYEARDAY", "BYWEEKNO", "BYMONTH", "BYSETPOS"]; ICAL.Recur = function icalrecur(data) { this.wrappedJSObject = this; this.parts = {}; if (typeof(data) === 'object') { for (var key in data) { this[key] = data[key]; } if (this.until && !(this.until instanceof ICAL.Time)) { this.until = new ICAL.Time(this.until); } } if (!this.parts) { this.parts = {}; } }; ICAL.Recur.prototype = { parts: null, interval: 1, wkst: ICAL.Time.MONDAY, until: null, count: null, freq: null, icalclass: "icalrecur", icaltype: "recur", iterator: function(aStart) { return new ICAL.RecurIterator({ rule: this, dtstart: aStart }); }, clone: function clone() { return new ICAL.Recur(this.toJSON()); }, isFinite: function isfinite() { return !!(this.count || this.until); }, isByCount: function isbycount() { return !!(this.count && !this.until); }, addComponent: function addPart(aType, aValue) { if (!(aType in this.parts)) { this.parts[aType] = [aValue]; } else { this.parts[aType].push(aValue); } }, setComponent: function setComponent(aType, aValues) { this.parts[aType] = aValues; }, getComponent: function getComponent(aType, aCount) { var ucName = aType.toUpperCase(); var components = (ucName in this.parts ? this.parts[ucName] : []); if (aCount) aCount.value = components.length; return components; }, getNextOccurrence: function getNextOccurrence(aStartTime, aRecurrenceId) { var iter = this.iterator(aStartTime); var next, cdt; do { next = iter.next(); } while (next && next.compare(aRecurrenceId) <= 0); if (next && aRecurrenceId.zone) { next.zone = aRecurrenceId.zone; } return next; }, toJSON: function() { //XXX: extract this list up to proto? var propsToCopy = [ "freq", "count", "until", "wkst", "interval", "parts" ]; var result = Object.create(null); var i = 0; var len = propsToCopy.length; var prop; for (; i < len; i++) { var prop = propsToCopy[i]; result[prop] = this[prop]; } if (result.until instanceof ICAL.Time) { result.until = result.until.toJSON(); } return result; }, 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]; } if (this.until ){ str += ';UNTIL=' + this.until.toString(); } if ('wkst' in this && this.wkst !== ICAL.Time.DEFAULT_WEEK_START) { str += ';WKST=' + ICAL.Recur.numericDayToIcalDay(this.wkst); } return str; } }; function parseNumericValue(type, min, max, value) { var result = value; if (value[0] === '+') { result = value.substr(1); } result = ICAL.helpers.strictParseInt(result); if (min !== undefined && value < min) { throw new Error( type + ': invalid value "' + value + '" must be > ' + min ); } if (max !== undefined && value > max) { throw new Error( type + ': invalid value "' + value + '" must be < ' + min ); } return result; } /** * Convert an ical representation of a day (SU, MO, etc..) * into a numeric value of that day. * * @param {String} day ical day. * @return {Numeric} numeric value of given day. */ ICAL.Recur.icalDayToNumericDay = function toNumericDay(string) { //XXX: this is here so we can deal // with possibly invalid string values. return DOW_MAP[string]; }; /** * Convert a numeric day value into its ical representation (SU, MO, etc..) * * @param {Numeric} numeric value of given day. * @return {String} day ical day. */ ICAL.Recur.numericDayToIcalDay = function toIcalDay(num) { //XXX: this is here so we can deal with possibly invalid number values. // Also, this allows consistent mapping between day numbers and day // names for external users. return REVERSE_DOW_MAP[num]; }; var VALID_DAY_NAMES = /^(SU|MO|TU|WE|TH|FR|SA)$/; var VALID_BYDAY_PART = /^([+-])?(5[0-3]|[1-4][0-9]|[1-9])?(SU|MO|TU|WE|TH|FR|SA)$/ var ALLOWED_FREQ = ['SECONDLY', 'MINUTELY', 'HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY']; var optionDesign = { FREQ: function(value, dict) { // yes this is actually equal or faster then regex. // upside here is we can enumerate the valid values. if (ALLOWED_FREQ.indexOf(value) !== -1) { dict.freq = value; } else { throw new Error( 'invalid frequency "' + value + '" expected: "' + ALLOWED_FREQ.join(', ') + '"' ); } }, COUNT: function(value, dict) { dict.count = ICAL.helpers.strictParseInt(value); }, INTERVAL: function(value, dict) { dict.interval = ICAL.helpers.strictParseInt(value); if (dict.interval < 1) { // 0 or negative values are not allowed, some engines seem to generate // it though. Assume 1 instead. dict.interval = 1; } }, UNTIL: function(value, dict) { dict.until = ICAL.Time.fromString(value); }, WKST: function(value, dict) { if (VALID_DAY_NAMES.test(value)) { dict.wkst = ICAL.Recur.icalDayToNumericDay(value); } else { throw new Error('invalid WKST value "' + value + '"'); } } }; var partDesign = { BYSECOND: parseNumericValue.bind(this, 'BYSECOND', 0, 60), BYMINUTE: parseNumericValue.bind(this, 'BYMINUTE', 0, 59), BYHOUR: parseNumericValue.bind(this, 'BYHOUR', 0, 23), BYDAY: function(value) { if (VALID_BYDAY_PART.test(value)) { return value; } else { throw new Error('invalid BYDAY value "' + value + '"'); } }, BYMONTHDAY: parseNumericValue.bind(this, 'BYMONTHDAY', -31, 31), BYYEARDAY: parseNumericValue.bind(this, 'BYYEARDAY', -366, 366), BYWEEKNO: parseNumericValue.bind(this, 'BYWEEKNO', -53, 53), BYMONTH: parseNumericValue.bind(this, 'BYMONTH', 0, 12), BYSETPOS: parseNumericValue.bind(this, 'BYSETPOS', -366, 366) }; ICAL.Recur.fromString = function(string) { var dict = Object.create(null); var dictParts = dict.parts = Object.create(null); // split is slower in FF but fast enough. // v8 however this is faster then manual split? var values = string.split(';'); var len = values.length; for (var i = 0; i < len; i++) { var parts = values[i].split('='); var name = parts[0]; var value = parts[1]; if (name in partDesign) { var partArr = value.split(','); var partArrIdx = 0; var partArrLen = partArr.length; for (; partArrIdx < partArrLen; partArrIdx++) { partArr[partArrIdx] = partDesign[name](partArr[partArrIdx]); } dictParts[name] = partArr; } else if (name in optionDesign) { optionDesign[name](value, dict); } } return new ICAL.Recur(dict); }; })();