uicore-ts 1.1.385 → 1.1.387

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -107,11 +107,8 @@ const _UICore = class extends import_UIObject.UIObject {
107
107
  });
108
108
  };
109
109
  window.addEventListener("scroll", didScroll, false);
110
- const hashDidChange = () => {
111
- this.reapplyCurrentRoute();
112
- };
113
- window.addEventListener("hashchange", hashDidChange, false);
114
- hashDidChange();
110
+ import_UIRoute.UIRoute._installNativeHistoryNavigationGuard(() => this.reapplyCurrentRoute());
111
+ this.reapplyCurrentRoute();
115
112
  }
116
113
  reapplyCurrentRoute() {
117
114
  return __async(this, null, function* () {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../scripts/UICore.ts"],
4
- "sourcesContent": ["import \"./UICoreExtensions\"\nimport { UILanguageService } from \"./UIInterfaces\"\nimport { nil, NO, UIObject } from \"./UIObject\"\nimport { UIRoute } from \"./UIRoute\"\nimport { UIView } from \"./UIView\"\nimport { UIViewController } from \"./UIViewController\"\n\n\nexport class UICore extends UIObject {\n\n rootViewController: UIViewController\n\n\n\n static RootViewControllerClass: typeof UIViewController\n static main: UICore\n\n static languageService: UILanguageService = nil\n\n static readonly broadcastEventName = {\n\n \"RouteDidChange\": \"RouteDidChange\",\n \"WindowDidResize\": \"WindowDidResize\"\n\n }\n\n // See `reapplyCurrentRoute` for why these exist and why route application\n // must always go through it rather than calling `rootViewController.handleRoute`\n // or `handleRouteRecursively` directly.\n _isApplyingRoute = false\n _hasQueuedRouteChangeWhileApplying = false\n _currentRouteApplicationPromise?: Promise<void>\n\n constructor(rootDivElementID: string, rootViewControllerClass: typeof UIViewController, public paddingLength = 20) {\n \n super()\n \n UICore.RootViewControllerClass = rootViewControllerClass\n UICore.main = UICore.main || this\n \n const rootViewElement = document.getElementById(rootDivElementID)\n const rootView = new UIView(rootDivElementID, rootViewElement)\n rootView.pausesPointerEvents = NO //YES;\n rootView.core = this\n \n if (UICore.RootViewControllerClass) {\n \n if (!(UICore.RootViewControllerClass.prototype instanceof UIViewController) ||\n (UICore.RootViewControllerClass as any) === UIViewController) {\n \n console.log(\n \"Error, UICore.RootViewControllerClass must be UIViewController or a subclass of UIViewController, \" +\n \"falling back to UIViewController.\"\n )\n \n UICore.RootViewControllerClass = UIViewController\n \n }\n \n this.rootViewController = new UICore.RootViewControllerClass(rootView)\n \n }\n else {\n \n this.rootViewController = new UIViewController(rootView)\n \n }\n \n this.rootViewController.viewWillAppear().then(() =>\n this.rootViewController.viewDidAppear()\n )\n \n \n this.rootViewController.view.addTargetForControlEvent(\n UIView.controlEvent.PointerUpInside,\n (sender, event) => {\n\n // Only dismiss focus for taps genuinely outside the focused element - not for\n // the tap that just focused it (or a tap on one of its own children). Native\n // controls like <select> gain focus on mousedown and open their own popup; if\n // this handler blurs them on the very next PointerUpInside regardless of target,\n // it closes that popup before the user can pick an option (macOS Chrome renders\n // <select> popups as a native control that closes as soon as its anchor blurs).\n const activeElement = document.activeElement as HTMLElement | null\n const eventTarget = event?.target as Node | null\n\n if (activeElement && eventTarget &&\n (activeElement === eventTarget || activeElement.contains(eventTarget))) {\n return\n }\n\n activeElement?.blur?.()\n\n }\n )\n \n \n const windowDidResize = () => {\n \n UIView.resetLayoutCycleTrackingForAllViews()\n \n this.rootViewController.view.forEachViewInSubtree((view) => {\n \n view._frameCache = undefined\n \n })\n // Doing layout two times to prevent page scrollbars from confusing the layout\n this.rootViewController.view.setNeedsLayout()\n this.rootViewController._triggerLayoutViewSubviews()\n UIView.layoutViewsIfNeeded()\n \n this.rootViewController._triggerLayoutViewSubviews()\n UIView.layoutViewsIfNeeded()\n \n this.rootViewController.view.broadcastEventInSubtree({\n \n name: UICore.broadcastEventName.WindowDidResize,\n parameters: nil\n \n })\n \n }\n \n window.addEventListener(\"resize\", windowDidResize)\n \n const didScroll = () => {\n \n //code\n \n this.rootViewController.view.broadcastEventInSubtree({\n \n name: UIView.broadcastEventName.PageDidScroll,\n parameters: nil\n \n })\n \n \n }\n \n window.addEventListener(\"scroll\", didScroll, false)\n\n const hashDidChange = () => {\n\n //code\n\n this.reapplyCurrentRoute()\n\n }\n\n window.addEventListener(\"hashchange\", hashDidChange, false)\n\n hashDidChange()\n\n\n }\n\n\n /**\n * Re-runs route handling for `UIRoute.currentRoute` against `rootViewController`,\n * serialized against every other caller of this method (including the\n * `hashchange` listener installed in the constructor).\n *\n * This is the ONLY correct way to force route handling to (re-)run - never\n * call `rootViewController.handleRoute(...)` or `handleRouteRecursively(...)`\n * directly. A route change can legitimately be requested twice in quick\n * succession from independent, uncoordinated places (e.g. two related\n * server-pushed broadcasts derived from one real-world event, each\n * triggering its own re-apply). Without serialization, two concurrent,\n * un-synchronized route applications race on shared root-view-controller\n * state (`contentViewController`, `detailsViewController`) and whichever\n * finishes last wins - regardless of which request was actually last, or\n * which request even reflects the current, settled state. Serializing here\n * instead coalesces a request that arrives mid-application into a single\n * trailing re-run rather than starting a second, overlapping one, and that\n * re-run re-reads `UIRoute.currentRoute` fresh once the burst has settled,\n * so the final state always matches the truly-last route.\n */\n async reapplyCurrentRoute(): Promise<void> {\n\n if (this._isApplyingRoute) {\n\n // Joining an in-flight application: request one more loop pass\n // (guaranteed to observe `UIRoute.currentRoute` fresh, since the\n // owner below re-reads it at the top of every pass) and await the\n // SAME promise the owner is already awaiting, rather than\n // returning early - a caller of this method expects route\n // handling to have actually run by the time it resolves.\n this._hasQueuedRouteChangeWhileApplying = true\n return this._currentRouteApplicationPromise\n\n }\n\n this._isApplyingRoute = true\n\n this._currentRouteApplicationPromise = (async () => {\n\n try {\n\n do {\n\n this._hasQueuedRouteChangeWhileApplying = false\n\n await this.rootViewController.handleRouteRecursively(UIRoute.currentRoute)\n\n this.rootViewController.view.broadcastEventInSubtree({\n\n name: UICore.broadcastEventName.RouteDidChange,\n parameters: nil\n\n })\n\n } while (this._hasQueuedRouteChangeWhileApplying)\n\n }\n finally {\n\n this._isApplyingRoute = false\n this._currentRouteApplicationPromise = undefined\n\n }\n\n })()\n\n return this._currentRouteApplicationPromise\n\n }\n\n\n}\n\n\nArray.prototype.indexOf || (Array.prototype.indexOf = function (d, e) {\n let a\n if (null == this) {\n throw new TypeError(\"\\\"this\\\" is null or not defined\")\n }\n const c = Object(this),\n b = c.length >>> 0\n if (0 === b) {\n return -1\n }\n // @ts-ignore\n a = +e || 0\n Infinity === Math.abs(a) && (a = 0)\n if (a >= b) {\n return -1\n }\n for (a = Math.max(0 <= a ? a : b - Math.abs(a), 0); a < b;) {\n if (a in c && c[a] === d) {\n return a\n }\n a++\n }\n return -1\n})\n\n\n\n\n\n\n\n\n\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import \"./UICoreExtensions\"\nimport { UILanguageService } from \"./UIInterfaces\"\nimport { nil, NO, UIObject } from \"./UIObject\"\nimport { UIRoute } from \"./UIRoute\"\nimport { UIView } from \"./UIView\"\nimport { UIViewController } from \"./UIViewController\"\n\n\nexport class UICore extends UIObject {\n\n rootViewController: UIViewController\n\n\n\n static RootViewControllerClass: typeof UIViewController\n static main: UICore\n\n static languageService: UILanguageService = nil\n\n static readonly broadcastEventName = {\n\n \"RouteDidChange\": \"RouteDidChange\",\n \"WindowDidResize\": \"WindowDidResize\"\n\n }\n\n // See `reapplyCurrentRoute` for why these exist and why route application\n // must always go through it rather than calling `rootViewController.handleRoute`\n // or `handleRouteRecursively` directly.\n _isApplyingRoute = false\n _hasQueuedRouteChangeWhileApplying = false\n _currentRouteApplicationPromise?: Promise<void>\n\n constructor(rootDivElementID: string, rootViewControllerClass: typeof UIViewController, public paddingLength = 20) {\n \n super()\n \n UICore.RootViewControllerClass = rootViewControllerClass\n UICore.main = UICore.main || this\n \n const rootViewElement = document.getElementById(rootDivElementID)\n const rootView = new UIView(rootDivElementID, rootViewElement)\n rootView.pausesPointerEvents = NO //YES;\n rootView.core = this\n \n if (UICore.RootViewControllerClass) {\n \n if (!(UICore.RootViewControllerClass.prototype instanceof UIViewController) ||\n (UICore.RootViewControllerClass as any) === UIViewController) {\n \n console.log(\n \"Error, UICore.RootViewControllerClass must be UIViewController or a subclass of UIViewController, \" +\n \"falling back to UIViewController.\"\n )\n \n UICore.RootViewControllerClass = UIViewController\n \n }\n \n this.rootViewController = new UICore.RootViewControllerClass(rootView)\n \n }\n else {\n \n this.rootViewController = new UIViewController(rootView)\n \n }\n \n this.rootViewController.viewWillAppear().then(() =>\n this.rootViewController.viewDidAppear()\n )\n \n \n this.rootViewController.view.addTargetForControlEvent(\n UIView.controlEvent.PointerUpInside,\n (sender, event) => {\n\n // Only dismiss focus for taps genuinely outside the focused element - not for\n // the tap that just focused it (or a tap on one of its own children). Native\n // controls like <select> gain focus on mousedown and open their own popup; if\n // this handler blurs them on the very next PointerUpInside regardless of target,\n // it closes that popup before the user can pick an option (macOS Chrome renders\n // <select> popups as a native control that closes as soon as its anchor blurs).\n const activeElement = document.activeElement as HTMLElement | null\n const eventTarget = event?.target as Node | null\n\n if (activeElement && eventTarget &&\n (activeElement === eventTarget || activeElement.contains(eventTarget))) {\n return\n }\n\n activeElement?.blur?.()\n\n }\n )\n \n \n const windowDidResize = () => {\n \n UIView.resetLayoutCycleTrackingForAllViews()\n \n this.rootViewController.view.forEachViewInSubtree((view) => {\n \n view._frameCache = undefined\n \n })\n // Doing layout two times to prevent page scrollbars from confusing the layout\n this.rootViewController.view.setNeedsLayout()\n this.rootViewController._triggerLayoutViewSubviews()\n UIView.layoutViewsIfNeeded()\n \n this.rootViewController._triggerLayoutViewSubviews()\n UIView.layoutViewsIfNeeded()\n \n this.rootViewController.view.broadcastEventInSubtree({\n \n name: UICore.broadcastEventName.WindowDidResize,\n parameters: nil\n \n })\n \n }\n \n window.addEventListener(\"resize\", windowDidResize)\n \n const didScroll = () => {\n \n //code\n \n this.rootViewController.view.broadcastEventInSubtree({\n \n name: UIView.broadcastEventName.PageDidScroll,\n parameters: nil\n \n })\n \n \n }\n \n window.addEventListener(\"scroll\", didScroll, false)\n\n // Native Back/Forward navigation fires `hashchange` directly, which\n // would otherwise reapply the route unconditionally below with no\n // chance for the app to veto it (e.g. to protect unsaved changes) the\n // way `UIRoute.apply()` can. This makes native history traversal go\n // through the same `UIRoute.shouldApplyRouteChange` check first.\n UIRoute._installNativeHistoryNavigationGuard(() => this.reapplyCurrentRoute())\n\n this.reapplyCurrentRoute()\n\n\n }\n\n\n /**\n * Re-runs route handling for `UIRoute.currentRoute` against `rootViewController`,\n * serialized against every other caller of this method (including the\n * `hashchange` listener installed in the constructor).\n *\n * This is the ONLY correct way to force route handling to (re-)run - never\n * call `rootViewController.handleRoute(...)` or `handleRouteRecursively(...)`\n * directly. A route change can legitimately be requested twice in quick\n * succession from independent, uncoordinated places (e.g. two related\n * server-pushed broadcasts derived from one real-world event, each\n * triggering its own re-apply). Without serialization, two concurrent,\n * un-synchronized route applications race on shared root-view-controller\n * state (`contentViewController`, `detailsViewController`) and whichever\n * finishes last wins - regardless of which request was actually last, or\n * which request even reflects the current, settled state. Serializing here\n * instead coalesces a request that arrives mid-application into a single\n * trailing re-run rather than starting a second, overlapping one, and that\n * re-run re-reads `UIRoute.currentRoute` fresh once the burst has settled,\n * so the final state always matches the truly-last route.\n */\n async reapplyCurrentRoute(): Promise<void> {\n\n if (this._isApplyingRoute) {\n\n // Joining an in-flight application: request one more loop pass\n // (guaranteed to observe `UIRoute.currentRoute` fresh, since the\n // owner below re-reads it at the top of every pass) and await the\n // SAME promise the owner is already awaiting, rather than\n // returning early - a caller of this method expects route\n // handling to have actually run by the time it resolves.\n this._hasQueuedRouteChangeWhileApplying = true\n return this._currentRouteApplicationPromise\n\n }\n\n this._isApplyingRoute = true\n\n this._currentRouteApplicationPromise = (async () => {\n\n try {\n\n do {\n\n this._hasQueuedRouteChangeWhileApplying = false\n\n await this.rootViewController.handleRouteRecursively(UIRoute.currentRoute)\n\n this.rootViewController.view.broadcastEventInSubtree({\n\n name: UICore.broadcastEventName.RouteDidChange,\n parameters: nil\n\n })\n\n } while (this._hasQueuedRouteChangeWhileApplying)\n\n }\n finally {\n\n this._isApplyingRoute = false\n this._currentRouteApplicationPromise = undefined\n\n }\n\n })()\n\n return this._currentRouteApplicationPromise\n\n }\n\n\n}\n\n\nArray.prototype.indexOf || (Array.prototype.indexOf = function (d, e) {\n let a\n if (null == this) {\n throw new TypeError(\"\\\"this\\\" is null or not defined\")\n }\n const c = Object(this),\n b = c.length >>> 0\n if (0 === b) {\n return -1\n }\n // @ts-ignore\n a = +e || 0\n Infinity === Math.abs(a) && (a = 0)\n if (a >= b) {\n return -1\n }\n for (a = Math.max(0 <= a ? a : b - Math.abs(a), 0); a < b;) {\n if (a in c && c[a] === d) {\n return a\n }\n a++\n }\n return -1\n})\n\n\n\n\n\n\n\n\n\n\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -17,6 +17,11 @@ export interface UIRouteChangeDescriptor {
17
17
  export type UIRouteShouldApplyRouteChangeCallbackFunction = (routeChangeDescriptor: UIRouteChangeDescriptor) => boolean | Promise<boolean>;
18
18
  export declare class UIRoute extends Array<UIRouteComponent> {
19
19
  static shouldApplyRouteChange?: UIRouteShouldApplyRouteChangeCallbackFunction;
20
+ static _presentedRoute: UIRoute;
21
+ static _presentedHistoryPosition: number;
22
+ static _recordedHistoryLength: number;
23
+ static _selfInitiatedRouteChange?: UIRouteChangeDescriptor;
24
+ static _nativeHistoryNavigationGeneration: number;
20
25
  constructor(hash?: string);
21
26
  static get currentRoute(): UIRoute;
22
27
  /**
@@ -29,11 +34,11 @@ export declare class UIRoute extends Array<UIRouteComponent> {
29
34
  */
30
35
  apply(forcefully?: boolean): boolean | Promise<boolean>;
31
36
  /**
32
- * Applies this route without consulting `UIRoute.routeApplicationShouldProceed`.
37
+ * Applies this route without consulting `UIRoute.shouldApplyRouteChange`.
33
38
  * Use this only for route changes that are internal bookkeeping or that already
34
39
  * passed a higher-level confirmation flow.
35
40
  */
36
- applyWithoutRouteApplicationCheck(forcefully?: boolean): boolean;
41
+ applyWithoutRouteChangeApproval(forcefully?: boolean): boolean;
37
42
  /**
38
43
  * @param forcefully When `true`, replaces the current history entry with
39
44
  * this route even when the route is identical to the current URL hash.
@@ -42,14 +47,57 @@ export declare class UIRoute extends Array<UIRouteComponent> {
42
47
  applyByReplacingCurrentRouteInHistory(forcefully?: boolean): boolean | Promise<boolean>;
43
48
  /**
44
49
  * Replaces the current history entry without consulting
45
- * `UIRoute.routeApplicationShouldProceed`.
50
+ * `UIRoute.shouldApplyRouteChange`.
46
51
  */
47
- applyByReplacingCurrentRouteInHistoryWithoutRouteApplicationCheck(forcefully?: boolean): boolean;
48
- _applyRouteAfterRouteApplicationCheck(forcefully: boolean, replacesCurrentRouteInHistory: boolean): boolean | Promise<boolean>;
49
- _applyRouteIfAllowed(routeApplicationShouldProceed: boolean, forcefully: boolean, replacesCurrentRouteInHistory: boolean): boolean;
50
- _applyRouteWithoutRouteApplicationCheck(forcefully: boolean, replacesCurrentRouteInHistory: boolean): boolean;
51
- _needsRouteApplication(forcefully: boolean): boolean;
52
- _routeApplication(forcefully: boolean, replacesCurrentRouteInHistory: boolean): UIRouteChangeDescriptor;
52
+ applyByReplacingCurrentRouteInHistoryWithoutRouteChangeApproval(forcefully?: boolean): boolean;
53
+ _applyRouteAfterRouteChangeApproval(forcefully: boolean, replacesCurrentRouteInHistory: boolean): boolean | Promise<boolean>;
54
+ _applyRouteIfAllowed(isRouteChangeApproved: boolean, forcefully: boolean, replacesCurrentRouteInHistory: boolean): boolean;
55
+ _applyRouteWithoutRouteChangeApproval(forcefully: boolean, replacesCurrentRouteInHistory: boolean): boolean;
56
+ _needsRouteChange(forcefully: boolean): boolean;
57
+ _routeChangeDescriptor(forcefully: boolean, replacesCurrentRouteInHistory: boolean): UIRouteChangeDescriptor;
58
+ /**
59
+ * Installs handling so that native browser Back/Forward navigation
60
+ * consults `shouldApplyRouteChange` the same way `apply()` does, instead
61
+ * of silently bypassing it. Call exactly once, from `UICore`'s
62
+ * constructor.
63
+ *
64
+ * @param reapplyCurrentRoute Re-runs route handling for `UIRoute.currentRoute`.
65
+ * Called once a native navigation is allowed to proceed (or is self-initiated),
66
+ * so its effect actually appears on screen.
67
+ */
68
+ static _installNativeHistoryNavigationGuard(reapplyCurrentRoute: () => void | Promise<void>): void;
69
+ static _historyPositionFromCurrentEntry(): number | undefined;
70
+ static _recordPresentedRouteAtHistoryPosition(route: UIRoute, historyPosition: number): void;
71
+ /**
72
+ * The single listener installed for every `hashchange` - self-initiated,
73
+ * native Back/Forward, or an untagged bypass (see the class-level comment
74
+ * above `_presentedRoute`) - and the point where those three cases are
75
+ * told apart.
76
+ *
77
+ * @param event The native event. Only used to call
78
+ * `stopImmediatePropagation()` when a native traversal needs to be held
79
+ * back pending `shouldApplyRouteChange`, so `UICore`'s own route handling
80
+ * doesn't run before that check settles.
81
+ * @param reapplyCurrentRoute Forwarded to `_applyHistoryNavigationToRouteAtPositionIfAllowed`
82
+ * - see its own doc comment.
83
+ */
84
+ static _hashDidChange(event: HashChangeEvent, reapplyCurrentRoute: () => void | Promise<void>): void;
85
+ /**
86
+ * Consults `shouldApplyRouteChange` for a native Back/Forward traversal,
87
+ * exactly like `apply()` does for a programmatic one, then either lets it
88
+ * through or reverts it with `history.go(...)`.
89
+ *
90
+ * @param targetRoute The route the native traversal is asking to move to.
91
+ * @param targetHistoryPosition That route's tagged position, used both to
92
+ * revert (`history.go`) and to detect a newer navigation arriving mid-check.
93
+ * @param navigationGeneration A token captured by the caller at dispatch
94
+ * time. If it no longer matches `_nativeHistoryNavigationGeneration` by
95
+ * the time `shouldApplyRouteChange` resolves, a newer navigation has
96
+ * superseded this one and this check's result is discarded.
97
+ * @param reapplyCurrentRoute Called once the navigation is approved, so
98
+ * `UIRoute.currentRoute`'s new value actually takes effect on screen.
99
+ */
100
+ static _applyHistoryNavigationToRouteAtPositionIfAllowed(targetRoute: UIRoute, targetHistoryPosition: number, navigationGeneration: number, reapplyCurrentRoute: () => void | Promise<void>): Promise<void>;
53
101
  copy(): UIRoute;
54
102
  routeByRemovingComponentsOtherThanOnesNamed(componentNames: string[]): UIRoute;
55
103
  routeByRemovingComponentNamed(componentName: string): UIRoute;
@@ -16,13 +16,34 @@ var __copyProps = (to, from, except, desc) => {
16
16
  return to;
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var __async = (__this, __arguments, generator) => {
20
+ return new Promise((resolve, reject) => {
21
+ var fulfilled = (value) => {
22
+ try {
23
+ step(generator.next(value));
24
+ } catch (e) {
25
+ reject(e);
26
+ }
27
+ };
28
+ var rejected = (value) => {
29
+ try {
30
+ step(generator.throw(value));
31
+ } catch (e) {
32
+ reject(e);
33
+ }
34
+ };
35
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
36
+ step((generator = generator.apply(__this, __arguments)).next());
37
+ });
38
+ };
19
39
  var UIRoute_exports = {};
20
40
  __export(UIRoute_exports, {
21
41
  UIRoute: () => UIRoute
22
42
  });
23
43
  module.exports = __toCommonJS(UIRoute_exports);
24
44
  var import_UIObject = require("./UIObject");
25
- class UIRoute extends Array {
45
+ const historyPositionStateKey = "uiRouteHistoryPosition";
46
+ const _UIRoute = class extends Array {
26
47
  constructor(hash) {
27
48
  super();
28
49
  if (!hash || !hash.startsWith) {
@@ -56,35 +77,35 @@ class UIRoute extends Array {
56
77
  });
57
78
  }
58
79
  static get currentRoute() {
59
- return new UIRoute(window.location.hash);
80
+ return new _UIRoute(window.location.hash);
60
81
  }
61
82
  apply(forcefully = import_UIObject.NO) {
62
- return this._applyRouteAfterRouteApplicationCheck(forcefully, import_UIObject.NO);
83
+ return this._applyRouteAfterRouteChangeApproval(forcefully, import_UIObject.NO);
63
84
  }
64
- applyWithoutRouteApplicationCheck(forcefully = import_UIObject.NO) {
65
- return this._applyRouteWithoutRouteApplicationCheck(forcefully, import_UIObject.NO);
85
+ applyWithoutRouteChangeApproval(forcefully = import_UIObject.NO) {
86
+ return this._applyRouteWithoutRouteChangeApproval(forcefully, import_UIObject.NO);
66
87
  }
67
88
  applyByReplacingCurrentRouteInHistory(forcefully = import_UIObject.NO) {
68
- return this._applyRouteAfterRouteApplicationCheck(forcefully, import_UIObject.YES);
89
+ return this._applyRouteAfterRouteChangeApproval(forcefully, import_UIObject.YES);
69
90
  }
70
- applyByReplacingCurrentRouteInHistoryWithoutRouteApplicationCheck(forcefully = import_UIObject.NO) {
71
- return this._applyRouteWithoutRouteApplicationCheck(forcefully, import_UIObject.YES);
91
+ applyByReplacingCurrentRouteInHistoryWithoutRouteChangeApproval(forcefully = import_UIObject.NO) {
92
+ return this._applyRouteWithoutRouteChangeApproval(forcefully, import_UIObject.YES);
72
93
  }
73
- _applyRouteAfterRouteApplicationCheck(forcefully, replacesCurrentRouteInHistory) {
74
- if (!this._needsRouteApplication(forcefully)) {
94
+ _applyRouteAfterRouteChangeApproval(forcefully, replacesCurrentRouteInHistory) {
95
+ if (!this._needsRouteChange(forcefully)) {
75
96
  return import_UIObject.NO;
76
97
  }
77
- const routeApplicationShouldProceed = UIRoute.shouldApplyRouteChange;
78
- if (!routeApplicationShouldProceed) {
79
- return this._applyRouteWithoutRouteApplicationCheck(forcefully, replacesCurrentRouteInHistory);
98
+ const shouldApplyRouteChange = _UIRoute.shouldApplyRouteChange;
99
+ if (!shouldApplyRouteChange) {
100
+ return this._applyRouteWithoutRouteChangeApproval(forcefully, replacesCurrentRouteInHistory);
80
101
  }
81
- const shouldProceed = routeApplicationShouldProceed(
82
- this._routeApplication(forcefully, replacesCurrentRouteInHistory)
102
+ const shouldProceed = shouldApplyRouteChange(
103
+ this._routeChangeDescriptor(forcefully, replacesCurrentRouteInHistory)
83
104
  );
84
105
  if (shouldProceed instanceof Promise) {
85
- return shouldProceed.then((routeApplicationShouldProceed2) => {
106
+ return shouldProceed.then((isRouteChangeApproved) => {
86
107
  return this._applyRouteIfAllowed(
87
- routeApplicationShouldProceed2,
108
+ isRouteChangeApproved,
88
109
  forcefully,
89
110
  replacesCurrentRouteInHistory
90
111
  );
@@ -92,16 +113,17 @@ class UIRoute extends Array {
92
113
  }
93
114
  return this._applyRouteIfAllowed(shouldProceed, forcefully, replacesCurrentRouteInHistory);
94
115
  }
95
- _applyRouteIfAllowed(routeApplicationShouldProceed, forcefully, replacesCurrentRouteInHistory) {
96
- if (!routeApplicationShouldProceed) {
116
+ _applyRouteIfAllowed(isRouteChangeApproved, forcefully, replacesCurrentRouteInHistory) {
117
+ if (!isRouteChangeApproved) {
97
118
  return import_UIObject.NO;
98
119
  }
99
- return this._applyRouteWithoutRouteApplicationCheck(forcefully, replacesCurrentRouteInHistory);
120
+ return this._applyRouteWithoutRouteChangeApproval(forcefully, replacesCurrentRouteInHistory);
100
121
  }
101
- _applyRouteWithoutRouteApplicationCheck(forcefully, replacesCurrentRouteInHistory) {
102
- if (!this._needsRouteApplication(forcefully)) {
122
+ _applyRouteWithoutRouteChangeApproval(forcefully, replacesCurrentRouteInHistory) {
123
+ if (!this._needsRouteChange(forcefully)) {
103
124
  return import_UIObject.NO;
104
125
  }
126
+ _UIRoute._selfInitiatedRouteChange = this._routeChangeDescriptor(forcefully, replacesCurrentRouteInHistory);
105
127
  if (replacesCurrentRouteInHistory) {
106
128
  window.location.replace(this.linkRepresentation);
107
129
  } else {
@@ -109,22 +131,111 @@ class UIRoute extends Array {
109
131
  }
110
132
  return import_UIObject.YES;
111
133
  }
112
- _needsRouteApplication(forcefully) {
113
- if (!forcefully && new UIRoute(window.location.hash).stringRepresentation == this.stringRepresentation) {
134
+ _needsRouteChange(forcefully) {
135
+ if (!forcefully && new _UIRoute(window.location.hash).stringRepresentation == this.stringRepresentation) {
114
136
  return import_UIObject.NO;
115
137
  }
116
138
  return import_UIObject.YES;
117
139
  }
118
- _routeApplication(forcefully, replacesCurrentRouteInHistory) {
140
+ _routeChangeDescriptor(forcefully, replacesCurrentRouteInHistory) {
119
141
  return {
120
- currentRoute: UIRoute.currentRoute,
142
+ currentRoute: _UIRoute.currentRoute,
121
143
  targetRoute: this.copy(),
122
144
  forcefully,
123
145
  replacesCurrentRouteInHistory
124
146
  };
125
147
  }
148
+ static _installNativeHistoryNavigationGuard(reapplyCurrentRoute) {
149
+ var _a;
150
+ _UIRoute._recordPresentedRouteAtHistoryPosition(
151
+ _UIRoute.currentRoute,
152
+ (_a = _UIRoute._historyPositionFromCurrentEntry()) != null ? _a : 0
153
+ );
154
+ window.addEventListener(
155
+ "hashchange",
156
+ (event) => _UIRoute._hashDidChange(event, reapplyCurrentRoute)
157
+ );
158
+ }
159
+ static _historyPositionFromCurrentEntry() {
160
+ var _a;
161
+ const historyPosition = (_a = window.history.state) == null ? void 0 : _a[historyPositionStateKey];
162
+ if (typeof historyPosition !== "number") {
163
+ return void 0;
164
+ }
165
+ return historyPosition;
166
+ }
167
+ static _recordPresentedRouteAtHistoryPosition(route, historyPosition) {
168
+ const historyState = Object.assign({}, window.history.state);
169
+ historyState[historyPositionStateKey] = historyPosition;
170
+ window.history.replaceState(historyState, "");
171
+ _UIRoute._presentedRoute = route.copy();
172
+ _UIRoute._presentedHistoryPosition = historyPosition;
173
+ _UIRoute._recordedHistoryLength = window.history.length;
174
+ }
175
+ static _hashDidChange(event, reapplyCurrentRoute) {
176
+ const targetRoute = _UIRoute.currentRoute;
177
+ const selfInitiatedRouteChange = _UIRoute._selfInitiatedRouteChange;
178
+ _UIRoute._selfInitiatedRouteChange = void 0;
179
+ if (selfInitiatedRouteChange && selfInitiatedRouteChange.targetRoute.stringRepresentation === targetRoute.stringRepresentation) {
180
+ let targetHistoryPosition2 = _UIRoute._presentedHistoryPosition;
181
+ if (!selfInitiatedRouteChange.replacesCurrentRouteInHistory) {
182
+ targetHistoryPosition2 = targetHistoryPosition2 + 1;
183
+ }
184
+ _UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, targetHistoryPosition2);
185
+ return;
186
+ }
187
+ const targetHistoryPosition = _UIRoute._historyPositionFromCurrentEntry();
188
+ if (targetHistoryPosition === void 0) {
189
+ let untaggedHistoryPosition = _UIRoute._presentedHistoryPosition;
190
+ if (window.history.length > _UIRoute._recordedHistoryLength) {
191
+ untaggedHistoryPosition = untaggedHistoryPosition + 1;
192
+ }
193
+ _UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, untaggedHistoryPosition);
194
+ return;
195
+ }
196
+ if (targetHistoryPosition === _UIRoute._presentedHistoryPosition) {
197
+ _UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, targetHistoryPosition);
198
+ return;
199
+ }
200
+ event.stopImmediatePropagation();
201
+ const navigationGeneration = ++_UIRoute._nativeHistoryNavigationGeneration;
202
+ _UIRoute._applyHistoryNavigationToRouteAtPositionIfAllowed(
203
+ targetRoute,
204
+ targetHistoryPosition,
205
+ navigationGeneration,
206
+ reapplyCurrentRoute
207
+ ).catch((error) => {
208
+ console.error("UIRoute: native history navigation check failed", error);
209
+ });
210
+ }
211
+ static _applyHistoryNavigationToRouteAtPositionIfAllowed(targetRoute, targetHistoryPosition, navigationGeneration, reapplyCurrentRoute) {
212
+ return __async(this, null, function* () {
213
+ const shouldApplyRouteChange = _UIRoute.shouldApplyRouteChange;
214
+ const isRouteChangeApproved = shouldApplyRouteChange ? yield shouldApplyRouteChange({
215
+ currentRoute: _UIRoute._presentedRoute.copy(),
216
+ targetRoute: targetRoute.copy(),
217
+ forcefully: import_UIObject.NO,
218
+ replacesCurrentRouteInHistory: import_UIObject.NO
219
+ }) : import_UIObject.YES;
220
+ if (navigationGeneration !== _UIRoute._nativeHistoryNavigationGeneration) {
221
+ return;
222
+ }
223
+ if (_UIRoute.currentRoute.stringRepresentation !== targetRoute.stringRepresentation || _UIRoute._historyPositionFromCurrentEntry() !== targetHistoryPosition) {
224
+ return;
225
+ }
226
+ if (isRouteChangeApproved) {
227
+ _UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, targetHistoryPosition);
228
+ yield reapplyCurrentRoute();
229
+ return;
230
+ }
231
+ const historyDelta = _UIRoute._presentedHistoryPosition - targetHistoryPosition;
232
+ if (historyDelta !== 0) {
233
+ window.history.go(historyDelta);
234
+ }
235
+ });
236
+ }
126
237
  copy() {
127
- const result = new UIRoute(this.stringRepresentation);
238
+ const result = new _UIRoute(this.stringRepresentation);
128
239
  return result;
129
240
  }
130
241
  routeByRemovingComponentsOtherThanOnesNamed(componentNames) {
@@ -232,7 +343,12 @@ class UIRoute extends Array {
232
343
  });
233
344
  return result;
234
345
  }
235
- }
346
+ };
347
+ let UIRoute = _UIRoute;
348
+ UIRoute._presentedRoute = new _UIRoute();
349
+ UIRoute._presentedHistoryPosition = 0;
350
+ UIRoute._recordedHistoryLength = 0;
351
+ UIRoute._nativeHistoryNavigationGeneration = 0;
236
352
  // Annotate the CommonJS export names for ESM import in node:
237
353
  0 && (module.exports = {
238
354
  UIRoute
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../scripts/UIRoute.ts"],
4
- "sourcesContent": ["import { IS_NIL, IS_NOT, NO, ValueOf, YES } from \"./UIObject\"\nimport { UIViewController } from \"./UIViewController\"\n\n\nexport type PropType<TObj, TProp extends keyof TObj> = TObj[TProp];\n\nexport type UIRouteParameters<T = any> = {\n \n [key: string]: string;\n \n} | T;\n\n\nexport interface UIRouteComponent<T = any> {\n \n name: string;\n parameters: UIRouteParameters<T>;\n \n}\n\n\nexport interface UIRouteChangeDescriptor {\n \n currentRoute: UIRoute;\n targetRoute: UIRoute;\n forcefully: boolean;\n replacesCurrentRouteInHistory: boolean;\n \n}\n\n\nexport type UIRouteShouldApplyRouteChangeCallbackFunction = (\n routeChangeDescriptor: UIRouteChangeDescriptor\n) => boolean | Promise<boolean>;\n\n\nexport class UIRoute extends Array<UIRouteComponent> {\n \n static shouldApplyRouteChange?: UIRouteShouldApplyRouteChangeCallbackFunction\n \n constructor(hash?: string) {\n \n super()\n \n if (!hash || !hash.startsWith) {\n \n return\n \n }\n \n if (hash.startsWith(\"#\")) {\n hash = hash.slice(1)\n }\n \n hash = decodeURIComponent(hash)\n \n const components = hash.split(\"]\")\n components.forEach(component => {\n \n const componentName = component.split(\"[\")[0]\n const parameters: Record<string, string> = {}\n \n if (!componentName) {\n return\n }\n \n const parametersString = component.split(\"[\")[1] || \"\"\n const parameterPairStrings = parametersString.split(\",\") || []\n \n parameterPairStrings.forEach(pairString => {\n \n const keyAndValueArray = pairString.split(\":\")\n const key = decodeURIComponent(keyAndValueArray[0])\n const value = decodeURIComponent(keyAndValueArray[1])\n \n if (key) {\n parameters[key] = value\n }\n \n })\n \n \n this.push({\n name: componentName,\n parameters: parameters\n })\n \n })\n \n \n }\n \n \n static get currentRoute() {\n \n return new UIRoute(window.location.hash)\n \n }\n \n \n /**\n * @param forcefully When `true`, applies the route even if its string\n * representation is identical to the current URL hash, causing the browser\n * to fire a `hashchange` event and re-run the full `handleRoute` flow.\n * Use this only when you explicitly need re-entrant navigation (e.g. the\n * \"tap the already-active tab to reset to root\" feature in TopBarView).\n * Defaults to `false`.\n */\n apply(forcefully = NO): boolean | Promise<boolean> {\n \n return this._applyRouteAfterRouteApplicationCheck(forcefully, NO)\n \n }\n \n \n /**\n * Applies this route without consulting `UIRoute.routeApplicationShouldProceed`.\n * Use this only for route changes that are internal bookkeeping or that already\n * passed a higher-level confirmation flow.\n */\n applyWithoutRouteApplicationCheck(forcefully = NO): boolean {\n \n return this._applyRouteWithoutRouteApplicationCheck(forcefully, NO)\n \n }\n \n \n /**\n * @param forcefully When `true`, replaces the current history entry with\n * this route even when the route is identical to the current URL hash.\n * Defaults to `false`.\n */\n applyByReplacingCurrentRouteInHistory(forcefully = NO): boolean | Promise<boolean> {\n \n return this._applyRouteAfterRouteApplicationCheck(forcefully, YES)\n \n }\n \n \n /**\n * Replaces the current history entry without consulting\n * `UIRoute.routeApplicationShouldProceed`.\n */\n applyByReplacingCurrentRouteInHistoryWithoutRouteApplicationCheck(forcefully = NO): boolean {\n \n return this._applyRouteWithoutRouteApplicationCheck(forcefully, YES)\n \n }\n \n \n _applyRouteAfterRouteApplicationCheck(\n forcefully: boolean,\n replacesCurrentRouteInHistory: boolean\n ): boolean | Promise<boolean> {\n \n if (!this._needsRouteApplication(forcefully)) {\n return NO\n }\n \n const routeApplicationShouldProceed = UIRoute.shouldApplyRouteChange\n if (!routeApplicationShouldProceed) {\n return this._applyRouteWithoutRouteApplicationCheck(forcefully, replacesCurrentRouteInHistory)\n }\n \n const shouldProceed = routeApplicationShouldProceed(\n this._routeApplication(forcefully, replacesCurrentRouteInHistory)\n )\n \n if (shouldProceed instanceof Promise) {\n return shouldProceed.then(routeApplicationShouldProceed => {\n return this._applyRouteIfAllowed(\n routeApplicationShouldProceed,\n forcefully,\n replacesCurrentRouteInHistory\n )\n })\n }\n \n return this._applyRouteIfAllowed(shouldProceed, forcefully, replacesCurrentRouteInHistory)\n \n }\n \n \n _applyRouteIfAllowed(\n routeApplicationShouldProceed: boolean,\n forcefully: boolean,\n replacesCurrentRouteInHistory: boolean\n ): boolean {\n \n if (!routeApplicationShouldProceed) {\n return NO\n }\n \n return this._applyRouteWithoutRouteApplicationCheck(forcefully, replacesCurrentRouteInHistory)\n \n }\n \n \n _applyRouteWithoutRouteApplicationCheck(\n forcefully: boolean,\n replacesCurrentRouteInHistory: boolean\n ): boolean {\n \n if (!this._needsRouteApplication(forcefully)) {\n return NO\n }\n \n if (replacesCurrentRouteInHistory) {\n window.location.replace(this.linkRepresentation)\n }\n else {\n window.location.hash = this.stringRepresentation\n }\n \n return YES\n \n }\n \n \n _needsRouteApplication(forcefully: boolean): boolean {\n \n if (!forcefully && new UIRoute(window.location.hash).stringRepresentation == this.stringRepresentation) {\n return NO\n }\n \n return YES\n \n }\n \n \n _routeApplication(forcefully: boolean, replacesCurrentRouteInHistory: boolean): UIRouteChangeDescriptor {\n \n return {\n currentRoute: UIRoute.currentRoute,\n targetRoute: this.copy(),\n forcefully: forcefully,\n replacesCurrentRouteInHistory: replacesCurrentRouteInHistory\n }\n \n }\n \n \n override copy() {\n const result = new UIRoute(this.stringRepresentation)\n return result\n }\n \n \n routeByRemovingComponentsOtherThanOnesNamed(componentNames: string[]) {\n const result = this.copy()\n const indexesToRemove: number[] = []\n result.forEach(function (component, index, array) {\n if (!componentNames.contains(component.name)) {\n indexesToRemove.push(index)\n }\n })\n indexesToRemove.forEach(function (indexToRemove, index, array) {\n result.removeElementAtIndex(indexToRemove)\n })\n return result\n }\n \n \n routeByRemovingComponentNamed(componentName: string) {\n const result = this.copy()\n const componentIndex = result.findIndex(function (component, index) {\n return (component.name == componentName)\n })\n if (componentIndex != -1) {\n result.splice(componentIndex, 1)\n }\n return result\n }\n \n \n routeByRemovingParameterInComponent(componentName: string, parameterName: string, removeComponentIfEmpty = NO) {\n let result = this.copy()\n let parameters = result.componentWithName(componentName)?.parameters ?? {}\n delete parameters[parameterName]\n result = result.routeWithComponent(componentName, parameters)\n if (removeComponentIfEmpty && Object.keys(parameters).length == 0) {\n result = result.routeByRemovingComponentNamed(componentName)\n }\n return result\n }\n \n routeBySettingParameterInComponent(componentName: string, parameterName: string, valueToSet: string) {\n let result = this.copy()\n if (IS_NIL(valueToSet) || IS_NIL(parameterName)) {\n return result\n }\n let parameters = result.componentWithName(componentName)?.parameters\n if (IS_NOT(parameters)) {\n parameters = {}\n }\n parameters[parameterName] = valueToSet\n result = result.routeWithComponent(componentName, parameters)\n return result\n }\n \n \n routeWithViewControllerComponent<T extends typeof UIViewController>(\n viewController: T,\n parameters: UIRouteParameters<{ [P in keyof T[\"ParameterIdentifierName\"]]: string }>,\n extendParameters: boolean = NO\n ) {\n \n return this.routeWithComponent(viewController.routeComponentName, parameters, extendParameters)\n \n }\n \n routeWithComponent(name: string, parameters: UIRouteParameters, extendParameters: boolean = NO) {\n \n const result = this.copy()\n let component = result.componentWithName(name)\n if (IS_NOT(component)) {\n component = {\n name: name,\n parameters: {}\n }\n result.push(component)\n }\n \n if (IS_NOT(parameters)) {\n \n parameters = {}\n \n }\n \n if (extendParameters) {\n component.parameters = Object.assign(component.parameters, parameters)\n }\n else {\n component.parameters = parameters\n }\n \n return result\n \n }\n \n navigateBySettingComponent(name: string, parameters: UIRouteParameters, extendParameters: boolean = NO) {\n \n return this.routeWithComponent(name, parameters, extendParameters).apply()\n \n }\n \n \n componentWithViewController<T extends typeof UIViewController>(viewController: T): UIRouteComponent<{ [P in ValueOf<T[\"ParameterIdentifierName\"]>]: string }> | undefined {\n \n return this.componentWithName(viewController.routeComponentName)\n \n }\n \n componentWithName(name: string): UIRouteComponent | undefined {\n let result\n this.forEach(function (component, index, self) {\n if (component.name == name) {\n result = component\n }\n })\n return result\n }\n \n \n get linkRepresentation() {\n return \"#\" + this.stringRepresentation\n }\n \n \n get stringRepresentation() {\n \n let result = \"\"\n this.forEach(function (component, index, self) {\n result = result + component.name\n const parameters = component.parameters\n result = result + \"[\"\n Object.keys(parameters).forEach(function (key, index, keys) {\n if (index) {\n result = result + \",\"\n }\n result = result + encodeURIComponent(key) + \":\" + encodeURIComponent(parameters[key])\n })\n result = result + \"]\"\n })\n \n return result\n \n }\n \n \n}\n"],
5
- "mappings": "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",
6
- "names": ["routeApplicationShouldProceed", "index"]
4
+ "sourcesContent": ["import { IS_NIL, IS_NOT, NO, ValueOf, YES } from \"./UIObject\"\nimport { UIViewController } from \"./UIViewController\"\n\n\nexport type PropType<TObj, TProp extends keyof TObj> = TObj[TProp];\n\nexport type UIRouteParameters<T = any> = {\n \n [key: string]: string;\n \n} | T;\n\n\nexport interface UIRouteComponent<T = any> {\n \n name: string;\n parameters: UIRouteParameters<T>;\n \n}\n\n\nexport interface UIRouteChangeDescriptor {\n \n currentRoute: UIRoute;\n targetRoute: UIRoute;\n forcefully: boolean;\n replacesCurrentRouteInHistory: boolean;\n \n}\n\n\nexport type UIRouteShouldApplyRouteChangeCallbackFunction = (\n routeChangeDescriptor: UIRouteChangeDescriptor\n) => boolean | Promise<boolean>;\n\n\nconst historyPositionStateKey = \"uiRouteHistoryPosition\"\n\n\nexport class UIRoute extends Array<UIRouteComponent> {\n \n static shouldApplyRouteChange?: UIRouteShouldApplyRouteChangeCallbackFunction\n \n // Native Back/Forward navigation fires `hashchange` directly, bypassing\n // `apply()` and therefore `shouldApplyRouteChange` - the fields and\n // methods below (installed via `_installNativeHistoryNavigationGuard`)\n // close that gap by tagging every history entry with a running position,\n // so a native traversal can be told apart from this class's own hash\n // mutations and, when declined, undone with `history.go(...)`.\n static _presentedRoute: UIRoute = new UIRoute()\n static _presentedHistoryPosition = 0\n static _recordedHistoryLength = 0\n static _selfInitiatedRouteChange?: UIRouteChangeDescriptor\n static _nativeHistoryNavigationGeneration = 0\n \n constructor(hash?: string) {\n \n super()\n \n if (!hash || !hash.startsWith) {\n \n return\n \n }\n \n if (hash.startsWith(\"#\")) {\n hash = hash.slice(1)\n }\n \n hash = decodeURIComponent(hash)\n \n const components = hash.split(\"]\")\n components.forEach(component => {\n \n const componentName = component.split(\"[\")[0]\n const parameters: Record<string, string> = {}\n \n if (!componentName) {\n return\n }\n \n const parametersString = component.split(\"[\")[1] || \"\"\n const parameterPairStrings = parametersString.split(\",\") || []\n \n parameterPairStrings.forEach(pairString => {\n \n const keyAndValueArray = pairString.split(\":\")\n const key = decodeURIComponent(keyAndValueArray[0])\n const value = decodeURIComponent(keyAndValueArray[1])\n \n if (key) {\n parameters[key] = value\n }\n \n })\n \n \n this.push({\n name: componentName,\n parameters: parameters\n })\n \n })\n \n \n }\n \n \n static get currentRoute() {\n\n return new UIRoute(window.location.hash)\n\n }\n\n\n /**\n * @param forcefully When `true`, applies the route even if its string\n * representation is identical to the current URL hash, causing the browser\n * to fire a `hashchange` event and re-run the full `handleRoute` flow.\n * Use this only when you explicitly need re-entrant navigation (e.g. the\n * \"tap the already-active tab to reset to root\" feature in TopBarView).\n * Defaults to `false`.\n */\n apply(forcefully = NO): boolean | Promise<boolean> {\n \n return this._applyRouteAfterRouteChangeApproval(forcefully, NO)\n \n }\n \n \n /**\n * Applies this route without consulting `UIRoute.shouldApplyRouteChange`.\n * Use this only for route changes that are internal bookkeeping or that already\n * passed a higher-level confirmation flow.\n */\n applyWithoutRouteChangeApproval(forcefully = NO): boolean {\n \n return this._applyRouteWithoutRouteChangeApproval(forcefully, NO)\n \n }\n \n \n /**\n * @param forcefully When `true`, replaces the current history entry with\n * this route even when the route is identical to the current URL hash.\n * Defaults to `false`.\n */\n applyByReplacingCurrentRouteInHistory(forcefully = NO): boolean | Promise<boolean> {\n \n return this._applyRouteAfterRouteChangeApproval(forcefully, YES)\n \n }\n \n \n /**\n * Replaces the current history entry without consulting\n * `UIRoute.shouldApplyRouteChange`.\n */\n applyByReplacingCurrentRouteInHistoryWithoutRouteChangeApproval(forcefully = NO): boolean {\n \n return this._applyRouteWithoutRouteChangeApproval(forcefully, YES)\n \n }\n \n \n _applyRouteAfterRouteChangeApproval(\n forcefully: boolean,\n replacesCurrentRouteInHistory: boolean\n ): boolean | Promise<boolean> {\n \n if (!this._needsRouteChange(forcefully)) {\n return NO\n }\n \n const shouldApplyRouteChange = UIRoute.shouldApplyRouteChange\n if (!shouldApplyRouteChange) {\n return this._applyRouteWithoutRouteChangeApproval(forcefully, replacesCurrentRouteInHistory)\n }\n \n const shouldProceed = shouldApplyRouteChange(\n this._routeChangeDescriptor(forcefully, replacesCurrentRouteInHistory)\n )\n \n if (shouldProceed instanceof Promise) {\n return shouldProceed.then(isRouteChangeApproved => {\n return this._applyRouteIfAllowed(\n isRouteChangeApproved,\n forcefully,\n replacesCurrentRouteInHistory\n )\n })\n }\n \n return this._applyRouteIfAllowed(shouldProceed, forcefully, replacesCurrentRouteInHistory)\n \n }\n \n \n _applyRouteIfAllowed(\n isRouteChangeApproved: boolean,\n forcefully: boolean,\n replacesCurrentRouteInHistory: boolean\n ): boolean {\n \n if (!isRouteChangeApproved) {\n return NO\n }\n \n return this._applyRouteWithoutRouteChangeApproval(forcefully, replacesCurrentRouteInHistory)\n \n }\n \n \n _applyRouteWithoutRouteChangeApproval(\n forcefully: boolean,\n replacesCurrentRouteInHistory: boolean\n ): boolean {\n \n if (!this._needsRouteChange(forcefully)) {\n return NO\n }\n\n // Tags the hashchange this is about to cause as this class's own\n // doing, so `_hashDidChange` can tell it apart from a native\n // Back/Forward traversal instead of falling back to guessing from\n // `window.history.length`.\n UIRoute._selfInitiatedRouteChange = this._routeChangeDescriptor(forcefully, replacesCurrentRouteInHistory)\n\n if (replacesCurrentRouteInHistory) {\n window.location.replace(this.linkRepresentation)\n }\n else {\n window.location.hash = this.stringRepresentation\n }\n\n return YES\n\n }\n \n \n _needsRouteChange(forcefully: boolean): boolean {\n \n if (!forcefully && new UIRoute(window.location.hash).stringRepresentation == this.stringRepresentation) {\n return NO\n }\n \n return YES\n \n }\n \n \n _routeChangeDescriptor(forcefully: boolean, replacesCurrentRouteInHistory: boolean): UIRouteChangeDescriptor {\n \n return {\n currentRoute: UIRoute.currentRoute,\n targetRoute: this.copy(),\n forcefully: forcefully,\n replacesCurrentRouteInHistory: replacesCurrentRouteInHistory\n }\n\n }\n\n\n /**\n * Installs handling so that native browser Back/Forward navigation\n * consults `shouldApplyRouteChange` the same way `apply()` does, instead\n * of silently bypassing it. Call exactly once, from `UICore`'s\n * constructor.\n *\n * @param reapplyCurrentRoute Re-runs route handling for `UIRoute.currentRoute`.\n * Called once a native navigation is allowed to proceed (or is self-initiated),\n * so its effect actually appears on screen.\n */\n static _installNativeHistoryNavigationGuard(reapplyCurrentRoute: () => void | Promise<void>) {\n\n UIRoute._recordPresentedRouteAtHistoryPosition(\n UIRoute.currentRoute,\n UIRoute._historyPositionFromCurrentEntry() ?? 0\n )\n\n window.addEventListener(\"hashchange\", event =>\n UIRoute._hashDidChange(event, reapplyCurrentRoute)\n )\n\n }\n\n\n static _historyPositionFromCurrentEntry(): number | undefined {\n\n const historyPosition = (window.history.state as Record<string, unknown> | null)?.[historyPositionStateKey]\n if (typeof historyPosition !== \"number\") {\n return undefined\n }\n return historyPosition\n\n }\n\n\n static _recordPresentedRouteAtHistoryPosition(route: UIRoute, historyPosition: number) {\n\n const historyState = Object.assign({}, window.history.state)\n historyState[historyPositionStateKey] = historyPosition\n window.history.replaceState(historyState, \"\")\n\n UIRoute._presentedRoute = route.copy()\n UIRoute._presentedHistoryPosition = historyPosition\n UIRoute._recordedHistoryLength = window.history.length\n\n }\n\n\n /**\n * The single listener installed for every `hashchange` - self-initiated,\n * native Back/Forward, or an untagged bypass (see the class-level comment\n * above `_presentedRoute`) - and the point where those three cases are\n * told apart.\n *\n * @param event The native event. Only used to call\n * `stopImmediatePropagation()` when a native traversal needs to be held\n * back pending `shouldApplyRouteChange`, so `UICore`'s own route handling\n * doesn't run before that check settles.\n * @param reapplyCurrentRoute Forwarded to `_applyHistoryNavigationToRouteAtPositionIfAllowed`\n * - see its own doc comment.\n */\n static _hashDidChange(event: HashChangeEvent, reapplyCurrentRoute: () => void | Promise<void>) {\n\n const targetRoute = UIRoute.currentRoute\n const selfInitiatedRouteChange = UIRoute._selfInitiatedRouteChange\n UIRoute._selfInitiatedRouteChange = undefined\n\n if (\n selfInitiatedRouteChange &&\n selfInitiatedRouteChange.targetRoute.stringRepresentation === targetRoute.stringRepresentation\n ) {\n\n let targetHistoryPosition = UIRoute._presentedHistoryPosition\n if (!selfInitiatedRouteChange.replacesCurrentRouteInHistory) {\n targetHistoryPosition = targetHistoryPosition + 1\n }\n UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, targetHistoryPosition)\n return\n\n }\n\n const targetHistoryPosition = UIRoute._historyPositionFromCurrentEntry()\n if (targetHistoryPosition === undefined) {\n\n // A hash mutation that bypassed this class entirely (e.g. a\n // direct `window.location.hash = ...` assignment) produces an\n // untagged entry. A larger native history length means it was\n // pushed; an unchanged length means the current entry was\n // replaced. Preserve both established escape hatches while\n // bringing the entry into this sequence.\n let untaggedHistoryPosition = UIRoute._presentedHistoryPosition\n if (window.history.length > UIRoute._recordedHistoryLength) {\n untaggedHistoryPosition = untaggedHistoryPosition + 1\n }\n UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, untaggedHistoryPosition)\n return\n\n }\n if (targetHistoryPosition === UIRoute._presentedHistoryPosition) {\n UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, targetHistoryPosition)\n return\n }\n\n event.stopImmediatePropagation()\n const navigationGeneration = ++UIRoute._nativeHistoryNavigationGeneration\n UIRoute._applyHistoryNavigationToRouteAtPositionIfAllowed(\n targetRoute,\n targetHistoryPosition,\n navigationGeneration,\n reapplyCurrentRoute\n ).catch(error => {\n console.error(\"UIRoute: native history navigation check failed\", error)\n })\n\n }\n\n\n /**\n * Consults `shouldApplyRouteChange` for a native Back/Forward traversal,\n * exactly like `apply()` does for a programmatic one, then either lets it\n * through or reverts it with `history.go(...)`.\n *\n * @param targetRoute The route the native traversal is asking to move to.\n * @param targetHistoryPosition That route's tagged position, used both to\n * revert (`history.go`) and to detect a newer navigation arriving mid-check.\n * @param navigationGeneration A token captured by the caller at dispatch\n * time. If it no longer matches `_nativeHistoryNavigationGeneration` by\n * the time `shouldApplyRouteChange` resolves, a newer navigation has\n * superseded this one and this check's result is discarded.\n * @param reapplyCurrentRoute Called once the navigation is approved, so\n * `UIRoute.currentRoute`'s new value actually takes effect on screen.\n */\n static async _applyHistoryNavigationToRouteAtPositionIfAllowed(\n targetRoute: UIRoute,\n targetHistoryPosition: number,\n navigationGeneration: number,\n reapplyCurrentRoute: () => void | Promise<void>\n ) {\n\n const shouldApplyRouteChange = UIRoute.shouldApplyRouteChange\n const isRouteChangeApproved = shouldApplyRouteChange\n ? await shouldApplyRouteChange({\n currentRoute: UIRoute._presentedRoute.copy(),\n targetRoute: targetRoute.copy(),\n forcefully: NO,\n replacesCurrentRouteInHistory: NO\n })\n : YES\n\n if (navigationGeneration !== UIRoute._nativeHistoryNavigationGeneration) {\n return\n }\n if (\n UIRoute.currentRoute.stringRepresentation !== targetRoute.stringRepresentation ||\n UIRoute._historyPositionFromCurrentEntry() !== targetHistoryPosition\n ) {\n return\n }\n\n if (isRouteChangeApproved) {\n UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, targetHistoryPosition)\n await reapplyCurrentRoute()\n return\n }\n\n const historyDelta = UIRoute._presentedHistoryPosition - targetHistoryPosition\n if (historyDelta !== 0) {\n window.history.go(historyDelta)\n }\n\n }\n\n\n override copy() {\n const result = new UIRoute(this.stringRepresentation)\n return result\n }\n \n \n routeByRemovingComponentsOtherThanOnesNamed(componentNames: string[]) {\n const result = this.copy()\n const indexesToRemove: number[] = []\n result.forEach(function (component, index, array) {\n if (!componentNames.contains(component.name)) {\n indexesToRemove.push(index)\n }\n })\n indexesToRemove.forEach(function (indexToRemove, index, array) {\n result.removeElementAtIndex(indexToRemove)\n })\n return result\n }\n \n \n routeByRemovingComponentNamed(componentName: string) {\n const result = this.copy()\n const componentIndex = result.findIndex(function (component, index) {\n return (component.name == componentName)\n })\n if (componentIndex != -1) {\n result.splice(componentIndex, 1)\n }\n return result\n }\n \n \n routeByRemovingParameterInComponent(componentName: string, parameterName: string, removeComponentIfEmpty = NO) {\n let result = this.copy()\n let parameters = result.componentWithName(componentName)?.parameters ?? {}\n delete parameters[parameterName]\n result = result.routeWithComponent(componentName, parameters)\n if (removeComponentIfEmpty && Object.keys(parameters).length == 0) {\n result = result.routeByRemovingComponentNamed(componentName)\n }\n return result\n }\n \n routeBySettingParameterInComponent(componentName: string, parameterName: string, valueToSet: string) {\n let result = this.copy()\n if (IS_NIL(valueToSet) || IS_NIL(parameterName)) {\n return result\n }\n let parameters = result.componentWithName(componentName)?.parameters\n if (IS_NOT(parameters)) {\n parameters = {}\n }\n parameters[parameterName] = valueToSet\n result = result.routeWithComponent(componentName, parameters)\n return result\n }\n \n \n routeWithViewControllerComponent<T extends typeof UIViewController>(\n viewController: T,\n parameters: UIRouteParameters<{ [P in keyof T[\"ParameterIdentifierName\"]]: string }>,\n extendParameters: boolean = NO\n ) {\n \n return this.routeWithComponent(viewController.routeComponentName, parameters, extendParameters)\n \n }\n \n routeWithComponent(name: string, parameters: UIRouteParameters, extendParameters: boolean = NO) {\n \n const result = this.copy()\n let component = result.componentWithName(name)\n if (IS_NOT(component)) {\n component = {\n name: name,\n parameters: {}\n }\n result.push(component)\n }\n \n if (IS_NOT(parameters)) {\n \n parameters = {}\n \n }\n \n if (extendParameters) {\n component.parameters = Object.assign(component.parameters, parameters)\n }\n else {\n component.parameters = parameters\n }\n \n return result\n \n }\n \n navigateBySettingComponent(name: string, parameters: UIRouteParameters, extendParameters: boolean = NO) {\n \n return this.routeWithComponent(name, parameters, extendParameters).apply()\n \n }\n \n \n componentWithViewController<T extends typeof UIViewController>(viewController: T): UIRouteComponent<{ [P in ValueOf<T[\"ParameterIdentifierName\"]>]: string }> | undefined {\n \n return this.componentWithName(viewController.routeComponentName)\n \n }\n \n componentWithName(name: string): UIRouteComponent | undefined {\n let result\n this.forEach(function (component, index, self) {\n if (component.name == name) {\n result = component\n }\n })\n return result\n }\n \n \n get linkRepresentation() {\n return \"#\" + this.stringRepresentation\n }\n \n \n get stringRepresentation() {\n \n let result = \"\"\n this.forEach(function (component, index, self) {\n result = result + component.name\n const parameters = component.parameters\n result = result + \"[\"\n Object.keys(parameters).forEach(function (key, index, keys) {\n if (index) {\n result = result + \",\"\n }\n result = result + encodeURIComponent(key) + \":\" + encodeURIComponent(parameters[key])\n })\n result = result + \"]\"\n })\n \n return result\n \n }\n \n \n}\n"],
5
+ "mappings": "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",
6
+ "names": ["targetHistoryPosition", "index"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uicore-ts",
3
- "version": "1.1.385",
3
+ "version": "1.1.387",
4
4
  "description": "UICore is a library to build native-like user interfaces using pure Typescript. No HTML is needed at all. Components are described as TS classes and all user interactions are handled explicitly. This library is strongly inspired by the UIKit framework that is used in IOS. In addition, UICore has tools to handle URL based routing, array sorting and filtering and adds a number of other utilities for convenience.",
5
5
  "main": "compiledScripts/index.js",
6
6
  "types": "compiledScripts/index.d.ts",
package/scripts/UICore.ts CHANGED
@@ -139,17 +139,14 @@ export class UICore extends UIObject {
139
139
 
140
140
  window.addEventListener("scroll", didScroll, false)
141
141
 
142
- const hashDidChange = () => {
143
-
144
- //code
145
-
146
- this.reapplyCurrentRoute()
147
-
148
- }
149
-
150
- window.addEventListener("hashchange", hashDidChange, false)
151
-
152
- hashDidChange()
142
+ // Native Back/Forward navigation fires `hashchange` directly, which
143
+ // would otherwise reapply the route unconditionally below with no
144
+ // chance for the app to veto it (e.g. to protect unsaved changes) the
145
+ // way `UIRoute.apply()` can. This makes native history traversal go
146
+ // through the same `UIRoute.shouldApplyRouteChange` check first.
147
+ UIRoute._installNativeHistoryNavigationGuard(() => this.reapplyCurrentRoute())
148
+
149
+ this.reapplyCurrentRoute()
153
150
 
154
151
 
155
152
  }
@@ -34,10 +34,25 @@ export type UIRouteShouldApplyRouteChangeCallbackFunction = (
34
34
  ) => boolean | Promise<boolean>;
35
35
 
36
36
 
37
+ const historyPositionStateKey = "uiRouteHistoryPosition"
38
+
39
+
37
40
  export class UIRoute extends Array<UIRouteComponent> {
38
41
 
39
42
  static shouldApplyRouteChange?: UIRouteShouldApplyRouteChangeCallbackFunction
40
43
 
44
+ // Native Back/Forward navigation fires `hashchange` directly, bypassing
45
+ // `apply()` and therefore `shouldApplyRouteChange` - the fields and
46
+ // methods below (installed via `_installNativeHistoryNavigationGuard`)
47
+ // close that gap by tagging every history entry with a running position,
48
+ // so a native traversal can be told apart from this class's own hash
49
+ // mutations and, when declined, undone with `history.go(...)`.
50
+ static _presentedRoute: UIRoute = new UIRoute()
51
+ static _presentedHistoryPosition = 0
52
+ static _recordedHistoryLength = 0
53
+ static _selfInitiatedRouteChange?: UIRouteChangeDescriptor
54
+ static _nativeHistoryNavigationGeneration = 0
55
+
41
56
  constructor(hash?: string) {
42
57
 
43
58
  super()
@@ -92,12 +107,12 @@ export class UIRoute extends Array<UIRouteComponent> {
92
107
 
93
108
 
94
109
  static get currentRoute() {
95
-
110
+
96
111
  return new UIRoute(window.location.hash)
97
-
112
+
98
113
  }
99
-
100
-
114
+
115
+
101
116
  /**
102
117
  * @param forcefully When `true`, applies the route even if its string
103
118
  * representation is identical to the current URL hash, causing the browser
@@ -108,19 +123,19 @@ export class UIRoute extends Array<UIRouteComponent> {
108
123
  */
109
124
  apply(forcefully = NO): boolean | Promise<boolean> {
110
125
 
111
- return this._applyRouteAfterRouteApplicationCheck(forcefully, NO)
126
+ return this._applyRouteAfterRouteChangeApproval(forcefully, NO)
112
127
 
113
128
  }
114
129
 
115
130
 
116
131
  /**
117
- * Applies this route without consulting `UIRoute.routeApplicationShouldProceed`.
132
+ * Applies this route without consulting `UIRoute.shouldApplyRouteChange`.
118
133
  * Use this only for route changes that are internal bookkeeping or that already
119
134
  * passed a higher-level confirmation flow.
120
135
  */
121
- applyWithoutRouteApplicationCheck(forcefully = NO): boolean {
136
+ applyWithoutRouteChangeApproval(forcefully = NO): boolean {
122
137
 
123
- return this._applyRouteWithoutRouteApplicationCheck(forcefully, NO)
138
+ return this._applyRouteWithoutRouteChangeApproval(forcefully, NO)
124
139
 
125
140
  }
126
141
 
@@ -132,44 +147,44 @@ export class UIRoute extends Array<UIRouteComponent> {
132
147
  */
133
148
  applyByReplacingCurrentRouteInHistory(forcefully = NO): boolean | Promise<boolean> {
134
149
 
135
- return this._applyRouteAfterRouteApplicationCheck(forcefully, YES)
150
+ return this._applyRouteAfterRouteChangeApproval(forcefully, YES)
136
151
 
137
152
  }
138
153
 
139
154
 
140
155
  /**
141
156
  * Replaces the current history entry without consulting
142
- * `UIRoute.routeApplicationShouldProceed`.
157
+ * `UIRoute.shouldApplyRouteChange`.
143
158
  */
144
- applyByReplacingCurrentRouteInHistoryWithoutRouteApplicationCheck(forcefully = NO): boolean {
159
+ applyByReplacingCurrentRouteInHistoryWithoutRouteChangeApproval(forcefully = NO): boolean {
145
160
 
146
- return this._applyRouteWithoutRouteApplicationCheck(forcefully, YES)
161
+ return this._applyRouteWithoutRouteChangeApproval(forcefully, YES)
147
162
 
148
163
  }
149
164
 
150
165
 
151
- _applyRouteAfterRouteApplicationCheck(
166
+ _applyRouteAfterRouteChangeApproval(
152
167
  forcefully: boolean,
153
168
  replacesCurrentRouteInHistory: boolean
154
169
  ): boolean | Promise<boolean> {
155
170
 
156
- if (!this._needsRouteApplication(forcefully)) {
171
+ if (!this._needsRouteChange(forcefully)) {
157
172
  return NO
158
173
  }
159
174
 
160
- const routeApplicationShouldProceed = UIRoute.shouldApplyRouteChange
161
- if (!routeApplicationShouldProceed) {
162
- return this._applyRouteWithoutRouteApplicationCheck(forcefully, replacesCurrentRouteInHistory)
175
+ const shouldApplyRouteChange = UIRoute.shouldApplyRouteChange
176
+ if (!shouldApplyRouteChange) {
177
+ return this._applyRouteWithoutRouteChangeApproval(forcefully, replacesCurrentRouteInHistory)
163
178
  }
164
179
 
165
- const shouldProceed = routeApplicationShouldProceed(
166
- this._routeApplication(forcefully, replacesCurrentRouteInHistory)
180
+ const shouldProceed = shouldApplyRouteChange(
181
+ this._routeChangeDescriptor(forcefully, replacesCurrentRouteInHistory)
167
182
  )
168
183
 
169
184
  if (shouldProceed instanceof Promise) {
170
- return shouldProceed.then(routeApplicationShouldProceed => {
185
+ return shouldProceed.then(isRouteChangeApproved => {
171
186
  return this._applyRouteIfAllowed(
172
- routeApplicationShouldProceed,
187
+ isRouteChangeApproved,
173
188
  forcefully,
174
189
  replacesCurrentRouteInHistory
175
190
  )
@@ -182,42 +197,48 @@ export class UIRoute extends Array<UIRouteComponent> {
182
197
 
183
198
 
184
199
  _applyRouteIfAllowed(
185
- routeApplicationShouldProceed: boolean,
200
+ isRouteChangeApproved: boolean,
186
201
  forcefully: boolean,
187
202
  replacesCurrentRouteInHistory: boolean
188
203
  ): boolean {
189
204
 
190
- if (!routeApplicationShouldProceed) {
205
+ if (!isRouteChangeApproved) {
191
206
  return NO
192
207
  }
193
208
 
194
- return this._applyRouteWithoutRouteApplicationCheck(forcefully, replacesCurrentRouteInHistory)
209
+ return this._applyRouteWithoutRouteChangeApproval(forcefully, replacesCurrentRouteInHistory)
195
210
 
196
211
  }
197
212
 
198
213
 
199
- _applyRouteWithoutRouteApplicationCheck(
214
+ _applyRouteWithoutRouteChangeApproval(
200
215
  forcefully: boolean,
201
216
  replacesCurrentRouteInHistory: boolean
202
217
  ): boolean {
203
218
 
204
- if (!this._needsRouteApplication(forcefully)) {
219
+ if (!this._needsRouteChange(forcefully)) {
205
220
  return NO
206
221
  }
207
-
222
+
223
+ // Tags the hashchange this is about to cause as this class's own
224
+ // doing, so `_hashDidChange` can tell it apart from a native
225
+ // Back/Forward traversal instead of falling back to guessing from
226
+ // `window.history.length`.
227
+ UIRoute._selfInitiatedRouteChange = this._routeChangeDescriptor(forcefully, replacesCurrentRouteInHistory)
228
+
208
229
  if (replacesCurrentRouteInHistory) {
209
230
  window.location.replace(this.linkRepresentation)
210
231
  }
211
232
  else {
212
233
  window.location.hash = this.stringRepresentation
213
234
  }
214
-
235
+
215
236
  return YES
216
-
237
+
217
238
  }
218
239
 
219
240
 
220
- _needsRouteApplication(forcefully: boolean): boolean {
241
+ _needsRouteChange(forcefully: boolean): boolean {
221
242
 
222
243
  if (!forcefully && new UIRoute(window.location.hash).stringRepresentation == this.stringRepresentation) {
223
244
  return NO
@@ -228,7 +249,7 @@ export class UIRoute extends Array<UIRouteComponent> {
228
249
  }
229
250
 
230
251
 
231
- _routeApplication(forcefully: boolean, replacesCurrentRouteInHistory: boolean): UIRouteChangeDescriptor {
252
+ _routeChangeDescriptor(forcefully: boolean, replacesCurrentRouteInHistory: boolean): UIRouteChangeDescriptor {
232
253
 
233
254
  return {
234
255
  currentRoute: UIRoute.currentRoute,
@@ -236,10 +257,183 @@ export class UIRoute extends Array<UIRouteComponent> {
236
257
  forcefully: forcefully,
237
258
  replacesCurrentRouteInHistory: replacesCurrentRouteInHistory
238
259
  }
239
-
260
+
240
261
  }
241
-
242
-
262
+
263
+
264
+ /**
265
+ * Installs handling so that native browser Back/Forward navigation
266
+ * consults `shouldApplyRouteChange` the same way `apply()` does, instead
267
+ * of silently bypassing it. Call exactly once, from `UICore`'s
268
+ * constructor.
269
+ *
270
+ * @param reapplyCurrentRoute Re-runs route handling for `UIRoute.currentRoute`.
271
+ * Called once a native navigation is allowed to proceed (or is self-initiated),
272
+ * so its effect actually appears on screen.
273
+ */
274
+ static _installNativeHistoryNavigationGuard(reapplyCurrentRoute: () => void | Promise<void>) {
275
+
276
+ UIRoute._recordPresentedRouteAtHistoryPosition(
277
+ UIRoute.currentRoute,
278
+ UIRoute._historyPositionFromCurrentEntry() ?? 0
279
+ )
280
+
281
+ window.addEventListener("hashchange", event =>
282
+ UIRoute._hashDidChange(event, reapplyCurrentRoute)
283
+ )
284
+
285
+ }
286
+
287
+
288
+ static _historyPositionFromCurrentEntry(): number | undefined {
289
+
290
+ const historyPosition = (window.history.state as Record<string, unknown> | null)?.[historyPositionStateKey]
291
+ if (typeof historyPosition !== "number") {
292
+ return undefined
293
+ }
294
+ return historyPosition
295
+
296
+ }
297
+
298
+
299
+ static _recordPresentedRouteAtHistoryPosition(route: UIRoute, historyPosition: number) {
300
+
301
+ const historyState = Object.assign({}, window.history.state)
302
+ historyState[historyPositionStateKey] = historyPosition
303
+ window.history.replaceState(historyState, "")
304
+
305
+ UIRoute._presentedRoute = route.copy()
306
+ UIRoute._presentedHistoryPosition = historyPosition
307
+ UIRoute._recordedHistoryLength = window.history.length
308
+
309
+ }
310
+
311
+
312
+ /**
313
+ * The single listener installed for every `hashchange` - self-initiated,
314
+ * native Back/Forward, or an untagged bypass (see the class-level comment
315
+ * above `_presentedRoute`) - and the point where those three cases are
316
+ * told apart.
317
+ *
318
+ * @param event The native event. Only used to call
319
+ * `stopImmediatePropagation()` when a native traversal needs to be held
320
+ * back pending `shouldApplyRouteChange`, so `UICore`'s own route handling
321
+ * doesn't run before that check settles.
322
+ * @param reapplyCurrentRoute Forwarded to `_applyHistoryNavigationToRouteAtPositionIfAllowed`
323
+ * - see its own doc comment.
324
+ */
325
+ static _hashDidChange(event: HashChangeEvent, reapplyCurrentRoute: () => void | Promise<void>) {
326
+
327
+ const targetRoute = UIRoute.currentRoute
328
+ const selfInitiatedRouteChange = UIRoute._selfInitiatedRouteChange
329
+ UIRoute._selfInitiatedRouteChange = undefined
330
+
331
+ if (
332
+ selfInitiatedRouteChange &&
333
+ selfInitiatedRouteChange.targetRoute.stringRepresentation === targetRoute.stringRepresentation
334
+ ) {
335
+
336
+ let targetHistoryPosition = UIRoute._presentedHistoryPosition
337
+ if (!selfInitiatedRouteChange.replacesCurrentRouteInHistory) {
338
+ targetHistoryPosition = targetHistoryPosition + 1
339
+ }
340
+ UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, targetHistoryPosition)
341
+ return
342
+
343
+ }
344
+
345
+ const targetHistoryPosition = UIRoute._historyPositionFromCurrentEntry()
346
+ if (targetHistoryPosition === undefined) {
347
+
348
+ // A hash mutation that bypassed this class entirely (e.g. a
349
+ // direct `window.location.hash = ...` assignment) produces an
350
+ // untagged entry. A larger native history length means it was
351
+ // pushed; an unchanged length means the current entry was
352
+ // replaced. Preserve both established escape hatches while
353
+ // bringing the entry into this sequence.
354
+ let untaggedHistoryPosition = UIRoute._presentedHistoryPosition
355
+ if (window.history.length > UIRoute._recordedHistoryLength) {
356
+ untaggedHistoryPosition = untaggedHistoryPosition + 1
357
+ }
358
+ UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, untaggedHistoryPosition)
359
+ return
360
+
361
+ }
362
+ if (targetHistoryPosition === UIRoute._presentedHistoryPosition) {
363
+ UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, targetHistoryPosition)
364
+ return
365
+ }
366
+
367
+ event.stopImmediatePropagation()
368
+ const navigationGeneration = ++UIRoute._nativeHistoryNavigationGeneration
369
+ UIRoute._applyHistoryNavigationToRouteAtPositionIfAllowed(
370
+ targetRoute,
371
+ targetHistoryPosition,
372
+ navigationGeneration,
373
+ reapplyCurrentRoute
374
+ ).catch(error => {
375
+ console.error("UIRoute: native history navigation check failed", error)
376
+ })
377
+
378
+ }
379
+
380
+
381
+ /**
382
+ * Consults `shouldApplyRouteChange` for a native Back/Forward traversal,
383
+ * exactly like `apply()` does for a programmatic one, then either lets it
384
+ * through or reverts it with `history.go(...)`.
385
+ *
386
+ * @param targetRoute The route the native traversal is asking to move to.
387
+ * @param targetHistoryPosition That route's tagged position, used both to
388
+ * revert (`history.go`) and to detect a newer navigation arriving mid-check.
389
+ * @param navigationGeneration A token captured by the caller at dispatch
390
+ * time. If it no longer matches `_nativeHistoryNavigationGeneration` by
391
+ * the time `shouldApplyRouteChange` resolves, a newer navigation has
392
+ * superseded this one and this check's result is discarded.
393
+ * @param reapplyCurrentRoute Called once the navigation is approved, so
394
+ * `UIRoute.currentRoute`'s new value actually takes effect on screen.
395
+ */
396
+ static async _applyHistoryNavigationToRouteAtPositionIfAllowed(
397
+ targetRoute: UIRoute,
398
+ targetHistoryPosition: number,
399
+ navigationGeneration: number,
400
+ reapplyCurrentRoute: () => void | Promise<void>
401
+ ) {
402
+
403
+ const shouldApplyRouteChange = UIRoute.shouldApplyRouteChange
404
+ const isRouteChangeApproved = shouldApplyRouteChange
405
+ ? await shouldApplyRouteChange({
406
+ currentRoute: UIRoute._presentedRoute.copy(),
407
+ targetRoute: targetRoute.copy(),
408
+ forcefully: NO,
409
+ replacesCurrentRouteInHistory: NO
410
+ })
411
+ : YES
412
+
413
+ if (navigationGeneration !== UIRoute._nativeHistoryNavigationGeneration) {
414
+ return
415
+ }
416
+ if (
417
+ UIRoute.currentRoute.stringRepresentation !== targetRoute.stringRepresentation ||
418
+ UIRoute._historyPositionFromCurrentEntry() !== targetHistoryPosition
419
+ ) {
420
+ return
421
+ }
422
+
423
+ if (isRouteChangeApproved) {
424
+ UIRoute._recordPresentedRouteAtHistoryPosition(targetRoute, targetHistoryPosition)
425
+ await reapplyCurrentRoute()
426
+ return
427
+ }
428
+
429
+ const historyDelta = UIRoute._presentedHistoryPosition - targetHistoryPosition
430
+ if (historyDelta !== 0) {
431
+ window.history.go(historyDelta)
432
+ }
433
+
434
+ }
435
+
436
+
243
437
  override copy() {
244
438
  const result = new UIRoute(this.stringRepresentation)
245
439
  return result