From d500ade42193b34312e10063850af9e6906321ae Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sat, 18 Jan 2025 03:10:53 +0100 Subject: [PATCH] Refactor router --- lib/global.d.ts | 3 +- lib/main.ts | 6 +- lib/utils/language.ts | 4 +- lib/utils/router.ts | 200 ++++++++++++++++++++++++------------------ 4 files changed, 120 insertions(+), 93 deletions(-) diff --git a/lib/global.d.ts b/lib/global.d.ts index 29bfc22b..250fee3d 100644 --- a/lib/global.d.ts +++ b/lib/global.d.ts @@ -1,5 +1,4 @@ import { Config } from "./config_default"; -import Polyglot from "node-polyglot"; import { Router } from "./utils/router"; export {}; @@ -7,6 +6,6 @@ export {}; declare global { interface Window { config: Config; - router: ReturnType; + router: Router; } } diff --git a/lib/main.ts b/lib/main.ts index 0df3cd09..9925a597 100644 --- a/lib/main.ts +++ b/lib/main.ts @@ -80,7 +80,7 @@ export const main = () => { let config = window.config; let language = Language(); - let router = (window.router = Router(language)); + let router = (window.router = new Router(language)); config.dataPath.forEach(function (_element, i) { config.dataPath[i] += "meshviewer.json"; @@ -108,14 +108,14 @@ export const main = () => { })(); }); }) - .then(function (nodesData) { + .then(function (nodesData: any) { let gui = Gui(language); gui.setData(nodesData); router.setData(nodesData); router.resolve(); window.setInterval(function () { - update().then(function (nodesData) { + update().then(function (nodesData: any) { gui.setData(nodesData); router.setData(nodesData); }); diff --git a/lib/utils/language.ts b/lib/utils/language.ts index f583a580..aa48e5de 100644 --- a/lib/utils/language.ts +++ b/lib/utils/language.ts @@ -8,7 +8,7 @@ export type LanguageCode = string; export let _: Polyglot & { phrases?: { [k: string]: any } }; export const Language = function () { - let router: ReturnType; + let router: Router; let config = globalThis.config; function languageSelect(el: HTMLElement) { @@ -62,7 +62,7 @@ export const Language = function () { } } - function init(routing: ReturnType) { + function init(routing: Router) { router = routing; /** global: _ */ _ = new Polyglot({ locale: getLocale(routing.getLang()), allowMissing: true }); diff --git a/lib/utils/router.ts b/lib/utils/router.ts index 6f32f4f1..082c925c 100644 --- a/lib/utils/router.ts +++ b/lib/utils/router.ts @@ -1,4 +1,4 @@ -import Navigo from "navigo"; +import Navigo, { Match } from "navigo"; import { Language } from "./language"; import { Link, NodeId } from "./node"; import { Moment } from "moment"; @@ -28,12 +28,12 @@ interface Views { [k: string]: () => any; } -export const Router = function (language: ReturnType) { - let init = false; - let objects: Objects = { nodeDict: [], links: [] }; - let targets: Target[] = []; - let views: Views = {}; - let current = { +export class Router extends Navigo { + init = false; + objects: Objects = { nodeDict: [], links: [] }; + targets: Target[] = []; + views: Views = {}; + currentState = { lang: undefined, // like de or en view: undefined, // map or graph node: undefined, // Node ID @@ -42,51 +42,59 @@ export const Router = function (language: ReturnType) { lat: undefined, lng: undefined, }; - let state = { lang: language.getLocale(), view: "map" }; + state = { lang: null, view: "map" }; + language = undefined; + + constructor(language: ReturnType) { + super("/", { hash: true }); + this.language = language; + this.state.lang = language.getLocale(); + this.initRoutes(); + } - function resetView() { - targets.forEach(function (target) { + resetView() { + this.targets.forEach(function (target) { target.resetView(); }); } - function gotoNode(node: { nodeId: NodeId }) { - if (objects.nodeDict[node.nodeId]) { - targets.forEach(function (target) { - target.gotoNode(objects.nodeDict[node.nodeId], objects.nodeDict); + gotoNode(node: { nodeId: NodeId }) { + if (this.objects.nodeDict[node.nodeId]) { + this.targets.forEach((target) => { + target.gotoNode(this.objects.nodeDict[node.nodeId], this.objects.nodeDict); }); } } - function gotoLink(linkData: { linkId: string }) { - let link = objects.links.filter(function (value) { + gotoLink(linkData: { linkId: string }) { + let link = this.objects.links.filter(function (value) { return value.id === linkData.linkId; }); if (link) { - targets.forEach(function (target) { + this.targets.forEach(function (target) { target.gotoLink(link); }); } } - function view(data: { view: string }) { - if (data.view in views) { - views[data.view](); - state.view = data.view; - resetView(); + view(data: { view: string }) { + if (data.view in this.views) { + this.views[data.view](); + this.state.view = data.view; + this.resetView(); } } - function customRoute( - lang?: string, - viewValue?: "map" | "graph" | string, - node?: string, - link?: string, - zoom?: number | string, - lat?: number | string, - lng?: number | string, - ) { - current = { + customRoute(match?: Match) { + let lang: string | undefined = match.data[0]; + let viewValue: "map" | "graph" | string | undefined = match.data[1]; + let node: string | undefined = match.data[2]; + let link: string | undefined = match.data[3]; + let zoom: number | string | undefined = match.data[4]; + let lat: number | string | undefined = match.data[5]; + let lng: number | string | undefined = match.data[6]; + + this.currentState = { lang: lang, view: viewValue, node: node, @@ -96,55 +104,78 @@ export const Router = function (language: ReturnType) { lng: lng, }; - if (lang && lang !== state.lang && lang === language.getLocale(lang)) { + if (match.url == "" && !!match.hashString) { + // Fix double # urls on reload + location.hash = match.hashString; + location.reload(); + } + + if (lang && lang !== this.state.lang && lang === this.language.getLocale(lang)) { + location.hash = "/" + match.url; location.reload(); } - if (!init || (viewValue && viewValue !== state.view)) { + if (!this.init || (viewValue && viewValue !== this.state.view)) { if (!viewValue) { - viewValue = state.view; + viewValue = this.state.view; } - view({ view: viewValue }); - init = true; + this.view({ view: viewValue }); + this.init = true; } if (node) { - gotoNode({ nodeId: node }); + this.gotoNode({ nodeId: node }); } else if (link) { - gotoLink({ linkId: link }); + this.gotoLink({ linkId: link }); } else if (lat) { - targets.forEach(function (target) { + this.targets.forEach((target) => { target.gotoLocation({ - zoom: parseInt(current.zoom, 10), - lat: parseFloat(current.lat), - lng: parseFloat(current.lng), + zoom: parseInt(this.currentState.zoom, 10), + lat: parseFloat(this.currentState.lat), + lng: parseFloat(this.currentState.lng), }); }); } else { - resetView(); + this.resetView(); } } - let router = new Navigo(null, true, "#!"); - - router - .on( - /^\/?#?!?\/(\w{2})?\/?(map|graph)?\/?([a-f\d]{12})?([a-f\d\-]{25})?\/?(?:(\d+)\/(-?[\d.]+)\/(-?[\d.]+))?$/, - customRoute, - ) - .on({ - "*": function () { - router.fullUrl(); + initRoutes() { + this.on( + // Redirect legacy URL format + /^\/?!(.*)?$/, + (match?: Match) => { + console.info("legacy"); + this.navigate(match.data[0]); }, - }); + ) + .on( + // lang, viewValue, node, link, zoom, lat, lon + /^\/?(\w{2})?\/?(map|graph)?\/?([a-f\d]{12})?([a-f\d\-]{25})?\/?(?:(\d+)\/(-?[\d.]+)\/(-?[\d.]+))?$/, + (match?: Match) => { + console.info("customRoute"); + this.customRoute(match); + }, + ) + // Default response + .on((match?: Match) => { + console.info("default", match); + this.fullUrl(); + }) + // 404 response + .notFound(() => { + console.info("notFound"); + this.fullUrl(); + }); + } - router.generateLink = function generateLink(data?: {}, full?: boolean, deep?: boolean) { - let result = "#!"; + generateLink(data?: {}, full?: boolean, deep?: boolean) { + let result = "#"; if (full) { - data = Object.assign({}, state, data); + data = Object.assign({}, this.state, data); } else if (deep) { - data = Object.assign({}, current, data); + data = Object.assign({}, this.currentState, data); } for (let key in data) { @@ -155,45 +186,42 @@ export const Router = function (language: ReturnType) { } return result; - }; + } - router.fullUrl = function fullUrl(data?: {}, e?: Event | false, deep?: boolean) { + fullUrl(data?: {}, e?: Event | false, deep?: boolean) { if (e) { e.preventDefault(); } - router.navigate(router.generateLink(data, !deep, deep), false); - }; + this.navigate(this.generateLink(data, !deep, deep)); + } - router.getLang = function getLang() { - let lang = location.hash.match(/^\/?#!?\/(\w{2})\//); + getLang() { + let lang = location.hash.match(/^\/?#?\/(\w{2})\//); if (lang) { - state.lang = language.getLocale(lang[1]); + this.state.lang = this.language.getLocale(lang[1]); return lang[1]; } return null; - }; - - router.addTarget = function addTarget(target: Target) { - targets.push(target); - }; + } - router.removeTarget = function removeTarget(target: Target) { - targets = targets.filter(function (t) { + addTarget(target: Target) { + this.targets.push(target); + } + removeTarget(target: Target) { + this.targets = this.targets.filter(function (t) { return target !== t; }); - }; - - router.addView = function addView(key: string, view: () => any) { - views[key] = view; - }; + } - router.currentView = function currentView(): string | undefined { - return current.view; - }; + addView(key: string, view: () => any) { + this.views[key] = view; + } - router.setData = function setData(data: Objects) { - objects = data; - }; + currentView(): string | undefined { + return this.currentState.view; + } - return router; -}; + setData(data: Objects) { + this.objects = data; + } +}