/* Functions */ function openNewTabWithHTML(htmlContent) { const newTab = window.open("", "_blank"); newTab.document.write(htmlContent); newTab.document.close(); }; function fetchUrlContent(url, callback) { fetch(url) .then(response => { if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } return response.text(); }) .then(data => callback(null, data)) .catch(error => { console.error('Fetch error: ', error); callback(error, ""); }); }; function formatDate(dateStr) { return new Date(dateStr).toISOString().replace(/[-:]/g, "").split(".")[0] + "Z"; } function formatDateReadable(dateStr) { return new Date(dateStr).toDateString(); } function jsonToIcal(data) { if (!data.start || !data.end || !data.title || !data.id) { alert("Required fields missing from input data."); throw new Error("Required fields missing from input data."); } let icalStr = 'BEGIN:VCALENDAR\nVERSION:1.0\nPRODID:-//42Cal//\n'; icalStr += 'BEGIN:VEVENT\n'; icalStr += `UID:${data.id}\n`; icalStr += `SUMMARY:${data.title}\n`; icalStr += `DESCRIPTION:${`DURATION: ${data.duration}\\n\\n` + data.description.trim()}\n`; icalStr += `LOCATION:${data.location}\n`; icalStr += `DTSTART:${formatDate(data.start)}\n`; icalStr += `DTEND:${formatDate(data.end)}\n`; icalStr += 'END:VEVENT\n'; icalStr += 'END:VCALENDAR'; return icalStr; }; function addExtraData(event, callback) { let kind = 'events'; if (event.kind === 'exam') { kind = 'exams'; } fetchUrlContent(`https://profile.intra.42.fr/${kind}/${event.id}`, (error, data) => { if (error) { alert('Error2: ' + error); console.log('Error2: ' + error); callback(); } else { parser = new DOMParser(); doc = parser.parseFromString(data, "text/html"); const description = doc.querySelector('.notification-text').textContent; const duration = doc.querySelector('.icon-clock')?.parentElement.children[2].textContent; const _location = doc.querySelector('.icon-location')?.parentElement.children[1].textContent; if (description) event.description = description; else event.description = 'Could not fetch description'; if (duration) event.duration = duration; else event.duration = 'Could not fetch duration'; if (_location) event.location = _location; else event.location = 'Could not fetch location'; callback(event) } }); } function eventsToHtml(events) { let htmlString = '

Download iCals

    '; events.sort((a,b) => new Date(a.start) - new Date(b.start)).forEach(event => { const icalString = jsonToIcal(event); const base64Data = btoa(unescape(encodeURIComponent(icalString))); const fileName = event.title ? event.title.replace(/\s+/g, '_') + '.ics' : `event_${index + 1}.ics`; const date_formatted = formatDateReadable(event.start); htmlString += `
  1. `; }); htmlString += '
'; return htmlString; } function generateDownloadPage(dataList, only_subscribed_events) { const promises = dataList.map((event, index) => { return new Promise((resolve, reject) => { if (!event.id || !event.title || !event.end || !event.start) { resolve(); return; } if (only_subscribed_events && !event.is_subscribed) { resolve(); return; } addExtraData(event, event => { resolve(event); }); }); }); return Promise.all(promises) .then(events => { return eventsToHtml(events.filter(Boolean)); }); }; function generateApiDateParams() { const currentDate = new Date(); const year = currentDate.getFullYear(); const nextYear = year + 1; const month = String(currentDate.getMonth() + 1).padStart(2, '0'); const day = String(currentDate.getDate()).padStart(2, '0'); const formattedDateStart = `${year}-${month}-${day}`; const formattedDateEnd = `${nextYear}-${month}-${day}`; return `start=${formattedDateStart}&end=${formattedDateEnd}` }; function open_upcoming_events_new_tab(only_subscribed_events) { fetchUrlContent(`https://profile.intra.42.fr/events.json?${generateApiDateParams()}`, (error, data) => { if (error) { alert('Error: ' + error); } else { generateDownloadPage(JSON.parse(data), only_subscribed_events) .then(html => { openNewTabWithHTML(html) }) .catch(error => { alert("Error1: " + error); } ); } }); } /* End Functions */ open_upcoming_events_new_tab(false);