// modules/core/router.js import { init as initClickManager } from './ClickManager.js'; import { navigateTo } from './navigateTo.js'; const routes = new Map(); const wildcards = []; const guards = new Map(); let currentRoute = null; let layoutCallback = null; let metaCallback = null; /** * Registriert eine neue Route mit optionalem Handler * @param {string} path - Pfad der Route * @param {Function} handler - Callback bei Treffer */ export function defineRoute(path, handler) { if (path.includes('*')) { wildcards.push({ pattern: new RegExp('^' + path.replace('*', '.*') + '$'), handler }); } else { routes.set(path, handler); } } /** * Definiert einen Guard für eine Route * @param {string} path - Pfad * @param {Function} guard - Guard-Funktion (return false = block) */ export function guardRoute(path, guard) { guards.set(path, guard); } /** * Gibt die aktuell aktive Route zurück */ export function getRouteContext() { return currentRoute; } /** * Setzt eine Callback-Funktion für dynamische Layout-Switches */ export function onLayoutSwitch(fn) { layoutCallback = fn; } /** * Setzt eine Callback-Funktion für Meta-Daten (z. B. title, theme) */ export function onMetaUpdate(fn) { metaCallback = fn; } function matchRoute(href) { if (routes.has(href)) return routes.get(href); for (const entry of wildcards) { if (entry.pattern.test(href)) return entry.handler; } return null; } function runGuard(href) { const guard = guards.get(href); return guard ? guard(href) !== false : true; } function extractMetaFromHTML(html) { const temp = document.createElement('div'); temp.innerHTML = html; const metaTags = {}; temp.querySelectorAll('[data-meta]').forEach(el => { for (const attr of el.attributes) { if (attr.name.startsWith('data-meta-')) { const key = attr.name.replace('data-meta-', ''); metaTags[key] = attr.value; } } }) //const title = temp.querySelector('[data-meta-title]')?.getAttribute('data-meta-title'); //const theme = temp.querySelector('[data-meta-theme]')?.getAttribute('data-meta-theme'); return { metaTags }; } function animateLayoutSwitch(type) { document.body.dataset.layout = type; document.body.classList.add('layout-transition'); setTimeout(() => document.body.classList.remove('layout-transition'), 300); } /** * Startet den Router */ export function startRouter() { initClickManager((href, link, options) => { if (!runGuard(href)) return; if (options.modal) { const handler = matchRoute(href); currentRoute = { href, modal: true, link, options }; handler?.(currentRoute); layoutCallback?.(currentRoute); metaCallback?.(currentRoute); } else { navigateTo(href, { ...options, onUpdate: (html) => { const container = document.querySelector('main'); if (container) container.innerHTML = html; const routeHandler = matchRoute(href); currentRoute = { href, html, modal: false }; const meta = extractMetaFromHTML(html); if (meta.title) document.title = meta.title; if (meta.theme) document.documentElement.style.setProperty('--theme-color', meta.theme); routeHandler?.(currentRoute); layoutCallback?.(currentRoute); metaCallback?.(currentRoute); } }); } }); // Bei Seitenstart erste Route prüfen window.addEventListener('DOMContentLoaded', () => { const href = location.pathname; const routeHandler = matchRoute(href); currentRoute = { href, modal: false }; routeHandler?.(currentRoute); layoutCallback?.(currentRoute); metaCallback?.(currentRoute); }); } export { animateLayoutSwitch, extractMetaFromHTML };