mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-07-01 21:43:26 +00:00
User can now choose between an 'absolute' or 'relative time display on events. Furthermore, when set to 'absolute', there's an option to set an 'urgency' range of days and only those events within that period will be displayed with a relative time (in x days) while all others will have an absolute date.
342 lines
9.5 KiB
JavaScript
342 lines
9.5 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", // Fontawsome 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 ": ""
|
|
},
|
|
},
|
|
|
|
// Define required scripts.
|
|
getStyles: function() {
|
|
return ["calendar.css", "font-awesome.css"];
|
|
},
|
|
|
|
// Define required scripts.
|
|
getScripts: function() {
|
|
return ["moment.js"];
|
|
},
|
|
|
|
// Define required translations.
|
|
getTranslations: function() {
|
|
return {
|
|
en: "translations/en.json",
|
|
de: "translations/de.json",
|
|
nl: "translations/nl.json",
|
|
fr: "translations/fr.json"
|
|
};
|
|
},
|
|
|
|
// 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 = {};
|
|
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();
|
|
var one_hour = 1000 * 60 * 60;
|
|
var one_day = one_hour * 24;
|
|
if (event.fullDayEvent) {
|
|
if (event.today) {
|
|
timeWrapper.innerHTML = this.translate("TODAY");
|
|
} else if (event.startDate - now < 24 * one_hour && event.startDate - now > 0) {
|
|
timeWrapper.innerHTML = this.translate("TOMORROW");
|
|
} else {
|
|
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 time frame (in days) 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
|
|
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 {
|
|
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 time frame (in days) 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];
|
|
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) {
|
|
this.sendSocketNotification("ADD_CALENDAR", {
|
|
url: url,
|
|
maximumEntries: this.config.maximumEntries,
|
|
maximumNumberOfDays: this.config.maximumNumberOfDays,
|
|
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;
|
|
},
|
|
/* 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) + "…";
|
|
}
|
|
|
|
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;
|
|
}
|
|
});
|