(() => { const main = document.getElementById("app-main"); const footerYear = document.getElementById("footer-year"); const pageStyleSelectors = "[data-page-style='true']"; const pageScriptSelectors = "script[data-page-script='true']"; const links = Array.from(document.querySelectorAll("[data-route]")); if (footerYear) { footerYear.textContent = new Date().getFullYear().toString(); } function clearInjectedPageStyles() { document.querySelectorAll(pageStyleSelectors).forEach((node) => node.remove()); } function clearInjectedPageScripts() { document.dispatchEvent(new CustomEvent("page:before-route-change")); document.querySelectorAll(pageScriptSelectors).forEach((node) => node.remove()); } function injectPageStyles(parsedDocument, sourceUrl) { clearInjectedPageStyles(); const head = parsedDocument.head; if (!head) { return; } const styleNodes = Array.from(head.querySelectorAll("link[rel='stylesheet'], style")); styleNodes.forEach((node) => { const clonedNode = node.cloneNode(true); clonedNode.setAttribute("data-page-style", "true"); if (clonedNode.tagName === "LINK") { const href = clonedNode.getAttribute("href"); if (href) { clonedNode.href = new URL(href, sourceUrl).toString(); } } document.head.appendChild(clonedNode); }); } async function injectPageScripts(parsedDocument, sourceUrl) { clearInjectedPageScripts(); const scriptNodes = Array.from(parsedDocument.querySelectorAll("script")); for (const node of scriptNodes) { const scriptElement = document.createElement("script"); scriptElement.setAttribute("data-page-script", "true"); const src = node.getAttribute("src"); if (src) { scriptElement.src = new URL(src, sourceUrl).toString(); scriptElement.async = false; if (node.defer) { scriptElement.defer = true; } await new Promise((resolve, reject) => { scriptElement.onload = () => resolve(); scriptElement.onerror = () => reject(new Error(`Impossible de charger le script: ${src}`)); document.body.appendChild(scriptElement); }); continue; } scriptElement.textContent = node.textContent; document.body.appendChild(scriptElement); } } function normalizeRoute(value) { const raw = (value ?? "").toString().trim().toLowerCase(); if (!raw || raw === "/" || raw === "home" || raw === "index" || raw === "accueil") { return "index"; } return raw.replace(/^\/+|\/+$/g, "") || "index"; } function routeFromLocation() { const params = new URLSearchParams(window.location.search); if (params.has("p")) { return normalizeRoute(params.get("p")); } const rawSearch = window.location.search.replace(/^\?/, ""); if (rawSearch.startsWith("p/")) { return normalizeRoute(rawSearch.slice(2)); } if (rawSearch.startsWith("p=")) { return normalizeRoute(rawSearch.slice(2)); } const path = window.location.pathname.replace(/^\/+|\/+$/g, ""); if (path) { if (path.endsWith(".html")) { return "index"; } const parts = path.split("/"); if (parts[0] === "p" && parts[1]) { return normalizeRoute(parts.slice(1).join("/")); } return normalizeRoute(parts[0]); } return "index"; } function setActiveRoute(route) { links.forEach((link) => { const isActive = link.dataset.route === route; if (isActive) { link.setAttribute("aria-current", "page"); } else { link.removeAttribute("aria-current"); } }); } function setCanonicalUrl(route, replace = false) { const url = route === "index" ? "/?p/index/" : `/?p/${route}/`; if (replace) { history.replaceState({ route }, "", url); return; } history.pushState({ route }, "", url); } function routeToSource(route) { return `p/${route}/index.html`; } function routeToTitle(route) { if (route === "index") { return "Fws Web"; } const label = route .split("/") .filter(Boolean) .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)) .join(" / "); return `Fws Web - ${label}`; } async function loadRoute(route) { clearInjectedPageScripts(); const source = routeToSource(route); const response = await fetch(source, { cache: "no-store" }); if (!response.ok) { throw new Error(`Impossible de charger ${source} (${response.status})`); } const html = await response.text(); const parsed = new DOMParser().parseFromString(html, "text/html"); injectPageStyles(parsed, response.url); const injected = parsed.querySelector("[data-page-content]") ?? parsed.body; main.innerHTML = injected.innerHTML.trim(); await injectPageScripts(parsed, response.url); } function render(route, { replaceHistory = false } = {}) { const safeRoute = route || "index"; loadRoute(safeRoute) .then(() => { setActiveRoute(safeRoute); setCanonicalUrl(safeRoute, replaceHistory); document.title = routeToTitle(safeRoute); window.scrollTo({ top: 0, behavior: "smooth" }); }) .catch((error) => { main.innerHTML = `

Erreur de chargement

${error.message}

`; setActiveRoute("index"); }); } document.addEventListener("click", (event) => { const link = event.target.closest("[data-route]"); if (!link) { return; } const route = normalizeRoute(link.dataset.route); if (!route) { return; } event.preventDefault(); render(route); }); window.addEventListener("popstate", () => { render(routeFromLocation(), { replaceHistory: true }); }); render(routeFromLocation(), { replaceHistory: true }); })();