import { safeRun } from "../common/util.ts"; function encodePageUrl(name: string): string { return name.replaceAll(" ", "_"); } function decodePageUrl(url: string): string { return url.replaceAll("_", " "); } export class PathPageNavigator { navigationResolve?: () => void; constructor(readonly indexPage: string, readonly root: string = "") {} async navigate(page: string, pos?: number | string, replaceState = false) { let encodedPage = encodePageUrl(page); if (page === this.indexPage) { encodedPage = ""; } if (replaceState) { window.history.replaceState( { page, pos }, page, `${this.root}/${encodedPage}`, ); } else { window.history.pushState( { page, pos }, page, `${this.root}/${encodedPage}`, ); } window.dispatchEvent( new PopStateEvent("popstate", { state: { page, pos }, }), ); await new Promise((resolve) => { this.navigationResolve = resolve; }); this.navigationResolve = undefined; } subscribe( pageLoadCallback: (pageName: string, pos: number | string) => Promise, ): void { const cb = (event?: PopStateEvent) => { const gotoPage = this.getCurrentPage(); if (!gotoPage) { return; } safeRun(async () => { await pageLoadCallback( this.getCurrentPage(), event?.state?.pos || this.getCurrentPos(), ); if (this.navigationResolve) { this.navigationResolve(); } }); }; window.addEventListener("popstate", cb); cb(); } decodeURI(): [string, number | string] { let [page, pos] = decodeURI( location.pathname.substring(this.root.length + 1), ).split("@"); if (pos) { if (pos.match(/^\d+$/)) { return [page, +pos]; } else { return [page, pos]; } } else { return [page, 0]; } } getCurrentPage(): string { return decodePageUrl(this.decodeURI()[0]) || this.indexPage; } getCurrentPos(): number | string { // console.log("Pos", this.decodeURI()[1]); return this.decodeURI()[1]; } }