mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-07-01 21:43:26 +00:00
Merge pull request #2206 from Alvinger/calendar-enhance
Calendar fixes and updates
This commit is contained in:
commit
f54690c829
@ -16,6 +16,8 @@ _This release is scheduled to be released on 2021-01-01._
|
|||||||
- Added Hindi & Gujarati translation.
|
- Added Hindi & Gujarati translation.
|
||||||
- Chuvash translation.
|
- Chuvash translation.
|
||||||
|
|
||||||
|
- Calendar: new options "limitDays" and "coloredEvents"
|
||||||
|
|
||||||
### Updated
|
### Updated
|
||||||
|
|
||||||
- Weather module - forecast now show TODAY and TOMORROW instead of weekday, to make it easier to understand.
|
- Weather module - forecast now show TODAY and TOMORROW instead of weekday, to make it easier to understand.
|
||||||
@ -34,6 +36,7 @@ _This release is scheduled to be released on 2021-01-01._
|
|||||||
- Add a space after icons of sunrise and sunset (#2169)
|
- Add a space after icons of sunrise and sunset (#2169)
|
||||||
- Fix calendar when no DTEND record found in event, startDate overlay when endDate set (#2177)
|
- Fix calendar when no DTEND record found in event, startDate overlay when endDate set (#2177)
|
||||||
- Fix calendar full day event east of UTC start time (#2200)
|
- Fix calendar full day event east of UTC start time (#2200)
|
||||||
|
- Corrected logic for timeFormat "relative" and "absolute"
|
||||||
|
|
||||||
## [2.13.0] - 2020-10-01
|
## [2.13.0] - 2020-10-01
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ Module.register("calendar", {
|
|||||||
defaults: {
|
defaults: {
|
||||||
maximumEntries: 10, // Total Maximum Entries
|
maximumEntries: 10, // Total Maximum Entries
|
||||||
maximumNumberOfDays: 365,
|
maximumNumberOfDays: 365,
|
||||||
|
limitDays: 0, // Limit the number of days shown, 0 = no limit
|
||||||
displaySymbol: true,
|
displaySymbol: true,
|
||||||
defaultSymbol: "calendar", // Fontawesome Symbol see https://fontawesome.com/cheatsheet?from=io
|
defaultSymbol: "calendar", // Fontawesome Symbol see https://fontawesome.com/cheatsheet?from=io
|
||||||
showLocation: false,
|
showLocation: false,
|
||||||
@ -37,6 +38,7 @@ Module.register("calendar", {
|
|||||||
hideOngoing: false,
|
hideOngoing: false,
|
||||||
colored: false,
|
colored: false,
|
||||||
coloredSymbolOnly: false,
|
coloredSymbolOnly: false,
|
||||||
|
coloredEvents: [], // Array of Keyword/Color where Keyword is a regexp and color the color code to use when regexp matches
|
||||||
tableClass: "small",
|
tableClass: "small",
|
||||||
calendars: [
|
calendars: [
|
||||||
{
|
{
|
||||||
@ -153,6 +155,12 @@ Module.register("calendar", {
|
|||||||
|
|
||||||
// Override dom generator.
|
// Override dom generator.
|
||||||
getDom: function () {
|
getDom: function () {
|
||||||
|
// Define second, minute, hour, and day constants
|
||||||
|
const oneSecond = 1000; // 1,000 milliseconds
|
||||||
|
const oneMinute = oneSecond * 60;
|
||||||
|
const oneHour = oneMinute * 60;
|
||||||
|
const oneDay = oneHour * 24;
|
||||||
|
|
||||||
var events = this.createEventList();
|
var events = this.createEventList();
|
||||||
var wrapper = document.createElement("table");
|
var wrapper = document.createElement("table");
|
||||||
wrapper.className = this.config.tableClass;
|
wrapper.className = this.config.tableClass;
|
||||||
@ -248,6 +256,18 @@ Module.register("calendar", {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.config.coloredEvents.length > 0) {
|
||||||
|
for (var ev in this.config.coloredEvents) {
|
||||||
|
var needle = new RegExp(this.config.coloredEvents[ev].keyword, "gi");
|
||||||
|
if (needle.test(event.title)) {
|
||||||
|
eventWrapper.style.cssText = "color:" + this.config.coloredEvents[ev].color;
|
||||||
|
titleWrapper.style.cssText = "color:" + this.config.coloredEvents[ev].color;
|
||||||
|
if (this.config.displaySymbol) symbolWrapper.style.cssText = "color:" + this.config.coloredEvents[ev].color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
titleWrapper.innerHTML = this.titleTransform(event.title, this.config.titleReplace, this.config.wrapEvents, this.config.maxTitleLength, this.config.maxTitleLines) + repeatingCountTitle;
|
titleWrapper.innerHTML = this.titleTransform(event.title, this.config.titleReplace, this.config.wrapEvents, this.config.maxTitleLength, this.config.maxTitleLines) + repeatingCountTitle;
|
||||||
|
|
||||||
var titleClass = this.titleClassForUrl(event.url);
|
var titleClass = this.titleClassForUrl(event.url);
|
||||||
@ -280,82 +300,56 @@ Module.register("calendar", {
|
|||||||
|
|
||||||
eventWrapper.appendChild(titleWrapper);
|
eventWrapper.appendChild(titleWrapper);
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
// Define second, minute, hour, and day variables
|
|
||||||
var oneSecond = 1000; // 1,000 milliseconds
|
if (this.config.timeFormat === "absolute") {
|
||||||
var oneMinute = oneSecond * 60;
|
// Use dateFormat
|
||||||
var oneHour = oneMinute * 60;
|
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
|
||||||
var oneDay = oneHour * 24;
|
// Add end time if showEnd
|
||||||
if (event.fullDayEvent) {
|
if (this.config.showEnd) {
|
||||||
//subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day
|
timeWrapper.innerHTML += "-";
|
||||||
event.endDate -= oneSecond;
|
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat));
|
||||||
if (event.today) {
|
}
|
||||||
timeWrapper.innerHTML = this.capFirst(this.translate("TODAY"));
|
// For full day events we use the fullDayEventDateFormat
|
||||||
} else if (event.startDate - now < oneDay && event.startDate - now > 0) {
|
if (event.fullDayEvent) {
|
||||||
timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW"));
|
//subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day
|
||||||
} else if (event.startDate - now < 2 * oneDay && event.startDate - now > 0) {
|
event.endDate -= oneSecond;
|
||||||
if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") {
|
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat));
|
||||||
timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW"));
|
}
|
||||||
} else {
|
if (this.config.getRelative > 0 && event.startDate < now) {
|
||||||
|
// Ongoing and getRelative is set
|
||||||
|
timeWrapper.innerHTML = this.capFirst(
|
||||||
|
this.translate("RUNNING", {
|
||||||
|
fallback: this.translate("RUNNING") + " {timeUntilEnd}",
|
||||||
|
timeUntilEnd: moment(event.endDate, "x").fromNow(true)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (this.config.urgency > 0 && event.startDate - now < this.config.urgency * oneDay) {
|
||||||
|
// Within urgency days
|
||||||
|
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||||
|
}
|
||||||
|
if (event.fullDayEvent && this.config.nextDaysRelative) {
|
||||||
|
// Full days events within the next two days
|
||||||
|
if (event.today) {
|
||||||
|
timeWrapper.innerHTML = this.capFirst(this.translate("TODAY"));
|
||||||
|
} else if (event.startDate - now < oneDay && event.startDate - now > 0) {
|
||||||
|
timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW"));
|
||||||
|
} else if (event.startDate - now < 2 * oneDay && event.startDate - now > 0) {
|
||||||
|
if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") {
|
||||||
|
timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show relative times
|
||||||
|
if (event.startDate >= now) {
|
||||||
|
// Use relative time
|
||||||
|
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
|
||||||
|
if (event.startDate - now < this.config.getRelative * oneHour) {
|
||||||
|
// If event is within getRelative hours, display 'in xxx' time format or moment.fromNow()
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Check to see if the user displays absolute or relative dates with their events
|
// Ongoing event
|
||||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
|
||||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
|
||||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
|
||||||
*
|
|
||||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
|
||||||
*/
|
|
||||||
if (this.config.timeFormat === "absolute") {
|
|
||||||
if (this.config.urgency > 1 && event.startDate - now < this.config.urgency * oneDay) {
|
|
||||||
// This event falls within the config.urgency period that the user has set
|
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
|
|
||||||
} else {
|
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.config.showEnd) {
|
|
||||||
timeWrapper.innerHTML += "-";
|
|
||||||
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.fullDayEventDateFormat));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (event.startDate >= new Date()) {
|
|
||||||
if (event.startDate - now < 2 * oneDay) {
|
|
||||||
// This event is within the next 48 hours (2 days)
|
|
||||||
if (event.startDate - now < this.config.getRelative * oneHour) {
|
|
||||||
// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
|
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
|
||||||
} else {
|
|
||||||
if (this.config.timeFormat === "absolute" && !this.config.nextDaysRelative) {
|
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
|
|
||||||
} else {
|
|
||||||
// Otherwise just say 'Today/Tomorrow at such-n-such time'
|
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Check to see if the user displays absolute or relative dates with their events
|
|
||||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
|
||||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
|
||||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
|
||||||
*
|
|
||||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
|
||||||
*/
|
|
||||||
if (this.config.timeFormat === "absolute") {
|
|
||||||
if (this.config.urgency > 1 && event.startDate - now < this.config.urgency * oneDay) {
|
|
||||||
// This event falls within the config.urgency period that the user has set
|
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
|
||||||
} else {
|
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
timeWrapper.innerHTML = this.capFirst(
|
timeWrapper.innerHTML = this.capFirst(
|
||||||
this.translate("RUNNING", {
|
this.translate("RUNNING", {
|
||||||
fallback: this.translate("RUNNING") + " {timeUntilEnd}",
|
fallback: this.translate("RUNNING") + " {timeUntilEnd}",
|
||||||
@ -363,12 +357,7 @@ Module.register("calendar", {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (this.config.showEnd) {
|
|
||||||
timeWrapper.innerHTML += "-";
|
|
||||||
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
|
|
||||||
timeWrapper.className = "time light " + this.timeClassForUrl(event.url);
|
timeWrapper.className = "time light " + this.timeClassForUrl(event.url);
|
||||||
eventWrapper.appendChild(timeWrapper);
|
eventWrapper.appendChild(timeWrapper);
|
||||||
}
|
}
|
||||||
@ -521,6 +510,35 @@ Module.register("calendar", {
|
|||||||
events.sort(function (a, b) {
|
events.sort(function (a, b) {
|
||||||
return a.startDate - b.startDate;
|
return a.startDate - b.startDate;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
// if date of event is later than lastdate
|
||||||
|
// check if we already are showing max unique days
|
||||||
|
if (eventDate > lastDate) {
|
||||||
|
// if the only entry in the first day is a full day event that day is not counted as unique
|
||||||
|
if (newEvents.length === 1 && days === 1 && newEvents[0].fullDayEvent) {
|
||||||
|
days--;
|
||||||
|
}
|
||||||
|
days++;
|
||||||
|
if (days > this.config.limitDays) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
lastDate = eventDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newEvents.push(ev);
|
||||||
|
}
|
||||||
|
events = newEvents;
|
||||||
|
}
|
||||||
|
|
||||||
return events.slice(0, this.config.maximumEntries);
|
return events.slice(0, this.config.maximumEntries);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -376,7 +376,21 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
|
|||||||
return a.startDate - b.startDate;
|
return a.startDate - b.startDate;
|
||||||
});
|
});
|
||||||
|
|
||||||
events = newEvents.slice(0, maximumEntries);
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
self.broadcastEvents();
|
self.broadcastEvents();
|
||||||
scheduleTimer();
|
scheduleTimer();
|
||||||
|
@ -22,11 +22,11 @@ let config = {
|
|||||||
config: {
|
config: {
|
||||||
calendars: [
|
calendars: [
|
||||||
{
|
{
|
||||||
|
maximumEntries: 4,
|
||||||
|
maximumNumberOfDays: 10000,
|
||||||
symbol: "birthday-cake",
|
symbol: "birthday-cake",
|
||||||
fullDaySymbol: "calendar-day",
|
fullDaySymbol: "calendar-day",
|
||||||
recurringSymbol: "undo",
|
recurringSymbol: "undo",
|
||||||
maximumEntries: 4,
|
|
||||||
maximumNumberOfDays: 10000,
|
|
||||||
url: "http://localhost:8080/tests/configs/data/calendar_test_icons.ics"
|
url: "http://localhost:8080/tests/configs/data/calendar_test_icons.ics"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user