white-label-router 1.1.2 → 5.0.0

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.
package/dist/index.js CHANGED
@@ -1,351 +1,254 @@
1
- (function (global, factory) {
2
- if (typeof define === "function" && define.amd) {
3
- define(['module', 'gator'], factory);
4
- } else if (typeof exports !== "undefined") {
5
- factory(module, require('gator'));
6
- } else {
7
- var mod = {
8
- exports: {}
1
+ "use strict";
2
+ /** History API router that also dispatches request URLs in non-browser runtimes. */
3
+ class Router {
4
+ routes;
5
+ url;
6
+ route;
7
+ scope;
8
+ previousRoute;
9
+ pageTitle;
10
+ mediator;
11
+ boundPushStateClick;
12
+ boundPopState;
13
+ boundMediatorNavigate;
14
+ listenersInitialized;
15
+ locationData = { url: '', data: { url: [], mediator: undefined, query: {} } };
16
+ /** Create an instance with its own state and listener references. */
17
+ constructor() {
18
+ this.routes = {
19
+ defaultRoute: {
20
+ view: {
21
+ initialize: () => true,
22
+ destroy: () => true
23
+ }
24
+ }
9
25
  };
10
- factory(mod, global.gator);
11
- global.index = mod.exports;
12
- }
13
- })(this, function (module, _gator) {
14
- 'use strict';
15
-
16
- var _gator2 = _interopRequireDefault(_gator);
17
-
18
- function _interopRequireDefault(obj) {
19
- return obj && obj.__esModule ? obj : {
20
- default: obj
26
+ this.url = '';
27
+ this.route = null;
28
+ this.scope = null;
29
+ this.previousRoute = null;
30
+ this.pageTitle = null;
31
+ this.mediator = false;
32
+ this.boundPushStateClick = this.eventPushStateClick.bind(this);
33
+ this.boundPopState = this.eventPopState.bind(this);
34
+ this.boundMediatorNavigate = (data = {}) => {
35
+ this.navigate(data.url, data, false);
21
36
  };
37
+ this.listenersInitialized = false;
38
+ }
39
+ /** True when browser navigation APIs are available. */
40
+ isBrowserRuntime() {
41
+ return typeof window !== 'undefined' && typeof document !== 'undefined';
22
42
  }
23
-
24
- var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
25
- return typeof obj;
26
- } : function (obj) {
27
- return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;
28
- };
29
-
30
- function _classCallCheck(instance, Constructor) {
31
- if (!(instance instanceof Constructor)) {
32
- throw new TypeError("Cannot call a class as a function");
43
+ /** Stable base URL for WHATWG URL parsing in browser and server runtimes. */
44
+ getOrigin() {
45
+ return this.isBrowserRuntime() ? window.location.origin : 'http://white-label.invalid';
46
+ }
47
+ /** Read the authoritative browser URL, or retain the supplied server URL. */
48
+ getCurrentUrl() {
49
+ if (!this.isBrowserRuntime()) {
50
+ return this.url || '/';
33
51
  }
52
+ return window.location.pathname + window.location.search + (window.location.hash || '');
34
53
  }
35
-
36
- var _createClass = function () {
37
- function defineProperties(target, props) {
38
- for (var i = 0; i < props.length; i++) {
39
- var descriptor = props[i];
40
- descriptor.enumerable = descriptor.enumerable || false;
41
- descriptor.configurable = true;
42
- if ("value" in descriptor) descriptor.writable = true;
43
- Object.defineProperty(target, descriptor.key, descriptor);
44
- }
54
+ /** Normalize browser navigation to a same-origin path, query, and hash. */
55
+ normalizeBrowserUrl(url) {
56
+ if (!this.isBrowserRuntime()) {
57
+ return url;
58
+ }
59
+ const parsedUrl = new URL(url || '/', window.location.href || `${window.location.origin}/`);
60
+ if (parsedUrl.origin !== window.location.origin) {
61
+ return null;
62
+ }
63
+ return `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;
64
+ }
65
+ /**
66
+ * Start this instance and return it for lifecycle chaining.
67
+ * In a browser the URL is optional and defaults to window.location. On a server pass the request URL.
68
+ */
69
+ initialize(url) {
70
+ this.url = url ?? this.getCurrentUrl();
71
+ this.addListeners();
72
+ this.navigate(undefined, {}, true);
73
+ return this;
74
+ }
75
+ /** Register browser and optional mediator handlers once. */
76
+ addListeners() {
77
+ if (this.listenersInitialized) {
78
+ return this;
79
+ }
80
+ if (this.isBrowserRuntime()) {
81
+ document.addEventListener('click', this.boundPushStateClick);
82
+ window.addEventListener('popstate', this.boundPopState);
83
+ }
84
+ if (this.mediator) {
85
+ this.mediator.on('router:navigate', this.boundMediatorNavigate);
86
+ }
87
+ this.listenersInitialized = true;
88
+ return this;
89
+ }
90
+ /** Release owned state and listeners so the instance can leave the application lifecycle. */
91
+ destroy() {
92
+ this.removeListeners();
93
+ return this;
94
+ }
95
+ /** Release listeners owned by this instance; subclasses may extend the lifecycle hook. */
96
+ removeListeners() {
97
+ if (!this.listenersInitialized) {
98
+ return this;
45
99
  }
46
-
47
- return function (Constructor, protoProps, staticProps) {
48
- if (protoProps) defineProperties(Constructor.prototype, protoProps);
49
- if (staticProps) defineProperties(Constructor, staticProps);
50
- return Constructor;
100
+ if (this.isBrowserRuntime()) {
101
+ document.removeEventListener('click', this.boundPushStateClick);
102
+ window.removeEventListener('popstate', this.boundPopState);
103
+ }
104
+ if (this.mediator) {
105
+ this.mediator.removeListener('router:navigate', this.boundMediatorNavigate);
106
+ }
107
+ this.listenersInitialized = false;
108
+ return this;
109
+ }
110
+ /** Intercept eligible same-origin push-state links while preserving normal browser actions. */
111
+ eventPushStateClick(e) {
112
+ if (typeof window === 'undefined') {
113
+ return true;
114
+ }
115
+ if (e.defaultPrevented || (e.button !== undefined && e.button !== 0) || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
116
+ return true;
117
+ }
118
+ const eventTarget = e.target;
119
+ const source = eventTarget && (typeof eventTarget.closest === 'function' ? eventTarget : eventTarget.parentElement);
120
+ const anchor = source && typeof source.closest === 'function' ? source.closest('a[href][data-pushstate]') : null;
121
+ if (!anchor) {
122
+ return true;
123
+ }
124
+ const target = anchor.getAttribute('target');
125
+ if (anchor.hasAttribute('download') || (target && target.toLowerCase() !== '_self')) {
126
+ return true;
127
+ }
128
+ const location = new URL(anchor.getAttribute('href') || '', window.location.href || `${window.location.origin}/`);
129
+ if (location.origin !== window.location.origin) {
130
+ return true;
131
+ }
132
+ e.preventDefault();
133
+ this.url = `${location.pathname}${location.search}${location.hash}`;
134
+ this.navigate(undefined, {}, false);
135
+ return this;
136
+ }
137
+ /** Restore browser history state without adding a new history entry. */
138
+ eventPopState() {
139
+ if (typeof window === 'undefined') {
140
+ return this;
141
+ }
142
+ this.url = this.getCurrentUrl();
143
+ this.navigate(undefined, {}, true);
144
+ return this;
145
+ }
146
+ /** Decode URL query parameters, keeping the last value for duplicate keys. */
147
+ parseQueryString(queryString) {
148
+ return Object.fromEntries(new URLSearchParams(queryString));
149
+ }
150
+ /** Build decoded path, query, and mediator data for the selected route. */
151
+ setLocationData(mediatorData) {
152
+ const parsedUrl = new URL(this.url || '/', this.getOrigin());
153
+ const matchedRoute = this.route && this.route !== 'defaultRoute' ? this.route : '';
154
+ this.locationData = {
155
+ url: this.url,
156
+ data: { url: [], mediator: mediatorData, query: {} }
51
157
  };
52
- }();
53
-
54
- (function () {
55
-
56
- 'use strict';
57
-
58
- module.exports = function () {
59
- function _class() {
60
- _classCallCheck(this, _class);
61
-
62
- // placeholder for routes defined when extended
63
- this.routes = {
64
- defaultRoute: {
65
- view: {
66
- initialize: function initialize() {
67
- return true;
68
- },
69
- destroy: function destroy() {
70
- return true;
71
- }
72
- }
73
- }
74
- };
75
-
76
- // this will hold the current url
77
- this.url = '';
78
-
79
- // this holds the current route
80
- this.route = null;
81
-
82
- // can house a container for the router to act upon
83
- this.scope = null;
84
-
85
- // this holds the previous route for destorying later
86
- this.previousRoute = null;
87
-
88
- // this will hold the page title (not yet supported by browsers)
89
- this.pageTitle = null;
90
-
91
- // optional mediator placeholder
92
- this.mediator = false;
158
+ this.locationData.data.url = parsedUrl.pathname
159
+ .slice(matchedRoute.length)
160
+ .split('/')
161
+ .filter(Boolean)
162
+ .map((fragment) => {
163
+ try {
164
+ return decodeURIComponent(fragment);
93
165
  }
94
-
95
- _createClass(_class, [{
96
- key: 'initialize',
97
- value: function initialize() {
98
-
99
- // set orgin if not supported by the browser
100
- if (!window.location.origin) {
101
- window.location.origin = window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
102
- }
103
-
104
- //get the url fragment w/query string
105
- this.url = window.location.pathname + (window.location.search || '') || '';
106
-
107
- // set up the events
108
- this.addListeners();
109
-
110
- // navigate to correct view
111
- this.navigate(false, {}, false);
112
-
113
- return this;
114
- }
115
- }, {
116
- key: 'addListeners',
117
- value: function addListeners() {
118
- var _this = this;
119
-
120
- //bind all pushstate links
121
- (0, _gator2.default)(document).on('click', 'a[href][data-pushstate]', this.eventPushStateClick.bind(this));
122
-
123
- //bind window popstates
124
- window.addEventListener('popstate', this.eventPopState.bind(this));
125
-
126
- //listen to a mediator if present
127
- if (this.mediator) {
128
-
129
- this.mediator.on('router:navigate', function (data) {
130
- _this.navigate(data.url, data || {}, false);
131
- });
132
- }
133
-
134
- return this;
135
- }
136
- }, {
137
- key: 'destroy',
138
- value: function destroy() {
139
-
140
- this.removeListeners();
141
-
142
- return this;
143
- }
144
- }, {
145
- key: 'removeListeners',
146
- value: function removeListeners() {
147
-
148
- //unbind all pushstate links
149
- (0, _gator2.default)(document).off('click', 'a[href][data-pushstate]');
150
-
151
- //bind window popstates
152
- window.removeEventListener('popstate', this.eventPopState.bind(this));
153
-
154
- return this;
155
- }
156
- }, {
157
- key: 'eventPushStateClick',
158
- value: function eventPushStateClick(e) {
159
-
160
- e.preventDefault();
161
-
162
- //for example an image tag can initiate a click event in side an anchor
163
- if (e.target.tagName === 'A') {
164
- this.url = e.target.getAttribute('href') || '';
165
- } else {
166
- this.url = e.target.parentNode.getAttribute('href') || '';
167
- }
168
-
169
- this.navigate(false, {}, false);
170
-
171
- return this;
172
- }
173
- }, {
174
- key: 'eventPopState',
175
- value: function eventPopState(e) {
176
-
177
- this.url = e.state || '';
178
-
179
- this.navigate(false, {}, true);
180
-
181
- return this;
182
- }
183
- }, {
184
- key: 'parseQueryString',
185
- value: function parseQueryString(queryString) {
186
-
187
- var params = {};
188
- var queries = void 0;
189
- var temp = void 0;
190
- var i = void 0;
191
- var l = void 0;
192
-
193
- // Split into key/value pairs
194
- queries = queryString.split('&amp;');
195
-
196
- // Convert the array of strings into an object
197
- for (i = 0, l = queries.length; i < l; i++) {
198
- temp = queries[i].split('=');
199
- params[temp[0]] = temp[1];
200
- }
201
-
202
- return params;
203
- }
204
- }, {
205
- key: 'setLocationData',
206
- value: function setLocationData(mediatorData) {
207
-
208
- //split the url here to seperate query strings from url path
209
- var urlFragments = this.url.split('?');
210
-
211
- // this is the object passed to the matching view
212
- this.locationData = {
213
- url: this.url,
214
- data: {
215
- url: [],
216
- mediator: mediatorData,
217
- query: {}
218
- }
219
- };
220
-
221
- // parse out the url data
222
- this.locationData.data.url = urlFragments[0].replace(this.route, '').split('/');
223
- this.locationData.data.url.splice(0, 1);
224
-
225
- // remove any trailing empty items from the array
226
- if (this.locationData.data.url.length && this.locationData.data.url[this.locationData.data.url.length - 1] === '') {
227
- this.locationData.data.url.splice(this.locationData.data.url.length - 1, 1);
228
- }
229
-
230
- //add query string data
231
- if (urlFragments[1]) {
232
- this.locationData.data.query = this.parseQueryString(urlFragments[1].replace('?', ''));
233
- }
234
- }
235
- }, {
236
- key: 'navigate',
237
- value: function navigate(url, mediatorData, isPopState) {
238
-
239
- // allow navigate to use a specified url
240
- if (url) {
241
- this.url = url;
242
- }
243
-
244
- //clean up url for matching
245
- this.url = this.url.replace(window.location.origin, '');
246
-
247
- // reset this to null for new location
248
- this.route = null;
249
-
250
- //find the first matcher that starts with the same string as a defined route
251
- for (var route in this.routes) {
252
-
253
- // we have a match
254
- if (this.url.lastIndexOf(route, 0) === 0) {
255
-
256
- this.route = route;
257
-
258
- //stop looping we are done
259
- break;
260
- }
261
- }
262
-
263
- this.setLocationData(mediatorData);
264
-
265
- // if a catch all default route is defined execute that when not match is found
266
- if (!this.route && this.routes.defaultRoute) {
267
-
268
- this.route = 'defaultRoute';
269
- }
270
-
271
- if (this.route) {
272
-
273
- // check if the page is secure and if the user passes the specified secure checking function
274
- if (typeof this.routes[this.route].secure === 'function' && this.routes[this.route].secure(this.scope, this.locationData) !== true) {
275
- return false;
276
- }
277
-
278
- //if previous route then destroy it
279
- if (this.previousRoute) {
280
-
281
- // else if it is an object check if there is a view class
282
- if (_typeof(this.routes[this.previousRoute]) === 'object' && _typeof(this.routes[this.previousRoute].view) === 'object' && typeof this.routes[this.previousRoute].view.destroy === 'function') {
283
-
284
- //destroy the view
285
- this.routes[this.previousRoute].view.destroy(this.scope, this.locationData);
286
- }
287
- }
288
-
289
- //execute the route if it is a simple function
290
- if (typeof this.routes[this.route] === 'function') {
291
-
292
- //execute the view
293
- this.routes[this.route](this.scope, this.locationData);
294
-
295
- //cache the old route
296
- this.previousRoute = this.route;
297
- } else if (_typeof(this.routes[this.route]) === 'object' && typeof this.routes[this.route].view === 'function') {
298
-
299
- //execute the view
300
- this.routes[this.route].view(this.scope, this.locationData);
301
-
302
- // if a title is defined for this route we'll set it
303
- if (typeof this.routes[this.route].title === 'string') {
304
- this.pageTitle = this.routes[this.route].title;
305
- } else {
306
- this.pageTitle = null;
307
- }
308
-
309
- //cache the old route
310
- this.previousRoute = this.route;
311
-
312
- // else if it is an object check if there is a view class
313
- } else if (_typeof(this.routes[this.route]) === 'object' && _typeof(this.routes[this.route].view) === 'object' && typeof this.routes[this.route].view.initialize === 'function') {
314
-
315
- // if a title is defined for this route we'll set it
316
- if (typeof this.routes[this.route].title === 'string') {
317
- this.pageTitle = this.routes[this.route].title;
318
- } else {
319
- this.pageTitle = null;
320
- }
321
-
322
- //execute the view
323
- this.routes[this.route].view.initialize(this.scope, this.locationData);
324
-
325
- //cache the old route
326
- this.previousRoute = this.route;
327
- } else {
328
-
329
- return false;
330
- }
331
- }
332
-
333
- // make sure to not set pushstate on back button click
334
- if (!isPopState) {
335
- //set in history the new url
336
- window.history.pushState(this.url, this.pageTitle, this.url);
337
- }
338
-
339
- // since browsers don't support setting the title with pushstate yet
340
- if (this.pageTitle) {
341
- document.title = this.pageTitle;
342
- }
343
-
344
- return this;
166
+ catch (_error) {
167
+ return fragment;
168
+ }
169
+ });
170
+ this.locationData.data.query = this.parseQueryString(parsedUrl.search);
171
+ }
172
+ /** Update page context after a route renders. DOM effects are browser-only. */
173
+ applyPageContext(route) {
174
+ if (typeof route === 'function') {
175
+ this.pageTitle = null;
176
+ return this;
177
+ }
178
+ this.pageTitle = typeof route.title === 'string' ? route.title : null;
179
+ if (!this.isBrowserRuntime()) {
180
+ return this;
181
+ }
182
+ if (this.pageTitle) {
183
+ document.title = this.pageTitle;
184
+ }
185
+ if (route.focus !== false) {
186
+ const target = document.querySelector(route.focus || 'main h1');
187
+ if (target) {
188
+ if (!target.hasAttribute('tabindex')) {
189
+ target.setAttribute('tabindex', '-1');
345
190
  }
346
- }]);
347
-
348
- return _class;
349
- }();
350
- })();
351
- });
191
+ target.focus();
192
+ }
193
+ }
194
+ return this;
195
+ }
196
+ /** Select a route, enforce its guard, transition view lifecycles, and update browser history when available. */
197
+ navigate(url, mediatorData, isPopState = false) {
198
+ if (url !== undefined) {
199
+ this.url = url;
200
+ }
201
+ if (!this.url) {
202
+ this.url = this.getCurrentUrl();
203
+ }
204
+ const normalizedUrl = this.normalizeBrowserUrl(this.url);
205
+ if (normalizedUrl === null) {
206
+ return false;
207
+ }
208
+ this.url = normalizedUrl;
209
+ this.route = null;
210
+ const pathname = new URL(this.url, this.getOrigin()).pathname;
211
+ for (const route in this.routes) {
212
+ if (route === 'defaultRoute' || (pathname !== route && !pathname.startsWith(`${route}/`))) {
213
+ continue;
214
+ }
215
+ if (!this.route || route.length > this.route.length) {
216
+ this.route = route;
217
+ }
218
+ }
219
+ this.setLocationData(mediatorData);
220
+ if (!this.route && this.routes.defaultRoute) {
221
+ this.route = 'defaultRoute';
222
+ }
223
+ if (this.route) {
224
+ const selected = this.routes[this.route];
225
+ if (typeof selected !== 'function' && selected.secure && selected.secure(this.scope, this.locationData) !== true) {
226
+ return false;
227
+ }
228
+ const previous = this.previousRoute ? this.routes[this.previousRoute] : undefined;
229
+ if (previous && typeof previous !== 'function' && typeof previous.view === 'object' && previous.view.destroy) {
230
+ previous.view.destroy(this.scope, this.locationData);
231
+ }
232
+ if (typeof selected === 'function') {
233
+ selected(this.scope, this.locationData);
234
+ }
235
+ else if (typeof selected.view === 'function') {
236
+ selected.view(this.scope, this.locationData);
237
+ }
238
+ else if (selected.view && typeof selected.view.initialize === 'function') {
239
+ selected.view.initialize(this.scope, this.locationData);
240
+ }
241
+ else {
242
+ return false;
243
+ }
244
+ this.applyPageContext(selected);
245
+ this.previousRoute = this.route;
246
+ }
247
+ if (!isPopState && this.isBrowserRuntime()) {
248
+ window.history.pushState(this.url, this.pageTitle || '', this.url);
249
+ }
250
+ return this;
251
+ }
252
+ }
253
+ module.exports = Router;
254
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/** Navigation payload, including caller-defined metadata. */\ninterface NavigationData {url?: string; [key: string]: unknown}\n/** URL-derived values supplied to route handlers. */\ninterface LocationData {url: string; data: {url: string[]; mediator: NavigationData | undefined; query: Record<string, string>}}\ntype RouteHandler = (scope: Element | null, location: LocationData) => unknown;\n/** An object route can own a function or a view lifecycle. */\ninterface RouteObject {title?: string; focus?: string | false; secure?: RouteHandler; view?: RouteHandler | {initialize?: RouteHandler; destroy?: RouteHandler}}\ntype Route = RouteHandler | RouteObject;\n/** Minimal event-bus contract required by the router. */\ninterface NavigationMediator {\n on(event: string, callback: (data?: NavigationData) => unknown): unknown;\n removeListener(event: string, callback: (data?: NavigationData) => unknown): unknown;\n}\n/** History API router that also dispatches request URLs in non-browser runtimes. */\nclass Router {\n routes: Record<string, Route>;\n url: string;\n route: string | null;\n scope: Element | null;\n previousRoute: string | null;\n pageTitle: string | null;\n mediator: NavigationMediator | false;\n boundPushStateClick: (event: MouseEvent) => unknown;\n boundPopState: (event: PopStateEvent) => unknown;\n boundMediatorNavigate: (data?: NavigationData) => unknown;\n listenersInitialized: boolean;\n locationData: LocationData = {url: '', data: {url: [], mediator: undefined, query: {}}};\n\n /** Create an instance with its own state and listener references. */\n constructor() {\n this.routes = {\n defaultRoute: {\n view: {\n initialize: () => true,\n destroy: () => true\n }\n }\n };\n this.url = '';\n this.route = null;\n this.scope = null;\n this.previousRoute = null;\n this.pageTitle = null;\n this.mediator = false;\n this.boundPushStateClick = this.eventPushStateClick.bind(this);\n this.boundPopState = this.eventPopState.bind(this);\n this.boundMediatorNavigate = (data = {}) => {\n this.navigate(data.url, data, false);\n };\n this.listenersInitialized = false;\n }\n\n /** True when browser navigation APIs are available. */\n private isBrowserRuntime() {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n }\n\n /** Stable base URL for WHATWG URL parsing in browser and server runtimes. */\n private getOrigin() {\n return this.isBrowserRuntime() ? window.location.origin : 'http://white-label.invalid';\n }\n\n /** Read the authoritative browser URL, or retain the supplied server URL. */\n private getCurrentUrl() {\n if (!this.isBrowserRuntime()) {return this.url || '/';}\n return window.location.pathname + window.location.search + (window.location.hash || '');\n }\n\n /** Normalize browser navigation to a same-origin path, query, and hash. */\n private normalizeBrowserUrl(url: string) {\n if (!this.isBrowserRuntime()) {return url;}\n const parsedUrl = new URL(url || '/', window.location.href || `${window.location.origin}/`);\n if (parsedUrl.origin !== window.location.origin) {return null;}\n return `${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;\n }\n\n /**\n * Start this instance and return it for lifecycle chaining.\n * In a browser the URL is optional and defaults to window.location. On a server pass the request URL.\n */\n initialize(url?: string) {\n this.url = url ?? this.getCurrentUrl();\n this.addListeners();\n this.navigate(undefined, {}, true);\n return this;\n }\n\n /** Register browser and optional mediator handlers once. */\n addListeners() {\n if (this.listenersInitialized) {return this;}\n if (this.isBrowserRuntime()) {\n document.addEventListener('click', this.boundPushStateClick);\n window.addEventListener('popstate', this.boundPopState);\n }\n if (this.mediator) {this.mediator.on('router:navigate', this.boundMediatorNavigate);}\n this.listenersInitialized = true;\n return this;\n }\n\n /** Release owned state and listeners so the instance can leave the application lifecycle. */\n destroy() {\n this.removeListeners();\n return this;\n }\n\n /** Release listeners owned by this instance; subclasses may extend the lifecycle hook. */\n removeListeners() {\n if (!this.listenersInitialized) {return this;}\n if (this.isBrowserRuntime()) {\n document.removeEventListener('click', this.boundPushStateClick);\n window.removeEventListener('popstate', this.boundPopState);\n }\n if (this.mediator) {this.mediator.removeListener('router:navigate', this.boundMediatorNavigate);}\n this.listenersInitialized = false;\n return this;\n }\n\n /** Intercept eligible same-origin push-state links while preserving normal browser actions. */\n eventPushStateClick(e: MouseEvent) {\n if (typeof window === 'undefined') {return true;}\n if (e.defaultPrevented || (e.button !== undefined && e.button !== 0) || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {\n return true;\n }\n const eventTarget = e.target as Element | null;\n const source = eventTarget && (typeof eventTarget.closest === 'function' ? eventTarget : eventTarget.parentElement);\n const anchor = source && typeof source.closest === 'function' ? source.closest('a[href][data-pushstate]') : null;\n if (!anchor) {return true;}\n const target = anchor.getAttribute('target');\n if (anchor.hasAttribute('download') || (target && target.toLowerCase() !== '_self')) {return true;}\n const location = new URL(anchor.getAttribute('href') || '', window.location.href || `${window.location.origin}/`);\n if (location.origin !== window.location.origin) {return true;}\n e.preventDefault();\n this.url = `${location.pathname}${location.search}${location.hash}`;\n this.navigate(undefined, {}, false);\n return this;\n }\n\n /** Restore browser history state without adding a new history entry. */\n eventPopState() {\n if (typeof window === 'undefined') {return this;}\n this.url = this.getCurrentUrl();\n this.navigate(undefined, {}, true);\n return this;\n }\n\n /** Decode URL query parameters, keeping the last value for duplicate keys. */\n parseQueryString(queryString: string) {\n return Object.fromEntries(new URLSearchParams(queryString));\n }\n\n /** Build decoded path, query, and mediator data for the selected route. */\n setLocationData(mediatorData?: NavigationData) {\n const parsedUrl = new URL(this.url || '/', this.getOrigin());\n const matchedRoute = this.route && this.route !== 'defaultRoute' ? this.route : '';\n this.locationData = {\n url: this.url,\n data: {url: [], mediator: mediatorData, query: {}}\n };\n this.locationData.data.url = parsedUrl.pathname\n .slice(matchedRoute.length)\n .split('/')\n .filter(Boolean)\n .map((fragment) => {\n try {return decodeURIComponent(fragment);} catch (_error) {return fragment;}\n });\n this.locationData.data.query = this.parseQueryString(parsedUrl.search);\n }\n\n /** Update page context after a route renders. DOM effects are browser-only. */\n applyPageContext(route: Route) {\n if (typeof route === 'function') {\n this.pageTitle = null;\n return this;\n }\n this.pageTitle = typeof route.title === 'string' ? route.title : null;\n if (!this.isBrowserRuntime()) {return this;}\n if (this.pageTitle) {document.title = this.pageTitle;}\n if (route.focus !== false) {\n const target = document.querySelector<HTMLElement>(route.focus || 'main h1');\n if (target) {\n if (!target.hasAttribute('tabindex')) {target.setAttribute('tabindex', '-1');}\n target.focus();\n }\n }\n return this;\n }\n\n /** Select a route, enforce its guard, transition view lifecycles, and update browser history when available. */\n navigate(url?: string, mediatorData?: NavigationData, isPopState = false) {\n if (url !== undefined) {this.url = url;}\n if (!this.url) {this.url = this.getCurrentUrl();}\n const normalizedUrl = this.normalizeBrowserUrl(this.url);\n if (normalizedUrl === null) {return false;}\n this.url = normalizedUrl;\n this.route = null;\n const pathname = new URL(this.url, this.getOrigin()).pathname;\n for (const route in this.routes) {\n if (route === 'defaultRoute' || (pathname !== route && !pathname.startsWith(`${route}/`))) {continue;}\n if (!this.route || route.length > this.route.length) {this.route = route;}\n }\n this.setLocationData(mediatorData);\n if (!this.route && this.routes.defaultRoute) {this.route = 'defaultRoute';}\n if (this.route) {\n const selected = this.routes[this.route]!;\n if (typeof selected !== 'function' && selected.secure && selected.secure(this.scope, this.locationData) !== true) {\n return false;\n }\n const previous = this.previousRoute ? this.routes[this.previousRoute] : undefined;\n if (previous && typeof previous !== 'function' && typeof previous.view === 'object' && previous.view.destroy) {\n previous.view.destroy(this.scope, this.locationData);\n }\n if (typeof selected === 'function') {\n selected(this.scope, this.locationData);\n } else if (typeof selected.view === 'function') {\n selected.view(this.scope, this.locationData);\n } else if (selected.view && typeof selected.view.initialize === 'function') {\n selected.view.initialize(this.scope, this.locationData);\n } else {\n return false;\n }\n this.applyPageContext(selected);\n this.previousRoute = this.route;\n }\n if (!isPopState && this.isBrowserRuntime()) {\n window.history.pushState(this.url, this.pageTitle || '', this.url);\n }\n return this;\n }\n}\n\n/** Public route configuration and navigation payload types. */\nnamespace Router {\n export type Handler = RouteHandler;\n export type Route = RouteHandler | RouteObject;\n export type Location = LocationData;\n export type Navigation = NavigationData;\n}\nexport = Router;\n"]}