Initial commit

This commit is contained in:
tosu 2023-08-18 01:52:33 +02:00
commit 08d15d5866
Signed by: tosu
GPG Key ID: C00746F2E0F36492
2 changed files with 165 additions and 0 deletions

164
42cal_bookmarklet.js Normal file
View File

@ -0,0 +1,164 @@
/* 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 = '<h1>Download iCals</h1><ol>';
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 += `<li>
<a href="data:text/calendar;base64,${base64Data}" download="${fileName}">
<button style="background-color: ${event.is_subscribed ? 'lightgreen' : 'rgb(239, 239, 239)'}; ">
${date_formatted}: Download '<span style="color: #1443d3; font-weight: bold; ">${event.title || 'Event'}.ical</span>'
</button>
</a>
</li>`;
});
htmlString += '</ol>';
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);

1
42cal_bookmarklet.min.js vendored Normal file
View File

@ -0,0 +1 @@
b="https://profile.intra.42.fr/",n=(t,r)=>fetch(t).then(t=>{if(!t.ok)throw Error(`HTTP Error! Status:${t.status}`);return t.text()}).then(t=>r(null,t)).catch(t=>r(t,"")),a=t=>new Date(t).toISOString().replace(/[-:]/g,"").split(".")[0]+"Z",n(b+`events.json?start=${d=(q=new Date).getFullYear()}-${u=String(q.getMonth()+1).padStart(2,"0")}-${v=String(q.getDate()).padStart(2,"0")}&end=${d+1}-${u}-${v}`,(t,r)=>{Promise.all(JSON.parse(r).map(t=>new Promise(r=>{t.id&&t.title&&t.end&&t.start?n(b+`${kind="exam"==t.kind?"exams":"events"}/${t.id}`,(o,l)=>{o?(alert("Error2:"+o),r()):(o=(h=(parser=new DOMParser).parseFromString(l,"text/html")).querySelector(".modal-body").textContent,e=h.querySelector(".icon-clock").parentElement.children[2].textContent,c=h.querySelector(".icon-location").parentElement.children[1].textContent,t.p=o||"Could not fetch p",e?t.e=e:t.e="Could not fetch duration",c?t.f=c:t.f="Could not fetch location",r(t))}):r()}))).then(t=>(t.filter(t=>t),x="<h1>Download iCals</h1><ol>",t.sort((t,r)=>new Date(t.start)-new Date(r.start)).forEach((t,r)=>{x+=`<li><a href="data:text/calendar;base64,${btoa(unescape(encodeURIComponent(`BEGIN:VCALENDAR\nVERSION:1.0\nPRODID:-//42Cal//\nBEGIN:VEVENT\nUID:${t.id}\nSUMMARY:${t.title}\nDESCRIPTION:${`DURATION:${t.e}\\n\\n`+t.p.trim()}\nLOCATION:${t.f}\nDTSTART:${a(t.start)}\nDTEND:${a(t.end)}\nEND:VEVENT\nEND:VCALENDAR`)))}" download="${t.title?t.title.replace(/\s+/g,"_"):`event_${r+1}`}.ics"><button ${t.is_subscribed&&"style=background:lightgreen"}>${new Date(t).toDateString()}:Download "<span style="color:#1443d3;font-weight:700;">${t.title||"Event"}.ics</span>"</button></a></li>`}),x+"</ol>")).then(t=>window.open("","_blank").document.write(t)).catch(t=>alert("Error1:"+t))});