import ICAL from "https://unpkg.com/ical.js/dist/ical.min.js"; /** * Parse an ICS calendar and return upcoming event occurrences. * * @param {string} icsText The contents of the .ics file * @param {Date} now Events must start after this date * @param {number} maxEvents Maximum number of events to return * @param {number} maxDays Maximum number of days into the future * @returns {{start: Date, name: string, url: string}[]} */ function getUpcomingEvents(icsText, now, maxEvents, maxDays) { const jcal = ICAL.parse(icsText); const calendar = new ICAL.Component(jcal); const end = new Date(now.getTime()); end.setDate(end.getDate() + maxDays); const events = []; for (const component of calendar.getAllSubcomponents("vevent")) { const event = new ICAL.Event(component); if (!event.startDate) { continue; } if (event.isRecurring()) { const iterator = event.iterator(); while (true) { const occurrence = iterator.next(); if (!occurrence) { break; } const start = occurrence.toJSDate(); // Recurrences are chronological, so we're done // once we pass the end of our search window. if (start > end) { break; } if (start > now) { events.push({ start, name: event.summary ?? "", url: event.url ?? "", }); } } } else { const start = event.startDate.toJSDate(); if (start > now && start <= end) { events.push({ start, name: event.summary ?? "", url: event.url ?? "", }); } } } // We have occurrences from multiple events, so sort them // before applying the maximum event count. events.sort((a, b) => a.start - b.start); return events.slice(0, maxEvents); } document.addEventListener("DOMContentLoaded", () => { const ics = "/calendars/all.ics"; const max_days = 20; const max_items = 5; const now = new Date(); const table = document.getElementById("upcoming"); fetch(ics) .then(response => response.text()) .then(icsText => { getUpcomingEvents(icsText, now, max_items, max_days).forEach(event => { const row = document.createElement("tr"); const colBegin = document.createElement("td"); const formattedStart = event.start.toLocaleString("de-DE", { weekday: "long", day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit", }).replace(",", ""); colBegin.innerText = `${formattedStart.replace(" ", ", ")} Uhr`; row.appendChild(colBegin); const colName = document.createElement("td"); const a = document.createElement("a"); a.href = event.url; a.text = event.name; colName.appendChild(a); row.appendChild(colName); table.appendChild(row); }); }); });