import Observable from "./helper/observable"; import { Page, PageProps } from "./page"; import { h } from "preact"; function serializQuery(obj: any) { var str = []; for (var p in obj) if (obj.hasOwnProperty(p)) { str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); } return str.join("&"); } function parseQuery(query: string) { let data: any = {}; if (query.startsWith("?")) query = query.slice(1) query.split("&").forEach(e => { let [key, value] = e.split("="); key = decodeURIComponent(key) value = decodeURIComponent(value) data[key] = value }) return data } export default class Navigation { private static _pages: Map = new Map(); private static _page: { route: string, page: JSX.Element }; private static pageObservableServer = new Observable(false); public static pageObservable = Navigation.pageObservableServer.getPublicApi(); private static _state: { [key: string]: any }; private static _hidden_state: { [key: string]: any }; public static addPage(route: string, comp: typeof Page) { Navigation._pages.set(route, comp); } // public static get state() { // return Navigation._state; // } public static set default(comp: typeof Page) { console.log("Set default"); Navigation._pages.set("/", comp); } public static set not_found(comp: typeof Page) { Navigation._pages.set("/404", comp); } public static setPage(route: string, state?: { [key: string]: string }, hidden?: { [key: string]: string }, replace?: boolean) { let component = Navigation._pages.get(route); if (!component && route !== "/404") { Navigation.setPage("/404", { route, key: "404" }) } else { if (!Navigation.page || Navigation.page.route !== route || JSON.stringify(state) !== JSON.stringify(Navigation._state) || JSON.stringify(Navigation._hidden_state) !== JSON.stringify(hidden)) { let s = ""; if (state) { s = "?" + serializQuery(state) } let newhash = "#" + route + s; let newkey = newhash + serializQuery(hidden) let whash = window.location.hash; if (!whash || whash === "") whash = "#/" let oldkey = whash + serializQuery(history.state); if (newkey !== oldkey) { if (replace) window.history.replaceState(hidden, document.title, newhash); else window.history.pushState(hidden, document.title, newhash); } let page = h(component as any, { state: state, key: newhash + serializQuery(hidden), hidden: hidden }); Navigation._state = state; Navigation._hidden_state = hidden; Navigation._page = { page, route }; Navigation.pageObservableServer.send(page); } } } static get page() { return Navigation._page; } static onHashChange(hidden_state: { [key: string]: string }) { let hash = window.location.hash.substring(1); if (hash && hash !== "") { let [route, state] = hash.split("?"); let s; if (state) { try { s = parseQuery(state) } catch (err) { s = undefined; console.error(err); } } Navigation.setPage(route, s, hidden_state) } else { Navigation.setPage("/"); } } static start() { window.addEventListener("popstate", (ev) => { Navigation.onHashChange(ev.state); }) Navigation.onHashChange(undefined); } }