/** * Page changes without reloading the whole dashboard. * * A click on a link to another dashboard page (or a GET form) fetches that page and swaps its
, title, page * styles and page scripts in, keeping the sidebar, the top bar and the live WebSocket. Back, forward, deep links and * #hash filters behave as before. Before a swap, what the old page set up is taken down: its document and window * listeners, intervals, Live watchers, DataTables and anything it added to . Page scripts then run again in * order; DOMContentLoaded and load handlers they add run once they all have. * * Falls back to a normal page load when fetching fails on back/forward, the answer isn't a signed-in dashboard page, * the permissions or user changed, or either page is one that has to load on its own: module scripts, an import map * (the 3D views) or data-nav="reload" anywhere in it. A link with data-nav="off" always loads normally. * * Nav.refresh() updates the current page in place: the server's HTML for the page is compared with the HTML the page * started from, and only what differs is patched into the live page, so what scripts put there and what the user is * typing stay. Where that isn't possible (the page's layout changed, or a changed part holds what a script built) the * page is swapped in again. Nav.go(url) opens a page from a script. */ (function () { 'use strict'; // ---- Rules (no DOM; tests/dWebTests/nav.test.mjs) ---- // Paths that are never swapped in: the API, files, the signed-out pages and the metrics var NO_SWAP = /^\/(api|css|js|ws|metrics|login|logout|register|forgot_password|reset_password|verify_email|oauth2)(\/|$)|^\/status\/widget(\/|$)|\.[A-Za-z0-9]{1,5}$/; var rules = { /** * How to follow a link from `here` to `target` (URL objects or {origin, protocol, pathname, search, hash}): * 'swap' to fetch and swap it in, 'hash' when only the fragment of this page changes (left to the browser), * null for a normal load. */ kind: function (target, here) { if (target.origin !== here.origin || !/^https?:$/.test(target.protocol)) return null; if (target.pathname === here.pathname && target.search === here.search && target.hash) return 'hash'; if (NO_SWAP.test(target.pathname)) return null; return 'swap'; }, // The kind of page a path is, with its numbers folded: every property's 3D view is '/properties/*/3d' pageKind: function (path) { return String(path).replace(/\/\d+(?=\/|$)/g, '/*'); }, // Whether a