Merge branch 'develop' into pr/wolfen351/2057

This commit is contained in:
Michael Teeuw
2020-07-04 21:15:14 +02:00
229 changed files with 5816 additions and 11620 deletions

View File

@@ -1,26 +1,27 @@
/* global Module */
/* global cloneObject */
/* Magic Mirror
* Module: Calendar
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://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/
defaultSymbol: "calendar", // Fontawesome Symbol see https://fontawesome.com/cheatsheet?from=io
showLocation: false,
displayRepeatingCountTitle: false,
defaultRepeatingCountTitle: "",
maxTitleLength: 25,
maxLocationTitleLength: 25,
wrapEvents: false, // wrap events to multiple lines breaking at maxTitleLength
wrapLocationEvents: false,
maxTitleLines: 3,
maxEventTitleLines: 3,
fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
animationSpeed: 2000,
fade: true,
@@ -40,13 +41,16 @@ Module.register("calendar", {
calendars: [
{
symbol: "calendar",
url: "http://www.calendarlabs.com/templates/ical/US-Holidays.ics",
},
url: "https://www.calendarlabs.com/templates/ical/US-Holidays.ics"
}
],
titleReplace: {
"De verjaardag van ": "",
"'s birthday": ""
},
locationTitleReplace: {
"street ": ""
},
broadcastEvents: true,
excludedEvents: [],
sliceMultiDayEvents: false,
@@ -86,7 +90,7 @@ Module.register("calendar", {
var calendarConfig = {
maximumEntries: calendar.maximumEntries,
maximumNumberOfDays: calendar.maximumNumberOfDays,
broadcastPastEvents: calendar.broadcastPastEvents,
broadcastPastEvents: calendar.broadcastPastEvents
};
if (calendar.symbolClass === "undefined" || calendar.symbolClass === null) {
calendarConfig.symbolClass = "";
@@ -99,7 +103,7 @@ Module.register("calendar", {
}
// we check user and password here for backwards compatibility with old configs
if(calendar.user && calendar.pass) {
if (calendar.user && calendar.pass) {
Log.warn("Deprecation warning: Please update your calendar authentication configuration.");
Log.warn("https://github.com/MichMich/MagicMirror/tree/v2.1.2/modules/default/calendar#calendar-authentication-options");
calendar.auth = {
@@ -113,7 +117,7 @@ Module.register("calendar", {
// Trigger ADD_CALENDAR every fetchInterval to make sure there is always a calendar
// fetcher running on the server side.
var self = this;
setInterval(function() {
setInterval(function () {
self.addCalendar(calendar.url, calendar.auth, calendarConfig);
}, self.config.fetchInterval);
}
@@ -124,6 +128,10 @@ Module.register("calendar", {
// Override socket notification handler.
socketNotificationReceived: function (notification, payload) {
if (this.identifier !== payload.id) {
return;
}
if (notification === "CALENDAR_EVENTS") {
if (this.hasCalendarURL(payload.url)) {
this.calendarData[payload.url] = payload.events;
@@ -145,13 +153,12 @@ Module.register("calendar", {
// Override dom generator.
getDom: function () {
var events = this.createEventList();
var wrapper = document.createElement("table");
wrapper.className = this.config.tableClass;
if (events.length === 0) {
wrapper.innerHTML = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING");
wrapper.innerHTML = this.loaded ? this.translate("EMPTY") : this.translate("LOADING");
wrapper.className = this.config.tableClass + " dimmed";
return wrapper;
}
@@ -170,8 +177,8 @@ Module.register("calendar", {
for (var e in events) {
var event = events[e];
var dateAsString = moment(event.startDate, "x").format(this.config.dateFormat);
if(this.config.timeFormat === "dateheaders"){
if(lastSeenDate !== dateAsString){
if (this.config.timeFormat === "dateheaders") {
if (lastSeenDate !== dateAsString) {
var dateRow = document.createElement("tr");
dateRow.className = "normal";
var dateCell = document.createElement("td");
@@ -182,9 +189,10 @@ Module.register("calendar", {
dateRow.appendChild(dateCell);
wrapper.appendChild(dateRow);
if (e >= startFade) { //fading
if (e >= startFade) {
//fading
currentFadeStep = e - startFade;
dateRow.style.opacity = 1 - (1 / fadeSteps * currentFadeStep);
dateRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
}
lastSeenDate = dateAsString;
@@ -210,20 +218,20 @@ Module.register("calendar", {
symbolWrapper.className = "symbol align-right " + symbolClass;
var symbols = this.symbolsForUrl(event.url);
if(typeof symbols === "string") {
if (typeof symbols === "string") {
symbols = [symbols];
}
for(var i = 0; i < symbols.length; i++) {
for (var i = 0; i < symbols.length; i++) {
var symbol = document.createElement("span");
symbol.className = "fa fa-fw fa-" + symbols[i];
if(i > 0){
if (i > 0) {
symbol.style.paddingLeft = "5px";
}
symbolWrapper.appendChild(symbol);
}
eventWrapper.appendChild(symbolWrapper);
} else if(this.config.timeFormat === "dateheaders"){
} else if (this.config.timeFormat === "dateheaders") {
var blankCell = document.createElement("td");
blankCell.innerHTML = "&nbsp;&nbsp;&nbsp;";
eventWrapper.appendChild(blankCell);
@@ -233,7 +241,6 @@ Module.register("calendar", {
repeatingCountTitle = "";
if (this.config.displayRepeatingCountTitle && event.firstYear !== undefined) {
repeatingCountTitle = this.countTitleForUrl(event.url);
if (repeatingCountTitle !== "") {
@@ -244,7 +251,7 @@ Module.register("calendar", {
}
}
titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle;
titleWrapper.innerHTML = this.titleTransform(event.title, this.config.titleReplace, this.config.wrapEvents, this.config.maxTitleLength, this.config.maxTitleLines) + repeatingCountTitle;
var titleClass = this.titleClassForUrl(event.url);
@@ -254,17 +261,15 @@ Module.register("calendar", {
titleWrapper.className = "title " + titleClass;
}
if(this.config.timeFormat === "dateheaders"){
var timeWrapper;
if (this.config.timeFormat === "dateheaders") {
if (event.fullDayEvent) {
titleWrapper.colSpan = "2";
titleWrapper.align = "left";
} else {
var timeClass = this.timeClassForUrl(event.url);
var timeWrapper = document.createElement("td");
timeWrapper.className = "time light " + timeClass;
timeWrapper = document.createElement("td");
timeWrapper.className = "time light " + this.timeClassForUrl(event.url);
timeWrapper.align = "left";
timeWrapper.style.paddingLeft = "2px";
timeWrapper.innerHTML = moment(event.startDate, "x").format("LT");
@@ -274,10 +279,9 @@ Module.register("calendar", {
eventWrapper.appendChild(titleWrapper);
} else {
var timeWrapper = document.createElement("td");
timeWrapper = document.createElement("td");
eventWrapper.appendChild(titleWrapper);
//console.log(event.today);
var now = new Date();
// Define second, minute, hour, and day variables
var oneSecond = 1000; // 1,000 milliseconds
@@ -299,14 +303,14 @@ Module.register("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
*/
* 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 * oneDay))) {
if (this.config.urgency > 1 && event.startDate - now < this.config.urgency * oneDay) {
// This event falls within the config.urgency period that the user has set
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
} else {
@@ -316,9 +320,9 @@ Module.register("calendar", {
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
}
}
if(this.config.showEnd){
timeWrapper.innerHTML += "-" ;
timeWrapper.innerHTML += this.capFirst(moment(event.endDate , "x").format(this.config.fullDayEventDateFormat));
if (this.config.showEnd) {
timeWrapper.innerHTML += "-";
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.fullDayEventDateFormat));
}
} else {
if (event.startDate >= new Date()) {
@@ -328,7 +332,7 @@ Module.register("calendar", {
// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
} else {
if(this.config.timeFormat === "absolute" && !this.config.nextDaysRelative) {
if (this.config.timeFormat === "absolute" && !this.config.nextDaysRelative) {
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
} else {
// Otherwise just say 'Today/Tomorrow at such-n-such time'
@@ -337,14 +341,14 @@ Module.register("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
*/
* 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 * oneDay))) {
if (this.config.urgency > 1 && event.startDate - now < this.config.urgency * oneDay) {
// This event falls within the config.urgency period that the user has set
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
} else {
@@ -365,13 +369,10 @@ Module.register("calendar", {
if (this.config.showEnd) {
timeWrapper.innerHTML += "-";
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat));
}
}
//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
//console.log(event);
var timeClass = this.timeClassForUrl(event.url);
timeWrapper.className = "time light " + timeClass;
timeWrapper.className = "time light " + this.timeClassForUrl(event.url);
eventWrapper.appendChild(timeWrapper);
}
@@ -380,7 +381,7 @@ Module.register("calendar", {
// Create fade effect.
if (e >= startFade) {
currentFadeStep = e - startFade;
eventWrapper.style.opacity = 1 - (1 / fadeSteps * currentFadeStep);
eventWrapper.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
}
if (this.config.showLocation) {
@@ -396,14 +397,14 @@ Module.register("calendar", {
var descCell = document.createElement("td");
descCell.className = "location";
descCell.colSpan = "2";
descCell.innerHTML = event.location;
descCell.innerHTML = this.titleTransform(event.location, this.config.locationTitleReplace, this.config.wrapLocationEvents, this.config.maxLocationTitleLength, this.config.maxEventTitleLines);
locationRow.appendChild(descCell);
wrapper.appendChild(locationRow);
if (e >= startFade) {
currentFadeStep = e - startFade;
locationRow.style.opacity = 1 - (1 / fadeSteps * currentFadeStep);
locationRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
}
}
}
@@ -420,20 +421,17 @@ Module.register("calendar", {
* @param {number} timeFormat Specifies either 12 or 24 hour time format
* @returns {moment.LocaleSpecification}
*/
getLocaleSpecification: function(timeFormat) {
getLocaleSpecification: function (timeFormat) {
switch (timeFormat) {
case 12: {
return { longDateFormat: {LT: "h:mm A"} };
break;
}
case 24: {
return { longDateFormat: {LT: "HH:mm"} };
break;
}
default: {
return { longDateFormat: {LT: moment.localeData().longDateFormat("LT")} };
break;
}
case 12: {
return { longDateFormat: { LT: "h:mm A" } };
}
case 24: {
return { longDateFormat: { LT: "HH:mm" } };
}
default: {
return { longDateFormat: { LT: moment.localeData().longDateFormat("LT") } };
}
}
},
@@ -471,41 +469,41 @@ Module.register("calendar", {
var event = JSON.parse(JSON.stringify(calendar[e])); // clone object
// correct data for the current timezone
var offset = - ((new Date()).getTimezoneOffset() * 60 * 1000);
event.startDate = (event.startDate - offset);
event.endDate = (event.endDate - offset);
if(event.endDate < now) {
var offset = -(new Date().getTimezoneOffset() * 60 * 1000);
event.startDate = event.startDate - offset;
event.endDate = event.endDate - offset;
if (event.endDate < now) {
continue;
}
if(this.config.hidePrivate) {
if(event.class === "PRIVATE") {
// do not add the current event, skip it
continue;
}
}
if(this.config.hideOngoing) {
if(event.startDate < now) {
if (this.config.hidePrivate) {
if (event.class === "PRIVATE") {
// do not add the current event, skip it
continue;
}
}
if(this.listContainsEvent(events,event)){
if (this.config.hideOngoing) {
if (event.startDate < now) {
continue;
}
}
if (this.listContainsEvent(events, event)) {
continue;
}
event.url = c;
event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
event.today = event.startDate >= today && event.startDate < today + 24 * 60 * 60 * 1000;
/* if sliceMultiDayEvents is set to true, multiday events (events exceeding at least one midnight) are sliced into days,
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
*/
var maxCount = Math.ceil(((event.endDate - 1) - moment(event.startDate, "x").endOf("day").format("x"))/(1000*60*60*24)) + 1;
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
*/
var maxCount = Math.ceil((event.endDate - 1 - moment(event.startDate, "x").endOf("day").format("x")) / (1000 * 60 * 60 * 24)) + 1;
if (this.config.sliceMultiDayEvents && maxCount > 1) {
var splitEvents = [];
var midnight = moment(event.startDate, "x").clone().startOf("day").add(1, "day").format("x");
var count = 1;
while (event.endDate > midnight) {
var thisEvent = JSON.parse(JSON.stringify(event)); // clone object
thisEvent.today = thisEvent.startDate >= today && thisEvent.startDate < (today + 24 * 60 * 60 * 1000);
thisEvent.today = thisEvent.startDate >= today && thisEvent.startDate < today + 24 * 60 * 60 * 1000;
thisEvent.endDate = midnight;
thisEvent.title += " (" + count + "/" + maxCount + ")";
splitEvents.push(thisEvent);
@@ -515,11 +513,11 @@ Module.register("calendar", {
midnight = moment(midnight, "x").add(1, "day").format("x"); // next day
}
// Last day
event.title += " ("+count+"/"+maxCount+")";
event.title += " (" + count + "/" + maxCount + ")";
splitEvents.push(event);
for (event of splitEvents) {
if ((event.endDate > now) && (event.endDate <= future)) {
if (event.endDate > now && event.endDate <= future) {
events.push(event);
}
}
@@ -535,9 +533,9 @@ Module.register("calendar", {
return events.slice(0, this.config.maximumEntries);
},
listContainsEvent: function(eventList, event){
for(var evt of eventList){
if(evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate)){
listContainsEvent: function (eventList, event) {
for (var evt of eventList) {
if (evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate)) {
return true;
}
}
@@ -551,6 +549,7 @@ Module.register("calendar", {
*/
addCalendar: function (url, auth, calendarConfig) {
this.sendSocketNotification("ADD_CALENDAR", {
id: this.identifier,
url: url,
excludedEvents: calendarConfig.excludedEvents || this.config.excludedEvents,
maximumEntries: calendarConfig.maximumEntries || this.config.maximumEntries,
@@ -560,7 +559,7 @@ Module.register("calendar", {
titleClass: calendarConfig.titleClass,
timeClass: calendarConfig.timeClass,
auth: auth,
broadcastPastEvents: calendarConfig.broadcastPastEvents || this.config.broadcastPastEvents,
broadcastPastEvents: calendarConfig.broadcastPastEvents || this.config.broadcastPastEvents
});
},
@@ -687,8 +686,9 @@ Module.register("calendar", {
for (var i = 0; i < words.length; i++) {
var word = words[i];
if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) { // max - 1 to account for a space
currentLine += (word + " ");
if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) {
// max - 1 to account for a space
currentLine += word + " ";
} else {
line++;
if (line > maxTitleLines - 1) {
@@ -699,9 +699,9 @@ Module.register("calendar", {
}
if (currentLine.length > 0) {
temp += (currentLine + "<br>" + word + " ");
temp += currentLine + "<br>" + word + " ";
} else {
temp += (word + "<br>");
temp += word + "<br>";
}
currentLine = "";
}
@@ -734,20 +734,20 @@ Module.register("calendar", {
*
* return string - The transformed title.
*/
titleTransform: function (title) {
for (var needle in this.config.titleReplace) {
var replacement = this.config.titleReplace[needle];
titleTransform: function (title, titleReplace, wrapEvents, maxTitleLength, maxTitleLines) {
for (var needle in titleReplace) {
var replacement = titleReplace[needle];
var regParts = needle.match(/^\/(.+)\/([gim]*)$/);
if (regParts) {
// the parsed pattern is a regexp.
needle = new RegExp(regParts[1], regParts[2]);
// the parsed pattern is a regexp.
needle = new RegExp(regParts[1], regParts[2]);
}
title = title.replace(needle, replacement);
}
title = this.shorten(title, this.config.maxTitleLength, this.config.wrapEvents, this.config.maxTitleLines);
title = this.shorten(title, maxTitleLength, wrapEvents, maxTitleLines);
return title;
},
@@ -769,11 +769,10 @@ Module.register("calendar", {
}
}
eventList.sort(function(a,b) {
eventList.sort(function (a, b) {
return a.startDate - b.startDate;
});
this.sendNotification("CALENDAR_EVENTS", eventList);
}
});