f1b56268bb
popstate messages from the browser don't have an event.state, and i don't even know what the overridePopState stuff is trying to do. If event.state is null, try window.location.hash, or the last part of the URL path.
109 lines
3.2 KiB
TypeScript
109 lines
3.2 KiB
TypeScript
export interface Page {
|
|
name: string;
|
|
title: string;
|
|
url: string;
|
|
show: () => boolean;
|
|
hide: () => boolean;
|
|
shouldSkip: () => boolean;
|
|
index?: number;
|
|
};
|
|
|
|
export interface PageConfig {
|
|
hideOthersOnPageShow: boolean;
|
|
defaultName: string;
|
|
defaultTitle: string;
|
|
}
|
|
|
|
export class PageManager {
|
|
pages: Map<string, Page>;
|
|
pageList: string[];
|
|
hideOthers: boolean;
|
|
defaultName: string = "";
|
|
defaultTitle: string = "";
|
|
|
|
private _overridePushState = () => {
|
|
const pushState = window.history.pushState;
|
|
window.history.pushState = function (data: any, __: string, _: string | URL) {
|
|
pushState.apply(window.history, arguments);
|
|
let ev = { state: data as string } as PopStateEvent;
|
|
window.onpopstate(ev);
|
|
};
|
|
}
|
|
|
|
private _onpopstate = (event: PopStateEvent) => {
|
|
let name = event.state;
|
|
if (name == null) {
|
|
// Attempt to use hash from URL, if it isn't there, try the last part of the URL.
|
|
if (window.location.hash && window.location.hash.charAt(0) == "#") {
|
|
name = window.location.hash.substring(1);
|
|
} else {
|
|
name = window.location.pathname.split("/").filter(Boolean).at(-1);
|
|
}
|
|
}
|
|
if (!this.pages.has(name)) {
|
|
name = this.pageList[0]
|
|
}
|
|
let success = this.pages.get(name).show();
|
|
if (!success) {
|
|
return;
|
|
}
|
|
if (!(this.hideOthers)) {
|
|
return;
|
|
}
|
|
for (let k of this.pageList) {
|
|
if (name != k) {
|
|
this.pages.get(k).hide();
|
|
}
|
|
}
|
|
}
|
|
|
|
constructor(c: PageConfig) {
|
|
this.pages = new Map<string, Page>;
|
|
this.pageList = [];
|
|
this.hideOthers = c.hideOthersOnPageShow;
|
|
this.defaultName = c.defaultName;
|
|
this.defaultTitle = c.defaultTitle;
|
|
|
|
this._overridePushState();
|
|
window.onpopstate = this._onpopstate;
|
|
}
|
|
|
|
setPage(p: Page) {
|
|
p.index = this.pageList.length;
|
|
this.pages.set(p.name, p);
|
|
this.pageList.push(p.name);
|
|
}
|
|
|
|
load(name: string = "") {
|
|
if (!this.pages.has(name)) return window.history.pushState(name || this.defaultName, this.defaultTitle, "")
|
|
const p = this.pages.get(name);
|
|
this.loadPage(p);
|
|
}
|
|
|
|
loadPage (p: Page) {
|
|
window.history.pushState(p.name || this.defaultName, p.title, p.url + window.location.search);
|
|
}
|
|
|
|
prev(name: string = "") {
|
|
if (!this.pages.has(name)) return console.error(`previous page ${name} not found`);
|
|
let p = this.pages.get(name);
|
|
let shouldSkip = true;
|
|
while (shouldSkip && p.index > 0) {
|
|
p = this.pages.get(this.pageList[p.index-1]);
|
|
shouldSkip = p.shouldSkip();
|
|
}
|
|
this.loadPage(p);
|
|
}
|
|
|
|
next(name: string = "") {
|
|
if (!this.pages.has(name)) return console.error(`previous page ${name} not found`);
|
|
let p = this.pages.get(name);
|
|
let shouldSkip = true;
|
|
while (shouldSkip && p.index < this.pageList.length) {
|
|
p = this.pages.get(this.pageList[p.index+1]);
|
|
shouldSkip = p.shouldSkip();
|
|
}
|
|
this.loadPage(p);
|
|
}
|
|
};
|