mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-08-24 22:13:33 +00:00
Add calendar module.
This commit is contained in:
29
modules/calendar/calendar.css
Normal file
29
modules/calendar/calendar.css
Normal file
@@ -0,0 +1,29 @@
|
||||
.calendar .symbol {
|
||||
padding-left: 0px;
|
||||
padding-right: 10px;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.calendar .symbol span {
|
||||
display: inline-block;
|
||||
|
||||
-ms-transform: translate(0px,2px); /* IE 9 */
|
||||
-webkit-transform: translate(0px,2px); /* Safari */
|
||||
transform: translate(0px,2px);
|
||||
}
|
||||
|
||||
.calendar .title {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.calendar .time {
|
||||
padding-left: 30px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.calendar table {
|
||||
width: 100%;
|
||||
border-spacing: 0px;
|
||||
border-collapse: separate;
|
||||
}
|
240
modules/calendar/calendar.js
Normal file
240
modules/calendar/calendar.js
Normal file
@@ -0,0 +1,240 @@
|
||||
/* global Module */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Calendar
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
Module.create({
|
||||
|
||||
// Define module defaults
|
||||
defaults: {
|
||||
maximumEntries: 10, // Total Maximum Entries
|
||||
displaySymbol: true,
|
||||
defaultSymbol: 'calendar', // Fontawsome Symbol see http://fontawesome.io/cheatsheet/
|
||||
maxTitleLength: 25,
|
||||
fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
|
||||
animationSpeed: 2000,
|
||||
fade: true,
|
||||
fadePoint: 0.25, // Start on 1/4th of the list.
|
||||
calendars: [
|
||||
{
|
||||
symbol: 'calendar',
|
||||
url: 'http://www.calendarlabs.com/templates/ical/US-Holidays.ics',
|
||||
},
|
||||
],
|
||||
titleReplace: {
|
||||
'De verjaardag van ' : ''
|
||||
}
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getStyles: function() {
|
||||
return ['calendar.css', 'font-awesome.css'];
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
return ['moment.js'];
|
||||
},
|
||||
|
||||
// Override start method.
|
||||
start: function() {
|
||||
Log.log('Starting module: ' + this.name);
|
||||
|
||||
// Set locale.
|
||||
moment.locale(config.language);
|
||||
|
||||
for (var c in this.config.calendars) {
|
||||
var calendar = this.config.calendars[c];
|
||||
calendar.url = calendar.url.replace('webcal://', 'http://');
|
||||
this.addCalendar(calendar.url);
|
||||
}
|
||||
|
||||
this.calendarData = {};
|
||||
},
|
||||
|
||||
// Override socket notification handler.
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
if (notification === 'CALENDAR_EVENTS') {
|
||||
if (this.hasCalendarURL(payload.url)) {
|
||||
this.calendarData[payload.url] = payload.events;
|
||||
}
|
||||
} else if(notification === 'FETCH_ERROR') {
|
||||
Log.error('Calendar Error. Could not fetch calendar: ' + payload.url);
|
||||
} else if(notification === 'INCORRECT_URL') {
|
||||
Log.error('Calendar Error. Incorrect url: ' + payload.url);
|
||||
} else {
|
||||
Log.log('Calendar received an unknown socket notification: '+notification);
|
||||
}
|
||||
|
||||
this.updateDom(this.config.animationSpeed);
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
|
||||
var events = this.createEventList();
|
||||
var wrapper = document.createElement("table");
|
||||
wrapper.className = "small";
|
||||
|
||||
if (events.length === 0) {
|
||||
wrapper.innerHTML = "Loading events ...";
|
||||
wrapper.className = "small dimmed";
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
for (var e in events) {
|
||||
var event = events[e];
|
||||
|
||||
var eventWrapper = document.createElement("tr");
|
||||
eventWrapper.className = "normal";
|
||||
|
||||
if (this.config.displaySymbol) {
|
||||
var symbolWrapper = document.createElement("td");
|
||||
symbolWrapper.className = "symbol";
|
||||
var symbol = document.createElement("span");
|
||||
symbol.className = "fa fa-" + this.symbolForUrl(event.url);
|
||||
symbolWrapper.appendChild(symbol);
|
||||
eventWrapper.appendChild(symbolWrapper);
|
||||
}
|
||||
|
||||
var titleWrapper = document.createElement("td");
|
||||
titleWrapper.innerHTML = this.titleTransform(event.title);
|
||||
titleWrapper.className = "title bright";
|
||||
eventWrapper.appendChild(titleWrapper);
|
||||
|
||||
var timeWrapper = document.createElement("td");
|
||||
timeWrapper.innerHTML = moment(event.startDate,'x').fromNow();
|
||||
timeWrapper.className = "time light";
|
||||
eventWrapper.appendChild(timeWrapper);
|
||||
|
||||
wrapper.appendChild(eventWrapper);
|
||||
|
||||
// Create fade effect.
|
||||
if (this.config.fade && this.config.fadePoint < 1) {
|
||||
if (this.config.fadePoint < 0) {
|
||||
this.config.fadePoint = 0;
|
||||
}
|
||||
var startingPoint = events.length * this.config.fadePoint;
|
||||
var steps = events.length - startingPoint;
|
||||
if (e >= startingPoint) {
|
||||
var currentStep = e - startingPoint;
|
||||
eventWrapper.style.opacity = 1 - (1 / steps * currentStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
/* hasCalendarURL(url)
|
||||
* Check if this config contains the calendar url.
|
||||
*
|
||||
* argument url sting - Url to look for.
|
||||
*
|
||||
* return bool - Has calendar url
|
||||
*/
|
||||
hasCalendarURL: function(url) {
|
||||
for (var c in this.config.calendars) {
|
||||
var calendar = this.config.calendars[c];
|
||||
if (calendar.url === url) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/* createEventList()
|
||||
* Creates the sorted list of all events.
|
||||
*
|
||||
* return array - Array with events.
|
||||
*/
|
||||
createEventList: function() {
|
||||
var events = [];
|
||||
for (var c in this.calendarData) {
|
||||
var calendar = this.calendarData[c];
|
||||
for (var e in calendar) {
|
||||
var event = calendar[e];
|
||||
event.url = c;
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
events.sort(function(a,b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
return events.slice(0, this.config.maximumEntries);
|
||||
},
|
||||
|
||||
/* createEventList(url)
|
||||
* Requests node helper to add calendar url.
|
||||
*
|
||||
* argument url sting - Url to add.
|
||||
*/
|
||||
addCalendar: function(url) {
|
||||
this.sendSocketNotification('ADD_CALENDAR', {
|
||||
url: url,
|
||||
maximumEntries: this.config.maximumEntries,
|
||||
fetchInterval: this.config.fetchInterval
|
||||
});
|
||||
},
|
||||
|
||||
/* symbolForUrl(url)
|
||||
* Retrieves the symbol for a specific url.
|
||||
*
|
||||
* argument url sting - Url to look for.
|
||||
*
|
||||
* return string - The Symbol
|
||||
*/
|
||||
symbolForUrl: function(url) {
|
||||
for (var c in this.config.calendars) {
|
||||
var calendar = this.config.calendars[c];
|
||||
if (calendar.url === url && typeof calendar.symbol === 'string') {
|
||||
return calendar.symbol;
|
||||
}
|
||||
}
|
||||
|
||||
return this.config.defaultSymbol;
|
||||
},
|
||||
|
||||
/* shorten(string, maxLength)
|
||||
* Shortens a sting if it's longer than maxLenthg.
|
||||
* Adds an ellipsis to the end.
|
||||
*
|
||||
* argument string string - The string to shorten.
|
||||
* argument maxLength number - The max lenth of the string.
|
||||
*
|
||||
* return string - The shortened string.
|
||||
*/
|
||||
shorten: function(string, maxLength) {
|
||||
if (string.length > maxLength) {
|
||||
return string.slice(0,maxLength) + "…";
|
||||
}
|
||||
|
||||
return string;
|
||||
},
|
||||
|
||||
/* titleTransform(title)
|
||||
* Transforms the title of an event for usage.
|
||||
* Replaces parts of the text as defined in config.titleReplace.
|
||||
* Shortens title based on config.maxTitleLength
|
||||
*
|
||||
* argument title string - The title to transform.
|
||||
*
|
||||
* return string - The transformed title.
|
||||
*/
|
||||
titleTransform: function(title) {
|
||||
for (var needle in this.config.titleReplace) {
|
||||
var replacement = this.config.titleReplace[needle];
|
||||
title = title.replace(needle, replacement);
|
||||
}
|
||||
|
||||
title = this.shorten(title, this.config.maxTitleLength);
|
||||
return title;
|
||||
}
|
||||
});
|
208
modules/calendar/node_helper.js
Normal file
208
modules/calendar/node_helper.js
Normal file
@@ -0,0 +1,208 @@
|
||||
/* Magic Mirror
|
||||
* Node Helper: Calendar
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
var NodeHelper = require('node_helper');
|
||||
var ical = require('ical');
|
||||
var moment = require('moment');
|
||||
var validUrl = require('valid-url');
|
||||
|
||||
var CalendarFetcher = function(url, reloadInterval, maximumEntries) {
|
||||
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 = [];
|
||||
|
||||
for (var e in data) {
|
||||
var event = data[e];
|
||||
|
||||
if (event.type === 'VEVENT') {
|
||||
var startDate = (event.start.length === 8) ? moment(event.start, 'YYYYMMDD') : moment(new Date(event.start));
|
||||
|
||||
var today = moment().startOf('day');
|
||||
|
||||
if (startDate > today) {
|
||||
newEvents.push({
|
||||
title: event.summary,
|
||||
startDate: startDate.format('x')
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
if (events.length <= 0) {
|
||||
//console.log('No events to broadcast yet.');
|
||||
return;
|
||||
}
|
||||
//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 = NodeHelper.create({
|
||||
// Override start method.
|
||||
start: function() {
|
||||
var self = this;
|
||||
var events = [];
|
||||
|
||||
this.fetchers = [];
|
||||
|
||||
console.log('Starting node helper for: ' + this.name);
|
||||
|
||||
|
||||
},
|
||||
|
||||
// Override socketNotificationReceived method.
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
if (notification === 'ADD_CALENDAR') {
|
||||
//console.log('ADD_CALENDAR: ');
|
||||
this.createFetcher(payload.url, payload.fetchInterval, payload.maximumEntries);
|
||||
}
|
||||
},
|
||||
|
||||
/* createFetcher(url, reloadInterval)
|
||||
* Creates a fetcher for a new url if it doesn't exsist yet.
|
||||
* Otherwise it reuses the exsisting one.
|
||||
*
|
||||
* attribute url string - URL of the news feed.
|
||||
* attribute reloadInterval number - Reload interval in milliseconds.
|
||||
*/
|
||||
|
||||
createFetcher: function(url, fetchInterval, maximumEntries) {
|
||||
var self = this;
|
||||
|
||||
if (!validUrl.isUri(url)){
|
||||
self.sendSocketNotification('INCORRECT_URL', {url:url});
|
||||
return;
|
||||
}
|
||||
|
||||
var fetcher;
|
||||
if (typeof self.fetchers[url] === 'undefined') {
|
||||
console.log('Create new calendar fetcher for url: ' + url + ' - Interval: ' + fetchInterval);
|
||||
fetcher = new CalendarFetcher(url, fetchInterval, maximumEntries);
|
||||
|
||||
fetcher.onReceive(function(fetcher) {
|
||||
//console.log('Broadcast events.');
|
||||
//console.log(fetcher.events());
|
||||
|
||||
self.sendSocketNotification('CALENDAR_EVENTS', {
|
||||
url: fetcher.url(),
|
||||
events: fetcher.events()
|
||||
});
|
||||
});
|
||||
|
||||
fetcher.onError(function(fetcher, error) {
|
||||
self.sendSocketNotification('FETCH_ERROR', {
|
||||
url: fetcher.url(),
|
||||
error: error
|
||||
});
|
||||
});
|
||||
|
||||
self.fetchers[url] = fetcher;
|
||||
} else {
|
||||
console.log('Use exsisting news fetcher for url: ' + url);
|
||||
fetcher = self.fetchers[url];
|
||||
fetcher.broadcastEvents();
|
||||
}
|
||||
|
||||
fetcher.startFetch();
|
||||
}
|
||||
});
|
||||
|
@@ -13,7 +13,7 @@ Module.create({
|
||||
defaults: {
|
||||
feedUrl: 'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml',
|
||||
showPublishDate: true,
|
||||
reloadInterval: 10 * 60 * 1000, // every 10 minutes
|
||||
reloadInterval: 5 * 60 * 1000, // every 5 minutes
|
||||
updateInterval: 7.5 * 1000,
|
||||
animationSpeed: 2.5 * 1000,
|
||||
},
|
||||
|
@@ -1,3 +1,10 @@
|
||||
/* Magic Mirror
|
||||
* Node Helper: Newsfeed
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
var NodeHelper = require('node_helper');
|
||||
var validUrl = require('valid-url');
|
||||
var Fetcher = require('./fetcher.js');
|
||||
|
2
modules/node_modules/node_helper/index.js
generated
vendored
2
modules/node_modules/node_helper/index.js
generated
vendored
@@ -70,7 +70,7 @@ NodeHelper = Class.extend({
|
||||
// register catch all.
|
||||
socket.on('*', function (notification, payload) {
|
||||
if (notification !== '*')
|
||||
console.log('received message in namespace: ' + namespace);
|
||||
//console.log('received message in namespace: ' + namespace);
|
||||
self.socketNotificationReceived(notification, payload);
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user