forked from devhack-seattle/devhack.net
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex-cal.js
132 lines (115 loc) · 4.11 KB
/
index-cal.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
126
127
128
129
130
131
132
// assumes ical.js loaded
const icsUrl = 'calendar.ics';
async function fetchCalendar(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch calendar: ${response.status} ${response.statusText}`);
}
const icsData = await response.text();
const jcalData = ICAL.parse(icsData);
const vcalendar = new ICAL.Component(jcalData);
return vcalendar;
}
// Armed with a list of events, draw them into an HTML ul list.
function drawEventList(events) {
const dateOptions = {
weekday: 'long',
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
};
const ul = document.createElement('ul');
events.forEach(event => {
const li = document.createElement('li');
const dateStr = `${event.start.toLocaleString('en-US', dateOptions)}`.toLocaleLowerCase();
const recurrenceStr = event.recurrence ? ` (${event.recurrence})` : '';
li.textContent = `${event.summary}: ${dateStr}${recurrenceStr}`;
ul.appendChild(li);
});
return ul;
}
// Given an ical event, return a human-readable description of the recurrence
// (such as "every week").
//
// TODO: This is jank english language hacking - maybe use a library for this?
function describeRecurrence(icalEvent) {
const rrule = icalEvent.component.getFirstPropertyValue('rrule');
if (!rrule) return 'recurring';
let description = 'every ';
if (rrule.interval > 1) {
description += rrule.interval + ' ';
}
switch (rrule.freq) {
case 'DAILY':
description += 'day';
break;
case 'WEEKLY':
description += 'week';
break;
case 'MONTHLY':
description += 'month';
break;
case 'YEARLY':
description += 'year';
break;
default:
description += rrule.freq.toLowerCase();
}
if (rrule.interval > 1) {
description += 's';
}
return description;
}
// Given a parsed ical.js calendar, return a list of upcoming events ("upcoming"
// here, for now, means events after six hours ago. This is so that in-progress
// events are still shown).
function getUpcomingEvents(vcalendar, numEvents = 10) {
const events = vcalendar.getAllSubcomponents('vevent');
const sixHoursAgo = ICAL.Time.now();
sixHoursAgo.addDuration(ICAL.Duration.fromSeconds(-6 * 60 * 60));
const upcomingEvents = [];
events.forEach(event => {
const icalEvent = new ICAL.Event(event);
if (icalEvent.isRecurring()) {
const expand = icalEvent.iterator();
let next;
// only add the next instance (later we show the recurrence)
// so this actually only runs once
while (next = expand.next()) {
if (next.compare(sixHoursAgo) >= 0) {
upcomingEvents.push({
summary: icalEvent.summary,
start: next.toJSDate(),
recurrence: describeRecurrence(icalEvent)
});
break;
}
}
} else {
const eventStart = icalEvent.startDate;
if (eventStart.compare(sixHoursAgo) >= 0) {
upcomingEvents.push({
summary: icalEvent.summary,
start: eventStart.toJSDate()
});
}
}
});
return upcomingEvents.sort((a, b) => a.start - b.start).slice(0, numEvents);
}
// Main function :)
async function doCalendar(calendarUrl, targetElementId) {
const targetElement = document.getElementById(targetElementId);
try {
const vcalendar = await fetchCalendar(calendarUrl);
const upcomingEvents = getUpcomingEvents(vcalendar);
const eventList = drawEventList(upcomingEvents);
targetElement.innerHTML = '';
targetElement.appendChild(eventList);
}
catch (error) {
targetElement.innerHTML = '<p>Sorry - couldn\'t load the calendar :(</p> <small>' + error + '</small>';
}
}
doCalendar(icsUrl, 'cal-parsed');