Initial commit
This commit is contained in:
commit
08d15d5866
|
@ -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);
|
|
@ -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))});
|
Loading…
Reference in New Issue