From 0b37ed072cd5e7b9d8ec66a21a40ef32c127d591 Mon Sep 17 00:00:00 2001 From: rejas Date: Wed, 23 Dec 2020 12:45:03 +0100 Subject: [PATCH 01/13] Refactor fetcher methods into util class --- modules/default/calendar/calendarfetcher.js | 212 ++----------------- modules/default/calendar/calendarutils.js | 215 ++++++++++++++++++++ 2 files changed, 228 insertions(+), 199 deletions(-) create mode 100644 modules/default/calendar/calendarutils.js diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 31ba5022..0ffd3172 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -4,6 +4,7 @@ * By Michael Teeuw https://michaelteeuw.nl * MIT Licensed. */ +const CalendarUtils = require("./calendarutils"); const Log = require("logger"); const ical = require("node-ical"); const fetch = require("node-fetch"); @@ -104,7 +105,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn }; const eventDate = function (event, time) { - return isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time])); + return CalendarUtils.isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time])); }; Log.debug("there are " + Object.entries(data).length + " calendar entries"); Object.entries(data).forEach(([key, event]) => { @@ -153,7 +154,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn startDate = startDate.startOf("day"); } - const title = getTitleFromEvent(event); + const title = CalendarUtils.getTitleFromEvent(event); let excluded = false, dateFilter = null; @@ -191,7 +192,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn filter = filter.toLowerCase(); } - if (testTitleByFilter(testTitle, filter, useRegex, regexFlags)) { + if (CalendarUtils.testTitleByFilter(testTitle, filter, useRegex, regexFlags)) { if (until) { dateFilter = until; } else { @@ -227,7 +228,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn // kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time let pastLocal = 0; let futureLocal = 0; - if (isFullDayEvent(event)) { + if (CalendarUtils.isFullDayEvent(event)) { // if full day event, only use the date part of the ranges pastLocal = pastMoment.toDate(); futureLocal = futureMoment.toDate(); @@ -272,7 +273,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn let showRecurrence = true; // for full day events, the time might be off from RRULE/Luxon problem - if (isFullDayEvent(event)) { + if (CalendarUtils.isFullDayEvent(event)) { Log.debug("fullday"); // if the offset is negative, east of GMT where the problem is if (date.getTimezoneOffset() < 0) { @@ -292,7 +293,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn } startDate = moment(date); - let adjustDays = getCorrection(event, date); + let adjustDays = CalendarUtils.calculateTimezoneAdjustment(event, date); // For each date that we're checking, it's possible that there is a recurrence override for that one day. if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { @@ -313,7 +314,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn endDate = endDate.endOf("day"); } - const recurrenceTitle = getTitleFromEvent(curEvent); + const recurrenceTitle = CalendarUtils.getTitleFromEvent(curEvent); // If this recurrence ends before the start of the date range, or starts after the end of the date range, don"t add // it to the event list. @@ -321,7 +322,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn showRecurrence = false; } - if (timeFilterApplies(now, endDate, dateFilter)) { + if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { showRecurrence = false; } @@ -332,7 +333,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn title: recurrenceTitle, startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), - fullDayEvent: isFullDayEvent(event), + fullDayEvent: CalendarUtils.isFullDayEvent(event), recurringEvent: true, class: event.class, firstYear: event.start.getFullYear(), @@ -345,7 +346,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn // end recurring event parsing } else { // Single event. - const fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event); + const fullDayEvent = isFacebookBirthday ? true : CalendarUtils.isFullDayEvent(event); // Log.debug("full day event") if (includePastEvents) { @@ -370,7 +371,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn return; } - if (timeFilterApplies(now, endDate, dateFilter)) { + if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { return; } @@ -383,7 +384,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn endDate = endDate.endOf("day"); } // get correction for date saving and dst change between now and then - let adjustDays = getCorrection(event, startDate.toDate()); + let adjustDays = CalendarUtils.getCorrection(event, startDate.toDate()); // Every thing is good. Add it to the list. newEvents.push({ title: title, @@ -424,117 +425,6 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn }); }; - /* - * - * get the time correction, either dst/std or full day in cases where utc time is day before plus offset - * - */ - const getCorrection = function (event, date) { - let adjustHours = 0; - // if a timezone was specified - if (!event.start.tz) { - Log.debug(" if no tz, guess based on now"); - event.start.tz = moment.tz.guess(); - } - Log.debug("initial tz=" + event.start.tz); - - // if there is a start date specified - if (event.start.tz) { - // if this is a windows timezone - if (event.start.tz.includes(" ")) { - // use the lookup table to get theIANA name as moment and date don't know MS timezones - let tz = getIanaTZFromMS(event.start.tz); - Log.debug("corrected TZ=" + tz); - // watch out for unregistered windows timezone names - // if we had a successfule lookup - if (tz) { - // change the timezone to the IANA name - event.start.tz = tz; - // Log.debug("corrected timezone="+event.start.tz) - } - } - Log.debug("corrected tz=" + event.start.tz); - let current_offset = 0; // offset from TZ string or calculated - let mm = 0; // date with tz or offset - let start_offset = 0; // utc offset of created with tz - // if there is still an offset, lookup failed, use it - if (event.start.tz.startsWith("(")) { - const regex = /[+|-]\d*:\d*/; - const start_offsetString = event.start.tz.match(regex).toString().split(":"); - let start_offset = parseInt(start_offsetString[0]); - start_offset *= event.start.tz[1] === "-" ? -1 : 1; - adjustHours = start_offset; - Log.debug("defined offset=" + start_offset + " hours"); - current_offset = start_offset; - event.start.tz = ""; - Log.debug("ical offset=" + current_offset + " date=" + date); - mm = moment(date); - let x = parseInt(moment(new Date()).utcOffset()); - Log.debug("net mins=" + (current_offset * 60 - x)); - - mm = mm.add(x - current_offset * 60, "minutes"); - adjustHours = (current_offset * 60 - x) / 60; - event.start = mm.toDate(); - Log.debug("adjusted date=" + event.start); - } else { - // get the start time in that timezone - Log.debug("start date/time=" + moment(event.start).toDate()); - start_offset = moment.tz(moment(event.start), event.start.tz).utcOffset(); - Log.debug("start offset=" + start_offset); - - Log.debug("start date/time w tz =" + moment.tz(moment(event.start), event.start.tz).toDate()); - - // get the specified date in that timezone - mm = moment.tz(moment(date), event.start.tz); - Log.debug("event date=" + mm.toDate()); - current_offset = mm.utcOffset(); - } - Log.debug("event offset=" + current_offset + " hour=" + mm.format("H") + " event date=" + mm.toDate()); - - // if the offset is greater than 0, east of london - if (current_offset !== start_offset) { - // big offset - Log.debug("offset"); - let h = parseInt(mm.format("H")); - // check if the event time is less than the offset - if (h > 0 && h < Math.abs(current_offset) / 60) { - // if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time) - // we need to fix that - adjustHours = 24; - // Log.debug("adjusting date") - } - //-300 > -240 - //if (Math.abs(current_offset) > Math.abs(start_offset)){ - if (current_offset > start_offset) { - adjustHours -= 1; - Log.debug("adjust down 1 hour dst change"); - //} else if (Math.abs(current_offset) < Math.abs(start_offset)) { - } else if (current_offset < start_offset) { - adjustHours += 1; - Log.debug("adjust up 1 hour dst change"); - } - } - } - Log.debug("adjustHours=" + adjustHours); - return adjustHours; - }; - - /** - * - * lookup iana tz from windows - */ - let zoneTable = null; - const getIanaTZFromMS = function (msTZName) { - if (!zoneTable) { - const p = require("path"); - zoneTable = require(p.join(__dirname, "windowsZones.json")); - } - // Get hash entry - const he = zoneTable[msTZName]; - // If found return iana name, else null - return he ? he.iana[0] : null; - }; - /** * Schedule the timer for the next update. */ @@ -545,82 +435,6 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn }, reloadInterval); }; - /** - * Checks if an event is a fullday event. - * - * @param {object} event The event object to check. - * @returns {boolean} True if the event is a fullday event, false otherwise - */ - const isFullDayEvent = function (event) { - if (event.start.length === 8 || event.start.dateOnly || event.datetype === "date") { - return true; - } - - const start = event.start || 0; - const startDate = new Date(start); - const end = event.end || 0; - if ((end - start) % (24 * 60 * 60 * 1000) === 0 && startDate.getHours() === 0 && startDate.getMinutes() === 0) { - // Is 24 hours, and starts on the middle of the night. - return true; - } - - return false; - }; - - /** - * Determines if the user defined time filter should apply - * - * @param {Date} now Date object using previously created object for consistency - * @param {Moment} endDate Moment object representing the event end date - * @param {string} filter The time to subtract from the end date to determine if an event should be shown - * @returns {boolean} True if the event should be filtered out, false otherwise - */ - const timeFilterApplies = function (now, endDate, filter) { - if (filter) { - const until = filter.split(" "), - value = parseInt(until[0]), - increment = until[1].slice(-1) === "s" ? until[1] : until[1] + "s", // Massage the data for moment js - filterUntil = moment(endDate.format()).subtract(value, increment); - - return now < filterUntil.format("x"); - } - - return false; - }; - - /** - * Gets the title from the event. - * - * @param {object} event The event object to check. - * @returns {string} The title of the event, or "Event" if no title is found. - */ - const getTitleFromEvent = function (event) { - let title = "Event"; - if (event.summary) { - title = typeof event.summary.val !== "undefined" ? event.summary.val : event.summary; - } else if (event.description) { - title = event.description; - } - - return title; - }; - - const testTitleByFilter = function (title, filter, useRegex, regexFlags) { - if (useRegex) { - // Assume if leading slash, there is also trailing slash - if (filter[0] === "/") { - // Strip leading and trailing slashes - filter = filter.substr(1).slice(0, -1); - } - - filter = new RegExp(filter, regexFlags); - - return filter.test(title); - } else { - return title.includes(filter); - } - }; - /* public methods */ /** diff --git a/modules/default/calendar/calendarutils.js b/modules/default/calendar/calendarutils.js new file mode 100644 index 00000000..f57f342f --- /dev/null +++ b/modules/default/calendar/calendarutils.js @@ -0,0 +1,215 @@ +/* Magic Mirror + * Calendar Util Methods + * + * By Michael Teeuw https://michaelteeuw.nl + * MIT Licensed. + */ + +/** + * @external Moment + */ +const moment = require("moment"); +const path = require("path"); +const zoneTable = require(path.join(__dirname, "windowsZones.json")); +const Log = require("../../../js/logger.js"); + +const CalendarUtils = { + /** + * Calculate the time correction, either dst/std or full day in cases where + * utc time is day before plus offset + * + * @param event + * @param date + * @returns {number} the necessary adjustment in hours + */ + calculateTimezoneAdjustment: function (event, date) { + let adjustHours = 0; + // if a timezone was specified + if (!event.start.tz) { + Log.debug(" if no tz, guess based on now"); + event.start.tz = moment.tz.guess(); + } + Log.debug("initial tz=" + event.start.tz); + + // if there is a start date specified + if (event.start.tz) { + // if this is a windows timezone + if (event.start.tz.includes(" ")) { + // use the lookup table to get theIANA name as moment and date don't know MS timezones + let tz = CalendarUtils.getIanaTZFromMS(event.start.tz); + Log.debug("corrected TZ=" + tz); + // watch out for unregistered windows timezone names + // if we had a successful lookup + if (tz) { + // change the timezone to the IANA name + event.start.tz = tz; + // Log.debug("corrected timezone="+event.start.tz) + } + } + Log.debug("corrected tz=" + event.start.tz); + let current_offset = 0; // offset from TZ string or calculated + let mm = 0; // date with tz or offset + let start_offset = 0; // utc offset of created with tz + // if there is still an offset, lookup failed, use it + if (event.start.tz.startsWith("(")) { + const regex = /[+|-]\d*:\d*/; + const start_offsetString = event.start.tz.match(regex).toString().split(":"); + let start_offset = parseInt(start_offsetString[0]); + start_offset *= event.start.tz[1] === "-" ? -1 : 1; + adjustHours = start_offset; + Log.debug("defined offset=" + start_offset + " hours"); + current_offset = start_offset; + event.start.tz = ""; + Log.debug("ical offset=" + current_offset + " date=" + date); + mm = moment(date); + let x = parseInt(moment(new Date()).utcOffset()); + Log.debug("net mins=" + (current_offset * 60 - x)); + + mm = mm.add(x - current_offset * 60, "minutes"); + adjustHours = (current_offset * 60 - x) / 60; + event.start = mm.toDate(); + Log.debug("adjusted date=" + event.start); + } else { + // get the start time in that timezone + Log.debug("start date/time=" + moment(event.start).toDate()); + start_offset = moment.tz(moment(event.start), event.start.tz).utcOffset(); + Log.debug("start offset=" + start_offset); + + Log.debug("start date/time w tz =" + moment.tz(moment(event.start), event.start.tz).toDate()); + + // get the specified date in that timezone + mm = moment.tz(moment(date), event.start.tz); + Log.debug("event date=" + mm.toDate()); + current_offset = mm.utcOffset(); + } + Log.debug("event offset=" + current_offset + " hour=" + mm.format("H") + " event date=" + mm.toDate()); + + // if the offset is greater than 0, east of london + if (current_offset !== start_offset) { + // big offset + Log.debug("offset"); + let h = parseInt(mm.format("H")); + // check if the event time is less than the offset + if (h > 0 && h < Math.abs(current_offset) / 60) { + // if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time) + // we need to fix that + adjustHours = 24; + // Log.debug("adjusting date") + } + //-300 > -240 + //if (Math.abs(current_offset) > Math.abs(start_offset)){ + if (current_offset > start_offset) { + adjustHours -= 1; + Log.debug("adjust down 1 hour dst change"); + //} else if (Math.abs(current_offset) < Math.abs(start_offset)) { + } else if (current_offset < start_offset) { + adjustHours += 1; + Log.debug("adjust up 1 hour dst change"); + } + } + } + Log.debug("adjustHours=" + adjustHours); + return adjustHours; + }, + + /** + * Lookup iana tz from windows + * + * @param msTZName + * @returns {*|null} + */ + getIanaTZFromMS: function (msTZName) { + // Get hash entry + const he = zoneTable[msTZName]; + // If found return iana name, else null + return he ? he.iana[0] : null; + }, + + /** + * Gets the title from the event. + * + * @param {object} event The event object to check. + * @returns {string} The title of the event, or "Event" if no title is found. + */ + getTitleFromEvent: function (event) { + let title = "Event"; + if (event.summary) { + title = typeof event.summary.val !== "undefined" ? event.summary.val : event.summary; + } else if (event.description) { + title = event.description; + } + + return title; + }, + + /** + * Checks if an event is a fullday event. + * + * @param {object} event The event object to check. + * @returns {boolean} True if the event is a fullday event, false otherwise + */ + isFullDayEvent: function (event) { + if (event.start.length === 8 || event.start.dateOnly || event.datetype === "date") { + return true; + } + + const start = event.start || 0; + const startDate = new Date(start); + const end = event.end || 0; + if ((end - start) % (24 * 60 * 60 * 1000) === 0 && startDate.getHours() === 0 && startDate.getMinutes() === 0) { + // Is 24 hours, and starts on the middle of the night. + return true; + } + + return false; + }, + + /** + * Determines if the user defined time filter should apply + * + * @param {Date} now Date object using previously created object for consistency + * @param {Moment} endDate Moment object representing the event end date + * @param {string} filter The time to subtract from the end date to determine if an event should be shown + * @returns {boolean} True if the event should be filtered out, false otherwise + */ + timeFilterApplies: function (now, endDate, filter) { + if (filter) { + const until = filter.split(" "), + value = parseInt(until[0]), + increment = until[1].slice(-1) === "s" ? until[1] : until[1] + "s", // Massage the data for moment js + filterUntil = moment(endDate.format()).subtract(value, increment); + + return now < filterUntil.format("x"); + } + + return false; + }, + + /** + * + * @param title + * @param filter + * @param useRegex + * @param regexFlags + * @returns {boolean|*} + */ + titleFilterApplies: function (title, filter, useRegex, regexFlags) { + if (useRegex) { + // Assume if leading slash, there is also trailing slash + if (filter[0] === "/") { + // Strip leading and trailing slashes + filter = filter.substr(1).slice(0, -1); + } + + filter = new RegExp(filter, regexFlags); + + return filter.test(title); + } else { + return title.includes(filter); + } + } +}; + +if (typeof module !== "undefined") { + module.exports = CalendarUtils; +} From bc60ae21c400d358b43c8fa644de080b1dc4dc4d Mon Sep 17 00:00:00 2001 From: rejas Date: Wed, 23 Dec 2020 20:15:04 +0100 Subject: [PATCH 02/13] Cleanup node_helper to look more like the one from newsfeed module --- modules/default/calendar/node_helper.js | 37 +++++++++++++++---------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/modules/default/calendar/node_helper.js b/modules/default/calendar/node_helper.js index e1813737..66e423db 100644 --- a/modules/default/calendar/node_helper.js +++ b/modules/default/calendar/node_helper.js @@ -38,41 +38,48 @@ module.exports = NodeHelper.create({ * @param {string} identifier ID of the module */ createFetcher: function (url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents, selfSignedCert, identifier) { - var self = this; - if (!validUrl.isUri(url)) { - self.sendSocketNotification("INCORRECT_URL", { id: identifier, url: url }); + this.sendSocketNotification("INCORRECT_URL", { id: identifier, url: url }); return; } - var fetcher; - if (typeof self.fetchers[identifier + url] === "undefined") { + let fetcher; + if (typeof this.fetchers[identifier + url] === "undefined") { Log.log("Create new calendar fetcher for url: " + url + " - Interval: " + fetchInterval); fetcher = new CalendarFetcher(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents, selfSignedCert); - fetcher.onReceive(function (fetcher) { - self.sendSocketNotification("CALENDAR_EVENTS", { - id: identifier, - url: fetcher.url(), - events: fetcher.events() - }); + fetcher.onReceive((fetcher) => { + this.broadcastEvents(fetcher, identifier); }); - fetcher.onError(function (fetcher, error) { + fetcher.onError((fetcher, error) => { Log.error("Calendar Error. Could not fetch calendar: ", fetcher.url(), error); - self.sendSocketNotification("FETCH_ERROR", { + this.sendSocketNotification("FETCH_ERROR", { id: identifier, url: fetcher.url(), error: error }); }); - self.fetchers[identifier + url] = fetcher; + this.fetchers[identifier + url] = fetcher; } else { Log.log("Use existing calendar fetcher for url: " + url); - fetcher = self.fetchers[identifier + url]; + fetcher = this.fetchers[identifier + url]; + fetcher.setReloadInterval(fetchInterval); + fetcher.broadcastEvents(); } fetcher.startFetch(); + }, + + /** + * + */ + broadcastEvents: function (fetcher, identifier) { + this.sendSocketNotification("CALENDAR_EVENTS", { + id: identifier, + url: fetcher.url(), + events: fetcher.events() + }); } }); From 9b93066cbeccc0f6b439a31e13c5eea8e1b3198e Mon Sep 17 00:00:00 2001 From: rejas Date: Thu, 24 Dec 2020 00:56:21 +0100 Subject: [PATCH 03/13] Remove unused variable --- modules/default/calendar/calendar.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 2cce00c7..2e7887f6 100755 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -595,8 +595,6 @@ Module.register("calendar", { * @param {object} calendarConfig The config of the specific calendar */ addCalendar: function (url, auth, calendarConfig) { - var self = this; - this.sendSocketNotification("ADD_CALENDAR", { id: this.identifier, url: url, From 0ac5032db91b6997a4a609427d1a4450840a555f Mon Sep 17 00:00:00 2001 From: rejas Date: Fri, 25 Dec 2020 12:59:08 +0100 Subject: [PATCH 04/13] Move filter function into seperate method --- modules/default/calendar/calendarfetcher.js | 590 ++++++++++---------- 1 file changed, 296 insertions(+), 294 deletions(-) diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 0ffd3172..9a18a1fb 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -90,339 +90,341 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn try { data = ical.parseICS(responseData); + Log.debug("parsed data=" + JSON.stringify(data)); + filterEvents(data); } catch (error) { fetchFailedCallback(self, error.message); scheduleTimer(); + return; + } + self.broadcastEvents(); + scheduleTimer(); + }); + }; + + const filterEvents = function (data) { + const newEvents = []; + + // limitFunction doesn't do much limiting, see comment re: the dates array in rrule section below as to why we need to do the filtering ourselves + const limitFunction = function (date, i) { + return true; + }; + + const eventDate = function (event, time) { + return CalendarUtils.isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time])); + }; + Log.debug("there are " + Object.entries(data).length + " calendar entries"); + Object.entries(data).forEach(([key, event]) => { + const now = new Date(); + const today = moment().startOf("day").toDate(); + const 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. + let past = today; + Log.debug("have entries "); + if (includePastEvents) { + past = moment().startOf("day").subtract(maximumNumberOfDays, "days").toDate(); + } + + // FIXME: Ugly fix to solve the facebook birthday issue. + // Otherwise, the recurring events only show the birthday for next year. + let isFacebookBirthday = false; + if (typeof event.uid !== "undefined") { + if (event.uid.indexOf("@facebook.com") !== -1) { + isFacebookBirthday = true; + } + } + + if (event.type === "VEVENT") { + let startDate = eventDate(event, "start"); + let endDate; + + Log.debug("\nevent=" + JSON.stringify(event)); + if (typeof event.end !== "undefined") { + endDate = eventDate(event, "end"); + } else if (typeof event.duration !== "undefined") { + endDate = startDate.clone().add(moment.duration(event.duration)); + } else { + if (!isFacebookBirthday) { + // make copy of start date, separate storage area + endDate = moment(startDate.format("x"), "x"); + } else { + endDate = moment(startDate).add(1, "days"); + } } - Log.debug(" parsed data=" + JSON.stringify(data)); + Log.debug(" start=" + startDate.toDate() + " end=" + endDate.toDate()); - const newEvents = []; + // calculate the duration of the event for use with recurring events. + let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x")); - // limitFunction doesn't do much limiting, see comment re: the dates array in rrule section below as to why we need to do the filtering ourselves - const limitFunction = function (date, i) { - return true; - }; + if (event.start.length === 8) { + startDate = startDate.startOf("day"); + } - const eventDate = function (event, time) { - return CalendarUtils.isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time])); - }; - Log.debug("there are " + Object.entries(data).length + " calendar entries"); - Object.entries(data).forEach(([key, event]) => { - const now = new Date(); - const today = moment().startOf("day").toDate(); - const 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. - let past = today; - Log.debug("have entries "); - if (includePastEvents) { - past = moment().startOf("day").subtract(maximumNumberOfDays, "days").toDate(); + const title = CalendarUtils.getTitleFromEvent(event); + + let excluded = false, + dateFilter = null; + + for (let f in excludedEvents) { + let filter = excludedEvents[f], + testTitle = title.toLowerCase(), + until = null, + useRegex = false, + regexFlags = "g"; + + if (filter instanceof Object) { + if (typeof filter.until !== "undefined") { + until = filter.until; + } + + if (typeof filter.regex !== "undefined") { + useRegex = filter.regex; + } + + // If additional advanced filtering is added in, this section + // must remain last as we overwrite the filter object with the + // filterBy string + if (filter.caseSensitive) { + filter = filter.filterBy; + testTitle = title; + } else if (useRegex) { + filter = filter.filterBy; + testTitle = title; + regexFlags += "i"; + } else { + filter = filter.filterBy.toLowerCase(); + } + } else { + filter = filter.toLowerCase(); } - // FIXME: Ugly fix to solve the facebook birthday issue. - // Otherwise, the recurring events only show the birthday for next year. - let isFacebookBirthday = false; - if (typeof event.uid !== "undefined") { - if (event.uid.indexOf("@facebook.com") !== -1) { - isFacebookBirthday = true; + if (CalendarUtils.testTitleByFilter(testTitle, filter, useRegex, regexFlags)) { + if (until) { + dateFilter = until; + } else { + excluded = true; } + break; + } + } + + if (excluded) { + return; + } + + const location = event.location || false; + const geo = event.geo || false; + const description = event.description || false; + + if (typeof event.rrule !== "undefined" && event.rrule !== null && !isFacebookBirthday) { + const rule = event.rrule; + let addedEvents = 0; + + const pastMoment = moment(past); + const futureMoment = moment(future); + + // can cause problems with e.g. birthdays before 1900 + if ((rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900) || (rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900)) { + rule.origOptions.dtstart.setYear(1900); + rule.options.dtstart.setYear(1900); } - if (event.type === "VEVENT") { - let startDate = eventDate(event, "start"); - let endDate; - - Log.debug("\nevent=" + JSON.stringify(event)); - if (typeof event.end !== "undefined") { - endDate = eventDate(event, "end"); - } else if (typeof event.duration !== "undefined") { - endDate = startDate.clone().add(moment.duration(event.duration)); + // For recurring events, get the set of start dates that fall within the range + // of dates we're looking for. + // kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time + let pastLocal = 0; + let futureLocal = 0; + if (CalendarUtils.isFullDayEvent(event)) { + // if full day event, only use the date part of the ranges + pastLocal = pastMoment.toDate(); + futureLocal = futureMoment.toDate(); + } else { + // if we want past events + if (includePastEvents) { + // use the calculated past time for the between from + pastLocal = pastMoment.toDate(); } else { - if (!isFacebookBirthday) { - // make copy of start date, separate storage area - endDate = moment(startDate.format("x"), "x"); - } else { - endDate = moment(startDate).add(1, "days"); + // otherwise use NOW.. cause we shouldnt use any before now + pastLocal = moment().toDate(); //now + } + futureLocal = futureMoment.toDate(); // future + } + Log.debug(" between=" + pastLocal + " to " + futureLocal); + const dates = rule.between(pastLocal, futureLocal, true, limitFunction); + Log.debug("title=" + event.summary + " dates=" + JSON.stringify(dates)); + // The "dates" array contains the set of dates within our desired date range range that are valid + // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that + // had its date changed from outside the range to inside the range. For the time being, + // we'll handle this by adding *all* recurrence entries into the set of dates that we check, + // because the logic below will filter out any recurrences that don't actually belong within + // our display range. + // Would be great if there was a better way to handle this. + if (event.recurrences !== undefined) { + for (let r in event.recurrences) { + // Only add dates that weren't already in the range we added from the rrule so that + // we don"t double-add those events. + if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) { + dates.push(new Date(r)); } } + } + // Loop through the set of date entries to see which recurrences should be added to our event list. + for (let d in dates) { + let date = dates[d]; + // ical.js started returning recurrences and exdates as ISOStrings without time information. + // .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same + // (see https://github.com/peterbraden/ical.js/pull/84 ) + const dateKey = date.toISOString().substring(0, 10); + let curEvent = event; + let showRecurrence = true; - Log.debug(" start=" + startDate.toDate() + " end=" + endDate.toDate()); - - // calculate the duration of the event for use with recurring events. - let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x")); - - if (event.start.length === 8) { - startDate = startDate.startOf("day"); - } - - const title = CalendarUtils.getTitleFromEvent(event); - - let excluded = false, - dateFilter = null; - - for (let f in excludedEvents) { - let filter = excludedEvents[f], - testTitle = title.toLowerCase(), - until = null, - useRegex = false, - regexFlags = "g"; - - if (filter instanceof Object) { - if (typeof filter.until !== "undefined") { - until = filter.until; - } - - if (typeof filter.regex !== "undefined") { - useRegex = filter.regex; - } - - // If additional advanced filtering is added in, this section - // must remain last as we overwrite the filter object with the - // filterBy string - if (filter.caseSensitive) { - filter = filter.filterBy; - testTitle = title; - } else if (useRegex) { - filter = filter.filterBy; - testTitle = title; - regexFlags += "i"; - } else { - filter = filter.filterBy.toLowerCase(); - } - } else { - filter = filter.toLowerCase(); - } - - if (CalendarUtils.testTitleByFilter(testTitle, filter, useRegex, regexFlags)) { - if (until) { - dateFilter = until; - } else { - excluded = true; - } - break; + // for full day events, the time might be off from RRULE/Luxon problem + if (CalendarUtils.isFullDayEvent(event)) { + Log.debug("fullday"); + // if the offset is negative, east of GMT where the problem is + if (date.getTimezoneOffset() < 0) { + // get the offset of today where we are processing + // this will be the correction we need to apply + let nowOffset = new Date().getTimezoneOffset(); + Log.debug("now offset is " + nowOffset); + // reduce the time by the offset + Log.debug(" recurring date is " + date + " offset is " + date.getTimezoneOffset()); + // apply the correction to the date/time to get it UTC relative + date = new Date(date.getTime() - Math.abs(nowOffset) * 60000); + // the duration was calculated way back at the top before we could correct the start time.. + // fix it for this event entry + duration = 24 * 60 * 60 * 1000; + Log.debug("new recurring date is " + date); } } + startDate = moment(date); - if (excluded) { - return; + let adjustDays = CalendarUtils.calculateTimezoneAdjustment(event, date); + + // For each date that we're checking, it's possible that there is a recurrence override for that one day. + if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { + // We found an override, so for this recurrence, use a potentially different title, start date, and duration. + curEvent = curEvent.recurrences[dateKey]; + startDate = moment(curEvent.start); + duration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x")); + } + // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. + else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) { + // This date is an exception date, which means we should skip it in the recurrence pattern. + showRecurrence = false; + } + Log.debug("duration=" + duration); + + endDate = moment(parseInt(startDate.format("x")) + duration, "x"); + if (startDate.format("x") === endDate.format("x")) { + endDate = endDate.endOf("day"); } - const location = event.location || false; - const geo = event.geo || false; - const description = event.description || false; + const recurrenceTitle = CalendarUtils.getTitleFromEvent(curEvent); - if (typeof event.rrule !== "undefined" && event.rrule !== null && !isFacebookBirthday) { - const rule = event.rrule; - let addedEvents = 0; + // If this recurrence ends before the start of the date range, or starts after the end of the date range, don"t add + // it to the event list. + if (endDate.isBefore(past) || startDate.isAfter(future)) { + showRecurrence = false; + } - const pastMoment = moment(past); - const futureMoment = moment(future); + if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { + showRecurrence = false; + } - // can cause problems with e.g. birthdays before 1900 - if ((rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900) || (rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900)) { - rule.origOptions.dtstart.setYear(1900); - rule.options.dtstart.setYear(1900); - } - - // For recurring events, get the set of start dates that fall within the range - // of dates we're looking for. - // kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time - let pastLocal = 0; - let futureLocal = 0; - if (CalendarUtils.isFullDayEvent(event)) { - // if full day event, only use the date part of the ranges - pastLocal = pastMoment.toDate(); - futureLocal = futureMoment.toDate(); - } else { - // if we want past events - if (includePastEvents) { - // use the calculated past time for the between from - pastLocal = pastMoment.toDate(); - } else { - // otherwise use NOW.. cause we shouldnt use any before now - pastLocal = moment().toDate(); //now - } - futureLocal = futureMoment.toDate(); // future - } - Log.debug(" between=" + pastLocal + " to " + futureLocal); - const dates = rule.between(pastLocal, futureLocal, true, limitFunction); - Log.debug("title=" + event.summary + " dates=" + JSON.stringify(dates)); - // The "dates" array contains the set of dates within our desired date range range that are valid - // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that - // had its date changed from outside the range to inside the range. For the time being, - // we'll handle this by adding *all* recurrence entries into the set of dates that we check, - // because the logic below will filter out any recurrences that don't actually belong within - // our display range. - // Would be great if there was a better way to handle this. - if (event.recurrences !== undefined) { - for (let r in event.recurrences) { - // Only add dates that weren't already in the range we added from the rrule so that - // we don"t double-add those events. - if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) { - dates.push(new Date(r)); - } - } - } - // Loop through the set of date entries to see which recurrences should be added to our event list. - for (let d in dates) { - let date = dates[d]; - // ical.js started returning recurrences and exdates as ISOStrings without time information. - // .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same - // (see https://github.com/peterbraden/ical.js/pull/84 ) - const dateKey = date.toISOString().substring(0, 10); - let curEvent = event; - let showRecurrence = true; - - // for full day events, the time might be off from RRULE/Luxon problem - if (CalendarUtils.isFullDayEvent(event)) { - Log.debug("fullday"); - // if the offset is negative, east of GMT where the problem is - if (date.getTimezoneOffset() < 0) { - // get the offset of today where we are processing - // this will be the correction we need to apply - let nowOffset = new Date().getTimezoneOffset(); - Log.debug("now offset is " + nowOffset); - // reduce the time by the offset - Log.debug(" recurring date is " + date + " offset is " + date.getTimezoneOffset()); - // apply the correction to the date/time to get it UTC relative - date = new Date(date.getTime() - Math.abs(nowOffset) * 60000); - // the duration was calculated way back at the top before we could correct the start time.. - // fix it for this event entry - duration = 24 * 60 * 60 * 1000; - Log.debug("new recurring date is " + date); - } - } - startDate = moment(date); - - let adjustDays = CalendarUtils.calculateTimezoneAdjustment(event, date); - - // For each date that we're checking, it's possible that there is a recurrence override for that one day. - if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { - // We found an override, so for this recurrence, use a potentially different title, start date, and duration. - curEvent = curEvent.recurrences[dateKey]; - startDate = moment(curEvent.start); - duration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x")); - } - // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. - else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) { - // This date is an exception date, which means we should skip it in the recurrence pattern. - showRecurrence = false; - } - Log.debug("duration=" + duration); - - endDate = moment(parseInt(startDate.format("x")) + duration, "x"); - if (startDate.format("x") === endDate.format("x")) { - endDate = endDate.endOf("day"); - } - - const recurrenceTitle = CalendarUtils.getTitleFromEvent(curEvent); - - // If this recurrence ends before the start of the date range, or starts after the end of the date range, don"t add - // it to the event list. - if (endDate.isBefore(past) || startDate.isAfter(future)) { - showRecurrence = false; - } - - if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { - showRecurrence = false; - } - - if (showRecurrence === true) { - Log.debug("saving event =" + description); - addedEvents++; - newEvents.push({ - title: recurrenceTitle, - startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), - endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), - fullDayEvent: CalendarUtils.isFullDayEvent(event), - recurringEvent: true, - class: event.class, - firstYear: event.start.getFullYear(), - location: location, - geo: geo, - description: description - }); - } - } - // end recurring event parsing - } else { - // Single event. - const fullDayEvent = isFacebookBirthday ? true : CalendarUtils.isFullDayEvent(event); - // Log.debug("full day event") - - if (includePastEvents) { - // Past event is too far in the past, so skip. - if (endDate < past) { - return; - } - } else { - // It's not a fullday event, and it is in the past, so skip. - if (!fullDayEvent && endDate < new Date()) { - return; - } - - // It's a fullday event, and it is before today, So skip. - if (fullDayEvent && endDate <= today) { - return; - } - } - - // It exceeds the maximumNumberOfDays limit, so skip. - if (startDate > future) { - return; - } - - if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { - return; - } - - // Adjust start date so multiple day events will be displayed as happening today even though they started some days ago already - if (fullDayEvent && startDate <= today) { - startDate = moment(today); - } - // if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00) - if (fullDayEvent && startDate.format("x") === endDate.format("x")) { - endDate = endDate.endOf("day"); - } - // get correction for date saving and dst change between now and then - let adjustDays = CalendarUtils.getCorrection(event, startDate.toDate()); - // Every thing is good. Add it to the list. + if (showRecurrence === true) { + Log.debug("saving event =" + description); + addedEvents++; newEvents.push({ - title: title, + title: recurrenceTitle, startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), - fullDayEvent: fullDayEvent, + fullDayEvent: CalendarUtils.isFullDayEvent(event), + recurringEvent: true, class: event.class, + firstYear: event.start.getFullYear(), location: location, geo: geo, description: description }); } } - }); + // end recurring event parsing + } else { + // Single event. + const fullDayEvent = isFacebookBirthday ? true : CalendarUtils.isFullDayEvent(event); + // Log.debug("full day event") - newEvents.sort(function (a, b) { - return a.startDate - b.startDate; - }); + if (includePastEvents) { + // Past event is too far in the past, so skip. + if (endDate < past) { + return; + } + } else { + // It's not a fullday event, and it is in the past, so skip. + if (!fullDayEvent && endDate < new Date()) { + return; + } - // include up to maximumEntries current or upcoming events - // If past events should be included, include all past events - const now = moment(); - var entries = 0; - events = []; - for (let ne of newEvents) { - if (moment(ne.endDate, "x").isBefore(now)) { - if (includePastEvents) events.push(ne); - continue; + // It's a fullday event, and it is before today, So skip. + if (fullDayEvent && endDate <= today) { + return; + } } - entries++; - // If max events has been saved, skip the rest - if (entries > maximumEntries) break; - events.push(ne); - } - self.broadcastEvents(); - scheduleTimer(); - }); + // It exceeds the maximumNumberOfDays limit, so skip. + if (startDate > future) { + return; + } + + if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { + return; + } + + // Adjust start date so multiple day events will be displayed as happening today even though they started some days ago already + if (fullDayEvent && startDate <= today) { + startDate = moment(today); + } + // if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00) + if (fullDayEvent && startDate.format("x") === endDate.format("x")) { + endDate = endDate.endOf("day"); + } + // get correction for date saving and dst change between now and then + let adjustDays = CalendarUtils.getCorrection(event, startDate.toDate()); + // Every thing is good. Add it to the list. + newEvents.push({ + title: title, + startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), + endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), + fullDayEvent: fullDayEvent, + class: event.class, + location: location, + geo: geo, + description: description + }); + } + } + }); + + newEvents.sort(function (a, b) { + return a.startDate - b.startDate; + }); + + // include up to maximumEntries current or upcoming events + // If past events should be included, include all past events + const now = moment(); + var entries = 0; + events = []; + for (let ne of newEvents) { + if (moment(ne.endDate, "x").isBefore(now)) { + if (includePastEvents) events.push(ne); + continue; + } + entries++; + // If max events has been saved, skip the rest + if (entries > maximumEntries) break; + events.push(ne); + } }; /** From c2f5d038de34f45e0143d5c02a2d7e446b6c99f2 Mon Sep 17 00:00:00 2001 From: rejas Date: Sat, 26 Dec 2020 12:12:44 +0100 Subject: [PATCH 05/13] Move filter function into utils class too --- CHANGELOG.md | 1 + modules/default/calendar/calendarfetcher.js | 332 +------------------- modules/default/calendar/calendarutils.js | 330 +++++++++++++++++++ 3 files changed, 337 insertions(+), 326 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 850bd0fd..75744ba1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,7 @@ Special thanks to the following contributors: @Alvinger, @AndyPoms, @ashishtank, - Added missing function call in module.show() - Translator variables can have falsy values (e.g. empty string) - Fix issue with weather module with DEGREE label in FEELS like +- Refactor calendar fetcher ### Deleted diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 9a18a1fb..ef88c1d1 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -91,7 +91,12 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn try { data = ical.parseICS(responseData); Log.debug("parsed data=" + JSON.stringify(data)); - filterEvents(data); + events = CalendarUtils.filterEvents(data, { + excludedEvents, + includePastEvents, + maximumEntries, + maximumNumberOfDays + }); } catch (error) { fetchFailedCallback(self, error.message); scheduleTimer(); @@ -102,331 +107,6 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn }); }; - const filterEvents = function (data) { - const newEvents = []; - - // limitFunction doesn't do much limiting, see comment re: the dates array in rrule section below as to why we need to do the filtering ourselves - const limitFunction = function (date, i) { - return true; - }; - - const eventDate = function (event, time) { - return CalendarUtils.isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time])); - }; - Log.debug("there are " + Object.entries(data).length + " calendar entries"); - Object.entries(data).forEach(([key, event]) => { - const now = new Date(); - const today = moment().startOf("day").toDate(); - const 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. - let past = today; - Log.debug("have entries "); - if (includePastEvents) { - past = moment().startOf("day").subtract(maximumNumberOfDays, "days").toDate(); - } - - // FIXME: Ugly fix to solve the facebook birthday issue. - // Otherwise, the recurring events only show the birthday for next year. - let isFacebookBirthday = false; - if (typeof event.uid !== "undefined") { - if (event.uid.indexOf("@facebook.com") !== -1) { - isFacebookBirthday = true; - } - } - - if (event.type === "VEVENT") { - let startDate = eventDate(event, "start"); - let endDate; - - Log.debug("\nevent=" + JSON.stringify(event)); - if (typeof event.end !== "undefined") { - endDate = eventDate(event, "end"); - } else if (typeof event.duration !== "undefined") { - endDate = startDate.clone().add(moment.duration(event.duration)); - } else { - if (!isFacebookBirthday) { - // make copy of start date, separate storage area - endDate = moment(startDate.format("x"), "x"); - } else { - endDate = moment(startDate).add(1, "days"); - } - } - - Log.debug(" start=" + startDate.toDate() + " end=" + endDate.toDate()); - - // calculate the duration of the event for use with recurring events. - let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x")); - - if (event.start.length === 8) { - startDate = startDate.startOf("day"); - } - - const title = CalendarUtils.getTitleFromEvent(event); - - let excluded = false, - dateFilter = null; - - for (let f in excludedEvents) { - let filter = excludedEvents[f], - testTitle = title.toLowerCase(), - until = null, - useRegex = false, - regexFlags = "g"; - - if (filter instanceof Object) { - if (typeof filter.until !== "undefined") { - until = filter.until; - } - - if (typeof filter.regex !== "undefined") { - useRegex = filter.regex; - } - - // If additional advanced filtering is added in, this section - // must remain last as we overwrite the filter object with the - // filterBy string - if (filter.caseSensitive) { - filter = filter.filterBy; - testTitle = title; - } else if (useRegex) { - filter = filter.filterBy; - testTitle = title; - regexFlags += "i"; - } else { - filter = filter.filterBy.toLowerCase(); - } - } else { - filter = filter.toLowerCase(); - } - - if (CalendarUtils.testTitleByFilter(testTitle, filter, useRegex, regexFlags)) { - if (until) { - dateFilter = until; - } else { - excluded = true; - } - break; - } - } - - if (excluded) { - return; - } - - const location = event.location || false; - const geo = event.geo || false; - const description = event.description || false; - - if (typeof event.rrule !== "undefined" && event.rrule !== null && !isFacebookBirthday) { - const rule = event.rrule; - let addedEvents = 0; - - const pastMoment = moment(past); - const futureMoment = moment(future); - - // can cause problems with e.g. birthdays before 1900 - if ((rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900) || (rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900)) { - rule.origOptions.dtstart.setYear(1900); - rule.options.dtstart.setYear(1900); - } - - // For recurring events, get the set of start dates that fall within the range - // of dates we're looking for. - // kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time - let pastLocal = 0; - let futureLocal = 0; - if (CalendarUtils.isFullDayEvent(event)) { - // if full day event, only use the date part of the ranges - pastLocal = pastMoment.toDate(); - futureLocal = futureMoment.toDate(); - } else { - // if we want past events - if (includePastEvents) { - // use the calculated past time for the between from - pastLocal = pastMoment.toDate(); - } else { - // otherwise use NOW.. cause we shouldnt use any before now - pastLocal = moment().toDate(); //now - } - futureLocal = futureMoment.toDate(); // future - } - Log.debug(" between=" + pastLocal + " to " + futureLocal); - const dates = rule.between(pastLocal, futureLocal, true, limitFunction); - Log.debug("title=" + event.summary + " dates=" + JSON.stringify(dates)); - // The "dates" array contains the set of dates within our desired date range range that are valid - // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that - // had its date changed from outside the range to inside the range. For the time being, - // we'll handle this by adding *all* recurrence entries into the set of dates that we check, - // because the logic below will filter out any recurrences that don't actually belong within - // our display range. - // Would be great if there was a better way to handle this. - if (event.recurrences !== undefined) { - for (let r in event.recurrences) { - // Only add dates that weren't already in the range we added from the rrule so that - // we don"t double-add those events. - if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) { - dates.push(new Date(r)); - } - } - } - // Loop through the set of date entries to see which recurrences should be added to our event list. - for (let d in dates) { - let date = dates[d]; - // ical.js started returning recurrences and exdates as ISOStrings without time information. - // .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same - // (see https://github.com/peterbraden/ical.js/pull/84 ) - const dateKey = date.toISOString().substring(0, 10); - let curEvent = event; - let showRecurrence = true; - - // for full day events, the time might be off from RRULE/Luxon problem - if (CalendarUtils.isFullDayEvent(event)) { - Log.debug("fullday"); - // if the offset is negative, east of GMT where the problem is - if (date.getTimezoneOffset() < 0) { - // get the offset of today where we are processing - // this will be the correction we need to apply - let nowOffset = new Date().getTimezoneOffset(); - Log.debug("now offset is " + nowOffset); - // reduce the time by the offset - Log.debug(" recurring date is " + date + " offset is " + date.getTimezoneOffset()); - // apply the correction to the date/time to get it UTC relative - date = new Date(date.getTime() - Math.abs(nowOffset) * 60000); - // the duration was calculated way back at the top before we could correct the start time.. - // fix it for this event entry - duration = 24 * 60 * 60 * 1000; - Log.debug("new recurring date is " + date); - } - } - startDate = moment(date); - - let adjustDays = CalendarUtils.calculateTimezoneAdjustment(event, date); - - // For each date that we're checking, it's possible that there is a recurrence override for that one day. - if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { - // We found an override, so for this recurrence, use a potentially different title, start date, and duration. - curEvent = curEvent.recurrences[dateKey]; - startDate = moment(curEvent.start); - duration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x")); - } - // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. - else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) { - // This date is an exception date, which means we should skip it in the recurrence pattern. - showRecurrence = false; - } - Log.debug("duration=" + duration); - - endDate = moment(parseInt(startDate.format("x")) + duration, "x"); - if (startDate.format("x") === endDate.format("x")) { - endDate = endDate.endOf("day"); - } - - const recurrenceTitle = CalendarUtils.getTitleFromEvent(curEvent); - - // If this recurrence ends before the start of the date range, or starts after the end of the date range, don"t add - // it to the event list. - if (endDate.isBefore(past) || startDate.isAfter(future)) { - showRecurrence = false; - } - - if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { - showRecurrence = false; - } - - if (showRecurrence === true) { - Log.debug("saving event =" + description); - addedEvents++; - newEvents.push({ - title: recurrenceTitle, - startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), - endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), - fullDayEvent: CalendarUtils.isFullDayEvent(event), - recurringEvent: true, - class: event.class, - firstYear: event.start.getFullYear(), - location: location, - geo: geo, - description: description - }); - } - } - // end recurring event parsing - } else { - // Single event. - const fullDayEvent = isFacebookBirthday ? true : CalendarUtils.isFullDayEvent(event); - // Log.debug("full day event") - - if (includePastEvents) { - // Past event is too far in the past, so skip. - if (endDate < past) { - return; - } - } else { - // It's not a fullday event, and it is in the past, so skip. - if (!fullDayEvent && endDate < new Date()) { - return; - } - - // It's a fullday event, and it is before today, So skip. - if (fullDayEvent && endDate <= today) { - return; - } - } - - // It exceeds the maximumNumberOfDays limit, so skip. - if (startDate > future) { - return; - } - - if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { - return; - } - - // Adjust start date so multiple day events will be displayed as happening today even though they started some days ago already - if (fullDayEvent && startDate <= today) { - startDate = moment(today); - } - // if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00) - if (fullDayEvent && startDate.format("x") === endDate.format("x")) { - endDate = endDate.endOf("day"); - } - // get correction for date saving and dst change between now and then - let adjustDays = CalendarUtils.getCorrection(event, startDate.toDate()); - // Every thing is good. Add it to the list. - newEvents.push({ - title: title, - startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), - endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), - fullDayEvent: fullDayEvent, - class: event.class, - location: location, - geo: geo, - description: description - }); - } - } - }); - - newEvents.sort(function (a, b) { - return a.startDate - b.startDate; - }); - - // include up to maximumEntries current or upcoming events - // If past events should be included, include all past events - const now = moment(); - var entries = 0; - events = []; - for (let ne of newEvents) { - if (moment(ne.endDate, "x").isBefore(now)) { - if (includePastEvents) events.push(ne); - continue; - } - entries++; - // If max events has been saved, skip the rest - if (entries > maximumEntries) break; - events.push(ne); - } - }; - /** * Schedule the timer for the next update. */ diff --git a/modules/default/calendar/calendarutils.js b/modules/default/calendar/calendarutils.js index f57f342f..35f61458 100644 --- a/modules/default/calendar/calendarutils.js +++ b/modules/default/calendar/calendarutils.js @@ -112,6 +112,336 @@ const CalendarUtils = { return adjustHours; }, + filterEvents: function (data, config) { + const newEvents = []; + + // limitFunction doesn't do much limiting, see comment re: the dates + // array in rrule section below as to why we need to do the filtering + // ourselves + const limitFunction = function (date, i) { + return true; + }; + + const eventDate = function (event, time) { + return CalendarUtils.isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time])); + }; + + Log.debug("there are " + Object.entries(data).length + " calendar entries"); + Object.entries(data).forEach(([key, event]) => { + const now = new Date(); + const today = moment().startOf("day").toDate(); + const future = moment().startOf("day").add(config.maximumNumberOfDays, "days").subtract(1, "seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat. + let past = today; + Log.debug("have entries "); + if (config.includePastEvents) { + past = moment().startOf("day").subtract(config.maximumNumberOfDays, "days").toDate(); + } + + // FIXME: Ugly fix to solve the facebook birthday issue. + // Otherwise, the recurring events only show the birthday for next year. + let isFacebookBirthday = false; + if (typeof event.uid !== "undefined") { + if (event.uid.indexOf("@facebook.com") !== -1) { + isFacebookBirthday = true; + } + } + + if (event.type === "VEVENT") { + let startDate = eventDate(event, "start"); + let endDate; + + Log.debug("\nevent=" + JSON.stringify(event)); + if (typeof event.end !== "undefined") { + endDate = eventDate(event, "end"); + } else if (typeof event.duration !== "undefined") { + endDate = startDate.clone().add(moment.duration(event.duration)); + } else { + if (!isFacebookBirthday) { + // make copy of start date, separate storage area + endDate = moment(startDate.format("x"), "x"); + } else { + endDate = moment(startDate).add(1, "days"); + } + } + + Log.debug(" start=" + startDate.toDate() + " end=" + endDate.toDate()); + + // calculate the duration of the event for use with recurring events. + let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x")); + + if (event.start.length === 8) { + startDate = startDate.startOf("day"); + } + + const title = CalendarUtils.getTitleFromEvent(event); + + let excluded = false, + dateFilter = null; + + for (let f in config.excludedEvents) { + let filter = config.excludedEvents[f], + testTitle = title.toLowerCase(), + until = null, + useRegex = false, + regexFlags = "g"; + + if (filter instanceof Object) { + if (typeof filter.until !== "undefined") { + until = filter.until; + } + + if (typeof filter.regex !== "undefined") { + useRegex = filter.regex; + } + + // If additional advanced filtering is added in, this section + // must remain last as we overwrite the filter object with the + // filterBy string + if (filter.caseSensitive) { + filter = filter.filterBy; + testTitle = title; + } else if (useRegex) { + filter = filter.filterBy; + testTitle = title; + regexFlags += "i"; + } else { + filter = filter.filterBy.toLowerCase(); + } + } else { + filter = filter.toLowerCase(); + } + + if (CalendarUtils.titleFilterApplies(testTitle, filter, useRegex, regexFlags)) { + if (until) { + dateFilter = until; + } else { + excluded = true; + } + break; + } + } + + if (excluded) { + return; + } + + const location = event.location || false; + const geo = event.geo || false; + const description = event.description || false; + + if (typeof event.rrule !== "undefined" && event.rrule !== null && !isFacebookBirthday) { + const rule = event.rrule; + let addedEvents = 0; + + const pastMoment = moment(past); + const futureMoment = moment(future); + + // can cause problems with e.g. birthdays before 1900 + if ((rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900) || (rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900)) { + rule.origOptions.dtstart.setYear(1900); + rule.options.dtstart.setYear(1900); + } + + // For recurring events, get the set of start dates that fall within the range + // of dates we're looking for. + // kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time + let pastLocal = 0; + let futureLocal = 0; + if (CalendarUtils.isFullDayEvent(event)) { + // if full day event, only use the date part of the ranges + pastLocal = pastMoment.toDate(); + futureLocal = futureMoment.toDate(); + } else { + // if we want past events + if (config.includePastEvents) { + // use the calculated past time for the between from + pastLocal = pastMoment.toDate(); + } else { + // otherwise use NOW.. cause we shouldn't use any before now + pastLocal = moment().toDate(); //now + } + futureLocal = futureMoment.toDate(); // future + } + Log.debug(" between=" + pastLocal + " to " + futureLocal); + const dates = rule.between(pastLocal, futureLocal, true, limitFunction); + Log.debug("title=" + event.summary + " dates=" + JSON.stringify(dates)); + // The "dates" array contains the set of dates within our desired date range range that are valid + // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that + // had its date changed from outside the range to inside the range. For the time being, + // we'll handle this by adding *all* recurrence entries into the set of dates that we check, + // because the logic below will filter out any recurrences that don't actually belong within + // our display range. + // Would be great if there was a better way to handle this. + if (event.recurrences !== undefined) { + for (let r in event.recurrences) { + // Only add dates that weren't already in the range we added from the rrule so that + // we don"t double-add those events. + if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) { + dates.push(new Date(r)); + } + } + } + // Loop through the set of date entries to see which recurrences should be added to our event list. + for (let d in dates) { + let date = dates[d]; + // ical.js started returning recurrences and exdates as ISOStrings without time information. + // .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same + // (see https://github.com/peterbraden/ical.js/pull/84 ) + const dateKey = date.toISOString().substring(0, 10); + let curEvent = event; + let showRecurrence = true; + + // for full day events, the time might be off from RRULE/Luxon problem + if (CalendarUtils.isFullDayEvent(event)) { + Log.debug("fullday"); + // if the offset is negative, east of GMT where the problem is + if (date.getTimezoneOffset() < 0) { + // get the offset of today where we are processing + // this will be the correction we need to apply + let nowOffset = new Date().getTimezoneOffset(); + Log.debug("now offset is " + nowOffset); + // reduce the time by the offset + Log.debug(" recurring date is " + date + " offset is " + date.getTimezoneOffset()); + // apply the correction to the date/time to get it UTC relative + date = new Date(date.getTime() - Math.abs(nowOffset) * 60000); + // the duration was calculated way back at the top before we could correct the start time.. + // fix it for this event entry + duration = 24 * 60 * 60 * 1000; + Log.debug("new recurring date is " + date); + } + } + startDate = moment(date); + + let adjustDays = CalendarUtils.calculateTimezoneAdjustment(event, date); + + // For each date that we're checking, it's possible that there is a recurrence override for that one day. + if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { + // We found an override, so for this recurrence, use a potentially different title, start date, and duration. + curEvent = curEvent.recurrences[dateKey]; + startDate = moment(curEvent.start); + duration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x")); + } + // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. + else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) { + // This date is an exception date, which means we should skip it in the recurrence pattern. + showRecurrence = false; + } + Log.debug("duration=" + duration); + + endDate = moment(parseInt(startDate.format("x")) + duration, "x"); + if (startDate.format("x") === endDate.format("x")) { + endDate = endDate.endOf("day"); + } + + const recurrenceTitle = CalendarUtils.getTitleFromEvent(curEvent); + + // If this recurrence ends before the start of the date range, or starts after the end of the date range, don"t add + // it to the event list. + if (endDate.isBefore(past) || startDate.isAfter(future)) { + showRecurrence = false; + } + + if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { + showRecurrence = false; + } + + if (showRecurrence === true) { + Log.debug("saving event =" + description); + addedEvents++; + newEvents.push({ + title: recurrenceTitle, + startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), + endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), + fullDayEvent: CalendarUtils.isFullDayEvent(event), + recurringEvent: true, + class: event.class, + firstYear: event.start.getFullYear(), + location: location, + geo: geo, + description: description + }); + } + } + // end recurring event parsing + } else { + // Single event. + const fullDayEvent = isFacebookBirthday ? true : CalendarUtils.isFullDayEvent(event); + // Log.debug("full day event") + + if (config.includePastEvents) { + // Past event is too far in the past, so skip. + if (endDate < past) { + return; + } + } else { + // It's not a fullday event, and it is in the past, so skip. + if (!fullDayEvent && endDate < new Date()) { + return; + } + + // It's a fullday event, and it is before today, So skip. + if (fullDayEvent && endDate <= today) { + return; + } + } + + // It exceeds the maximumNumberOfDays limit, so skip. + if (startDate > future) { + return; + } + + if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { + return; + } + + // Adjust start date so multiple day events will be displayed as happening today even though they started some days ago already + if (fullDayEvent && startDate <= today) { + startDate = moment(today); + } + // if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00) + if (fullDayEvent && startDate.format("x") === endDate.format("x")) { + endDate = endDate.endOf("day"); + } + // get correction for date saving and dst change between now and then + let adjustDays = CalendarUtils.calculateTimezoneAdjustment(event, startDate.toDate()); + // Every thing is good. Add it to the list. + newEvents.push({ + title: title, + startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), + endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), + fullDayEvent: fullDayEvent, + class: event.class, + location: location, + geo: geo, + description: description + }); + } + } + }); + + newEvents.sort(function (a, b) { + return a.startDate - b.startDate; + }); + + // include up to maximumEntries current or upcoming events + // If past events should be included, include all past events + const now = moment(); + let entries = 0; + let events = []; + for (let ne of newEvents) { + if (moment(ne.endDate, "x").isBefore(now)) { + if (config.includePastEvents) events.push(ne); + continue; + } + entries++; + // If max events has been saved, skip the rest + if (entries > config.maximumEntries) break; + events.push(ne); + } + + return events; + }, + /** * Lookup iana tz from windows * From 05c3a5bf834f76ce775a77c0eac1afad793883e3 Mon Sep 17 00:00:00 2001 From: rejas Date: Thu, 31 Dec 2020 21:51:20 +0100 Subject: [PATCH 06/13] Remove self variable --- modules/default/calendar/calendarfetcher.js | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index ef88c1d1..4c7d1020 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -12,14 +12,6 @@ const digest = require("digest-fetch"); const https = require("https"); const base64 = require("base-64"); -/** - * Moment date - * - * @external Moment - * @see {@link http://momentjs.com} - */ -const moment = require("moment"); - /** * * @param {string} url The url of the calendar to fetch @@ -33,8 +25,6 @@ const moment = require("moment"); * @class */ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, includePastEvents, selfSignedCert) { - const self = this; - let reloadTimer = null; let events = []; @@ -44,7 +34,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn /** * Initiates calendar fetch. */ - const fetchCalendar = function () { + const fetchCalendar = () => { clearTimeout(reloadTimer); reloadTimer = null; const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]); @@ -102,7 +92,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn scheduleTimer(); return; } - self.broadcastEvents(); + this.broadcastEvents(); scheduleTimer(); }); }; @@ -131,7 +121,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn */ this.broadcastEvents = function () { Log.info("Calendar-Fetcher: Broadcasting " + events.length + " events."); - eventsReceivedCallback(self); + eventsReceivedCallback(this); }; /** From 6de983aeb2009c37cf60618db66860e7562a67cb Mon Sep 17 00:00:00 2001 From: rejas Date: Fri, 1 Jan 2021 21:58:02 +0100 Subject: [PATCH 07/13] Update CHANGELOG --- CHANGELOG.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75744ba1..fc8bc125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ _This release is scheduled to be released on 2021-04-01._ - Cleaned up jsdoc and tests. - Exposed logger as node module for easier access for 3rd party modules - Replaced deprecated `request` package with `node-fetch` and `digest-fetch` +- Refactored calendar fetcher ### Removed @@ -93,11 +94,10 @@ Special thanks to the following contributors: @Alvinger, @AndyPoms, @ashishtank, - Added missing function call in module.show() - Translator variables can have falsy values (e.g. empty string) - Fix issue with weather module with DEGREE label in FEELS like -- Refactor calendar fetcher ### Deleted -- Removed Travis CI intergration. +- Removed Travis CI integration. ### Fixed @@ -114,8 +114,8 @@ Special thanks to the following contributors: @Alvinger, @AndyPoms, @ashishtank, - Fix non-fullday recurring rule processing. (#2216) - Catch errors when parsing calendar data with ical. (#2022) - Fix Default Alert Module does not hide black overlay when alert is dismissed manually. (#2228) -- Weather module - Always displays night icons when local is other then English. (#2221) -- Update Node-ical 0.12.4 , fix invalid RRULE format in cal entries +- Weather module - Always displays night icons when local is other than English. (#2221) +- Update node-ical 0.12.4, fix invalid RRULE format in cal entries - Fix package.json for optional electron dependency (2378) - Update node-ical version again, 0.12.5, change RRULE fix (#2371, #2379) - Remove undefined objects from modules array (#2382) @@ -130,11 +130,11 @@ Special thanks to the following contributors: @bryanzzhu, @bugsounet, @chamakura ### Added -- `--dry-run` option adde in fetch call within updatenotification node_helper. This is to prevent +- `--dry-run` Added option in fetch call within updatenotification node_helper. This is to prevent MagicMirror from consuming any fetch result. Causes conflict with MMPM when attempting to check for updates to MagicMirror and/or MagicMirror modules. - Test coverage with Istanbul, run it with `npm run test:coverage`. -- Add lithuanian language. +- Added lithuanian language. - Added support in weatherforecast for OpenWeather onecall API. - Added config option to calendar-icons for recurring- and fullday-events. - Added current, hourly (max 48), and daily (max 7) weather forecasts to weather module via OpenWeatherMap One Call API. @@ -198,7 +198,7 @@ Special thanks to the following contributors: @AndreKoepke, @andrezibaia, @bryan - Fix the use of "maxNumberOfDays" in the module "weatherforecast". [#2018](https://github.com/MichMich/MagicMirror/issues/2018) - Throw error when check_config fails. [#1928](https://github.com/MichMich/MagicMirror/issues/1928) - Bug fix related to 'maxEntries' not displaying Calendar events. [#2050](https://github.com/MichMich/MagicMirror/issues/2050) -- Updated ical library to latest version. [#1926](https://github.com/MichMich/MagicMirror/issues/1926) +- Updated ical library to the latest version. [#1926](https://github.com/MichMich/MagicMirror/issues/1926) - Fix config check after merge of prettier [#2109](https://github.com/MichMich/MagicMirror/issues/2109) ## [2.11.0] - 2020-04-01 From d3d64d3ca0fe0976afea9a390fee58e09320cb12 Mon Sep 17 00:00:00 2001 From: rejas Date: Sat, 2 Jan 2021 21:08:53 +0100 Subject: [PATCH 08/13] Cleanups --- modules/default/calendar/calendar.js | 2 +- modules/default/calendar/calendarutils.js | 4 ++-- modules/default/calendar/node_helper.js | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 2e7887f6..5cf02ee4 100755 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -77,7 +77,7 @@ Module.register("calendar", { // Define required translations. getTranslations: function () { // The translations for the default modules are defined in the core translation files. - // Therefor we can just return false. Otherwise we should have returned a dictionary. + // Therefore we can just return false. Otherwise we should have returned a dictionary. // If you're trying to build your own module including translations, check out the documentation. return false; }, diff --git a/modules/default/calendar/calendarutils.js b/modules/default/calendar/calendarutils.js index 35f61458..aaf51a12 100644 --- a/modules/default/calendar/calendarutils.js +++ b/modules/default/calendar/calendarutils.js @@ -18,8 +18,8 @@ const CalendarUtils = { * Calculate the time correction, either dst/std or full day in cases where * utc time is day before plus offset * - * @param event - * @param date + * @param {object} event + * @param {Date} date * @returns {number} the necessary adjustment in hours */ calculateTimezoneAdjustment: function (event, date) { diff --git a/modules/default/calendar/node_helper.js b/modules/default/calendar/node_helper.js index 66e423db..b734309e 100644 --- a/modules/default/calendar/node_helper.js +++ b/modules/default/calendar/node_helper.js @@ -74,6 +74,8 @@ module.exports = NodeHelper.create({ /** * + * @param {object} fetcher the fetcher associated with the calendar + * @param {string} identifier the identifier of the calendar */ broadcastEvents: function (fetcher, identifier) { this.sendSocketNotification("CALENDAR_EVENTS", { From 3d4429d418995375f24b46e8a05b4a6762a28966 Mon Sep 17 00:00:00 2001 From: rejas Date: Mon, 4 Jan 2021 21:48:34 +0100 Subject: [PATCH 09/13] Remove copypasted-function that doesnt exist --- modules/default/calendar/node_helper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/default/calendar/node_helper.js b/modules/default/calendar/node_helper.js index b734309e..5a3504e5 100644 --- a/modules/default/calendar/node_helper.js +++ b/modules/default/calendar/node_helper.js @@ -65,7 +65,6 @@ module.exports = NodeHelper.create({ } else { Log.log("Use existing calendar fetcher for url: " + url); fetcher = this.fetchers[identifier + url]; - fetcher.setReloadInterval(fetchInterval); fetcher.broadcastEvents(); } From 5e6cbeb9ba9099337c4960de209a8cd2c5708795 Mon Sep 17 00:00:00 2001 From: rejas Date: Mon, 4 Jan 2021 21:59:41 +0100 Subject: [PATCH 10/13] Convert some code to es6 --- modules/default/calendar/calendar.js | 104 ++++++++++++-------------- tests/e2e/modules/calendar_spec.js | 2 +- tests/unit/functions/calendar_spec.js | 12 +-- 3 files changed, 56 insertions(+), 62 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 5cf02ee4..05826322 100755 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -95,16 +95,16 @@ Module.register("calendar", { // indicate no data available yet this.loaded = false; - for (var c in this.config.calendars) { - var calendar = this.config.calendars[c]; + this.config.calendars.forEach((calendar) => { calendar.url = calendar.url.replace("webcal://", "http://"); - var calendarConfig = { + const calendarConfig = { maximumEntries: calendar.maximumEntries, maximumNumberOfDays: calendar.maximumNumberOfDays, broadcastPastEvents: calendar.broadcastPastEvents, selfSignedCert: calendar.selfSignedCert }; + if (calendar.symbolClass === "undefined" || calendar.symbolClass === null) { calendarConfig.symbolClass = ""; } @@ -128,7 +128,7 @@ Module.register("calendar", { // tell helper to start a fetcher for this calendar // fetcher till cycle this.addCalendar(calendar.url, calendar.auth, calendarConfig); - } + }); }, // Override socket notification handler. @@ -164,8 +164,8 @@ Module.register("calendar", { const oneHour = oneMinute * 60; const oneDay = oneHour * 24; - var events = this.createEventList(); - var wrapper = document.createElement("table"); + const events = this.createEventList(); + const wrapper = document.createElement("table"); wrapper.className = this.config.tableClass; if (events.length === 0) { @@ -192,10 +192,10 @@ Module.register("calendar", { var dateAsString = moment(event.startDate, "x").format(this.config.dateFormat); if (this.config.timeFormat === "dateheaders") { if (lastSeenDate !== dateAsString) { - var dateRow = document.createElement("tr"); + const dateRow = document.createElement("tr"); dateRow.className = "normal"; - var dateCell = document.createElement("td"); + const dateCell = document.createElement("td"); dateCell.colSpan = "3"; dateCell.innerHTML = dateAsString; dateCell.style.paddingTop = "10px"; @@ -212,7 +212,7 @@ Module.register("calendar", { } } - var eventWrapper = document.createElement("tr"); + const eventWrapper = document.createElement("tr"); if (this.config.colored && !this.config.coloredSymbolOnly) { eventWrapper.style.cssText = "color:" + this.colorForUrl(event.url); @@ -220,17 +220,17 @@ Module.register("calendar", { eventWrapper.className = "normal event"; - if (this.config.displaySymbol) { - var symbolWrapper = document.createElement("td"); + const symbolWrapper = document.createElement("td"); + if (this.config.displaySymbol) { if (this.config.colored && this.config.coloredSymbolOnly) { symbolWrapper.style.cssText = "color:" + this.colorForUrl(event.url); } - var symbolClass = this.symbolClassForUrl(event.url); + const symbolClass = this.symbolClassForUrl(event.url); symbolWrapper.className = "symbol align-right " + symbolClass; - var symbols = this.symbolsForEvent(event); + const symbols = this.symbolsForEvent(event); // If symbols are displayed and custom symbol is set, replace event symbol if (this.config.displaySymbol && this.config.customEvents.length > 0) { for (ev in this.config.customEvents) { @@ -243,31 +243,29 @@ Module.register("calendar", { } } } - - for (var i = 0; i < symbols.length; i++) { - var symbol = document.createElement("span"); - symbol.className = "fa fa-fw fa-" + symbols[i]; - if (i > 0) { + symbols.forEach((s, index) => { + const symbol = document.createElement("span"); + symbol.className = "fa fa-fw fa-" + s; + if (index > 0) { symbol.style.paddingLeft = "5px"; } symbolWrapper.appendChild(symbol); - } - + }); eventWrapper.appendChild(symbolWrapper); } else if (this.config.timeFormat === "dateheaders") { - var blankCell = document.createElement("td"); + const blankCell = document.createElement("td"); blankCell.innerHTML = "   "; eventWrapper.appendChild(blankCell); } - var titleWrapper = document.createElement("td"), - repeatingCountTitle = ""; + const titleWrapper = document.createElement("td"); + let repeatingCountTitle = ""; if (this.config.displayRepeatingCountTitle && event.firstYear !== undefined) { repeatingCountTitle = this.countTitleForUrl(event.url); if (repeatingCountTitle !== "") { - var thisYear = new Date(parseInt(event.startDate)).getFullYear(), + const thisYear = new Date(parseInt(event.startDate)).getFullYear(), yearDiff = thisYear - event.firstYear; repeatingCountTitle = ", " + yearDiff + ". " + repeatingCountTitle; @@ -296,7 +294,7 @@ Module.register("calendar", { 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); + const titleClass = this.titleClassForUrl(event.url); if (!this.config.colored) { titleWrapper.className = "title bright " + titleClass; @@ -304,14 +302,12 @@ Module.register("calendar", { titleWrapper.className = "title " + titleClass; } - var timeWrapper; - if (this.config.timeFormat === "dateheaders") { if (event.fullDayEvent) { titleWrapper.colSpan = "2"; titleWrapper.align = "left"; } else { - timeWrapper = document.createElement("td"); + const timeWrapper = document.createElement("td"); timeWrapper.className = "time light " + this.timeClassForUrl(event.url); timeWrapper.align = "left"; timeWrapper.style.paddingLeft = "2px"; @@ -322,10 +318,10 @@ Module.register("calendar", { eventWrapper.appendChild(titleWrapper); } else { - timeWrapper = document.createElement("td"); + const timeWrapper = document.createElement("td"); eventWrapper.appendChild(titleWrapper); - var now = new Date(); + const now = new Date(); if (this.config.timeFormat === "absolute") { // Use dateFormat @@ -408,15 +404,15 @@ Module.register("calendar", { if (this.config.showLocation) { if (event.location !== false) { - var locationRow = document.createElement("tr"); + const locationRow = document.createElement("tr"); locationRow.className = "normal xsmall light"; if (this.config.displaySymbol) { - var symbolCell = document.createElement("td"); + const symbolCell = document.createElement("td"); locationRow.appendChild(symbolCell); } - var descCell = document.createElement("td"); + const descCell = document.createElement("td"); descCell.className = "location"; descCell.colSpan = "2"; descCell.innerHTML = this.titleTransform(event.location, this.config.locationTitleReplace, this.config.wrapLocationEvents, this.config.maxLocationTitleLength, this.config.maxEventTitleLines); @@ -464,8 +460,7 @@ Module.register("calendar", { * @returns {boolean} True if the calendar config contains the url, False otherwise */ hasCalendarURL: function (url) { - for (var c in this.config.calendars) { - var calendar = this.config.calendars[c]; + for (const calendar of this.config.calendars) { if (calendar.url === url) { return true; } @@ -480,10 +475,11 @@ Module.register("calendar", { * @returns {object[]} Array with events. */ createEventList: function () { - var events = []; - var today = moment().startOf("day"); - var now = new Date(); - var future = moment().startOf("day").add(this.config.maximumNumberOfDays, "days").toDate(); + const now = new Date(); + const today = moment().startOf("day"); + const future = moment().startOf("day").add(this.config.maximumNumberOfDays, "days").toDate(); + let events = []; + for (var c in this.calendarData) { var calendar = this.calendarData[c]; for (var e in calendar) { @@ -512,11 +508,11 @@ Module.register("calendar", { /* 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; + const 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; + const splitEvents = []; + let midnight = moment(event.startDate, "x").clone().startOf("day").add(1, "day").format("x"); + let 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; @@ -532,9 +528,9 @@ Module.register("calendar", { event.title += " (" + count + "/" + maxCount + ")"; splitEvents.push(event); - for (event of splitEvents) { - if (event.endDate > now && event.endDate <= future) { - events.push(event); + for (let splitEvent of splitEvents) { + if (splitEvent.endDate > now && splitEvent.endDate <= future) { + events.push(splitEvent); } } } else { @@ -550,12 +546,11 @@ Module.register("calendar", { // Limit the number of days displayed // If limitDays is set > 0, limit display to that number of days if (this.config.limitDays > 0) { - var newEvents = []; - var lastDate = today.clone().subtract(1, "days").format("YYYYMMDD"); - var days = 0; - var eventDate; - for (var ev of events) { - eventDate = moment(ev.startDate, "x").format("YYYYMMDD"); + let newEvents = []; + let lastDate = today.clone().subtract(1, "days").format("YYYYMMDD"); + let days = 0; + for (const ev of events) { + let eventDate = moment(ev.startDate, "x").format("YYYYMMDD"); // if date of event is later than lastdate // check if we already are showing max unique days if (eventDate > lastDate) { @@ -579,7 +574,7 @@ Module.register("calendar", { }, listContainsEvent: function (eventList, event) { - for (var evt of eventList) { + for (const evt of eventList) { if (evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate)) { return true; } @@ -708,8 +703,7 @@ Module.register("calendar", { * @returns {*} The property */ getCalendarProperty: function (url, property, defaultValue) { - for (var c in this.config.calendars) { - var calendar = this.config.calendars[c]; + for (const calendar of this.config.calendars) { if (calendar.url === url && calendar.hasOwnProperty(property)) { return calendar[property]; } diff --git a/tests/e2e/modules/calendar_spec.js b/tests/e2e/modules/calendar_spec.js index 8d9b0c5a..34cc0164 100644 --- a/tests/e2e/modules/calendar_spec.js +++ b/tests/e2e/modules/calendar_spec.js @@ -10,7 +10,7 @@ const afterEach = global.afterEach; describe("Calendar module", function () { helpers.setupTimeout(this); - var app = null; + let app = null; beforeEach(function () { return helpers diff --git a/tests/unit/functions/calendar_spec.js b/tests/unit/functions/calendar_spec.js index 44091816..34dfb5e1 100644 --- a/tests/unit/functions/calendar_spec.js +++ b/tests/unit/functions/calendar_spec.js @@ -45,42 +45,42 @@ describe("Functions into modules/default/calendar/calendar.js", function () { }); it("should return a 12-hour longDateFormat when using the 'en' locale", function () { - var localeBackup = moment.locale(); + const localeBackup = moment.locale(); moment.locale("en"); expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "h:mm A" } }); moment.locale(localeBackup); }); it("should return a 12-hour longDateFormat when using the 'au' locale", function () { - var localeBackup = moment.locale(); + const localeBackup = moment.locale(); moment.locale("au"); expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "h:mm A" } }); moment.locale(localeBackup); }); it("should return a 12-hour longDateFormat when using the 'eg' locale", function () { - var localeBackup = moment.locale(); + const localeBackup = moment.locale(); moment.locale("eg"); expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "h:mm A" } }); moment.locale(localeBackup); }); it("should return a 24-hour longDateFormat when using the 'nl' locale", function () { - var localeBackup = moment.locale(); + const localeBackup = moment.locale(); moment.locale("nl"); expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "HH:mm" } }); moment.locale(localeBackup); }); it("should return a 24-hour longDateFormat when using the 'fr' locale", function () { - var localeBackup = moment.locale(); + const localeBackup = moment.locale(); moment.locale("fr"); expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "HH:mm" } }); moment.locale(localeBackup); }); it("should return a 24-hour longDateFormat when using the 'uk' locale", function () { - var localeBackup = moment.locale(); + const localeBackup = moment.locale(); moment.locale("uk"); expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "HH:mm" } }); moment.locale(localeBackup); From 85c9d3b331567b9c9d9f3568bc35aee3c40fb2a0 Mon Sep 17 00:00:00 2001 From: rejas Date: Tue, 9 Feb 2021 17:37:43 +0100 Subject: [PATCH 11/13] More es6 notations --- modules/default/calendar/calendar.js | 80 ++++++++++++++-------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 05826322..4a485e4f 100755 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -174,22 +174,22 @@ Module.register("calendar", { return wrapper; } + let currentFadeStep = 0; + let startFade; + let fadeSteps; + if (this.config.fade && this.config.fadePoint < 1) { if (this.config.fadePoint < 0) { this.config.fadePoint = 0; } - var startFade = events.length * this.config.fadePoint; - var fadeSteps = events.length - startFade; + startFade = events.length * this.config.fadePoint; + fadeSteps = events.length - startFade; } - var currentFadeStep = 0; - var lastSeenDate = ""; - var ev; - var needle; + let lastSeenDate = ""; - for (var e in events) { - var event = events[e]; - var dateAsString = moment(event.startDate, "x").format(this.config.dateFormat); + events.forEach((event, index) => { + const dateAsString = moment(event.startDate, "x").format(this.config.dateFormat); if (this.config.timeFormat === "dateheaders") { if (lastSeenDate !== dateAsString) { const dateRow = document.createElement("tr"); @@ -202,9 +202,9 @@ Module.register("calendar", { dateRow.appendChild(dateCell); wrapper.appendChild(dateRow); - if (e >= startFade) { + if (this.config.fade && index >= startFade) { //fading - currentFadeStep = e - startFade; + currentFadeStep = index - startFade; dateRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep; } @@ -233,9 +233,9 @@ Module.register("calendar", { const symbols = this.symbolsForEvent(event); // If symbols are displayed and custom symbol is set, replace event symbol if (this.config.displaySymbol && this.config.customEvents.length > 0) { - for (ev in this.config.customEvents) { + for (let ev in this.config.customEvents) { if (typeof this.config.customEvents[ev].symbol !== "undefined" && this.config.customEvents[ev].symbol !== "") { - needle = new RegExp(this.config.customEvents[ev].keyword, "gi"); + let needle = new RegExp(this.config.customEvents[ev].keyword, "gi"); if (needle.test(event.title)) { symbols[0] = this.config.customEvents[ev].symbol; break; @@ -274,9 +274,9 @@ Module.register("calendar", { // Color events if custom color is specified if (this.config.customEvents.length > 0) { - for (ev in this.config.customEvents) { + for (let ev in this.config.customEvents) { if (typeof this.config.customEvents[ev].color !== "undefined" && this.config.customEvents[ev].color !== "") { - needle = new RegExp(this.config.customEvents[ev].keyword, "gi"); + let needle = new RegExp(this.config.customEvents[ev].keyword, "gi"); if (needle.test(event.title)) { // Respect parameter ColoredSymbolOnly also for custom events if (!this.config.coloredSymbolOnly) { @@ -397,8 +397,8 @@ Module.register("calendar", { wrapper.appendChild(eventWrapper); // Create fade effect. - if (e >= startFade) { - currentFadeStep = e - startFade; + if (index >= startFade) { + currentFadeStep = index - startFade; eventWrapper.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep; } @@ -420,13 +420,13 @@ Module.register("calendar", { wrapper.appendChild(locationRow); - if (e >= startFade) { - currentFadeStep = e - startFade; + if (index >= startFade) { + currentFadeStep = index - startFade; locationRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep; } } } - } + }); return wrapper; }, @@ -480,10 +480,11 @@ Module.register("calendar", { const future = moment().startOf("day").add(this.config.maximumNumberOfDays, "days").toDate(); let events = []; - for (var c in this.calendarData) { - var calendar = this.calendarData[c]; - for (var e in calendar) { - var event = JSON.parse(JSON.stringify(calendar[e])); // clone object + for (const calendarUrl in this.calendarData) { + const calendar = this.calendarData[calendarUrl]; + console.log(calendar); + for (const e in calendar) { + const event = JSON.parse(JSON.stringify(calendar[e])); // clone object if (event.endDate < now) { continue; @@ -502,7 +503,7 @@ Module.register("calendar", { if (this.listContainsEvent(events, event)) { continue; } - event.url = c; + event.url = calendarUrl; 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, @@ -514,7 +515,7 @@ Module.register("calendar", { let midnight = moment(event.startDate, "x").clone().startOf("day").add(1, "day").format("x"); let count = 1; while (event.endDate > midnight) { - var thisEvent = JSON.parse(JSON.stringify(event)); // clone object + const thisEvent = JSON.parse(JSON.stringify(event)); // clone object thisEvent.today = thisEvent.startDate >= today && thisEvent.startDate < today + 24 * 60 * 60 * 1000; thisEvent.endDate = midnight; thisEvent.title += " (" + count + "/" + maxCount + ")"; @@ -737,13 +738,13 @@ Module.register("calendar", { } if (wrapEvents === true) { - var temp = ""; - var currentLine = ""; - var words = string.split(" "); - var line = 0; + const words = string.split(" "); + let temp = ""; + let currentLine = ""; + let line = 0; - for (var i = 0; i < words.length; i++) { - var word = words[i]; + for (let i = 0; i < words.length; i++) { + const word = words[i]; if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) { // max - 1 to account for a space currentLine += word + " "; @@ -798,10 +799,10 @@ Module.register("calendar", { * @returns {string} The transformed title. */ titleTransform: function (title, titleReplace, wrapEvents, maxTitleLength, maxTitleLines) { - for (var needle in titleReplace) { - var replacement = titleReplace[needle]; + for (let needle in titleReplace) { + const replacement = titleReplace[needle]; - var regParts = needle.match(/^\/(.+)\/([gim]*)$/); + const regParts = needle.match(/^\/(.+)\/([gim]*)$/); if (regParts) { // the parsed pattern is a regexp. needle = new RegExp(regParts[1], regParts[2]); @@ -819,11 +820,10 @@ Module.register("calendar", { * The all events available in one array, sorted on startdate. */ broadcastEvents: function () { - var eventList = []; - for (var url in this.calendarData) { - var calendar = this.calendarData[url]; - for (var e in calendar) { - var event = cloneObject(calendar[e]); + const eventList = []; + for (const url in this.calendarData) { + for (const ev of this.calendarData[url]) { + const event = cloneObject(ev); event.symbol = this.symbolsForEvent(event); event.calendarName = this.calendarNameForUrl(url); event.color = this.colorForUrl(url); From 71bd45dfd4833b27357395be911dbea015a3e5de Mon Sep 17 00:00:00 2001 From: rejas Date: Sun, 14 Mar 2021 10:01:52 +0100 Subject: [PATCH 12/13] Fix errors introduced after latest rebase --- modules/default/calendar/calendarfetcher.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 4c7d1020..5b6c79bc 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -64,12 +64,12 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn fetcher .catch((error) => { - fetchFailedCallback(self, error); + fetchFailedCallback(this, error); scheduleTimer(); }) .then((response) => { if (response.status !== 200) { - fetchFailedCallback(self, response.statusText); + fetchFailedCallback(this, response.statusText); scheduleTimer(); } return response; From 0643a103ac9810ff0ec96c32e3312302e34e1489 Mon Sep 17 00:00:00 2001 From: veeck Date: Sun, 14 Mar 2021 10:39:32 +0100 Subject: [PATCH 13/13] Update dependencies --- package-lock.json | 318 +++++++++++++++++++--------------------------- package.json | 20 +-- 2 files changed, 143 insertions(+), 195 deletions(-) diff --git a/package-lock.json b/package-lock.json index 15382503..bbea9c98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -370,6 +370,21 @@ "js-yaml": "^3.13.1", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "requires": { + "type-fest": "^0.8.1" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } } }, "@istanbuljs/load-nyc-config": { @@ -1286,9 +1301,9 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chai": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.3.tgz", - "integrity": "sha512-MPSLOZwxxnA0DhLE84klnGPojWFK5KuhP7/j5dTsxpr2S3XlkqJP5WbyYl1gCTWvG2Z5N+HD4F472WsbEZL6Pw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -1385,12 +1400,6 @@ } } }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, "clarinet": { "version": "0.12.4", "resolved": "https://registry.npmjs.org/clarinet/-/clarinet-0.12.4.tgz", @@ -1474,12 +1483,6 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true - }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -2065,9 +2068,9 @@ } }, "engine.io": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.1.tgz", - "integrity": "sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.0.0.tgz", + "integrity": "sha512-BATIdDV3H1SrE9/u2BAotvsmjJg0t1P4+vGedImSs1lkFAtQdvk4Ev1y4LDiPF7BPWgXWEG+NDY+nLvW3UrMWw==", "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", @@ -2143,18 +2146,24 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "requires": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", + "estraverse": "^5.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" }, "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -2197,9 +2206,9 @@ } }, "eslint": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.21.0.tgz", - "integrity": "sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==", + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.22.0.tgz", + "integrity": "sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg==", "requires": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.0", @@ -2218,7 +2227,7 @@ "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", - "globals": "^12.1.0", + "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -2226,7 +2235,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.20", + "lodash": "^4.17.21", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -2723,15 +2732,6 @@ "path-exists": "^4.0.0" } }, - "find-versions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", - "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", - "dev": true, - "requires": { - "semver-regex": "^3.1.2" - } - }, "flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -2975,17 +2975,17 @@ } }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.6.0.tgz", + "integrity": "sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ==", "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" }, "dependencies": { "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" } } }, @@ -3134,9 +3134,9 @@ "integrity": "sha512-G8tp0wUMI7i8wkMk2xLcEvESg5PiCitFMYgGRc/PwULB0RVhTP5GFdxOwvJwp9XVha8CuS8mnhmE8I/8dx/pbw==" }, "hosted-git-info": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", - "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.0.tgz", + "integrity": "sha512-fqhGdjk4av7mT9fU/B01dUtZ+WZSc/XEXMoLXDVZukiQRXxeHSSz3AqbeWRJHtF8EQYHlAgB1NSAHU0Cm7aqZA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -3251,22 +3251,10 @@ "dev": true }, "husky": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz", - "integrity": "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "compare-versions": "^3.6.0", - "cosmiconfig": "^7.0.0", - "find-versions": "^4.0.0", - "opencollective-postinstall": "^2.0.2", - "pkg-dir": "^5.0.0", - "please-upgrade-node": "^3.2.0", - "slash": "^3.0.0", - "which-pm-runs": "^1.0.0" - } + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-5.1.3.tgz", + "integrity": "sha512-fbNJ+Gz5wx2LIBtMweJNY1D7Uc8p1XERi5KNRMccwfQA+rXlxWNSdUxswo0gT8XqxywTIw7Ywm/F4v/O35RdMg==", + "dev": true }, "iconv-lite": { "version": "0.6.2", @@ -3343,12 +3331,6 @@ "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, "ip6": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/ip6/-/ip6-0.0.4.tgz", @@ -3614,49 +3596,61 @@ "dev": true }, "jsdom": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", - "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "version": "16.5.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.5.1.tgz", + "integrity": "sha512-pF73EOsJgwZekbDHEY5VO/yKXUkab/DuvrQB/ANVizbr6UAHJsDdHXuotZYwkJSGQl1JM+ivXaqY+XBDDL4TiA==", "dev": true, "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", + "abab": "^2.0.5", + "acorn": "^8.0.5", "acorn-globals": "^6.0.0", "cssom": "^0.4.4", - "cssstyle": "^2.2.0", + "cssstyle": "^2.3.0", "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", + "decimal.js": "^10.2.1", "domexception": "^2.0.1", - "escodegen": "^1.14.1", + "escodegen": "^2.0.0", "html-encoding-sniffer": "^2.0.1", "is-potential-custom-element-name": "^1.0.0", "nwsapi": "^2.2.0", - "parse5": "5.1.1", + "parse5": "6.0.1", "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", + "request-promise-native": "^1.0.9", + "saxes": "^5.0.1", "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", + "tough-cookie": "^4.0.0", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.0.0", - "ws": "^7.2.3", + "ws": "^7.4.4", "xml-name-validator": "^3.0.0" }, "dependencies": { + "acorn": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.0.tgz", + "integrity": "sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==", + "dev": true + }, "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", "dev": true, "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" } + }, + "ws": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==", + "dev": true } } }, @@ -3944,9 +3938,9 @@ } }, "map-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", - "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.0.tgz", + "integrity": "sha512-NAq0fCmZYGz9UFEQyndp7sisrow4GroyGeKluyKC/chuITZsPyOyC1UJZPJlVFImhXdROIP5xqouRLThT3BbpQ==", "dev": true }, "marky": { @@ -4172,9 +4166,9 @@ "dev": true }, "mocha": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.0.tgz", - "integrity": "sha512-TQqyC89V1J/Vxx0DhJIXlq9gbbL9XFNdeLQ1+JsnZsVaSOV1z3tWfw0qZmQJGQRIfkvZcs7snQnZnOCKoldq1Q==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.2.tgz", + "integrity": "sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", @@ -4348,25 +4342,14 @@ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-ical": { - "version": "0.12.8", - "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.12.8.tgz", - "integrity": "sha512-SocIwrKSSej/Ufv7a3E7AzffuMnXeydu8Dh6d1arlCMqG5+2qD1vlKohX97/TiTqCAuOXF6CxUHunT4nFEW9Zw==", + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.12.9.tgz", + "integrity": "sha512-5nUEZfZPpBpeZbmYCCmNRLsoP08+SGZy/fKxNBX9k67JMUTMFPLEyZ0CXApPDIExX0izMRndG1PsymhEkkSL2Q==", "requires": { "moment-timezone": "^0.5.31", "request": "^2.88.2", - "rrule": "2.6.6", + "rrule": "2.6.8", "uuid": "^8.3.1" - }, - "dependencies": { - "rrule": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.6.6.tgz", - "integrity": "sha512-h6tb/hRo9SNv8xKjcvsEfdmhXvElMXsU3Yz0KmqMehUqxP6a4Qjmth2EuL1FsjdawADjajLS0eBbWfsZzn3SIw==", - "requires": { - "luxon": "^1.21.3", - "tslib": "^1.10.0" - } - } } }, "node-preload": { @@ -4385,12 +4368,12 @@ "dev": true }, "normalize-package-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", - "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.1.tgz", + "integrity": "sha512-D/ttLdxo71msR4FF3VgSwK4blHfE3/vGByz1NCeE7/Dh8reQOKNJJjk5L10mLq9jxa+ZHzT1/HLgxljzbXE7Fw==", "dev": true, "requires": { - "hosted-git-info": "^3.0.6", + "hosted-git-info": "^4.0.0", "resolve": "^1.17.0", "semver": "^7.3.2", "validate-npm-package-license": "^3.0.1" @@ -4651,12 +4634,6 @@ "mimic-fn": "^2.1.0" } }, - "opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", - "dev": true - }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -4755,9 +4732,9 @@ } }, "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, "parseurl": { @@ -4826,24 +4803,6 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "optional": true }, - "pkg-dir": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", - "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", - "dev": true, - "requires": { - "find-up": "^5.0.0" - } - }, - "please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" - } - }, "postcss": { "version": "7.0.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", @@ -5730,13 +5689,8 @@ "semver-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" - }, - "semver-regex": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.2.tgz", - "integrity": "sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA==", - "dev": true + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "optional": true }, "send": { "version": "0.17.1", @@ -5898,9 +5852,9 @@ } }, "socket.io": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.2.tgz", - "integrity": "sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.0.0.tgz", + "integrity": "sha512-/c1riZMV/4yz7KEpaMhDQbwhJDIoO55whXaRKgyEBQrLU9zUHXo9rzeTMvTOqwL9mbKfHKdrXcMoCeQ/1YtMsg==", "requires": { "@types/cookie": "^0.4.0", "@types/cors": "^2.8.8", @@ -5908,15 +5862,15 @@ "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.1", - "engine.io": "~4.1.0", - "socket.io-adapter": "~2.1.0", + "engine.io": "~5.0.0", + "socket.io-adapter": "~2.2.0", "socket.io-parser": "~4.0.3" } }, "socket.io-adapter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz", - "integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.2.0.tgz", + "integrity": "sha512-rG49L+FwaVEwuAdeBRq49M97YI3ElVabJPzvHT9S6a2CWhDKnjSFasvwAwSYPRhQzfn4NtDIbCaGYgOCOU/rlg==" }, "socket.io-parser": { "version": "4.0.4", @@ -6099,9 +6053,9 @@ "dev": true }, "stylelint": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.11.0.tgz", - "integrity": "sha512-DhrKSWDWGZkCiQMtU+VroXM6LWJVC8hSK24nrUngTSQvXGK75yZUq4yNpynqrxD3a/fzKMED09V+XxO4z4lTbw==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.12.0.tgz", + "integrity": "sha512-P8O1xDy41B7O7iXaSlW+UuFbE5+ZWQDb61ndGDxKIt36fMH50DtlQTbwLpFLf8DikceTAb3r6nPrRv30wBlzXw==", "dev": true, "requires": { "@stylelint/postcss-css-in-js": "^0.37.2", @@ -6114,7 +6068,7 @@ "execall": "^2.0.0", "fast-glob": "^3.2.5", "fastest-levenshtein": "^1.0.12", - "file-entry-cache": "^6.0.0", + "file-entry-cache": "^6.0.1", "get-stdin": "^8.0.0", "global-modules": "^2.0.0", "globby": "^11.0.2", @@ -6124,7 +6078,7 @@ "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "known-css-properties": "^0.21.0", - "lodash": "^4.17.20", + "lodash": "^4.17.21", "log-symbols": "^4.0.0", "mathml-tag-names": "^2.1.3", "meow": "^9.0.0", @@ -6144,7 +6098,7 @@ "resolve-from": "^5.0.0", "slash": "^3.0.0", "specificity": "^0.4.1", - "string-width": "^4.2.0", + "string-width": "^4.2.2", "strip-ansi": "^6.0.0", "style-search": "^0.1.0", "sugarss": "^2.0.0", @@ -6175,18 +6129,18 @@ "dev": true }, "stylelint-config-recommended": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-3.0.0.tgz", - "integrity": "sha512-F6yTRuc06xr1h5Qw/ykb2LuFynJ2IxkKfCMf+1xqPffkxh0S09Zc902XCffcsw/XMFq/OzQ1w54fLIDtmRNHnQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-4.0.0.tgz", + "integrity": "sha512-sgna89Ng+25Hr9kmmaIxpGWt2LStVm1xf1807PdcWasiPDaOTkOHRL61sINw0twky7QMzafCGToGDnHT/kTHtQ==", "dev": true }, "stylelint-config-standard": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-20.0.0.tgz", - "integrity": "sha512-IB2iFdzOTA/zS4jSVav6z+wGtin08qfj+YyExHB3LF9lnouQht//YyB0KZq9gGz5HNPkddHOzcY8HsUey6ZUlA==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-21.0.0.tgz", + "integrity": "sha512-Yf6mx5oYEbQQJxWuW7X3t1gcxqbUx52qC9SMS3saC2ruOVYEyqmr5zSW6k3wXflDjjFrPhar3kp68ugRopmlzg==", "dev": true, "requires": { - "stylelint-config-recommended": "^3.0.0" + "stylelint-config-recommended": "^4.0.0" } }, "stylelint-prettier": { @@ -6247,9 +6201,9 @@ }, "dependencies": { "ajv": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz", - "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.1.tgz", + "integrity": "sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ==", "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -6493,9 +6447,9 @@ } }, "unist-util-is": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", - "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", "dev": true }, "unist-util-stringify-position": { @@ -6549,9 +6503,9 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" }, "valid-url": { "version": "1.0.9", @@ -6887,12 +6841,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "which-pm-runs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", - "dev": true - }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -7003,9 +6951,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true }, "yargs": { diff --git a/package.json b/package.json index 0120b8c1..566b2d0e 100644 --- a/package.json +++ b/package.json @@ -43,16 +43,16 @@ }, "homepage": "https://magicmirror.builders", "devDependencies": { - "chai": "^4.3.0", + "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "eslint-config-prettier": "^8.1.0", "eslint-plugin-jsdoc": "^32.2.0", "eslint-plugin-prettier": "^3.3.1", "express-basic-auth": "^1.2.0", - "husky": "^4.3.8", - "jsdom": "^16.4.0", + "husky": "^5.1.3", + "jsdom": "^16.5.1", "lodash": "^4.17.21", - "mocha": "^8.3.0", + "mocha": "^8.3.2", "mocha-each": "^2.0.1", "mocha-logger": "^1.0.7", "nyc": "^15.1.0", @@ -60,10 +60,10 @@ "pretty-quick": "^3.1.0", "sinon": "^9.2.4", "spectron": "^13.0.0", - "stylelint": "^13.11.0", + "stylelint": "^13.12.0", "stylelint-config-prettier": "^8.0.2", - "stylelint-config-standard": "^20.0.0", - "stylelint-prettier": "^1.1.2" + "stylelint-config-standard": "^21.0.0", + "stylelint-prettier": "^1.2.0" }, "optionalDependencies": { "electron": "^11.3.0" @@ -72,7 +72,7 @@ "colors": "^1.4.0", "console-stamp": "^3.0.0-rc4.2", "digest-fetch": "^1.1.6", - "eslint": "^7.20.0", + "eslint": "^7.22.0", "express": "^4.17.1", "express-ipfilter": "^1.1.2", "feedme": "^2.0.2", @@ -81,11 +81,11 @@ "module-alias": "^2.2.2", "moment": "^2.29.1", "node-fetch": "^2.6.1", - "node-ical": "^0.12.8", + "node-ical": "^0.12.9", "rrule": "^2.6.8", "rrule-alt": "^2.2.8", "simple-git": "^2.36.2", - "socket.io": "^3.1.2", + "socket.io": "^4.0.0", "valid-url": "^1.0.9" }, "_moduleAliases": {