import { slides } from "@state/slides.svelte"; import { site } from "@state/site.svelte"; import { locale } from "@state/locale.svelte"; let siteInitialized = false; function normalizePath(path) { const stripped = path.replace(/^\/en(\/|$)/, "$1") || "/"; return stripped === "/" ? "/home" : stripped; } function apiPrefix() { return locale.current === "en" ? "/en" : ""; } /** * Trouve l'index de la slide correspondant au path. * Si le path exact n'existe pas, essaie le chemin parent * (ex: /blog/article-slug → /blog). */ function findSlideIndex(path) { let idx = slides.getIndexByPath(path); if (idx !== -1) return idx; const parentPath = path.replace(/\/[^/]+$/, ""); if (parentPath) return slides.getIndexByPath(parentPath); return -1; } async function loadSlide(path) { let slidePath = path; let idx = slides.getIndexByPath(slidePath); // Sub-page: resolve to parent slide (ex: /blog/slug → /blog) if (idx === -1) { const parentPath = path.replace(/\/[^/]+$/, ""); if (parentPath && parentPath !== path) { const parentIdx = slides.getIndexByPath(parentPath); if (parentIdx !== -1) { idx = parentIdx; slidePath = parentPath; } else if (!siteInitialized) { // Slides not yet initialized — assume sub-page, fetch parent to bootstrap slidePath = parentPath; } } } if (idx !== -1) { const slide = slides.all[idx]; if (slide.loaded || slide.loading) return; slides.setLoading(slidePath, true); } try { const response = await fetch(`${apiPrefix()}${slidePath}.json`); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); console.log("slide data", data); if (!siteInitialized && data.site) { site.set(data.site); locale.initialize(data.site.language, data.site.languages); slides.init(data.site.navigation); siteInitialized = true; } const finalIdx = slides.getIndexByPath(slidePath); if (finalIdx !== -1) { slides.setData(slidePath, data); } else { slides.setStandalone(data); } } catch (error) { console.error(`[router] Failed to load slide ${slidePath}:`, error); slides.setLoading(slidePath, false); } } function loadAllSlidesInBackground(exceptPath) { slides.all .filter((s) => s.path !== exceptPath) .forEach((s) => loadSlide(s.path)); } export function slideTo(path, { skipHistory = false } = {}) { path = normalizePath(path); if (!skipHistory) { const historyPath = locale.current === "en" ? path === "/home" ? "/en" : `/en${path}` : path === "/home" ? "/" : path; history.pushState({}, "", historyPath); } const idx = findSlideIndex(path); const slidePath = idx !== -1 ? slides.all[idx].path : path; if (idx !== -1) { slides.clearStandalone(); if (slides.all[idx].title) { document.title = `World Game - ${slides.all[idx].title}`; } if (idx === slides.activeIndex && !skipHistory) { window.dispatchEvent(new PopStateEvent('popstate')); return; } slides.slideTo(slidePath); if (!slides.all[idx].loaded) { loadSlide(slidePath); } } else { // Page standalone (hors navigation) loadSlide(path); } } export async function initRouter() { // Language detection: URL prefix > localStorage > navigator const hasEnPrefix = window.location.pathname.startsWith("/en"); if (hasEnPrefix) { locale.setLanguage("en"); localStorage.setItem("wg_lang", "en"); } else if (!localStorage.getItem("wg_lang")) { const navLang = navigator.language || navigator.languages?.[0] || "fr"; if (navLang.startsWith("en")) { window.location.replace("/en" + window.location.pathname); return; } } else if (localStorage.getItem("wg_lang") === "en") { window.location.replace("/en" + window.location.pathname); return; } const initialPath = normalizePath(window.location.pathname); await loadSlide(initialPath); const idx = findSlideIndex(initialPath); if (idx !== -1) { slides.setActiveIndex(idx); } loadAllSlidesInBackground(idx !== -1 ? slides.all[idx].path : initialPath); window.addEventListener("popstate", () => { const path = normalizePath(window.location.pathname); slideTo(path, { skipHistory: true }); }); document.addEventListener("click", (e) => { const link = e.target.closest("a"); if (!link) return; const url = new URL(link.href, window.location.origin); if ( url.origin === window.location.origin && !link.target && !link.hasAttribute("download") ) { e.preventDefault(); slideTo(url.pathname); } }); } // Keep navigateTo as alias so existing views don't break export const navigateTo = slideTo;