Files
nat-as-server/website/js/router.js
tom.hempel 06fc134299
Some checks failed
Tests / test (push) Has been cancelled
just testing the environment
2026-05-20 07:10:08 +02:00

77 lines
2.0 KiB
JavaScript

const routes = [];
let currentCleanup = null;
/** @returns {{ regex: RegExp, paramNames: string[] }} */
export function compileRoute(pattern) {
const paramNames = [];
const parts = pattern.replace(/\/:([^/]+)/g, (_, name) => {
paramNames.push(name);
return '/([^/]+)';
});
return { regex: new RegExp('^' + parts + '$'), paramNames };
}
/** Match a path against a string route pattern; returns params or null. */
export function matchRoute(pattern, path) {
const { regex, paramNames } = compileRoute(pattern);
const match = path.match(regex);
if (!match) {
return null;
}
const params = {};
paramNames.forEach((name, i) => {
params[name] = decodeURIComponent(match[i + 1]);
});
return params;
}
export function addRoute(pattern, handler) {
let regex;
let paramNames = [];
if (pattern instanceof RegExp) {
regex = pattern;
} else {
({ regex, paramNames } = compileRoute(pattern));
}
routes.push({ regex, paramNames, handler });
}
export function navigate(hash) {
window.location.hash = hash;
}
export function currentHash() {
return window.location.hash.slice(1) || '/';
}
async function resolve() {
if (typeof currentCleanup === 'function') {
currentCleanup();
currentCleanup = null;
}
const path = currentHash();
for (const route of routes) {
const match = path.match(route.regex);
if (match) {
const params = {};
route.paramNames.forEach((name, i) => {
params[name] = decodeURIComponent(match[i + 1]);
});
try {
currentCleanup = await route.handler(params) || null;
} catch (e) {
console.error('Route handler error:', e);
}
return;
}
}
// fallback: dashboard
navigate('#/');
}
export function startRouter() {
window.addEventListener('hashchange', resolve);
resolve();
}