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/LICENSE +9 -0
- package/README.md +188 -266
- package/dist/index.d.ts +89 -0
- package/dist/index.js +245 -342
- package/dist/index.js.map +1 -0
- package/package.json +70 -16
- package/.babelrc +0 -4
- package/.editorconfig +0 -13
- package/.npmignore +0 -2
- package/.travis.yml +0 -5
- package/bin/compile.sh +0 -2
- package/bower.json +0 -26
- package/src/index.js +0 -325
package/dist/index.js
CHANGED
|
@@ -1,351 +1,254 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
25
|
-
return
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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('&');
|
|
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
|
-
|
|
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"]}
|