/**
@namespace
*/
(function(module, ns) {
// Iterate over all entries if x is an array, otherwise just call fn on x.
function ForAll(x, fn) {
if (!(x instanceof Array)) {
fn(x);
return;
}
for (var n = 0; n < x.length; ++n)
fn(x[n]);
}
/* Pattern for an individual entry: name:value */
var ENTRY = /^([A-Za-z0-9-]+)((?:;[A-Za-z0-9-]+=(?:"[^"]+"|[^";:,]+)(?:,(?:"[^"]+"|[^";:,]+))*)*):(.*)$/;
/* Pattern for an individual parameter: name=value[,value] */
var PARAM = /;([A-Za-z0-9-]+)=((?:"[^"]+"|[^";:,]+)(?:,(?:"[^"]+"|[^";:,]+))*)/g;
/* Pattern for an individual parameter value: value | "value" */
var PARAM_VALUE = /,?("[^"]+"|[^";:,]+)/g;
// Parse a calendar in iCal format.
function ParseICal (text, success, error) {
// Parse the text into an object graph
var lines = text.replace('\r', '').split('\n');
var tos = Object.create(null);
var stack = [tos];
// Parse parameters for an entry. Foramt: <param>=<pvalue>[;...]
function parseParams(params) {
var map = Object.create(null);
var param = PARAM.exec(params);
while (param) {
var values = [];
var value = PARAM_VALUE.exec(param[2]);
while (value) {
values.push(value[1].replace(/^"(.*)"$/, '$1'));
value = PARAM_VALUE.exec(param[2]);
}
map[param[1].toLowerCase()] = (values.length > 1 ? values : values[0]);
param = PARAM.exec(params);
}
return map;
}
// Add a property to the current object. If a property with the same name
// already exists, turn it into an array.
function add(prop, value, params) {
if (params)
value = { parameters: parseParams(params), value: value };
if (prop in tos) {
var previous = tos[prop];
if (previous instanceof Array) {
previous.push(value);
return;
}
value = [previous, value];
}
tos[prop] = value;
}
for (var n = 0; n < lines.length; ++n) {
var line = lines[n];
// check whether the line continues (next line stats with space or tab)
var nextLine;
while ((nextLine = lines[n+1]) && (nextLine[0] == ' ' || nextLine[0] == '\t')) {
line += nextLine.substr(1);
++n;
continue;
}
// parse the entry, format is 'PROPERTY:VALUE'
var matches = ENTRY.exec(line);
if (!matches)
return error('invalid format');
var prop = matches[1].toLowerCase();
var params = matches[2];
var value = matches[3];
switch (prop) {
case 'begin':
var obj = Object.create(null);
add(value.toLowerCase(), obj);
stack.push(tos = obj);
break;
case 'end':
stack.pop();
tos = stack[stack.length - 1];
if (stack.length == 1) {
var cal = stack[0];
if (typeof cal.vcalendar != 'object' || cal.vcalendar instanceof Array)
return error('single vcalendar object expected');
return success(cal.vcalendar);
}
break;
default:
add(prop, value, params);
break;
}
}
return error('unexpected end of file');
}
function Value(v) {
return (typeof v !== 'object') ? v : v.value;
}
function Parameter(v, name) {
if (typeof v !== 'object')
return undefined;
return v.parameters[name];
}
// Parse a time specification.
function ParseDateTime(v) {
var dt = Value(v);
if (Parameter(v, 'VALUE') == 'DATE') {
// 20081202
return new Date(dt.substr(0, 4), dt.substr(4, 2), dt.substr(6, 2));
}
v = Value(v);
// 20120426T130000Z
var year = dt.substr(0, 4);
var month = dt.substr(4, 2) - 1;
var day = dt.substr(6, 2);
var hour = dt.substr(9, 2);
var min = dt.substr(11, 2);
var sec = dt.substr(13, 2);
if (dt[15] == 'Z')
return new Date(Date.UTC(year, month, day, hour, min, sec));
return new Date(year, month, day, hour, min, sec);
}
module.exports = ParseICal;
}.apply(
this,
(this.Webcals) ?
[Webcals('ics'), Webcals] :
[module, require('./webcals')]
));