New calendar display format with date headers for days and times listed next to events for that date

IE:

Sunday, May 1st
  2:00 pm       Soccer
  4:00 pm       Basketball
This commit is contained in:
= 2018-05-09 22:32:15 -04:00
parent 1eaa9d32ea
commit 94c46f9881
2 changed files with 137 additions and 64 deletions

2
modules/default/calendar/README.md Normal file → Executable file
View File

@ -41,7 +41,7 @@ The following properties can be configured:
| `displayRepeatingCountTitle` | Show count title for yearly repeating events (e.g. "X. Birthday", "X. Anniversary") <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false` | `displayRepeatingCountTitle` | Show count title for yearly repeating events (e.g. "X. Birthday", "X. Anniversary") <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `dateFormat` | Format to use for the date of events (when using absolute dates) <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `MMM Do` (e.g. Jan 18th) | `dateFormat` | Format to use for the date of events (when using absolute dates) <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `MMM Do` (e.g. Jan 18th)
| `fullDayEventDateFormat` | Format to use for the date of full day events (when using absolute dates) <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `MMM Do` (e.g. Jan 18th) | `fullDayEventDateFormat` | Format to use for the date of full day events (when using absolute dates) <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `MMM Do` (e.g. Jan 18th)
| `timeFormat` | Display event times as absolute dates, or relative time <br><br> **Possible values:** `absolute` or `relative` <br> **Default value:** `relative` | `timeFormat` | Display event times as absolute dates, or relative time, or using absolute date headers with times for each event next to it <br><br> **Possible values:** `absolute` or `relative` or `dateheader` <br> **Default value:** `relative`
| `getRelative` | How much time (in hours) should be left until calendar events start getting relative? <br><br> **Possible values:** `0` (events stay absolute) - `48` (48 hours before the event starts) <br> **Default value:** `6` | `getRelative` | How much time (in hours) should be left until calendar events start getting relative? <br><br> **Possible values:** `0` (events stay absolute) - `48` (48 hours before the event starts) <br> **Default value:** `6`
| `urgency` | When using a timeFormat of `absolute`, the `urgency` setting allows you to display events within a specific time frame as `relative`. This allows events within a certain time frame to be displayed as relative (in xx days) while others are displayed as absolute dates <br><br> **Possible values:** a positive integer representing the number of days for which you want a relative date, for example `7` (for 7 days) <br><br> **Default value:** `7` | `urgency` | When using a timeFormat of `absolute`, the `urgency` setting allows you to display events within a specific time frame as `relative`. This allows events within a certain time frame to be displayed as relative (in xx days) while others are displayed as absolute dates <br><br> **Possible values:** a positive integer representing the number of days for which you want a relative date, for example `7` (for 7 days) <br><br> **Default value:** `7`
| `broadcastEvents` | If this property is set to true, the calendar will broadcast all the events to all other modules with the notification message: `CALENDAR_EVENTS`. The event objects are stored in an array and contain the following fields: `title`, `startDate`, `endDate`, `fullDayEvent`, `location` and `geo`. <br><br> **Possible values:** `true`, `false` <br><br> **Default value:** `true` | `broadcastEvents` | If this property is set to true, the calendar will broadcast all the events to all other modules with the notification message: `CALENDAR_EVENTS`. The event objects are stored in an array and contain the following fields: `title`, `startDate`, `endDate`, `fullDayEvent`, `location` and `geo`. <br><br> **Possible values:** `true`, `false` <br><br> **Default value:** `true`

199
modules/default/calendar/calendar.js Normal file → Executable file
View File

@ -132,8 +132,29 @@ Module.register("calendar", {
return wrapper; return wrapper;
} }
var lastSeenDate = '';
for (var e in events) { for (var e in events) {
var event = events[e]; var event = events[e];
var dateAsString = moment(event.startDate, "x").format(this.config.dateFormat);
if(this.config.timeFormat === "dateheaders"){
if(lastSeenDate !== dateAsString){
var dateRow = document.createElement("tr");
dateRow.className = "normal"
var dateCell = document.createElement("td");
dateCell.colSpan = "3";
dateCell.innerHTML = dateAsString;
dateRow.appendChild(dateCell);
wrapper.appendChild(dateRow);
lastSeenDate = dateAsString;
}
}
var eventWrapper = document.createElement("tr"); var eventWrapper = document.createElement("tr");
if (this.config.colored && !this.config.coloredSymbolOnly) { if (this.config.colored && !this.config.coloredSymbolOnly) {
@ -164,6 +185,10 @@ Module.register("calendar", {
symbolWrapper.appendChild(symbol); symbolWrapper.appendChild(symbol);
} }
eventWrapper.appendChild(symbolWrapper); eventWrapper.appendChild(symbolWrapper);
}else if(this.config.timeFormat === "dateheaders"){
var blankCell = document.createElement("td");
blankCell.innerHTML = "&nbsp;&nbsp;&nbsp;"
eventWrapper.appendChild(blankCell);
} }
var titleWrapper = document.createElement("td"), var titleWrapper = document.createElement("td"),
@ -189,89 +214,123 @@ Module.register("calendar", {
titleWrapper.className = "title"; titleWrapper.className = "title";
} }
eventWrapper.appendChild(titleWrapper); if(this.config.timeFormat === "dateheaders"){
var timeWrapper = document.createElement("td"); if (event.fullDayEvent) {
//console.log(event.today); titleWrapper.colSpan = "2";
var now = new Date(); titleWrapper.align = "left";
// Define second, minute, hour, and day variables
var oneSecond = 1000; // 1,000 milliseconds }else{
var oneMinute = oneSecond * 60; var timeWrapper = document.createElement("td");
var oneHour = oneMinute * 60; timeWrapper.className = "time light";
var oneDay = oneHour * 24; timeWrapper.align = "left";
if (event.fullDayEvent) { timeWrapper.style.paddingLeft = "2px";
if (event.today) { var timeFormatString = "";
timeWrapper.innerHTML = this.capFirst(this.translate("TODAY")); switch (config.timeFormat) {
} else if (event.startDate - now < oneDay && event.startDate - now > 0) { case 12: {
timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW")); timeFormatString = "h:mm A";
} else if (event.startDate - now < 2 * oneDay && event.startDate - now > 0) { break;
if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") { }
timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW")); case 24: {
} else { timeFormatString = "HH:mm";
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow()); break;
} }
} else { default: {
/* Check to see if the user displays absolute or relative dates with their events timeFormatString = "HH:mm";
* Also check to see if an event is happening within an 'urgency' time frameElement break;
* 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.fullDayEventDateFormat));
} }
} else {
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
} }
timeWrapper.innerHTML = moment(event.startDate, "x").format(timeFormatString);
eventWrapper.appendChild(timeWrapper);
titleWrapper.align = "right";
} }
} else {
if (event.startDate >= new Date()) { eventWrapper.appendChild(titleWrapper);
if (event.startDate - now < 2 * oneDay) { }else{
// This event is within the next 48 hours (2 days) var timeWrapper = document.createElement("td");
if (event.startDate - now < this.config.getRelative * oneHour) {
// If event is within 6 hour, display 'in xxx' time format or moment.fromNow() eventWrapper.appendChild(titleWrapper);
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow()); //console.log(event.today);
var now = new Date();
// Define second, minute, hour, and day variables
var oneSecond = 1000; // 1,000 milliseconds
var oneMinute = oneSecond * 60;
var oneHour = oneMinute * 60;
var oneDay = oneHour * 24;
if (event.fullDayEvent) {
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 { } else {
// Otherwise just say 'Today/Tomorrow at such-n-such time' timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
} }
} else { } else {
/* Check to see if the user displays absolute or relative dates with their events /* 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 * 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 * 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() * 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 * 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.timeFormat === "absolute") {
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) { if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
// This event falls within the config.urgency period that the user has set // This event falls within the config.urgency period that the user has set
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow()); timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
} else { } else {
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat)); timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat));
} }
} else { } else {
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow()); timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
} }
} }
} else { } else {
timeWrapper.innerHTML = this.capFirst( if (event.startDate >= new Date()) {
this.translate("RUNNING", { if (event.startDate - now < 2 * oneDay) {
fallback: this.translate("RUNNING") + " {timeUntilEnd}", // This event is within the next 48 hours (2 days)
timeUntilEnd: moment(event.endDate, "x").fromNow(true) 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 {
// 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(
this.translate("RUNNING", {
fallback: this.translate("RUNNING") + " {timeUntilEnd}",
timeUntilEnd: moment(event.endDate, "x").fromNow(true)
})
);
}
} }
//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
//console.log(event);
timeWrapper.className = "time light";
eventWrapper.appendChild(timeWrapper);
} }
//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
//console.log(event);
timeWrapper.className = "time light";
eventWrapper.appendChild(timeWrapper);
wrapper.appendChild(eventWrapper); wrapper.appendChild(eventWrapper);
@ -359,6 +418,9 @@ Module.register("calendar", {
continue; continue;
} }
} }
if(this.listContainsEvent(events,event)){
continue;
}
event.url = c; event.url = c;
event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000); event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
events.push(event); events.push(event);
@ -372,6 +434,17 @@ Module.register("calendar", {
return events.slice(0, this.config.maximumEntries); return events.slice(0, this.config.maximumEntries);
}, },
listContainsEvent: function(eventList, event){
for(let evt of eventList){
if(evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate)){
return true;
}
}
return false;
},
/* createEventList(url) /* createEventList(url)
* Requests node helper to add calendar url. * Requests node helper to add calendar url.
* *