0&&t.push(`Code: ${n.modules.join(", ")}`),t.join("\n")}function generateIcs(e){const n=[];n.push("BEGIN:VCALENDAR"),n.push("VERSION:2.0"),n.push("PRODID:-//Assas//Calendar Exporter//EN"),n.push("CALSCALE:GREGORIAN"),n.push("METHOD:PUBLISH"),n.push("X-WR-CALNAME:Assas Calendar"),n.push("X-WR-TIMEZONE:Europe/Paris");for(const t of e){const e=i(t);n.push(e)}return n.push("END:VCALENDAR"),n.join("\r\n")}function i(r){const a=[];a.push("BEGIN:VEVENT");const i=n(r.id);a.push(t(`UID:${i}`)),a.push(`DTSTAMP:${o()}`);const l=e(r.start);a.push(`DTSTART:${l}`);const u=e(r.end);a.push(`DTEND:${u}`);const d=s(r.eventCategory)||"Cours";let p=d;r.parsed&&r.parsed.module&&(p=`${d} - ${r.parsed.module}`);const m=n(p);a.push(t(`SUMMARY:${m}`));const g=n(c(r.parsed||{},r));a.push(t(`DESCRIPTION:${g}`));let f="";if(r.parsed&&r.parsed.room?f=r.parsed.room:r.sites&&r.sites.length>0&&(f=r.sites.join(", ")),f){const e=n(f);a.push(t(`LOCATION:${e}`))}if(r.eventCategory){const e=n(r.eventCategory);a.push(t(`CATEGORIES:${e}`))}return a.push("STATUS:CONFIRMED"),a.push("TRANSP:OPAQUE"),a.push("END:VEVENT"),a.join("\r\n")}function extractStudentId(){try{const e=document.querySelector(".logInOrOut");if(e){const n=e.querySelector(".small");if(n&&n.textContent){const e=n.textContent.trim().match(/\d+/);if(e)return console.log("[Assas Exporter] Found student ID in navbar:",e[0]),e[0]}}}catch(e){console.log("[Assas Exporter] Could not extract from navbar:",e)}try{const e=document.querySelectorAll("*");for(const n of e){const e=(n.textContent||"").match(/[-\s](\d{7})/);if(e)return console.log("[Assas Exporter] Found student ID in DOM:",e[1]),e[1]}}catch(e){console.log("[Assas Exporter] Could not scan DOM:",e)}const e=window.location.href,n=e.match(/federationIds(?:%5B%5D|\[\])=([^&]+)/);if(n)return console.log("[Assas Exporter] Found student ID in query params:",n[1]),n[1];const t=e.match(/#.*federationIds(?:%5B%5D|\[\])=([^&]+)/);if(t)return console.log("[Assas Exporter] Found student ID in hash fragment:",t[1]),t[1];const o=e.match(/[?&]id=([^&]+)/);if(o)return console.log("[Assas Exporter] Found student ID in id param:",o[1]),o[1];const r=e.match(/\/student\/(\d+)/);return r?(console.log("[Assas Exporter] Found student ID in path:",r[1]),r[1]):(console.log("[Assas Exporter] Could not extract student ID automatically"),null)}function l(){const e=new Date,n=e.getFullYear(),t=e.getMonth()+1>=9?n:n-1,o=`${t}-09-01`,r=`${t+1}-08-31`,s=new Date(o),a=new Date(r),c=`${s.getDate()} ${u(s.getMonth())} ${s.getFullYear()}`,i=`${a.getDate()} ${u(a.getMonth())} ${a.getFullYear()}`,l=prompt(`đ
DATE DE DĂBUT\n\nFormat : ANNĂE-MOIS-JOUR (YYYY-MM-DD)\n\nExemple : (${c}) â ${o}\nExemple : (8 juin 2025) â 2025-06-08\n\nEntre la date de dĂ©but :`,o);if(!l)return null;const d=prompt(`đ
DATE DE FIN\n\nFormat : ANNĂE-MOIS-JOUR (YYYY-MM-DD)\n\nExemple : (${i}) â ${r}\nExemple : (31 aoĂ»t 2026) â 2026-08-31\n\nEntre la date de fin :`,r);return d?/^\d{4}-\d{2}-\d{2}$/.test(l)&&/^\d{4}-\d{2}-\d{2}$/.test(d)?{startDate:l,endDate:d}:(alert("â Format de date invalide !\n\nUtilise le format : ANNĂE-MOIS-JOUR\nExemple : 2025-09-01 pour le 1er septembre 2025"),null):null}function u(e){return["janvier","fĂ©vrier","mars","avril","mai","juin","juillet","aoĂ»t","septembre","octobre","novembre","dĂ©cembre"][e]}async function d(e){const n=new URLSearchParams({"federationIds[]":e,resType:"104"});try{const e=await fetch("https://celcat-web.u-paris2.fr/calendar/Home/LoadDisplayNames",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:n.toString(),credentials:"include"});if(!e.ok)return console.log("[Assas Exporter] Could not fetch student name"),null;const t=await e.json();if(t&&t.length>0&&t[0].displayName){const e=t[0].displayName.split(",");if(e.length>=2){const n=e[1].trim();return n.charAt(0).toUpperCase()+n.slice(1).toLowerCase()}}}catch(e){console.log("[Assas Exporter] Error fetching student name:",e)}return null}async function fetchCalendarData(e,n,t){const o=new URLSearchParams({start:n,end:t,resType:"104",calView:"agendaDay","federationIds[]":e});console.log("[Assas Exporter] Fetching calendar data...",{studentId:e,startDate:n,endDate:t});const r=await fetch("https://celcat-web.u-paris2.fr/calendar/Home/GetCalendarData",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:o.toString(),credentials:"include"});if(!r.ok)throw new Error(`Erreur API: ${r.status} ${r.statusText}`);const s=await r.json();return console.log("[Assas Exporter] Fetched events:",s.length),s}function p(){return/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)||navigator.maxTouchPoints&&navigator.maxTouchPoints>2}async function downloadIcsFile(e,n="assas-calendar.ics"){const t=new Blob([e],{type:"text/calendar;charset=utf-8"});if(p()&&navigator.share&&navigator.canShare)try{const e=new File([t],n,{type:"text/calendar;charset=utf-8"});if(navigator.canShare({files:[e]}))return await navigator.share({files:[e],title:"Calendrier Assas",text:"Importer dans ton calendrier"}),console.log("[Assas Exporter] File shared successfully"),void showStatus("đ
Fichier partagĂ© ! Choisis ton app calendrier","success")}catch(e){"AbortError"===e.name?(console.log("[Assas Exporter] User cancelled share, falling back to download"),showStatus("đ„ TĂ©lĂ©chargement du fichier...","info")):console.log("[Assas Exporter] Share failed, falling back to download:",e)}const o=URL.createObjectURL(t),r=document.createElement("a");r.href=o,r.download=n,document.body.appendChild(r),r.click(),document.body.removeChild(r),setTimeout(()=>URL.revokeObjectURL(o),100),console.log("[Assas Exporter] File download triggered:",n)}"undefined"!=typeof module&&module.exports;const m={toasts:[],maxToasts:5,toastHeight:60,baseTop:20};function showStatus(e,n="info"){const t={info:"#2196f3",success:"#4caf50",error:"#f44336"};if(m.toasts.length>=m.maxToasts){const e=m.toasts.shift();e&&e.parentNode&&e.remove()}const o=m.baseTop+m.toasts.length*m.toastHeight,r=document.createElement("div");r.textContent=e,r.style.cssText=`\n position: fixed;\n top: ${o}px;\n right: 20px;\n padding: 15px 20px;\n background: ${t[n]||t.info};\n color: white;\n border-radius: 8px;\n z-index: 9999999;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\n font-size: 14px;\n font-weight: 500;\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n max-width: 300px;\n transition: top 0.2s ease, opacity 0.2s ease;\n `,document.body.appendChild(r),m.toasts.push(r),setTimeout(()=>{const e=m.toasts.indexOf(r);e>-1&&(m.toasts.splice(e,1),m.toasts.forEach((e,n)=>{e.style.top=`${m.baseTop+n*m.toastHeight}px`})),r.parentNode&&(r.style.opacity="0",setTimeout(()=>r.remove(),200))},3e3)}"undefined"!=typeof module&&module.exports;const g="assas-exp",f=`\n .${g}-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5);\n z-index: 999998;\n }\n .${g}-dialog {\n position: fixed;\n top: 50%; left: 50%;\n transform: translate(-50%, -50%);\n background: #fff;\n border-radius: 16px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\n font-size: 14px;\n max-width: 90vw;\n max-height: 80vh;\n overflow: auto;\n min-width: 320px;\n }\n .${g}-header {\n padding: 20px;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n font-weight: 600;\n font-size: 18px;\n border-radius: 16px 16px 0 0;\n }\n .${g}-body {\n padding: 20px;\n }\n .${g}-footer {\n padding: 16px 20px;\n border-top: 1px solid #e9ecef;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n }\n .${g}-btn {\n padding: 12px 24px;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 600;\n transition: all 0.2s ease;\n }\n .${g}-btn-primary {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n }\n .${g}-btn-primary:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 16px rgba(102, 126, 234, 0.5);\n }\n .${g}-btn-secondary {\n background: #e0e0e0;\n color: #333;\n }\n .${g}-btn-secondary:hover {\n background: #d0d0d0;\n }\n .${g}-input {\n width: 100%;\n padding: 10px 12px;\n border: 2px solid #e0e0e0;\n border-radius: 8px;\n font-size: 14px;\n box-sizing: border-box;\n transition: border-color 0.2s;\n }\n .${g}-input:focus {\n outline: none;\n border-color: #667eea;\n }\n .${g}-row {\n margin-bottom: 16px;\n }\n .${g}-label {\n display: block;\n margin-bottom: 6px;\n font-weight: 500;\n color: #2c3e50;\n }\n .${g}-section {\n margin-bottom: 20px;\n }\n .${g}-section-title {\n font-weight: 600;\n margin-bottom: 10px;\n color: #2c3e50;\n font-size: 15px;\n }\n .${g}-checkbox-list {\n border: 2px solid #e0e0e0;\n border-radius: 8px;\n padding: 8px 12px;\n background: #f8f9fa;\n }\n .${g}-checkbox-list-scrollable {\n max-height: 200px;\n overflow-y: auto;\n }\n .${g}-checkbox-item {\n display: block;\n padding: 6px 0;\n cursor: pointer;\n }\n .${g}-checkbox-item:hover {\n color: #667eea;\n }\n .${g}-checkbox-item input {\n margin-right: 10px;\n accent-color: #667eea;\n }\n .${g}-info {\n background: #e7f3ff;\n border-left: 4px solid #2196f3;\n padding: 12px 16px;\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 14px;\n color: #1565c0;\n }\n .${g}-accordion-toggle {\n cursor: pointer;\n user-select: none;\n display: flex;\n align-items: center;\n }\n .${g}-accordion-toggle::before {\n content: "â¶";\n font-size: 10px;\n margin-right: 8px;\n transition: transform 0.2s;\n display: inline-block;\n }\n .${g}-accordion-toggle.${g}-open::before {\n transform: rotate(90deg);\n }\n .${g}-accordion-content {\n display: none;\n padding-left: 24px;\n margin-top: 4px;\n }\n .${g}-accordion-content.${g}-open {\n display: block;\n }\n`;let h=!1;function $(){if(h)return;const e=document.createElement("style");e.textContent=f,document.head.appendChild(e),h=!0}function x(e){return $(),new Promise((n,t)=>{const o=document.createElement("div");o.className=`${g}-overlay`;const r=document.createElement("div");function s(){o.remove(),r.remove()}r.className=`${g}-dialog`,r.innerHTML=`\n \n ${e.body||""}
\n \n `,document.body.appendChild(o),document.body.appendChild(r),r.addEventListener("click",o=>{const a=o.target.dataset.action;if("cancel"===a)s(),t(new Error("cancelled"));else if("confirm"===a){const t=!e.onConfirm||e.onConfirm(r);s(),n(t)}}),o.addEventListener("click",()=>{s(),t(new Error("cancelled"))})})}function showDateDialog(){const e=new Date,n=e.getMonth()>=8?e.getFullYear():e.getFullYear()-1;return x({title:"đ Assas Calendar Exporter",body:`\n đ
Sélectionne la période à exporter
\n \n \n \n
\n \n \n \n
\n `,confirmText:"Continuer",onConfirm:e=>({startDate:e.querySelector(`#${g}-start`).value,endDate:e.querySelector(`#${g}-end`).value})})}function extractFilterOptions(e){const n={},t={},o=[];e.forEach(e=>{const r=e.parsed;if(r.group&&r.group.toUpperCase().includes("OPTION")){const n=r.module||"Cours sans nom",o=r.staff||"",s=`${n}|||${o}`;t[s]||(t[s]={events:[],module:n,staff:o}),t[s].events.push(e)}else if(r.group){const t=r.group;n[t]||(n[t]={events:[],courses:{}}),n[t].events.push(e);const o=r.module||"Cours sans nom",s=r.staff||"",a=`${o}|||${s}`;n[t].courses[a]||(n[t].courses[a]={module:o,staff:s,count:0}),n[t].courses[a].count++}else o.push(e)});const r=Object.entries(n).map(([e,n])=>({id:e,label:`Groupe ${e}`,count:n.events.length,courses:Object.entries(n.courses).map(([n,t])=>({id:`${e}|||${n}`,module:t.module,staff:t.staff,label:t.staff?`${t.module} (${t.staff})`:t.module,count:t.count})).sort((e,n)=>e.label.localeCompare(n.label))})).sort((e,n)=>e.id.localeCompare(n.id)),s=Object.entries(t).map(([e,n])=>({id:e,module:n.module,staff:n.staff,label:n.staff?`${n.module} (${n.staff})`:n.module,count:n.events.length})).sort((e,n)=>e.label.localeCompare(n.label)),a={};o.forEach(e=>{const n=e.parsed?.module||"Autre";a[n]||(a[n]=0),a[n]++});return{groups:r,options:s,troncCommun:Object.entries(a).map(([e,n])=>({module:e,count:n})).sort((e,n)=>n.count-e.count),troncCommunCount:o.length}}function showFilterDialog(e,n,t){function o(e,n,o){return t.filter(t=>{const r=t.parsed;if(r.group&&r.group.toUpperCase().includes("OPTION")){const e=`${r.module||"Cours sans nom"}|||${r.staff||""}`;return n.includes(e)}if(r.group){const n=r.module||"Cours sans nom",t=r.staff||"",o=`${r.group}|||${n}|||${t}`;return e.includes(o)}const s=r.module||"Autre";return o.includes(s)})}function r(e){if(0===e.length)return'Aucun cours sélectionné
';const n=[...e].sort((e,n)=>new Date(e.start)-new Date(n.start)),t={};n.forEach(e=>{const n=new Date(e.start),o=`${n.getFullYear()}-${String(n.getMonth()+1).padStart(2,"0")}`,r=n.toLocaleDateString("fr-FR",{month:"long",year:"numeric"});t[o]||(t[o]={label:r,events:[]}),t[o].events.push(e)});let o="";return Object.values(t).forEach(e=>{o+=`${e.label} (${e.events.length})
`,e.events.slice(0,10).forEach(e=>{const n=new Date(e.start),t=n.toLocaleDateString("fr-FR",{weekday:"short",day:"numeric"}),r=n.toLocaleTimeString("fr-FR",{hour:"2-digit",minute:"2-digit"}),s=e.parsed?.module||"Cours";o+=`đ
${t} ${r} - ${b(s.substring(0,40))}${s.length>40?"...":""}
`}),e.events.length>10&&(o+=`... et ${e.events.length-10} autres
`)}),o}let s=`đ ${n} cours trouvĂ©s. SĂ©lectionne ceux que tu veux exporter.
`;e.groups.length>0&&(s+=`\n \n
đ„ Groupes
\n
\n ${e.groups.map(e=>`\n
\n `).join("")}\n
\n
\n `),e.options.length>0&&(s+=`\n \n
đ Mes options
\n
\n ${e.options.map(e=>`\n \n `).join("")}\n
\n
\n `),e.troncCommun&&e.troncCommun.length>0&&(s+=`\n \n
đ Tronc commun
\n
\n ${e.troncCommun.map(e=>`\n \n `).join("")}\n
\n
\n `);const a=o(e.groups.flatMap(e=>e.courses.map(e=>e.id)),e.options.map(e=>e.id),e.troncCommun.map(e=>e.module));return s+=`\n \n
đ RĂ©cap : ${a.length} cours Ă exporter
\n
\n ${r(a)}\n
\n
\n `,new Promise((e,n)=>{$();const t=document.createElement("div");t.className=`${g}-overlay`;const a=document.createElement("div");function c(){const e=o([...a.querySelectorAll('input[name="groupCourse"]:checked')].map(e=>e.value),[...a.querySelectorAll('input[name="option"]:checked')].map(e=>e.value),[...a.querySelectorAll('input[name="troncCommun"]:checked')].map(e=>e.value)),n=a.querySelector(`#${g}-recap`),t=a.querySelector(`#${g}-recap-count`);n&&(n.innerHTML=r(e)),t&&(t.textContent=e.length)}function i(){t.remove(),a.remove()}a.className=`${g}-dialog`,a.innerHTML=`\n \n ${s}
\n \n `,document.body.appendChild(t),document.body.appendChild(a),a.querySelectorAll('input[name="groupParent"]').forEach(e=>{e.addEventListener("change",e=>{const n=e.target.value,t=e.target.checked;a.querySelectorAll(`input[name="groupCourse"][data-parent="${n}"]`).forEach(e=>{e.checked=t}),c()})}),a.querySelectorAll('input[name="groupCourse"]').forEach(e=>{e.addEventListener("change",e=>{const n=e.target.dataset.parent,t=a.querySelectorAll(`input[name="groupCourse"][data-parent="${n}"]`),o=a.querySelectorAll(`input[name="groupCourse"][data-parent="${n}"]:checked`),r=a.querySelector(`input[name="groupParent"][value="${n}"]`);r&&(r.checked=o.length>0,r.indeterminate=o.length>0&&o.length{e.addEventListener("change",c)}),a.querySelectorAll(`.${g}-accordion-toggle`).forEach(e=>{e.addEventListener("click",n=>{if("INPUT"===n.target.tagName)return;const t=e.dataset.accordion,o=a.querySelector(`[data-accordion-content="${t}"]`);e.classList.toggle(`${g}-open`),o&&o.classList.toggle(`${g}-open`)})}),a.addEventListener("click",t=>{const o=t.target.dataset.action;if("cancel"===o)i(),n(new Error("cancelled"));else if("confirm"===o){const n=[...a.querySelectorAll('input[name="groupCourse"]:checked')].map(e=>e.value),t=[...a.querySelectorAll('input[name="option"]:checked')].map(e=>e.value),o=[...a.querySelectorAll('input[name="troncCommun"]:checked')].map(e=>e.value);i(),e({groupCourseIds:n,optionIds:t,troncCommunModules:o})}}),t.addEventListener("click",()=>{i(),n(new Error("cancelled"))})})}function filterEvents(e,n){return e.filter(e=>{const t=e.parsed;if(t.group&&t.group.toUpperCase().includes("OPTION")){const e=`${t.module||"Cours sans nom"}|||${t.staff||""}`;return n.optionIds?.includes(e)??!0}if(t.group){const e=t.module||"Cours sans nom",o=t.staff||"",r=`${t.group}|||${e}|||${o}`;return n.groupCourseIds?.includes(r)??!0}const o=t.module||"Autre";return n.troncCommunModules?.includes(o)??!0})}function b(e){return e?e.replace(/&/g,"&").replace(//g,">").replace(/"/g,"""):""}"undefined"!=typeof module&&module.exports,async function(){"use strict";const e="assas-exporter-running";if(document.getElementById(e))return console.log("[Assas] Already running, skipping..."),void showStatus("â ïž DĂ©jĂ en cours !","info");const n=document.createElement("div");n.id=e,n.style.display="none",document.body.appendChild(n);try{console.log("[Assas] Starting..."),showStatus("đ Recherche de ton ID...","info");let e=extractStudentId();if(!e){if(e=prompt("đ ID ĂTUDIANT\n\nTon ID n'a pas pu ĂȘtre dĂ©tectĂ©.\nTrouve-le en haut Ă droite de CELCAT (7 chiffres).\n\nEntre ton ID :",""),!e||!e.trim())return void showStatus("Oups, annulĂ© ! đ","error");e=e.trim()}console.log("[Assas] Student ID:",e);let n,t=null;try{t=await d(e),t&&(showStatus(`Yo ${t} ! đ€`,"success"),await new Promise(e=>setTimeout(e,1e3)))}catch(e){}try{n=await showDateDialog()}catch(e){return void showStatus("Bon, une autre fois alors ! đ","error")}const{startDate:o,endDate:r}=n;let s;console.log("[Assas] Dates:",o,"->",r),showStatus("⥠RĂ©cup du planning...","info");try{s=await fetchCalendarData(e,o,r)}catch(e){return console.error("[Assas] API Error:",e),void showStatus(`AĂŻe ! ${e.message} đ„`,"error")}if(!s||0===s.length)return void showStatus("ZĂ©ro cours trouvĂ© ! ïżœ","error");console.log("[Assas] Fetched:",s.length,"events"),showStatus("đŹ Analyse en cours...","info");const a=s.map(e=>({...e,parsed:parseDescription(e.description)})),c=extractFilterOptions(a);let i;console.log("[Assas] Found groups:",c.groups),console.log("[Assas] Found options:",c.options.length);try{i=await showFilterDialog(c,a.length,a)}catch(e){return void showStatus("Pas de souci, Ă plus ! âïž","error")}const l=filterEvents(a,i);if(console.log("[Assas] After filter:",l.length,"events"),0===l.length)return void showStatus("Rien Ă exporter ! Coche des trucs ïżœ","error");let u;showStatus("đ ïž Fabrication du fichier...","info");try{u=generateIcs(l)}catch(e){return console.error("[Assas] ICS Error:",e),void showStatus(`Oups ! ${e.message} đ
`,"error")}const p=`assas-calendar-${o}-${r}.ics`;await downloadIcsFile(u,p);showStatus(t?`Hop ! ${l.length} cours exportĂ©s pour ${t} đ„`:`Bam ! ${l.length} cours exportĂ©s đ„`,"success"),console.log("[Assas] Done!",p)}catch(e){console.error("[Assas] Error:",e),showStatus(`Erreur: ${e.message}`,"error")}finally{const e=document.getElementById("assas-exporter-running");e&&e.remove()}}();})();