-
Notifications
You must be signed in to change notification settings - Fork 0
/
icaltools.js
125 lines (103 loc) · 3.91 KB
/
icaltools.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
const ical = require('node-ical');
const moment = require('moment');
/*
returns an iCal event JSON object or null if none found
@calendarUrl - full url to a public iCal resource
@eventSummary - the summary or title of the event
@lookoutDays - number of days from now to look ahead for the next instance
*/
async function getNextEvent(calendarUrl, eventSummary, lookoutDays) {
const webEvents = await ical.async.fromURL(calendarUrl);
let nextEvent;
for(let i in webEvents){
if(webEvents[i].summary && webEvents[i].summary.includes(eventSummary)) {
nextEvent = getNextDate(webEvents[i], lookoutDays);
if(nextEvent.summary) break;
}
}
if(nextEvent) {
return nextEvent;
} else {
throw `No event named ${eventSummary} found within ${lookoutDays}`;
}
}
// returns the next instance of an iCal @event within @lookoutDays of now
// derived from: https://github.com/peterbraden/ical.js/blob/master/example_rrule.js
function getNextDate(event, lookoutDays) {
if(event.type !== 'VEVENT') {
return null;
}
let rangeStart = moment();
let rangeEnd = moment().add(lookoutDays, 'days');
let summary = null;
let description = null;
let startDate = moment(event.start);
let endDate = moment(event.end);
// No recurrences
if (typeof event.rrule === 'undefined')
{
if (endDate.isBefore(rangeStart) || startDate.isAfter(rangeEnd)) {
return null;
}
}
// Has recurrences
else if (typeof event.rrule !== 'undefined')
{
// For recurring events, get the set of event start dates that fall within the range
// of dates we're looking for.
var dates = event.rrule.between(
rangeStart.toDate(),
rangeEnd.toDate(),
true,
function(date, i) {return true;}
)
if (event.recurrences != undefined)
{
for (var 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(rangeStart, rangeEnd) != true)
{
dates.push(new Date(r));
}
}
}
// Calculate the duration of the event for use with recurring events.
var duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x"));
// Loop through the set of date entries to see which recurrences should be printed.
for(var i in dates) {
var date = dates[i];
var curEvent = event;
var curDuration = duration;
startDate = moment(date);
// Use just the date of the recurrence to look up overrides and exceptions (i.e. chop off time information)
var dateLookupKey = date.toISOString().substring(0, 10);
// 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[dateLookupKey] != undefined))
{
curEvent = curEvent.recurrences[dateLookupKey];
startDate = moment(curEvent.start);
curDuration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x"));
}
// If there's a recurrence override for this date, skip it
else if ((curEvent.exdate != undefined) && (curEvent.exdate[dateLookupKey] != undefined)) { continue; }
// Set the end date from either the regular event or the recurrence override.
endDate = moment(parseInt(startDate.format("x")) + curDuration, 'x');
// If this recurrence ends before the start of the date range, or starts after the end of the date range, skip it
if (endDate.isBefore(rangeStart) || startDate.isAfter(rangeEnd)) { continue; }
summary = curEvent.summary;
description = curEvent.description;
break;
}
}
return summary == null ? {} : {
'summary': summary,
'description': description,
'start': startDate.format(),
'end': endDate.format(),
};
}
module.exports = {
getNextEvent: getNextEvent,
}