diff --git a/CHANGELOG.md b/CHANGELOG.md index 08610111f6..efd7e19d71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,12 @@ _This release is scheduled to be released on 2021-01-01._ ### Fixed +- Fixed the "un-hide on update" problem with "currentwather" and "watherforcast" modules. +- JSON Parse translation files with comments crashing UI. (#2149) +- Calendar parsing where RRULE bug returns wrong date, add Windows timezone name support. (#2145, #2151) +- Wrong node-ical version installed (package.json) requested version. (#2153) +- Fix calendar fetcher subsequent timing (#2160) + ## [2.13.0] - 2020-10-01 Special thanks to the following contributors: @bryanzzhu, @bugsounet, @chamakura, @cjbrunner, @easyas314, @larryare, @oemel09, @rejas, @sdetweil & @sthuber90. diff --git a/js/translator.js b/js/translator.js index 0f4bc00a23..5dc500456a 100644 --- a/js/translator.js +++ b/js/translator.js @@ -19,7 +19,15 @@ var Translator = (function () { xhr.open("GET", file, true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { - callback(JSON.parse(xhr.responseText)); + // needs error handler try/catch at least + let fileinfo = null; + try { + fileinfo = JSON.parse(xhr.responseText); + } catch (exception) { + // nothing here, but don't die + Log.error(" loading json file =" + file + " failed"); + } + callback(fileinfo); } }; xhr.send(null); diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index af8041398b..b2737586f5 100755 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -58,6 +58,8 @@ Module.register("calendar", { nextDaysRelative: false }, + requiresVersion: "2.1.0", + // Define required scripts. getStyles: function () { return ["calendar.css", "font-awesome.css"]; @@ -83,6 +85,12 @@ Module.register("calendar", { // Set locale. moment.updateLocale(config.language, this.getLocaleSpecification(config.timeFormat)); + // clear data holder before start + this.calendarData = {}; + + // indicate no data available yet + this.loaded = false; + for (var c in this.config.calendars) { var calendar = this.config.calendars[c]; calendar.url = calendar.url.replace("webcal://", "http://"); @@ -112,18 +120,10 @@ Module.register("calendar", { }; } + // tell helper to start a fetcher for this calendar + // fetcher till cycle this.addCalendar(calendar.url, calendar.auth, calendarConfig); - - // Trigger ADD_CALENDAR every fetchInterval to make sure there is always a calendar - // fetcher running on the server side. - var self = this; - setInterval(function () { - self.addCalendar(calendar.url, calendar.auth, calendarConfig); - }, self.config.fetchInterval); } - - this.calendarData = {}; - this.loaded = false; }, // Override socket notification handler. @@ -541,6 +541,8 @@ 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, diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 964cb95563..f153f7c0bb 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -110,7 +110,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn if (event.type === "VEVENT") { let startDate = eventDate(event, "start"); let endDate; - + // console.log("\nevent="+JSON.stringify(event)) if (typeof event.end !== "undefined") { endDate = eventDate(event, "end"); } else if (typeof event.duration !== "undefined") { @@ -212,8 +212,9 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn pastLocal = pastMoment.subtract(past.getTimezoneOffset(), "minutes").toDate(); futureLocal = futureMoment.subtract(future.getTimezoneOffset(), "minutes").toDate(); } - const dates = rule.between(pastLocal, futureLocal, true, limitFunction); + const dates = rule.between(pastLocal, futureLocal, true, limitFunction); + // console.log("title="+event.summary.val+" 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, @@ -242,6 +243,53 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn let showRecurrence = true; startDate = moment(date); + // console.log("now timezone="+ moment.tz.guess()); + // whether we need to adjust for RRULE returning the wrong date, with the right time (forward of URC timezones) + let adjustDays = 0; + // if a timezone was specified + if (!event.start.tz) { + event.start.tz = moment.tz.guess(); + } + // console.log("tz="+event.start.tz) + if (event.start.tz) { + // if this is a windows timezone + if (event.start.tz.indexOf(" ") > 0) { + // use the lookup table to get theIANA name as moment and date don't know MS timezones + let tz = getIanaTZFromMS(event.start.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 = getIanaTZFromMS(event.start.tz); + // console.log("corrected timezone="+event.start.tz) + } + } + // get the start time in that timezone + let mms = moment.tz(moment(event.start), event.start.tz).utcOffset(); + // console.log("ms offset="+mms) + // get the specified date in that timezone + let mm = moment.tz(moment(date), event.start.tz); + let mmo = mm.utcOffset(); + // console.log("mm ofset="+ mmo+" hour="+mm.format("H")+" event date="+mm.toDate()) + // if the offset is greater than 0, east of london + if (mmo > 0) { + let h = parseInt(mm.format("H")); + // check if the event time is less than the offset + if (h > 0 && h < mmo / 60) { + // if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time) + // we need to fix that + adjustDays = 24; + // console.log("adjusting date") + } + if (mmo > mms) { + adjustDays += 1; + // console.log("adjust up 1 hour dst change") + } else if (mmo < mms) { + adjustDays -= 1; + //console.log("adjust down 1 hour dst change") + } + } + } // 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) { @@ -256,6 +304,8 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn showRecurrence = false; } + //console.log("duration="+duration) + endDate = moment(parseInt(startDate.format("x")) + duration, "x"); if (startDate.format("x") === endDate.format("x")) { endDate = endDate.endOf("day"); @@ -277,8 +327,8 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn addedEvents++; newEvents.push({ title: recurrenceTitle, - startDate: startDate.format("x"), - endDate: endDate.format("x"), + startDate: (adjustDays ? startDate.subtract(adjustDays, "hours") : startDate).format("x"), + endDate: (adjustDays ? endDate.subtract(adjustDays, "hours") : endDate).format("x"), fullDayEvent: isFullDayEvent(event), recurringEvent: true, class: event.class, @@ -293,7 +343,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn } else { // Single event. const fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event); - + // console.log("full day event") if (includePastEvents) { // Past event is too far in the past, so skip. if (endDate < past) { @@ -324,6 +374,11 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn 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")) { + //console.log("end same as start") + endDate = endDate.endOf("day"); + } // Every thing is good. Add it to the list. newEvents.push({ @@ -351,6 +406,22 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn }); }; + /** + * + * 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. */ diff --git a/modules/default/calendar/node_helper.js b/modules/default/calendar/node_helper.js index 862755a03c..06fa28ec41 100644 --- a/modules/default/calendar/node_helper.js +++ b/modules/default/calendar/node_helper.js @@ -70,7 +70,6 @@ module.exports = NodeHelper.create({ } else { Log.log("Use existing calendar fetcher for url: " + url); fetcher = self.fetchers[identifier + url]; - fetcher.broadcastEvents(); } fetcher.startFetch(); diff --git a/modules/default/calendar/windowsZones.json b/modules/default/calendar/windowsZones.json new file mode 100644 index 0000000000..2e6bd6b3fe --- /dev/null +++ b/modules/default/calendar/windowsZones.json @@ -0,0 +1,139 @@ +{ + "Dateline Standard Time": { "iana": ["Etc/GMT+12"] }, + "UTC-11": { "iana": ["Etc/GMT+11"] }, + "Aleutian Standard Time": { "iana": ["America/Adak"] }, + "Hawaiian Standard Time": { "iana": ["Pacific/Honolulu"] }, + "Marquesas Standard Time": { "iana": ["Pacific/Marquesas"] }, + "Alaskan Standard Time": { "iana": ["America/Anchorage"] }, + "UTC-09": { "iana": ["Etc/GMT+9"] }, + "Pacific Standard Time (Mexico)": { "iana": ["America/Tijuana"] }, + "UTC-08": { "iana": ["Etc/GMT+8"] }, + "Pacific Standard Time": { "iana": ["America/Los_Angeles"] }, + "US Mountain Standard Time": { "iana": ["America/Phoenix"] }, + "Mountain Standard Time (Mexico)": { "iana": ["America/Chihuahua"] }, + "Mountain Standard Time": { "iana": ["America/Denver"] }, + "Central America Standard Time": { "iana": ["America/Guatemala"] }, + "Central Standard Time": { "iana": ["America/Chicago"] }, + "Easter Island Standard Time": { "iana": ["Pacific/Easter"] }, + "Central Standard Time (Mexico)": { "iana": ["America/Mexico_City"] }, + "Canada Central Standard Time": { "iana": ["America/Regina"] }, + "SA Pacific Standard Time": { "iana": ["America/Bogota"] }, + "Eastern Standard Time (Mexico)": { "iana": ["America/Cancun"] }, + "Eastern Standard Time": { "iana": ["America/New_York"] }, + "Haiti Standard Time": { "iana": ["America/Port-au-Prince"] }, + "Cuba Standard Time": { "iana": ["America/Havana"] }, + "US Eastern Standard Time": { "iana": ["America/Indianapolis"] }, + "Turks And Caicos Standard Time": { "iana": ["America/Grand_Turk"] }, + "Paraguay Standard Time": { "iana": ["America/Asuncion"] }, + "Atlantic Standard Time": { "iana": ["America/Halifax"] }, + "Venezuela Standard Time": { "iana": ["America/Caracas"] }, + "Central Brazilian Standard Time": { "iana": ["America/Cuiaba"] }, + "SA Western Standard Time": { "iana": ["America/La_Paz"] }, + "Pacific SA Standard Time": { "iana": ["America/Santiago"] }, + "Newfoundland Standard Time": { "iana": ["America/St_Johns"] }, + "Tocantins Standard Time": { "iana": ["America/Araguaina"] }, + "E. South America Standard Time": { "iana": ["America/Sao_Paulo"] }, + "SA Eastern Standard Time": { "iana": ["America/Cayenne"] }, + "Argentina Standard Time": { "iana": ["America/Buenos_Aires"] }, + "Greenland Standard Time": { "iana": ["America/Godthab"] }, + "Montevideo Standard Time": { "iana": ["America/Montevideo"] }, + "Magallanes Standard Time": { "iana": ["America/Punta_Arenas"] }, + "Saint Pierre Standard Time": { "iana": ["America/Miquelon"] }, + "Bahia Standard Time": { "iana": ["America/Bahia"] }, + "UTC-02": { "iana": ["Etc/GMT+2"] }, + "Azores Standard Time": { "iana": ["Atlantic/Azores"] }, + "Cape Verde Standard Time": { "iana": ["Atlantic/Cape_Verde"] }, + "UTC": { "iana": ["Etc/GMT"] }, + "GMT Standard Time": { "iana": ["Europe/London"] }, + "Greenwich Standard Time": { "iana": ["Atlantic/Reykjavik"] }, + "Sao Tome Standard Time": { "iana": ["Africa/Sao_Tome"] }, + "Morocco Standard Time": { "iana": ["Africa/Casablanca"] }, + "W. Europe Standard Time": { "iana": ["Europe/Berlin"] }, + "Central Europe Standard Time": { "iana": ["Europe/Budapest"] }, + "Romance Standard Time": { "iana": ["Europe/Paris"] }, + "Central European Standard Time": { "iana": ["Europe/Warsaw"] }, + "W. Central Africa Standard Time": { "iana": ["Africa/Lagos"] }, + "Jordan Standard Time": { "iana": ["Asia/Amman"] }, + "GTB Standard Time": { "iana": ["Europe/Bucharest"] }, + "Middle East Standard Time": { "iana": ["Asia/Beirut"] }, + "Egypt Standard Time": { "iana": ["Africa/Cairo"] }, + "E. Europe Standard Time": { "iana": ["Europe/Chisinau"] }, + "Syria Standard Time": { "iana": ["Asia/Damascus"] }, + "West Bank Standard Time": { "iana": ["Asia/Hebron"] }, + "South Africa Standard Time": { "iana": ["Africa/Johannesburg"] }, + "FLE Standard Time": { "iana": ["Europe/Kiev"] }, + "Israel Standard Time": { "iana": ["Asia/Jerusalem"] }, + "Kaliningrad Standard Time": { "iana": ["Europe/Kaliningrad"] }, + "Sudan Standard Time": { "iana": ["Africa/Khartoum"] }, + "Libya Standard Time": { "iana": ["Africa/Tripoli"] }, + "Namibia Standard Time": { "iana": ["Africa/Windhoek"] }, + "Arabic Standard Time": { "iana": ["Asia/Baghdad"] }, + "Turkey Standard Time": { "iana": ["Europe/Istanbul"] }, + "Arab Standard Time": { "iana": ["Asia/Riyadh"] }, + "Belarus Standard Time": { "iana": ["Europe/Minsk"] }, + "Russian Standard Time": { "iana": ["Europe/Moscow"] }, + "E. Africa Standard Time": { "iana": ["Africa/Nairobi"] }, + "Iran Standard Time": { "iana": ["Asia/Tehran"] }, + "Arabian Standard Time": { "iana": ["Asia/Dubai"] }, + "Astrakhan Standard Time": { "iana": ["Europe/Astrakhan"] }, + "Azerbaijan Standard Time": { "iana": ["Asia/Baku"] }, + "Russia Time Zone 3": { "iana": ["Europe/Samara"] }, + "Mauritius Standard Time": { "iana": ["Indian/Mauritius"] }, + "Saratov Standard Time": { "iana": ["Europe/Saratov"] }, + "Georgian Standard Time": { "iana": ["Asia/Tbilisi"] }, + "Volgograd Standard Time": { "iana": ["Europe/Volgograd"] }, + "Caucasus Standard Time": { "iana": ["Asia/Yerevan"] }, + "Afghanistan Standard Time": { "iana": ["Asia/Kabul"] }, + "West Asia Standard Time": { "iana": ["Asia/Tashkent"] }, + "Ekaterinburg Standard Time": { "iana": ["Asia/Yekaterinburg"] }, + "Pakistan Standard Time": { "iana": ["Asia/Karachi"] }, + "Qyzylorda Standard Time": { "iana": ["Asia/Qyzylorda"] }, + "India Standard Time": { "iana": ["Asia/Calcutta"] }, + "Sri Lanka Standard Time": { "iana": ["Asia/Colombo"] }, + "Nepal Standard Time": { "iana": ["Asia/Katmandu"] }, + "Central Asia Standard Time": { "iana": ["Asia/Almaty"] }, + "Bangladesh Standard Time": { "iana": ["Asia/Dhaka"] }, + "Omsk Standard Time": { "iana": ["Asia/Omsk"] }, + "Myanmar Standard Time": { "iana": ["Asia/Rangoon"] }, + "SE Asia Standard Time": { "iana": ["Asia/Bangkok"] }, + "Altai Standard Time": { "iana": ["Asia/Barnaul"] }, + "W. Mongolia Standard Time": { "iana": ["Asia/Hovd"] }, + "North Asia Standard Time": { "iana": ["Asia/Krasnoyarsk"] }, + "N. Central Asia Standard Time": { "iana": ["Asia/Novosibirsk"] }, + "Tomsk Standard Time": { "iana": ["Asia/Tomsk"] }, + "China Standard Time": { "iana": ["Asia/Shanghai"] }, + "North Asia East Standard Time": { "iana": ["Asia/Irkutsk"] }, + "Singapore Standard Time": { "iana": ["Asia/Singapore"] }, + "W. Australia Standard Time": { "iana": ["Australia/Perth"] }, + "Taipei Standard Time": { "iana": ["Asia/Taipei"] }, + "Ulaanbaatar Standard Time": { "iana": ["Asia/Ulaanbaatar"] }, + "Aus Central W. Standard Time": { "iana": ["Australia/Eucla"] }, + "Transbaikal Standard Time": { "iana": ["Asia/Chita"] }, + "Tokyo Standard Time": { "iana": ["Asia/Tokyo"] }, + "North Korea Standard Time": { "iana": ["Asia/Pyongyang"] }, + "Korea Standard Time": { "iana": ["Asia/Seoul"] }, + "Yakutsk Standard Time": { "iana": ["Asia/Yakutsk"] }, + "Cen. Australia Standard Time": { "iana": ["Australia/Adelaide"] }, + "AUS Central Standard Time": { "iana": ["Australia/Darwin"] }, + "E. Australia Standard Time": { "iana": ["Australia/Brisbane"] }, + "AUS Eastern Standard Time": { "iana": ["Australia/Sydney"] }, + "West Pacific Standard Time": { "iana": ["Pacific/Port_Moresby"] }, + "Tasmania Standard Time": { "iana": ["Australia/Hobart"] }, + "Vladivostok Standard Time": { "iana": ["Asia/Vladivostok"] }, + "Lord Howe Standard Time": { "iana": ["Australia/Lord_Howe"] }, + "Bougainville Standard Time": { "iana": ["Pacific/Bougainville"] }, + "Russia Time Zone 10": { "iana": ["Asia/Srednekolymsk"] }, + "Magadan Standard Time": { "iana": ["Asia/Magadan"] }, + "Norfolk Standard Time": { "iana": ["Pacific/Norfolk"] }, + "Sakhalin Standard Time": { "iana": ["Asia/Sakhalin"] }, + "Central Pacific Standard Time": { "iana": ["Pacific/Guadalcanal"] }, + "Russia Time Zone 11": { "iana": ["Asia/Kamchatka"] }, + "New Zealand Standard Time": { "iana": ["Pacific/Auckland"] }, + "UTC+12": { "iana": ["Etc/GMT-12"] }, + "Fiji Standard Time": { "iana": ["Pacific/Fiji"] }, + "Chatham Islands Standard Time": { "iana": ["Pacific/Chatham"] }, + "UTC+13": { "iana": ["Etc/GMT-13"] }, + "Tonga Standard Time": { "iana": ["Pacific/Tongatapu"] }, + "Samoa Standard Time": { "iana": ["Pacific/Apia"] }, + "Line Islands Standard Time": { "iana": ["Pacific/Kiritimati"] } +} diff --git a/modules/default/currentweather/currentweather.js b/modules/default/currentweather/currentweather.js index 0a43b23e6b..376732a9b5 100644 --- a/modules/default/currentweather/currentweather.js +++ b/modules/default/currentweather/currentweather.js @@ -495,11 +495,12 @@ Module.register("currentweather", { this.sunriseSunsetTime = timeString; this.sunriseSunsetIcon = sunrise < now && sunset > now ? "wi-sunset" : "wi-sunrise"; - - this.show(this.config.animationSpeed, { lockString: this.identifier }); - this.loaded = true; - this.updateDom(this.config.animationSpeed); - this.sendNotification("CURRENTWEATHER_DATA", { data: data }); + if (!this.hidden) { + this.show(this.config.animationSpeed, { lockString: this.identifier }); + this.loaded = true; + this.updateDom(this.config.animationSpeed); + this.sendNotification("CURRENTWEATHER_DATA", { data: data }); + } }, /* scheduleUpdate() diff --git a/modules/default/weatherforecast/weatherforecast.js b/modules/default/weatherforecast/weatherforecast.js index 4ecc18aae9..f855002271 100644 --- a/modules/default/weatherforecast/weatherforecast.js +++ b/modules/default/weatherforecast/weatherforecast.js @@ -408,9 +408,11 @@ Module.register("weatherforecast", { } //Log.log(this.forecast); - this.show(this.config.animationSpeed, { lockString: this.identifier }); - this.loaded = true; - this.updateDom(this.config.animationSpeed); + if (!this.hidden) { + this.show(this.config.animationSpeed, { lockString: this.identifier }); + this.loaded = true; + this.updateDom(this.config.animationSpeed); + } }, /* scheduleUpdate() diff --git a/package-lock.json b/package-lock.json index 204e37a2dd..3eb0550ea3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5723,9 +5723,9 @@ "dev": true }, "node-ical": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.12.0.tgz", - "integrity": "sha512-whPA/GABFAWMVzqKeTuBjzPGCfNR9eoCSWPHE6MkHyDlQqScdVfyWr0dRy50Lvfz9JCNqFqiko1GpHJ21pn8YA==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.12.1.tgz", + "integrity": "sha512-1YlvRyfOs0fUr8KWtgj4r/4VMI0tuBLfVSyrmDk00jLuUkXouiSU0PptEGdJs3U3pibF8ZThU6rVVS/jkcXJBQ==", "requires": { "moment-timezone": "^0.5.31", "request": "^2.88.2", diff --git a/package.json b/package.json index 8178fba398..195099ec12 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "iconv-lite": "^0.6.2", "module-alias": "^2.2.2", "moment": "^2.28.0", - "node-ical": "^0.12.0", + "node-ical": "^0.12.1", "request": "^2.88.2", "rrule": "^2.6.6", "rrule-alt": "^2.2.8",