Daniel Buecheler cfeef98261 Private events are hidden
Events with the class:PRIVATE iCal property are not shown in the
calendar module. They are not added to the array of calendar
events in createEventList.

This feature can be turned on and off in the config via the property
hidePrivate: true/false.
2016-11-30 21:09:57 +01:00

376 lines
12 KiB
JavaScript

/* global Module */
/* Magic Mirror
* Module: Calendar
*
* By Michael Teeuw http://michaelteeuw.nl
* MIT Licensed.
*/
Module.register("calendar",{
// Define module defaults
defaults: {
maximumEntries: 10, // Total Maximum Entries
maximumNumberOfDays: 365,
displaySymbol: true,
defaultSymbol: "calendar", // Fontawesome Symbol see http://fontawesome.io/cheatsheet/
displayRepeatingCountTitle: false,
defaultRepeatingCountTitle: '',
maxTitleLength: 25,
fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
animationSpeed: 2000,
fade: true,
urgency: 7,
timeFormat: "relative",
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 ": "",
"'s birthday": ""
},
},
// Define required scripts.
getStyles: function() {
return ["calendar.css", "font-awesome.css"];
},
// Define required scripts.
getScripts: function() {
return ["moment.js"];
},
// Define required translations.
getTranslations: function() {
// The translations for the defaut modules are defined in the core translation files.
// Therefor we can just return false. Otherwise we should have returned a dictionairy.
// If you're trying to build your own module including translations, check out the documentation.
return false;
},
// 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, calendar.user, calendar.pass);
}
this.calendarData = {};
this.loaded = false;
},
// Override socket notification handler.
socketNotificationReceived: function(notification, payload) {
if (notification === "CALENDAR_EVENTS") {
if (this.hasCalendarURL(payload.url)) {
this.calendarData[payload.url] = payload.events;
this.loaded = true;
}
} 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 = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING");
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"),
repeatingCountTitle = '';
if (this.config.displayRepeatingCountTitle) {
repeatingCountTitle = this.countTitleForUrl(event.url);
if(repeatingCountTitle !== '') {
var thisYear = new Date().getFullYear(),
yearDiff = thisYear - event.firstYear;
repeatingCountTitle = ', '+ yearDiff + '. ' + repeatingCountTitle;
}
}
titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle;
titleWrapper.className = "title bright";
eventWrapper.appendChild(titleWrapper);
var timeWrapper = document.createElement("td");
//console.log(event.today);
var now = new Date();
// Define second, minute, hour, and day variables
var one_second = 1000; // 1,000 milliseconds
var one_minute = one_second * 60;
var one_hour = one_minute * 60;
var one_day = one_hour * 24;
if (event.fullDayEvent) {
if (event.today) {
timeWrapper.innerHTML = this.translate("TODAY");
} else if (event.startDate - now < one_day && event.startDate - now > 0) {
timeWrapper.innerHTML = this.translate("TOMORROW");
} else if (event.startDate - now < 2*one_day && event.startDate - now > 0) {
/*Provide ability to show "the day after tomorrow" instead of "in a day"
*if "DAYAFTERTOMORROW" is configured in a language's translation .json file,
*,which can be found in MagicMirror/translations/
*/
if (this.translate('DAYAFTERTOMORROW') !== 'DAYAFTERTOMORROW') {
timeWrapper.innerHTML = this.translate("DAYAFTERTOMORROW");
} else {
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
}
} else {
/* Check to see if the user displays absolute or relative dates with their events
* Also check to see if an event is happening within an 'urgency' time frameElement
* For example, if the user set an .urgency of 7 days, those events that fall within that
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
*
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
*/
if (this.config.timeFormat === "absolute") {
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * one_day))) {
// This event falls within the config.urgency period that the user has set
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
} else {
timeWrapper.innerHTML = moment(event.startDate, "x").format("MMM Do");
}
} else {
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
}
}
} else {
if (event.startDate >= new Date()) {
if (event.startDate - now < 2 * one_day) {
// This event is within the next 48 hours (2 days)
if (event.startDate - now < 6 * one_hour) {
// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
} else {
// Otherwise just say 'Today/Tomorrow at such-n-such time'
timeWrapper.innerHTML = moment(event.startDate, "x").calendar();
}
} else {
/* Check to see if the user displays absolute or relative dates with their events
* Also check to see if an event is happening within an 'urgency' time frameElement
* For example, if the user set an .urgency of 7 days, those events that fall within that
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
*
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
*/
if (this.config.timeFormat === "absolute") {
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * one_day))) {
// This event falls within the config.urgency period that the user has set
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
} else {
timeWrapper.innerHTML = moment(event.startDate, "x").format("MMM Do");
}
} else {
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
}
}
} else {
timeWrapper.innerHTML = this.translate("RUNNING") + ' ' + moment(event.endDate,"x").fromNow(true);
}
}
//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
//console.log(event);
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 = [];
var today = moment().startOf("day");
for (var c in this.calendarData) {
var calendar = this.calendarData[c];
for (var e in calendar) {
var event = calendar[e];
if(this.config.hidePrivate) {
if(event.class === "PRIVATE") {
// do not add the current event, skip it
continue;
}
}
event.url = c;
event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
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, user, pass) {
this.sendSocketNotification("ADD_CALENDAR", {
url: url,
maximumEntries: this.config.maximumEntries,
maximumNumberOfDays: this.config.maximumNumberOfDays,
fetchInterval: this.config.fetchInterval,
user: user,
pass: pass
});
},
/* 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;
},
/* countTitleForUrl(url)
* Retrieves the name for a specific url.
*
* argument url sting - Url to look for.
*
* return string - The Symbol
*/
countTitleForUrl: function(url) {
for (var c in this.config.calendars) {
var calendar = this.config.calendars[c];
if (calendar.url === url && typeof calendar.repeatingCountTitle === "string") {
return calendar.repeatingCountTitle;
}
}
return this.config.defaultRepeatingCountTitle;
},
/* 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) + "&hellip;";
}
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;
}
});