From 14f48ec44006301aa066b6d5a0c2d4742b2691e8 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Wed, 13 Apr 2016 09:23:54 +0200
Subject: [PATCH 01/38] Prevent clipping of header. Fixes: #167
---
css/main.css | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/css/main.css b/css/main.css
index 1d9a0de7..1795e999 100644
--- a/css/main.css
+++ b/css/main.css
@@ -94,8 +94,8 @@ header {
font-family: "Roboto Condensed";
font-weight: 400;
border-bottom: 1px solid #666;
- line-height: 0.35em;
- padding-bottom: 10px;
+ line-height: 15px;
+ padding-bottom: 5px;
margin-bottom: 10px;
color: #999;
}
From bc8dccd4b5c02a0b63a9bd102d2c6510a64edaba Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Wed, 13 Apr 2016 09:27:30 +0200
Subject: [PATCH 02/38] Fix #153
---
config/config.js.sample | 4 ++--
modules/default/helloworld/README.md | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/config/config.js.sample b/config/config.js.sample
index ef3e9aef..084bf017 100644
--- a/config/config.js.sample
+++ b/config/config.js.sample
@@ -33,7 +33,7 @@ var config = {
},
{
module: 'compliments',
- position: 'lower_third',
+ position: 'lower_third'
},
{
module: 'currentweather',
@@ -57,7 +57,7 @@ var config = {
position: 'bottom_bar',
config: {
feedUrl: 'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml',
- showPublishDate: true,
+ showPublishDate: true
}
},
]
diff --git a/modules/default/helloworld/README.md b/modules/default/helloworld/README.md
index d97726db..72e7d023 100644
--- a/modules/default/helloworld/README.md
+++ b/modules/default/helloworld/README.md
@@ -10,7 +10,7 @@ modules: [
position: 'bottom_bar', // This can be any of the regions.
config: {
// See 'Configuration options' for more information.
- text: 'Hello world!',
+ text: 'Hello world!'
}
}
]
From d6fa1f9cbc3c6c0e71e23c7880dd8324a2ef7c0d Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Wed, 13 Apr 2016 09:58:56 +0200
Subject: [PATCH 03/38] Improve scrollbar hiding.
---
css/main.css | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/css/main.css b/css/main.css
index 1795e999..93d6a7ee 100644
--- a/css/main.css
+++ b/css/main.css
@@ -3,8 +3,11 @@ html {
overflow:hidden;
}
+::-webkit-scrollbar {
+ display: none;
+}
+
body {
- overflow:hidden;
margin: 60px;
position: absolute;
height: calc(100% - 120px);
From 6e60d309708b8603d2f9b5a7680cb7e61bb845a0 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Wed, 13 Apr 2016 15:00:05 +0200
Subject: [PATCH 04/38] Prevent autostart after installation.
---
installers/raspberry.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/installers/raspberry.sh b/installers/raspberry.sh
index 26591329..aa9e6410 100644
--- a/installers/raspberry.sh
+++ b/installers/raspberry.sh
@@ -35,4 +35,4 @@ echo "Cloning MagicMirror ..."
git clone -b v2-beta https://github.com/MichMich/MagicMirror.git || exit
cd ~/MagicMirror || exit
npm install || exit
-echo "We're ready! Run `DISPLAY=:0 npm start` from the MagicMirror directory."
+echo "We're ready! Run [DISPLAY=:0 npm start] from the MagicMirror directory."
From 69a33546172d01f1acaeee4d3c5be358d753bd07 Mon Sep 17 00:00:00 2001
From: Christopher
Date: Fri, 15 Apr 2016 09:53:34 +0200
Subject: [PATCH 05/38] ignore .DS_Store files
ignore Desktop Services Store file for OS X
---
.gitignore | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.gitignore b/.gitignore
index 7deaa919..d4dd3ee1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,6 @@
# Ignore changes to the custom css files.
/css/custom.css
+
+# Ignore Desktop Services Store files (OS X)
+**/.DS_Store
\ No newline at end of file
From 2657e1194c5bb6517cc5f683691703683e07da03 Mon Sep 17 00:00:00 2001
From: Christopher
Date: Fri, 15 Apr 2016 10:02:03 +0200
Subject: [PATCH 06/38] add link to Netatmo module
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index e4dc5238..8b503a9a 100644
--- a/README.md
+++ b/README.md
@@ -99,6 +99,8 @@ The following modules are created by their respective authors.
- **[MMM-SystemTemperature by MichMich](https://github.com/MichMich/mmm-systemtemperature)**
Display your Raspberry Pi's processor temperature on your MagicMirror.
+- **[MMM-Netatmo by CFenner](https://github.com/CFenner/MagicMirror-Netatmo-Module)**
Display your Netatmo in/outdoor weather station data on your MagicMirror.
+
**Note:** If you want to build your own modules, check out the [MagicMirror² Module Development Documentation](modules)
## Known issues
From c03485b93e75028f0130e4c2382afd58e13ea5e1 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Fri, 15 Apr 2016 11:36:36 +0200
Subject: [PATCH 07/38] Add information about the Raspberry Pi support.
---
README.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/README.md b/README.md
index 8b503a9a..e01793f2 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,9 @@ MagicMirror² focuses on a modular plugin system and uses [Electron](http://elec
## Usage
+#### Raspberry Pi Support
+Electron, the app wrapper around MagicMirror², only supports the Raspberry Pi 2 & 3. The Raspberry Pi 1 is currently **not** supported. If you want to run this on a Raspberry Pi 1, use the [server only](#server-only) feature and setup a fullscreen browser yourself.
+
#### Automatic Installer (Raspberry Pi Only!)
Execute the following command on your Raspberry Pi to install MagicMirror²:
From f45a2e7806668e22b016c798cf4b50769190e4be Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Fri, 15 Apr 2016 12:18:59 +0200
Subject: [PATCH 08/38] Fix repeating appointments, Fix: #169
---
modules/default/calendar/calendarfetcher.js | 187 ++++++++++++++++++++
modules/default/calendar/debug.js | 33 ++++
modules/default/calendar/node_helper.js | 173 +-----------------
3 files changed, 221 insertions(+), 172 deletions(-)
create mode 100644 modules/default/calendar/calendarfetcher.js
create mode 100644 modules/default/calendar/debug.js
diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js
new file mode 100644
index 00000000..4007bc9a
--- /dev/null
+++ b/modules/default/calendar/calendarfetcher.js
@@ -0,0 +1,187 @@
+/* Magic Mirror
+ * Node Helper: Calendar - CalendarFetcher
+ *
+ * By Michael Teeuw http://michaelteeuw.nl
+ * MIT Licensed.
+ */
+
+var ical = require("ical");
+var moment = require("moment");
+
+var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumberOfDays) {
+ var self = this;
+
+ var reloadTimer = null;
+ var events = [];
+
+ var fetchFailedCallback = function() {};
+ var eventsReceivedCallback = function() {};
+
+ /* fetchCalendar()
+ * Initiates calendar fetch.
+ */
+ var fetchCalendar = function() {
+
+ clearTimeout(reloadTimer);
+ reloadTimer = null;
+
+ ical.fromURL(url, {}, function(err, data) {
+ if (err) {
+ fetchFailedCallback(self, err);
+ scheduleTimer();
+ return;
+ }
+
+ //console.log(data);
+ newEvents = [];
+
+ var limitFunction = function(date, i) {return i < maximumEntries;};
+
+ for (var e in data) {
+ var event = data[e];
+ var now = new Date();
+ var today = moment().startOf("day").toDate();
+ var future = moment().startOf("day").add(maximumNumberOfDays, "days").toDate();
+
+
+
+ // FIXME:
+ // Ugly fix to solve the facebook birthday issue.
+ // Otherwise, the recurring events only show the birthday for next year.
+ var isFacebookBirthday = false;
+ if (typeof event.uid !== "undefined") {
+ if (event.uid.indexOf("@facebook.com") !== -1) {
+ isFacebookBirthday = true;
+ }
+ }
+
+ if (event.type === "VEVENT") {
+
+ var startDate = (event.start.length === 8) ? moment(event.start, "YYYYMMDD") : moment(new Date(event.start));
+ if (event.start.length === 8) {
+ startDate = startDate.startOf("day");
+ }
+
+ if (typeof event.rrule != "undefined" && !isFacebookBirthday) {
+ var rule = event.rrule;
+ // console.log("Repeating event ...");
+
+ // Check if the timeset is set to this current time.
+ // If so, the RRULE line does not contain any BYHOUR, BYMINUTE, BYSECOND params.
+ // This causes the times of the recurring event to be incorrect.
+ // By adjusting the timeset property, this issue is solved.
+
+
+ if (rule.timeset[0].hour == now.getHours(),
+ rule.timeset[0].minute == now.getMinutes(),
+ rule.timeset[0].second == now.getSeconds()) {
+
+ rule.timeset[0].hour = startDate.format("H");
+ rule.timeset[0].minute = startDate.format("m");
+ rule.timeset[0].second = startDate.format("s");
+ }
+
+ var dates = rule.between(today, future, true, limitFunction);
+
+ for (var d in dates) {
+ startDate = moment(new Date(dates[d]));
+ newEvents.push({
+ title: (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary,
+ startDate: startDate.format("x"),
+ fullDayEvent: (event.start.length === 8)
+
+ });
+ }
+ } else {
+ // console.log("Single event ...");
+ // Single event.
+ if (startDate >= today && startDate <= future) {
+ newEvents.push({
+ title: (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary,
+ startDate: startDate.format("x"),
+ fullDayEvent: (event.start.length === 8)
+ });
+ }
+ }
+ }
+ }
+
+ newEvents.sort(function(a, b) {
+ return a.startDate - b.startDate;
+ });
+
+ events = newEvents.slice(0, maximumEntries);
+
+ self.broadcastEvents();
+ scheduleTimer();
+ });
+ };
+
+ /* scheduleTimer()
+ * Schedule the timer for the next update.
+ */
+ var scheduleTimer = function() {
+ //console.log('Schedule update timer.');
+ clearTimeout(reloadTimer);
+ reloadTimer = setTimeout(function() {
+ fetchCalendar();
+ }, reloadInterval);
+ };
+
+ /* public methods */
+
+ /* startFetch()
+ * Initiate fetchCalendar();
+ */
+ this.startFetch = function() {
+ fetchCalendar();
+ };
+
+ /* broadcastItems()
+ * Broadcast the exsisting events.
+ */
+ this.broadcastEvents = function() {
+ //console.log('Broadcasting ' + events.length + ' events.');
+ eventsReceivedCallback(self);
+ };
+
+ /* onReceive(callback)
+ * Sets the on success callback
+ *
+ * argument callback function - The on success callback.
+ */
+ this.onReceive = function(callback) {
+ eventsReceivedCallback = callback;
+ };
+
+ /* onError(callback)
+ * Sets the on error callback
+ *
+ * argument callback function - The on error callback.
+ */
+ this.onError = function(callback) {
+ fetchFailedCallback = callback;
+ };
+
+ /* url()
+ * Returns the url of this fetcher.
+ *
+ * return string - The url of this fetcher.
+ */
+ this.url = function() {
+ return url;
+ };
+
+ /* events()
+ * Returns current available events for this fetcher.
+ *
+ * return array - The current available events for this fetcher.
+ */
+ this.events = function() {
+ return events;
+ };
+
+};
+
+
+module.exports = CalendarFetcher;
\ No newline at end of file
diff --git a/modules/default/calendar/debug.js b/modules/default/calendar/debug.js
new file mode 100644
index 00000000..22d23066
--- /dev/null
+++ b/modules/default/calendar/debug.js
@@ -0,0 +1,33 @@
+/* CalendarFetcher Tester
+ * use this script with `node debug.js` to test the fetcher without the need
+ * of starting the MagicMirror core. Adjust the values below to your desire.
+ *
+ * By Michael Teeuw http://michaelteeuw.nl
+ * MIT Licensed.
+ */
+
+var CalendarFetcher = require("./calendarfetcher.js");
+
+var url = 'https://github.com/MichMich/MagicMirror/files/217285/reachcalendar.txt';
+var fetchInterval = 60 * 60 * 1000;
+var maximumEntries = 10;
+var maximumNumberOfDays = 365;
+
+console.log('Create fetcher ...');
+
+fetcher = new CalendarFetcher(url, fetchInterval, maximumEntries, maximumNumberOfDays);
+
+fetcher.onReceive(function(fetcher) {
+ console.log(fetcher.events());
+ console.log('------------------------------------------------------------');
+});
+
+fetcher.onError(function(fetcher, error) {
+ console.log("Fetcher error:");
+ console.log(error);
+});
+
+fetcher.startFetch();
+
+console.log('Create fetcher done! ');
+
diff --git a/modules/default/calendar/node_helper.js b/modules/default/calendar/node_helper.js
index 89df6ec5..1d558d29 100644
--- a/modules/default/calendar/node_helper.js
+++ b/modules/default/calendar/node_helper.js
@@ -6,179 +6,8 @@
*/
var NodeHelper = require("node_helper");
-var ical = require("ical");
-var moment = require("moment");
var validUrl = require("valid-url");
-
-var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumberOfDays) {
- var self = this;
-
- var reloadTimer = null;
- var events = [];
-
- var fetchFailedCallback = function() {};
- var eventsReceivedCallback = function() {};
-
- /* fetchCalendar()
- * Initiates calendar fetch.
- */
- var fetchCalendar = function() {
-
- clearTimeout(reloadTimer);
- reloadTimer = null;
-
- ical.fromURL(url, {}, function(err, data) {
- if (err) {
- fetchFailedCallback(self, err);
- scheduleTimer();
- return;
- }
-
- //console.log(data);
- newEvents = [];
-
- var limitFunction = function(date, i) {return i < maximumEntries;};
-
- for (var e in data) {
- var event = data[e];
- var now = new Date();
- var today = moment().startOf("day");
-
- //console.log(event);
-
- // FIXME:
- // Ugly fix to solve the facebook birthday issue.
- // Otherwise, the recurring events only show the birthday for next year.
- var isFacebookBirthday = false;
- if (typeof event.uid !== "undefined") {
- if (event.uid.indexOf("@facebook.com") !== -1) {
- isFacebookBirthday = true;
- }
- }
-
- if (event.type === "VEVENT") {
- var startDate = (event.start.length === 8) ? moment(event.start, "YYYYMMDD") : moment(new Date(event.start));
- if (event.start.length === 8) {
- startDate = startDate.startOf("day");
- }
-
- if (typeof event.rrule != "undefined" && !isFacebookBirthday) {
- var rule = event.rrule;
-
- // Check if the timeset is set to this current time.
- // If so, the RRULE line does not contain any BYHOUR, BYMINUTE, BYSECOND params.
- // This causes the times of the recurring event to be incorrect.
- // By adjusting the timeset property, this issue is solved.
-
- if (rule.timeset[0].hour == now.getHours(),
- rule.timeset[0].minute == now.getMinutes(),
- rule.timeset[0].second == now.getSeconds()) {
-
- rule.timeset[0].hour = startDate.format("H");
- rule.timeset[0].minute = startDate.format("m");
- rule.timeset[0].second = startDate.format("s");
- }
-
- var dates = rule.between(today, today.add(maximumNumberOfDays, "days") , true, limitFunction);
- //console.log(dates);
- for (var d in dates) {
- startDate = moment(new Date(dates[d]));
- newEvents.push({
- title: (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary,
- startDate: startDate.format("x"),
- fullDayEvent: (event.start.length === 8)
-
- });
- }
- } else {
- // Single event.
- if (startDate >= today && startDate <= today.add(maximumNumberOfDays, "days")) {
- newEvents.push({
- title: (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary,
- startDate: startDate.format("x"),
- fullDayEvent: (event.start.length === 8)
- });
- }
- }
- }
- }
-
- newEvents.sort(function(a, b) {
- return a.startDate - b.startDate;
- });
-
- events = newEvents.slice(0, maximumEntries);
-
- self.broadcastEvents();
- scheduleTimer();
- });
- };
-
- /* scheduleTimer()
- * Schedule the timer for the next update.
- */
- var scheduleTimer = function() {
- //console.log('Schedule update timer.');
- clearTimeout(reloadTimer);
- reloadTimer = setTimeout(function() {
- fetchCalendar();
- }, reloadInterval);
- };
-
- /* public methods */
-
- /* startFetch()
- * Initiate fetchCalendar();
- */
- this.startFetch = function() {
- fetchCalendar();
- };
-
- /* broadcastItems()
- * Broadcast the exsisting events.
- */
- this.broadcastEvents = function() {
- //console.log('Broadcasting ' + events.length + ' events.');
- eventsReceivedCallback(self);
- };
-
- /* onReceive(callback)
- * Sets the on success callback
- *
- * argument callback function - The on success callback.
- */
- this.onReceive = function(callback) {
- eventsReceivedCallback = callback;
- };
-
- /* onError(callback)
- * Sets the on error callback
- *
- * argument callback function - The on error callback.
- */
- this.onError = function(callback) {
- fetchFailedCallback = callback;
- };
-
- /* url()
- * Returns the url of this fetcher.
- *
- * return string - The url of this fetcher.
- */
- this.url = function() {
- return url;
- };
-
- /* events()
- * Returns current available events for this fetcher.
- *
- * return array - The current available events for this fetcher.
- */
- this.events = function() {
- return events;
- };
-
-};
+var CalendarFetcher = require("./calendarfetcher.js");
module.exports = NodeHelper.create({
// Override start method.
From 614e5e62af78bfd66af0d5681b7ac3836ba2cb8e Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Fri, 15 Apr 2016 12:50:34 +0200
Subject: [PATCH 09/38] Show relative time to non full day events. Fix: #162
---
modules/default/calendar/calendar.js | 7 ++++-
modules/default/calendar/calendarfetcher.js | 33 +++++++++++++++++----
2 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js
index 253ea708..b2828c22 100644
--- a/modules/default/calendar/calendar.js
+++ b/modules/default/calendar/calendar.js
@@ -115,7 +115,12 @@ Module.register("calendar",{
eventWrapper.appendChild(titleWrapper);
var timeWrapper = document.createElement("td");
- timeWrapper.innerHTML = (event.today) ? this.config.todayText : moment(event.startDate,"x").fromNow();
+ //console.log(event.today);
+ if (event.fullDayEvent) {
+ timeWrapper.innerHTML = (event.today) ? this.config.todayText : moment(event.startDate,"x").fromNow();
+ } else {
+ timeWrapper.innerHTML = moment(event.startDate,"x").fromNow();
+ }
// timeWrapper.innerHTML = moment(event.startDate,'x').format('lll');
timeWrapper.className = "time light";
eventWrapper.appendChild(timeWrapper);
diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js
index 4007bc9a..7e6501e3 100644
--- a/modules/default/calendar/calendarfetcher.js
+++ b/modules/default/calendar/calendarfetcher.js
@@ -95,13 +95,34 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
} else {
// console.log("Single event ...");
// Single event.
- if (startDate >= today && startDate <= future) {
- newEvents.push({
- title: (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary,
- startDate: startDate.format("x"),
- fullDayEvent: (event.start.length === 8)
- });
+ var fullDayEvent = (event.start.length === 8);
+ var title = (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary;
+
+ if (!fullDayEvent && startDate < new Date()) {
+ // it's not a fullday event, and it is in the past. So skip.
+ console.log("It's not a fullday event, and it is in the past. So skip: " + title);
+ continue;
}
+
+ if (fullDayEvent && startDate < today) {
+ // it's a fullday event, and it is before today. So skip.
+ console.log("It's a fullday event, and it is before today. So skip: " + title);
+ continue;
+ }
+
+ if (startDate > future) {
+ // it exceeds the maximumNumberOfDays limit. So skip
+ console.log("It exceeds the maximumNumberOfDays limit. So skip: " + title);
+ continue;
+ }
+
+ // Every thing is good. Add it to the list.
+ newEvents.push({
+ title: title,
+ startDate: startDate.format("x"),
+ fullDayEvent: fullDayEvent
+ });
+
}
}
}
From 27a47d4e4adf8e666d5c004b72a5963022363ce3 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Fri, 15 Apr 2016 13:13:06 +0200
Subject: [PATCH 10/38] Show text when event is still running.
---
modules/default/calendar/README.md | 6 ++++++
modules/default/calendar/calendar.js | 9 +++++++--
modules/default/calendar/calendarfetcher.js | 8 +++++---
modules/default/calendar/debug.js | 2 +-
4 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/modules/default/calendar/README.md b/modules/default/calendar/README.md
index 385b917d..92574415 100644
--- a/modules/default/calendar/README.md
+++ b/modules/default/calendar/README.md
@@ -130,6 +130,12 @@ The following properties can be configured:
Default value: 'Today'
+
+ runningText |
+ Text to display when an event is still running.
+ Default value: 'Ends in'
+ |
+
diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js
index b2828c22..1c2dfb28 100644
--- a/modules/default/calendar/calendar.js
+++ b/modules/default/calendar/calendar.js
@@ -33,7 +33,8 @@ Module.register("calendar",{
emptyCalendarText: "No upcoming events.",
// TODO: It would be nice if there is a way to get this from the Moment.js locale.
- todayText: "Today"
+ todayText: "Today",
+ runningText: "Ends in"
},
// Define required scripts.
@@ -119,7 +120,11 @@ Module.register("calendar",{
if (event.fullDayEvent) {
timeWrapper.innerHTML = (event.today) ? this.config.todayText : moment(event.startDate,"x").fromNow();
} else {
- timeWrapper.innerHTML = moment(event.startDate,"x").fromNow();
+ if (event.startDate >= new Date()) {
+ timeWrapper.innerHTML = moment(event.startDate,"x").fromNow();
+ } else {
+ timeWrapper.innerHTML = this.config.runningText + ' ' + moment(event.endDate,"x").fromNow(true);
+ }
}
// timeWrapper.innerHTML = moment(event.startDate,'x').format('lll');
timeWrapper.className = "time light";
diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js
index 7e6501e3..a3c9b082 100644
--- a/modules/default/calendar/calendarfetcher.js
+++ b/modules/default/calendar/calendarfetcher.js
@@ -58,6 +58,7 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
if (event.type === "VEVENT") {
var startDate = (event.start.length === 8) ? moment(event.start, "YYYYMMDD") : moment(new Date(event.start));
+ var endDate = (event.end.length === 8) ? moment(event.end, "YYYYMMDD") : moment(new Date(event.end));
if (event.start.length === 8) {
startDate = startDate.startOf("day");
}
@@ -88,8 +89,8 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
newEvents.push({
title: (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary,
startDate: startDate.format("x"),
+ endDate: endDate.format("x"),
fullDayEvent: (event.start.length === 8)
-
});
}
} else {
@@ -98,13 +99,13 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
var fullDayEvent = (event.start.length === 8);
var title = (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary;
- if (!fullDayEvent && startDate < new Date()) {
+ if (!fullDayEvent && endDate < new Date()) {
// it's not a fullday event, and it is in the past. So skip.
console.log("It's not a fullday event, and it is in the past. So skip: " + title);
continue;
}
- if (fullDayEvent && startDate < today) {
+ if (fullDayEvent && endDate < today) {
// it's a fullday event, and it is before today. So skip.
console.log("It's a fullday event, and it is before today. So skip: " + title);
continue;
@@ -120,6 +121,7 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
newEvents.push({
title: title,
startDate: startDate.format("x"),
+ endDate: endDate.format("x"),
fullDayEvent: fullDayEvent
});
diff --git a/modules/default/calendar/debug.js b/modules/default/calendar/debug.js
index 22d23066..c709c312 100644
--- a/modules/default/calendar/debug.js
+++ b/modules/default/calendar/debug.js
@@ -8,7 +8,7 @@
var CalendarFetcher = require("./calendarfetcher.js");
-var url = 'https://github.com/MichMich/MagicMirror/files/217285/reachcalendar.txt';
+var url = 'http://p35-calendars.icloud.com/published/2/qsud2R2XlRhT3puEVfrzkeSd3X_QSfCMBmpgTsjVqOD2W_fyy0Y-5QskbvWw2jOQB_cPsmcjpGtcg1a3jBQ_QIS2H0XWSxjqCa8LX0dznpE';
var fetchInterval = 60 * 60 * 1000;
var maximumEntries = 10;
var maximumNumberOfDays = 365;
From e56c1a58538adba5d5a33e88b857d56020a8c4a4 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Fri, 15 Apr 2016 13:16:02 +0200
Subject: [PATCH 11/38] Disable logging.
---
modules/default/calendar/calendarfetcher.js | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js
index a3c9b082..10891134 100644
--- a/modules/default/calendar/calendarfetcher.js
+++ b/modules/default/calendar/calendarfetcher.js
@@ -100,20 +100,17 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
var title = (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary;
if (!fullDayEvent && endDate < new Date()) {
- // it's not a fullday event, and it is in the past. So skip.
- console.log("It's not a fullday event, and it is in the past. So skip: " + title);
+ //console.log("It's not a fullday event, and it is in the past. So skip: " + title);
continue;
}
if (fullDayEvent && endDate < today) {
- // it's a fullday event, and it is before today. So skip.
- console.log("It's a fullday event, and it is before today. So skip: " + title);
+ //console.log("It's a fullday event, and it is before today. So skip: " + title);
continue;
}
if (startDate > future) {
- // it exceeds the maximumNumberOfDays limit. So skip
- console.log("It exceeds the maximumNumberOfDays limit. So skip: " + title);
+ //console.log("It exceeds the maximumNumberOfDays limit. So skip: " + title);
continue;
}
From 33ab2f9cc5e7f5d4d4a2c986446d5faff6d9c714 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Fri, 15 Apr 2016 14:26:06 +0200
Subject: [PATCH 12/38] Move module list to wiki.
---
README.md | 16 ++--------------
1 file changed, 2 insertions(+), 14 deletions(-)
diff --git a/README.md b/README.md
index e01793f2..ba8d5b9f 100644
--- a/README.md
+++ b/README.md
@@ -90,21 +90,9 @@ The following modules are installed by default.
- [**Hello World**](modules/default/helloworld)
- [**Alert**](modules/default/alert)
-The following modules are created by their respective authors.
+For more available modules, check out out the wiki page: [MagicMirror² Modules](https://github.com/MichMich/MagicMirror/wiki/MagicMirror²-Modules).
-- **[MMM-FRITZ-Box-Callmonitor by PaViRo](https://github.com/paviro/MMM-FRITZ-Box-Callmonitor)**
FRITZ!Box Callmonitor (Display an alert when someone is calling ...)
-
-- **[MMM-Facial-Recognition by PaViRo](https://github.com/paviro/MMM-Facial-Recognition)**
Facial recognition and module swapping based on the current user ...
-
-- **[MMM-Wunderlist by PaViRo](https://github.com/paviro/MMM-Wunderlist)**
Displays your Wunderlist todos on your mirror ...
-
-- **[MMM-wordnik by Vendittelli](https://github.com/SVendittelli/MMM-wordnik)**
Get the word of the day, its definition, and origin ...
-
-- **[MMM-SystemTemperature by MichMich](https://github.com/MichMich/mmm-systemtemperature)**
Display your Raspberry Pi's processor temperature on your MagicMirror.
-
-- **[MMM-Netatmo by CFenner](https://github.com/CFenner/MagicMirror-Netatmo-Module)**
Display your Netatmo in/outdoor weather station data on your MagicMirror.
-
-**Note:** If you want to build your own modules, check out the [MagicMirror² Module Development Documentation](modules)
+**Note:** If you want to build your own modules, check out the [MagicMirror² Module Development Documentation](modules).
## Known issues
From a231ebc4085a6fb8a7aae484d0430a8b7f35e40d Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Fri, 15 Apr 2016 14:27:50 +0200
Subject: [PATCH 13/38] Cleanup.
---
README.md | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/README.md b/README.md
index ba8d5b9f..03e55306 100644
--- a/README.md
+++ b/README.md
@@ -90,9 +90,7 @@ The following modules are installed by default.
- [**Hello World**](modules/default/helloworld)
- [**Alert**](modules/default/alert)
-For more available modules, check out out the wiki page: [MagicMirror² Modules](https://github.com/MichMich/MagicMirror/wiki/MagicMirror²-Modules).
-
-**Note:** If you want to build your own modules, check out the [MagicMirror² Module Development Documentation](modules).
+For more available modules, check out out the wiki page: [MagicMirror² Modules](https://github.com/MichMich/MagicMirror/wiki/MagicMirror²-Modules). If you want to build your own modules, check out the [MagicMirror² Module Development Documentation](modules).
## Known issues
From 47a18666b6ccb037d5d9925df64f3299e9c8cbbd Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Fri, 15 Apr 2016 22:09:59 +0200
Subject: [PATCH 14/38] Possible fix for #180
---
modules/default/calendar/calendarfetcher.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js
index 10891134..74e4b2da 100644
--- a/modules/default/calendar/calendarfetcher.js
+++ b/modules/default/calendar/calendarfetcher.js
@@ -58,7 +58,13 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
if (event.type === "VEVENT") {
var startDate = (event.start.length === 8) ? moment(event.start, "YYYYMMDD") : moment(new Date(event.start));
- var endDate = (event.end.length === 8) ? moment(event.end, "YYYYMMDD") : moment(new Date(event.end));
+ var endDate;
+ if (typeof event.end !== "undefined") {
+ endDate = (event.end.length === 8) ? moment(event.end, "YYYYMMDD") : moment(new Date(event.end));
+ } else {
+ endDate = startDate;
+ }
+
if (event.start.length === 8) {
startDate = startDate.startOf("day");
}
From 44a6604fe8519e01b3f93868a4f75247615c2b67 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Sat, 16 Apr 2016 19:55:19 +0200
Subject: [PATCH 15/38] Possible fix for #181
---
modules/default/currentweather/currentweather.js | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/modules/default/currentweather/currentweather.js b/modules/default/currentweather/currentweather.js
index 103a1d78..120b8bf6 100644
--- a/modules/default/currentweather/currentweather.js
+++ b/modules/default/currentweather/currentweather.js
@@ -203,10 +203,14 @@ Module.register("currentweather",{
var sunset = moment(data.sys.sunset * 1000).format("x");
if (sunrise < now && sunset > now) {
- this.sunriseSunsetTime = moment(data.sys.sunset * 1000).format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
+ if (sunrise.isValid()) {
+ this.sunriseSunsetTime = moment(data.sys.sunset * 1000).format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
+ }
this.sunriseSunsetIcon = "wi-sunset";
} else {
- this.sunriseSunsetTime = moment(data.sys.sunrise * 1000).format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
+ if (sunset.isValid()) {
+ this.sunriseSunsetTime = moment(data.sys.sunrise * 1000).format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
+ }
this.sunriseSunsetIcon = "wi-sunrise";
}
From 9c9e93974ea863356543cc2c01c303d94baeaacb Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Sat, 16 Apr 2016 20:01:02 +0200
Subject: [PATCH 16/38] possible fix #181
---
modules/default/currentweather/currentweather.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/modules/default/currentweather/currentweather.js b/modules/default/currentweather/currentweather.js
index 120b8bf6..c033848f 100644
--- a/modules/default/currentweather/currentweather.js
+++ b/modules/default/currentweather/currentweather.js
@@ -198,18 +198,18 @@ Module.register("currentweather",{
this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
this.weatherType = this.config.iconTable[data.weather[0].icon];
- var now = moment().format("x");
- var sunrise = moment(data.sys.sunrise * 1000).format("x");
- var sunset = moment(data.sys.sunset * 1000).format("x");
+ var now = moment();
+ var sunrise = moment(data.sys.sunrise * 1000);
+ var sunset = moment(data.sys.sunset * 1000);
if (sunrise < now && sunset > now) {
if (sunrise.isValid()) {
- this.sunriseSunsetTime = moment(data.sys.sunset * 1000).format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
+ this.sunriseSunsetTime = sunset.format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
}
this.sunriseSunsetIcon = "wi-sunset";
} else {
if (sunset.isValid()) {
- this.sunriseSunsetTime = moment(data.sys.sunrise * 1000).format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
+ this.sunriseSunsetTime = sunrise.format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
}
this.sunriseSunsetIcon = "wi-sunrise";
From 8adb16e1f613e5cedb6ee4e47eeae542fde24c2e Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Sat, 16 Apr 2016 21:42:44 +0200
Subject: [PATCH 17/38] Change formatting. #181
---
modules/default/clock/clock.js | 3 ++-
modules/default/currentweather/currentweather.js | 10 +++-------
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js
index 3acdf53d..3ab7a248 100644
--- a/modules/default/clock/clock.js
+++ b/modules/default/clock/clock.js
@@ -48,8 +48,9 @@ Module.register("clock",{
secondsWrapper.className = "dimmed";
// Set content of wrappers.
+ var format = (this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a";
dateWrapper.innerHTML = moment().format("dddd, LL");
- timeWrapper.innerHTML = moment().format((this.config.timeFormat === 24) ? "HH:mm" : ("hh:mm"));
+ timeWrapper.innerHTML = moment().format(format);
secondsWrapper.innerHTML = moment().format("ss");
// Combine wrappers.
diff --git a/modules/default/currentweather/currentweather.js b/modules/default/currentweather/currentweather.js
index c033848f..887270b8 100644
--- a/modules/default/currentweather/currentweather.js
+++ b/modules/default/currentweather/currentweather.js
@@ -201,18 +201,14 @@ Module.register("currentweather",{
var now = moment();
var sunrise = moment(data.sys.sunrise * 1000);
var sunset = moment(data.sys.sunset * 1000);
+ var format = (this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a";
if (sunrise < now && sunset > now) {
- if (sunrise.isValid()) {
- this.sunriseSunsetTime = sunset.format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
- }
+ this.sunriseSunsetTime = sunset.format(format);
this.sunriseSunsetIcon = "wi-sunset";
} else {
- if (sunset.isValid()) {
- this.sunriseSunsetTime = sunrise.format((this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a");
- }
+ this.sunriseSunsetTime = sunrise.format(format);
this.sunriseSunsetIcon = "wi-sunrise";
-
}
this.loaded = true;
From 6677f2f5d4ba3a14bbf0a78b7fb80d0cfa1e9f5d Mon Sep 17 00:00:00 2001
From: Nicholas Hubbard
Date: Sat, 16 Apr 2016 21:36:46 -0400
Subject: [PATCH 18/38] Standardize the GitIgnore
---
.gitignore | 59 +++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 54 insertions(+), 5 deletions(-)
diff --git a/.gitignore b/.gitignore
index d4dd3ee1..67feae54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,57 @@
-# Ignore all node modules.
-/node_modules
+# Various Node ignoramuses.
+
+logs
+*.log
+npm-debug.log*
+pids
+*.pid
+*.seed
+lib-cov
+coverage
+.grunt
+.lock-wscript
+build/Release
+node_modules
+jspm_modules
+.npm
+.node_repl_history
+
+# Various Windows ignoramuses.
+Thumbs.db
+ehthumbs.db
+Desktop.ini
+$RECYCLE.BIN/
+*.cab
+*.msi
+*.msm
+*.msp
+*.lnk
+
+# Various OSX ignoramuses.
+.DS_Store
+.AppleDouble
+.LSOverride
+Icon
+._*
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# Various Linux ignoramuses.
+
+.fuse_hidden*
+.directory
+.Trash-*
+
+# Various Magic Mirror ignoramuses and anti-ignoramuses.
# Don't ignore the node_helper nore module.
!/modules/node_helper
@@ -12,6 +64,3 @@
# Ignore changes to the custom css files.
/css/custom.css
-
-# Ignore Desktop Services Store files (OS X)
-**/.DS_Store
\ No newline at end of file
From 4639911a04a55ec0b3543162cea123b95d71c6bd Mon Sep 17 00:00:00 2001
From: Nicholas Hubbard
Date: Sat, 16 Apr 2016 21:38:42 -0400
Subject: [PATCH 19/38] Remove ESLint: Part I
---
.github/CONTRIBUTING.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index c9e817d5..ced77501 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -12,13 +12,13 @@ First, before you run the linters, you will need to install them all **and** ins
npm install
```
-### JavaScript: Run JSCS and ESLint
+### JavaScript: Run JSCS
-We use [JSCS](http://jscs.info) and [ESLint](http://eslint.org) on our JavaScript files.
+We use [JSCS](http://jscs.info) on our JavaScript files.
-Our JSCS configuration is in our .jscsrc file, and we use [eslint-config-google](https://www.npmjs.com/package/eslint-config-google) in ESLint.
+Our JSCS configuration is in our .jscsrc file.
-To run ESLint, use `npm run eslint`. To run JSCS, use `npm run jscs`.
+To run JSCS, use `npm run jscs`.
### CSS: Run StyleLint
From 104a62e24d8989c6b38b64db5200dfb12d177ab1 Mon Sep 17 00:00:00 2001
From: Nicholas Hubbard
Date: Sat, 16 Apr 2016 21:40:05 -0400
Subject: [PATCH 20/38] Remove ESLint: Part II
---
package.json | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/package.json b/package.json
index d93b9587..a107f10f 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,7 @@
"description": "A modular interface for smart mirrors.",
"main": "js/electron.js",
"scripts": {
- "start": "electron js/electron.js",
- "eslint": "eslint .",
+ "start": "electron js/electron.js"
"jscs": "jscs **/**/**/**/*.js",
"stylelint": "stylelint css/main.css fonts/roboto.css",
"htmlvalidator": "html-validator --file=index.html",
@@ -31,9 +30,7 @@
"devDependencies": {
"electron-prebuilt": "latest",
"stylelint": "latest",
- "stylelint-config-standard": "latest",
- "eslint": "latest",
- "eslint-config-google": "latest"
+ "stylelint-config-standard": "latest"
},
"dependencies": {
"express": "latest",
@@ -46,8 +43,5 @@
"socket.io": "latest",
"valid-url": "latest",
"walk": "latest"
- },
- "eslintConfig": {
- "extends": "google"
}
}
From fe8cb7c46178e9275ef66b483d3ab17d6904dd15 Mon Sep 17 00:00:00 2001
From: Nicholas Hubbard
Date: Sun, 17 Apr 2016 08:55:48 -0400
Subject: [PATCH 21/38] Add Snyk
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 03e55306..07a4a01c 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@
+
**MagicMirror²** is an open source modular smart mirror platform. With a growing list of installable modules, the **MagicMirror²** allows you to convert your hallway or bathroom mirror into your personal assistant. **MagicMirror²** is built by the creator of [the original MagicMirror](http://michaelteeuw.nl/tagged/magicmirror) with the incredible help of a [growing community of contributors](https://github.com/MichMich/MagicMirror/graphs/contributors).
From bc0ba2f524c2d7636d401e3eb98e0d036492cdf1 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Sun, 17 Apr 2016 17:12:40 +0200
Subject: [PATCH 22/38] fix typo. #190
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index a107f10f..3ce3d15d 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"description": "A modular interface for smart mirrors.",
"main": "js/electron.js",
"scripts": {
- "start": "electron js/electron.js"
+ "start": "electron js/electron.js",
"jscs": "jscs **/**/**/**/*.js",
"stylelint": "stylelint css/main.css fonts/roboto.css",
"htmlvalidator": "html-validator --file=index.html",
From bef5b48fce182c4ed1025eee91ab1ff0c14d44ac Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Mon, 18 Apr 2016 16:24:59 +0200
Subject: [PATCH 23/38] Prevent crash when no internet connection is available.
#193
---
modules/default/newsfeed/fetcher.js | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/modules/default/newsfeed/fetcher.js b/modules/default/newsfeed/fetcher.js
index 32081457..c406aed7 100644
--- a/modules/default/newsfeed/fetcher.js
+++ b/modules/default/newsfeed/fetcher.js
@@ -9,6 +9,17 @@ var FeedMe = require("feedme");
var request = require("request");
var iconv = require("iconv-lite");
+
+// The next part is here to prevent a major exception when there
+// is no internet connection. This could probable be solved better.
+// If this is not here, the request on line 75 crashes when there
+// is no internet connection available.
+process.on('uncaughtException', function (err) {
+ console.log("There was an uncaught exception...");
+ console.error(err);
+ console.log("MagicMirror will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
+});
+
/* Fetcher
* Responsible for requesting an update on the set interval and broadcasting the data.
*
From a3a9a8e3f11507da9fede50ceabbeb87702e59ba Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Mon, 18 Apr 2016 17:16:06 +0200
Subject: [PATCH 24/38] Info about the supported OS.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 07a4a01c..33077642 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ MagicMirror² focuses on a modular plugin system and uses [Electron](http://elec
## Usage
#### Raspberry Pi Support
-Electron, the app wrapper around MagicMirror², only supports the Raspberry Pi 2 & 3. The Raspberry Pi 1 is currently **not** supported. If you want to run this on a Raspberry Pi 1, use the [server only](#server-only) feature and setup a fullscreen browser yourself.
+Electron, the app wrapper around MagicMirror², only supports the Raspberry Pi 2 & 3. The Raspberry Pi 1 is currently **not** supported. If you want to run this on a Raspberry Pi 1, use the [server only](#server-only) feature and setup a fullscreen browser yourself. Note the only Jessie is currently supported. If you want to use Wheezy, check out [this issue](https://github.com/MichMich/MagicMirror/issues/188).
#### Automatic Installer (Raspberry Pi Only!)
From 8793196bb348b0b722cc4e95e8e4f2272ebe6962 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Mon, 18 Apr 2016 19:12:24 +0200
Subject: [PATCH 25/38] Possible fix for #181
---
modules/default/clock/clock.js | 13 ++++++++--
.../default/currentweather/currentweather.js | 26 ++++++++++++-------
2 files changed, 27 insertions(+), 12 deletions(-)
diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js
index 3ab7a248..09b30da5 100644
--- a/modules/default/clock/clock.js
+++ b/modules/default/clock/clock.js
@@ -48,9 +48,18 @@ Module.register("clock",{
secondsWrapper.className = "dimmed";
// Set content of wrappers.
- var format = (this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a";
+ // The moment().format('h') method has a bug on the Raspberry Pi.
+ // So we need to generate the timestring manually.
+ // See issue: https://github.com/MichMich/MagicMirror/issues/181
+ var timeString = moment().format('HH:mm');
+ if (this.config.timeFormat !== 24) {
+ var now = new Date();
+ var hours = now.getHours() % 12 || 12;
+ timeString = hours + moment().format(':mm a');
+ }
+
dateWrapper.innerHTML = moment().format("dddd, LL");
- timeWrapper.innerHTML = moment().format(format);
+ timeWrapper.innerHTML = timeString;
secondsWrapper.innerHTML = moment().format("ss");
// Combine wrappers.
diff --git a/modules/default/currentweather/currentweather.js b/modules/default/currentweather/currentweather.js
index 887270b8..242bbe8e 100644
--- a/modules/default/currentweather/currentweather.js
+++ b/modules/default/currentweather/currentweather.js
@@ -198,19 +198,25 @@ Module.register("currentweather",{
this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
this.weatherType = this.config.iconTable[data.weather[0].icon];
- var now = moment();
- var sunrise = moment(data.sys.sunrise * 1000);
- var sunset = moment(data.sys.sunset * 1000);
- var format = (this.config.timeFormat === 24) ? "HH:mm" : "hh:mm a";
+ var now = new Date();
+ var sunrise = new Date(data.sys.sunrise * 1000);
+ var sunset = new Date(data.sys.sunset * 1000);
- if (sunrise < now && sunset > now) {
- this.sunriseSunsetTime = sunset.format(format);
- this.sunriseSunsetIcon = "wi-sunset";
- } else {
- this.sunriseSunsetTime = sunrise.format(format);
- this.sunriseSunsetIcon = "wi-sunrise";
+ // The moment().format('h') method has a bug on the Raspberry Pi.
+ // So we need to generate the timestring manually.
+ // See issue: https://github.com/MichMich/MagicMirror/issues/181
+ var sunriseSunsetDateObject = (sunrise < now && sunset > now) ? sunset : sunrise;
+ var timeString = moment(sunriseSunsetDateObject).format('HH:mm');
+ if (this.config.timeFormat !== 24) {
+ var hours = sunriseSunsetDateObject.getHours() % 12 || 12;
+ timeString = hours + moment(sunriseSunsetDateObject).format(':mm a');
}
+ this.sunriseSunsetTime = timeString;
+ this.sunriseSunsetIcon = (sunrise < now && sunset > now) ? "wi-sunset" : "wi-sunrise";
+
+
+
this.loaded = true;
this.updateDom(this.config.animationSpeed);
},
From 3892ce3ed4bda69a6dc2d189d17e3aca0c175d23 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Mon, 18 Apr 2016 20:03:12 +0200
Subject: [PATCH 26/38] Fix for #188, #194
---
js/main.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/js/main.js b/js/main.js
index ed89826e..7a585425 100644
--- a/js/main.js
+++ b/js/main.js
@@ -433,4 +433,8 @@ var MM = (function() {
})();
+// Add polyfill for Object.assign.
+if (typeof Object.assign != 'function') { (function () { Object.assign = function (target) { 'use strict'; if (target === undefined || target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var output = Object(target); for (var index = 1; index < arguments.length; index++) { var source = arguments[index]; if (source !== undefined && source !== null) { for (var nextKey in source) { if (source.hasOwnProperty(nextKey)) { output[nextKey] = source[nextKey]; } } } } return output; }; })(); }
+
+
MM.init();
From 16a7ea2b75892ea1e55d498dc9e43fe3eae7bb25 Mon Sep 17 00:00:00 2001
From: Cody
Date: Mon, 18 Apr 2016 19:52:40 -0400
Subject: [PATCH 27/38] Allow 12 hour period config
Allow for configuring the 12 hour format period (AM/PM).
---
modules/default/clock/clock.js | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js
index 09b30da5..cdae1db3 100644
--- a/modules/default/clock/clock.js
+++ b/modules/default/clock/clock.js
@@ -13,6 +13,8 @@ Module.register("clock",{
defaults: {
timeFormat: config.timeFormat,
displaySeconds: true,
+ showPeriod: true,
+ showPeriodUpper: false,
},
// Define required scripts.
@@ -55,7 +57,15 @@ Module.register("clock",{
if (this.config.timeFormat !== 24) {
var now = new Date();
var hours = now.getHours() % 12 || 12;
- timeString = hours + moment().format(':mm a');
+ if (this.config.showPeriod) {
+ if (this.config.showPeriodUpper) {
+ timeString = hours + moment().format(':mm A');
+ } else {
+ timeString = hours + moment().format(':mm a');
+ }
+ } else {
+ timeString = hours + moment().format(':mm');
+ }
}
dateWrapper.innerHTML = moment().format("dddd, LL");
From b4df63f37f5682739f36a3f48e64e13eea857cda Mon Sep 17 00:00:00 2001
From: Cody
Date: Mon, 18 Apr 2016 19:53:34 -0400
Subject: [PATCH 28/38] Allow 12 hour period config
Allow for configuring the 12 hour format period (AM/PM).
---
modules/default/currentweather/currentweather.js | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/modules/default/currentweather/currentweather.js b/modules/default/currentweather/currentweather.js
index 242bbe8e..3a7520ce 100644
--- a/modules/default/currentweather/currentweather.js
+++ b/modules/default/currentweather/currentweather.js
@@ -17,6 +17,8 @@ Module.register("currentweather",{
updateInterval: 10 * 60 * 1000, // every 10 minutes
animationSpeed: 1000,
timeFormat: config.timeFormat,
+ showPeriod: true,
+ showPeriodUpper: false,
lang: config.language,
initialLoadDelay: 0, // 0 seconds delay
@@ -209,7 +211,15 @@ Module.register("currentweather",{
var timeString = moment(sunriseSunsetDateObject).format('HH:mm');
if (this.config.timeFormat !== 24) {
var hours = sunriseSunsetDateObject.getHours() % 12 || 12;
- timeString = hours + moment(sunriseSunsetDateObject).format(':mm a');
+ if (this.config.showPeriod) {
+ if (this.config.showPeriodUpper) {
+ timeString = hours + moment(sunriseSunsetDateObject).format(':mm A');
+ } else {
+ timeString = hours + moment(sunriseSunsetDateObject).format(':mm a');
+ }
+ } else {
+ timeString = hours + moment(sunriseSunsetDateObject).format(':mm');
+ }
}
this.sunriseSunsetTime = timeString;
From d0e57df1d88acfa6a8ebe62be16df87e11d5f2c1 Mon Sep 17 00:00:00 2001
From: Cody
Date: Mon, 18 Apr 2016 20:01:53 -0400
Subject: [PATCH 29/38] Add documentation for 12 hour period config
Add the documentation for the 12 hour period config
---
modules/default/clock/README.md | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/modules/default/clock/README.md b/modules/default/clock/README.md
index ab65d3f4..522b1ad2 100644
--- a/modules/default/clock/README.md
+++ b/modules/default/clock/README.md
@@ -45,5 +45,19 @@ The following properties can be configured:
Default value: true
+
+ showPeriod |
+ Show the period (am/pm) with 12 hour format
+ Possible values: true or false
+ Default value: true
+ |
+
+
+ showPeriodUpper |
+ Show the period (AM/PM) with 12 hour format as uppercase
+ Possible values: true or false
+ Default value: false
+ |
+
From 54eeb27f0952e14a62dcf427907a45492cfa1e38 Mon Sep 17 00:00:00 2001
From: Cody
Date: Mon, 18 Apr 2016 20:03:26 -0400
Subject: [PATCH 30/38] Add documentation for 12 hour period config
Add the documentation for the 12 hour period config
---
modules/default/currentweather/README.md | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/modules/default/currentweather/README.md b/modules/default/currentweather/README.md
index 4a418ad9..3c818eee 100644
--- a/modules/default/currentweather/README.md
+++ b/modules/default/currentweather/README.md
@@ -76,6 +76,20 @@ The following properties can be configured:
Default value: uses value of config.timeFormat
+
+ showPeriod |
+ Show the period (am/pm) with 12 hour format
+ Possible values: true or false
+ Default value: true
+ |
+
+
+ showPeriodUpper |
+ Show the period (AM/PM) with 12 hour format as uppercase
+ Possible values: true or false
+ Default value: false
+ |
+
lang |
The language of the days.
@@ -140,6 +154,5 @@ The following properties can be configured:
}
|
-
From 095f08d44322ac5bec5baa6ca47e016be1956941 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Tue, 19 Apr 2016 10:34:14 +0200
Subject: [PATCH 31/38] Fix: #162, Possible fix for: #197
---
modules/default/calendar/calendar.js | 8 +++++-
modules/default/calendar/calendarfetcher.js | 28 +++++++++++++++++++--
2 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js
index 1c2dfb28..0b5627ff 100644
--- a/modules/default/calendar/calendar.js
+++ b/modules/default/calendar/calendar.js
@@ -121,7 +121,13 @@ Module.register("calendar",{
timeWrapper.innerHTML = (event.today) ? this.config.todayText : moment(event.startDate,"x").fromNow();
} else {
if (event.startDate >= new Date()) {
- timeWrapper.innerHTML = moment(event.startDate,"x").fromNow();
+ var now = new Date();
+ if (event.startDate - now > 48 * 60 * 60 * 1000) {
+ // if the event is no longer than 2 days away, display the absolute time.
+ timeWrapper.innerHTML = moment(event.startDate,"x").fromNow();
+ } else {
+ timeWrapper.innerHTML = moment(event.startDate,"x").calendar();
+ }
} else {
timeWrapper.innerHTML = this.config.runningText + ' ' + moment(event.endDate,"x").fromNow(true);
}
diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js
index 74e4b2da..4d4a0bcd 100644
--- a/modules/default/calendar/calendarfetcher.js
+++ b/modules/default/calendar/calendarfetcher.js
@@ -96,13 +96,13 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
title: (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary,
startDate: startDate.format("x"),
endDate: endDate.format("x"),
- fullDayEvent: (event.start.length === 8)
+ fullDayEvent: isFullDayEvent(event)
});
}
} else {
// console.log("Single event ...");
// Single event.
- var fullDayEvent = (event.start.length === 8);
+ var fullDayEvent = isFullDayEvent(event);
var title = (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary;
if (!fullDayEvent && endDate < new Date()) {
@@ -154,6 +154,30 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
}, reloadInterval);
};
+ /* isFullDayEvent(event)
+ * Checks if an event is a fullday event.
+ *
+ * argument event obejct - The event object to check.
+ *
+ * return bool - The event is a fullday event.
+ */
+ var isFullDayEvent = function(event) {
+ if (event.start.length === 8) {
+ return true;
+ }
+
+ var start = event.start || 0;
+ var startDate = new Date(start);
+ var end = event.end || 0;
+
+ if (end - start === 24 * 60 * 60 * 1000 && startDate.getHours() === 0 && startDate.getMinutes() === 0) {
+ // Is 24 hours, and starts on the middle of the night.
+ return true;
+ }
+
+ return false;
+ };
+
/* public methods */
/* startFetch()
From a3fedb47cbff3cdcebaf627cdb6a194a1dc2b14b Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Tue, 19 Apr 2016 11:04:36 +0200
Subject: [PATCH 32/38] Fix socket reference bug. #196
---
js/socketclient.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/js/socketclient.js b/js/socketclient.js
index f7d1843a..8ea468a8 100644
--- a/js/socketclient.js
+++ b/js/socketclient.js
@@ -8,11 +8,11 @@ var MMSocket = function(moduleName) {
self.moduleName = moduleName;
// Private Methods
- socket = io.connect("/" + self.moduleName);
+ self.socket = io("/" + self.moduleName);
var notificationCallback = function() {};
- var onevent = socket.onevent;
- socket.onevent = function(packet) {
+ var onevent = self.socket.onevent;
+ self.socket.onevent = function(packet) {
var args = packet.data || [];
onevent.call(this, packet); // original call
packet.data = ["*"].concat(args);
@@ -20,7 +20,7 @@ var MMSocket = function(moduleName) {
};
// register catch all.
- socket.on("*", function(notification, payload) {
+ self.socket.on("*", function(notification, payload) {
if (notification !== "*") {
//console.log('Received notification: ' + notification +', payload: ' + payload);
notificationCallback(notification, payload);
@@ -36,6 +36,6 @@ var MMSocket = function(moduleName) {
if (typeof payload === "undefined") {
payload = {};
}
- socket.emit(notification, payload);
+ self.socket.emit(notification, payload);
};
};
From 7d8cde2f0fbe955555e9a0238b4e9d2a338b6291 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Tue, 19 Apr 2016 11:25:50 +0200
Subject: [PATCH 33/38] Move exception handling to the core app.
---
js/app.js | 9 +++++++++
modules/default/newsfeed/fetcher.js | 11 -----------
2 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/js/app.js b/js/app.js
index 8257bcd0..012d33ad 100644
--- a/js/app.js
+++ b/js/app.js
@@ -10,6 +10,15 @@ var Server = require(__dirname + "/server.js");
var defaultModules = require(__dirname + "/../modules/default/defaultmodules.js");
var path = require("path");
+// The next part is here to prevent a major exception when there
+// is no internet connection. This could probable be solved better.
+process.on('uncaughtException', function (err) {
+ console.log("Whoops! There was an uncaught exception...");
+ console.error(err);
+ console.log("MagicMirror will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
+ console.log("If you think this really is an issue, please open an issue on GitHub: https://github.com/MichMich/MagicMirror/issues");
+});
+
/* App - The core app.
*/
var App = function() {
diff --git a/modules/default/newsfeed/fetcher.js b/modules/default/newsfeed/fetcher.js
index c406aed7..32081457 100644
--- a/modules/default/newsfeed/fetcher.js
+++ b/modules/default/newsfeed/fetcher.js
@@ -9,17 +9,6 @@ var FeedMe = require("feedme");
var request = require("request");
var iconv = require("iconv-lite");
-
-// The next part is here to prevent a major exception when there
-// is no internet connection. This could probable be solved better.
-// If this is not here, the request on line 75 crashes when there
-// is no internet connection available.
-process.on('uncaughtException', function (err) {
- console.log("There was an uncaught exception...");
- console.error(err);
- console.log("MagicMirror will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
-});
-
/* Fetcher
* Responsible for requesting an update on the set interval and broadcasting the data.
*
From b16a49109fd1cd1c15dbbf0d8d2cf399c26747e2 Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Tue, 19 Apr 2016 16:45:57 +0200
Subject: [PATCH 34/38] Possible fix for #185
---
modules/default/calendar/calendarfetcher.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js
index 4d4a0bcd..fc9a4d1c 100644
--- a/modules/default/calendar/calendarfetcher.js
+++ b/modules/default/calendar/calendarfetcher.js
@@ -41,7 +41,7 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
var event = data[e];
var now = new Date();
var today = moment().startOf("day").toDate();
- var future = moment().startOf("day").add(maximumNumberOfDays, "days").toDate();
+ var future = moment().startOf("day").add(maximumNumberOfDays, "days").subtract(1,"seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat.
@@ -88,6 +88,7 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
rule.timeset[0].second = startDate.format("s");
}
+ rule.options.dtstart = startDate.toDate();
var dates = rule.between(today, future, true, limitFunction);
for (var d in dates) {
From 6fdcd7f7e91827d63c72eab963c8d97a271d6aeb Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Tue, 19 Apr 2016 20:26:22 +0200
Subject: [PATCH 35/38] Add darkTheme.
---
js/electron.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/electron.js b/js/electron.js
index 5de41003..ce3efa88 100755
--- a/js/electron.js
+++ b/js/electron.js
@@ -17,7 +17,7 @@ let mainWindow;
function createWindow() {
// Create the browser window.
- mainWindow = new BrowserWindow({width: 800, height: 600, fullscreen: true, autoHideMenuBar: true, webPreferences: {nodeIntegration: false}});
+ mainWindow = new BrowserWindow({width: 800, height: 600, fullscreen: true, autoHideMenuBar: true, darkTheme: true, webPreferences: {nodeIntegration: false}});
// and load the index.html of the app.
//mainWindow.loadURL('file://' + __dirname + '../../index.html');
From 287452169b69e43b3cd429db970dd1de8f1020bb Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Wed, 20 Apr 2016 11:32:48 +0200
Subject: [PATCH 36/38] Fix for #197 and #185
---
modules/default/calendar/calendarfetcher.js | 22 +-
modules/default/calendar/debug.js | 2 +-
.../calendar/vendor/ical.js/.travis.yml | 6 +
.../default/calendar/vendor/ical.js/LICENSE | 178 +++
.../default/calendar/vendor/ical.js/NOTICE | 13 +
.../calendar/vendor/ical.js/example.js | 13 +
.../default/calendar/vendor/ical.js/ical.js | 309 +++++
.../default/calendar/vendor/ical.js/index.js | 8 +
.../calendar/vendor/ical.js/node-ical.js | 37 +
.../calendar/vendor/ical.js/package.json | 28 +
.../default/calendar/vendor/ical.js/readme.md | 51 +
.../calendar/vendor/ical.js/test/test.js | 393 ++++++
.../calendar/vendor/ical.js/test/test1.ics | 78 ++
.../calendar/vendor/ical.js/test/test10.ics | 34 +
.../calendar/vendor/ical.js/test/test11.ics | 41 +
.../calendar/vendor/ical.js/test/test2.ics | 83 ++
.../calendar/vendor/ical.js/test/test3.ics | 226 ++++
.../calendar/vendor/ical.js/test/test4.ics | 747 +++++++++++
.../calendar/vendor/ical.js/test/test5.ics | 41 +
.../calendar/vendor/ical.js/test/test6.ics | 1170 +++++++++++++++++
.../calendar/vendor/ical.js/test/test7.ics | 16 +
.../calendar/vendor/ical.js/test/test8.ics | 23 +
.../calendar/vendor/ical.js/test/test9.ics | 21 +
package.json | 1 -
24 files changed, 3519 insertions(+), 22 deletions(-)
create mode 100644 modules/default/calendar/vendor/ical.js/.travis.yml
create mode 100644 modules/default/calendar/vendor/ical.js/LICENSE
create mode 100644 modules/default/calendar/vendor/ical.js/NOTICE
create mode 100644 modules/default/calendar/vendor/ical.js/example.js
create mode 100755 modules/default/calendar/vendor/ical.js/ical.js
create mode 100644 modules/default/calendar/vendor/ical.js/index.js
create mode 100644 modules/default/calendar/vendor/ical.js/node-ical.js
create mode 100644 modules/default/calendar/vendor/ical.js/package.json
create mode 100644 modules/default/calendar/vendor/ical.js/readme.md
create mode 100755 modules/default/calendar/vendor/ical.js/test/test.js
create mode 100644 modules/default/calendar/vendor/ical.js/test/test1.ics
create mode 100644 modules/default/calendar/vendor/ical.js/test/test10.ics
create mode 100644 modules/default/calendar/vendor/ical.js/test/test11.ics
create mode 100644 modules/default/calendar/vendor/ical.js/test/test2.ics
create mode 100644 modules/default/calendar/vendor/ical.js/test/test3.ics
create mode 100644 modules/default/calendar/vendor/ical.js/test/test4.ics
create mode 100644 modules/default/calendar/vendor/ical.js/test/test5.ics
create mode 100644 modules/default/calendar/vendor/ical.js/test/test6.ics
create mode 100755 modules/default/calendar/vendor/ical.js/test/test7.ics
create mode 100644 modules/default/calendar/vendor/ical.js/test/test8.ics
create mode 100644 modules/default/calendar/vendor/ical.js/test/test9.ics
diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js
index fc9a4d1c..96f5588e 100644
--- a/modules/default/calendar/calendarfetcher.js
+++ b/modules/default/calendar/calendarfetcher.js
@@ -5,7 +5,7 @@
* MIT Licensed.
*/
-var ical = require("ical");
+var ical = require("./vendor/ical.js");
var moment = require("moment");
var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumberOfDays) {
@@ -71,26 +71,8 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
if (typeof event.rrule != "undefined" && !isFacebookBirthday) {
var rule = event.rrule;
- // console.log("Repeating event ...");
-
- // Check if the timeset is set to this current time.
- // If so, the RRULE line does not contain any BYHOUR, BYMINUTE, BYSECOND params.
- // This causes the times of the recurring event to be incorrect.
- // By adjusting the timeset property, this issue is solved.
-
-
- if (rule.timeset[0].hour == now.getHours(),
- rule.timeset[0].minute == now.getMinutes(),
- rule.timeset[0].second == now.getSeconds()) {
-
- rule.timeset[0].hour = startDate.format("H");
- rule.timeset[0].minute = startDate.format("m");
- rule.timeset[0].second = startDate.format("s");
- }
-
- rule.options.dtstart = startDate.toDate();
var dates = rule.between(today, future, true, limitFunction);
-
+
for (var d in dates) {
startDate = moment(new Date(dates[d]));
newEvents.push({
diff --git a/modules/default/calendar/debug.js b/modules/default/calendar/debug.js
index c709c312..bc0adae6 100644
--- a/modules/default/calendar/debug.js
+++ b/modules/default/calendar/debug.js
@@ -8,7 +8,7 @@
var CalendarFetcher = require("./calendarfetcher.js");
-var url = 'http://p35-calendars.icloud.com/published/2/qsud2R2XlRhT3puEVfrzkeSd3X_QSfCMBmpgTsjVqOD2W_fyy0Y-5QskbvWw2jOQB_cPsmcjpGtcg1a3jBQ_QIS2H0XWSxjqCa8LX0dznpE';
+var url = 'https://calendar.google.com/calendar/ical/pkm1t2uedjbp0uvq1o7oj1jouo%40group.calendar.google.com/private-08ba559f89eec70dd74bbd887d0a3598/basic.ics';
var fetchInterval = 60 * 60 * 1000;
var maximumEntries = 10;
var maximumNumberOfDays = 365;
diff --git a/modules/default/calendar/vendor/ical.js/.travis.yml b/modules/default/calendar/vendor/ical.js/.travis.yml
new file mode 100644
index 00000000..0a4c7980
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/.travis.yml
@@ -0,0 +1,6 @@
+language: node_js
+node_js:
+ - "0.10"
+ - "0.12"
+ - "4.2"
+install: npm install
diff --git a/modules/default/calendar/vendor/ical.js/LICENSE b/modules/default/calendar/vendor/ical.js/LICENSE
new file mode 100644
index 00000000..e454a525
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/LICENSE
@@ -0,0 +1,178 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/modules/default/calendar/vendor/ical.js/NOTICE b/modules/default/calendar/vendor/ical.js/NOTICE
new file mode 100644
index 00000000..d5f926db
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/NOTICE
@@ -0,0 +1,13 @@
+Copyright 2012 Peter Braden
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/modules/default/calendar/vendor/ical.js/example.js b/modules/default/calendar/vendor/ical.js/example.js
new file mode 100644
index 00000000..aceb0e7d
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/example.js
@@ -0,0 +1,13 @@
+var ical = require('ical')
+ , months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+
+ical.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics', {}, function(err, data){
+ for (var k in data){
+ if (data.hasOwnProperty(k)){
+ var ev = data[k]
+ console.log("Conference", ev.summary, 'is in', ev.location, 'on the', ev.start.getDate(), 'of', months[ev.start.getMonth()] );
+ }
+ }
+})
+
diff --git a/modules/default/calendar/vendor/ical.js/ical.js b/modules/default/calendar/vendor/ical.js/ical.js
new file mode 100755
index 00000000..252e426b
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/ical.js
@@ -0,0 +1,309 @@
+(function(name, definition) {
+
+/****************
+ * A tolerant, minimal icalendar parser
+ * (http://tools.ietf.org/html/rfc5545)
+ *
+ *
+ * **************/
+
+ if (typeof module !== 'undefined') {
+ module.exports = definition();
+ } else if (typeof define === 'function' && typeof define.amd === 'object'){
+ define(definition);
+ } else {
+ this[name] = definition();
+ }
+
+}('ical', function(){
+
+ // Unescape Text re RFC 4.3.11
+ var text = function(t){
+ t = t || "";
+ return (t
+ .replace(/\\\,/g, ',')
+ .replace(/\\\;/g, ';')
+ .replace(/\\[nN]/g, '\n')
+ .replace(/\\\\/g, '\\')
+ )
+ }
+
+ var parseParams = function(p){
+ var out = {}
+ for (var i = 0; i -1){
+ var segs = p[i].split('=');
+
+ out[segs[0]] = parseValue(segs.slice(1).join('='));
+
+ }
+ }
+ return out || sp
+ }
+
+ var parseValue = function(val){
+ if ('TRUE' === val)
+ return true;
+
+ if ('FALSE' === val)
+ return false;
+
+ var number = Number(val);
+ if (!isNaN(number))
+ return number;
+
+ return val;
+ }
+
+ var storeParam = function(name){
+ return function(val, params, curr){
+ var data;
+ if (params && params.length && !(params.length==1 && params[0]==='CHARSET=utf-8')){
+ data = {params:parseParams(params), val:text(val)}
+ }
+ else
+ data = text(val)
+
+ var current = curr[name];
+ if (Array.isArray(current)){
+ current.push(data);
+ return curr;
+ }
+
+ if (current != null){
+ curr[name] = [current, data];
+ return curr;
+ }
+
+ curr[name] = data;
+ return curr
+ }
+ }
+
+ var addTZ = function(dt, name, params){
+ var p = parseParams(params);
+
+ if (params && p){
+ dt[name].tz = p.TZID
+ }
+
+ return dt
+ }
+
+
+ var dateParam = function(name){
+ return function(val, params, curr){
+
+ // Store as string - worst case scenario
+ storeParam(name)(val, undefined, curr)
+
+ if (params && params[0] === "VALUE=DATE") {
+ // Just Date
+
+ var comps = /^(\d{4})(\d{2})(\d{2})$/.exec(val);
+ if (comps !== null) {
+ // No TZ info - assume same timezone as this computer
+ curr[name] = new Date(
+ comps[1],
+ parseInt(comps[2], 10)-1,
+ comps[3]
+ );
+
+ return addTZ(curr, name, params);
+ }
+ }
+
+
+ //typical RFC date-time format
+ var comps = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?$/.exec(val);
+ if (comps !== null) {
+ if (comps[7] == 'Z'){ // GMT
+ curr[name] = new Date(Date.UTC(
+ parseInt(comps[1], 10),
+ parseInt(comps[2], 10)-1,
+ parseInt(comps[3], 10),
+ parseInt(comps[4], 10),
+ parseInt(comps[5], 10),
+ parseInt(comps[6], 10 )
+ ));
+ // TODO add tz
+ } else {
+ curr[name] = new Date(
+ parseInt(comps[1], 10),
+ parseInt(comps[2], 10)-1,
+ parseInt(comps[3], 10),
+ parseInt(comps[4], 10),
+ parseInt(comps[5], 10),
+ parseInt(comps[6], 10)
+ );
+ }
+ }
+
+ return addTZ(curr, name, params)
+ }
+ }
+
+
+ var geoParam = function(name){
+ return function(val, params, curr){
+ storeParam(val, params, curr)
+ var parts = val.split(';');
+ curr[name] = {lat:Number(parts[0]), lon:Number(parts[1])};
+ return curr
+ }
+ }
+
+ var categoriesParam = function (name) {
+ var separatorPattern = /\s*,\s*/g;
+ return function (val, params, curr) {
+ storeParam(val, params, curr)
+ if (curr[name] === undefined)
+ curr[name] = val ? val.split(separatorPattern) : []
+ else
+ if (val)
+ curr[name] = curr[name].concat(val.split(separatorPattern))
+ return curr
+ }
+ }
+
+ var addFBType = function(fb, params){
+ var p = parseParams(params);
+
+ if (params && p){
+ fb.type = p.FBTYPE || "BUSY"
+ }
+
+ return fb;
+ }
+
+ var freebusyParam = function (name) {
+ return function(val, params, curr){
+ var fb = addFBType({}, params);
+ curr[name] = curr[name] || []
+ curr[name].push(fb);
+
+ storeParam(val, params, fb);
+
+ var parts = val.split('/');
+
+ ['start', 'end'].forEach(function (name, index) {
+ dateParam(name)(parts[index], params, fb);
+ });
+
+ return curr;
+ }
+ }
+
+ return {
+
+
+ objectHandlers : {
+ 'BEGIN' : function(component, params, curr, stack){
+ stack.push(curr)
+
+ return {type:component, params:params}
+ }
+
+ , 'END' : function(component, params, curr, stack){
+ // prevents the need to search the root of the tree for the VCALENDAR object
+ if (component === "VCALENDAR") {
+ //scan all high level object in curr and drop all strings
+ var key,
+ obj;
+
+ for (key in curr) {
+ if(curr.hasOwnProperty(key)) {
+ obj = curr[key];
+ if (typeof obj === 'string') {
+ delete curr[key];
+ }
+ }
+ }
+
+ return curr
+ }
+
+ var par = stack.pop()
+
+ if (curr.uid)
+ par[curr.uid] = curr
+ else
+ par[Math.random()*100000] = curr // Randomly assign ID : TODO - use true GUID
+
+ return par
+ }
+
+ , 'SUMMARY' : storeParam('summary')
+ , 'DESCRIPTION' : storeParam('description')
+ , 'URL' : storeParam('url')
+ , 'UID' : storeParam('uid')
+ , 'LOCATION' : storeParam('location')
+ , 'DTSTART' : dateParam('start')
+ , 'DTEND' : dateParam('end')
+ ,' CLASS' : storeParam('class')
+ , 'TRANSP' : storeParam('transparency')
+ , 'GEO' : geoParam('geo')
+ , 'PERCENT-COMPLETE': storeParam('completion')
+ , 'COMPLETED': dateParam('completed')
+ , 'CATEGORIES': categoriesParam('categories')
+ , 'FREEBUSY': freebusyParam('freebusy')
+ },
+
+
+ handleObject : function(name, val, params, ctx, stack, line){
+ var self = this
+
+ if(self.objectHandlers[name])
+ return self.objectHandlers[name](val, params, ctx, stack, line)
+
+ //handling custom properties
+ if(name.match(/X\-[\w\-]+/) && stack.length > 0) {
+ //trimming the leading and perform storeParam
+ name = name.substring(2);
+ return (storeParam(name))(val, params, ctx, stack, line);
+ }
+
+ return storeParam(name.toLowerCase())(val, params, ctx);
+ },
+
+
+ parseICS : function(str){
+ var self = this
+ var lines = str.split(/\r?\n/)
+ var ctx = {}
+ var stack = []
+
+ for (var i = 0, ii = lines.length, l = lines[0]; i (peterbraden.co.uk)",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/peterbraden/ical.js.git"
+ },
+ "dependencies": {
+ "request": "2.68.0",
+ "rrule": "2.0.0"
+ },
+ "devDependencies": {
+ "vows": "0.7.0",
+ "underscore": "1.3.0"
+ },
+ "scripts": {
+ "test": "./node_modules/vows/bin/vows ./test/test.js"
+ }
+}
diff --git a/modules/default/calendar/vendor/ical.js/readme.md b/modules/default/calendar/vendor/ical.js/readme.md
new file mode 100644
index 00000000..2daba074
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/readme.md
@@ -0,0 +1,51 @@
+# ical.js #
+(Formerly node-ical)
+
+[](https://travis-ci.org/peterbraden/ical.js)
+
+A tolerant, minimal icalendar parser for javascript/node
+(http://tools.ietf.org/html/rfc5545)
+
+
+## Install - Node.js ##
+
+ical.js is availble on npm:
+
+ npm install ical
+
+
+
+## API ##
+
+ ical.parseICS(str)
+
+Parses a string with an ICS File
+
+ var data = ical.parseFile(filename)
+
+Reads in the specified iCal file, parses it and returns the parsed data
+
+ ical.fromURL(url, options, function(err, data) {} )
+
+Use the request library to fetch the specified URL (```opts``` gets passed on to the ```request()``` call), and call the function with the result (either an error or the data).
+
+
+
+## Example 1 - Print list of upcoming node conferences (see example.js)
+```javascript
+ var ical = require('ical')
+ , months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+ ical.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics', {}, function(err, data) {
+ for (var k in data){
+ if (data.hasOwnProperty(k)) {
+ var ev = data[k]
+ console.log("Conference",
+ ev.summary,
+ 'is in',
+ ev.location,
+ 'on the', ev.start.getDate(), 'of', months[ev.start.getMonth()]);
+ }
+ }
+ });
+```
diff --git a/modules/default/calendar/vendor/ical.js/test/test.js b/modules/default/calendar/vendor/ical.js/test/test.js
new file mode 100755
index 00000000..2aeff1ea
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test.js
@@ -0,0 +1,393 @@
+/****
+ * Tests
+ *
+ *
+ ***/
+process.env.TZ = 'America/San_Francisco';
+var ical = require('../index')
+
+var vows = require('vows')
+ , assert = require('assert')
+ , _ = require('underscore')
+
+vows.describe('node-ical').addBatch({
+ 'when parsing test1.ics (node conferences schedule from lanyrd.com, modified)': {
+ topic: function () {
+ return ical.parseFile('./test/test1.ics')
+ }
+
+ ,'we get 9 events': function (topic) {
+ var events = _.select(_.values(topic), function(x){ return x.type==='VEVENT'})
+ assert.equal (events.length, 9);
+ }
+
+ ,'event 47f6e' : {
+ topic: function(events){
+ return _.select(_.values(events),
+ function(x){
+ return x.uid ==='47f6ea3f28af2986a2192fa39a91fa7d60d26b76'})[0]
+ }
+ ,'is in fort lauderdale' : function(topic){
+ assert.equal(topic.location, "Fort Lauderdale, United States")
+ }
+ ,'starts Tue, 29 Nov 2011' : function(topic){
+ assert.equal(topic.start.toDateString(), new Date(2011,10,29).toDateString())
+ }
+ }
+ , 'event 480a' : {
+ topic: function(events){
+ return _.select(_.values(events),
+ function(x){
+ return x.uid ==='480a3ad48af5ed8965241f14920f90524f533c18'})[0]
+ }
+ , 'has a summary (invalid colon handling tolerance)' : function(topic){
+ assert.equal(topic.summary, '[Async]: Everything Express')
+ }
+ }
+ , 'event d4c8' :{
+ topic : function(events){
+ return _.select(_.values(events),
+ function(x){
+ return x.uid === 'd4c826dfb701f611416d69b4df81caf9ff80b03a'})[0]
+ }
+ , 'has a start datetime' : function(topic){
+ assert.equal(topic.start.toDateString(), new Date(Date.UTC(2011, 2, 12, 20, 0, 0)).toDateString())
+ }
+ }
+
+ , 'event sdfkf09fsd0 (Invalid Date)' :{
+ topic : function(events){
+ return _.select(_.values(events),
+ function(x){
+ return x.uid === 'sdfkf09fsd0'})[0]
+ }
+ , 'has a start datetime' : function(topic){
+ assert.equal(topic.start, "Next Year")
+ }
+ }
+ }
+ , 'with test2.ics (testing ical features)' : {
+ topic: function () {
+ return ical.parseFile('./test/test2.ics')
+ }
+ , 'todo item uid4@host1.com' : {
+ topic : function(items){
+ return items['uid4@host1.com']
+ }
+ , 'is a VTODO' : function(topic){
+ assert.equal(topic.type, 'VTODO')
+ }
+ }
+ , 'vfreebusy' : {
+ topic: function(events) {
+ return _.select(_.values(events), function(x) {
+ return x.type === 'VFREEBUSY';
+ })[0];
+ }
+ , 'has a URL' : function(topic) {
+ assert.equal(topic.url, 'http://www.host.com/calendar/busytime/jsmith.ifb');
+ }
+ }
+ , 'vfreebusy first freebusy' : {
+ topic: function(events) {
+ return _.select(_.values(events), function(x) {
+ return x.type === 'VFREEBUSY';
+ })[0].freebusy[0];
+ }
+ , 'has undefined type defaulting to busy' : function(topic) {
+ assert.equal(topic.type, "BUSY");
+ }
+ , 'has an start datetime' : function(topic) {
+ assert.equal(topic.start.getFullYear(), 1998);
+ assert.equal(topic.start.getUTCMonth(), 2);
+ assert.equal(topic.start.getUTCDate(), 14);
+ assert.equal(topic.start.getUTCHours(), 23);
+ assert.equal(topic.start.getUTCMinutes(), 30);
+ }
+ , 'has an end datetime' : function(topic) {
+ assert.equal(topic.end.getFullYear(), 1998);
+ assert.equal(topic.end.getUTCMonth(), 2);
+ assert.equal(topic.end.getUTCDate(), 15);
+ assert.equal(topic.end.getUTCHours(), 00);
+ assert.equal(topic.end.getUTCMinutes(), 30);
+ }
+ }
+ }
+ , 'with test3.ics (testing tvcountdown.com)' : {
+ topic: function() {
+ return ical.parseFile('./test/test3.ics');
+ }
+ , 'event -83' : {
+ topic: function(events) {
+ return _.select(_.values(events), function(x) {
+ return x.uid === '20110505T220000Z-83@tvcountdown.com';
+ })[0];
+ }
+ , 'has a start datetime' : function(topic) {
+ assert.equal(topic.start.getFullYear(), 2011);
+ assert.equal(topic.start.getMonth(), 4);
+ }
+ , 'has an end datetime' : function(topic) {
+ assert.equal(topic.end.getFullYear(), 2011);
+ assert.equal(topic.end.getMonth(), 4);
+ }
+ }
+ }
+
+ , 'with test4.ics (testing tripit.com)' : {
+ topic: function() {
+ return ical.parseFile('./test/test4.ics');
+ }
+ , 'event c32a5...' : {
+ topic: function(events) {
+ return _.select(_.values(events), function(x) {
+ return x.uid === 'c32a5eaba2354bb29e012ec18da827db90550a3b@tripit.com';
+ })[0];
+ }
+ , 'has a start datetime' : function(topic) {
+ assert.equal(topic.start.getFullYear(), 2011);
+ assert.equal(topic.start.getMonth(), 09);
+ assert.equal(topic.start.getDate(), 11);
+ }
+
+ , 'has a summary' : function(topic){
+ // escaped commas and semicolons should be replaced
+ assert.equal(topic.summary, 'South San Francisco, CA, October 2011;')
+
+ }
+
+ , 'has a description' : function(topic){
+ var desired = 'John Doe is in South San Francisco, CA from Oct 11 ' +
+ 'to Oct 13, 2011\nView and/or edit details in TripIt : http://www.tripit.c' +
+ 'om/trip/show/id/23710889\nTripIt - organize your travel at http://www.trip' +
+ 'it.com\n'
+ assert.equal(topic.description, desired)
+
+ }
+
+ , 'has a geolocation' : function(topic){
+ assert.ok(topic.geo, 'no geo param')
+ assert.equal(topic.geo.lat, 37.654656)
+ assert.equal(topic.geo.lon, -122.40775)
+ }
+
+ , 'has transparency' : function(topic){
+ assert.equal(topic.transparency, 'TRANSPARENT')
+ }
+
+ }
+ }
+
+
+
+ , 'with test5.ics (testing meetup.com)' : {
+ topic: function () {
+ return ical.parseFile('./test/test5.ics')
+ }
+ , 'event nsmxnyppbfc@meetup.com' : {
+ topic: function(events) {
+ return _.select(_.values(events), function(x) {
+ return x.uid === 'event_nsmxnyppbfc@meetup.com';
+ })[0];
+ }
+ , 'has a start' : function(topic){
+ assert.equal(topic.start.tz, 'America/Phoenix')
+ assert.equal(topic.start.toISOString(), new Date(2011, 10, 09, 19, 0,0).toISOString())
+ }
+ }
+ }
+
+ , 'with test6.ics (testing assembly.org)' : {
+ topic: function () {
+ return ical.parseFile('./test/test6.ics')
+ }
+ , 'event with no ID' : {
+ topic: function(events) {
+ return _.select(_.values(events), function(x) {
+ return x.summary === 'foobar Summer 2011 starts!';
+ })[0];
+ }
+ , 'has a start' : function(topic){
+ assert.equal(topic.start.toISOString(), new Date(2011, 07, 04, 12, 0,0).toISOString())
+ }
+ }
+ , 'event with rrule' :{
+ topic: function(events){
+ return _.select(_.values(events), function(x){
+ return x.summary == "foobarTV broadcast starts"
+ })[0];
+ }
+ , "Has an RRULE": function(topic){
+ assert.notEqual(topic.rrule, undefined);
+ }
+ , "RRule text": function(topic){
+ assert.equal(topic.rrule.toText(), "every 5 weeks on Monday, Friday until January 30, 2013")
+ }
+ }
+ }
+ , 'with test7.ics (testing dtstart of rrule)' :{
+ topic: function() {
+ return ical.parseFile('./test/test7.ics');
+ },
+ 'recurring yearly event (14 july)': {
+ topic: function(events){
+ var ev = _.values(events)[0];
+ return ev.rrule.between(new Date(2013, 0, 1), new Date(2014, 0, 1));
+ },
+ 'dt start well set': function(topic) {
+ assert.equal(topic[0].toDateString(), new Date(2013, 6, 14).toDateString());
+ }
+ }
+ }
+ , "with test 8.ics (VTODO completion)": {
+ topic: function() {
+ return ical.parseFile('./test/test8.ics');
+ },
+ 'grabbing VTODO task': {
+ topic: function(topic) {
+ return _.values(topic)[0];
+ },
+ 'task completed': function(task){
+ assert.equal(task.completion, 100);
+ assert.equal(task.completed.toISOString(), new Date(2013, 06, 16, 10, 57, 45).toISOString());
+ }
+ }
+ }
+ , "with test 9.ics (VEVENT with VALARM)": {
+ topic: function() {
+ return ical.parseFile('./test/test9.ics');
+ },
+ 'grabbing VEVENT task': {
+ topic: function(topic) {
+ return _.values(topic)[0];
+ },
+ 'task completed': function(task){
+ assert.equal(task.summary, "Event with an alarm");
+ }
+ }
+ }
+ , 'with test 11.ics (VEVENT with custom properties)': {
+ topic: function() {
+ return ical.parseFile('./test10.ics');
+ },
+ 'grabbing custom properties': {
+ topic: function(topic) {
+
+ }
+ }
+ },
+
+ 'with test10.ics': {
+ topic: function () {
+ return ical.parseFile('./test/test10.ics');
+ },
+
+ 'when categories present': {
+ topic: function (t) {return _.values(t)[0]},
+
+ 'should be a list': function (e) {
+ assert(e.categories instanceof [].constructor);
+ },
+
+ 'should contain individual category values': function (e) {
+ assert.deepEqual(e.categories, ['cat1', 'cat2', 'cat3']);
+ }
+ },
+
+ 'when categories present with trailing whitespace': {
+ topic: function (t) {return _.values(t)[1]},
+
+ 'should contain individual category values without whitespace': function (e) {
+ assert.deepEqual(e.categories, ['cat1', 'cat2', 'cat3']);
+ }
+ },
+
+ 'when categories present but empty': {
+ topic: function (t) {return _.values(t)[2]},
+
+ 'should be an empty list': function (e) {
+ assert.deepEqual(e.categories, []);
+ }
+ },
+
+ 'when categories present but singular': {
+ topic: function (t) {return _.values(t)[3]},
+
+ 'should be a list of single item': function (e) {
+ assert.deepEqual(e.categories, ['lonely-cat']);
+ }
+ },
+
+ 'when categories present on multiple lines': {
+ topic: function (t) {return _.values(t)[4]},
+
+ 'should contain the category values in an array': function (e) {
+ assert.deepEqual(e.categories, ['cat1', 'cat2', 'cat3']);
+ }
+ }
+ },
+
+ 'with test11.ics (testing zimbra freebusy)': {
+ topic: function () {
+ return ical.parseFile('./test/test11.ics');
+ },
+
+ 'freebusy params' : {
+ topic: function(events) {
+ return _.values(events)[0];
+ }
+ , 'has a URL' : function(topic) {
+ assert.equal(topic.url, 'http://mail.example.com/yvr-2a@example.com/20140416');
+ }
+ , 'has an ORGANIZER' : function(topic) {
+ assert.equal(topic.organizer, 'mailto:yvr-2a@example.com');
+ }
+ , 'has an start datetime' : function(topic) {
+ assert.equal(topic.start.getFullYear(), 2014);
+ assert.equal(topic.start.getMonth(), 3);
+ }
+ , 'has an end datetime' : function(topic) {
+ assert.equal(topic.end.getFullYear(), 2014);
+ assert.equal(topic.end.getMonth(), 6);
+ }
+ }
+ , 'freebusy busy events' : {
+ topic: function(events) {
+ return _.select(_.values(events)[0].freebusy, function(x) {
+ return x.type === 'BUSY';
+ })[0];
+ }
+ , 'has an start datetime' : function(topic) {
+ assert.equal(topic.start.getFullYear(), 2014);
+ assert.equal(topic.start.getMonth(), 3);
+ assert.equal(topic.start.getUTCHours(), 15);
+ assert.equal(topic.start.getUTCMinutes(), 15);
+ }
+ , 'has an end datetime' : function(topic) {
+ assert.equal(topic.end.getFullYear(), 2014);
+ assert.equal(topic.end.getMonth(), 3);
+ assert.equal(topic.end.getUTCHours(), 19);
+ assert.equal(topic.end.getUTCMinutes(), 00);
+ }
+ }
+ },
+
+ 'url request errors' : {
+ topic : function () {
+ ical.fromURL('http://not.exist/', {}, this.callback);
+ }
+ , 'are passed back to the callback' : function (err, result) {
+ assert.instanceOf(err, Error);
+ if (!err){
+ console.log(">E:", err, result)
+ }
+ }
+ }
+}).export(module)
+
+
+//ical.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics',
+// {},
+// function(err, data){
+// console.log("OUT:", data)
+// })
diff --git a/modules/default/calendar/vendor/ical.js/test/test1.ics b/modules/default/calendar/vendor/ical.js/test/test1.ics
new file mode 100644
index 00000000..ff8cc076
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test1.ics
@@ -0,0 +1,78 @@
+BEGIN:VCALENDAR
+PRODID:-//lanyrd.com//Lanyrd//EN
+X-ORIGINAL-URL:http://lanyrd.com/topics/nodejs/nodejs.ics
+X-WR-CALNAME;CHARSET=utf-8:Node.js conferences
+VERSION:2.0
+METHOD:PUBLISH
+BEGIN:VEVENT
+SUMMARY;CHARSET=utf-8:Dyncon 2011
+LOCATION;CHARSET=utf-8:Stockholm, Sweden
+URL:http://lanyrd.com/2011/dyncon/
+UID:d4c826dfb701f611416d69b4df81caf9ff80b03a
+DTSTART:20110312T200000Z
+DTEND;VALUE=DATE:20110314
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY;CHARSET=utf-8:[Async]: Everything Express
+LOCATION;CHARSET=utf-8:Brighton, United Kingdom
+URL:http://lanyrd.com/2011/asyncjs-express/
+UID:480a3ad48af5ed8965241f14920f90524f533c18
+DTSTART;VALUE=DATE:20110324
+DTEND;VALUE=DATE:20110325
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY;CHARSET=utf-8:JSConf US 2011
+LOCATION;CHARSET=utf-8:Portland, United States
+URL:http://lanyrd.com/2011/jsconf/
+UID:ed334cc85db5ebdff5ff5a630a7a48631a677dbe
+DTSTART;VALUE=DATE:20110502
+DTEND;VALUE=DATE:20110504
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY;CHARSET=utf-8:NodeConf 2011
+LOCATION;CHARSET=utf-8:Portland, United States
+URL:http://lanyrd.com/2011/nodeconf/
+UID:25169a7b1ba5c248278f47120a40878055dc8c15
+DTSTART;VALUE=DATE:20110505
+DTEND;VALUE=DATE:20110506
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY;CHARSET=utf-8:BrazilJS
+LOCATION;CHARSET=utf-8:Fortaleza, Brazil
+URL:http://lanyrd.com/2011/braziljs/
+UID:dafee3be83624f3388c5635662229ff11766bb9c
+DTSTART;VALUE=DATE:20110513
+DTEND;VALUE=DATE:20110515
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY;CHARSET=utf-8:Falsy Values
+LOCATION;CHARSET=utf-8:Warsaw, Poland
+URL:http://lanyrd.com/2011/falsy-values/
+UID:73cad6a09ac4e7310979c6130f871d17d990b5ad
+DTSTART;VALUE=DATE:20110518
+DTEND;VALUE=DATE:20110521
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY;CHARSET=utf-8:nodecamp.eu
+LOCATION;CHARSET=utf-8:Cologne, Germany
+URL:http://lanyrd.com/2011/nodecampde/
+UID:b728a5fdb5f292b6293e4a2fd97a1ccfc69e9d6f
+DTSTART;VALUE=DATE:20110611
+DTEND;VALUE=DATE:20110613
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY;CHARSET=utf-8:Rich Web Experience 2011
+LOCATION;CHARSET=utf-8:Fort Lauderdale, United States
+URL:http://lanyrd.com/2011/rich-web-experience/
+UID:47f6ea3f28af2986a2192fa39a91fa7d60d26b76
+DTSTART;VALUE=DATE:20111129
+DTEND;VALUE=DATE:20111203
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY;CHARSET=utf-8:Foobar
+UID:sdfkf09fsd0
+DTSTART;VALUE=DATE:Next Year
+DTEND;VALUE=DATE:20111203
+END:VEVENT
+
+END:VCALENDAR
diff --git a/modules/default/calendar/vendor/ical.js/test/test10.ics b/modules/default/calendar/vendor/ical.js/test/test10.ics
new file mode 100644
index 00000000..40763b3b
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test10.ics
@@ -0,0 +1,34 @@
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:1
+SUMMARY:Event with a category
+DESCRIPTION:Details for an event with a category
+CATEGORIES:cat1,cat2,cat3
+END:VEVENT
+BEGIN:VEVENT
+UID:2
+SUMMARY:Event with a category
+DESCRIPTION:Details for an event with a category
+CATEGORIES:cat1 , cat2, cat3
+END:VEVENT
+BEGIN:VEVENT
+UID:3
+SUMMARY:Event with a category
+DESCRIPTION:Details for an event with a category
+CATEGORIES:
+END:VEVENT
+BEGIN:VEVENT
+UID:4
+SUMMARY:Event with a category
+DESCRIPTION:Details for an event with a category
+CATEGORIES:lonely-cat
+END:VEVENT
+BEGIN:VEVENT
+UID:5
+SUMMARY:Event with a category
+DESCRIPTION:Details for an event with a category
+CATEGORIES:cat1
+CATEGORIES:cat2
+CATEGORIES:cat3
+END:VEVENT
+END:VCALENDAR
diff --git a/modules/default/calendar/vendor/ical.js/test/test11.ics b/modules/default/calendar/vendor/ical.js/test/test11.ics
new file mode 100644
index 00000000..3a7e80de
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test11.ics
@@ -0,0 +1,41 @@
+BEGIN:VCALENDAR
+PRODID:Zimbra-Calendar-Provider
+VERSION:2.0
+METHOD:PUBLISH
+BEGIN:VFREEBUSY
+ORGANIZER:mailto:yvr-2a@example.com
+DTSTAMP:20140516T235436Z
+DTSTART:20140415T235436Z
+DTEND:20140717T235436Z
+URL:http://mail.example.com/yvr-2a@example.com/20140416
+FREEBUSY;FBTYPE=BUSY:20140416T151500Z/20140416T190000Z
+FREEBUSY;FBTYPE=BUSY:20140416T195500Z/20140416T231500Z
+FREEBUSY;FBTYPE=BUSY:20140417T193000Z/20140417T203000Z
+FREEBUSY;FBTYPE=BUSY:20140421T210000Z/20140421T213000Z
+FREEBUSY;FBTYPE=BUSY:20140423T180000Z/20140423T190000Z
+FREEBUSY;FBTYPE=BUSY:20140423T200000Z/20140423T210000Z
+FREEBUSY;FBTYPE=BUSY:20140423T223500Z/20140423T231500Z
+FREEBUSY;FBTYPE=BUSY:20140424T155000Z/20140424T165500Z
+FREEBUSY;FBTYPE=BUSY:20140424T170000Z/20140424T183000Z
+FREEBUSY;FBTYPE=BUSY:20140424T195000Z/20140424T230000Z
+FREEBUSY;FBTYPE=BUSY:20140425T144500Z/20140425T161500Z
+FREEBUSY;FBTYPE=BUSY:20140425T180000Z/20140425T194500Z
+FREEBUSY;FBTYPE=BUSY:20140425T223000Z/20140425T230000Z
+FREEBUSY;FBTYPE=BUSY:20140428T151500Z/20140428T163000Z
+FREEBUSY;FBTYPE=BUSY:20140428T170000Z/20140428T173000Z
+FREEBUSY;FBTYPE=BUSY:20140428T195500Z/20140428T213000Z
+FREEBUSY;FBTYPE=BUSY:20140428T231000Z/20140428T234000Z
+FREEBUSY;FBTYPE=BUSY:20140429T152500Z/20140429T170000Z
+FREEBUSY;FBTYPE=BUSY:20140429T180000Z/20140429T183000Z
+FREEBUSY;FBTYPE=BUSY:20140429T201500Z/20140429T230000Z
+FREEBUSY;FBTYPE=BUSY:20140430T162500Z/20140430T165500Z
+FREEBUSY;FBTYPE=BUSY:20140430T180000Z/20140430T190000Z
+FREEBUSY;FBTYPE=BUSY:20140501T170000Z/20140501T173000Z
+FREEBUSY;FBTYPE=BUSY:20140501T175000Z/20140501T190000Z
+FREEBUSY;FBTYPE=BUSY:20140501T232000Z/20140501T235000Z
+FREEBUSY;FBTYPE=BUSY:20140502T163500Z/20140502T173000Z
+FREEBUSY;FBTYPE=BUSY:20140505T165500Z/20140505T173000Z
+FREEBUSY;FBTYPE=BUSY:20140505T201500Z/20140505T203000Z
+FREEBUSY;FBTYPE=BUSY:20140505T210000Z/20140505T213000Z
+END:VFREEBUSY
+END:VCALENDAR
diff --git a/modules/default/calendar/vendor/ical.js/test/test2.ics b/modules/default/calendar/vendor/ical.js/test/test2.ics
new file mode 100644
index 00000000..29baf8cd
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test2.ics
@@ -0,0 +1,83 @@
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+X-WR-TIMEZONE;VALUE=TEXT:US/Pacific
+METHOD:PUBLISH
+PRODID:-//Apple Computer\, Inc//iCal 1.0//EN
+X-WR-CALNAME;VALUE=TEXT:Example
+VERSION:2.0
+BEGIN:VEVENT
+SEQUENCE:5
+DTSTART;TZID=US/Pacific:20021028T140000
+DTSTAMP:20021028T011706Z
+SUMMARY:Coffee with Jason
+UID:EC9439B1-FF65-11D6-9973-003065F99D04
+DTEND;TZID=US/Pacific:20021028T150000
+END:VEVENT
+BEGIN:VALARM
+TRIGGER;VALUE=DURATION:-P1D
+ACTION:DISPLAY
+DESCRIPTION:Event reminder
+END:VALARM
+BEGIN:VEVENT
+SEQUENCE:1
+DTSTAMP:20021128T012034Z
+SUMMARY:Code Review
+UID:EC944331-FF65-11D6-9973-003065F99D04
+DTSTART;TZID=US/Pacific:20021127T120000
+DURATION:PT1H
+END:VEVENT
+BEGIN:VEVENT
+SEQUENCE:1
+DTSTAMP:20021028T012034Z
+SUMMARY:Dinner with T
+UID:EC944CFA-FF65-11D6-9973-003065F99D04
+DTSTART;TZID=US/Pacific:20021216T200000
+DURATION:PT1H
+END:VEVENT
+BEGIN:VTODO
+DTSTAMP:19980130T134500Z
+SEQUENCE:2
+UID:uid4@host1.com
+ORGANIZER:MAILTO:unclesam@us.gov
+ATTENDEE;PARTSTAT=ACCEPTED:MAILTO:jqpublic@host.com
+DUE:19980415T235959
+STATUS:NEEDS-ACTION
+SUMMARY:Submit Income Taxes
+END:VTODO
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER:19980403T120000
+ATTACH;FMTTYPE=audio/basic:http://host.com/pub/audio-
+ files/ssbanner.aud
+REPEAT:4
+DURATION:PT1H
+END:VALARM
+BEGIN:VJOURNAL
+DTSTAMP:19970324T120000Z
+UID:uid5@host1.com
+ORGANIZER:MAILTO:jsmith@host.com
+STATUS:DRAFT
+CLASS:PUBLIC
+CATEGORY:Project Report, XYZ, Weekly Meeting
+DESCRIPTION:Project xyz Review Meeting Minutes\n
+ Agenda\n1. Review of project version 1.0 requirements.\n2.
+ Definition
+ of project processes.\n3. Review of project schedule.\n
+ Participants: John Smith, Jane Doe, Jim Dandy\n-It was
+ decided that the requirements need to be signed off by
+ product marketing.\n-Project processes were accepted.\n
+ -Project schedule needs to account for scheduled holidays
+ and employee vacation time. Check with HR for specific
+ dates.\n-New schedule will be distributed by Friday.\n-
+ Next weeks meeting is cancelled. No meeting until 3/23.
+END:VJOURNAL
+BEGIN:VFREEBUSY
+ORGANIZER:MAILTO:jsmith@host.com
+DTSTART:19980313T141711Z
+DTEND:19980410T141711Z
+FREEBUSY:19980314T233000Z/19980315T003000Z
+FREEBUSY:19980316T153000Z/19980316T163000Z
+FREEBUSY:19980318T030000Z/19980318T040000Z
+URL:http://www.host.com/calendar/busytime/jsmith.ifb
+END:VFREEBUSY
+END:VCALENDAR
diff --git a/modules/default/calendar/vendor/ical.js/test/test3.ics b/modules/default/calendar/vendor/ical.js/test/test3.ics
new file mode 100644
index 00000000..ee7111e7
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test3.ics
@@ -0,0 +1,226 @@
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:tvcountdown.com
+X-WR-CALNAME:tvcountdown.com
+VERSION:2.0
+METHOD:PUBLISH
+X-WR-TIMEZONE:US/Eastern
+X-WR-CALNAME;VALUE=TEXT:tvcountdown.com
+X-WR-CALDESC:
+BEGIN:VEVENT
+UID:20110519T200000Z-79@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110519T200000
+DTEND;VALUE=DATE-TIME:20110519T203000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Big Bang Theory - S04E24 - The Roomate Transmogrfication
+END:VEVENT
+BEGIN:VEVENT
+UID:20110512T200000Z-79@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110512T200000
+DTEND;VALUE=DATE-TIME:20110512T203000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Big Bang Theory - S04E23 - The Engagement Reaction
+END:VEVENT
+BEGIN:VEVENT
+UID:20110505T220000Z-83@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110505T220000
+DTEND;VALUE=DATE-TIME:20110505T223000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:30 Rock - S05E23 - Respawn
+END:VEVENT
+BEGIN:VEVENT
+UID:20110505T200000Z-79@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110505T200000
+DTEND;VALUE=DATE-TIME:20110505T203000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Big Bang Theory - S04E22 - The Wildebeest Implementation
+END:VEVENT
+BEGIN:VEVENT
+UID:20110504T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110504T230000
+DTEND;VALUE=DATE-TIME:20110504T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E59 - David Barton
+END:VEVENT
+BEGIN:VEVENT
+UID:20110503T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110503T230000
+DTEND;VALUE=DATE-TIME:20110503T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E58 - Rachel Maddow
+END:VEVENT
+BEGIN:VEVENT
+UID:20110502T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110502T230000
+DTEND;VALUE=DATE-TIME:20110502T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E57 - Philip K. Howard
+END:VEVENT
+BEGIN:VEVENT
+UID:20110428T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110428T230000
+DTEND;VALUE=DATE-TIME:20110428T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E56 - William Cohan
+END:VEVENT
+BEGIN:VEVENT
+UID:20110428T220000Z-83@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110428T220000
+DTEND;VALUE=DATE-TIME:20110428T223000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:30 Rock - S05E22 - Everything Sunny All the Time Always
+END:VEVENT
+BEGIN:VEVENT
+UID:20110428T200000Z-79@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110428T200000
+DTEND;VALUE=DATE-TIME:20110428T203000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Big Bang Theory - S04E21 - The Agreement Dissection
+END:VEVENT
+BEGIN:VEVENT
+UID:20110427T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110427T230000
+DTEND;VALUE=DATE-TIME:20110427T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E55 - Sen. Bernie Sanders
+END:VEVENT
+BEGIN:VEVENT
+UID:20110426T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110426T230000
+DTEND;VALUE=DATE-TIME:20110426T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E54 - Elizabeth Warren
+END:VEVENT
+BEGIN:VEVENT
+UID:20110425T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110425T230000
+DTEND;VALUE=DATE-TIME:20110425T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E53 - Gigi Ibrahim
+END:VEVENT
+BEGIN:VEVENT
+UID:20110421T220000Z-83@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110421T220000
+DTEND;VALUE=DATE-TIME:20110421T223000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:30 Rock - S05E21 - 100th Episode Part 2 of 2
+END:VEVENT
+BEGIN:VEVENT
+UID:20110421T220000Z-83@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110421T220000
+DTEND;VALUE=DATE-TIME:20110421T223000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:30 Rock - S05E20 - 100th Episode Part 1 of 2
+END:VEVENT
+BEGIN:VEVENT
+UID:20110414T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110414T230000
+DTEND;VALUE=DATE-TIME:20110414T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E52 - Ricky Gervais
+END:VEVENT
+BEGIN:VEVENT
+UID:20110414T220000Z-83@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110414T220000
+DTEND;VALUE=DATE-TIME:20110414T223000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:30 Rock - S05E19 - I Heart Connecticut
+END:VEVENT
+BEGIN:VEVENT
+UID:20110413T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110413T230000
+DTEND;VALUE=DATE-TIME:20110413T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E51 - Tracy Morgan
+END:VEVENT
+BEGIN:VEVENT
+UID:20110412T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110412T230000
+DTEND;VALUE=DATE-TIME:20110412T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E50 - Gov. Deval Patrick
+END:VEVENT
+BEGIN:VEVENT
+UID:20110411T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110411T230000
+DTEND;VALUE=DATE-TIME:20110411T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E49 - Foo Fighters
+END:VEVENT
+BEGIN:VEVENT
+UID:20110407T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110407T230000
+DTEND;VALUE=DATE-TIME:20110407T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E48 - Jamie Oliver
+END:VEVENT
+BEGIN:VEVENT
+UID:20110407T200000Z-79@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110407T200000
+DTEND;VALUE=DATE-TIME:20110407T203000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Big Bang Theory - S04E20 - The Herb Garden Germination
+END:VEVENT
+BEGIN:VEVENT
+UID:20110406T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110406T230000
+DTEND;VALUE=DATE-TIME:20110406T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E47 - Mike Huckabee
+END:VEVENT
+BEGIN:VEVENT
+UID:20110405T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110405T230000
+DTEND;VALUE=DATE-TIME:20110405T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E46 - Colin Quinn
+END:VEVENT
+BEGIN:VEVENT
+UID:20110404T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110404T230000
+DTEND;VALUE=DATE-TIME:20110404T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E45 - Billy Crystal
+END:VEVENT
+BEGIN:VEVENT
+UID:20110331T230000Z-289@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110331T230000
+DTEND;VALUE=DATE-TIME:20110331T233000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Daily Show - S16E44 - Norm MacDonald
+END:VEVENT
+BEGIN:VEVENT
+UID:20110331T200000Z-79@tvcountdown.com
+DTSTART;VALUE=DATE-TIME:20110331T200000
+DTEND;VALUE=DATE-TIME:20110331T203000
+DTSTAMP:20110430T192946Z
+URL;VALUE=URI:
+SUMMARY:The Big Bang Theory - S04E19 - The Zarnecki Incursion
+END:VEVENT
+END:VCALENDAR
\ No newline at end of file
diff --git a/modules/default/calendar/vendor/ical.js/test/test4.ics b/modules/default/calendar/vendor/ical.js/test/test4.ics
new file mode 100644
index 00000000..390a404c
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test4.ics
@@ -0,0 +1,747 @@
+BEGIN:VCALENDAR
+X-WR-CALNAME:John Doe (TripIt)
+X-WR-CALDESC:TripIt Calendar
+X-PUBLISHED-TTL:PT15M
+PRODID:-//John Doe/NONSGML Bennu 0.1//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+UID:c32a5eaba2354bb29e012ec18da827db90550a3b@tripit.com
+DTSTART;VALUE=DATE:20111011
+DTEND;VALUE=DATE:20111014
+SUMMARY:South San Francisco\, CA\, October 2011\;
+LOCATION:South San Francisco\, CA
+GEO:37.654656;-122.40775
+TRANSP:TRANSPARENT
+DESCRIPTION:John Doe is in South San Francisco\, CA from Oct 11
+ to Oct 13\, 2011\nView and/or edit details in TripIt : http://www.tripit.c
+ om/trip/show/id/23710889\nTripIt - organize your travel at http://www.trip
+ it.com\n
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+TRANSP:TRANSPARENT
+UID:item-ee275ccffa83f492d9eb63b01953b39f18d4f944@tripit.com
+DTSTART:20111011T100500
+DTEND:20111011T110500
+SUMMARY:Directions from SFO to Embassy Suites San Francisco Airport - Sout
+ h San Francisco
+LOCATION:250 GATEWAY BLVD\, South San Francisco\, CA\, 94080
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/1234\n \n[Directions] 10/11/2011 10:05am - Directions from S
+ FO to Embassy Suites San Francisco Airport - South San Francisco \nfrom: S
+ FO \nto: 250 GATEWAY BLVD\, South San Francisco\, CA\, 94080 \nView direct
+ ions here: http://maps.google.com/maps?output=mobile&saddr=SFO&daddr=250+G
+ ATEWAY+BLVD%2C+South+San+Francisco%2C+CA%2C+94080 \n \n \n\nTripIt - organ
+ ize your travel at http://www.tripit.com
+GEO:37.655634;-122.401273
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111011T165500Z
+SUMMARY:US403 PHX to SFO
+LOCATION:Phoenix (PHX)
+UID:item-c576afd397cf1f90578b4ba35e781b61ba8897db@tripit.com
+DTSTART:20111011T144500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/1234\n \n[Flight] 10/11/2011 US Airways(US) #403 dep PHX 7:4
+ 5am MST arr SFO 9:55am PDT\; John Doe\; seat(s) 8B\; conf #DXH9K
+ Z\, BXQ9WH \nBooked on http://www.americanexpress-travel.com/\; Reference
+ #: 4127 8626 9715\; http://www.americanexpress-travel.com/\; US:1-800-297-
+ 2977\, Outside:210-582-2716 \n \n \n\nTripIt - organize your travel at htt
+ p://www.tripit.com
+GEO:37.618889;-122.375
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Pick-up Rental Car: Dollar Rent A Car
+TRANSP:TRANSPARENT
+UID:item-e99a90ee1c7e4f5b68a4e551009e5bb6c475940c@tripit.com
+DTSTART:20111011T172500Z
+DTEND:20111011T182500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/1234\n \n[Car Rental] Dollar Rent A Car\; San Francisco Inte
+ rnational Airport\; primary driver John Doe\; conf #R9508361 \np
+ ickup 10/11/2011 10:25am\; dropoff 10/13/2011 6:49pm \nEconomy \nBooked on
+ http://www.americanexpress-travel.com/\; Reference #: 4127 8626 9715\; ht
+ tp://www.americanexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582
+ -2716 \n \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-in: Embassy Suites San Francisco Airport - South San Francis
+ co
+TRANSP:TRANSPARENT
+UID:item-7f3288d418bed063cc82b4512e792fbb5d8ae761@tripit.com
+DTSTART:20111011T185500Z
+DTEND:20111011T195500Z
+LOCATION:250 GATEWAY BLVD\, South San Francisco\, CA\, 94080
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/23710889\n \n[Lodging] Embassy Suites San Francisco Airport - So
+ uth San Francisco\; primary guest John Doe\; conf #R9508361 \n25
+ 0 GATEWAY BLVD\, South San Francisco\, CA\, 94080\; tel 1.650.589.3400 \na
+ rrive 10/11/2011\; depart 10/13/2011\; rooms: 1 \nBooked on http://www.ame
+ ricanexpress-travel.com/\; Reference #: 4127 8626 9715\; http://www.americ
+ anexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582-2716 \n \n \n\
+ nTripIt - organize your travel at http://www.tripit.com
+GEO:37.655634;-122.401273
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-out: Embassy Suites San Francisco Airport - South San Franci
+ sco
+TRANSP:TRANSPARENT
+UID:item-5eb4cb5fc25c55b0423921e18336e57f8c34598d@tripit.com
+DTSTART:20111014T011900Z
+DTEND:20111014T021900Z
+LOCATION:250 GATEWAY BLVD\, South San Francisco\, CA\, 94080
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/23710889\n \n[Lodging] Embassy Suites San Francisco Airport - So
+ uth San Francisco\; primary guest John Doe\; conf #R9508361 \n25
+ 0 GATEWAY BLVD\, South San Francisco\, CA\, 94080\; tel 1.650.589.3400 \na
+ rrive 10/11/2011\; depart 10/13/2011\; rooms: 1 \nBooked on http://www.ame
+ ricanexpress-travel.com/\; Reference #: 4127 8626 9715\; http://www.americ
+ anexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582-2716 \n \n \n\
+ nTripIt - organize your travel at http://www.tripit.com
+GEO:37.655634;-122.401273
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Drop-off Rental Car: Dollar Rent A Car
+TRANSP:TRANSPARENT
+UID:item-11fdbf5d02e84646025716d9f9c7a4158e1fb025@tripit.com
+DTSTART:20111014T014900Z
+DTEND:20111014T024900Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/23710889\n \n[Car Rental] Dollar Rent A Car\; San Francisco Inte
+ rnational Airport\; primary driver John Doe\; conf #R9508361 \np
+ ickup 10/11/2011 10:25am\; dropoff 10/13/2011 6:49pm \nEconomy \nBooked on
+ http://www.americanexpress-travel.com/\; Reference #: 4127 8626 9715\; ht
+ tp://www.americanexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582
+ -2716 \n \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111014T051900Z
+SUMMARY:CO6256 SFO to PHX
+LOCATION:San Francisco (SFO)
+UID:item-cb485a571a01972d6bdc74c2b829905d6e3786bf@tripit.com
+DTSTART:20111014T031900Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/23710889\n \n[Flight] 10/13/2011 Continental Airlines(CO) #6256
+ dep SFO 8:19pm PDT arr PHX 10:19pm MST\; John Doe\; conf #DXH9KZ
+ \, BXQ9WH(Operated by United Airlines flight 6256) \nBooked on http://www.
+ americanexpress-travel.com/\; Reference #: 4127 8626 9715\; http://www.ame
+ ricanexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582-2716 \n \n
+ \n\nTripIt - organize your travel at http://www.tripit.com
+GEO:33.436111;-112.009444
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+UID:c7b133db1e7be2713a4a63b75dcbad209690cab5@tripit.com
+DTSTART;VALUE=DATE:20111023
+DTEND;VALUE=DATE:20111028
+SUMMARY:Santa Barbara\, CA\, October 2011
+LOCATION:Santa Barbara\, CA
+GEO:34.420831;-119.69819
+TRANSP:TRANSPARENT
+DESCRIPTION:John Doe is in Santa Barbara\, CA from Oct 23 to Oct
+ 27\, 2011\nView and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\nTripIt - organize your travel at http://www.tripit.com
+ \n
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111023T191200Z
+SUMMARY:US2719 PHX to SBA
+LOCATION:Phoenix (PHX)
+UID:item-c4375369e9070fcc04df39ed18c4d93087577591@tripit.com
+DTSTART:20111023T173500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\n \n[Flight] 10/23/2011 US Airways(US) #2719 dep PHX 10
+ :35am MST arr SBA 12:12pm PDT\; John Doe Ticket #0378717202638\;
+ conf #A44XS5\, PRX98G\, FYYJZ4 \nBooked on http://www.americanexpress-tra
+ vel.com/\; Reference #: 7128 8086 8504\; http://www.americanexpress-travel
+ .com/\; US:1-800-297-2977\, Outside:210-582-2716\; Total Cost: $699.99 \n
+ \n \n\nTripIt - organize your travel at http://www.tripit.com
+GEO:34.427778;-119.839444
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+TRANSP:TRANSPARENT
+UID:item-962e4f045d12149319d1837ec096bf43770abd6e@tripit.com
+DTSTART:20111025T094000
+DTEND:20111025T104000
+SUMMARY:Directions from Hertz to Sofitel San Francisco Bay
+LOCATION:223 Twin Dolphin Drive\, Redwood City\, CA\, 94065
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\n \n[Directions] 10/25/2011 9:40am - Directions from He
+ rtz to Sofitel San Francisco Bay \nfrom: 780 McDonnell Road\, San Francisc
+ o\, CA\, 94128 \nto: 223 Twin Dolphin Drive\, Redwood City\, CA\, 94065 \n
+ View directions here: http://maps.google.com/maps?output=mobile&saddr=780+
+ McDonnell+Road%2C+San+Francisco%2C+CA%2C+94128&daddr=223+Twin+Dolphin+Driv
+ e%2C+Redwood+City%2C+CA%2C+94065 \n \n \n\nTripIt - organize your travel a
+ t http://www.tripit.com
+GEO:37.5232475;-122.261296
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111025T162600Z
+SUMMARY:UA5304 SBA to SFO
+LOCATION:Santa Barbara (SBA)
+UID:item-ae300a6934c3820974dba2c9c5b8fae843c67693@tripit.com
+DTSTART:20111025T150900Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\n \n[Flight] 10/25/2011 United Airlines(UA) #5304 dep S
+ BA 8:09am PDT arr SFO 9:26am PDT\; John Doe Ticket #037871720263
+ 8\; seat(s) 11B\; conf #A44XS5\, PRX98G\, FYYJZ4 \nBooked on http://www.am
+ ericanexpress-travel.com/\; Reference #: 7128 8086 8504\; http://www.ameri
+ canexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582-2716\; Total
+ Cost: $699.99 \n \n \n\nTripIt - organize your travel at http://www.tripit
+ .com
+GEO:37.618889;-122.375
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Pick-up Rental Car: Hertz
+TRANSP:TRANSPARENT
+UID:item-2a9fd5a57a4cdda4677fc6ce23738e1954fdbe2a@tripit.com
+DTSTART:20111025T163000Z
+DTEND:20111025T173000Z
+LOCATION:780 McDonnell Road\, San Francisco\, CA\, 94128
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\n \n[Car Rental] Hertz\; San Francisco International Ai
+ rport\; primary driver John Doe\; conf #F2633064194 \n780 McDonn
+ ell Road\, San Francisco\, CA\, 94128 \npickup 10/25/2011 9:30am\; dropoff
+ 10/27/2011 7:00pm \nToyota Corolla or similar\; 84.57 USD \nBooked on htt
+ p://www.hertz.com/\; Reference #: F2633064194\; http://www.hertz.com/\; 80
+ 0-654-3131\; Booking Rate: 84.57 USD\; Total Cost: 333.76 USD \n \n \n\nTr
+ ipIt - organize your travel at http://www.tripit.com
+GEO:37.6297569;-122.4000351
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+TRANSP:TRANSPARENT
+UID:item-98dfcb0bcfdcffcce9c58a84947212ed67cadda6@tripit.com
+DTSTART:20111025T163600Z
+DTEND:20111025T173600Z
+SUMMARY:Directions from SFO to Sofitel San Francisco Bay
+LOCATION:223 Twin Dolphin Drive\, Redwood City\, CA\, 94065
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\n \n[Directions] 10/25/2011 9:36am - Directions from SF
+ O to Sofitel San Francisco Bay \nfrom: SFO \nto: 223 Twin Dolphin Drive\,
+ Redwood City\, CA\, 94065 \nView directions here: http://maps.google.com/m
+ aps?output=mobile&saddr=SFO&daddr=223+Twin+Dolphin+Drive%2C+Redwood+City%2
+ C+CA%2C+94065 \n \n \n\nTripIt - organize your travel at http://www.tripit
+ .com
+GEO:37.5232475;-122.261296
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-in: Sofitel San Francisco Bay
+TRANSP:TRANSPARENT
+UID:item-8de3937b336c333faf2d55ad0a41c5ca6cc02393@tripit.com
+DTSTART:20111025T220000Z
+DTEND:20111025T230000Z
+LOCATION:223 Twin Dolphin Drive\, Redwood City\, CA\, 94065
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\n \n[Lodging] Sofitel San Francisco Bay\; primary guest
+ John Doe\; conf #F80-0GMW \n223 Twin Dolphin Drive\, Redwood Ci
+ ty\, CA\, 94065\; tel (+1)650/598-9000 \narrive 10/25/2011\; depart 10/27/
+ 2011\; rooms: 1 \nBooked on http://www.sofitel.com/\; http://www.sofitel.c
+ om/\; Total Cost: 564.00 USD \n \n \n\nTripIt - organize your travel at ht
+ tp://www.tripit.com
+GEO:37.5232475;-122.261296
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-out: Sofitel San Francisco Bay
+TRANSP:TRANSPARENT
+UID:item-f3ade58646964bde101616a6d26ea7784a1a81e8@tripit.com
+DTSTART:20111027T190000Z
+DTEND:20111027T200000Z
+LOCATION:223 Twin Dolphin Drive\, Redwood City\, CA\, 94065
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\n \n[Lodging] Sofitel San Francisco Bay\; primary guest
+ John Doe\; conf #F80-0GMW \n223 Twin Dolphin Drive\, Redwood Ci
+ ty\, CA\, 94065\; tel (+1)650/598-9000 \narrive 10/25/2011\; depart 10/27/
+ 2011\; rooms: 1 \nBooked on http://www.sofitel.com/\; http://www.sofitel.c
+ om/\; Total Cost: 564.00 USD \n \n \n\nTripIt - organize your travel at ht
+ tp://www.tripit.com
+GEO:37.5232475;-122.261296
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Drop-off Rental Car: Hertz
+TRANSP:TRANSPARENT
+UID:item-50620273fea0614d37775649034d5e1de92ae361@tripit.com
+DTSTART:20111028T020000Z
+DTEND:20111028T030000Z
+LOCATION:780 McDonnell Road\, San Francisco\, CA\, 94128
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\n \n[Car Rental] Hertz\; San Francisco International Ai
+ rport\; primary driver John Doe\; conf #F2633064194 \n780 McDonn
+ ell Road\, San Francisco\, CA\, 94128 \npickup 10/25/2011 9:30am\; dropoff
+ 10/27/2011 7:00pm \nToyota Corolla or similar\; 84.57 USD \nBooked on htt
+ p://www.hertz.com/\; Reference #: F2633064194\; http://www.hertz.com/\; 80
+ 0-654-3131\; Booking Rate: 84.57 USD\; Total Cost: 333.76 USD \n \n \n\nTr
+ ipIt - organize your travel at http://www.tripit.com
+GEO:37.6297569;-122.4000351
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111028T051900Z
+SUMMARY:CO6256 SFO to PHX
+LOCATION:San Francisco (SFO)
+UID:item-71d327f30d8beeaf7bf50c8fa63ce16005b9b0df@tripit.com
+DTSTART:20111028T031900Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24259445\n \n[Flight] 10/27/2011 Continental Airlines(CO) #6256
+ dep SFO 8:19pm PDT arr PHX 10:19pm MST\; John Doe Ticket #037871
+ 7202638\; seat(s) 17D\; conf #A44XS5\, PRX98G\, FYYJZ4(Operated by United
+ Airlines flight 6256) \nBooked on http://www.americanexpress-travel.com/\;
+ Reference #: 7128 8086 8504\; http://www.americanexpress-travel.com/\; US
+ :1-800-297-2977\, Outside:210-582-2716\; Total Cost: $699.99 \n \n \n\nTri
+ pIt - organize your travel at http://www.tripit.com
+GEO:33.436111;-112.009444
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+UID:2d4b446e63a94ade7dab0f0e9546b2d1965f011c@tripit.com
+DTSTART;VALUE=DATE:20111108
+DTEND;VALUE=DATE:20111111
+SUMMARY:Redwood City\, CA\, November 2011
+LOCATION:Redwood City\, CA
+GEO:37.485215;-122.236355
+TRANSP:TRANSPARENT
+DESCRIPTION:John Doe is in Redwood City\, CA from Nov 8 to Nov 1
+ 0\, 2011\nView and/or edit details in TripIt : http://www.tripit.com/trip/
+ show/id/24913749\nTripIt - organize your travel at http://www.tripit.com\n
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111108T175700Z
+SUMMARY:US403 PHX to SFO
+LOCATION:Phoenix (PHX)
+UID:item-7de7d829b2f95991de6d01c3d68f24b84770168c@tripit.com
+DTSTART:20111108T154500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24913749\n \n[Flight] 11/8/2011 US Airways(US) #403 dep PHX 8:45
+ am MST arr SFO 9:57am PST\; John Doe\; seat(s) 21C\; conf #FJDX0
+ J\, I2W8HW \nBooked on http://www.americanexpress-travel.com/\; Reference
+ #: 4129 9623 4732\; http://www.americanexpress-travel.com/\; US:1-800-297-
+ 2977\, Outside:210-582-2716 \n \n \n\nTripIt - organize your travel at htt
+ p://www.tripit.com
+GEO:37.618889;-122.375
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Pick-up Rental Car: Dollar Rent A Car
+TRANSP:TRANSPARENT
+UID:item-1ac6982fefdd79bc5ea849785f415a6291c450b1@tripit.com
+DTSTART:20111108T182700Z
+DTEND:20111108T192700Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24913749\n \n[Car Rental] Dollar Rent A Car\; San Francisco Inte
+ rnational Airport\; primary driver John Doe\; conf #Q0058133 \np
+ ickup 11/8/2011 10:27am\; dropoff 11/10/2011 6:25pm \nEconomy \nBooked on
+ http://www.americanexpress-travel.com/\; Reference #: 4129 9623 4732\; htt
+ p://www.americanexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582-
+ 2716 \n \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-in: Sofitel San Francisco Bay
+TRANSP:TRANSPARENT
+UID:item-126e584ffbefbec32a15ca503f0bdf8d3f9cc2f4@tripit.com
+DTSTART:20111108T195700Z
+DTEND:20111108T205700Z
+LOCATION:223 TWIN DOLPHIN DR\, Redwood City\, CA\, 94065-1514
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24913749\n \n[Lodging] Sofitel San Francisco Bay\; primary guest
+ John Doe\; conf #Q0058133 \n223 TWIN DOLPHIN DR\, Redwood City\
+ , CA\, 94065-1514\; tel 1.650.598.9000 \narrive 11/8/2011\; depart 11/10/2
+ 011\; rooms: 1 \nBooked on http://www.americanexpress-travel.com/\; Refere
+ nce #: 4129 9623 4732\; http://www.americanexpress-travel.com/\; US:1-800-
+ 297-2977\, Outside:210-582-2716 \n \n \n\nTripIt - organize your travel at
+ http://www.tripit.com
+GEO:37.5232475;-122.261296
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-out: Sofitel San Francisco Bay
+TRANSP:TRANSPARENT
+UID:item-ff48c502022356ccaa862ebb61761a0de08a1ce9@tripit.com
+DTSTART:20111111T015500Z
+DTEND:20111111T025500Z
+LOCATION:223 TWIN DOLPHIN DR\, Redwood City\, CA\, 94065-1514
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24913749\n \n[Lodging] Sofitel San Francisco Bay\; primary guest
+ John Doe\; conf #Q0058133 \n223 TWIN DOLPHIN DR\, Redwood City\
+ , CA\, 94065-1514\; tel 1.650.598.9000 \narrive 11/8/2011\; depart 11/10/2
+ 011\; rooms: 1 \nBooked on http://www.americanexpress-travel.com/\; Refere
+ nce #: 4129 9623 4732\; http://www.americanexpress-travel.com/\; US:1-800-
+ 297-2977\, Outside:210-582-2716 \n \n \n\nTripIt - organize your travel at
+ http://www.tripit.com
+GEO:37.5232475;-122.261296
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Drop-off Rental Car: Dollar Rent A Car
+TRANSP:TRANSPARENT
+UID:item-c0273c03ddbb68a9b05d5d43a489bc318136ca42@tripit.com
+DTSTART:20111111T022500Z
+DTEND:20111111T032500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24913749\n \n[Car Rental] Dollar Rent A Car\; San Francisco Inte
+ rnational Airport\; primary driver John Doe\; conf #Q0058133 \np
+ ickup 11/8/2011 10:27am\; dropoff 11/10/2011 6:25pm \nEconomy \nBooked on
+ http://www.americanexpress-travel.com/\; Reference #: 4129 9623 4732\; htt
+ p://www.americanexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582-
+ 2716 \n \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111111T055400Z
+SUMMARY:CO496 SFO to PHX
+LOCATION:San Francisco (SFO)
+UID:item-3473cf9275326ac393b37859df3b04306b4849aa@tripit.com
+DTSTART:20111111T035500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/24913749\n \n[Flight] 11/10/2011 Continental Airlines(CO) #496 d
+ ep SFO 7:55pm PST arr PHX 10:54pm MST\; John Doe\; seat(s) 26B\;
+ conf #FJDX0J\, I2W8HW(Operated by United Airlines flight 496) \nBooked on
+ http://www.americanexpress-travel.com/\; Reference #: 4129 9623 4732\; ht
+ tp://www.americanexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582
+ -2716 \n \n \n\nTripIt - organize your travel at http://www.tripit.com
+GEO:33.436111;-112.009444
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+UID:4ee5ded058432990e3d8808f48ca851e04923b6d@tripit.com
+DTSTART;VALUE=DATE:20111129
+DTEND;VALUE=DATE:20111202
+SUMMARY:Milpitas\, CA\, November 2011
+LOCATION:Milpitas\, CA
+GEO:37.428272;-121.906624
+TRANSP:TRANSPARENT
+DESCRIPTION:John Doe is in Milpitas\, CA from Nov 29 to Dec 1\,
+ 2011\nView and/or edit details in TripIt : http://www.tripit.com/trip/show
+ /id/25671681\nTripIt - organize your travel at http://www.tripit.com\n
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111129T172400Z
+SUMMARY:US282 PHX to SJC
+LOCATION:Phoenix (PHX)
+UID:item-644d5973b50d521d50e475ccf5321605d54bd0d5@tripit.com
+DTSTART:20111129T152500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/25671681\n \n[Flight] 11/29/2011 US Airways(US) #282 dep PHX 8:2
+ 5am MST arr SJC 9:24am PST\; John Doe\; seat(s) 17C\; conf #DQKD
+ GY \nBooked on http://www.americanexpress-travel.com/\; Reference #: 4131
+ 3301 9911\; http://www.americanexpress-travel.com/\; US:1-800-297-2977\, O
+ utside:210-582-2716 \n \n \n\nTripIt - organize your travel at http://www.
+ tripit.com
+GEO:37.361111;-121.925556
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Pick-up Rental Car: Alamo
+TRANSP:TRANSPARENT
+UID:item-10368bbdbc9b6f26f83098500633cc4eb604c751@tripit.com
+DTSTART:20111129T175400Z
+DTEND:20111129T185400Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/25671681\n \n[Car Rental] Alamo\; San Jose International Airport
+ \; primary driver John Doe\; conf #372828149COUNT \npickup 11/29
+ /2011 9:54am\; dropoff 12/1/2011 5:45pm \nIntermediate \nBooked on http://
+ www.americanexpress-travel.com/\; Reference #: 4131 3301 9911\; http://www
+ .americanexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582-2716 \n
+ \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-in: The Beverly Heritage Hotel
+TRANSP:TRANSPARENT
+UID:item-98d8638d3f1c011d03cb8f58b3a14a0f1203339b@tripit.com
+DTSTART:20111129T192400Z
+DTEND:20111129T202400Z
+LOCATION:1820 Barber Lane\, Milpitas\, CA\, 95035
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/25671681\n \n[Lodging] The Beverly Heritage Hotel\; primary gues
+ t John Doe\; conf #372828149COUNT \n1820 Barber Lane\, Milpitas\
+ , CA\, 95035\; tel 1.408.943.9080 \narrive 11/29/2011\; depart 12/1/2011\;
+ rooms: 1 \nBooked on http://www.americanexpress-travel.com/\; Reference #
+ : 4131 3301 9911\; http://www.americanexpress-travel.com/\; US:1-800-297-2
+ 977\, Outside:210-582-2716 \n \n \n\nTripIt - organize your travel at http
+ ://www.tripit.com
+GEO:37.4010467;-121.9116284
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111201T194400Z
+SUMMARY:US273 SJC to PHX
+LOCATION:San Jose (SJC)
+UID:item-7b9ee9bb4edfe69743e32b33f9be55753956a883@tripit.com
+DTSTART:20111201T175900Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/25671681\n \n[Flight] 12/1/2011 US Airways(US) #273 dep SJC 9:59
+ am PST arr PHX 12:44pm MST\; John Doe Ticket #0378727451156\; co
+ nf #EMF71T \nBooked on http://www.americanexpress-travel.com/\; Reference
+ #: 5133 5264 1627\; http://www.americanexpress-travel.com/\; US:1-800-297-
+ 2977\, Outside:210-582-2716\; Total Cost: $316.69 \n \n \n\nTripIt - organ
+ ize your travel at http://www.tripit.com
+GEO:33.436111;-112.009444
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-out: The Beverly Heritage Hotel
+TRANSP:TRANSPARENT
+UID:item-f79f203072002b8f06598dcb2be0e36af17b625b@tripit.com
+DTSTART:20111202T011500Z
+DTEND:20111202T021500Z
+LOCATION:1820 Barber Lane\, Milpitas\, CA\, 95035
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/25671681\n \n[Lodging] The Beverly Heritage Hotel\; primary gues
+ t John Doe\; conf #372828149COUNT \n1820 Barber Lane\, Milpitas\
+ , CA\, 95035\; tel 1.408.943.9080 \narrive 11/29/2011\; depart 12/1/2011\;
+ rooms: 1 \nBooked on http://www.americanexpress-travel.com/\; Reference #
+ : 4131 3301 9911\; http://www.americanexpress-travel.com/\; US:1-800-297-2
+ 977\, Outside:210-582-2716 \n \n \n\nTripIt - organize your travel at http
+ ://www.tripit.com
+GEO:37.4010467;-121.9116284
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Drop-off Rental Car: Alamo
+TRANSP:TRANSPARENT
+UID:item-69f526ad49fa8ca0a74486f4fc77cc3f9d23a72f@tripit.com
+DTSTART:20111202T014500Z
+DTEND:20111202T024500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/25671681\n \n[Car Rental] Alamo\; San Jose International Airport
+ \; primary driver John Doe\; conf #372828149COUNT \npickup 11/29
+ /2011 9:54am\; dropoff 12/1/2011 5:45pm \nIntermediate \nBooked on http://
+ www.americanexpress-travel.com/\; Reference #: 4131 3301 9911\; http://www
+ .americanexpress-travel.com/\; US:1-800-297-2977\, Outside:210-582-2716 \n
+ \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111202T045900Z
+SUMMARY:US288 SJC to PHX
+LOCATION:San Jose (SJC)
+UID:item-dab68a87c8dd49064ab0ba1dec5ba75ba46ff1d3@tripit.com
+DTSTART:20111202T031500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/25671681\n \n[Flight] 12/1/2011 US Airways(US) #288 dep SJC 7:15
+ pm PST arr PHX 9:59pm MST\; John Doe\; seat(s) 13C\; conf #DQKDG
+ Y \nBooked on http://www.americanexpress-travel.com/\; Reference #: 4131 3
+ 301 9911\; http://www.americanexpress-travel.com/\; US:1-800-297-2977\, Ou
+ tside:210-582-2716 \n \n \n\nTripIt - organize your travel at http://www.t
+ ripit.com
+GEO:33.436111;-112.009444
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+UID:67d48ddde166a2e9bbac2cf7d93fe493b0860008@tripit.com
+DTSTART;VALUE=DATE:20111213
+DTEND;VALUE=DATE:20111216
+SUMMARY:San Jose\, CA\, December 2011
+LOCATION:San Jose\, CA
+GEO:37.339386;-121.894955
+TRANSP:TRANSPARENT
+DESCRIPTION:John Doe is in San Jose\, CA from Dec 13 to Dec 15\,
+ 2011\nView and/or edit details in TripIt : http://www.tripit.com/trip/sho
+ w/id/27037117\nTripIt - organize your travel at http://www.tripit.com\n
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111213T172400Z
+SUMMARY:US282 PHX to SJC
+LOCATION:Phoenix (PHX)
+UID:item-2b1b9021be548a87dd335f190b60ab78c33b619d@tripit.com
+DTSTART:20111213T152500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27037117\n \n[Flight] 12/13/2011 US Airways(US) #282 dep PHX 8:2
+ 5am MST arr SJC 9:24am PST\; John Doe Ticket #0378728465928\; se
+ at(s) 15C\; conf #GGNV29 \nBooked on http://www.americanexpress-travel.com
+ /\; Reference #: 3134 0525 5102\; http://www.americanexpress-travel.com/\;
+ US:1-800-297-2977\, Outside:210-582-2716\; Total Cost: $406.39 \n \n \n\n
+ TripIt - organize your travel at http://www.tripit.com
+GEO:37.361111;-121.925556
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Pick-up Rental Car: Advantage
+TRANSP:TRANSPARENT
+UID:item-619d345bb08aaef68e8767b672277243697f5bff@tripit.com
+DTSTART:20111213T180000Z
+DTEND:20111213T190000Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27037117\n \n[Car Rental] Advantage\; San Jose International Air
+ port\; primary driver John Doe\; conf #F31539020E7 \npickup 12/1
+ 3/2011 10:00am\; dropoff 12/15/2011 7:00pm \nStandard Convertible \nRefere
+ nce #: 3134 0526 3890 \n \n \n\nTripIt - organize your travel at http://ww
+ w.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-in: Crestview Hotel:
+TRANSP:TRANSPARENT
+UID:item-fbe6c08e7523c82fac69b40ad1d0899f3d8d5982@tripit.com
+DTSTART:20111213T192400Z
+DTEND:20111213T202400Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27037117\n \n[Lodging] Crestview Hotel:\; conf #CR31342159 \ntel
+ 650-966-8848 \narrive 12/13/2011\; depart 12/15/2011 \nBooking Rate: 153.
+ 30 \n \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-out: Crestview Hotel:
+TRANSP:TRANSPARENT
+UID:item-7ed8b84628e650a6b37161c7825bac9e72add49f@tripit.com
+DTSTART:20111216T011500Z
+DTEND:20111216T021500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27037117\n \n[Lodging] Crestview Hotel:\; conf #CR31342159 \ntel
+ 650-966-8848 \narrive 12/13/2011\; depart 12/15/2011 \nBooking Rate: 153.
+ 30 \n \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Drop-off Rental Car: Advantage
+TRANSP:TRANSPARENT
+UID:item-623b54ebe07ffd48845f1a120a86940ce79c698b@tripit.com
+DTSTART:20111216T030000Z
+DTEND:20111216T040000Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27037117\n \n[Car Rental] Advantage\; San Jose International Air
+ port\; primary driver John Doe\; conf #F31539020E7 \npickup 12/1
+ 3/2011 10:00am\; dropoff 12/15/2011 7:00pm \nStandard Convertible \nRefere
+ nce #: 3134 0526 3890 \n \n \n\nTripIt - organize your travel at http://ww
+ w.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20111216T045900Z
+SUMMARY:US288 SJC to PHX
+LOCATION:San Jose (SJC)
+UID:item-52481e672972d2e88d5eaa5cf49bb801562c6014@tripit.com
+DTSTART:20111216T031500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27037117\n \n[Flight] 12/15/2011 US Airways(US) #288 dep SJC 7:1
+ 5pm PST arr PHX 9:59pm MST\; John Doe Ticket #0378728465928\; se
+ at(s) 7B\; conf #GGNV29 \nBooked on http://www.americanexpress-travel.com/
+ \; Reference #: 3134 0525 5102\; http://www.americanexpress-travel.com/\;
+ US:1-800-297-2977\, Outside:210-582-2716\; Total Cost: $406.39 \n \n \n\nT
+ ripIt - organize your travel at http://www.tripit.com
+GEO:33.436111;-112.009444
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+UID:7299ff29daed7d5c3e2ed4acc74deec5b7942bd5@tripit.com
+DTSTART;VALUE=DATE:20120103
+DTEND;VALUE=DATE:20120106
+SUMMARY:San Francisco\, CA\, January 2012
+LOCATION:San Francisco\, CA
+GEO:37.774929;-122.419415
+TRANSP:TRANSPARENT
+DESCRIPTION:John Doe is in San Francisco\, CA from Jan 3 to Jan
+ 5\, 2012\nView and/or edit details in TripIt : http://www.tripit.com/trip/
+ show/id/27863159\nTripIt - organize your travel at http://www.tripit.com\n
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20120103T175700Z
+SUMMARY:US403 PHX to SFO
+LOCATION:Phoenix (PHX)
+UID:item-f099e76114bf43ef3b122432579d8b40995412a7@tripit.com
+DTSTART:20120103T154500Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27863159\n \n[Flight] 1/3/2012 US Airways(US) #403 dep PHX 8:45a
+ m MST arr SFO 9:57am PST\; John Doe Ticket #0378731791515\; conf
+ #FH9B72\, L4F9M5 \nBooked on http://www.americanexpress-travel.com/\; Ref
+ erence #: 6135 7391 6119\; http://www.americanexpress-travel.com/\; US:1-8
+ 00-297-2977\, Outside:210-582-2716\; Total Cost: $668.39 \n \n \n\nTripIt
+ - organize your travel at http://www.tripit.com
+GEO:37.618889;-122.375
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Pick-up Rental Car: Alamo
+TRANSP:TRANSPARENT
+UID:item-fae4b4b07b66fc87df125238e0aaf645106cf4f3@tripit.com
+DTSTART:20120103T180000Z
+DTEND:20120103T190000Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27863159\n \n[Car Rental] Alamo\; San Francisco International Ai
+ rport\; primary driver John Doe\; conf #373525981COUNT \npickup
+ 1/3/2012 10:00am\; dropoff 1/5/2012 6:00pm \nCompact \nReference #: 6135 7
+ 391 6898 \n \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-in: Grand Hotel Sunnyvale
+TRANSP:TRANSPARENT
+UID:item-d89a856eb9da9dfdcb4da46f42e49af3a838fcbb@tripit.com
+DTSTART:20120103T195700Z
+DTEND:20120103T205700Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27863159\n \n[Lodging] Grand Hotel Sunnyvale\; conf #22084SY0361
+ 18 \ntel 1-408-7208500 \narrive 1/3/2012\; depart 1/5/2012 \nBooking Rate:
+ USD 169.00 \nPolicies: Guarantee to valid form of payment is required at
+ time of booking\; Cancel 1 day prior to arrival date to avoid penalty of 1
+ Nights Room Charge. Change fee may apply for early departures and changes
+ made to confirmed reservations.\; \n \n \n\nTripIt - organize your travel
+ at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Check-out: Grand Hotel Sunnyvale
+TRANSP:TRANSPARENT
+UID:item-6edc82f6411fd0b66f2f7f6baafa41623a8623a9@tripit.com
+DTSTART:20120106T010900Z
+DTEND:20120106T020900Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27863159\n \n[Lodging] Grand Hotel Sunnyvale\; conf #22084SY0361
+ 18 \ntel 1-408-7208500 \narrive 1/3/2012\; depart 1/5/2012 \nBooking Rate:
+ USD 169.00 \nPolicies: Guarantee to valid form of payment is required at
+ time of booking\; Cancel 1 day prior to arrival date to avoid penalty of 1
+ Nights Room Charge. Change fee may apply for early departures and changes
+ made to confirmed reservations.\; \n \n \n\nTripIt - organize your travel
+ at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+SUMMARY:Drop-off Rental Car: Alamo
+TRANSP:TRANSPARENT
+UID:item-58a31b96066ffd09b800af49de59a84f7b7a3a06@tripit.com
+DTSTART:20120106T020000Z
+DTEND:20120106T030000Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27863159\n \n[Car Rental] Alamo\; San Francisco International Ai
+ rport\; primary driver John Doe\; conf #373525981COUNT \npickup
+ 1/3/2012 10:00am\; dropoff 1/5/2012 6:00pm \nCompact \nReference #: 6135 7
+ 391 6898 \n \n \n\nTripIt - organize your travel at http://www.tripit.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20120101T215311Z
+DTEND:20120106T050500Z
+SUMMARY:CO496 SFO to PHX
+LOCATION:San Francisco (SFO)
+UID:item-7884351ce42d503b90ccc48c33c7c30bd4f44767@tripit.com
+DTSTART:20120106T030900Z
+DESCRIPTION:View and/or edit details in TripIt : http://www.tripit.com/tri
+ p/show/id/27863159\n \n[Flight] 1/5/2012 Continental Airlines(CO) #496 dep
+ SFO 7:09pm PST arr PHX 10:05pm MST\; John Doe Ticket #037873179
+ 1515\; conf #FH9B72\, L4F9M5(Operated by United Airlines flight 496) \nBoo
+ ked on http://www.americanexpress-travel.com/\; Reference #: 6135 7391 611
+ 9\; http://www.americanexpress-travel.com/\; US:1-800-297-2977\, Outside:2
+ 10-582-2716\; Total Cost: $668.39 \n \n \n\nTripIt - organize your travel
+ at http://www.tripit.com
+GEO:33.436111;-112.009444
+END:VEVENT
+END:VCALENDAR
diff --git a/modules/default/calendar/vendor/ical.js/test/test5.ics b/modules/default/calendar/vendor/ical.js/test/test5.ics
new file mode 100644
index 00000000..c51f25ee
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test5.ics
@@ -0,0 +1,41 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Meetup//RemoteApi//EN
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+X-ORIGINAL-URL:http://www.meetup.com/events/ical/8333638/dfdba2e469216075
+ 3404f737feace78d526ff0ce/going
+X-WR-CALNAME:My Meetups
+X-MS-OLK-FORCEINSPECTOROPEN:TRUE
+BEGIN:VTIMEZONE
+TZID:America/Phoenix
+TZURL:http://tzurl.org/zoneinfo-outlook/America/Phoenix
+X-LIC-LOCATION:America/Phoenix
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0700
+TZNAME:MST
+DTSTART:19700101T000000
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20111106T155927Z
+DTSTART;TZID=America/Phoenix:20111109T190000
+DTEND;TZID=America/Phoenix:20111109T210000
+STATUS:CONFIRMED
+SUMMARY:Phoenix Drupal User Group Monthly Meetup
+DESCRIPTION:Phoenix Drupal User Group\nWednesday\, November 9 at 7:00 PM\
+ n\nCustomizing node display with template pages in Drupal 6\n\n Jon Shee
+ han and Matthew Berry of the Office of Knowledge Enterprise Development
+ (OKED) Knowledge...\n\nDetails: http://www.meetup.com/Phoenix-Drupal-Use
+ r-Group/events/33627272/
+CLASS:PUBLIC
+CREATED:20100630T083023Z
+GEO:33.56;-111.90
+LOCATION:Open Source Project Tempe (1415 E University Dr. #103A\, Tempe\,
+ AZ 85281)
+URL:http://www.meetup.com/Phoenix-Drupal-User-Group/events/33627272/
+LAST-MODIFIED:20111102T213309Z
+UID:event_nsmxnyppbfc@meetup.com
+END:VEVENT
+END:VCALENDAR
\ No newline at end of file
diff --git a/modules/default/calendar/vendor/ical.js/test/test6.ics b/modules/default/calendar/vendor/ical.js/test/test6.ics
new file mode 100644
index 00000000..3762d02b
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test6.ics
@@ -0,0 +1,1170 @@
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110804T120000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T120000
+LOCATION:All Areas
+SUMMARY:foobar Summer 2011 starts!
+URL:F
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110804T120000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T120000
+LOCATION:Main entrance
+SUMMARY:Main entrance opened
+URL:http://www.foobar.org/summer11/come-to-party/entry-setup
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110804T120000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T120000
+LOCATION:Loading entrances #1 and #2
+SUMMARY:Loading doors are opened
+URL:http://www.foobar.org/summer11/come-to-party/entry-setup
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110804T120000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T120000
+LOCATION:foobarTV
+SUMMARY:foobarTV broadcast starts
+RRULE:FREQ=WEEKLY;BYDAY=MO,FR;INTERVAL=5;UNTIL=20130130T230000Z
+URL:http://www.foobartv.net
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110804T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T180000
+LOCATION:Loading entrances #1 and #2
+SUMMARY:Loading doors close
+URL:http://www.foobar.org/summer11/come-to-party/entry-setup
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110805T120000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T120000
+LOCATION:Loading entrance #1
+SUMMARY:Loading doors are opened
+URL:http://www.foobar.org/summer11/come-to-party/entry-setup
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110805T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T180000
+LOCATION:Loading entrance #1
+SUMMARY:Loading doors close
+URL:http://www.foobar.org/summer11/come-to-party/entry-setup
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_deadline
+DTEND;VALUE=DATE:20110804T220000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T220000
+LOCATION:PMS
+SUMMARY:Oldskool demo\, individual and 4k intro compo deadline
+URL:http://www.foobar.org/summer11/compos/how-to/submit-entry
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_deadline
+DTEND;VALUE=DATE:20110805T200000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T200000
+LOCATION:PMS
+SUMMARY:Group compo deadline
+URL:http://www.foobar.org/summer11/compos/how-to/submit-entry
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110804T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T120000
+LOCATION:PMS
+SUMMARY:Sign-up for street basketball & Guitar Hero solo guitar
+URL:http://www.foobar.org/summer11/compos/gaming-sports
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110805T110000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T120000
+LOCATION:PMS
+SUMMARY:Sign-up for sports and machine dance compos
+URL:http://www.foobar.org/summer11/compos/gaming-sports
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110806T160000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T120000
+LOCATION:PMS
+SUMMARY:Sign-up for poker tournament
+URL:http://www.foobar.org/summer11/compos/sponsored-unofficial/poker
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110804T220000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T200000
+LOCATION:Outside main entrance
+SUMMARY:Street basketball
+URL:http://www.foobar.org/summer11/compos/sports-competitions/team/#stre
+ etbasketball
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110805T124500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T123000
+LOCATION:Infodesk
+SUMMARY:Sport compos - gather up at the infodesk
+URL:http://www.foobar.org/summer11/compos/gaming-sports
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110805T140000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T130000
+LOCATION:Grass fields by Messukeskus
+SUMMARY:Disk throwing
+URL:http://www.foobar.org/summer11/compos/sports-competitions/individual
+ /#disk
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110805T140000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T130000
+LOCATION:Grass fields by Messukeskus
+SUMMARY:CD throwing
+URL:http://www.foobar.org/summer11/compos/sports-competitions/individual
+ /#cd
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110805T160000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T140000
+LOCATION:Grass fields by Messukeskus
+SUMMARY:ASUS soccer tournament
+URL:http://www.foobar.org/summer11/compos/sports-competitions/team/#socc
+ er
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110805T000000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T180000
+LOCATION:Guitar Hero
+SUMMARY:Guitar Hero: Guitar tournament - qualifying round #1
+URL:http://www.foobar.org/summer11/compos/gaming-sports/individual
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110805T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T120000
+LOCATION:Guitar Hero
+SUMMARY:Guitar Hero: Guitar tournament - qualifying round #2
+URL:http://www.foobar.org/summer11/compos/gaming-sports/individual
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110806T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T120000
+LOCATION:Guitar Hero
+SUMMARY:Guitar Hero: Guitar tournament - semifinal
+URL:http://www.foobar.org/summer11/compos/gaming-sports/individual
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110807T130000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T120000
+LOCATION:Stage
+SUMMARY:Guitar Hero: Guitar Tournament final
+URL:http://www.foobar.org/summer11/compos/gaming-sports/individual
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110805T210000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T160000
+LOCATION:Third floor
+SUMMARY:Machine Dance tournament
+URL:http://www.foobar.org/summer11/compos/gaming-sports/individual/#danc
+ e
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110807T120000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T110000
+LOCATION:Stage
+SUMMARY:Machine Dance tournament finals
+URL:http://www.foobar.org/summer11/compos/gaming-sports/individual/#danc
+ e
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110807T000000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T200000
+LOCATION:Seminar area
+SUMMARY:Poker tournament
+URL:http://www.foobar.org/summer11/compos/sponsored-unofficial/poker
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110804T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T150000
+LOCATION:Infodesk
+SUMMARY:GameDev compo recording - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/realtime/gamedev
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_showing
+DTEND;VALUE=DATE:20110805T010000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T000000
+LOCATION:Big screen
+SUMMARY:GameDev compo
+URL:http://www.foobar.org/summer11/compos/realtime/gamedev
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110804T190000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T190000
+LOCATION:Website
+SUMMARY:Fast music - sample set released
+URL:http://www.foobar.org/summer11/compos/sound-vision/fast#music
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_deadline
+DTEND;VALUE=DATE:20110804T203000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T203000
+LOCATION:PMS
+SUMMARY:Fast music - deadline
+URL:http://www.foobar.org/summer11/compos/sound-vision/fast#music
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110805T003000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T223000
+LOCATION:Infodesk
+SUMMARY:Fast music - jury - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/sound-vision/fast#music
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110805T161500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T153000
+LOCATION:Big screen
+SUMMARY:Fast music compo
+URL:http://www.foobar.org/summer11/compos/sound-vision/fast#music
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110804T203000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T203000
+LOCATION:Website
+SUMMARY:Fast graphics - objectives released
+URL:http://www.foobar.org/summer11/compos/sound-vision/fast#graphics
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_deadline
+DTEND;VALUE=DATE:20110804T220000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T220000
+LOCATION:PMS
+SUMMARY:Fast graphics - deadline
+URL:http://www.foobar.org/summer11/compos/sound-vision/fast#graphics
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110805T001500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T233000
+LOCATION:Infodesk
+SUMMARY:Fast graphics - jury - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/sound-vision/fast#graphics
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110805T164500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T163000
+LOCATION:Big screen
+SUMMARY:Fast graphics compo
+URL:http://www.foobar.org/summer11/compos/sound-vision/fast#graphics
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110805T021500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T221500
+LOCATION:Infodesk
+SUMMARY:Extreme music and Oldskool demo - jury - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/realtime/oldskool
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110805T150000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T110000
+LOCATION:Infodesk
+SUMMARY:Extreme music and Oldskool demo - compo entry recording\, come to
+ Infodesk
+URL:http://www.foobar.org/summer11/compos/realtime/oldskool
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110806T144500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T140000
+LOCATION:Big screen
+SUMMARY:Extreme music compo
+URL:http://www.foobar.org/summer11/compos/realtime/extreme#music
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110806T171500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T163000
+LOCATION:Big screen
+SUMMARY:Oldskool demo compo
+URL:http://www.foobar.org/summer11/compos/realtime/oldskool
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110805T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T130000
+LOCATION:Infodesk
+SUMMARY:Music compo - jury - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/sound-vision/music#freestyle
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110806T161500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T153000
+LOCATION:Big screen
+SUMMARY:Music compo
+URL:http://www.foobar.org/summer11/compos/sound-vision/music#freestyle
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110805T163000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T140000
+LOCATION:Infodesk
+SUMMARY:Graphics compo - jury - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/sound-vision/graphics#freestyl
+ e
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110806T151500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T150000
+LOCATION:Big screen
+SUMMARY:Graphics compo
+URL:http://www.foobar.org/summer11/compos/sound-vision/graphics#freestyl
+ e
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110805T110000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T090000
+LOCATION:Infodesk
+SUMMARY:Real wild demo - recording assistance - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/realtime/demo#mobile
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110805T223000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T203000
+LOCATION:Infodesk
+SUMMARY:Real wild demo - jury - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/realtime/demo#mobile
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110806T181500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T173000
+LOCATION:Big screen
+SUMMARY:Real wild demo compo
+URL:http://www.foobar.org/summer11/compos/realtime/demo#mobile
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110805T013000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T220000
+LOCATION:Infodesk
+SUMMARY:Short film & Real Wild demo - format conversions help - come to In
+ fodesk
+URL:http://www.foobar.org/summer11/compos/sound-vision/short-film
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110806T123000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T100000
+LOCATION:Infodesk
+SUMMARY:Short film - jury - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/sound-vision/short-film
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110806T204500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T194500
+LOCATION:Big screen
+SUMMARY:Short film compo
+URL:http://www.foobar.org/summer11/compos/sound-vision/short-film
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110805T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T160000
+LOCATION:Infodesk
+SUMMARY:4k intro - jury - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/realtime/demo#64k
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110806T214500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T210000
+LOCATION:Big screen
+SUMMARY:4k intro compo
+URL:http://www.foobar.org/summer11/compos/realtime/demo#64k
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110806T010000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T230000
+LOCATION:Infodesk
+SUMMARY:64k intro - jury - come to Infodesk
+URL:http://www.foobar.org/summer11/compos/realtime/demo#64k
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110806T224500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T220000
+LOCATION:Big screen
+SUMMARY:64k intro compo
+URL:http://www.foobar.org/summer11/compos/realtime/demo#64k
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_jury
+DTEND;VALUE=DATE:20110806T160000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T130000
+LOCATION:Infodesk
+SUMMARY:Demo - jury
+URL:http://www.foobar.org/summer11/compos/realtime/demo#demo
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_show
+DTEND;VALUE=DATE:20110806T230000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T220000
+LOCATION:Big screen
+SUMMARY:Demo compo
+URL:http://www.foobar.org/summer11/compos/realtime/demo#demo
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110804T160000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T120000
+LOCATION:Powerplay
+SUMMARY:ASUS ROG Starcraft 2 - Brackets D\, E\, F
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110804T210000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T170000
+LOCATION:Powerplay
+SUMMARY:ASUS ROG Starcraft 2 - Brackets C\, G\, H
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T020000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T220000
+LOCATION:Powerplay
+SUMMARY:ASUS ROG Starcraft 2 - Brackets A\, B
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T140000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T110000
+LOCATION:Powerplay
+SUMMARY:ASUS ROG Starcraft 2 - Single elimination 16 players
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T153000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T140000
+LOCATION:Powerplay
+SUMMARY:ASUS ROG Starcraft 2 - Single elimination 6 players
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T193000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T180000
+LOCATION:Powerplay
+SUMMARY:ASUS ROG Starcraft 2 - Semifinal 1
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T210000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T193000
+LOCATION:Powerplay
+SUMMARY:ASUS ROG Starcraft 2 - Semifinal 2
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T223000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T210000
+LOCATION:Powerplay
+SUMMARY:ASUS ROG Starcraft 2 - Bronze match
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110806T003000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T230000
+LOCATION:Stage
+SUMMARY:ASUS ROG Starcraft 2 - Final
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110806T160000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T130000
+LOCATION:Game hall
+SUMMARY:WCG.fi Starcraft 2 – Brackets
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110806T193000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T173000
+LOCATION:Game hall
+SUMMARY:WCG.fi Starcraft 2 - Single elimination 8 players
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110806T210000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T200000
+LOCATION:Game hall
+SUMMARY:WCG.fi Starcraft 2 - Semifinal 1
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110806T220000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T210000
+LOCATION:Game hall
+SUMMARY:WCG.fi Starcraft 2 - Semifinal 2
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110807T003000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T223000
+LOCATION:Game hall
+SUMMARY:WCG.fi Starcraft 2 - Bronze match
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110807T023000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T000000
+LOCATION:Stage
+SUMMARY:WCG.fi Starcraft 2 - Final
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T003000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T193000
+LOCATION:Game hall
+SUMMARY:Brink - Brackets
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T140000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T120000
+LOCATION:Game hall
+SUMMARY:Brink - Bronze match
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T190000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T173000
+LOCATION:Stage
+SUMMARY:Brink - Final
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T190000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T120000
+LOCATION:Game hall
+SUMMARY:Team Fortress 2 - Brackets
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T230000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T210000
+LOCATION:Game hall
+SUMMARY:Team Fortress 2 - Semifinal 1
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T230000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T210000
+LOCATION:Game hall
+SUMMARY:Team Fortress 2 - Semifinal 2
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110806T170000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T150000
+LOCATION:Game hall
+SUMMARY:Team Fortress 2 - Bronze match
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110806T130000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T110000
+LOCATION:Stage
+SUMMARY:Team Fortress 2 - Final
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T003000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T180000
+LOCATION:Game hall
+SUMMARY:FIFA 2011 - tournament
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110805T230000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T190000
+LOCATION:Game hall
+SUMMARY:NHL 2011 - tournament
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110806T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T140000
+LOCATION:Game hall
+SUMMARY:Tekken 6 - tournament
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110806T190000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T150000
+LOCATION:Game hall
+SUMMARY:Super Street Fighter - tournament
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo_game
+DTEND;VALUE=DATE:20110807T060000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T180000
+LOCATION:Own computer place
+SUMMARY:Heroes of Newerth - Tournament
+URL:http://www.peliliiga.fi
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110804T213000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T210000
+LOCATION:Stage
+SUMMARY:Opening ceremony
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110806T170000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T170000
+LOCATION:PMS
+SUMMARY:Voting closes for individual and Game development compos
+URL:http://www.foobar.org/summer11/party_manual/intranet/vote
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110806T193000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T183000
+LOCATION:Stage
+SUMMARY:Prize ceremony 1
+URL:http://www.foobar.org/summer11/compos/prizes/
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110807T120000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T120000
+LOCATION:PMS
+SUMMARY:Voting closes
+URL:http://www.foobar.org/summer11/party_manual/intranet/vote
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Compo
+DTEND;VALUE=DATE:20110807T143000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T140000
+LOCATION:Stage
+SUMMARY:Prize ceremony 2
+URL:http://www.foobar.org/summer11/compos/prizes/
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110807T150000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T143000
+LOCATION:Stage
+SUMMARY:Closing ceremony
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110806T130000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T110000
+LOCATION:Outside loading door #1
+SUMMARY:Robowar tournament qualifier
+URL:http://www.foobar.org/summer11/program/robosota
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110806T160000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T150000
+LOCATION:Outside loading door #1
+SUMMARY:Robowar tournament final
+URL:http://www.foobar.org/summer11/program/robosota
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110804T233000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T220000
+LOCATION:Main stage
+SUMMARY:Concert - Machinae Supremacy
+URL:http://www.foobar.org/summer11/program/concerts
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110805T220000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T203000
+LOCATION:Main stage
+SUMMARY:Concert - Press Play on Tape
+URL:http://www.foobar.org/summer11/program/concerts
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110805T143000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T140000
+LOCATION:Stage
+SUMMARY:Cosplay event presented by BatMud
+URL:http://www.foobar.org/summer11/program/
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110806T190000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T140000
+LOCATION:Powerplay
+SUMMARY:ASUS presents - Paragon WOW raiding
+URL:http://www.foobar.org/summer11/program/
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110804T184500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T180000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Digital resistance\, East European demo art
+URL:http://www.foobar.org/summer11/seminars/sessions#demoart
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110804T194500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T190000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Why Being Poor and Having No Budget is Good For
+ Making Game
+URL:http://www.foobar.org/summer11/seminars/sessions#constraints
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110804T204500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T200000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Remix Culture In The Demoscene
+URL:http://www.foobar.org/summer11/seminars/sessions#remix
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110804T214500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110804T210000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - DRM of Pacman
+URL:http://www.foobar.org/summer11/seminars/sessions#drmpacman
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110805T104500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T100000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Perfect harmony: crowdsourcing matches musician
+ s and sound designers to game developers
+URL:http://www.foobar.org/summer11/seminars/sessions#music
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110805T124500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T120000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Browserscene: Creating demos on the Web
+URL:http://www.foobar.org/summer11/seminars/sessions#webgl2
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110805T144500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T140000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Fumbling my way through pixel-based effects mad
+ e learning WebGL a lot easier
+URL:http://www.foobar.org/summer11/seminars/sessions#webgl1
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110805T154500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T150000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - The Making of RO.ME
+URL:http://www.foobar.org/summer11/seminars/sessions#rome
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110805T164500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T160000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Developing next generation of online games
+URL:http://www.foobar.org/summer11/seminars/sessions#nextgenonline
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110805T174500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T170000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Developing Games for Nokia Phones
+URL:http://www.foobar.org/summer11/seminars/sessions#nokia_games
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110805T184500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T180000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Rocking your socks off with Windows Phone 7
+URL:http://www.foobar.org/summer11/seminars/sessions#wp7
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110805T194500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T190000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Wonders of Kinect Windows SDK
+URL:http://www.foobar.org/summer11/seminars/sessions#kinect
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110806T114500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T110000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Basics of demo programming
+URL:http://www.foobar.org/summer11/seminars/sessions#demoprogramming
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110806T124500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T120000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Kajak3D\, an open source multiplatform game eng
+ ine
+URL:http://www.foobar.org/summer11/seminars/sessions#kajak3d
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110806T134500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T130000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Adventures in multithreaded gameplay coding
+URL:http://www.foobar.org/summer11/seminars/sessions#threaded
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110806T144500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T140000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - The art and design of Ridge Racer Unbounded
+URL:http://www.foobar.org/summer11/seminars/sessions#ridgeracer
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110806T154500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T150000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Future of location based mobile games
+URL:http://www.foobar.org/summer11/seminars/sessions#location
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110806T184500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T180000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Robowar
+URL:http://www.foobar.org/summer11/seminars/sessions#robosota
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110807T100000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T100000
+LOCATION:Loading entrances #1 and #2
+SUMMARY:Loading doors open
+URL:http://www.foobar.org/summer11/come-to-party/leaving
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110807T161500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T161500
+LOCATION:All areas
+SUMMARY:Lights go on
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110807T163000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T163000
+LOCATION:All areas
+SUMMARY:Party network goes down
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110807T170000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T170000
+LOCATION:All areas
+SUMMARY:Electricity is cut
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110807T161500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T161500
+LOCATION:foobarTV
+SUMMARY:foobarTV broadcast ends
+URL:http://www.foobartv.net
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110807T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T180000
+LOCATION:Loading entrances #1\, #2 and #3
+SUMMARY:Loading doors closed
+URL:http://www.foobar.org/summer11/come-to-party/leaving
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110807T180000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110807T180000
+LOCATION:All areas
+SUMMARY:foobar Summer 2011 is over - see you next year!
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110805T153000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T150000
+LOCATION:Outside main entrance
+SUMMARY:RC helicopters: flying demonstration with T-REX 600N
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110805T173000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T170000
+LOCATION:Outside main entrance
+SUMMARY:RC helicopters: flying demonstration with T-REX 600N
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110806T133000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T130000
+LOCATION:Outside main entrance
+SUMMARY:RC helicopters: flying demonstration with T-REX 600N
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110806T163000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T160000
+LOCATION:Outside main entrance
+SUMMARY:RC helicopters: flying demonstration with T-REX 600N
+URL:
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110806T164500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T160000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars – All aboard to the Dark side of the Moon (Iron
+ Sky)
+URL:http://www.foobar.org/summer11/seminars/sessions#ironsky
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110806T174500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T170000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars – Angry Birds on the web
+URL:http://www.foobar.org/summer11/seminars/sessions#angrybirds
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110806T230000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T120000
+LOCATION:Pelitalo stand
+SUMMARY:Pelitalo events begin
+URL:http://pelitalo.nettiareena.fi/foobar/ohjelma/
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Event
+DTEND;VALUE=DATE:20110806T210000
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110806T200000
+LOCATION:Pelitalo stand
+SUMMARY:Bomberman tournament
+URL:http://pelitalo.nettiareena.fi/foobar/ohjelma/
+END:VEVENT
+BEGIN:VEVENT
+CATEGORIES:Seminar
+DTEND;VALUE=DATE:20110805T134500
+DTSTAMP;VALUE=DATE:20120214T224612
+DTSTART;VALUE=DATE:20110805T130000
+LOCATION:Seminar area
+SUMMARY:ARTtech seminars - Deconstructing a browserscene demo
+URL:http://www.foobar.org/summer11/seminars/sessions#webgl2
+END:VEVENT
+END:VCALENDAR
diff --git a/modules/default/calendar/vendor/ical.js/test/test7.ics b/modules/default/calendar/vendor/ical.js/test/test7.ics
new file mode 100755
index 00000000..4379cd0e
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test7.ics
@@ -0,0 +1,16 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:ownCloud Calendar 0.6.3
+X-WR-CALNAME:Fête Nationale - Férié
+BEGIN:VEVENT
+CREATED:20090502T140513Z
+DTSTAMP:20111106T124709Z
+UID:FA9831E7-C238-4FEC-95E5-CD46BD466421
+SUMMARY:Fête Nationale - Férié
+RRULE:FREQ=YEARLY
+DTSTART;VALUE=DATE:20120714
+DTEND;VALUE=DATE:20120715
+TRANSP:OPAQUE
+SEQUENCE:5
+END:VEVENT
+END:VCALENDAR
diff --git a/modules/default/calendar/vendor/ical.js/test/test8.ics b/modules/default/calendar/vendor/ical.js/test/test8.ics
new file mode 100644
index 00000000..e7b2f50f
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test8.ics
@@ -0,0 +1,23 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:ownCloud Calendar 0.6.3
+X-WR-CALNAME:Default calendar
+BEGIN:VTODO
+CREATED;VALUE=DATE-TIME:20130714T092804Z
+UID:0aa462f13c
+LAST-MODIFIED;VALUE=DATE-TIME:20130714T092804Z
+DTSTAMP;VALUE=DATE-TIME:20130714T092804Z
+CATEGORIES:Projets
+SUMMARY:Migrer le blog
+PERCENT-COMPLETE:100
+COMPLETED;VALUE=DATE-TIME;TZID=Europe/Monaco:20130716T105745
+END:VTODO
+BEGIN:VTODO
+CREATED;VALUE=DATE-TIME:20130714T092912Z
+UID:5e05bbcf34
+LAST-MODIFIED;VALUE=DATE-TIME:20130714T092912Z
+DTSTAMP;VALUE=DATE-TIME:20130714T092912Z
+SUMMARY:Créer test unitaire erreur ical
+CATEGORIES:Projets
+END:VTODO
+END:VCALENDAR
diff --git a/modules/default/calendar/vendor/ical.js/test/test9.ics b/modules/default/calendar/vendor/ical.js/test/test9.ics
new file mode 100644
index 00000000..76a1d7b9
--- /dev/null
+++ b/modules/default/calendar/vendor/ical.js/test/test9.ics
@@ -0,0 +1,21 @@
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:eb9e1bd2-ceba-499f-be77-f02773954c72
+SUMMARY:Event with an alarm
+DESCRIPTION:This is an event with an alarm.
+ORGANIZER="mailto:stomlinson@mozilla.com"
+DTSTART;TZID="America/Los_Angeles":20130418T110000
+DTEND;TZID="America/Los_Angeles":20130418T120000
+STATUS:CONFIRMED
+CLASS:PUBLIC
+TRANSP:OPAQUE
+LAST-MODIFIED:20130418T175632Z
+DTSTAMP:20130418T175632Z
+SEQUENCE:3
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;RELATED=START:-PT5M
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+END:VCALENDAR
diff --git a/package.json b/package.json
index 3ce3d15d..70970ec0 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,6 @@
"dependencies": {
"express": "latest",
"feedme": "latest",
- "ical": "latest",
"iconv-lite": "latest",
"moment": "latest",
"request": "latest",
From d778f2dd8b22683052c45e8af13190ecef43e6ec Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Wed, 20 Apr 2016 11:42:01 +0200
Subject: [PATCH 37/38] fix: #206
---
modules/default/clock/clock.js | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js
index cdae1db3..2c07e36c 100644
--- a/modules/default/clock/clock.js
+++ b/modules/default/clock/clock.js
@@ -43,6 +43,7 @@ Module.register("clock",{
var dateWrapper = document.createElement("div");
var timeWrapper = document.createElement("div");
var secondsWrapper = document.createElement("sup");
+ var periodWrapper = document.createElement("span");
// Style Wrappers
dateWrapper.className = "date normal medium";
@@ -57,27 +58,30 @@ Module.register("clock",{
if (this.config.timeFormat !== 24) {
var now = new Date();
var hours = now.getHours() % 12 || 12;
- if (this.config.showPeriod) {
- if (this.config.showPeriodUpper) {
- timeString = hours + moment().format(':mm A');
- } else {
- timeString = hours + moment().format(':mm a');
- }
- } else {
- timeString = hours + moment().format(':mm');
- }
+ timeString = hours + moment().format(':mm');
}
dateWrapper.innerHTML = moment().format("dddd, LL");
timeWrapper.innerHTML = timeString;
secondsWrapper.innerHTML = moment().format("ss");
+
+ if (this.config.showPeriodUpper) {
+ periodWrapper.innerHTML = moment().format('A');
+ } else {
+ periodWrapper.innerHTML = moment().format('a');
+ }
+
+
// Combine wrappers.
wrapper.appendChild(dateWrapper);
wrapper.appendChild(timeWrapper);
if (this.config.displaySeconds) {
timeWrapper.appendChild(secondsWrapper);
}
+ if (this.config.showPeriod) {
+ timeWrapper.appendChild(periodWrapper);
+ }
// Return the wrapper to the dom.
return wrapper;
From 3b1f1a41dcb09323eb4e88d2be502bb5c86d35cb Mon Sep 17 00:00:00 2001
From: Michael Teeuw
Date: Wed, 20 Apr 2016 12:16:41 +0200
Subject: [PATCH 38/38] Fix period display when using 24 hour mode.
---
modules/default/clock/clock.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js
index 2c07e36c..611170f4 100644
--- a/modules/default/clock/clock.js
+++ b/modules/default/clock/clock.js
@@ -79,7 +79,7 @@ Module.register("clock",{
if (this.config.displaySeconds) {
timeWrapper.appendChild(secondsWrapper);
}
- if (this.config.showPeriod) {
+ if (this.config.showPeriod && this.config.timeFormat !== 24) {
timeWrapper.appendChild(periodWrapper);
}