import { ROUTE_CHANGED, ROUTE_REQUESTED } from "#core/ids/route.ids"; import { applyHead, headOf, renderPage } from "#presentation/renderers/page.renderer"; import { emitEvent, subscribeEvent } from "#core/buses/base.bus"; import { loadPage, loadedPage } from "#domain/registries/page.registry"; import { pageFromPath, pagePath } from "#assets/link.assets"; import type { Disposer } from "#types/base.types"; import { HOME_PAGE } from "#core/ids/page.ids"; let latest = 0; const render = async function render(main: HTMLElement, page: string): Promise { latest += 1; const ticket = latest; await loadPage(page); if (ticket !== latest) { return; } main.replaceChildren(...renderPage(page)); applyHead(document, headOf(page, loadedPage(page), window.location.pathname)); main.scrollTo(0, 0); emitEvent({ name: ROUTE_CHANGED, page }); }; const navigate = function navigate(main: HTMLElement, page: string, path: string): void { if (window.location.pathname + window.location.search + window.location.hash !== path) { window.history.pushState({ page }, "", path); } void render(main, page); }; export const startRouting = function startRouting(main: HTMLElement): Disposer { const onPopState = function onPopState(): void { void render(main, pageFromPath(window.location.pathname, HOME_PAGE)); }; window.addEventListener("popstate", onPopState); const unsubscribe = subscribeEvent(ROUTE_REQUESTED, (event) => { navigate(main, event.page, event.path ?? pagePath(event.page)); }); onPopState(); return () => { window.removeEventListener("popstate", onPopState); unsubscribe(); }; };