From c9f8d700b80287117d9610ac183cf66854dc81e9 Mon Sep 17 00:00:00 2001 From: Christian Dittrich Date: Tue, 22 Jul 2014 11:35:33 +0200 Subject: [PATCH 01/10] more sanity checks on organizer data, added quotes to CN property to fix parsing error in Google Calendar --- icalevent.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/icalevent.js b/icalevent.js index 085ab95..2e23c0f 100644 --- a/icalevent.js +++ b/icalevent.js @@ -79,7 +79,17 @@ iCalEvent.prototype = { if (this.event.end) result += 'DTEND;TZID=' + this.event.timezone + ':' + this.event.end + '\r\n'; if (this.event.summary) result += 'SUMMARY:' + this.event.summary + '\r\n'; if (this.event.description) result += 'DESCRIPTION:' + this.event.description + '\r\n'; - if (this.event.organizer) result += 'ORGANIZER;CN=' + this.event.organizer.name + ':mailto:' + this.event.organizer.email + '\r\n'; + if (this.event.organizer) { + result += 'ORGANIZER' + if (this.event.organizer.name) { + result += ';CN="' + this.event.organizer.name; + '"'; + } + result += ':' + if (this.event.organizer.email) { + result += 'mailto:' + this.event.organizer.email + } + result += '\r\n'; + } if (this.event.location) result += 'LOCATION:' + this.event.location + '\r\n'; if (this.event.url) result += 'URL;VALUE=URI:' + this.event.url + '\r\n'; if (this.event.attendees) { From 89e9ac937b34a230289a6c94415951fd8b94f91f Mon Sep 17 00:00:00 2001 From: Christian Dittrich Date: Tue, 22 Jul 2014 11:38:06 +0200 Subject: [PATCH 02/10] replaced custom date parsing by Moment.js --- icalevent.js | 21 +++------------------ package.json | 3 ++- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/icalevent.js b/icalevent.js index 2e23c0f..a0c119c 100644 --- a/icalevent.js +++ b/icalevent.js @@ -1,4 +1,5 @@ var tzone = require('tzone'); +var moment = require('moment'); var iCalEvent = function(event){ @@ -24,21 +25,7 @@ iCalEvent.prototype = { }, format: function(datetime){ - function pad(n){ - return (n < 10 ? '0' : '') + n; - } - - var d = new Date(datetime); - d.setUTCMinutes(d.getUTCMinutes() - this.event.offset); - - padded = (d.getUTCFullYear() - + pad(d.getUTCMonth() + 1) - + pad(d.getUTCDate()) + 'T' - + pad(d.getUTCHours()) - + pad(d.getUTCMinutes()) - + pad(d.getUTCSeconds())); - - return padded; + return moment(datetime).format('YYYYMMDD[T]HHmmss'); }, get: function(key){ @@ -52,9 +39,7 @@ iCalEvent.prototype = { start: function(datetime){ if (!this.event.timezone) { - var d = new Date(datetime); - d.setUTCMinutes(d.getUTCMinutes() - this.event.offset); - this.event.timezone = tzone.getLocation(d); + this.event.timezone = tzone.getLocation(moment(datetime).toDate()); } this.event.start = this.format(datetime); }, diff --git a/package.json b/package.json index d72ee91..a8bbe1b 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "repository": "git://github.com/shanebo/icalevent.git", "author": "Shane Thacker ", "dependencies": { - "tzone": "0.0.2" + "tzone": "0.0.2", + "moment: "2.7.0" }, "engine": ">= 0.4.1", "main": "./icalevent" From 1138fbed61db98e4be56552906941ccdbd31d26a Mon Sep 17 00:00:00 2001 From: Christian Dittrich Date: Tue, 22 Jul 2014 11:39:21 +0200 Subject: [PATCH 03/10] added multiple event support --- example.js | 21 ++++++++++++------ icalevent.js | 61 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 14 deletions(-) diff --git a/example.js b/example.js index e8c21b9..8342fa5 100644 --- a/example.js +++ b/example.js @@ -4,10 +4,11 @@ var tzone = require('tzone'); var http = require('http'); -var event = new iCalEvent({ +var calendar = new iCalendar({ + method: 'request' +}, [{ uid: 9873647, offset: new Date().getTimezoneOffset(), - method: 'request', status: 'confirmed', attendees: [ { @@ -30,17 +31,20 @@ var event = new iCalEvent({ email: 'luchador@monastery.org' }, url: 'http://google.com/search?q=nacho+libre' -}); +}]); // or -var e = new iCalEvent(); +var c = new iCalendar(); + +c.set('method', 'request'); + +var e = c.addEvent(); e.set('uid', 9873647); e.set('offset', new Date().getTimezoneOffset()); -e.set('method', 'request'); e.set('status', 'confirmed'); e.set('attendees', [ { @@ -63,12 +67,15 @@ e.set('url', 'http://google.com/search?q=nacho+libre'); console.log('\n'); -console.log(e.toFile()); +console.log(c.toFile()); + +console.log('\n'); +console.log(calendar.toFile()); http.createServer(function(request, response){ response.writeHead(200, {'Content-Type': 'text/calendar'}); - var file = event.toFile(); + var file = calendar.toFile(); response.end(file); }).listen(9999, '127.0.0.1'); diff --git a/icalevent.js b/icalevent.js index a0c119c..83a398e 100644 --- a/icalevent.js +++ b/icalevent.js @@ -1,9 +1,61 @@ var tzone = require('tzone'); var moment = require('moment'); +var iCalendar = function(calendar, events) { + this.calendar = {} + this.calendar.id = '-//iCalEvent.js v0.3//EN'; + this.events = []; + if(calendar) { + this.create(calendar); + } + if(events) { + for(var i = 0; i < events.length; i++) { + this.addEvent(events[i]); + } + } +} + +iCalendar.prototype = { + create: function(calendar){ + for (var key in calendar) { + if (calendar.hasOwnProperty(key)) this.set(key, calendar[key]); + } + }, + + get: function(key){ + if (this.calendar[key]) return this.calendar[key]; + }, + + set: function(key, value){ + if (this[key]) this[key](value) + else this.calendar[key] = value; + }, + + addEvent: function(event) { + newEvent = new iCalEvent(event); + this.events.push(newEvent); + return newEvent; + }, + + toFile: function() { + var result = ''; + + result += 'BEGIN:VCALENDAR\r\n'; + result += 'VERSION:2.0\r\n'; + result += 'PRODID:' + this.calendar.id + '\r\n'; + if (this.calendar.method) result += 'METHOD:' + this.calendar.method.toUpperCase() + '\r\n'; + + for(var i = 0; i < this.events.length; i++) { + result += this.events[i].toFile(); + } + + result += 'END:VCALENDAR\r\n'; + + return result; + } +} var iCalEvent = function(event){ - this.id = '-//iCalEvent.js v0.3//EN'; this.event = {}; if (event) this.create(event); if (!this.event.uid) this.event.uid = this.createUID(); @@ -51,14 +103,10 @@ iCalEvent.prototype = { toFile: function(){ var result = ''; - result += 'BEGIN:VCALENDAR\r\n'; - result += 'VERSION:2.0\r\n'; - result += 'PRODID:' + this.id + '\r\n'; result += 'BEGIN:VEVENT\r\n'; result += 'UID:' + this.event.uid + '\r\n'; result += 'DTSTAMP:' + this.format(new Date()) + '\r\n'; - if (this.event.method) result += 'METHOD:' + this.event.method.toUpperCase() + '\r\n'; if (this.event.status) result += 'STATUS:' + this.event.status.toUpperCase() + '\r\n'; if (this.event.start) result += 'DTSTART;TZID=' + this.event.timezone + ':' + this.event.start + '\r\n'; if (this.event.end) result += 'DTEND;TZID=' + this.event.timezone + ':' + this.event.end + '\r\n'; @@ -84,7 +132,6 @@ iCalEvent.prototype = { } result += 'END:VEVENT\r\n'; - result += 'END:VCALENDAR\r\n'; return result; } @@ -92,4 +139,4 @@ iCalEvent.prototype = { } -module.exports = iCalEvent; \ No newline at end of file +module.exports = iCalendar; \ No newline at end of file From d3b1f0a13e46df83c1b3108f507b41167e86c514 Mon Sep 17 00:00:00 2001 From: Christian Dittrich Date: Tue, 22 Jul 2014 12:08:21 +0200 Subject: [PATCH 04/10] added support for all day events --- icalevent.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/icalevent.js b/icalevent.js index 83a398e..3bd73e8 100644 --- a/icalevent.js +++ b/icalevent.js @@ -76,8 +76,12 @@ iCalEvent.prototype = { }); }, - format: function(datetime){ - return moment(datetime).format('YYYYMMDD[T]HHmmss'); + format: function(datetime, onlyDate){ + if(onlyDate) { + return moment(datetime).format('YYYYMMDD'); + } else { + return moment(datetime).format('YYYYMMDD[T]HHmmss'); + } }, get: function(key){ @@ -93,11 +97,7 @@ iCalEvent.prototype = { if (!this.event.timezone) { this.event.timezone = tzone.getLocation(moment(datetime).toDate()); } - this.event.start = this.format(datetime); - }, - - end: function(datetime){ - this.event.end = this.format(datetime); + this.event.start = datetime; }, toFile: function(){ @@ -108,8 +108,8 @@ iCalEvent.prototype = { result += 'DTSTAMP:' + this.format(new Date()) + '\r\n'; if (this.event.status) result += 'STATUS:' + this.event.status.toUpperCase() + '\r\n'; - if (this.event.start) result += 'DTSTART;TZID=' + this.event.timezone + ':' + this.event.start + '\r\n'; - if (this.event.end) result += 'DTEND;TZID=' + this.event.timezone + ':' + this.event.end + '\r\n'; + if (this.event.start) result += 'DTSTART;TZID=' + this.event.timezone + (this.event.allDay ? ';VALUE=DATE' : '') + ':' + this.format(this.event.start, this.event.allDay) + '\r\n'; + if (this.event.end) result += 'DTEND;TZID=' + this.event.timezone + (this.event.allDay ? ';VALUE=DATE' : '') + ':' + this.format(this.event.end, this.event.allDay) + '\r\n'; if (this.event.summary) result += 'SUMMARY:' + this.event.summary + '\r\n'; if (this.event.description) result += 'DESCRIPTION:' + this.event.description + '\r\n'; if (this.event.organizer) { From 69df09c6fe0096e91b37cdab6747122a0c051a2d Mon Sep 17 00:00:00 2001 From: Christian Dittrich Date: Tue, 29 Jul 2014 11:22:58 +0200 Subject: [PATCH 05/10] added name and description for calendars --- icalevent.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/icalevent.js b/icalevent.js index 3bd73e8..247d6ef 100644 --- a/icalevent.js +++ b/icalevent.js @@ -44,6 +44,14 @@ iCalendar.prototype = { result += 'VERSION:2.0\r\n'; result += 'PRODID:' + this.calendar.id + '\r\n'; if (this.calendar.method) result += 'METHOD:' + this.calendar.method.toUpperCase() + '\r\n'; + if (this.calendar.name) { + result += 'NAME:' + this.calendar.name + '\r\n'; + result += 'X-WR-CALNAME:' + this.calendar.name + '\r\n'; + } + if (this.calendar.description) { + result += 'DESCRIPTION:' + this.calendar.description + '\r\n'; + result += 'X-WR-CALDESC:' + this.calendar.description + '\r\n'; + } for(var i = 0; i < this.events.length; i++) { result += this.events[i].toFile(); From 162b0e2fdc2e743e7adc5cdbb3a8d95c07e5f3ec Mon Sep 17 00:00:00 2001 From: Christian Dittrich Date: Tue, 29 Jul 2014 13:03:55 +0200 Subject: [PATCH 06/10] escape ',', ';' and '\' --- icalevent.js | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/icalevent.js b/icalevent.js index 247d6ef..a414457 100644 --- a/icalevent.js +++ b/icalevent.js @@ -1,6 +1,10 @@ var tzone = require('tzone'); var moment = require('moment'); +var escapeText = function(text) { + return text.toString().replace(/([,;\\])/g, "\\$1"); +} + var iCalendar = function(calendar, events) { this.calendar = {} this.calendar.id = '-//iCalEvent.js v0.3//EN'; @@ -42,15 +46,15 @@ iCalendar.prototype = { result += 'BEGIN:VCALENDAR\r\n'; result += 'VERSION:2.0\r\n'; - result += 'PRODID:' + this.calendar.id + '\r\n'; - if (this.calendar.method) result += 'METHOD:' + this.calendar.method.toUpperCase() + '\r\n'; + result += 'PRODID:' + escapeText(this.calendar.id) + '\r\n'; + if (this.calendar.method) result += 'METHOD:' + escapeText(this.calendar.method.toUpperCase()) + '\r\n'; if (this.calendar.name) { - result += 'NAME:' + this.calendar.name + '\r\n'; - result += 'X-WR-CALNAME:' + this.calendar.name + '\r\n'; + result += 'NAME:' + escapeText(this.calendar.name) + '\r\n'; + result += 'X-WR-CALNAME:' + escapeText(this.calendar.name) + '\r\n'; } if (this.calendar.description) { - result += 'DESCRIPTION:' + this.calendar.description + '\r\n'; - result += 'X-WR-CALDESC:' + this.calendar.description + '\r\n'; + result += 'DESCRIPTION:' + escapeText(this.calendar.description) + '\r\n'; + result += 'X-WR-CALDESC:' + escapeText(this.calendar.description) + '\r\n'; } for(var i = 0; i < this.events.length; i++) { @@ -112,30 +116,30 @@ iCalEvent.prototype = { var result = ''; result += 'BEGIN:VEVENT\r\n'; - result += 'UID:' + this.event.uid + '\r\n'; + result += 'UID:' + escapeText(this.event.uid) + '\r\n'; result += 'DTSTAMP:' + this.format(new Date()) + '\r\n'; - if (this.event.status) result += 'STATUS:' + this.event.status.toUpperCase() + '\r\n'; + if (this.event.status) result += 'STATUS:' + escapeText(this.event.status.toUpperCase()) + '\r\n'; if (this.event.start) result += 'DTSTART;TZID=' + this.event.timezone + (this.event.allDay ? ';VALUE=DATE' : '') + ':' + this.format(this.event.start, this.event.allDay) + '\r\n'; if (this.event.end) result += 'DTEND;TZID=' + this.event.timezone + (this.event.allDay ? ';VALUE=DATE' : '') + ':' + this.format(this.event.end, this.event.allDay) + '\r\n'; - if (this.event.summary) result += 'SUMMARY:' + this.event.summary + '\r\n'; - if (this.event.description) result += 'DESCRIPTION:' + this.event.description + '\r\n'; + if (this.event.summary) result += 'SUMMARY:' + escapeText(this.event.summary) + '\r\n'; + if (this.event.description) result += 'DESCRIPTION:' + escapeText(this.event.description) + '\r\n'; if (this.event.organizer) { - result += 'ORGANIZER' + result += 'ORGANIZER'; if (this.event.organizer.name) { - result += ';CN="' + this.event.organizer.name; + '"'; + result += ';CN=' + escapeText(this.event.organizer.name); } - result += ':' + result += ':'; if (this.event.organizer.email) { - result += 'mailto:' + this.event.organizer.email + result += 'mailto:' + this.event.organizer.email; } result += '\r\n'; } - if (this.event.location) result += 'LOCATION:' + this.event.location + '\r\n'; + if (this.event.location) result += 'LOCATION:' + escapeText(this.event.location) + '\r\n'; if (this.event.url) result += 'URL;VALUE=URI:' + this.event.url + '\r\n'; if (this.event.attendees) { this.event.attendees.forEach(function(attendee){ - result += 'ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=' + attendee.name + ':MAILTO:' + attendee.email + '\r\n'; + result += 'ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=' + escapeText(attendee.name) + ':MAILTO:' + attendee.email + '\r\n'; }); } From 561307907843fd157331da36e5499e78651f7f15 Mon Sep 17 00:00:00 2001 From: Christian Dittrich Date: Tue, 29 Jul 2014 13:04:40 +0200 Subject: [PATCH 07/10] updated example and readme --- README.md | 225 ++++++++++++++++++++++++++++++++++++++++++++--------- example.js | 46 ++++++++++- 2 files changed, 231 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 4371ea7..d939ded 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,20 @@ With [npm](http://npmjs.org): ## Examples ``` js -var iCalEvent = require('icalevent'); +var iCalendar = require('./icalevent'); -var event = new iCalEvent({ +var calendar = new iCalendar({ + method: 'request', + name: 'Personal calendar', + description: 'Personal calendar of Johnny Boy' +}, [{ uid: 9873647, offset: new Date().getTimezoneOffset(), - method: 'request', status: 'confirmed', attendees: [ { name: 'Johnny Boy', - email: 'johnny@numberfive.com' + email: 'johnny@numberfivevent.com' }, { name: 'Homer Simpson', @@ -36,28 +39,51 @@ var event = new iCalEvent({ description: 'Home flu visit.', location: 'Casa', organizer: { - name: 'Nacho Libre', + name: 'Libre, Nacho', email: 'luchador@monastery.org' }, - url: 'http://google.com/search?q=nacho+libre' -}); + url: 'http://googlevent.com/search?q=nacho+libre' +}, { + offset: new Date().getTimezoneOffset(), + status: 'confirmed', + attendees: [ + { + name: 'Johnny Boy', + email: 'johnny@numberfivevent.com' + } + ], + start: '2014-07-02T10:00:00-05:00', + end: '2014-07-02T11:30:00-05:00', + timezone: 'US/Central', + summary: 'Meet with Jane, bring cake', + location: 'Somwhere', + organizer: { + name: 'Doe, Jane', + email: 'jane@doevent.com' + } +}]); ``` Or: ``` js -var iCalEvent = require('icalevent'); +var iCalendar = require('./icalevent'); + +var calendar = new iCalendar(); + +calendar.set('method', 'request'); +calendar.set('name', 'Personal calendar'); +calendar.set('description', 'Personal calendar of Johnny Boy'); -var event = new iCalEvent(); +var event = calendar.addEvent(); event.set('uid', 9873647); event.set('offset', new Date().getTimezoneOffset()); -event.set('method', 'request'); event.set('status', 'confirmed'); event.set('attendees', [ { name: 'Johnny Boy', - email: 'johnny@numberfive.com' + email: 'johnny@numberfivevent.com' }, { name: 'Homer Simpson', @@ -70,14 +96,31 @@ event.set('timezone', 'US/Central'); event.set('summary', 'Priestly Duties.'); event.set('description', 'Home flu visit.'); event.set('location', 'Casa'); -event.set('organizer', { name: 'Nacho Libre', email: 'luchador@monastery.org' }); -event.set('url', 'http://google.com/search?q=nacho+libre'); +event.set('organizer', { name: 'Libre, Nacho', email: 'luchador@monastery.org' }); +event.set('url', 'http://googlevent.com/search?q=nacho+libre'); + +var event2 = calendar.addEvent(); + +event2.set('offset', new Date().getTimezoneOffset()); +event2.set('status', 'confirmed'); +event2.set('attendees', [ + { + name: 'Johnny Boy', + email: 'johnny@numberfivevent.com' + } +]); +event2.set('start', '2014-07-02T10:00:00-05:00'); +event2.set('end', '2014-07-02T11:30:00-05:00'); +event2.set('timezone', 'US/Central'); +event2.set('summary', 'Meet with Jane, bring cake'); +event2.set('location', 'Somwhere'); +event2.set('organizer', {name: 'Doe, Jane', email: 'jane@doevent.com'}); ``` To ics string: ``` js -event.toFile(); +calendar.toFile(); ``` Returns: @@ -86,40 +129,155 @@ Returns: BEGIN:VCALENDAR VERSION:2.0 PRODID:-//iCalEvent.js v0.3//EN +METHOD:REQUEST +NAME:Personal calendar +X-WR-CALNAME:Personal calendar +DESCRIPTION:Personal calendar of Johnny Boy +X-WR-CALDESC:Personal calendar of Johnny Boy BEGIN:VEVENT UID:9873647 -DTSTAMP:20140316T003036 +DTSTAMP:20140729T115736 +STATUS:CONFIRMED +DTSTART;TZID=US/Central:20140701T090000 +DTEND;TZID=US/Central:20140701T093000 +SUMMARY:Priestly Duties +DESCRIPTION:Home flu visit. +ORGANIZER;CN=Libre\, Nacho:mailto:luchador@monastery.org +LOCATION:Casa +URL;VALUE=URI:http://google.com/search?q=nacho+libre +ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=Johnny Boy:MAILTO:johnny@numberfive.com +ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=Homer Simpson:MAILTO:homer@powerplant.com +END:VEVENT +BEGIN:VEVENT +UID:104a0a1f-2b0b-4fcb-9a36-83d868e67a08 +DTSTAMP:20140729T115736 +STATUS:CONFIRMED +DTSTART;TZID=US/Central:20140702T170000 +DTEND;TZID=US/Central:20140702T183000 +SUMMARY:Meet with Jane\, bring cake +ORGANIZER;CN=Doe\, Jane:mailto:jane@doe.com +LOCATION:Somewhere +ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=Johnny Boy:MAILTO:johnny@numberfive.com +END:VEVENT +END:VCALENDAR +``` + +## Calendar-API + +### Properties + +* **method** (String) _The calendar method. For example, publish, request, reply, add, cancel, refresh, counter, and decline-counter_ +* **id** (String) _The identifier of the product, that created this calendar. Defaults to "-//iCalEvent.js v0.3//EN"._ +* **name** (String) _A name, that describes the events in this calendar in short._ +* **description** (String) _A full description of the kind of events in this calendar._ + +### Methods + +#### iCalendar(calendar, events) + +The constructor for a new iCalendar. Initializes the calendar properties with the values provided by the `calendar` hash and adds all events from the `events` array to the calendar. + +#### .addEvent(event) + +Creates a new event in this calendar and initializes it with the values provided by the `event` hash. Returns the newly created iCalEvent object. (see [Event-API](#event-api)) + +``` js +event = calendar.addEvent({ + offset: new Date().getTimezoneOffset(), + status: 'confirmed', + attendees: [ + { + name: 'Johnny Boy', + email: 'johnny@numberfivevent.com' + } + ], + start: '2014-07-02T10:00:00-05:00', + end: '2014-07-02T11:30:00-05:00', + timezone: 'US/Central', + summary: 'Meet with Jane, bring cake', + location: 'Somwhere', + organizer: { + name: 'Doe, Jane', + email: 'jane@doevent.com' + } +}); +``` + +#### .set(property, value) +``` js +calendar.set('method', 'publish'); +``` + +#### .get(property) +``` js +calendar.get('method'); +``` + +Returns: + +``` +publish +``` + +#### .toFile() +``` js +calendar.toFile(); +``` + +Returns: + +``` +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//iCalEvent.js v0.3//EN METHOD:REQUEST +NAME:Personal calendar +X-WR-CALNAME:Personal calendar +DESCRIPTION:Personal calendar of Johnny Boy +X-WR-CALDESC:Personal calendar of Johnny Boy +BEGIN:VEVENT +UID:9873647 +DTSTAMP:20140729T115736 STATUS:CONFIRMED -DTSTART;TZID=US/Central:20140701T020000 -DTEND;TZID=US/Central:20140701T023000 -SUMMARY:Priestly Duties. +DTSTART;TZID=US/Central:20140701T090000 +DTEND;TZID=US/Central:20140701T093000 +SUMMARY:Priestly Duties DESCRIPTION:Home flu visit. -ORGANIZER;CN=Nacho Libre:mailto:luchador@monastery.org +ORGANIZER;CN=Libre\, Nacho:mailto:luchador@monastery.org LOCATION:Casa URL;VALUE=URI:http://google.com/search?q=nacho+libre ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=Johnny Boy:MAILTO:johnny@numberfive.com ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=Homer Simpson:MAILTO:homer@powerplant.com END:VEVENT +BEGIN:VEVENT +UID:104a0a1f-2b0b-4fcb-9a36-83d868e67a08 +DTSTAMP:20140729T115736 +STATUS:CONFIRMED +DTSTART;TZID=US/Central:20140702T170000 +DTEND;TZID=US/Central:20140702T183000 +SUMMARY:Meet with Jane\, bring cake +ORGANIZER;CN=Doe\, Jane:mailto:jane@doe.com +LOCATION:Somewhere +ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=Johnny Boy:MAILTO:johnny@numberfive.com +END:VEVENT END:VCALENDAR ``` -## API +## Event-API ### Properties #### Required -* **offset** (Integer) _The Date.getTimezoneOffset() of the timezone the event is set in. Important: the offset has to be set before start and end times_ +* **offset** (Integer) _The Datevent.getTimezoneOffset() of the timezone the event is set in. Important: the offset has to be set before start and end times_ * **start** (Date Object) _The start date object of the event_ * **end** (Date Object) _The end date object of the event_ #### Optional * **uid** (String or Integer) _The event uid. If you don't set the uid it will be set for you._ -* **method** (String) _The event method. For example, publish, request, reply, add, cancel, refresh, counter, and decline-counter_ * **status** (String) _The event status. For example, cancelled, confirmed, tentative_ -* **timezone** (String) _The event's timezone in [ICS timezone format](https://github.com/shanebo/tzone/blob/master/lib/tzone.js). If you don't set the timezone it will be set for you._ +* **timezone** (String) _The event's timezone in [ICS timezone format](https://github.com/shanebo/tzone/blob/master/lib/tzonevent.js). If you don't set the timezone it will be set for you._ * **location** (String) _The event location of the event. For example, monastery_ * **url** (String) _A url corresponding to the event_ * **summary** (String) _A summary of the event_ @@ -138,7 +296,7 @@ END:VCALENDAR [ { name: 'Johnny Boy', - email: 'johnny@numberfive.com' + email: 'johnny@numberfivevent.com' }, { name: 'Homer Simpson', @@ -151,7 +309,7 @@ END:VCALENDAR #### .set(property, value) ``` js -event.set('url', 'http://google.com/search?q=nacho+libre'); +event.set('url', 'http://googlevent.com/search?q=nacho+libre'); ``` #### .get(property) @@ -162,7 +320,7 @@ event.get('url'); Returns: ``` -http://google.com/search?q=nacho+libre +http://googlevent.com/search?q=nacho+libre ``` #### .toFile() @@ -173,25 +331,20 @@ event.toFile(); Returns: ``` -BEGIN:VCALENDAR -VERSION:2.0 -PRODID:-//iCalEvent.js v0.3//EN BEGIN:VEVENT UID:9873647 -DTSTAMP:20140316T003036 -METHOD:REQUEST +DTSTAMP:20140729T115736 STATUS:CONFIRMED -DTSTART;TZID=US/Central:20140701T020000 -DTEND;TZID=US/Central:20140701T023000 -SUMMARY:Priestly Duties. +DTSTART;TZID=US/Central:20140701T090000 +DTEND;TZID=US/Central:20140701T093000 +SUMMARY:Priestly Duties DESCRIPTION:Home flu visit. -ORGANIZER;CN=Nacho Libre:mailto:luchador@monastery.org +ORGANIZER;CN=Libre\, Nacho:mailto:luchador@monastery.org LOCATION:Casa URL;VALUE=URI:http://google.com/search?q=nacho+libre ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=Johnny Boy:MAILTO:johnny@numberfive.com ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=Homer Simpson:MAILTO:homer@powerplant.com END:VEVENT -END:VCALENDAR ``` diff --git a/example.js b/example.js index 8342fa5..c753010 100644 --- a/example.js +++ b/example.js @@ -3,9 +3,10 @@ var iCalEvent = require('icalevent'); var tzone = require('tzone'); var http = require('http'); - var calendar = new iCalendar({ - method: 'request' + method: 'request', + name: 'Personal calendar', + description: 'Personal calendar of Johnny Boy' }, [{ uid: 9873647, offset: new Date().getTimezoneOffset(), @@ -27,10 +28,28 @@ var calendar = new iCalendar({ description: 'Home flu visit.', location: 'Casa', organizer: { - name: 'Nacho Libre', + name: 'Libre, Nacho', email: 'luchador@monastery.org' }, url: 'http://google.com/search?q=nacho+libre' +}, { + offset: new Date().getTimezoneOffset(), + status: 'confirmed', + attendees: [ + { + name: 'Johnny Boy', + email: 'johnny@numberfive.com' + } + ], + start: '2014-07-02T10:00:00-05:00', + end: '2014-07-02T11:30:00-05:00', + timezone: 'US/Central', + summary: 'Meet with Jane, bring cake', + location: 'Somewhere', + organizer: { + name: 'Doe, Jane', + email: 'jane@doe.com' + } }]); @@ -40,6 +59,8 @@ var calendar = new iCalendar({ var c = new iCalendar(); c.set('method', 'request'); +c.set('name', 'Personal calendar'); +c.set('description', 'Personal calendar of Johnny Boy'); var e = c.addEvent(); @@ -62,9 +83,26 @@ e.set('timezone', 'US/Central'); e.set('summary', 'Priestly Duties.'); e.set('description', 'Home flu visit.'); e.set('location', 'Casa'); -e.set('organizer', { name: 'Nacho Libre', email: 'luchador@monastery.org' }); +e.set('organizer', { name: 'Libre, Nacho', email: 'luchador@monastery.org' }); e.set('url', 'http://google.com/search?q=nacho+libre'); +var e2 = c.addEvent(); + +e2.set('offset', new Date().getTimezoneOffset()); +e2.set('status', 'confirmed'); +e2.set('attendees', [ + { + name: 'Johnny Boy', + email: 'johnny@numberfive.com' + } +]); +e2.set('start', '2014-07-02T10:00:00-05:00'); +e2.set('end', '2014-07-02T11:30:00-05:00'); +e2.set('timezone', 'US/Central'); +e2.set('summary', 'Meet with Jane, bring cake'); +e2.set('location', 'Somewhere'); +e2.set('organizer', {name: 'Doe, Jane', email: 'jane@doe.com'}); + console.log('\n'); console.log(c.toFile()); From dda9110aba4f627175701f08914846c736f0560d Mon Sep 17 00:00:00 2001 From: Christian Dittrich Date: Fri, 22 Aug 2014 15:52:01 +0200 Subject: [PATCH 08/10] more escaping --- icalevent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icalevent.js b/icalevent.js index a414457..c87e19d 100644 --- a/icalevent.js +++ b/icalevent.js @@ -2,7 +2,7 @@ var tzone = require('tzone'); var moment = require('moment'); var escapeText = function(text) { - return text.toString().replace(/([,;\\])/g, "\\$1"); + return text.toString().replace(/([,;\\])/g, "\\$1").replace(/\n/g, "\\n").replace(/\r/g, "\\r"); } var iCalendar = function(calendar, events) { From 3544490402f044e55f35f63e6b00326191243d11 Mon Sep 17 00:00:00 2001 From: Jeff Wear Date: Sun, 7 Feb 2016 21:18:54 -0800 Subject: [PATCH 09/10] Update documentation to note that property is no longer required Since 89e9ac937b34a230289a6c94415951fd8b94f91f, when we started using moment for date parsing. --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index d939ded..235d14a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ var calendar = new iCalendar({ description: 'Personal calendar of Johnny Boy' }, [{ uid: 9873647, - offset: new Date().getTimezoneOffset(), status: 'confirmed', attendees: [ { @@ -44,7 +43,6 @@ var calendar = new iCalendar({ }, url: 'http://googlevent.com/search?q=nacho+libre' }, { - offset: new Date().getTimezoneOffset(), status: 'confirmed', attendees: [ { @@ -78,7 +76,6 @@ calendar.set('description', 'Personal calendar of Johnny Boy'); var event = calendar.addEvent(); event.set('uid', 9873647); -event.set('offset', new Date().getTimezoneOffset()); event.set('status', 'confirmed'); event.set('attendees', [ { @@ -101,7 +98,6 @@ event.set('url', 'http://googlevent.com/search?q=nacho+libre'); var event2 = calendar.addEvent(); -event2.set('offset', new Date().getTimezoneOffset()); event2.set('status', 'confirmed'); event2.set('attendees', [ { @@ -183,7 +179,6 @@ Creates a new event in this calendar and initializes it with the values provided ``` js event = calendar.addEvent({ - offset: new Date().getTimezoneOffset(), status: 'confirmed', attendees: [ { @@ -269,7 +264,6 @@ END:VCALENDAR #### Required -* **offset** (Integer) _The Datevent.getTimezoneOffset() of the timezone the event is set in. Important: the offset has to be set before start and end times_ * **start** (Date Object) _The start date object of the event_ * **end** (Date Object) _The end date object of the event_ From ab19e50591b897ab55dc2c90f91904bb6f5bd876 Mon Sep 17 00:00:00 2001 From: Jeff Wear Date: Sun, 7 Feb 2016 21:27:23 -0800 Subject: [PATCH 10/10] Fix package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a8bbe1b..e107ca9 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "author": "Shane Thacker ", "dependencies": { "tzone": "0.0.2", - "moment: "2.7.0" + "moment": "2.7.0" }, "engine": ">= 0.4.1", "main": "./icalevent"