kdu-router 3.5.4 → 4.0.16
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 +2 -2
- package/README.md +10 -4
- package/dist/kdu-router.cjs.js +3484 -0
- package/dist/kdu-router.cjs.prod.js +2759 -0
- package/dist/kdu-router.d.ts +1349 -0
- package/dist/kdu-router.esm-browser.js +3460 -0
- package/dist/kdu-router.esm-bundler.js +3478 -0
- package/dist/kdu-router.global.js +3625 -0
- package/dist/kdu-router.global.prod.js +6 -0
- package/ketur/attributes.json +8 -14
- package/ketur/tags.json +2 -12
- package/package.json +77 -71
- package/dist/kdu-router.common.js +0 -3147
- package/dist/kdu-router.esm.browser.js +0 -3113
- package/dist/kdu-router.esm.browser.min.js +0 -11
- package/dist/kdu-router.esm.js +0 -3145
- package/dist/kdu-router.js +0 -3152
- package/dist/kdu-router.min.js +0 -11
- package/src/components/link.js +0 -224
- package/src/components/view.js +0 -155
- package/src/create-matcher.js +0 -226
- package/src/create-route-map.js +0 -220
- package/src/history/abstract.js +0 -72
- package/src/history/base.js +0 -379
- package/src/history/hash.js +0 -152
- package/src/history/html5.js +0 -99
- package/src/index.js +0 -293
- package/src/install.js +0 -52
- package/src/util/async.js +0 -18
- package/src/util/dom.js +0 -3
- package/src/util/errors.js +0 -86
- package/src/util/location.js +0 -69
- package/src/util/misc.js +0 -6
- package/src/util/params.js +0 -37
- package/src/util/path.js +0 -74
- package/src/util/push-state.js +0 -46
- package/src/util/query.js +0 -113
- package/src/util/resolve-components.js +0 -109
- package/src/util/route.js +0 -151
- package/src/util/scroll.js +0 -175
- package/src/util/state-key.js +0 -22
- package/src/util/warn.js +0 -14
- package/types/index.d.ts +0 -21
- package/types/kdu.d.ts +0 -22
- package/types/router.d.ts +0 -211
|
@@ -1,3113 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* kdu-router v3.5.4
|
|
3
|
-
* (c) 2022 NKDuy
|
|
4
|
-
* @license MIT
|
|
5
|
-
*/
|
|
6
|
-
/* */
|
|
7
|
-
|
|
8
|
-
function assert (condition, message) {
|
|
9
|
-
if (!condition) {
|
|
10
|
-
throw new Error(`[kdu-router] ${message}`)
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function warn (condition, message) {
|
|
15
|
-
if (!condition) {
|
|
16
|
-
typeof console !== 'undefined' && console.warn(`[kdu-router] ${message}`);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function extend (a, b) {
|
|
21
|
-
for (const key in b) {
|
|
22
|
-
a[key] = b[key];
|
|
23
|
-
}
|
|
24
|
-
return a
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/* */
|
|
28
|
-
|
|
29
|
-
const encodeReserveRE = /[!'()*]/g;
|
|
30
|
-
const encodeReserveReplacer = c => '%' + c.charCodeAt(0).toString(16);
|
|
31
|
-
const commaRE = /%2C/g;
|
|
32
|
-
|
|
33
|
-
// fixed encodeURIComponent which is more conformant to RFC3986:
|
|
34
|
-
// - escapes [!'()*]
|
|
35
|
-
// - preserve commas
|
|
36
|
-
const encode = str =>
|
|
37
|
-
encodeURIComponent(str)
|
|
38
|
-
.replace(encodeReserveRE, encodeReserveReplacer)
|
|
39
|
-
.replace(commaRE, ',');
|
|
40
|
-
|
|
41
|
-
function decode (str) {
|
|
42
|
-
try {
|
|
43
|
-
return decodeURIComponent(str)
|
|
44
|
-
} catch (err) {
|
|
45
|
-
{
|
|
46
|
-
warn(false, `Error decoding "${str}". Leaving it intact.`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return str
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function resolveQuery (
|
|
53
|
-
query,
|
|
54
|
-
extraQuery = {},
|
|
55
|
-
_parseQuery
|
|
56
|
-
) {
|
|
57
|
-
const parse = _parseQuery || parseQuery;
|
|
58
|
-
let parsedQuery;
|
|
59
|
-
try {
|
|
60
|
-
parsedQuery = parse(query || '');
|
|
61
|
-
} catch (e) {
|
|
62
|
-
warn(false, e.message);
|
|
63
|
-
parsedQuery = {};
|
|
64
|
-
}
|
|
65
|
-
for (const key in extraQuery) {
|
|
66
|
-
const value = extraQuery[key];
|
|
67
|
-
parsedQuery[key] = Array.isArray(value)
|
|
68
|
-
? value.map(castQueryParamValue)
|
|
69
|
-
: castQueryParamValue(value);
|
|
70
|
-
}
|
|
71
|
-
return parsedQuery
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const castQueryParamValue = value => (value == null || typeof value === 'object' ? value : String(value));
|
|
75
|
-
|
|
76
|
-
function parseQuery (query) {
|
|
77
|
-
const res = {};
|
|
78
|
-
|
|
79
|
-
query = query.trim().replace(/^(\?|#|&)/, '');
|
|
80
|
-
|
|
81
|
-
if (!query) {
|
|
82
|
-
return res
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
query.split('&').forEach(param => {
|
|
86
|
-
const parts = param.replace(/\+/g, ' ').split('=');
|
|
87
|
-
const key = decode(parts.shift());
|
|
88
|
-
const val = parts.length > 0 ? decode(parts.join('=')) : null;
|
|
89
|
-
|
|
90
|
-
if (res[key] === undefined) {
|
|
91
|
-
res[key] = val;
|
|
92
|
-
} else if (Array.isArray(res[key])) {
|
|
93
|
-
res[key].push(val);
|
|
94
|
-
} else {
|
|
95
|
-
res[key] = [res[key], val];
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return res
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function stringifyQuery (obj) {
|
|
103
|
-
const res = obj
|
|
104
|
-
? Object.keys(obj)
|
|
105
|
-
.map(key => {
|
|
106
|
-
const val = obj[key];
|
|
107
|
-
|
|
108
|
-
if (val === undefined) {
|
|
109
|
-
return ''
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (val === null) {
|
|
113
|
-
return encode(key)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (Array.isArray(val)) {
|
|
117
|
-
const result = [];
|
|
118
|
-
val.forEach(val2 => {
|
|
119
|
-
if (val2 === undefined) {
|
|
120
|
-
return
|
|
121
|
-
}
|
|
122
|
-
if (val2 === null) {
|
|
123
|
-
result.push(encode(key));
|
|
124
|
-
} else {
|
|
125
|
-
result.push(encode(key) + '=' + encode(val2));
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
return result.join('&')
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return encode(key) + '=' + encode(val)
|
|
132
|
-
})
|
|
133
|
-
.filter(x => x.length > 0)
|
|
134
|
-
.join('&')
|
|
135
|
-
: null;
|
|
136
|
-
return res ? `?${res}` : ''
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/* */
|
|
140
|
-
|
|
141
|
-
const trailingSlashRE = /\/?$/;
|
|
142
|
-
|
|
143
|
-
function createRoute (
|
|
144
|
-
record,
|
|
145
|
-
location,
|
|
146
|
-
redirectedFrom,
|
|
147
|
-
router
|
|
148
|
-
) {
|
|
149
|
-
const stringifyQuery = router && router.options.stringifyQuery;
|
|
150
|
-
|
|
151
|
-
let query = location.query || {};
|
|
152
|
-
try {
|
|
153
|
-
query = clone(query);
|
|
154
|
-
} catch (e) {}
|
|
155
|
-
|
|
156
|
-
const route = {
|
|
157
|
-
name: location.name || (record && record.name),
|
|
158
|
-
meta: (record && record.meta) || {},
|
|
159
|
-
path: location.path || '/',
|
|
160
|
-
hash: location.hash || '',
|
|
161
|
-
query,
|
|
162
|
-
params: location.params || {},
|
|
163
|
-
fullPath: getFullPath(location, stringifyQuery),
|
|
164
|
-
matched: record ? formatMatch(record) : []
|
|
165
|
-
};
|
|
166
|
-
if (redirectedFrom) {
|
|
167
|
-
route.redirectedFrom = getFullPath(redirectedFrom, stringifyQuery);
|
|
168
|
-
}
|
|
169
|
-
return Object.freeze(route)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function clone (value) {
|
|
173
|
-
if (Array.isArray(value)) {
|
|
174
|
-
return value.map(clone)
|
|
175
|
-
} else if (value && typeof value === 'object') {
|
|
176
|
-
const res = {};
|
|
177
|
-
for (const key in value) {
|
|
178
|
-
res[key] = clone(value[key]);
|
|
179
|
-
}
|
|
180
|
-
return res
|
|
181
|
-
} else {
|
|
182
|
-
return value
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// the starting route that represents the initial state
|
|
187
|
-
const START = createRoute(null, {
|
|
188
|
-
path: '/'
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
function formatMatch (record) {
|
|
192
|
-
const res = [];
|
|
193
|
-
while (record) {
|
|
194
|
-
res.unshift(record);
|
|
195
|
-
record = record.parent;
|
|
196
|
-
}
|
|
197
|
-
return res
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function getFullPath (
|
|
201
|
-
{ path, query = {}, hash = '' },
|
|
202
|
-
_stringifyQuery
|
|
203
|
-
) {
|
|
204
|
-
const stringify = _stringifyQuery || stringifyQuery;
|
|
205
|
-
return (path || '/') + stringify(query) + hash
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function isSameRoute (a, b, onlyPath) {
|
|
209
|
-
if (b === START) {
|
|
210
|
-
return a === b
|
|
211
|
-
} else if (!b) {
|
|
212
|
-
return false
|
|
213
|
-
} else if (a.path && b.path) {
|
|
214
|
-
return a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') && (onlyPath ||
|
|
215
|
-
a.hash === b.hash &&
|
|
216
|
-
isObjectEqual(a.query, b.query))
|
|
217
|
-
} else if (a.name && b.name) {
|
|
218
|
-
return (
|
|
219
|
-
a.name === b.name &&
|
|
220
|
-
(onlyPath || (
|
|
221
|
-
a.hash === b.hash &&
|
|
222
|
-
isObjectEqual(a.query, b.query) &&
|
|
223
|
-
isObjectEqual(a.params, b.params))
|
|
224
|
-
)
|
|
225
|
-
)
|
|
226
|
-
} else {
|
|
227
|
-
return false
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function isObjectEqual (a = {}, b = {}) {
|
|
232
|
-
// handle null value #1566
|
|
233
|
-
if (!a || !b) return a === b
|
|
234
|
-
const aKeys = Object.keys(a).sort();
|
|
235
|
-
const bKeys = Object.keys(b).sort();
|
|
236
|
-
if (aKeys.length !== bKeys.length) {
|
|
237
|
-
return false
|
|
238
|
-
}
|
|
239
|
-
return aKeys.every((key, i) => {
|
|
240
|
-
const aVal = a[key];
|
|
241
|
-
const bKey = bKeys[i];
|
|
242
|
-
if (bKey !== key) return false
|
|
243
|
-
const bVal = b[key];
|
|
244
|
-
// query values can be null and undefined
|
|
245
|
-
if (aVal == null || bVal == null) return aVal === bVal
|
|
246
|
-
// check nested equality
|
|
247
|
-
if (typeof aVal === 'object' && typeof bVal === 'object') {
|
|
248
|
-
return isObjectEqual(aVal, bVal)
|
|
249
|
-
}
|
|
250
|
-
return String(aVal) === String(bVal)
|
|
251
|
-
})
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function isIncludedRoute (current, target) {
|
|
255
|
-
return (
|
|
256
|
-
current.path.replace(trailingSlashRE, '/').indexOf(
|
|
257
|
-
target.path.replace(trailingSlashRE, '/')
|
|
258
|
-
) === 0 &&
|
|
259
|
-
(!target.hash || current.hash === target.hash) &&
|
|
260
|
-
queryIncludes(current.query, target.query)
|
|
261
|
-
)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function queryIncludes (current, target) {
|
|
265
|
-
for (const key in target) {
|
|
266
|
-
if (!(key in current)) {
|
|
267
|
-
return false
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
return true
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function handleRouteEntered (route) {
|
|
274
|
-
for (let i = 0; i < route.matched.length; i++) {
|
|
275
|
-
const record = route.matched[i];
|
|
276
|
-
for (const name in record.instances) {
|
|
277
|
-
const instance = record.instances[name];
|
|
278
|
-
const cbs = record.enteredCbs[name];
|
|
279
|
-
if (!instance || !cbs) continue
|
|
280
|
-
delete record.enteredCbs[name];
|
|
281
|
-
for (let i = 0; i < cbs.length; i++) {
|
|
282
|
-
if (!instance._isBeingDestroyed) cbs[i](instance);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
var View = {
|
|
289
|
-
name: 'RouterView',
|
|
290
|
-
functional: true,
|
|
291
|
-
props: {
|
|
292
|
-
name: {
|
|
293
|
-
type: String,
|
|
294
|
-
default: 'default'
|
|
295
|
-
}
|
|
296
|
-
},
|
|
297
|
-
render (_, { props, children, parent, data }) {
|
|
298
|
-
// used by devtools to display a router-view badge
|
|
299
|
-
data.routerView = true;
|
|
300
|
-
|
|
301
|
-
// directly use parent context's createElement() function
|
|
302
|
-
// so that components rendered by router-view can resolve named slots
|
|
303
|
-
const h = parent.$createElement;
|
|
304
|
-
const name = props.name;
|
|
305
|
-
const route = parent.$route;
|
|
306
|
-
const cache = parent._routerViewCache || (parent._routerViewCache = {});
|
|
307
|
-
|
|
308
|
-
// determine current view depth, also check to see if the tree
|
|
309
|
-
// has been toggled inactive but kept-alive.
|
|
310
|
-
let depth = 0;
|
|
311
|
-
let inactive = false;
|
|
312
|
-
while (parent && parent._routerRoot !== parent) {
|
|
313
|
-
const knodeData = parent.$knode ? parent.$knode.data : {};
|
|
314
|
-
if (knodeData.routerView) {
|
|
315
|
-
depth++;
|
|
316
|
-
}
|
|
317
|
-
if (knodeData.keepAlive && parent._directInactive && parent._inactive) {
|
|
318
|
-
inactive = true;
|
|
319
|
-
}
|
|
320
|
-
parent = parent.$parent;
|
|
321
|
-
}
|
|
322
|
-
data.routerViewDepth = depth;
|
|
323
|
-
|
|
324
|
-
// render previous view if the tree is inactive and kept-alive
|
|
325
|
-
if (inactive) {
|
|
326
|
-
const cachedData = cache[name];
|
|
327
|
-
const cachedComponent = cachedData && cachedData.component;
|
|
328
|
-
if (cachedComponent) {
|
|
329
|
-
// #2301
|
|
330
|
-
// pass props
|
|
331
|
-
if (cachedData.configProps) {
|
|
332
|
-
fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps);
|
|
333
|
-
}
|
|
334
|
-
return h(cachedComponent, data, children)
|
|
335
|
-
} else {
|
|
336
|
-
// render previous empty view
|
|
337
|
-
return h()
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const matched = route.matched[depth];
|
|
342
|
-
const component = matched && matched.components[name];
|
|
343
|
-
|
|
344
|
-
// render empty node if no matched route or no config component
|
|
345
|
-
if (!matched || !component) {
|
|
346
|
-
cache[name] = null;
|
|
347
|
-
return h()
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// cache component
|
|
351
|
-
cache[name] = { component };
|
|
352
|
-
|
|
353
|
-
// attach instance registration hook
|
|
354
|
-
// this will be called in the instance's injected lifecycle hooks
|
|
355
|
-
data.registerRouteInstance = (vm, val) => {
|
|
356
|
-
// val could be undefined for unregistration
|
|
357
|
-
const current = matched.instances[name];
|
|
358
|
-
if (
|
|
359
|
-
(val && current !== vm) ||
|
|
360
|
-
(!val && current === vm)
|
|
361
|
-
) {
|
|
362
|
-
matched.instances[name] = val;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// also register instance in prepatch hook
|
|
367
|
-
// in case the same component instance is reused across different routes
|
|
368
|
-
;(data.hook || (data.hook = {})).prepatch = (_, knode) => {
|
|
369
|
-
matched.instances[name] = knode.componentInstance;
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// register instance in init hook
|
|
373
|
-
// in case kept-alive component be actived when routes changed
|
|
374
|
-
data.hook.init = (knode) => {
|
|
375
|
-
if (knode.data.keepAlive &&
|
|
376
|
-
knode.componentInstance &&
|
|
377
|
-
knode.componentInstance !== matched.instances[name]
|
|
378
|
-
) {
|
|
379
|
-
matched.instances[name] = knode.componentInstance;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// if the route transition has already been confirmed then we weren't
|
|
383
|
-
// able to call the cbs during confirmation as the component was not
|
|
384
|
-
// registered yet, so we call it here.
|
|
385
|
-
handleRouteEntered(route);
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
const configProps = matched.props && matched.props[name];
|
|
389
|
-
// save route and configProps in cache
|
|
390
|
-
if (configProps) {
|
|
391
|
-
extend(cache[name], {
|
|
392
|
-
route,
|
|
393
|
-
configProps
|
|
394
|
-
});
|
|
395
|
-
fillPropsinData(component, data, route, configProps);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return h(component, data, children)
|
|
399
|
-
}
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
function fillPropsinData (component, data, route, configProps) {
|
|
403
|
-
// resolve props
|
|
404
|
-
let propsToPass = data.props = resolveProps(route, configProps);
|
|
405
|
-
if (propsToPass) {
|
|
406
|
-
// clone to prevent mutation
|
|
407
|
-
propsToPass = data.props = extend({}, propsToPass);
|
|
408
|
-
// pass non-declared props as attrs
|
|
409
|
-
const attrs = data.attrs = data.attrs || {};
|
|
410
|
-
for (const key in propsToPass) {
|
|
411
|
-
if (!component.props || !(key in component.props)) {
|
|
412
|
-
attrs[key] = propsToPass[key];
|
|
413
|
-
delete propsToPass[key];
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function resolveProps (route, config) {
|
|
420
|
-
switch (typeof config) {
|
|
421
|
-
case 'undefined':
|
|
422
|
-
return
|
|
423
|
-
case 'object':
|
|
424
|
-
return config
|
|
425
|
-
case 'function':
|
|
426
|
-
return config(route)
|
|
427
|
-
case 'boolean':
|
|
428
|
-
return config ? route.params : undefined
|
|
429
|
-
default:
|
|
430
|
-
{
|
|
431
|
-
warn(
|
|
432
|
-
false,
|
|
433
|
-
`props in "${route.path}" is a ${typeof config}, ` +
|
|
434
|
-
`expecting an object, function or boolean.`
|
|
435
|
-
);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/* */
|
|
441
|
-
|
|
442
|
-
function resolvePath (
|
|
443
|
-
relative,
|
|
444
|
-
base,
|
|
445
|
-
append
|
|
446
|
-
) {
|
|
447
|
-
const firstChar = relative.charAt(0);
|
|
448
|
-
if (firstChar === '/') {
|
|
449
|
-
return relative
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
if (firstChar === '?' || firstChar === '#') {
|
|
453
|
-
return base + relative
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const stack = base.split('/');
|
|
457
|
-
|
|
458
|
-
// remove trailing segment if:
|
|
459
|
-
// - not appending
|
|
460
|
-
// - appending to trailing slash (last segment is empty)
|
|
461
|
-
if (!append || !stack[stack.length - 1]) {
|
|
462
|
-
stack.pop();
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// resolve relative path
|
|
466
|
-
const segments = relative.replace(/^\//, '').split('/');
|
|
467
|
-
for (let i = 0; i < segments.length; i++) {
|
|
468
|
-
const segment = segments[i];
|
|
469
|
-
if (segment === '..') {
|
|
470
|
-
stack.pop();
|
|
471
|
-
} else if (segment !== '.') {
|
|
472
|
-
stack.push(segment);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// ensure leading slash
|
|
477
|
-
if (stack[0] !== '') {
|
|
478
|
-
stack.unshift('');
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
return stack.join('/')
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
function parsePath (path) {
|
|
485
|
-
let hash = '';
|
|
486
|
-
let query = '';
|
|
487
|
-
|
|
488
|
-
const hashIndex = path.indexOf('#');
|
|
489
|
-
if (hashIndex >= 0) {
|
|
490
|
-
hash = path.slice(hashIndex);
|
|
491
|
-
path = path.slice(0, hashIndex);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const queryIndex = path.indexOf('?');
|
|
495
|
-
if (queryIndex >= 0) {
|
|
496
|
-
query = path.slice(queryIndex + 1);
|
|
497
|
-
path = path.slice(0, queryIndex);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
return {
|
|
501
|
-
path,
|
|
502
|
-
query,
|
|
503
|
-
hash
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function cleanPath (path) {
|
|
508
|
-
return path.replace(/\/(?:\s*\/)+/g, '/')
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
var isarray = Array.isArray || function (arr) {
|
|
512
|
-
return Object.prototype.toString.call(arr) == '[object Array]';
|
|
513
|
-
};
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Expose `pathToRegexp`.
|
|
517
|
-
*/
|
|
518
|
-
var pathToRegexp_1 = pathToRegexp;
|
|
519
|
-
var parse_1 = parse;
|
|
520
|
-
var compile_1 = compile;
|
|
521
|
-
var tokensToFunction_1 = tokensToFunction;
|
|
522
|
-
var tokensToRegExp_1 = tokensToRegExp;
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* The main path matching regexp utility.
|
|
526
|
-
*
|
|
527
|
-
* @type {RegExp}
|
|
528
|
-
*/
|
|
529
|
-
var PATH_REGEXP = new RegExp([
|
|
530
|
-
// Match escaped characters that would otherwise appear in future matches.
|
|
531
|
-
// This allows the user to escape special characters that won't transform.
|
|
532
|
-
'(\\\\.)',
|
|
533
|
-
// Match Express-style parameters and un-named parameters with a prefix
|
|
534
|
-
// and optional suffixes. Matches appear as:
|
|
535
|
-
//
|
|
536
|
-
// "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
|
|
537
|
-
// "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
|
|
538
|
-
// "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
|
|
539
|
-
'([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'
|
|
540
|
-
].join('|'), 'g');
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Parse a string for the raw tokens.
|
|
544
|
-
*
|
|
545
|
-
* @param {string} str
|
|
546
|
-
* @param {Object=} options
|
|
547
|
-
* @return {!Array}
|
|
548
|
-
*/
|
|
549
|
-
function parse (str, options) {
|
|
550
|
-
var tokens = [];
|
|
551
|
-
var key = 0;
|
|
552
|
-
var index = 0;
|
|
553
|
-
var path = '';
|
|
554
|
-
var defaultDelimiter = options && options.delimiter || '/';
|
|
555
|
-
var res;
|
|
556
|
-
|
|
557
|
-
while ((res = PATH_REGEXP.exec(str)) != null) {
|
|
558
|
-
var m = res[0];
|
|
559
|
-
var escaped = res[1];
|
|
560
|
-
var offset = res.index;
|
|
561
|
-
path += str.slice(index, offset);
|
|
562
|
-
index = offset + m.length;
|
|
563
|
-
|
|
564
|
-
// Ignore already escaped sequences.
|
|
565
|
-
if (escaped) {
|
|
566
|
-
path += escaped[1];
|
|
567
|
-
continue
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
var next = str[index];
|
|
571
|
-
var prefix = res[2];
|
|
572
|
-
var name = res[3];
|
|
573
|
-
var capture = res[4];
|
|
574
|
-
var group = res[5];
|
|
575
|
-
var modifier = res[6];
|
|
576
|
-
var asterisk = res[7];
|
|
577
|
-
|
|
578
|
-
// Push the current path onto the tokens.
|
|
579
|
-
if (path) {
|
|
580
|
-
tokens.push(path);
|
|
581
|
-
path = '';
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
var partial = prefix != null && next != null && next !== prefix;
|
|
585
|
-
var repeat = modifier === '+' || modifier === '*';
|
|
586
|
-
var optional = modifier === '?' || modifier === '*';
|
|
587
|
-
var delimiter = res[2] || defaultDelimiter;
|
|
588
|
-
var pattern = capture || group;
|
|
589
|
-
|
|
590
|
-
tokens.push({
|
|
591
|
-
name: name || key++,
|
|
592
|
-
prefix: prefix || '',
|
|
593
|
-
delimiter: delimiter,
|
|
594
|
-
optional: optional,
|
|
595
|
-
repeat: repeat,
|
|
596
|
-
partial: partial,
|
|
597
|
-
asterisk: !!asterisk,
|
|
598
|
-
pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?')
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// Match any characters still remaining.
|
|
603
|
-
if (index < str.length) {
|
|
604
|
-
path += str.substr(index);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// If the path exists, push it onto the end.
|
|
608
|
-
if (path) {
|
|
609
|
-
tokens.push(path);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
return tokens
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
/**
|
|
616
|
-
* Compile a string to a template function for the path.
|
|
617
|
-
*
|
|
618
|
-
* @param {string} str
|
|
619
|
-
* @param {Object=} options
|
|
620
|
-
* @return {!function(Object=, Object=)}
|
|
621
|
-
*/
|
|
622
|
-
function compile (str, options) {
|
|
623
|
-
return tokensToFunction(parse(str, options), options)
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/**
|
|
627
|
-
* Prettier encoding of URI path segments.
|
|
628
|
-
*
|
|
629
|
-
* @param {string}
|
|
630
|
-
* @return {string}
|
|
631
|
-
*/
|
|
632
|
-
function encodeURIComponentPretty (str) {
|
|
633
|
-
return encodeURI(str).replace(/[\/?#]/g, function (c) {
|
|
634
|
-
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
|
635
|
-
})
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
|
|
640
|
-
*
|
|
641
|
-
* @param {string}
|
|
642
|
-
* @return {string}
|
|
643
|
-
*/
|
|
644
|
-
function encodeAsterisk (str) {
|
|
645
|
-
return encodeURI(str).replace(/[?#]/g, function (c) {
|
|
646
|
-
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
|
647
|
-
})
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* Expose a method for transforming tokens into the path function.
|
|
652
|
-
*/
|
|
653
|
-
function tokensToFunction (tokens, options) {
|
|
654
|
-
// Compile all the tokens into regexps.
|
|
655
|
-
var matches = new Array(tokens.length);
|
|
656
|
-
|
|
657
|
-
// Compile all the patterns before compilation.
|
|
658
|
-
for (var i = 0; i < tokens.length; i++) {
|
|
659
|
-
if (typeof tokens[i] === 'object') {
|
|
660
|
-
matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options));
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
return function (obj, opts) {
|
|
665
|
-
var path = '';
|
|
666
|
-
var data = obj || {};
|
|
667
|
-
var options = opts || {};
|
|
668
|
-
var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent;
|
|
669
|
-
|
|
670
|
-
for (var i = 0; i < tokens.length; i++) {
|
|
671
|
-
var token = tokens[i];
|
|
672
|
-
|
|
673
|
-
if (typeof token === 'string') {
|
|
674
|
-
path += token;
|
|
675
|
-
|
|
676
|
-
continue
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
var value = data[token.name];
|
|
680
|
-
var segment;
|
|
681
|
-
|
|
682
|
-
if (value == null) {
|
|
683
|
-
if (token.optional) {
|
|
684
|
-
// Prepend partial segment prefixes.
|
|
685
|
-
if (token.partial) {
|
|
686
|
-
path += token.prefix;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
continue
|
|
690
|
-
} else {
|
|
691
|
-
throw new TypeError('Expected "' + token.name + '" to be defined')
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
if (isarray(value)) {
|
|
696
|
-
if (!token.repeat) {
|
|
697
|
-
throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`')
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
if (value.length === 0) {
|
|
701
|
-
if (token.optional) {
|
|
702
|
-
continue
|
|
703
|
-
} else {
|
|
704
|
-
throw new TypeError('Expected "' + token.name + '" to not be empty')
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
for (var j = 0; j < value.length; j++) {
|
|
709
|
-
segment = encode(value[j]);
|
|
710
|
-
|
|
711
|
-
if (!matches[i].test(segment)) {
|
|
712
|
-
throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`')
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
path += (j === 0 ? token.prefix : token.delimiter) + segment;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
continue
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
segment = token.asterisk ? encodeAsterisk(value) : encode(value);
|
|
722
|
-
|
|
723
|
-
if (!matches[i].test(segment)) {
|
|
724
|
-
throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
path += token.prefix + segment;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
return path
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
/**
|
|
735
|
-
* Escape a regular expression string.
|
|
736
|
-
*
|
|
737
|
-
* @param {string} str
|
|
738
|
-
* @return {string}
|
|
739
|
-
*/
|
|
740
|
-
function escapeString (str) {
|
|
741
|
-
return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1')
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
/**
|
|
745
|
-
* Escape the capturing group by escaping special characters and meaning.
|
|
746
|
-
*
|
|
747
|
-
* @param {string} group
|
|
748
|
-
* @return {string}
|
|
749
|
-
*/
|
|
750
|
-
function escapeGroup (group) {
|
|
751
|
-
return group.replace(/([=!:$\/()])/g, '\\$1')
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
/**
|
|
755
|
-
* Attach the keys as a property of the regexp.
|
|
756
|
-
*
|
|
757
|
-
* @param {!RegExp} re
|
|
758
|
-
* @param {Array} keys
|
|
759
|
-
* @return {!RegExp}
|
|
760
|
-
*/
|
|
761
|
-
function attachKeys (re, keys) {
|
|
762
|
-
re.keys = keys;
|
|
763
|
-
return re
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
/**
|
|
767
|
-
* Get the flags for a regexp from the options.
|
|
768
|
-
*
|
|
769
|
-
* @param {Object} options
|
|
770
|
-
* @return {string}
|
|
771
|
-
*/
|
|
772
|
-
function flags (options) {
|
|
773
|
-
return options && options.sensitive ? '' : 'i'
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
/**
|
|
777
|
-
* Pull out keys from a regexp.
|
|
778
|
-
*
|
|
779
|
-
* @param {!RegExp} path
|
|
780
|
-
* @param {!Array} keys
|
|
781
|
-
* @return {!RegExp}
|
|
782
|
-
*/
|
|
783
|
-
function regexpToRegexp (path, keys) {
|
|
784
|
-
// Use a negative lookahead to match only capturing groups.
|
|
785
|
-
var groups = path.source.match(/\((?!\?)/g);
|
|
786
|
-
|
|
787
|
-
if (groups) {
|
|
788
|
-
for (var i = 0; i < groups.length; i++) {
|
|
789
|
-
keys.push({
|
|
790
|
-
name: i,
|
|
791
|
-
prefix: null,
|
|
792
|
-
delimiter: null,
|
|
793
|
-
optional: false,
|
|
794
|
-
repeat: false,
|
|
795
|
-
partial: false,
|
|
796
|
-
asterisk: false,
|
|
797
|
-
pattern: null
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
return attachKeys(path, keys)
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
/**
|
|
806
|
-
* Transform an array into a regexp.
|
|
807
|
-
*
|
|
808
|
-
* @param {!Array} path
|
|
809
|
-
* @param {Array} keys
|
|
810
|
-
* @param {!Object} options
|
|
811
|
-
* @return {!RegExp}
|
|
812
|
-
*/
|
|
813
|
-
function arrayToRegexp (path, keys, options) {
|
|
814
|
-
var parts = [];
|
|
815
|
-
|
|
816
|
-
for (var i = 0; i < path.length; i++) {
|
|
817
|
-
parts.push(pathToRegexp(path[i], keys, options).source);
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
|
|
821
|
-
|
|
822
|
-
return attachKeys(regexp, keys)
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
/**
|
|
826
|
-
* Create a path regexp from string input.
|
|
827
|
-
*
|
|
828
|
-
* @param {string} path
|
|
829
|
-
* @param {!Array} keys
|
|
830
|
-
* @param {!Object} options
|
|
831
|
-
* @return {!RegExp}
|
|
832
|
-
*/
|
|
833
|
-
function stringToRegexp (path, keys, options) {
|
|
834
|
-
return tokensToRegExp(parse(path, options), keys, options)
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
/**
|
|
838
|
-
* Expose a function for taking tokens and returning a RegExp.
|
|
839
|
-
*
|
|
840
|
-
* @param {!Array} tokens
|
|
841
|
-
* @param {(Array|Object)=} keys
|
|
842
|
-
* @param {Object=} options
|
|
843
|
-
* @return {!RegExp}
|
|
844
|
-
*/
|
|
845
|
-
function tokensToRegExp (tokens, keys, options) {
|
|
846
|
-
if (!isarray(keys)) {
|
|
847
|
-
options = /** @type {!Object} */ (keys || options);
|
|
848
|
-
keys = [];
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
options = options || {};
|
|
852
|
-
|
|
853
|
-
var strict = options.strict;
|
|
854
|
-
var end = options.end !== false;
|
|
855
|
-
var route = '';
|
|
856
|
-
|
|
857
|
-
// Iterate over the tokens and create our regexp string.
|
|
858
|
-
for (var i = 0; i < tokens.length; i++) {
|
|
859
|
-
var token = tokens[i];
|
|
860
|
-
|
|
861
|
-
if (typeof token === 'string') {
|
|
862
|
-
route += escapeString(token);
|
|
863
|
-
} else {
|
|
864
|
-
var prefix = escapeString(token.prefix);
|
|
865
|
-
var capture = '(?:' + token.pattern + ')';
|
|
866
|
-
|
|
867
|
-
keys.push(token);
|
|
868
|
-
|
|
869
|
-
if (token.repeat) {
|
|
870
|
-
capture += '(?:' + prefix + capture + ')*';
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
if (token.optional) {
|
|
874
|
-
if (!token.partial) {
|
|
875
|
-
capture = '(?:' + prefix + '(' + capture + '))?';
|
|
876
|
-
} else {
|
|
877
|
-
capture = prefix + '(' + capture + ')?';
|
|
878
|
-
}
|
|
879
|
-
} else {
|
|
880
|
-
capture = prefix + '(' + capture + ')';
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
route += capture;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
var delimiter = escapeString(options.delimiter || '/');
|
|
888
|
-
var endsWithDelimiter = route.slice(-delimiter.length) === delimiter;
|
|
889
|
-
|
|
890
|
-
// In non-strict mode we allow a slash at the end of match. If the path to
|
|
891
|
-
// match already ends with a slash, we remove it for consistency. The slash
|
|
892
|
-
// is valid at the end of a path match, not in the middle. This is important
|
|
893
|
-
// in non-ending mode, where "/test/" shouldn't match "/test//route".
|
|
894
|
-
if (!strict) {
|
|
895
|
-
route = (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) + '(?:' + delimiter + '(?=$))?';
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
if (end) {
|
|
899
|
-
route += '$';
|
|
900
|
-
} else {
|
|
901
|
-
// In non-ending mode, we need the capturing groups to match as much as
|
|
902
|
-
// possible by using a positive lookahead to the end or next path segment.
|
|
903
|
-
route += strict && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)';
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
return attachKeys(new RegExp('^' + route, flags(options)), keys)
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
/**
|
|
910
|
-
* Normalize the given path string, returning a regular expression.
|
|
911
|
-
*
|
|
912
|
-
* An empty array can be passed in for the keys, which will hold the
|
|
913
|
-
* placeholder key descriptions. For example, using `/user/:id`, `keys` will
|
|
914
|
-
* contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
|
|
915
|
-
*
|
|
916
|
-
* @param {(string|RegExp|Array)} path
|
|
917
|
-
* @param {(Array|Object)=} keys
|
|
918
|
-
* @param {Object=} options
|
|
919
|
-
* @return {!RegExp}
|
|
920
|
-
*/
|
|
921
|
-
function pathToRegexp (path, keys, options) {
|
|
922
|
-
if (!isarray(keys)) {
|
|
923
|
-
options = /** @type {!Object} */ (keys || options);
|
|
924
|
-
keys = [];
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
options = options || {};
|
|
928
|
-
|
|
929
|
-
if (path instanceof RegExp) {
|
|
930
|
-
return regexpToRegexp(path, /** @type {!Array} */ (keys))
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
if (isarray(path)) {
|
|
934
|
-
return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options)
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options)
|
|
938
|
-
}
|
|
939
|
-
pathToRegexp_1.parse = parse_1;
|
|
940
|
-
pathToRegexp_1.compile = compile_1;
|
|
941
|
-
pathToRegexp_1.tokensToFunction = tokensToFunction_1;
|
|
942
|
-
pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
|
|
943
|
-
|
|
944
|
-
/* */
|
|
945
|
-
|
|
946
|
-
// $flow-disable-line
|
|
947
|
-
const regexpCompileCache = Object.create(null);
|
|
948
|
-
|
|
949
|
-
function fillParams (
|
|
950
|
-
path,
|
|
951
|
-
params,
|
|
952
|
-
routeMsg
|
|
953
|
-
) {
|
|
954
|
-
params = params || {};
|
|
955
|
-
try {
|
|
956
|
-
const filler =
|
|
957
|
-
regexpCompileCache[path] ||
|
|
958
|
-
(regexpCompileCache[path] = pathToRegexp_1.compile(path));
|
|
959
|
-
|
|
960
|
-
// Fix #2505 resolving asterisk routes { name: 'not-found', params: { pathMatch: '/not-found' }}
|
|
961
|
-
// and fix #3106 so that you can work with location descriptor object having params.pathMatch equal to empty string
|
|
962
|
-
if (typeof params.pathMatch === 'string') params[0] = params.pathMatch;
|
|
963
|
-
|
|
964
|
-
return filler(params, { pretty: true })
|
|
965
|
-
} catch (e) {
|
|
966
|
-
{
|
|
967
|
-
// Fix #3072 no warn if `pathMatch` is string
|
|
968
|
-
warn(typeof params.pathMatch === 'string', `missing param for ${routeMsg}: ${e.message}`);
|
|
969
|
-
}
|
|
970
|
-
return ''
|
|
971
|
-
} finally {
|
|
972
|
-
// delete the 0 if it was added
|
|
973
|
-
delete params[0];
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
/* */
|
|
978
|
-
|
|
979
|
-
function normalizeLocation (
|
|
980
|
-
raw,
|
|
981
|
-
current,
|
|
982
|
-
append,
|
|
983
|
-
router
|
|
984
|
-
) {
|
|
985
|
-
let next = typeof raw === 'string' ? { path: raw } : raw;
|
|
986
|
-
// named target
|
|
987
|
-
if (next._normalized) {
|
|
988
|
-
return next
|
|
989
|
-
} else if (next.name) {
|
|
990
|
-
next = extend({}, raw);
|
|
991
|
-
const params = next.params;
|
|
992
|
-
if (params && typeof params === 'object') {
|
|
993
|
-
next.params = extend({}, params);
|
|
994
|
-
}
|
|
995
|
-
return next
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
// relative params
|
|
999
|
-
if (!next.path && next.params && current) {
|
|
1000
|
-
next = extend({}, next);
|
|
1001
|
-
next._normalized = true;
|
|
1002
|
-
const params = extend(extend({}, current.params), next.params);
|
|
1003
|
-
if (current.name) {
|
|
1004
|
-
next.name = current.name;
|
|
1005
|
-
next.params = params;
|
|
1006
|
-
} else if (current.matched.length) {
|
|
1007
|
-
const rawPath = current.matched[current.matched.length - 1].path;
|
|
1008
|
-
next.path = fillParams(rawPath, params, `path ${current.path}`);
|
|
1009
|
-
} else {
|
|
1010
|
-
warn(false, `relative params navigation requires a current route.`);
|
|
1011
|
-
}
|
|
1012
|
-
return next
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
const parsedPath = parsePath(next.path || '');
|
|
1016
|
-
const basePath = (current && current.path) || '/';
|
|
1017
|
-
const path = parsedPath.path
|
|
1018
|
-
? resolvePath(parsedPath.path, basePath, append || next.append)
|
|
1019
|
-
: basePath;
|
|
1020
|
-
|
|
1021
|
-
const query = resolveQuery(
|
|
1022
|
-
parsedPath.query,
|
|
1023
|
-
next.query,
|
|
1024
|
-
router && router.options.parseQuery
|
|
1025
|
-
);
|
|
1026
|
-
|
|
1027
|
-
let hash = next.hash || parsedPath.hash;
|
|
1028
|
-
if (hash && hash.charAt(0) !== '#') {
|
|
1029
|
-
hash = `#${hash}`;
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
return {
|
|
1033
|
-
_normalized: true,
|
|
1034
|
-
path,
|
|
1035
|
-
query,
|
|
1036
|
-
hash
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
/* */
|
|
1041
|
-
|
|
1042
|
-
// work around weird flow bug
|
|
1043
|
-
const toTypes = [String, Object];
|
|
1044
|
-
const eventTypes = [String, Array];
|
|
1045
|
-
|
|
1046
|
-
const noop = () => {};
|
|
1047
|
-
|
|
1048
|
-
let warnedCustomSlot;
|
|
1049
|
-
let warnedTagProp;
|
|
1050
|
-
let warnedEventProp;
|
|
1051
|
-
|
|
1052
|
-
var Link = {
|
|
1053
|
-
name: 'RouterLink',
|
|
1054
|
-
props: {
|
|
1055
|
-
to: {
|
|
1056
|
-
type: toTypes,
|
|
1057
|
-
required: true
|
|
1058
|
-
},
|
|
1059
|
-
tag: {
|
|
1060
|
-
type: String,
|
|
1061
|
-
default: 'a'
|
|
1062
|
-
},
|
|
1063
|
-
custom: Boolean,
|
|
1064
|
-
exact: Boolean,
|
|
1065
|
-
exactPath: Boolean,
|
|
1066
|
-
append: Boolean,
|
|
1067
|
-
replace: Boolean,
|
|
1068
|
-
activeClass: String,
|
|
1069
|
-
exactActiveClass: String,
|
|
1070
|
-
ariaCurrentValue: {
|
|
1071
|
-
type: String,
|
|
1072
|
-
default: 'page'
|
|
1073
|
-
},
|
|
1074
|
-
event: {
|
|
1075
|
-
type: eventTypes,
|
|
1076
|
-
default: 'click'
|
|
1077
|
-
}
|
|
1078
|
-
},
|
|
1079
|
-
render (h) {
|
|
1080
|
-
const router = this.$router;
|
|
1081
|
-
const current = this.$route;
|
|
1082
|
-
const { location, route, href } = router.resolve(
|
|
1083
|
-
this.to,
|
|
1084
|
-
current,
|
|
1085
|
-
this.append
|
|
1086
|
-
);
|
|
1087
|
-
|
|
1088
|
-
const classes = {};
|
|
1089
|
-
const globalActiveClass = router.options.linkActiveClass;
|
|
1090
|
-
const globalExactActiveClass = router.options.linkExactActiveClass;
|
|
1091
|
-
// Support global empty active class
|
|
1092
|
-
const activeClassFallback =
|
|
1093
|
-
globalActiveClass == null ? 'router-link-active' : globalActiveClass;
|
|
1094
|
-
const exactActiveClassFallback =
|
|
1095
|
-
globalExactActiveClass == null
|
|
1096
|
-
? 'router-link-exact-active'
|
|
1097
|
-
: globalExactActiveClass;
|
|
1098
|
-
const activeClass =
|
|
1099
|
-
this.activeClass == null ? activeClassFallback : this.activeClass;
|
|
1100
|
-
const exactActiveClass =
|
|
1101
|
-
this.exactActiveClass == null
|
|
1102
|
-
? exactActiveClassFallback
|
|
1103
|
-
: this.exactActiveClass;
|
|
1104
|
-
|
|
1105
|
-
const compareTarget = route.redirectedFrom
|
|
1106
|
-
? createRoute(null, normalizeLocation(route.redirectedFrom), null, router)
|
|
1107
|
-
: route;
|
|
1108
|
-
|
|
1109
|
-
classes[exactActiveClass] = isSameRoute(current, compareTarget, this.exactPath);
|
|
1110
|
-
classes[activeClass] = this.exact || this.exactPath
|
|
1111
|
-
? classes[exactActiveClass]
|
|
1112
|
-
: isIncludedRoute(current, compareTarget);
|
|
1113
|
-
|
|
1114
|
-
const ariaCurrentValue = classes[exactActiveClass] ? this.ariaCurrentValue : null;
|
|
1115
|
-
|
|
1116
|
-
const handler = e => {
|
|
1117
|
-
if (guardEvent(e)) {
|
|
1118
|
-
if (this.replace) {
|
|
1119
|
-
router.replace(location, noop);
|
|
1120
|
-
} else {
|
|
1121
|
-
router.push(location, noop);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
};
|
|
1125
|
-
|
|
1126
|
-
const on = { click: guardEvent };
|
|
1127
|
-
if (Array.isArray(this.event)) {
|
|
1128
|
-
this.event.forEach(e => {
|
|
1129
|
-
on[e] = handler;
|
|
1130
|
-
});
|
|
1131
|
-
} else {
|
|
1132
|
-
on[this.event] = handler;
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
const data = { class: classes };
|
|
1136
|
-
|
|
1137
|
-
const scopedSlot =
|
|
1138
|
-
!this.$scopedSlots.$hasNormal &&
|
|
1139
|
-
this.$scopedSlots.default &&
|
|
1140
|
-
this.$scopedSlots.default({
|
|
1141
|
-
href,
|
|
1142
|
-
route,
|
|
1143
|
-
navigate: handler,
|
|
1144
|
-
isActive: classes[activeClass],
|
|
1145
|
-
isExactActive: classes[exactActiveClass]
|
|
1146
|
-
});
|
|
1147
|
-
|
|
1148
|
-
if (scopedSlot) {
|
|
1149
|
-
if (!this.custom) {
|
|
1150
|
-
!warnedCustomSlot && warn(false, 'In Kdu Router 4, the k-slot API will by default wrap its content with an <a> element. Use the custom prop to remove this warning:\n<router-link k-slot="{ navigate, href }" custom></router-link>\n');
|
|
1151
|
-
warnedCustomSlot = true;
|
|
1152
|
-
}
|
|
1153
|
-
if (scopedSlot.length === 1) {
|
|
1154
|
-
return scopedSlot[0]
|
|
1155
|
-
} else if (scopedSlot.length > 1 || !scopedSlot.length) {
|
|
1156
|
-
{
|
|
1157
|
-
warn(
|
|
1158
|
-
false,
|
|
1159
|
-
`<router-link> with to="${
|
|
1160
|
-
this.to
|
|
1161
|
-
}" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element.`
|
|
1162
|
-
);
|
|
1163
|
-
}
|
|
1164
|
-
return scopedSlot.length === 0 ? h() : h('span', {}, scopedSlot)
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
{
|
|
1169
|
-
if ('tag' in this.$options.propsData && !warnedTagProp) {
|
|
1170
|
-
warn(
|
|
1171
|
-
false,
|
|
1172
|
-
`<router-link>'s tag prop is deprecated and has been removed in Kdu Router 4. Use the k-slot API to remove this warning: https://kdujs-router.web.app/guide/migration/#removal-of-event-and-tag-props-in-router-link.`
|
|
1173
|
-
);
|
|
1174
|
-
warnedTagProp = true;
|
|
1175
|
-
}
|
|
1176
|
-
if ('event' in this.$options.propsData && !warnedEventProp) {
|
|
1177
|
-
warn(
|
|
1178
|
-
false,
|
|
1179
|
-
`<router-link>'s event prop is deprecated and has been removed in Kdu Router 4. Use the k-slot API to remove this warning: https://kdujs-router.web.app/guide/migration/#removal-of-event-and-tag-props-in-router-link.`
|
|
1180
|
-
);
|
|
1181
|
-
warnedEventProp = true;
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
if (this.tag === 'a') {
|
|
1186
|
-
data.on = on;
|
|
1187
|
-
data.attrs = { href, 'aria-current': ariaCurrentValue };
|
|
1188
|
-
} else {
|
|
1189
|
-
// find the first <a> child and apply listener and href
|
|
1190
|
-
const a = findAnchor(this.$slots.default);
|
|
1191
|
-
if (a) {
|
|
1192
|
-
// in case the <a> is a static node
|
|
1193
|
-
a.isStatic = false;
|
|
1194
|
-
const aData = (a.data = extend({}, a.data));
|
|
1195
|
-
aData.on = aData.on || {};
|
|
1196
|
-
// transform existing events in both objects into arrays so we can push later
|
|
1197
|
-
for (const event in aData.on) {
|
|
1198
|
-
const handler = aData.on[event];
|
|
1199
|
-
if (event in on) {
|
|
1200
|
-
aData.on[event] = Array.isArray(handler) ? handler : [handler];
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
// append new listeners for router-link
|
|
1204
|
-
for (const event in on) {
|
|
1205
|
-
if (event in aData.on) {
|
|
1206
|
-
// on[event] is always a function
|
|
1207
|
-
aData.on[event].push(on[event]);
|
|
1208
|
-
} else {
|
|
1209
|
-
aData.on[event] = handler;
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
const aAttrs = (a.data.attrs = extend({}, a.data.attrs));
|
|
1214
|
-
aAttrs.href = href;
|
|
1215
|
-
aAttrs['aria-current'] = ariaCurrentValue;
|
|
1216
|
-
} else {
|
|
1217
|
-
// doesn't have <a> child, apply listener to self
|
|
1218
|
-
data.on = on;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
return h(this.tag, data, this.$slots.default)
|
|
1223
|
-
}
|
|
1224
|
-
};
|
|
1225
|
-
|
|
1226
|
-
function guardEvent (e) {
|
|
1227
|
-
// don't redirect with control keys
|
|
1228
|
-
if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return
|
|
1229
|
-
// don't redirect when preventDefault called
|
|
1230
|
-
if (e.defaultPrevented) return
|
|
1231
|
-
// don't redirect on right click
|
|
1232
|
-
if (e.button !== undefined && e.button !== 0) return
|
|
1233
|
-
// don't redirect if `target="_blank"`
|
|
1234
|
-
if (e.currentTarget && e.currentTarget.getAttribute) {
|
|
1235
|
-
const target = e.currentTarget.getAttribute('target');
|
|
1236
|
-
if (/\b_blank\b/i.test(target)) return
|
|
1237
|
-
}
|
|
1238
|
-
// this may be a Weex event which doesn't have this method
|
|
1239
|
-
if (e.preventDefault) {
|
|
1240
|
-
e.preventDefault();
|
|
1241
|
-
}
|
|
1242
|
-
return true
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
function findAnchor (children) {
|
|
1246
|
-
if (children) {
|
|
1247
|
-
let child;
|
|
1248
|
-
for (let i = 0; i < children.length; i++) {
|
|
1249
|
-
child = children[i];
|
|
1250
|
-
if (child.tag === 'a') {
|
|
1251
|
-
return child
|
|
1252
|
-
}
|
|
1253
|
-
if (child.children && (child = findAnchor(child.children))) {
|
|
1254
|
-
return child
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
let _Kdu;
|
|
1261
|
-
|
|
1262
|
-
function install (Kdu) {
|
|
1263
|
-
if (install.installed && _Kdu === Kdu) return
|
|
1264
|
-
install.installed = true;
|
|
1265
|
-
|
|
1266
|
-
_Kdu = Kdu;
|
|
1267
|
-
|
|
1268
|
-
const isDef = v => v !== undefined;
|
|
1269
|
-
|
|
1270
|
-
const registerInstance = (vm, callVal) => {
|
|
1271
|
-
let i = vm.$options._parentKnode;
|
|
1272
|
-
if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
|
|
1273
|
-
i(vm, callVal);
|
|
1274
|
-
}
|
|
1275
|
-
};
|
|
1276
|
-
|
|
1277
|
-
Kdu.mixin({
|
|
1278
|
-
beforeCreate () {
|
|
1279
|
-
if (isDef(this.$options.router)) {
|
|
1280
|
-
this._routerRoot = this;
|
|
1281
|
-
this._router = this.$options.router;
|
|
1282
|
-
this._router.init(this);
|
|
1283
|
-
Kdu.util.defineReactive(this, '_route', this._router.history.current);
|
|
1284
|
-
} else {
|
|
1285
|
-
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this;
|
|
1286
|
-
}
|
|
1287
|
-
registerInstance(this, this);
|
|
1288
|
-
},
|
|
1289
|
-
destroyed () {
|
|
1290
|
-
registerInstance(this);
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1293
|
-
|
|
1294
|
-
Object.defineProperty(Kdu.prototype, '$router', {
|
|
1295
|
-
get () { return this._routerRoot._router }
|
|
1296
|
-
});
|
|
1297
|
-
|
|
1298
|
-
Object.defineProperty(Kdu.prototype, '$route', {
|
|
1299
|
-
get () { return this._routerRoot._route }
|
|
1300
|
-
});
|
|
1301
|
-
|
|
1302
|
-
Kdu.component('RouterView', View);
|
|
1303
|
-
Kdu.component('RouterLink', Link);
|
|
1304
|
-
|
|
1305
|
-
const strats = Kdu.config.optionMergeStrategies;
|
|
1306
|
-
// use the same hook merging strategy for route hooks
|
|
1307
|
-
strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created;
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
/* */
|
|
1311
|
-
|
|
1312
|
-
const inBrowser = typeof window !== 'undefined';
|
|
1313
|
-
|
|
1314
|
-
/* */
|
|
1315
|
-
|
|
1316
|
-
function createRouteMap (
|
|
1317
|
-
routes,
|
|
1318
|
-
oldPathList,
|
|
1319
|
-
oldPathMap,
|
|
1320
|
-
oldNameMap,
|
|
1321
|
-
parentRoute
|
|
1322
|
-
) {
|
|
1323
|
-
// the path list is used to control path matching priority
|
|
1324
|
-
const pathList = oldPathList || [];
|
|
1325
|
-
// $flow-disable-line
|
|
1326
|
-
const pathMap = oldPathMap || Object.create(null);
|
|
1327
|
-
// $flow-disable-line
|
|
1328
|
-
const nameMap = oldNameMap || Object.create(null);
|
|
1329
|
-
|
|
1330
|
-
routes.forEach(route => {
|
|
1331
|
-
addRouteRecord(pathList, pathMap, nameMap, route, parentRoute);
|
|
1332
|
-
});
|
|
1333
|
-
|
|
1334
|
-
// ensure wildcard routes are always at the end
|
|
1335
|
-
for (let i = 0, l = pathList.length; i < l; i++) {
|
|
1336
|
-
if (pathList[i] === '*') {
|
|
1337
|
-
pathList.push(pathList.splice(i, 1)[0]);
|
|
1338
|
-
l--;
|
|
1339
|
-
i--;
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
{
|
|
1344
|
-
// warn if routes do not include leading slashes
|
|
1345
|
-
const found = pathList
|
|
1346
|
-
// check for missing leading slash
|
|
1347
|
-
.filter(path => path && path.charAt(0) !== '*' && path.charAt(0) !== '/');
|
|
1348
|
-
|
|
1349
|
-
if (found.length > 0) {
|
|
1350
|
-
const pathNames = found.map(path => `- ${path}`).join('\n');
|
|
1351
|
-
warn(false, `Non-nested routes must include a leading slash character. Fix the following routes: \n${pathNames}`);
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
return {
|
|
1356
|
-
pathList,
|
|
1357
|
-
pathMap,
|
|
1358
|
-
nameMap
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
function addRouteRecord (
|
|
1363
|
-
pathList,
|
|
1364
|
-
pathMap,
|
|
1365
|
-
nameMap,
|
|
1366
|
-
route,
|
|
1367
|
-
parent,
|
|
1368
|
-
matchAs
|
|
1369
|
-
) {
|
|
1370
|
-
const { path, name } = route;
|
|
1371
|
-
{
|
|
1372
|
-
assert(path != null, `"path" is required in a route configuration.`);
|
|
1373
|
-
assert(
|
|
1374
|
-
typeof route.component !== 'string',
|
|
1375
|
-
`route config "component" for path: ${String(
|
|
1376
|
-
path || name
|
|
1377
|
-
)} cannot be a ` + `string id. Use an actual component instead.`
|
|
1378
|
-
);
|
|
1379
|
-
|
|
1380
|
-
warn(
|
|
1381
|
-
// eslint-disable-next-line no-control-regex
|
|
1382
|
-
!/[^\u0000-\u007F]+/.test(path),
|
|
1383
|
-
`Route with path "${path}" contains unencoded characters, make sure ` +
|
|
1384
|
-
`your path is correctly encoded before passing it to the router. Use ` +
|
|
1385
|
-
`encodeURI to encode static segments of your path.`
|
|
1386
|
-
);
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
const pathToRegexpOptions =
|
|
1390
|
-
route.pathToRegexpOptions || {};
|
|
1391
|
-
const normalizedPath = normalizePath(path, parent, pathToRegexpOptions.strict);
|
|
1392
|
-
|
|
1393
|
-
if (typeof route.caseSensitive === 'boolean') {
|
|
1394
|
-
pathToRegexpOptions.sensitive = route.caseSensitive;
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
const record = {
|
|
1398
|
-
path: normalizedPath,
|
|
1399
|
-
regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
|
|
1400
|
-
components: route.components || { default: route.component },
|
|
1401
|
-
alias: route.alias
|
|
1402
|
-
? typeof route.alias === 'string'
|
|
1403
|
-
? [route.alias]
|
|
1404
|
-
: route.alias
|
|
1405
|
-
: [],
|
|
1406
|
-
instances: {},
|
|
1407
|
-
enteredCbs: {},
|
|
1408
|
-
name,
|
|
1409
|
-
parent,
|
|
1410
|
-
matchAs,
|
|
1411
|
-
redirect: route.redirect,
|
|
1412
|
-
beforeEnter: route.beforeEnter,
|
|
1413
|
-
meta: route.meta || {},
|
|
1414
|
-
props:
|
|
1415
|
-
route.props == null
|
|
1416
|
-
? {}
|
|
1417
|
-
: route.components
|
|
1418
|
-
? route.props
|
|
1419
|
-
: { default: route.props }
|
|
1420
|
-
};
|
|
1421
|
-
|
|
1422
|
-
if (route.children) {
|
|
1423
|
-
// Warn if route is named, does not redirect and has a default child route.
|
|
1424
|
-
// If users navigate to this route by name, the default child will
|
|
1425
|
-
// not be rendered (GH Issue #629)
|
|
1426
|
-
{
|
|
1427
|
-
if (
|
|
1428
|
-
route.name &&
|
|
1429
|
-
!route.redirect &&
|
|
1430
|
-
route.children.some(child => /^\/?$/.test(child.path))
|
|
1431
|
-
) {
|
|
1432
|
-
warn(
|
|
1433
|
-
false,
|
|
1434
|
-
`Named Route '${route.name}' has a default child route. ` +
|
|
1435
|
-
`When navigating to this named route (:to="{name: '${
|
|
1436
|
-
route.name
|
|
1437
|
-
}'}"), ` +
|
|
1438
|
-
`the default child route will not be rendered. Remove the name from ` +
|
|
1439
|
-
`this route and use the name of the default child route for named ` +
|
|
1440
|
-
`links instead.`
|
|
1441
|
-
);
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
route.children.forEach(child => {
|
|
1445
|
-
const childMatchAs = matchAs
|
|
1446
|
-
? cleanPath(`${matchAs}/${child.path}`)
|
|
1447
|
-
: undefined;
|
|
1448
|
-
addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs);
|
|
1449
|
-
});
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
if (!pathMap[record.path]) {
|
|
1453
|
-
pathList.push(record.path);
|
|
1454
|
-
pathMap[record.path] = record;
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
if (route.alias !== undefined) {
|
|
1458
|
-
const aliases = Array.isArray(route.alias) ? route.alias : [route.alias];
|
|
1459
|
-
for (let i = 0; i < aliases.length; ++i) {
|
|
1460
|
-
const alias = aliases[i];
|
|
1461
|
-
if (alias === path) {
|
|
1462
|
-
warn(
|
|
1463
|
-
false,
|
|
1464
|
-
`Found an alias with the same value as the path: "${path}". You have to remove that alias. It will be ignored in development.`
|
|
1465
|
-
);
|
|
1466
|
-
// skip in dev to make it work
|
|
1467
|
-
continue
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
const aliasRoute = {
|
|
1471
|
-
path: alias,
|
|
1472
|
-
children: route.children
|
|
1473
|
-
};
|
|
1474
|
-
addRouteRecord(
|
|
1475
|
-
pathList,
|
|
1476
|
-
pathMap,
|
|
1477
|
-
nameMap,
|
|
1478
|
-
aliasRoute,
|
|
1479
|
-
parent,
|
|
1480
|
-
record.path || '/' // matchAs
|
|
1481
|
-
);
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
if (name) {
|
|
1486
|
-
if (!nameMap[name]) {
|
|
1487
|
-
nameMap[name] = record;
|
|
1488
|
-
} else if (!matchAs) {
|
|
1489
|
-
warn(
|
|
1490
|
-
false,
|
|
1491
|
-
`Duplicate named routes definition: ` +
|
|
1492
|
-
`{ name: "${name}", path: "${record.path}" }`
|
|
1493
|
-
);
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
function compileRouteRegex (
|
|
1499
|
-
path,
|
|
1500
|
-
pathToRegexpOptions
|
|
1501
|
-
) {
|
|
1502
|
-
const regex = pathToRegexp_1(path, [], pathToRegexpOptions);
|
|
1503
|
-
{
|
|
1504
|
-
const keys = Object.create(null);
|
|
1505
|
-
regex.keys.forEach(key => {
|
|
1506
|
-
warn(
|
|
1507
|
-
!keys[key.name],
|
|
1508
|
-
`Duplicate param keys in route with path: "${path}"`
|
|
1509
|
-
);
|
|
1510
|
-
keys[key.name] = true;
|
|
1511
|
-
});
|
|
1512
|
-
}
|
|
1513
|
-
return regex
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
function normalizePath (
|
|
1517
|
-
path,
|
|
1518
|
-
parent,
|
|
1519
|
-
strict
|
|
1520
|
-
) {
|
|
1521
|
-
if (!strict) path = path.replace(/\/$/, '');
|
|
1522
|
-
if (path[0] === '/') return path
|
|
1523
|
-
if (parent == null) return path
|
|
1524
|
-
return cleanPath(`${parent.path}/${path}`)
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
/* */
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
function createMatcher (
|
|
1532
|
-
routes,
|
|
1533
|
-
router
|
|
1534
|
-
) {
|
|
1535
|
-
const { pathList, pathMap, nameMap } = createRouteMap(routes);
|
|
1536
|
-
|
|
1537
|
-
function addRoutes (routes) {
|
|
1538
|
-
createRouteMap(routes, pathList, pathMap, nameMap);
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
function addRoute (parentOrRoute, route) {
|
|
1542
|
-
const parent = (typeof parentOrRoute !== 'object') ? nameMap[parentOrRoute] : undefined;
|
|
1543
|
-
// $flow-disable-line
|
|
1544
|
-
createRouteMap([route || parentOrRoute], pathList, pathMap, nameMap, parent);
|
|
1545
|
-
|
|
1546
|
-
// add aliases of parent
|
|
1547
|
-
if (parent && parent.alias.length) {
|
|
1548
|
-
createRouteMap(
|
|
1549
|
-
// $flow-disable-line route is defined if parent is
|
|
1550
|
-
parent.alias.map(alias => ({ path: alias, children: [route] })),
|
|
1551
|
-
pathList,
|
|
1552
|
-
pathMap,
|
|
1553
|
-
nameMap,
|
|
1554
|
-
parent
|
|
1555
|
-
);
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
function getRoutes () {
|
|
1560
|
-
return pathList.map(path => pathMap[path])
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
function match (
|
|
1564
|
-
raw,
|
|
1565
|
-
currentRoute,
|
|
1566
|
-
redirectedFrom
|
|
1567
|
-
) {
|
|
1568
|
-
const location = normalizeLocation(raw, currentRoute, false, router);
|
|
1569
|
-
const { name } = location;
|
|
1570
|
-
|
|
1571
|
-
if (name) {
|
|
1572
|
-
const record = nameMap[name];
|
|
1573
|
-
{
|
|
1574
|
-
warn(record, `Route with name '${name}' does not exist`);
|
|
1575
|
-
}
|
|
1576
|
-
if (!record) return _createRoute(null, location)
|
|
1577
|
-
const paramNames = record.regex.keys
|
|
1578
|
-
.filter(key => !key.optional)
|
|
1579
|
-
.map(key => key.name);
|
|
1580
|
-
|
|
1581
|
-
if (typeof location.params !== 'object') {
|
|
1582
|
-
location.params = {};
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
if (currentRoute && typeof currentRoute.params === 'object') {
|
|
1586
|
-
for (const key in currentRoute.params) {
|
|
1587
|
-
if (!(key in location.params) && paramNames.indexOf(key) > -1) {
|
|
1588
|
-
location.params[key] = currentRoute.params[key];
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
location.path = fillParams(record.path, location.params, `named route "${name}"`);
|
|
1594
|
-
return _createRoute(record, location, redirectedFrom)
|
|
1595
|
-
} else if (location.path) {
|
|
1596
|
-
location.params = {};
|
|
1597
|
-
for (let i = 0; i < pathList.length; i++) {
|
|
1598
|
-
const path = pathList[i];
|
|
1599
|
-
const record = pathMap[path];
|
|
1600
|
-
if (matchRoute(record.regex, location.path, location.params)) {
|
|
1601
|
-
return _createRoute(record, location, redirectedFrom)
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
// no match
|
|
1606
|
-
return _createRoute(null, location)
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
function redirect (
|
|
1610
|
-
record,
|
|
1611
|
-
location
|
|
1612
|
-
) {
|
|
1613
|
-
const originalRedirect = record.redirect;
|
|
1614
|
-
let redirect = typeof originalRedirect === 'function'
|
|
1615
|
-
? originalRedirect(createRoute(record, location, null, router))
|
|
1616
|
-
: originalRedirect;
|
|
1617
|
-
|
|
1618
|
-
if (typeof redirect === 'string') {
|
|
1619
|
-
redirect = { path: redirect };
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
if (!redirect || typeof redirect !== 'object') {
|
|
1623
|
-
{
|
|
1624
|
-
warn(
|
|
1625
|
-
false, `invalid redirect option: ${JSON.stringify(redirect)}`
|
|
1626
|
-
);
|
|
1627
|
-
}
|
|
1628
|
-
return _createRoute(null, location)
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
const re = redirect;
|
|
1632
|
-
const { name, path } = re;
|
|
1633
|
-
let { query, hash, params } = location;
|
|
1634
|
-
query = re.hasOwnProperty('query') ? re.query : query;
|
|
1635
|
-
hash = re.hasOwnProperty('hash') ? re.hash : hash;
|
|
1636
|
-
params = re.hasOwnProperty('params') ? re.params : params;
|
|
1637
|
-
|
|
1638
|
-
if (name) {
|
|
1639
|
-
// resolved named direct
|
|
1640
|
-
const targetRecord = nameMap[name];
|
|
1641
|
-
{
|
|
1642
|
-
assert(targetRecord, `redirect failed: named route "${name}" not found.`);
|
|
1643
|
-
}
|
|
1644
|
-
return match({
|
|
1645
|
-
_normalized: true,
|
|
1646
|
-
name,
|
|
1647
|
-
query,
|
|
1648
|
-
hash,
|
|
1649
|
-
params
|
|
1650
|
-
}, undefined, location)
|
|
1651
|
-
} else if (path) {
|
|
1652
|
-
// 1. resolve relative redirect
|
|
1653
|
-
const rawPath = resolveRecordPath(path, record);
|
|
1654
|
-
// 2. resolve params
|
|
1655
|
-
const resolvedPath = fillParams(rawPath, params, `redirect route with path "${rawPath}"`);
|
|
1656
|
-
// 3. rematch with existing query and hash
|
|
1657
|
-
return match({
|
|
1658
|
-
_normalized: true,
|
|
1659
|
-
path: resolvedPath,
|
|
1660
|
-
query,
|
|
1661
|
-
hash
|
|
1662
|
-
}, undefined, location)
|
|
1663
|
-
} else {
|
|
1664
|
-
{
|
|
1665
|
-
warn(false, `invalid redirect option: ${JSON.stringify(redirect)}`);
|
|
1666
|
-
}
|
|
1667
|
-
return _createRoute(null, location)
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
function alias (
|
|
1672
|
-
record,
|
|
1673
|
-
location,
|
|
1674
|
-
matchAs
|
|
1675
|
-
) {
|
|
1676
|
-
const aliasedPath = fillParams(matchAs, location.params, `aliased route with path "${matchAs}"`);
|
|
1677
|
-
const aliasedMatch = match({
|
|
1678
|
-
_normalized: true,
|
|
1679
|
-
path: aliasedPath
|
|
1680
|
-
});
|
|
1681
|
-
if (aliasedMatch) {
|
|
1682
|
-
const matched = aliasedMatch.matched;
|
|
1683
|
-
const aliasedRecord = matched[matched.length - 1];
|
|
1684
|
-
location.params = aliasedMatch.params;
|
|
1685
|
-
return _createRoute(aliasedRecord, location)
|
|
1686
|
-
}
|
|
1687
|
-
return _createRoute(null, location)
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
function _createRoute (
|
|
1691
|
-
record,
|
|
1692
|
-
location,
|
|
1693
|
-
redirectedFrom
|
|
1694
|
-
) {
|
|
1695
|
-
if (record && record.redirect) {
|
|
1696
|
-
return redirect(record, redirectedFrom || location)
|
|
1697
|
-
}
|
|
1698
|
-
if (record && record.matchAs) {
|
|
1699
|
-
return alias(record, location, record.matchAs)
|
|
1700
|
-
}
|
|
1701
|
-
return createRoute(record, location, redirectedFrom, router)
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
return {
|
|
1705
|
-
match,
|
|
1706
|
-
addRoute,
|
|
1707
|
-
getRoutes,
|
|
1708
|
-
addRoutes
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
function matchRoute (
|
|
1713
|
-
regex,
|
|
1714
|
-
path,
|
|
1715
|
-
params
|
|
1716
|
-
) {
|
|
1717
|
-
const m = path.match(regex);
|
|
1718
|
-
|
|
1719
|
-
if (!m) {
|
|
1720
|
-
return false
|
|
1721
|
-
} else if (!params) {
|
|
1722
|
-
return true
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
for (let i = 1, len = m.length; i < len; ++i) {
|
|
1726
|
-
const key = regex.keys[i - 1];
|
|
1727
|
-
if (key) {
|
|
1728
|
-
// Fix #1994: using * with props: true generates a param named 0
|
|
1729
|
-
params[key.name || 'pathMatch'] = typeof m[i] === 'string' ? decode(m[i]) : m[i];
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
return true
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
function resolveRecordPath (path, record) {
|
|
1737
|
-
return resolvePath(path, record.parent ? record.parent.path : '/', true)
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
/* */
|
|
1741
|
-
|
|
1742
|
-
// use User Timing api (if present) for more accurate key precision
|
|
1743
|
-
const Time =
|
|
1744
|
-
inBrowser && window.performance && window.performance.now
|
|
1745
|
-
? window.performance
|
|
1746
|
-
: Date;
|
|
1747
|
-
|
|
1748
|
-
function genStateKey () {
|
|
1749
|
-
return Time.now().toFixed(3)
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
let _key = genStateKey();
|
|
1753
|
-
|
|
1754
|
-
function getStateKey () {
|
|
1755
|
-
return _key
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
function setStateKey (key) {
|
|
1759
|
-
return (_key = key)
|
|
1760
|
-
}
|
|
1761
|
-
|
|
1762
|
-
/* */
|
|
1763
|
-
|
|
1764
|
-
const positionStore = Object.create(null);
|
|
1765
|
-
|
|
1766
|
-
function setupScroll () {
|
|
1767
|
-
// Prevent browser scroll behavior on History popstate
|
|
1768
|
-
if ('scrollRestoration' in window.history) {
|
|
1769
|
-
window.history.scrollRestoration = 'manual';
|
|
1770
|
-
}
|
|
1771
|
-
// Fix for #1585 for Firefox
|
|
1772
|
-
// Fix for #2195 Add optional third attribute to workaround a bug in safari https://bugs.webkit.org/show_bug.cgi?id=182678
|
|
1773
|
-
// Fix for #2774 Support for apps loaded from Windows file shares not mapped to network drives: replaced location.origin with
|
|
1774
|
-
// window.location.protocol + '//' + window.location.host
|
|
1775
|
-
// location.host contains the port and location.hostname doesn't
|
|
1776
|
-
const protocolAndPath = window.location.protocol + '//' + window.location.host;
|
|
1777
|
-
const absolutePath = window.location.href.replace(protocolAndPath, '');
|
|
1778
|
-
// preserve existing history state as it could be overriden by the user
|
|
1779
|
-
const stateCopy = extend({}, window.history.state);
|
|
1780
|
-
stateCopy.key = getStateKey();
|
|
1781
|
-
window.history.replaceState(stateCopy, '', absolutePath);
|
|
1782
|
-
window.addEventListener('popstate', handlePopState);
|
|
1783
|
-
return () => {
|
|
1784
|
-
window.removeEventListener('popstate', handlePopState);
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
function handleScroll (
|
|
1789
|
-
router,
|
|
1790
|
-
to,
|
|
1791
|
-
from,
|
|
1792
|
-
isPop
|
|
1793
|
-
) {
|
|
1794
|
-
if (!router.app) {
|
|
1795
|
-
return
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
|
-
const behavior = router.options.scrollBehavior;
|
|
1799
|
-
if (!behavior) {
|
|
1800
|
-
return
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
|
-
{
|
|
1804
|
-
assert(typeof behavior === 'function', `scrollBehavior must be a function`);
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
// wait until re-render finishes before scrolling
|
|
1808
|
-
router.app.$nextTick(() => {
|
|
1809
|
-
const position = getScrollPosition();
|
|
1810
|
-
const shouldScroll = behavior.call(
|
|
1811
|
-
router,
|
|
1812
|
-
to,
|
|
1813
|
-
from,
|
|
1814
|
-
isPop ? position : null
|
|
1815
|
-
);
|
|
1816
|
-
|
|
1817
|
-
if (!shouldScroll) {
|
|
1818
|
-
return
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
if (typeof shouldScroll.then === 'function') {
|
|
1822
|
-
shouldScroll
|
|
1823
|
-
.then(shouldScroll => {
|
|
1824
|
-
scrollToPosition((shouldScroll), position);
|
|
1825
|
-
})
|
|
1826
|
-
.catch(err => {
|
|
1827
|
-
{
|
|
1828
|
-
assert(false, err.toString());
|
|
1829
|
-
}
|
|
1830
|
-
});
|
|
1831
|
-
} else {
|
|
1832
|
-
scrollToPosition(shouldScroll, position);
|
|
1833
|
-
}
|
|
1834
|
-
});
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
function saveScrollPosition () {
|
|
1838
|
-
const key = getStateKey();
|
|
1839
|
-
if (key) {
|
|
1840
|
-
positionStore[key] = {
|
|
1841
|
-
x: window.pageXOffset,
|
|
1842
|
-
y: window.pageYOffset
|
|
1843
|
-
};
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
|
|
1847
|
-
function handlePopState (e) {
|
|
1848
|
-
saveScrollPosition();
|
|
1849
|
-
if (e.state && e.state.key) {
|
|
1850
|
-
setStateKey(e.state.key);
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
function getScrollPosition () {
|
|
1855
|
-
const key = getStateKey();
|
|
1856
|
-
if (key) {
|
|
1857
|
-
return positionStore[key]
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
function getElementPosition (el, offset) {
|
|
1862
|
-
const docEl = document.documentElement;
|
|
1863
|
-
const docRect = docEl.getBoundingClientRect();
|
|
1864
|
-
const elRect = el.getBoundingClientRect();
|
|
1865
|
-
return {
|
|
1866
|
-
x: elRect.left - docRect.left - offset.x,
|
|
1867
|
-
y: elRect.top - docRect.top - offset.y
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
function isValidPosition (obj) {
|
|
1872
|
-
return isNumber(obj.x) || isNumber(obj.y)
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
function normalizePosition (obj) {
|
|
1876
|
-
return {
|
|
1877
|
-
x: isNumber(obj.x) ? obj.x : window.pageXOffset,
|
|
1878
|
-
y: isNumber(obj.y) ? obj.y : window.pageYOffset
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
function normalizeOffset (obj) {
|
|
1883
|
-
return {
|
|
1884
|
-
x: isNumber(obj.x) ? obj.x : 0,
|
|
1885
|
-
y: isNumber(obj.y) ? obj.y : 0
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
function isNumber (v) {
|
|
1890
|
-
return typeof v === 'number'
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1893
|
-
const hashStartsWithNumberRE = /^#\d/;
|
|
1894
|
-
|
|
1895
|
-
function scrollToPosition (shouldScroll, position) {
|
|
1896
|
-
const isObject = typeof shouldScroll === 'object';
|
|
1897
|
-
if (isObject && typeof shouldScroll.selector === 'string') {
|
|
1898
|
-
// getElementById would still fail if the selector contains a more complicated query like #main[data-attr]
|
|
1899
|
-
// but at the same time, it doesn't make much sense to select an element with an id and an extra selector
|
|
1900
|
-
const el = hashStartsWithNumberRE.test(shouldScroll.selector) // $flow-disable-line
|
|
1901
|
-
? document.getElementById(shouldScroll.selector.slice(1)) // $flow-disable-line
|
|
1902
|
-
: document.querySelector(shouldScroll.selector);
|
|
1903
|
-
|
|
1904
|
-
if (el) {
|
|
1905
|
-
let offset =
|
|
1906
|
-
shouldScroll.offset && typeof shouldScroll.offset === 'object'
|
|
1907
|
-
? shouldScroll.offset
|
|
1908
|
-
: {};
|
|
1909
|
-
offset = normalizeOffset(offset);
|
|
1910
|
-
position = getElementPosition(el, offset);
|
|
1911
|
-
} else if (isValidPosition(shouldScroll)) {
|
|
1912
|
-
position = normalizePosition(shouldScroll);
|
|
1913
|
-
}
|
|
1914
|
-
} else if (isObject && isValidPosition(shouldScroll)) {
|
|
1915
|
-
position = normalizePosition(shouldScroll);
|
|
1916
|
-
}
|
|
1917
|
-
|
|
1918
|
-
if (position) {
|
|
1919
|
-
// $flow-disable-line
|
|
1920
|
-
if ('scrollBehavior' in document.documentElement.style) {
|
|
1921
|
-
window.scrollTo({
|
|
1922
|
-
left: position.x,
|
|
1923
|
-
top: position.y,
|
|
1924
|
-
// $flow-disable-line
|
|
1925
|
-
behavior: shouldScroll.behavior
|
|
1926
|
-
});
|
|
1927
|
-
} else {
|
|
1928
|
-
window.scrollTo(position.x, position.y);
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
/* */
|
|
1934
|
-
|
|
1935
|
-
const supportsPushState =
|
|
1936
|
-
inBrowser &&
|
|
1937
|
-
(function () {
|
|
1938
|
-
const ua = window.navigator.userAgent;
|
|
1939
|
-
|
|
1940
|
-
if (
|
|
1941
|
-
(ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
|
|
1942
|
-
ua.indexOf('Mobile Safari') !== -1 &&
|
|
1943
|
-
ua.indexOf('Chrome') === -1 &&
|
|
1944
|
-
ua.indexOf('Windows Phone') === -1
|
|
1945
|
-
) {
|
|
1946
|
-
return false
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
return window.history && typeof window.history.pushState === 'function'
|
|
1950
|
-
})();
|
|
1951
|
-
|
|
1952
|
-
function pushState (url, replace) {
|
|
1953
|
-
saveScrollPosition();
|
|
1954
|
-
// try...catch the pushState call to get around Safari
|
|
1955
|
-
// DOM Exception 18 where it limits to 100 pushState calls
|
|
1956
|
-
const history = window.history;
|
|
1957
|
-
try {
|
|
1958
|
-
if (replace) {
|
|
1959
|
-
// preserve existing history state as it could be overriden by the user
|
|
1960
|
-
const stateCopy = extend({}, history.state);
|
|
1961
|
-
stateCopy.key = getStateKey();
|
|
1962
|
-
history.replaceState(stateCopy, '', url);
|
|
1963
|
-
} else {
|
|
1964
|
-
history.pushState({ key: setStateKey(genStateKey()) }, '', url);
|
|
1965
|
-
}
|
|
1966
|
-
} catch (e) {
|
|
1967
|
-
window.location[replace ? 'replace' : 'assign'](url);
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
|
|
1971
|
-
function replaceState (url) {
|
|
1972
|
-
pushState(url, true);
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
/* */
|
|
1976
|
-
|
|
1977
|
-
function runQueue (queue, fn, cb) {
|
|
1978
|
-
const step = index => {
|
|
1979
|
-
if (index >= queue.length) {
|
|
1980
|
-
cb();
|
|
1981
|
-
} else {
|
|
1982
|
-
if (queue[index]) {
|
|
1983
|
-
fn(queue[index], () => {
|
|
1984
|
-
step(index + 1);
|
|
1985
|
-
});
|
|
1986
|
-
} else {
|
|
1987
|
-
step(index + 1);
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
};
|
|
1991
|
-
step(0);
|
|
1992
|
-
}
|
|
1993
|
-
|
|
1994
|
-
// When changing thing, also edit router.d.ts
|
|
1995
|
-
const NavigationFailureType = {
|
|
1996
|
-
redirected: 2,
|
|
1997
|
-
aborted: 4,
|
|
1998
|
-
cancelled: 8,
|
|
1999
|
-
duplicated: 16
|
|
2000
|
-
};
|
|
2001
|
-
|
|
2002
|
-
function createNavigationRedirectedError (from, to) {
|
|
2003
|
-
return createRouterError(
|
|
2004
|
-
from,
|
|
2005
|
-
to,
|
|
2006
|
-
NavigationFailureType.redirected,
|
|
2007
|
-
`Redirected when going from "${from.fullPath}" to "${stringifyRoute(
|
|
2008
|
-
to
|
|
2009
|
-
)}" via a navigation guard.`
|
|
2010
|
-
)
|
|
2011
|
-
}
|
|
2012
|
-
|
|
2013
|
-
function createNavigationDuplicatedError (from, to) {
|
|
2014
|
-
const error = createRouterError(
|
|
2015
|
-
from,
|
|
2016
|
-
to,
|
|
2017
|
-
NavigationFailureType.duplicated,
|
|
2018
|
-
`Avoided redundant navigation to current location: "${from.fullPath}".`
|
|
2019
|
-
);
|
|
2020
|
-
// backwards compatible with the first introduction of Errors
|
|
2021
|
-
error.name = 'NavigationDuplicated';
|
|
2022
|
-
return error
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
function createNavigationCancelledError (from, to) {
|
|
2026
|
-
return createRouterError(
|
|
2027
|
-
from,
|
|
2028
|
-
to,
|
|
2029
|
-
NavigationFailureType.cancelled,
|
|
2030
|
-
`Navigation cancelled from "${from.fullPath}" to "${
|
|
2031
|
-
to.fullPath
|
|
2032
|
-
}" with a new navigation.`
|
|
2033
|
-
)
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
function createNavigationAbortedError (from, to) {
|
|
2037
|
-
return createRouterError(
|
|
2038
|
-
from,
|
|
2039
|
-
to,
|
|
2040
|
-
NavigationFailureType.aborted,
|
|
2041
|
-
`Navigation aborted from "${from.fullPath}" to "${
|
|
2042
|
-
to.fullPath
|
|
2043
|
-
}" via a navigation guard.`
|
|
2044
|
-
)
|
|
2045
|
-
}
|
|
2046
|
-
|
|
2047
|
-
function createRouterError (from, to, type, message) {
|
|
2048
|
-
const error = new Error(message);
|
|
2049
|
-
error._isRouter = true;
|
|
2050
|
-
error.from = from;
|
|
2051
|
-
error.to = to;
|
|
2052
|
-
error.type = type;
|
|
2053
|
-
|
|
2054
|
-
return error
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
|
-
const propertiesToLog = ['params', 'query', 'hash'];
|
|
2058
|
-
|
|
2059
|
-
function stringifyRoute (to) {
|
|
2060
|
-
if (typeof to === 'string') return to
|
|
2061
|
-
if ('path' in to) return to.path
|
|
2062
|
-
const location = {};
|
|
2063
|
-
propertiesToLog.forEach(key => {
|
|
2064
|
-
if (key in to) location[key] = to[key];
|
|
2065
|
-
});
|
|
2066
|
-
return JSON.stringify(location, null, 2)
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2069
|
-
function isError (err) {
|
|
2070
|
-
return Object.prototype.toString.call(err).indexOf('Error') > -1
|
|
2071
|
-
}
|
|
2072
|
-
|
|
2073
|
-
function isNavigationFailure (err, errorType) {
|
|
2074
|
-
return (
|
|
2075
|
-
isError(err) &&
|
|
2076
|
-
err._isRouter &&
|
|
2077
|
-
(errorType == null || err.type === errorType)
|
|
2078
|
-
)
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
/* */
|
|
2082
|
-
|
|
2083
|
-
function resolveAsyncComponents (matched) {
|
|
2084
|
-
return (to, from, next) => {
|
|
2085
|
-
let hasAsync = false;
|
|
2086
|
-
let pending = 0;
|
|
2087
|
-
let error = null;
|
|
2088
|
-
|
|
2089
|
-
flatMapComponents(matched, (def, _, match, key) => {
|
|
2090
|
-
// if it's a function and doesn't have cid attached,
|
|
2091
|
-
// assume it's an async component resolve function.
|
|
2092
|
-
// we are not using Kdu's default async resolving mechanism because
|
|
2093
|
-
// we want to halt the navigation until the incoming component has been
|
|
2094
|
-
// resolved.
|
|
2095
|
-
if (typeof def === 'function' && def.cid === undefined) {
|
|
2096
|
-
hasAsync = true;
|
|
2097
|
-
pending++;
|
|
2098
|
-
|
|
2099
|
-
const resolve = once(resolvedDef => {
|
|
2100
|
-
if (isESModule(resolvedDef)) {
|
|
2101
|
-
resolvedDef = resolvedDef.default;
|
|
2102
|
-
}
|
|
2103
|
-
// save resolved on async factory in case it's used elsewhere
|
|
2104
|
-
def.resolved = typeof resolvedDef === 'function'
|
|
2105
|
-
? resolvedDef
|
|
2106
|
-
: _Kdu.extend(resolvedDef);
|
|
2107
|
-
match.components[key] = resolvedDef;
|
|
2108
|
-
pending--;
|
|
2109
|
-
if (pending <= 0) {
|
|
2110
|
-
next();
|
|
2111
|
-
}
|
|
2112
|
-
});
|
|
2113
|
-
|
|
2114
|
-
const reject = once(reason => {
|
|
2115
|
-
const msg = `Failed to resolve async component ${key}: ${reason}`;
|
|
2116
|
-
warn(false, msg);
|
|
2117
|
-
if (!error) {
|
|
2118
|
-
error = isError(reason)
|
|
2119
|
-
? reason
|
|
2120
|
-
: new Error(msg);
|
|
2121
|
-
next(error);
|
|
2122
|
-
}
|
|
2123
|
-
});
|
|
2124
|
-
|
|
2125
|
-
let res;
|
|
2126
|
-
try {
|
|
2127
|
-
res = def(resolve, reject);
|
|
2128
|
-
} catch (e) {
|
|
2129
|
-
reject(e);
|
|
2130
|
-
}
|
|
2131
|
-
if (res) {
|
|
2132
|
-
if (typeof res.then === 'function') {
|
|
2133
|
-
res.then(resolve, reject);
|
|
2134
|
-
} else {
|
|
2135
|
-
// new syntax in Kdu 2.3
|
|
2136
|
-
const comp = res.component;
|
|
2137
|
-
if (comp && typeof comp.then === 'function') {
|
|
2138
|
-
comp.then(resolve, reject);
|
|
2139
|
-
}
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
});
|
|
2144
|
-
|
|
2145
|
-
if (!hasAsync) next();
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
function flatMapComponents (
|
|
2150
|
-
matched,
|
|
2151
|
-
fn
|
|
2152
|
-
) {
|
|
2153
|
-
return flatten(matched.map(m => {
|
|
2154
|
-
return Object.keys(m.components).map(key => fn(
|
|
2155
|
-
m.components[key],
|
|
2156
|
-
m.instances[key],
|
|
2157
|
-
m, key
|
|
2158
|
-
))
|
|
2159
|
-
}))
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
function flatten (arr) {
|
|
2163
|
-
return Array.prototype.concat.apply([], arr)
|
|
2164
|
-
}
|
|
2165
|
-
|
|
2166
|
-
const hasSymbol =
|
|
2167
|
-
typeof Symbol === 'function' &&
|
|
2168
|
-
typeof Symbol.toStringTag === 'symbol';
|
|
2169
|
-
|
|
2170
|
-
function isESModule (obj) {
|
|
2171
|
-
return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module')
|
|
2172
|
-
}
|
|
2173
|
-
|
|
2174
|
-
// in Webpack 2, require.ensure now also returns a Promise
|
|
2175
|
-
// so the resolve/reject functions may get called an extra time
|
|
2176
|
-
// if the user uses an arrow function shorthand that happens to
|
|
2177
|
-
// return that Promise.
|
|
2178
|
-
function once (fn) {
|
|
2179
|
-
let called = false;
|
|
2180
|
-
return function (...args) {
|
|
2181
|
-
if (called) return
|
|
2182
|
-
called = true;
|
|
2183
|
-
return fn.apply(this, args)
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
|
-
/* */
|
|
2188
|
-
|
|
2189
|
-
class History {
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
// implemented by sub-classes
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
constructor (router, base) {
|
|
2211
|
-
this.router = router;
|
|
2212
|
-
this.base = normalizeBase(base);
|
|
2213
|
-
// start with a route object that stands for "nowhere"
|
|
2214
|
-
this.current = START;
|
|
2215
|
-
this.pending = null;
|
|
2216
|
-
this.ready = false;
|
|
2217
|
-
this.readyCbs = [];
|
|
2218
|
-
this.readyErrorCbs = [];
|
|
2219
|
-
this.errorCbs = [];
|
|
2220
|
-
this.listeners = [];
|
|
2221
|
-
}
|
|
2222
|
-
|
|
2223
|
-
listen (cb) {
|
|
2224
|
-
this.cb = cb;
|
|
2225
|
-
}
|
|
2226
|
-
|
|
2227
|
-
onReady (cb, errorCb) {
|
|
2228
|
-
if (this.ready) {
|
|
2229
|
-
cb();
|
|
2230
|
-
} else {
|
|
2231
|
-
this.readyCbs.push(cb);
|
|
2232
|
-
if (errorCb) {
|
|
2233
|
-
this.readyErrorCbs.push(errorCb);
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
|
|
2238
|
-
onError (errorCb) {
|
|
2239
|
-
this.errorCbs.push(errorCb);
|
|
2240
|
-
}
|
|
2241
|
-
|
|
2242
|
-
transitionTo (
|
|
2243
|
-
location,
|
|
2244
|
-
onComplete,
|
|
2245
|
-
onAbort
|
|
2246
|
-
) {
|
|
2247
|
-
let route;
|
|
2248
|
-
// catch redirect option
|
|
2249
|
-
try {
|
|
2250
|
-
route = this.router.match(location, this.current);
|
|
2251
|
-
} catch (e) {
|
|
2252
|
-
this.errorCbs.forEach(cb => {
|
|
2253
|
-
cb(e);
|
|
2254
|
-
});
|
|
2255
|
-
// Exception should still be thrown
|
|
2256
|
-
throw e
|
|
2257
|
-
}
|
|
2258
|
-
const prev = this.current;
|
|
2259
|
-
this.confirmTransition(
|
|
2260
|
-
route,
|
|
2261
|
-
() => {
|
|
2262
|
-
this.updateRoute(route);
|
|
2263
|
-
onComplete && onComplete(route);
|
|
2264
|
-
this.ensureURL();
|
|
2265
|
-
this.router.afterHooks.forEach(hook => {
|
|
2266
|
-
hook && hook(route, prev);
|
|
2267
|
-
});
|
|
2268
|
-
|
|
2269
|
-
// fire ready cbs once
|
|
2270
|
-
if (!this.ready) {
|
|
2271
|
-
this.ready = true;
|
|
2272
|
-
this.readyCbs.forEach(cb => {
|
|
2273
|
-
cb(route);
|
|
2274
|
-
});
|
|
2275
|
-
}
|
|
2276
|
-
},
|
|
2277
|
-
err => {
|
|
2278
|
-
if (onAbort) {
|
|
2279
|
-
onAbort(err);
|
|
2280
|
-
}
|
|
2281
|
-
if (err && !this.ready) {
|
|
2282
|
-
// Initial redirection should not mark the history as ready yet
|
|
2283
|
-
// because it's triggered by the redirection instead
|
|
2284
|
-
if (!isNavigationFailure(err, NavigationFailureType.redirected) || prev !== START) {
|
|
2285
|
-
this.ready = true;
|
|
2286
|
-
this.readyErrorCbs.forEach(cb => {
|
|
2287
|
-
cb(err);
|
|
2288
|
-
});
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
);
|
|
2293
|
-
}
|
|
2294
|
-
|
|
2295
|
-
confirmTransition (route, onComplete, onAbort) {
|
|
2296
|
-
const current = this.current;
|
|
2297
|
-
this.pending = route;
|
|
2298
|
-
const abort = err => {
|
|
2299
|
-
// changed after adding errors
|
|
2300
|
-
// before that change, redirect and aborted navigation would produce an err == null
|
|
2301
|
-
if (!isNavigationFailure(err) && isError(err)) {
|
|
2302
|
-
if (this.errorCbs.length) {
|
|
2303
|
-
this.errorCbs.forEach(cb => {
|
|
2304
|
-
cb(err);
|
|
2305
|
-
});
|
|
2306
|
-
} else {
|
|
2307
|
-
{
|
|
2308
|
-
warn(false, 'uncaught error during route navigation:');
|
|
2309
|
-
}
|
|
2310
|
-
console.error(err);
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
onAbort && onAbort(err);
|
|
2314
|
-
};
|
|
2315
|
-
const lastRouteIndex = route.matched.length - 1;
|
|
2316
|
-
const lastCurrentIndex = current.matched.length - 1;
|
|
2317
|
-
if (
|
|
2318
|
-
isSameRoute(route, current) &&
|
|
2319
|
-
// in the case the route map has been dynamically appended to
|
|
2320
|
-
lastRouteIndex === lastCurrentIndex &&
|
|
2321
|
-
route.matched[lastRouteIndex] === current.matched[lastCurrentIndex]
|
|
2322
|
-
) {
|
|
2323
|
-
this.ensureURL();
|
|
2324
|
-
if (route.hash) {
|
|
2325
|
-
handleScroll(this.router, current, route, false);
|
|
2326
|
-
}
|
|
2327
|
-
return abort(createNavigationDuplicatedError(current, route))
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
const { updated, deactivated, activated } = resolveQueue(
|
|
2331
|
-
this.current.matched,
|
|
2332
|
-
route.matched
|
|
2333
|
-
);
|
|
2334
|
-
|
|
2335
|
-
const queue = [].concat(
|
|
2336
|
-
// in-component leave guards
|
|
2337
|
-
extractLeaveGuards(deactivated),
|
|
2338
|
-
// global before hooks
|
|
2339
|
-
this.router.beforeHooks,
|
|
2340
|
-
// in-component update hooks
|
|
2341
|
-
extractUpdateHooks(updated),
|
|
2342
|
-
// in-config enter guards
|
|
2343
|
-
activated.map(m => m.beforeEnter),
|
|
2344
|
-
// async components
|
|
2345
|
-
resolveAsyncComponents(activated)
|
|
2346
|
-
);
|
|
2347
|
-
|
|
2348
|
-
const iterator = (hook, next) => {
|
|
2349
|
-
if (this.pending !== route) {
|
|
2350
|
-
return abort(createNavigationCancelledError(current, route))
|
|
2351
|
-
}
|
|
2352
|
-
try {
|
|
2353
|
-
hook(route, current, (to) => {
|
|
2354
|
-
if (to === false) {
|
|
2355
|
-
// next(false) -> abort navigation, ensure current URL
|
|
2356
|
-
this.ensureURL(true);
|
|
2357
|
-
abort(createNavigationAbortedError(current, route));
|
|
2358
|
-
} else if (isError(to)) {
|
|
2359
|
-
this.ensureURL(true);
|
|
2360
|
-
abort(to);
|
|
2361
|
-
} else if (
|
|
2362
|
-
typeof to === 'string' ||
|
|
2363
|
-
(typeof to === 'object' &&
|
|
2364
|
-
(typeof to.path === 'string' || typeof to.name === 'string'))
|
|
2365
|
-
) {
|
|
2366
|
-
// next('/') or next({ path: '/' }) -> redirect
|
|
2367
|
-
abort(createNavigationRedirectedError(current, route));
|
|
2368
|
-
if (typeof to === 'object' && to.replace) {
|
|
2369
|
-
this.replace(to);
|
|
2370
|
-
} else {
|
|
2371
|
-
this.push(to);
|
|
2372
|
-
}
|
|
2373
|
-
} else {
|
|
2374
|
-
// confirm transition and pass on the value
|
|
2375
|
-
next(to);
|
|
2376
|
-
}
|
|
2377
|
-
});
|
|
2378
|
-
} catch (e) {
|
|
2379
|
-
abort(e);
|
|
2380
|
-
}
|
|
2381
|
-
};
|
|
2382
|
-
|
|
2383
|
-
runQueue(queue, iterator, () => {
|
|
2384
|
-
// wait until async components are resolved before
|
|
2385
|
-
// extracting in-component enter guards
|
|
2386
|
-
const enterGuards = extractEnterGuards(activated);
|
|
2387
|
-
const queue = enterGuards.concat(this.router.resolveHooks);
|
|
2388
|
-
runQueue(queue, iterator, () => {
|
|
2389
|
-
if (this.pending !== route) {
|
|
2390
|
-
return abort(createNavigationCancelledError(current, route))
|
|
2391
|
-
}
|
|
2392
|
-
this.pending = null;
|
|
2393
|
-
onComplete(route);
|
|
2394
|
-
if (this.router.app) {
|
|
2395
|
-
this.router.app.$nextTick(() => {
|
|
2396
|
-
handleRouteEntered(route);
|
|
2397
|
-
});
|
|
2398
|
-
}
|
|
2399
|
-
});
|
|
2400
|
-
});
|
|
2401
|
-
}
|
|
2402
|
-
|
|
2403
|
-
updateRoute (route) {
|
|
2404
|
-
this.current = route;
|
|
2405
|
-
this.cb && this.cb(route);
|
|
2406
|
-
}
|
|
2407
|
-
|
|
2408
|
-
setupListeners () {
|
|
2409
|
-
// Default implementation is empty
|
|
2410
|
-
}
|
|
2411
|
-
|
|
2412
|
-
teardown () {
|
|
2413
|
-
// clean up event listeners
|
|
2414
|
-
this.listeners.forEach(cleanupListener => {
|
|
2415
|
-
cleanupListener();
|
|
2416
|
-
});
|
|
2417
|
-
this.listeners = [];
|
|
2418
|
-
|
|
2419
|
-
// reset current history route
|
|
2420
|
-
this.current = START;
|
|
2421
|
-
this.pending = null;
|
|
2422
|
-
}
|
|
2423
|
-
}
|
|
2424
|
-
|
|
2425
|
-
function normalizeBase (base) {
|
|
2426
|
-
if (!base) {
|
|
2427
|
-
if (inBrowser) {
|
|
2428
|
-
// respect <base> tag
|
|
2429
|
-
const baseEl = document.querySelector('base');
|
|
2430
|
-
base = (baseEl && baseEl.getAttribute('href')) || '/';
|
|
2431
|
-
// strip full URL origin
|
|
2432
|
-
base = base.replace(/^https?:\/\/[^\/]+/, '');
|
|
2433
|
-
} else {
|
|
2434
|
-
base = '/';
|
|
2435
|
-
}
|
|
2436
|
-
}
|
|
2437
|
-
// make sure there's the starting slash
|
|
2438
|
-
if (base.charAt(0) !== '/') {
|
|
2439
|
-
base = '/' + base;
|
|
2440
|
-
}
|
|
2441
|
-
// remove trailing slash
|
|
2442
|
-
return base.replace(/\/$/, '')
|
|
2443
|
-
}
|
|
2444
|
-
|
|
2445
|
-
function resolveQueue (
|
|
2446
|
-
current,
|
|
2447
|
-
next
|
|
2448
|
-
) {
|
|
2449
|
-
let i;
|
|
2450
|
-
const max = Math.max(current.length, next.length);
|
|
2451
|
-
for (i = 0; i < max; i++) {
|
|
2452
|
-
if (current[i] !== next[i]) {
|
|
2453
|
-
break
|
|
2454
|
-
}
|
|
2455
|
-
}
|
|
2456
|
-
return {
|
|
2457
|
-
updated: next.slice(0, i),
|
|
2458
|
-
activated: next.slice(i),
|
|
2459
|
-
deactivated: current.slice(i)
|
|
2460
|
-
}
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2463
|
-
function extractGuards (
|
|
2464
|
-
records,
|
|
2465
|
-
name,
|
|
2466
|
-
bind,
|
|
2467
|
-
reverse
|
|
2468
|
-
) {
|
|
2469
|
-
const guards = flatMapComponents(records, (def, instance, match, key) => {
|
|
2470
|
-
const guard = extractGuard(def, name);
|
|
2471
|
-
if (guard) {
|
|
2472
|
-
return Array.isArray(guard)
|
|
2473
|
-
? guard.map(guard => bind(guard, instance, match, key))
|
|
2474
|
-
: bind(guard, instance, match, key)
|
|
2475
|
-
}
|
|
2476
|
-
});
|
|
2477
|
-
return flatten(reverse ? guards.reverse() : guards)
|
|
2478
|
-
}
|
|
2479
|
-
|
|
2480
|
-
function extractGuard (
|
|
2481
|
-
def,
|
|
2482
|
-
key
|
|
2483
|
-
) {
|
|
2484
|
-
if (typeof def !== 'function') {
|
|
2485
|
-
// extend now so that global mixins are applied.
|
|
2486
|
-
def = _Kdu.extend(def);
|
|
2487
|
-
}
|
|
2488
|
-
return def.options[key]
|
|
2489
|
-
}
|
|
2490
|
-
|
|
2491
|
-
function extractLeaveGuards (deactivated) {
|
|
2492
|
-
return extractGuards(deactivated, 'beforeRouteLeave', bindGuard, true)
|
|
2493
|
-
}
|
|
2494
|
-
|
|
2495
|
-
function extractUpdateHooks (updated) {
|
|
2496
|
-
return extractGuards(updated, 'beforeRouteUpdate', bindGuard)
|
|
2497
|
-
}
|
|
2498
|
-
|
|
2499
|
-
function bindGuard (guard, instance) {
|
|
2500
|
-
if (instance) {
|
|
2501
|
-
return function boundRouteGuard () {
|
|
2502
|
-
return guard.apply(instance, arguments)
|
|
2503
|
-
}
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
|
|
2507
|
-
function extractEnterGuards (
|
|
2508
|
-
activated
|
|
2509
|
-
) {
|
|
2510
|
-
return extractGuards(
|
|
2511
|
-
activated,
|
|
2512
|
-
'beforeRouteEnter',
|
|
2513
|
-
(guard, _, match, key) => {
|
|
2514
|
-
return bindEnterGuard(guard, match, key)
|
|
2515
|
-
}
|
|
2516
|
-
)
|
|
2517
|
-
}
|
|
2518
|
-
|
|
2519
|
-
function bindEnterGuard (
|
|
2520
|
-
guard,
|
|
2521
|
-
match,
|
|
2522
|
-
key
|
|
2523
|
-
) {
|
|
2524
|
-
return function routeEnterGuard (to, from, next) {
|
|
2525
|
-
return guard(to, from, cb => {
|
|
2526
|
-
if (typeof cb === 'function') {
|
|
2527
|
-
if (!match.enteredCbs[key]) {
|
|
2528
|
-
match.enteredCbs[key] = [];
|
|
2529
|
-
}
|
|
2530
|
-
match.enteredCbs[key].push(cb);
|
|
2531
|
-
}
|
|
2532
|
-
next(cb);
|
|
2533
|
-
})
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2536
|
-
|
|
2537
|
-
/* */
|
|
2538
|
-
|
|
2539
|
-
class HTML5History extends History {
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
constructor (router, base) {
|
|
2543
|
-
super(router, base);
|
|
2544
|
-
|
|
2545
|
-
this._startLocation = getLocation(this.base);
|
|
2546
|
-
}
|
|
2547
|
-
|
|
2548
|
-
setupListeners () {
|
|
2549
|
-
if (this.listeners.length > 0) {
|
|
2550
|
-
return
|
|
2551
|
-
}
|
|
2552
|
-
|
|
2553
|
-
const router = this.router;
|
|
2554
|
-
const expectScroll = router.options.scrollBehavior;
|
|
2555
|
-
const supportsScroll = supportsPushState && expectScroll;
|
|
2556
|
-
|
|
2557
|
-
if (supportsScroll) {
|
|
2558
|
-
this.listeners.push(setupScroll());
|
|
2559
|
-
}
|
|
2560
|
-
|
|
2561
|
-
const handleRoutingEvent = () => {
|
|
2562
|
-
const current = this.current;
|
|
2563
|
-
|
|
2564
|
-
// Avoiding first `popstate` event dispatched in some browsers but first
|
|
2565
|
-
// history route not updated since async guard at the same time.
|
|
2566
|
-
const location = getLocation(this.base);
|
|
2567
|
-
if (this.current === START && location === this._startLocation) {
|
|
2568
|
-
return
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
this.transitionTo(location, route => {
|
|
2572
|
-
if (supportsScroll) {
|
|
2573
|
-
handleScroll(router, route, current, true);
|
|
2574
|
-
}
|
|
2575
|
-
});
|
|
2576
|
-
};
|
|
2577
|
-
window.addEventListener('popstate', handleRoutingEvent);
|
|
2578
|
-
this.listeners.push(() => {
|
|
2579
|
-
window.removeEventListener('popstate', handleRoutingEvent);
|
|
2580
|
-
});
|
|
2581
|
-
}
|
|
2582
|
-
|
|
2583
|
-
go (n) {
|
|
2584
|
-
window.history.go(n);
|
|
2585
|
-
}
|
|
2586
|
-
|
|
2587
|
-
push (location, onComplete, onAbort) {
|
|
2588
|
-
const { current: fromRoute } = this;
|
|
2589
|
-
this.transitionTo(location, route => {
|
|
2590
|
-
pushState(cleanPath(this.base + route.fullPath));
|
|
2591
|
-
handleScroll(this.router, route, fromRoute, false);
|
|
2592
|
-
onComplete && onComplete(route);
|
|
2593
|
-
}, onAbort);
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
replace (location, onComplete, onAbort) {
|
|
2597
|
-
const { current: fromRoute } = this;
|
|
2598
|
-
this.transitionTo(location, route => {
|
|
2599
|
-
replaceState(cleanPath(this.base + route.fullPath));
|
|
2600
|
-
handleScroll(this.router, route, fromRoute, false);
|
|
2601
|
-
onComplete && onComplete(route);
|
|
2602
|
-
}, onAbort);
|
|
2603
|
-
}
|
|
2604
|
-
|
|
2605
|
-
ensureURL (push) {
|
|
2606
|
-
if (getLocation(this.base) !== this.current.fullPath) {
|
|
2607
|
-
const current = cleanPath(this.base + this.current.fullPath);
|
|
2608
|
-
push ? pushState(current) : replaceState(current);
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
|
-
getCurrentLocation () {
|
|
2613
|
-
return getLocation(this.base)
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2617
|
-
function getLocation (base) {
|
|
2618
|
-
let path = window.location.pathname;
|
|
2619
|
-
const pathLowerCase = path.toLowerCase();
|
|
2620
|
-
const baseLowerCase = base.toLowerCase();
|
|
2621
|
-
// base="/a" shouldn't turn path="/app" into "/a/pp"
|
|
2622
|
-
// so we ensure the trailing slash in the base
|
|
2623
|
-
if (base && ((pathLowerCase === baseLowerCase) ||
|
|
2624
|
-
(pathLowerCase.indexOf(cleanPath(baseLowerCase + '/')) === 0))) {
|
|
2625
|
-
path = path.slice(base.length);
|
|
2626
|
-
}
|
|
2627
|
-
return (path || '/') + window.location.search + window.location.hash
|
|
2628
|
-
}
|
|
2629
|
-
|
|
2630
|
-
/* */
|
|
2631
|
-
|
|
2632
|
-
class HashHistory extends History {
|
|
2633
|
-
constructor (router, base, fallback) {
|
|
2634
|
-
super(router, base);
|
|
2635
|
-
// check history fallback deeplinking
|
|
2636
|
-
if (fallback && checkFallback(this.base)) {
|
|
2637
|
-
return
|
|
2638
|
-
}
|
|
2639
|
-
ensureSlash();
|
|
2640
|
-
}
|
|
2641
|
-
|
|
2642
|
-
// this is delayed until the app mounts
|
|
2643
|
-
// to avoid the hashchange listener being fired too early
|
|
2644
|
-
setupListeners () {
|
|
2645
|
-
if (this.listeners.length > 0) {
|
|
2646
|
-
return
|
|
2647
|
-
}
|
|
2648
|
-
|
|
2649
|
-
const router = this.router;
|
|
2650
|
-
const expectScroll = router.options.scrollBehavior;
|
|
2651
|
-
const supportsScroll = supportsPushState && expectScroll;
|
|
2652
|
-
|
|
2653
|
-
if (supportsScroll) {
|
|
2654
|
-
this.listeners.push(setupScroll());
|
|
2655
|
-
}
|
|
2656
|
-
|
|
2657
|
-
const handleRoutingEvent = () => {
|
|
2658
|
-
const current = this.current;
|
|
2659
|
-
if (!ensureSlash()) {
|
|
2660
|
-
return
|
|
2661
|
-
}
|
|
2662
|
-
this.transitionTo(getHash(), route => {
|
|
2663
|
-
if (supportsScroll) {
|
|
2664
|
-
handleScroll(this.router, route, current, true);
|
|
2665
|
-
}
|
|
2666
|
-
if (!supportsPushState) {
|
|
2667
|
-
replaceHash(route.fullPath);
|
|
2668
|
-
}
|
|
2669
|
-
});
|
|
2670
|
-
};
|
|
2671
|
-
const eventType = supportsPushState ? 'popstate' : 'hashchange';
|
|
2672
|
-
window.addEventListener(
|
|
2673
|
-
eventType,
|
|
2674
|
-
handleRoutingEvent
|
|
2675
|
-
);
|
|
2676
|
-
this.listeners.push(() => {
|
|
2677
|
-
window.removeEventListener(eventType, handleRoutingEvent);
|
|
2678
|
-
});
|
|
2679
|
-
}
|
|
2680
|
-
|
|
2681
|
-
push (location, onComplete, onAbort) {
|
|
2682
|
-
const { current: fromRoute } = this;
|
|
2683
|
-
this.transitionTo(
|
|
2684
|
-
location,
|
|
2685
|
-
route => {
|
|
2686
|
-
pushHash(route.fullPath);
|
|
2687
|
-
handleScroll(this.router, route, fromRoute, false);
|
|
2688
|
-
onComplete && onComplete(route);
|
|
2689
|
-
},
|
|
2690
|
-
onAbort
|
|
2691
|
-
);
|
|
2692
|
-
}
|
|
2693
|
-
|
|
2694
|
-
replace (location, onComplete, onAbort) {
|
|
2695
|
-
const { current: fromRoute } = this;
|
|
2696
|
-
this.transitionTo(
|
|
2697
|
-
location,
|
|
2698
|
-
route => {
|
|
2699
|
-
replaceHash(route.fullPath);
|
|
2700
|
-
handleScroll(this.router, route, fromRoute, false);
|
|
2701
|
-
onComplete && onComplete(route);
|
|
2702
|
-
},
|
|
2703
|
-
onAbort
|
|
2704
|
-
);
|
|
2705
|
-
}
|
|
2706
|
-
|
|
2707
|
-
go (n) {
|
|
2708
|
-
window.history.go(n);
|
|
2709
|
-
}
|
|
2710
|
-
|
|
2711
|
-
ensureURL (push) {
|
|
2712
|
-
const current = this.current.fullPath;
|
|
2713
|
-
if (getHash() !== current) {
|
|
2714
|
-
push ? pushHash(current) : replaceHash(current);
|
|
2715
|
-
}
|
|
2716
|
-
}
|
|
2717
|
-
|
|
2718
|
-
getCurrentLocation () {
|
|
2719
|
-
return getHash()
|
|
2720
|
-
}
|
|
2721
|
-
}
|
|
2722
|
-
|
|
2723
|
-
function checkFallback (base) {
|
|
2724
|
-
const location = getLocation(base);
|
|
2725
|
-
if (!/^\/#/.test(location)) {
|
|
2726
|
-
window.location.replace(cleanPath(base + '/#' + location));
|
|
2727
|
-
return true
|
|
2728
|
-
}
|
|
2729
|
-
}
|
|
2730
|
-
|
|
2731
|
-
function ensureSlash () {
|
|
2732
|
-
const path = getHash();
|
|
2733
|
-
if (path.charAt(0) === '/') {
|
|
2734
|
-
return true
|
|
2735
|
-
}
|
|
2736
|
-
replaceHash('/' + path);
|
|
2737
|
-
return false
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
function getHash () {
|
|
2741
|
-
// We can't use window.location.hash here because it's not
|
|
2742
|
-
// consistent across browsers - Firefox will pre-decode it!
|
|
2743
|
-
let href = window.location.href;
|
|
2744
|
-
const index = href.indexOf('#');
|
|
2745
|
-
// empty path
|
|
2746
|
-
if (index < 0) return ''
|
|
2747
|
-
|
|
2748
|
-
href = href.slice(index + 1);
|
|
2749
|
-
|
|
2750
|
-
return href
|
|
2751
|
-
}
|
|
2752
|
-
|
|
2753
|
-
function getUrl (path) {
|
|
2754
|
-
const href = window.location.href;
|
|
2755
|
-
const i = href.indexOf('#');
|
|
2756
|
-
const base = i >= 0 ? href.slice(0, i) : href;
|
|
2757
|
-
return `${base}#${path}`
|
|
2758
|
-
}
|
|
2759
|
-
|
|
2760
|
-
function pushHash (path) {
|
|
2761
|
-
if (supportsPushState) {
|
|
2762
|
-
pushState(getUrl(path));
|
|
2763
|
-
} else {
|
|
2764
|
-
window.location.hash = path;
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
|
-
|
|
2768
|
-
function replaceHash (path) {
|
|
2769
|
-
if (supportsPushState) {
|
|
2770
|
-
replaceState(getUrl(path));
|
|
2771
|
-
} else {
|
|
2772
|
-
window.location.replace(getUrl(path));
|
|
2773
|
-
}
|
|
2774
|
-
}
|
|
2775
|
-
|
|
2776
|
-
/* */
|
|
2777
|
-
|
|
2778
|
-
class AbstractHistory extends History {
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
constructor (router, base) {
|
|
2783
|
-
super(router, base);
|
|
2784
|
-
this.stack = [];
|
|
2785
|
-
this.index = -1;
|
|
2786
|
-
}
|
|
2787
|
-
|
|
2788
|
-
push (location, onComplete, onAbort) {
|
|
2789
|
-
this.transitionTo(
|
|
2790
|
-
location,
|
|
2791
|
-
route => {
|
|
2792
|
-
this.stack = this.stack.slice(0, this.index + 1).concat(route);
|
|
2793
|
-
this.index++;
|
|
2794
|
-
onComplete && onComplete(route);
|
|
2795
|
-
},
|
|
2796
|
-
onAbort
|
|
2797
|
-
);
|
|
2798
|
-
}
|
|
2799
|
-
|
|
2800
|
-
replace (location, onComplete, onAbort) {
|
|
2801
|
-
this.transitionTo(
|
|
2802
|
-
location,
|
|
2803
|
-
route => {
|
|
2804
|
-
this.stack = this.stack.slice(0, this.index).concat(route);
|
|
2805
|
-
onComplete && onComplete(route);
|
|
2806
|
-
},
|
|
2807
|
-
onAbort
|
|
2808
|
-
);
|
|
2809
|
-
}
|
|
2810
|
-
|
|
2811
|
-
go (n) {
|
|
2812
|
-
const targetIndex = this.index + n;
|
|
2813
|
-
if (targetIndex < 0 || targetIndex >= this.stack.length) {
|
|
2814
|
-
return
|
|
2815
|
-
}
|
|
2816
|
-
const route = this.stack[targetIndex];
|
|
2817
|
-
this.confirmTransition(
|
|
2818
|
-
route,
|
|
2819
|
-
() => {
|
|
2820
|
-
const prev = this.current;
|
|
2821
|
-
this.index = targetIndex;
|
|
2822
|
-
this.updateRoute(route);
|
|
2823
|
-
this.router.afterHooks.forEach(hook => {
|
|
2824
|
-
hook && hook(route, prev);
|
|
2825
|
-
});
|
|
2826
|
-
},
|
|
2827
|
-
err => {
|
|
2828
|
-
if (isNavigationFailure(err, NavigationFailureType.duplicated)) {
|
|
2829
|
-
this.index = targetIndex;
|
|
2830
|
-
}
|
|
2831
|
-
}
|
|
2832
|
-
);
|
|
2833
|
-
}
|
|
2834
|
-
|
|
2835
|
-
getCurrentLocation () {
|
|
2836
|
-
const current = this.stack[this.stack.length - 1];
|
|
2837
|
-
return current ? current.fullPath : '/'
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
ensureURL () {
|
|
2841
|
-
// noop
|
|
2842
|
-
}
|
|
2843
|
-
}
|
|
2844
|
-
|
|
2845
|
-
/* */
|
|
2846
|
-
|
|
2847
|
-
class KduRouter {
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
constructor (options = {}) {
|
|
2868
|
-
{
|
|
2869
|
-
warn(this instanceof KduRouter, `Router must be called with the new operator.`);
|
|
2870
|
-
}
|
|
2871
|
-
this.app = null;
|
|
2872
|
-
this.apps = [];
|
|
2873
|
-
this.options = options;
|
|
2874
|
-
this.beforeHooks = [];
|
|
2875
|
-
this.resolveHooks = [];
|
|
2876
|
-
this.afterHooks = [];
|
|
2877
|
-
this.matcher = createMatcher(options.routes || [], this);
|
|
2878
|
-
|
|
2879
|
-
let mode = options.mode || 'hash';
|
|
2880
|
-
this.fallback =
|
|
2881
|
-
mode === 'history' && !supportsPushState && options.fallback !== false;
|
|
2882
|
-
if (this.fallback) {
|
|
2883
|
-
mode = 'hash';
|
|
2884
|
-
}
|
|
2885
|
-
if (!inBrowser) {
|
|
2886
|
-
mode = 'abstract';
|
|
2887
|
-
}
|
|
2888
|
-
this.mode = mode;
|
|
2889
|
-
|
|
2890
|
-
switch (mode) {
|
|
2891
|
-
case 'history':
|
|
2892
|
-
this.history = new HTML5History(this, options.base);
|
|
2893
|
-
break
|
|
2894
|
-
case 'hash':
|
|
2895
|
-
this.history = new HashHistory(this, options.base, this.fallback);
|
|
2896
|
-
break
|
|
2897
|
-
case 'abstract':
|
|
2898
|
-
this.history = new AbstractHistory(this, options.base);
|
|
2899
|
-
break
|
|
2900
|
-
default:
|
|
2901
|
-
{
|
|
2902
|
-
assert(false, `invalid mode: ${mode}`);
|
|
2903
|
-
}
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
|
|
2907
|
-
match (raw, current, redirectedFrom) {
|
|
2908
|
-
return this.matcher.match(raw, current, redirectedFrom)
|
|
2909
|
-
}
|
|
2910
|
-
|
|
2911
|
-
get currentRoute () {
|
|
2912
|
-
return this.history && this.history.current
|
|
2913
|
-
}
|
|
2914
|
-
|
|
2915
|
-
init (app /* Kdu component instance */) {
|
|
2916
|
-
assert(
|
|
2917
|
-
install.installed,
|
|
2918
|
-
`not installed. Make sure to call \`Kdu.use(KduRouter)\` ` +
|
|
2919
|
-
`before creating root instance.`
|
|
2920
|
-
);
|
|
2921
|
-
|
|
2922
|
-
this.apps.push(app);
|
|
2923
|
-
|
|
2924
|
-
// set up app destroyed handler
|
|
2925
|
-
app.$once('hook:destroyed', () => {
|
|
2926
|
-
// clean out app from this.apps array once destroyed
|
|
2927
|
-
const index = this.apps.indexOf(app);
|
|
2928
|
-
if (index > -1) this.apps.splice(index, 1);
|
|
2929
|
-
// ensure we still have a main app or null if no apps
|
|
2930
|
-
// we do not release the router so it can be reused
|
|
2931
|
-
if (this.app === app) this.app = this.apps[0] || null;
|
|
2932
|
-
|
|
2933
|
-
if (!this.app) this.history.teardown();
|
|
2934
|
-
});
|
|
2935
|
-
|
|
2936
|
-
// main app previously initialized
|
|
2937
|
-
// return as we don't need to set up new history listener
|
|
2938
|
-
if (this.app) {
|
|
2939
|
-
return
|
|
2940
|
-
}
|
|
2941
|
-
|
|
2942
|
-
this.app = app;
|
|
2943
|
-
|
|
2944
|
-
const history = this.history;
|
|
2945
|
-
|
|
2946
|
-
if (history instanceof HTML5History || history instanceof HashHistory) {
|
|
2947
|
-
const handleInitialScroll = routeOrError => {
|
|
2948
|
-
const from = history.current;
|
|
2949
|
-
const expectScroll = this.options.scrollBehavior;
|
|
2950
|
-
const supportsScroll = supportsPushState && expectScroll;
|
|
2951
|
-
|
|
2952
|
-
if (supportsScroll && 'fullPath' in routeOrError) {
|
|
2953
|
-
handleScroll(this, routeOrError, from, false);
|
|
2954
|
-
}
|
|
2955
|
-
};
|
|
2956
|
-
const setupListeners = routeOrError => {
|
|
2957
|
-
history.setupListeners();
|
|
2958
|
-
handleInitialScroll(routeOrError);
|
|
2959
|
-
};
|
|
2960
|
-
history.transitionTo(
|
|
2961
|
-
history.getCurrentLocation(),
|
|
2962
|
-
setupListeners,
|
|
2963
|
-
setupListeners
|
|
2964
|
-
);
|
|
2965
|
-
}
|
|
2966
|
-
|
|
2967
|
-
history.listen(route => {
|
|
2968
|
-
this.apps.forEach(app => {
|
|
2969
|
-
app._route = route;
|
|
2970
|
-
});
|
|
2971
|
-
});
|
|
2972
|
-
}
|
|
2973
|
-
|
|
2974
|
-
beforeEach (fn) {
|
|
2975
|
-
return registerHook(this.beforeHooks, fn)
|
|
2976
|
-
}
|
|
2977
|
-
|
|
2978
|
-
beforeResolve (fn) {
|
|
2979
|
-
return registerHook(this.resolveHooks, fn)
|
|
2980
|
-
}
|
|
2981
|
-
|
|
2982
|
-
afterEach (fn) {
|
|
2983
|
-
return registerHook(this.afterHooks, fn)
|
|
2984
|
-
}
|
|
2985
|
-
|
|
2986
|
-
onReady (cb, errorCb) {
|
|
2987
|
-
this.history.onReady(cb, errorCb);
|
|
2988
|
-
}
|
|
2989
|
-
|
|
2990
|
-
onError (errorCb) {
|
|
2991
|
-
this.history.onError(errorCb);
|
|
2992
|
-
}
|
|
2993
|
-
|
|
2994
|
-
push (location, onComplete, onAbort) {
|
|
2995
|
-
// $flow-disable-line
|
|
2996
|
-
if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
|
|
2997
|
-
return new Promise((resolve, reject) => {
|
|
2998
|
-
this.history.push(location, resolve, reject);
|
|
2999
|
-
})
|
|
3000
|
-
} else {
|
|
3001
|
-
this.history.push(location, onComplete, onAbort);
|
|
3002
|
-
}
|
|
3003
|
-
}
|
|
3004
|
-
|
|
3005
|
-
replace (location, onComplete, onAbort) {
|
|
3006
|
-
// $flow-disable-line
|
|
3007
|
-
if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
|
|
3008
|
-
return new Promise((resolve, reject) => {
|
|
3009
|
-
this.history.replace(location, resolve, reject);
|
|
3010
|
-
})
|
|
3011
|
-
} else {
|
|
3012
|
-
this.history.replace(location, onComplete, onAbort);
|
|
3013
|
-
}
|
|
3014
|
-
}
|
|
3015
|
-
|
|
3016
|
-
go (n) {
|
|
3017
|
-
this.history.go(n);
|
|
3018
|
-
}
|
|
3019
|
-
|
|
3020
|
-
back () {
|
|
3021
|
-
this.go(-1);
|
|
3022
|
-
}
|
|
3023
|
-
|
|
3024
|
-
forward () {
|
|
3025
|
-
this.go(1);
|
|
3026
|
-
}
|
|
3027
|
-
|
|
3028
|
-
getMatchedComponents (to) {
|
|
3029
|
-
const route = to
|
|
3030
|
-
? to.matched
|
|
3031
|
-
? to
|
|
3032
|
-
: this.resolve(to).route
|
|
3033
|
-
: this.currentRoute;
|
|
3034
|
-
if (!route) {
|
|
3035
|
-
return []
|
|
3036
|
-
}
|
|
3037
|
-
return [].concat.apply(
|
|
3038
|
-
[],
|
|
3039
|
-
route.matched.map(m => {
|
|
3040
|
-
return Object.keys(m.components).map(key => {
|
|
3041
|
-
return m.components[key]
|
|
3042
|
-
})
|
|
3043
|
-
})
|
|
3044
|
-
)
|
|
3045
|
-
}
|
|
3046
|
-
|
|
3047
|
-
resolve (
|
|
3048
|
-
to,
|
|
3049
|
-
current,
|
|
3050
|
-
append
|
|
3051
|
-
) {
|
|
3052
|
-
current = current || this.history.current;
|
|
3053
|
-
const location = normalizeLocation(to, current, append, this);
|
|
3054
|
-
const route = this.match(location, current);
|
|
3055
|
-
const fullPath = route.redirectedFrom || route.fullPath;
|
|
3056
|
-
const base = this.history.base;
|
|
3057
|
-
const href = createHref(base, fullPath, this.mode);
|
|
3058
|
-
return {
|
|
3059
|
-
location,
|
|
3060
|
-
route,
|
|
3061
|
-
href,
|
|
3062
|
-
// for backwards compat
|
|
3063
|
-
normalizedTo: location,
|
|
3064
|
-
resolved: route
|
|
3065
|
-
}
|
|
3066
|
-
}
|
|
3067
|
-
|
|
3068
|
-
getRoutes () {
|
|
3069
|
-
return this.matcher.getRoutes()
|
|
3070
|
-
}
|
|
3071
|
-
|
|
3072
|
-
addRoute (parentOrRoute, route) {
|
|
3073
|
-
this.matcher.addRoute(parentOrRoute, route);
|
|
3074
|
-
if (this.history.current !== START) {
|
|
3075
|
-
this.history.transitionTo(this.history.getCurrentLocation());
|
|
3076
|
-
}
|
|
3077
|
-
}
|
|
3078
|
-
|
|
3079
|
-
addRoutes (routes) {
|
|
3080
|
-
{
|
|
3081
|
-
warn(false, 'router.addRoutes() is deprecated and has been removed in Kdu Router 4. Use router.addRoute() instead.');
|
|
3082
|
-
}
|
|
3083
|
-
this.matcher.addRoutes(routes);
|
|
3084
|
-
if (this.history.current !== START) {
|
|
3085
|
-
this.history.transitionTo(this.history.getCurrentLocation());
|
|
3086
|
-
}
|
|
3087
|
-
}
|
|
3088
|
-
}
|
|
3089
|
-
|
|
3090
|
-
function registerHook (list, fn) {
|
|
3091
|
-
list.push(fn);
|
|
3092
|
-
return () => {
|
|
3093
|
-
const i = list.indexOf(fn);
|
|
3094
|
-
if (i > -1) list.splice(i, 1);
|
|
3095
|
-
}
|
|
3096
|
-
}
|
|
3097
|
-
|
|
3098
|
-
function createHref (base, fullPath, mode) {
|
|
3099
|
-
var path = mode === 'hash' ? '#' + fullPath : fullPath;
|
|
3100
|
-
return base ? cleanPath(base + '/' + path) : path
|
|
3101
|
-
}
|
|
3102
|
-
|
|
3103
|
-
KduRouter.install = install;
|
|
3104
|
-
KduRouter.version = '3.5.4';
|
|
3105
|
-
KduRouter.isNavigationFailure = isNavigationFailure;
|
|
3106
|
-
KduRouter.NavigationFailureType = NavigationFailureType;
|
|
3107
|
-
KduRouter.START_LOCATION = START;
|
|
3108
|
-
|
|
3109
|
-
if (inBrowser && window.Kdu) {
|
|
3110
|
-
window.Kdu.use(KduRouter);
|
|
3111
|
-
}
|
|
3112
|
-
|
|
3113
|
-
export default KduRouter;
|