summaryrefslogtreecommitdiff
path: root/static/recurrence/js/recurrence.js
diff options
context:
space:
mode:
Diffstat (limited to 'static/recurrence/js/recurrence.js')
-rw-r--r--static/recurrence/js/recurrence.js1105
1 files changed, 1105 insertions, 0 deletions
diff --git a/static/recurrence/js/recurrence.js b/static/recurrence/js/recurrence.js
new file mode 100644
index 0000000..8c00fe3
--- /dev/null
+++ b/static/recurrence/js/recurrence.js
@@ -0,0 +1,1105 @@
+if (!recurrence)
+ var recurrence = {};
+
+
+recurrence.Rule = function(freq, options) {
+ this.init(freq, options);
+};
+recurrence.Rule.prototype = {
+ init: function(freq, options) {
+ this.freq = freq;
+
+ options = options || {};
+ this.interval = options.interval || 1;
+ this.wkst = options.wkst || null;
+ this.count = options.count || null;
+ this.until = options.until || null;
+
+ recurrence.array.foreach(
+ recurrence.byparams, function (param) {
+ if (options[param]) {
+ var value = options[param];
+ if (value == null)
+ value = [];
+ this[param] = recurrence.array.from(value);
+ } else {
+ this[param] = [];
+ }
+ }, this);
+ },
+
+ copy: function() {
+ var until = this.until;
+ if (until)
+ until = new Date(until.valueOf());
+ var rule = new recurrence.Rule(this.freq, this);
+ rule.until = until;
+ return rule;
+ },
+
+ update: function(rule) {
+ rule = rule.copy();
+ this.freq = rule.freq;
+ this.interval = rule.interval;
+ this.wkst = rule.wkst;
+ this.until = rule.until;
+ this.count = rule.count;
+
+ recurrence.array.foreach(
+ recurrence.byparams, function(param) {
+ this[param] = rule[param];
+ }, this);
+ },
+
+ get_display_text: function(short) {
+ short = short || false;
+ var parts = [];
+
+ var get_position_display = function(position) {
+ if (short)
+ return recurrence.display.weekdays_position_short[
+ String(position)];
+ else
+ return recurrence.display.weekdays_position[
+ String(position)];
+ };
+ var get_weekday_display = function(number) {
+ if (short)
+ return recurrence.display.weekdays_short[number];
+ else
+ return recurrence.display.weekdays[number];
+ };
+ var get_position_weekday = function(rule) {
+ var items = [];
+ if (rule.bysetpos.length && rule.byday.length) {
+ recurrence.array.foreach(
+ rule.bysetpos, function(x) {
+ var label = get_position_display(x || 1);
+ recurrence.array.foreach(
+ rule.byday, function(y) {
+ var weekday_display = get_weekday_display(
+ recurrence.to_weekday(y).number);
+ items.push(
+ interpolate(
+ label, {'weekday': weekday_display}, true));
+ });
+ });
+
+ } else if (rule.byday.length) {
+ // TODO byday Weekday objects without index means
+ // every weekday in the month, and so should appear in
+ // plural. i.e. 'on sundays' instead of
+ // 'on the first sunday'.
+ recurrence.array.foreach(
+ rule.byday, function(x, i) {
+ var label = get_position_display(x.index || 1);
+ var weekday_display = get_weekday_display(
+ recurrence.to_weekday(x).number);
+ items.push(
+ interpolate(
+ label, {'weekday': weekday_display}, true));
+ });
+ }
+ return items.join(', ');
+ }
+
+ if (this.interval > 1)
+ parts.push(
+ interpolate(
+ recurrence.display.tokens.every_number_freq, {
+ 'number': this.interval,
+ 'freq': recurrence.display.timeintervals_plural[this.freq]
+ }, true));
+ else
+ parts.push(recurrence.display.frequencies[this.freq]);
+
+ if (this.freq == recurrence.YEARLY) {
+ if (this.bymonth.length) {
+ // i.e. 'each january, june'
+ if (short)
+ var months = recurrence.display.months_short;
+ else
+ var months = recurrence.display.months;
+ var items = recurrence.array.foreach(
+ this.bymonth, function(month, i) {
+ return months[month - 1];
+ });
+ items = items.join(', ');
+ parts.push(
+ interpolate(
+ recurrence.display.tokens.each,
+ {'items': items}, true));
+ }
+
+ if (this.byday.length || this.bysetpos.length) {
+ var weekday_items = get_position_weekday(this);
+ parts.push(
+ interpolate(
+ recurrence.display.tokens.on_the_items,
+ {'items': weekday_items}, true));
+ }
+ }
+
+ if (this.freq == recurrence.MONTHLY) {
+ if (this.bymonthday.length) {
+ // i.e. 'on the 1st, 5th, 10th'
+ var items = recurrence.array.foreach(
+ this.bymonthday, function(day, i) {
+ var dt = new Date();
+ dt.setMonth(0);
+ dt.setDate(day);
+ return recurrence.date.format(dt, recurrence.display.month_day);
+ });
+ items = items.join(', ');
+ parts.push(
+ interpolate(
+ recurrence.display.tokens.on_the_items,
+ {'items': items}, true));
+
+ } else if (this.byday.length) {
+ if (this.byday.length || this.bysetpos.length) {
+ var weekday_items = get_position_weekday(this);
+ parts.push(
+ interpolate(
+ recurrence.display.tokens.on_the_items,
+ {'items': weekday_items}, true));
+ }
+ }
+ }
+
+ if (this.freq == recurrence.WEEKLY) {
+ if (this.byday.length) {
+ // i.e. 'each tuesday, wednesday'
+ var items = recurrence.array.foreach(
+ this.byday, function(byday) {
+ var weekday_number = recurrence.to_weekday(byday).number;
+ if (short)
+ var weekday = recurrence.display.weekdays_short[
+ weekday_number];
+ else
+ var weekday = recurrence.display.weekdays[
+ weekday_number];
+ return weekday;
+ });
+ items = items.join(', ');
+ parts.push(
+ interpolate(
+ recurrence.display.tokens.each,
+ {'items': items}, true));
+ }
+ }
+
+ // daily frequencies has no additional formatting,
+ // hour/minute/second formatting not supported.
+
+ if (this.count) {
+ if (this.count == 1)
+ parts.push(
+ interpolate(
+ recurrence.display.tokens.count,
+ {'number': this.count}, true));
+ else
+ parts.push(
+ interpolate(
+ recurrence.display.tokens.count_plural,
+ {'number': this.count}, true));
+ } else if (this.until) {
+ parts.push(
+ interpolate(
+ recurrence.display.tokens.until,
+ {'date': recurrence.date.format(this.until, pgettext('Until date format', '%Y-%m-%d'))}, true));
+ }
+
+ return parts.join(', ');
+ }
+};
+
+
+recurrence.Recurrence = function(options) {
+ this.init(options);
+};
+recurrence.Recurrence.prototype = {
+ init: function(options) {
+ options = options || {};
+ this.dtstart = options.dtstart || null;
+ this.dtend = options.dtend || null;
+ this.rrules = recurrence.array.from(options.rrules || []);
+ this.exrules = recurrence.array.from(options.exrules || []);
+ this.rdates = recurrence.array.from(options.rdates || []);
+ this.exdates = recurrence.array.from(options.exdates || []);
+ },
+
+ copy: function() {
+ return new recurrence.Recurrence({
+ 'rrules': recurrence.array.foreach(
+ this.rrules, function(item) {return item.copy();}),
+ 'exrules': recurrence.array.foreach(
+ this.exrules, function(item) {return item.copy();}),
+ 'rdates': recurrence.array.foreach(
+ this.rdates, function(item) {return item.copy();}),
+ 'exdates': recurrence.array.foreach(
+ this.exdates, function(item) {return item.copy();})
+ });
+ },
+
+ serialize: function() {
+ return recurrence.serialize(this);
+ }
+};
+
+
+recurrence.Weekday = function(number, index) {
+ this.init(number, index);
+};
+recurrence.Weekday.prototype = {
+ init: function(number, index) {
+ this.number = number;
+ this.index = index || null;
+ },
+
+ with_index: function(index) {
+ if (index == this.index)
+ return this;
+ else
+ return new recurrence.Weekday(this.number, index);
+ },
+
+ equals: function(other) {
+ if (this.number == other.number && this.index == other.index)
+ return True;
+ else
+ return False;
+ },
+
+ toString: function() {
+ if (this.index)
+ return this.index + recurrence.weekdays[this.number];
+ else
+ return recurrence.weekdays[this.number];
+ }
+};
+
+
+recurrence.DateFormat = function(date) {
+ this.init(date);
+};
+recurrence.DateFormat.prototype = {
+ init: function(date) {
+ this.data = date;
+ },
+
+ format: function(format) {
+ var tokens = format.match(recurrence.DateFormat.formatchars);
+ recurrence.array.foreach(tokens, function(token) {
+ if (this[token.charAt(1)])
+ format = format.replace(token, this[token.charAt(1)]());
+ }, this);
+ return format;
+ },
+
+ a: function() {
+ if (this.data.getHours() > 11)
+ return recurrence.display.ampm.am;
+ else
+ return recurrence.display.ampm.pm;
+ },
+
+ A: function() {
+ if (this.data.getHours() > 11)
+ return recurrence.display.ampm.AM;
+ else
+ return recurrence.display.ampm.PM;
+ },
+
+ f: function() {
+ if (this.data.getMinutes() == 0)
+ return this.g();
+ else
+ return [this.g(), this.i()].join(':');
+ },
+
+ g: function() {
+ if (this.data.getHours() == 0)
+ return 12;
+ else
+ return this.data.getHours() - 12;
+ return this.data.getHours();
+ },
+
+ G: function() {
+ return this.data.getHours();
+ },
+
+ h: function() {
+ return recurrence.string.rjust(String(this.g()), 2, '0');
+ },
+
+ H: function() {
+ return recurrence.string.rjust(String(this.G()), 2, '0');
+ },
+
+ i: function() {
+ return recurrence.string.rjust(String(this.data.getMinutes()), 2, '0');
+ },
+
+ P: function() {
+ if (this.data.getMinutes() == 0 && this.data.getHours() == 0)
+ return recurrence.display.tokens.midnight;
+ if (this.data.getMinutes() == 0 && this.data.getHours() == 12)
+ return recurrence.display.tokens.noon;
+ },
+
+ s: function() {
+ return recurrence.string.rjust(String(this.data.getSeconds()), 2, '0');
+ },
+
+ Y: function() {
+ return this.data.getFullYear();
+ },
+
+ b: function() {
+ return recurrence.display.months_short[this.data.getMonth()];
+ },
+
+ d: function() {
+ return recurrence.string.rjust(String(this.data.getDate()), 2, '0');
+ },
+
+ D: function() {
+ return recurrence.display.weekdays_short[
+ recurrence.date.weekday(this.data)];
+ },
+
+ F: function() {
+ return recurrence.display.months[this.data.getMonth()];
+ },
+
+ I: function() {
+ var now = new Date();
+ var date1 = new Date(now.getFullYear(), 0, 1, 0, 0, 0, 0);
+ var date2 = new Date(now.getFullYear(), 6, 1, 0, 0, 0, 0);
+ var temp = date1.toGMTString();
+ var date3 = new Date(temp.substring(0, temp.lastIndexOf(' ')-1));
+ var temp = date2.toGMTString();
+ var date4 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
+ var hours_diff_standard_time = (date1 - date3) / (1000 * 60 * 60);
+ var hours_diff_daylight_time = (date2 - date4) / (1000 * 60 * 60);
+ if (hours_diff_daylight_time == hours_diff_standard_time)
+ return '0';
+ else
+ return '1';
+ },
+
+ j: function() {
+ return this.data.getDate();
+ },
+
+ l: function() {
+ return recurrence.display.weekdays[
+ recurrence.date.weekday(this.data)];
+ },
+
+ L: function() {
+ return recurrence.date.isleap(this.data);
+ },
+
+ m: function() {
+ return recurrence.string.rjust(
+ String(this.data.getMonth() + 1), 2, '0');
+ },
+
+ M: function() {
+ return recurrence.display.months_short[this.data.getMonth()];
+ },
+
+ n: function() {
+ return this.data.getMonth() + 1;
+ },
+
+ N: function() {
+ return recurrence.display.months_ap[this.data.getMonth()];
+ },
+
+ O: function() {
+ var seconds = this.Z();
+ return (
+ '+' +
+ recurrence.string.rjust(
+ String(Math.floor(seconds / 3600)), 2, '0') +
+ recurrence.string.rjust(
+ String(Math.floor((seconds / 60) % 60)), 2, '0'));
+ },
+
+ r: function() {
+ return recurrence.date.format(this, '%D, %j %M %Y %H:%i:%s %O');
+ },
+
+ S: function() {
+ var day = this.data.getDate();
+ var ordinal_indicator = recurrence.display.ordinal_indicator;
+ var language_code = recurrence.language_code;
+ if (language_code in ordinal_indicator)
+ return ordinal_indicator[language_code](day);
+ return '';
+ },
+
+ t: function() {
+ var month = this.data.getMonth()
+ var ndays =
+ recurrence.DateFormat.mdays[month] +
+ (month == recurrence.FEBRUARY && this.L());
+ return recurrence.string.rjust(String(ndays), 2, '0');
+ },
+
+ T: function() {
+ var tzname = String(this.data).match(/\([^\)]+\)/g).slice(1, -1);
+ if (!tzname)
+ tzname = recurrence.date.format(this, '%O');
+ return tzname;
+ },
+
+ U: function() {
+ return this.data.getTime();
+ },
+
+ w: function() {
+ return recurrence.date.weekday(this.data.weekday);
+ },
+
+ W: function() {
+ var week_number = null;
+ var jan1_weekday = new Date(
+ this.data.getFullYear(), this.data.getMonth(), 1);
+ var weekday = this.data.getDay();
+ var day_of_year = self.z();
+ var prev_year = new Date(this.data.getFullYear() - 1, 0, 1);
+ if (day_of_year <= (8 - jan1_weekday) && jan1_weekday > 4) {
+ if (jan1_weekday == 5 ||
+ (jan1_weekday == 6 && recurrence.date.isleap(prev_year)))
+ week_number = 53;
+ else
+ week_number = 52;
+ } else {
+ if (recurrence.date.isleap(this.data))
+ var i = 366;
+ else
+ var i = 365;
+ if ((i - day_of_year) < (4 - weekday)) {
+ week_number = 1;
+ } else {
+ var j = day_of_year + (7 - weekday) + (jan1_weekday - 1);
+ week_number = Math.floor(j / 7);
+ if (jan1_weekday > 4)
+ week_number = week_number - 1;
+ }
+ }
+ return week_number;
+ },
+
+ y: function() {
+ return String(this.data.getFullYear()).slice(2);
+ },
+
+ Y: function() {
+ return this.data.getFullYear();
+ },
+
+ z: function() {
+ var doy = recurrence.DateFormat.year_days[this.data.getMonth()] +
+ this.data.getDate();
+ if (this.L() && this.data.getMonth() > 2)
+ doy += 1;
+ return doy;
+ },
+
+ Z: function() {
+ var offset = this.data.getTimezoneOffset();
+ return offset * 60;
+ }
+};
+recurrence.DateFormat.formatchars = RegExp(
+ '%[aAbBdDfFgGhHiIjlLmMnNOPrsStTUwWyYzZ]', 'g');
+recurrence.DateFormat.year_days = [
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
+recurrence.DateFormat.mdays = [
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+
+recurrence.to_weekday = function(token) {
+ if (token.number && token.index) {
+ return new recurrence.Weekday(token.number, token.index);
+ } else if (String(token).match(/[^0-9\-]/g)) {
+ var token = String(token);
+ var constant = token.slice(-2, token.length);
+ var nth = token.slice(0, -2);
+ if (nth.match(/[^0-9\-]/g))
+ throw Error('Invalid weekday token.');
+ var weekday = recurrence.weekdays.indexOf(constant);
+ if (weekday < 0)
+ throw Error('Invalid weekday token.');
+ else
+ return new recurrence.Weekday(weekday, nth || null);
+ } else {
+ return new recurrence.Weekday(parseInt(token, 10));
+ }
+ throw Error('Invalid weekday token.');
+};
+
+
+recurrence.serialize = function(rule_or_recurrence) {
+ var serialize_dt = function(dt) {
+ var pad = function(initial, length) {
+ initial = String(initial)
+ var offset = length - initial.length;
+ if (offset < 0) {
+ return initial;
+ } else {
+ while (initial.length < length) {
+ initial = '0' + initial;
+ }
+ return initial;
+ }
+ };
+ return pad(dt.getUTCFullYear(), 4) +
+ pad(dt.getUTCMonth() + 1, 2) +
+ pad(dt.getUTCDate(), 2) + 'T' +
+ pad(dt.getUTCHours(), 2) +
+ pad(dt.getUTCMinutes(), 2) +
+ pad(dt.getUTCSeconds(), 2) + 'Z';
+ };
+
+ var serialize_rule = function(rule) {
+ var map_to_string = function(sequence) {
+ var new_sequence = [];
+ recurrence.array.foreach(sequence, function(item) {
+ new_sequence.push(String(item));
+ });
+ return new_sequence;
+ };
+
+ var map_to_param = function(sequence) {
+ var new_sequence = [];
+ recurrence.array.foreach(sequence, function(item) {
+ new_sequence.push(item[0] + '=' + item[1].join(','));
+ });
+ return new_sequence;
+ };
+
+ var values = [];
+
+ values.push(['FREQ', [recurrence.frequencies[rule.freq]]]);
+ if (rule.interval != 1)
+ values.push(['INTERVAL', [String(rule.interval)]]);
+ if (rule.wkst)
+ values.push(['WKST', [recurrence.weekdays[rule.wkst]]]);
+ if (rule.count != null)
+ values.push(['COUNT', [String(rule.count)]]);
+ else if (rule.until != null)
+ values.push(['UNTIL', [serialize_dt(rule.until)]]);
+ if (rule.byday.length) {
+ var days = recurrence.array.foreach(rule.byday, function(item) {
+ return recurrence.to_weekday(item).toString();
+ });
+ values.push(['BYDAY', days]);
+ }
+ recurrence.array.foreach(recurrence.byparams, function(param) {
+ if (param != 'byday') {
+ var value_list = rule[param] || [];
+ if (value_list.length)
+ values.push([param.toUpperCase(), map_to_string(value_list)]);
+ }
+ });
+ return map_to_param(values).join(';');
+ };
+
+ var map_to_property = function(sequence) {
+ var new_sequence = recurrence.array.foreach(
+ sequence, function(item) {
+ return item.join(':');
+ });
+ return new_sequence;
+ };
+
+ var obj = rule_or_recurrence;
+ if (obj.freq)
+ obj = new recurrence.Recurrence({'rrules': [obj]});
+
+ var items = [];
+
+ if (obj.dtstart)
+ items.push(['DTSTART', serialize_dt(obj.dtstart)]);
+ if (obj.dtend)
+ items.push(['DTEND', serialize_dt(obj.dtend)]);
+
+ recurrence.array.foreach(
+ obj.rrules, function(item) {
+ items.push(['RRULE', serialize_rule(item)]);
+ });
+ recurrence.array.foreach(
+ obj.exrules, function(item) {
+ items.push(['EXRULE', serialize_rule(item)]);
+ });
+ recurrence.array.foreach(
+ obj.rdates, function(item) {
+ items.push(['RDATE', serialize_dt(item)]);
+ });
+ recurrence.array.foreach(
+ obj.exdates, function(item) {
+ items.push(['EXDATE', serialize_dt(item)]);
+ });
+
+ return map_to_property(items).join('\n');
+};
+
+
+recurrence.deserialize = function(text) {
+ var deserialize_dt = function(text) {
+ var year = parseInt(text.slice(0, 4), 10);
+ var month = parseInt(text.slice(4, 6), 10);
+ var day = parseInt(text.slice(6, 8), 10);
+ if (text.indexOf('T') > 0) {
+ var hour = parseInt(text.slice(9, 11), 10);
+ var minute = parseInt(text.slice(11, 13), 10);
+ var second = parseInt(text.slice(13, 15), 10);
+ } else {
+ var hour = 0;
+ var minute = 0;
+ var second = 0;
+ }
+ var dt = new Date();
+ if (text.indexOf('Z') > 0) {
+ dt.setUTCFullYear(year);
+ dt.setUTCMonth(month - 1);
+ dt.setUTCDate(day);
+ dt.setUTCHours(hour);
+ dt.setUTCMinutes(minute);
+ dt.setUTCSeconds(second);
+ } else {
+ dt.setFullYear(year);
+ dt.setMonth(month - 1);
+ dt.setDate(day);
+ dt.setHours(hour);
+ dt.setMinutes(minute);
+ dt.setSeconds(second);
+ }
+ return dt;
+ };
+
+ var dtstart = null;
+ var dtend = null;
+ var rrules = [];
+ var exrules = [];
+ var rdates = [];
+ var exdates = [];
+
+ var pattern = /(DTSTART|DTEND|RRULE|EXRULE|RDATE|EXDATE)[^:]*:(.*)/g;
+ var tokens = text.match(pattern) || [];
+
+ recurrence.array.foreach(
+ tokens, function(token) {
+ var label = token.split(':', 2)[0];
+ var param_text = token.split(':', 2)[1];
+
+ if (param_text.indexOf('=') < 0) {
+ var params = param_text;
+ } else {
+ var param_tokens = param_text.split(';');
+ var params = recurrence.array.foreach(
+ param_tokens, function(item) {
+ var param_name = recurrence.string.strip(
+ item.split('=', 2)[0]);
+ var param_value = recurrence.string.strip(
+ item.split('=', 2)[1]);
+ var value_list = param_value.split(',');
+ var value_list = recurrence.array.foreach(
+ param_value.split(','), function(item) {
+ return recurrence.string.strip(item);
+ });
+ return [param_name, value_list];
+ });
+ }
+
+ if (label == 'RRULE' || label == 'EXRULE') {
+ var freq = 0;
+ var options = {};
+ recurrence.array.foreach(
+ params, function(item) {
+ var key = item[0];
+ var param = key.toLowerCase();
+ var value = item[1];
+
+ if (key == 'FREQ') {
+ if (recurrence.frequencies.indexOf(value[0]) != -1) {
+ freq = recurrence.frequencies.indexOf(value[0]);
+ }
+ } else if (key == 'INTERVAL') {
+ options[param] = parseInt(value[0], 10);
+ } else if (key == 'WKST') {
+ options[param] = recurrence.to_weekday(value[0]);
+ } else if (key == 'COUNT') {
+ options[param] = parseInt(value[0], 10);
+ } else if (key == 'UNTIL') {
+ options[param] = deserialize_dt(value[0]);
+ } else if (key == 'BYDAY') {
+ options[param] = recurrence.array.foreach(
+ value, function(item) {
+ return recurrence.to_weekday(item);
+ });
+ } else {
+ options[param] = recurrence.array.foreach(
+ value, function(item) {
+ return parseInt(item, 10);
+ });
+ }
+ });
+ if (label == 'RRULE')
+ rrules.push(new recurrence.Rule(freq, options));
+ else
+ exrules.push(new recurrence.Rule(freq, options));
+
+ } else if (label == 'DTSTART') {
+ dtstart = deserialize_dt(params);
+ } else if (label == 'DTEND') {
+ dtend = deserialize_dt(params);
+ } else if (label == 'RDATE') {
+ rdates.push(deserialize_dt(params));
+ } else if (label == 'EXDATE') {
+ exdates.push(deserialize_dt(params));
+ }
+ });
+
+ return new recurrence.Recurrence({
+ 'dtstart': dtstart, 'dtend': dtend,
+ 'rrules': rrules, 'exrules': exrules,
+ 'rdates': rdates, 'exdates': exdates
+ });
+};
+
+
+recurrence.log = function(message) {
+ var dom = document.createElement('div');
+ dom.innerHTML = message;
+ document.body.insertBefore(dom, document.body.firstChild);
+};
+
+
+recurrence.string = {
+ format: function(string, map) {
+ for (var key in map)
+ string = string.replace(RegExp('%' + key, 'g'), map[key]);
+ return string
+ },
+
+ capitalize: function(string) {
+ return (
+ string.charAt(0).toUpperCase() +
+ string.slice(1, string.length));
+ },
+
+ strip: function(string) {
+ return string.replace(
+ /^[ \t\n\r]*/, '').replace(/[ \t\n\r]*$/, '');
+ },
+
+ rjust: function(string, length, character) {
+ var initial = String(string);
+ var offset = length - initial.length;
+ character = character || ' ';
+ if (offset < 0) {
+ return initial;
+ } else {
+ while (initial.length < length) {
+ initial = character.charAt(0) + initial;
+ }
+ return initial;
+ }
+ },
+
+ ljust: function(string, length, character) {
+ var initial = String(string);
+ var offset = length - initial.length;
+ character = character || ' ';
+ if (offset < 0) {
+ return initial;
+ } else {
+ while (initial.length < length) {
+ initial = initial + character[0];
+ }
+ return initial;
+ }
+ }
+};
+
+
+recurrence.date = {
+ format: function(date, format) {
+ return new recurrence.DateFormat(date).format(format);
+ },
+
+ isleap: function(date) {
+ var year = date.getFullYear();
+ return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
+ },
+
+ weekday: function(date) {
+ var day = date.getDay() - 1;
+ if (day < 0)
+ day = day + 7;
+ else if (day > 6)
+ day = day - 7;
+ return day;
+ },
+
+ days_in_month: function(date) {
+ var m = date.getMonth() + 1;
+ var y = date.getFullYear();
+ if (m == 1 || m == 3 || m == 5 || m == 7 ||
+ m == 8 || m == 10 || m == 12)
+ return 31;
+ else if (m == 4 || m == 6 || m == 9 || m == 11)
+ return 30;
+ else if (m == 2 && recurrence.date.isleap(date))
+ return 29;
+ else
+ return 28;
+ }
+};
+
+
+recurrence.array = {
+ foreach: function(array, func, bindto) {
+ if (bindto)
+ func = recurrence.func.bind(func, bindto);
+ array = recurrence.array.from(array);
+ var return_values = [];
+ for (var i=0; i < array.length; i++)
+ return_values.push(func(array[i], i));
+ return return_values;
+ },
+
+ from: function(iterable) {
+ if (!iterable)
+ return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i=0, length=iterable.length; i < length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+ },
+
+ remove: function(array, item) {
+ array.splice(array.indexOf(item), 1);
+ }
+};
+
+
+recurrence.func = {
+ bind: function() {
+ var args = recurrence.array.from(arguments);
+ var func = args.shift();
+ var object = args.shift();
+ return function() {
+ return func.apply(
+ object, args.concat(recurrence.array.from(arguments)));
+ }
+ }
+};
+
+
+if (!Array.indexOf) {
+ // ie doesn't have indexOf on arrays
+ Array.prototype.indexOf = function(obj) {
+ for (var i=0; i < this.length; i++)
+ if (this[i] == obj)
+ return i;
+ return -1;
+ };
+}
+
+
+// frequencies
+recurrence.YEARLY = 0;
+recurrence.MONTHLY = 1;
+recurrence.WEEKLY = 2;
+recurrence.DAILY = 3;
+recurrence.HOURLY = 4;
+recurrence.MINUTELY = 5;
+recurrence.SECONDLY = 6;
+
+// months
+recurrence.JANUARY = 1;
+recurrence.FEBRUARY = 2;
+recurrence.MARCH = 3;
+recurrence.APRIL = 4;
+recurrence.MAY = 5;
+recurrence.JUNE = 6;
+recurrence.JULY = 7;
+recurrence.AUGUST = 8;
+recurrence.SEPTEMBER = 9;
+recurrence.OCTOBER = 10;
+recurrence.NOVEMBER = 11;
+recurrence.DECEMBER = 12;
+
+// weekdays
+recurrence.MONDAY = recurrence.MO = new recurrence.Weekday(0, null);
+recurrence.TUEDSAY = recurrence.TU = new recurrence.Weekday(1, null);
+recurrence.WEDNESDAY = recurrence.WE = new recurrence.Weekday(2, null);
+recurrence.THURSDAY = recurrence.TH = new recurrence.Weekday(3, null);
+recurrence.FRIDAY = recurrence.FR = new recurrence.Weekday(4, null);
+recurrence.SATURDAY = recurrence.SA = new recurrence.Weekday(5, null);
+recurrence.SUNDAY = recurrence.SU = new recurrence.Weekday(6, null);
+
+// enumerations
+recurrence.byparams = [
+ 'bysetpos', 'bymonth', 'bymonthday', 'byyearday',
+ 'byweekno', 'byday', 'byhour', 'byminute', 'bysecond'
+];
+recurrence.frequencies = [
+ 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY',
+ 'HOURLY', 'MINUTELY', 'SECONDLY'
+];
+recurrence.weekdays = [
+ 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'
+];
+// recurrence.firstweekday = 0;
+
+
+// i18n no-ops if jsi18n not loaded
+
+if (typeof(catalog) === 'undefined') {
+ var catalog = [];
+} else {
+ var catalog = catalog;
+}
+
+var gettext = gettext || function(msgid) {
+ var value = catalog[msgid];
+ if (typeof(value) == 'undefined') {
+ return msgid;
+ } else {
+ return (typeof(value) == 'string') ? value : value[0];
+ }
+};
+
+var interpolate = interpolate || function(fmt, obj, named) {
+ if (named) {
+ return fmt.replace(/%\(\w+\)s/g, function(match) {
+ return String(obj[match.slice(2,-2)])
+ });
+ } else {
+ return fmt.replace(/%s/g, function(match) {
+ return String(obj.shift())
+ });
+ }
+};
+
+
+// display
+
+if (!recurrence.display)
+ recurrence.display = {};
+
+recurrence.display.tokens = {
+ 'midnight': gettext('midnight'),
+ 'noon': gettext('noon'),
+ 'on_the_items': gettext('on the %(items)s'),
+ 'every_number_freq': gettext('every %(number)s %(freq)s'),
+ 'each': gettext('each %(items)s'),
+ 'count': gettext('occuring %(number)s time'),
+ 'count_plural': gettext('occuring %(number)s times'),
+ 'until': gettext('until %(date)s')
+};
+
+recurrence.display.timeintervals = [
+ gettext('year'), gettext('month'), gettext('week'), gettext('day'),
+ gettext('hour'), gettext('minute'), gettext('second')
+];
+recurrence.display.timeintervals_plural = [
+ gettext('years'), gettext('months'), gettext('weeks'), gettext('days'),
+ gettext('hours'), gettext('minutes'), gettext('seconds')
+];
+recurrence.display.frequencies = [
+ gettext('annually'), gettext('monthly'), gettext('weekly'), gettext('daily'),
+ gettext('hourly'), gettext('minutely'), gettext('secondly')
+];
+recurrence.display.weekdays = [
+ gettext('Monday'), gettext('Tuesday'), gettext('Wednesday'), gettext('Thursday'),
+ gettext('Friday'), gettext('Saturday'), gettext('Sunday')
+];
+recurrence.display.weekdays_short = [
+ gettext('Mon'), gettext('Tue'), gettext('Wed'), gettext('Thu'),
+ gettext('Fri'), gettext('Sat'), gettext('Sun')
+];
+recurrence.display.weekdays_oneletter = [
+ pgettext('Monday first letter', 'M'),
+ pgettext('Tuesday first letter', 'T'),
+ pgettext('Wednesday first letter', 'W'),
+ pgettext('Thursday first letter', 'T'),
+ pgettext('Friday first letter', 'F'),
+ pgettext('Saturday first letter', 'S'),
+ pgettext('Sunday first letter', 'S')
+];
+recurrence.display.weekdays_position = {
+ '1': gettext('first %(weekday)s'),
+ '2': gettext('second %(weekday)s'),
+ '3': gettext('third %(weekday)s'),
+ '4': gettext('fourth %(weekday)s'),
+ '-1': gettext('last %(weekday)s'),
+ '-2': gettext('second last %(weekday)s'),
+ '-3': gettext('third last %(weekday)s')
+};
+recurrence.display.weekdays_position_short = {
+ '1': gettext('1st %(weekday)s'),
+ '2': gettext('2nd %(weekday)s'),
+ '3': gettext('3rd %(weekday)s'),
+ '4': gettext('4th %(weekday)s'),
+ '-1': gettext('last %(weekday)s'),
+ '-2': gettext('2nd last %(weekday)s'),
+ '-3': gettext('3rd last %(weekday)s')
+};
+recurrence.display.months = [
+ gettext('January'), gettext('February'), gettext('March'),
+ gettext('April'), pgettext('month name', 'May'), gettext('June'),
+ gettext('July'), gettext('August'), gettext('September'),
+ gettext('October'), gettext('November'), gettext('December')
+];
+recurrence.display.months_short = [
+ gettext('Jan'), gettext('Feb'), gettext('Mar'),
+ gettext('Apr'), pgettext('month name', 'May'), gettext('Jun'),
+ gettext('Jul'), gettext('Aug'), gettext('Sep'),
+ gettext('Oct'), gettext('Nov'), gettext('Dec')
+];
+recurrence.display.months_ap = [
+ gettext('Jan.'), gettext('Feb.'), gettext('March'),
+ gettext('April'), pgettext('month name', 'May'), gettext('June'),
+ gettext('July'), gettext('Aug.'), gettext('Sept.'),
+ gettext('Oct.'), gettext('Nov.'), gettext('Dec.')
+];
+recurrence.display.ampm = {
+ 'am': gettext('a.m.'), 'pm': gettext('p.m.'),
+ 'AM': gettext('AM'), 'PM': gettext('PM')
+};
+recurrence.display.month_day = pgettext('Day of month', '%j%S');
+
+recurrence.display.ordinal_indicator = {
+ 'en-us': function(day) {
+ if (day == 11 || day == 12 || day == 13)
+ return 'th';
+ var last = day % 10;
+ if (last == 1)
+ return 'st';
+ if (last == 2)
+ return 'nd';
+ if (last == 3)
+ return 'rd';
+ return 'th';
+ },
+ 'fr-FR': function(day) {
+ if (day == 1)
+ return 'er';
+ return '';
+ }
+};