kdu-router 3.1.7 → 3.5.4
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/README.md +2 -11
- package/dist/kdu-router.common.js +640 -410
- package/dist/kdu-router.esm.browser.js +617 -371
- package/dist/kdu-router.esm.browser.min.js +3 -3
- package/dist/kdu-router.esm.js +640 -410
- package/dist/kdu-router.js +644 -415
- package/dist/kdu-router.min.js +3 -3
- package/ketur/attributes.json +38 -0
- package/ketur/tags.json +20 -0
- package/package.json +23 -14
- package/src/components/link.js +38 -4
- package/src/components/view.js +7 -1
- package/src/create-matcher.js +28 -2
- package/src/create-route-map.js +18 -3
- package/src/history/abstract.js +6 -3
- package/src/history/base.js +91 -64
- package/src/history/hash.js +25 -27
- package/src/history/html5.js +25 -6
- package/src/index.js +76 -44
- package/src/util/errors.js +86 -0
- package/src/util/path.js +1 -1
- package/src/util/push-state.js +1 -1
- package/src/util/query.js +49 -31
- package/src/util/resolve-components.js +2 -1
- package/src/util/route.js +29 -10
- package/src/util/scroll.js +26 -7
- package/src/util/warn.js +1 -12
- package/types/index.d.ts +6 -1
- package/types/router.d.ts +77 -11
- package/src/history/errors.js +0 -22
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* kdu-router v3.
|
|
2
|
+
* kdu-router v3.5.4
|
|
3
3
|
* (c) 2022 NKDuy
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
@@ -12,23 +12,11 @@ function assert (condition, message) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function warn (condition, message) {
|
|
15
|
-
if (
|
|
15
|
+
if (!condition) {
|
|
16
16
|
typeof console !== 'undefined' && console.warn(`[kdu-router] ${message}`);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function isError (err) {
|
|
21
|
-
return Object.prototype.toString.call(err).indexOf('Error') > -1
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function isExtendedError (constructor, err) {
|
|
25
|
-
return (
|
|
26
|
-
err instanceof constructor ||
|
|
27
|
-
// _name is to support IE9 too
|
|
28
|
-
(err && (err.name === constructor.name || err._name === constructor._name))
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
20
|
function extend (a, b) {
|
|
33
21
|
for (const key in b) {
|
|
34
22
|
a[key] = b[key];
|
|
@@ -36,153 +24,6 @@ function extend (a, b) {
|
|
|
36
24
|
return a
|
|
37
25
|
}
|
|
38
26
|
|
|
39
|
-
var View = {
|
|
40
|
-
name: 'RouterView',
|
|
41
|
-
functional: true,
|
|
42
|
-
props: {
|
|
43
|
-
name: {
|
|
44
|
-
type: String,
|
|
45
|
-
default: 'default'
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
render (_, { props, children, parent, data }) {
|
|
49
|
-
// used by devtools to display a router-view badge
|
|
50
|
-
data.routerView = true;
|
|
51
|
-
|
|
52
|
-
// directly use parent context's createElement() function
|
|
53
|
-
// so that components rendered by router-view can resolve named slots
|
|
54
|
-
const h = parent.$createElement;
|
|
55
|
-
const name = props.name;
|
|
56
|
-
const route = parent.$route;
|
|
57
|
-
const cache = parent._routerViewCache || (parent._routerViewCache = {});
|
|
58
|
-
|
|
59
|
-
// determine current view depth, also check to see if the tree
|
|
60
|
-
// has been toggled inactive but kept-alive.
|
|
61
|
-
let depth = 0;
|
|
62
|
-
let inactive = false;
|
|
63
|
-
while (parent && parent._routerRoot !== parent) {
|
|
64
|
-
const knodeData = parent.$knode ? parent.$knode.data : {};
|
|
65
|
-
if (knodeData.routerView) {
|
|
66
|
-
depth++;
|
|
67
|
-
}
|
|
68
|
-
if (knodeData.keepAlive && parent._directInactive && parent._inactive) {
|
|
69
|
-
inactive = true;
|
|
70
|
-
}
|
|
71
|
-
parent = parent.$parent;
|
|
72
|
-
}
|
|
73
|
-
data.routerViewDepth = depth;
|
|
74
|
-
|
|
75
|
-
// render previous view if the tree is inactive and kept-alive
|
|
76
|
-
if (inactive) {
|
|
77
|
-
const cachedData = cache[name];
|
|
78
|
-
const cachedComponent = cachedData && cachedData.component;
|
|
79
|
-
if (cachedComponent) {
|
|
80
|
-
// #2301
|
|
81
|
-
// pass props
|
|
82
|
-
if (cachedData.configProps) {
|
|
83
|
-
fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps);
|
|
84
|
-
}
|
|
85
|
-
return h(cachedComponent, data, children)
|
|
86
|
-
} else {
|
|
87
|
-
// render previous empty view
|
|
88
|
-
return h()
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const matched = route.matched[depth];
|
|
93
|
-
const component = matched && matched.components[name];
|
|
94
|
-
|
|
95
|
-
// render empty node if no matched route or no config component
|
|
96
|
-
if (!matched || !component) {
|
|
97
|
-
cache[name] = null;
|
|
98
|
-
return h()
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// cache component
|
|
102
|
-
cache[name] = { component };
|
|
103
|
-
|
|
104
|
-
// attach instance registration hook
|
|
105
|
-
// this will be called in the instance's injected lifecycle hooks
|
|
106
|
-
data.registerRouteInstance = (vm, val) => {
|
|
107
|
-
// val could be undefined for unregistration
|
|
108
|
-
const current = matched.instances[name];
|
|
109
|
-
if (
|
|
110
|
-
(val && current !== vm) ||
|
|
111
|
-
(!val && current === vm)
|
|
112
|
-
) {
|
|
113
|
-
matched.instances[name] = val;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// also register instance in prepatch hook
|
|
118
|
-
// in case the same component instance is reused across different routes
|
|
119
|
-
;(data.hook || (data.hook = {})).prepatch = (_, knode) => {
|
|
120
|
-
matched.instances[name] = knode.componentInstance;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// register instance in init hook
|
|
124
|
-
// in case kept-alive component be actived when routes changed
|
|
125
|
-
data.hook.init = (knode) => {
|
|
126
|
-
if (knode.data.keepAlive &&
|
|
127
|
-
knode.componentInstance &&
|
|
128
|
-
knode.componentInstance !== matched.instances[name]
|
|
129
|
-
) {
|
|
130
|
-
matched.instances[name] = knode.componentInstance;
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const configProps = matched.props && matched.props[name];
|
|
135
|
-
// save route and configProps in cachce
|
|
136
|
-
if (configProps) {
|
|
137
|
-
extend(cache[name], {
|
|
138
|
-
route,
|
|
139
|
-
configProps
|
|
140
|
-
});
|
|
141
|
-
fillPropsinData(component, data, route, configProps);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return h(component, data, children)
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
function fillPropsinData (component, data, route, configProps) {
|
|
149
|
-
// resolve props
|
|
150
|
-
let propsToPass = data.props = resolveProps(route, configProps);
|
|
151
|
-
if (propsToPass) {
|
|
152
|
-
// clone to prevent mutation
|
|
153
|
-
propsToPass = data.props = extend({}, propsToPass);
|
|
154
|
-
// pass non-declared props as attrs
|
|
155
|
-
const attrs = data.attrs = data.attrs || {};
|
|
156
|
-
for (const key in propsToPass) {
|
|
157
|
-
if (!component.props || !(key in component.props)) {
|
|
158
|
-
attrs[key] = propsToPass[key];
|
|
159
|
-
delete propsToPass[key];
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function resolveProps (route, config) {
|
|
166
|
-
switch (typeof config) {
|
|
167
|
-
case 'undefined':
|
|
168
|
-
return
|
|
169
|
-
case 'object':
|
|
170
|
-
return config
|
|
171
|
-
case 'function':
|
|
172
|
-
return config(route)
|
|
173
|
-
case 'boolean':
|
|
174
|
-
return config ? route.params : undefined
|
|
175
|
-
default:
|
|
176
|
-
{
|
|
177
|
-
warn(
|
|
178
|
-
false,
|
|
179
|
-
`props in "${route.path}" is a ${typeof config}, ` +
|
|
180
|
-
`expecting an object, function or boolean.`
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
27
|
/* */
|
|
187
28
|
|
|
188
29
|
const encodeReserveRE = /[!'()*]/g;
|
|
@@ -192,11 +33,21 @@ const commaRE = /%2C/g;
|
|
|
192
33
|
// fixed encodeURIComponent which is more conformant to RFC3986:
|
|
193
34
|
// - escapes [!'()*]
|
|
194
35
|
// - preserve commas
|
|
195
|
-
const encode = str =>
|
|
196
|
-
|
|
197
|
-
|
|
36
|
+
const encode = str =>
|
|
37
|
+
encodeURIComponent(str)
|
|
38
|
+
.replace(encodeReserveRE, encodeReserveReplacer)
|
|
39
|
+
.replace(commaRE, ',');
|
|
198
40
|
|
|
199
|
-
|
|
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
|
+
}
|
|
200
51
|
|
|
201
52
|
function resolveQuery (
|
|
202
53
|
query,
|
|
@@ -208,15 +59,20 @@ function resolveQuery (
|
|
|
208
59
|
try {
|
|
209
60
|
parsedQuery = parse(query || '');
|
|
210
61
|
} catch (e) {
|
|
211
|
-
|
|
62
|
+
warn(false, e.message);
|
|
212
63
|
parsedQuery = {};
|
|
213
64
|
}
|
|
214
65
|
for (const key in extraQuery) {
|
|
215
|
-
|
|
66
|
+
const value = extraQuery[key];
|
|
67
|
+
parsedQuery[key] = Array.isArray(value)
|
|
68
|
+
? value.map(castQueryParamValue)
|
|
69
|
+
: castQueryParamValue(value);
|
|
216
70
|
}
|
|
217
71
|
return parsedQuery
|
|
218
72
|
}
|
|
219
73
|
|
|
74
|
+
const castQueryParamValue = value => (value == null || typeof value === 'object' ? value : String(value));
|
|
75
|
+
|
|
220
76
|
function parseQuery (query) {
|
|
221
77
|
const res = {};
|
|
222
78
|
|
|
@@ -229,9 +85,7 @@ function parseQuery (query) {
|
|
|
229
85
|
query.split('&').forEach(param => {
|
|
230
86
|
const parts = param.replace(/\+/g, ' ').split('=');
|
|
231
87
|
const key = decode(parts.shift());
|
|
232
|
-
const val = parts.length > 0
|
|
233
|
-
? decode(parts.join('='))
|
|
234
|
-
: null;
|
|
88
|
+
const val = parts.length > 0 ? decode(parts.join('=')) : null;
|
|
235
89
|
|
|
236
90
|
if (res[key] === undefined) {
|
|
237
91
|
res[key] = val;
|
|
@@ -246,34 +100,39 @@ function parseQuery (query) {
|
|
|
246
100
|
}
|
|
247
101
|
|
|
248
102
|
function stringifyQuery (obj) {
|
|
249
|
-
const res = obj
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return ''
|
|
254
|
-
}
|
|
103
|
+
const res = obj
|
|
104
|
+
? Object.keys(obj)
|
|
105
|
+
.map(key => {
|
|
106
|
+
const val = obj[key];
|
|
255
107
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
108
|
+
if (val === undefined) {
|
|
109
|
+
return ''
|
|
110
|
+
}
|
|
259
111
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
val.forEach(val2 => {
|
|
263
|
-
if (val2 === undefined) {
|
|
264
|
-
return
|
|
112
|
+
if (val === null) {
|
|
113
|
+
return encode(key)
|
|
265
114
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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('&')
|
|
270
129
|
}
|
|
271
|
-
});
|
|
272
|
-
return result.join('&')
|
|
273
|
-
}
|
|
274
130
|
|
|
275
|
-
|
|
276
|
-
|
|
131
|
+
return encode(key) + '=' + encode(val)
|
|
132
|
+
})
|
|
133
|
+
.filter(x => x.length > 0)
|
|
134
|
+
.join('&')
|
|
135
|
+
: null;
|
|
277
136
|
return res ? `?${res}` : ''
|
|
278
137
|
}
|
|
279
138
|
|
|
@@ -346,23 +205,23 @@ function getFullPath (
|
|
|
346
205
|
return (path || '/') + stringify(query) + hash
|
|
347
206
|
}
|
|
348
207
|
|
|
349
|
-
function isSameRoute (a, b) {
|
|
208
|
+
function isSameRoute (a, b, onlyPath) {
|
|
350
209
|
if (b === START) {
|
|
351
210
|
return a === b
|
|
352
211
|
} else if (!b) {
|
|
353
212
|
return false
|
|
354
213
|
} else if (a.path && b.path) {
|
|
355
|
-
return (
|
|
356
|
-
a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
|
|
214
|
+
return a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') && (onlyPath ||
|
|
357
215
|
a.hash === b.hash &&
|
|
358
|
-
isObjectEqual(a.query, b.query)
|
|
359
|
-
)
|
|
216
|
+
isObjectEqual(a.query, b.query))
|
|
360
217
|
} else if (a.name && b.name) {
|
|
361
218
|
return (
|
|
362
219
|
a.name === b.name &&
|
|
363
|
-
|
|
220
|
+
(onlyPath || (
|
|
221
|
+
a.hash === b.hash &&
|
|
364
222
|
isObjectEqual(a.query, b.query) &&
|
|
365
|
-
isObjectEqual(a.params, b.params)
|
|
223
|
+
isObjectEqual(a.params, b.params))
|
|
224
|
+
)
|
|
366
225
|
)
|
|
367
226
|
} else {
|
|
368
227
|
return false
|
|
@@ -372,14 +231,18 @@ function isSameRoute (a, b) {
|
|
|
372
231
|
function isObjectEqual (a = {}, b = {}) {
|
|
373
232
|
// handle null value #1566
|
|
374
233
|
if (!a || !b) return a === b
|
|
375
|
-
const aKeys = Object.keys(a);
|
|
376
|
-
const bKeys = Object.keys(b);
|
|
234
|
+
const aKeys = Object.keys(a).sort();
|
|
235
|
+
const bKeys = Object.keys(b).sort();
|
|
377
236
|
if (aKeys.length !== bKeys.length) {
|
|
378
237
|
return false
|
|
379
238
|
}
|
|
380
|
-
return aKeys.every(key => {
|
|
239
|
+
return aKeys.every((key, i) => {
|
|
381
240
|
const aVal = a[key];
|
|
241
|
+
const bKey = bKeys[i];
|
|
242
|
+
if (bKey !== key) return false
|
|
382
243
|
const bVal = b[key];
|
|
244
|
+
// query values can be null and undefined
|
|
245
|
+
if (aVal == null || bVal == null) return aVal === bVal
|
|
383
246
|
// check nested equality
|
|
384
247
|
if (typeof aVal === 'object' && typeof bVal === 'object') {
|
|
385
248
|
return isObjectEqual(aVal, bVal)
|
|
@@ -407,6 +270,173 @@ function queryIncludes (current, target) {
|
|
|
407
270
|
return true
|
|
408
271
|
}
|
|
409
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
|
+
|
|
410
440
|
/* */
|
|
411
441
|
|
|
412
442
|
function resolvePath (
|
|
@@ -475,7 +505,7 @@ function parsePath (path) {
|
|
|
475
505
|
}
|
|
476
506
|
|
|
477
507
|
function cleanPath (path) {
|
|
478
|
-
return path.replace(
|
|
508
|
+
return path.replace(/\/(?:\s*\/)+/g, '/')
|
|
479
509
|
}
|
|
480
510
|
|
|
481
511
|
var isarray = Array.isArray || function (arr) {
|
|
@@ -1015,6 +1045,10 @@ const eventTypes = [String, Array];
|
|
|
1015
1045
|
|
|
1016
1046
|
const noop = () => {};
|
|
1017
1047
|
|
|
1048
|
+
let warnedCustomSlot;
|
|
1049
|
+
let warnedTagProp;
|
|
1050
|
+
let warnedEventProp;
|
|
1051
|
+
|
|
1018
1052
|
var Link = {
|
|
1019
1053
|
name: 'RouterLink',
|
|
1020
1054
|
props: {
|
|
@@ -1026,11 +1060,17 @@ var Link = {
|
|
|
1026
1060
|
type: String,
|
|
1027
1061
|
default: 'a'
|
|
1028
1062
|
},
|
|
1063
|
+
custom: Boolean,
|
|
1029
1064
|
exact: Boolean,
|
|
1065
|
+
exactPath: Boolean,
|
|
1030
1066
|
append: Boolean,
|
|
1031
1067
|
replace: Boolean,
|
|
1032
1068
|
activeClass: String,
|
|
1033
1069
|
exactActiveClass: String,
|
|
1070
|
+
ariaCurrentValue: {
|
|
1071
|
+
type: String,
|
|
1072
|
+
default: 'page'
|
|
1073
|
+
},
|
|
1034
1074
|
event: {
|
|
1035
1075
|
type: eventTypes,
|
|
1036
1076
|
default: 'click'
|
|
@@ -1066,11 +1106,13 @@ var Link = {
|
|
|
1066
1106
|
? createRoute(null, normalizeLocation(route.redirectedFrom), null, router)
|
|
1067
1107
|
: route;
|
|
1068
1108
|
|
|
1069
|
-
classes[exactActiveClass] = isSameRoute(current, compareTarget);
|
|
1070
|
-
classes[activeClass] = this.exact
|
|
1109
|
+
classes[exactActiveClass] = isSameRoute(current, compareTarget, this.exactPath);
|
|
1110
|
+
classes[activeClass] = this.exact || this.exactPath
|
|
1071
1111
|
? classes[exactActiveClass]
|
|
1072
1112
|
: isIncludedRoute(current, compareTarget);
|
|
1073
1113
|
|
|
1114
|
+
const ariaCurrentValue = classes[exactActiveClass] ? this.ariaCurrentValue : null;
|
|
1115
|
+
|
|
1074
1116
|
const handler = e => {
|
|
1075
1117
|
if (guardEvent(e)) {
|
|
1076
1118
|
if (this.replace) {
|
|
@@ -1104,13 +1146,17 @@ var Link = {
|
|
|
1104
1146
|
});
|
|
1105
1147
|
|
|
1106
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
|
+
}
|
|
1107
1153
|
if (scopedSlot.length === 1) {
|
|
1108
1154
|
return scopedSlot[0]
|
|
1109
1155
|
} else if (scopedSlot.length > 1 || !scopedSlot.length) {
|
|
1110
1156
|
{
|
|
1111
1157
|
warn(
|
|
1112
1158
|
false,
|
|
1113
|
-
|
|
1159
|
+
`<router-link> with to="${
|
|
1114
1160
|
this.to
|
|
1115
1161
|
}" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element.`
|
|
1116
1162
|
);
|
|
@@ -1119,9 +1165,26 @@ var Link = {
|
|
|
1119
1165
|
}
|
|
1120
1166
|
}
|
|
1121
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
|
+
|
|
1122
1185
|
if (this.tag === 'a') {
|
|
1123
1186
|
data.on = on;
|
|
1124
|
-
data.attrs = { href };
|
|
1187
|
+
data.attrs = { href, 'aria-current': ariaCurrentValue };
|
|
1125
1188
|
} else {
|
|
1126
1189
|
// find the first <a> child and apply listener and href
|
|
1127
1190
|
const a = findAnchor(this.$slots.default);
|
|
@@ -1149,6 +1212,7 @@ var Link = {
|
|
|
1149
1212
|
|
|
1150
1213
|
const aAttrs = (a.data.attrs = extend({}, a.data.attrs));
|
|
1151
1214
|
aAttrs.href = href;
|
|
1215
|
+
aAttrs['aria-current'] = ariaCurrentValue;
|
|
1152
1216
|
} else {
|
|
1153
1217
|
// doesn't have <a> child, apply listener to self
|
|
1154
1218
|
data.on = on;
|
|
@@ -1253,7 +1317,8 @@ function createRouteMap (
|
|
|
1253
1317
|
routes,
|
|
1254
1318
|
oldPathList,
|
|
1255
1319
|
oldPathMap,
|
|
1256
|
-
oldNameMap
|
|
1320
|
+
oldNameMap,
|
|
1321
|
+
parentRoute
|
|
1257
1322
|
) {
|
|
1258
1323
|
// the path list is used to control path matching priority
|
|
1259
1324
|
const pathList = oldPathList || [];
|
|
@@ -1263,7 +1328,7 @@ function createRouteMap (
|
|
|
1263
1328
|
const nameMap = oldNameMap || Object.create(null);
|
|
1264
1329
|
|
|
1265
1330
|
routes.forEach(route => {
|
|
1266
|
-
addRouteRecord(pathList, pathMap, nameMap, route);
|
|
1331
|
+
addRouteRecord(pathList, pathMap, nameMap, route, parentRoute);
|
|
1267
1332
|
});
|
|
1268
1333
|
|
|
1269
1334
|
// ensure wildcard routes are always at the end
|
|
@@ -1311,6 +1376,14 @@ function addRouteRecord (
|
|
|
1311
1376
|
path || name
|
|
1312
1377
|
)} cannot be a ` + `string id. Use an actual component instead.`
|
|
1313
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
|
+
);
|
|
1314
1387
|
}
|
|
1315
1388
|
|
|
1316
1389
|
const pathToRegexpOptions =
|
|
@@ -1325,7 +1398,13 @@ function addRouteRecord (
|
|
|
1325
1398
|
path: normalizedPath,
|
|
1326
1399
|
regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
|
|
1327
1400
|
components: route.components || { default: route.component },
|
|
1401
|
+
alias: route.alias
|
|
1402
|
+
? typeof route.alias === 'string'
|
|
1403
|
+
? [route.alias]
|
|
1404
|
+
: route.alias
|
|
1405
|
+
: [],
|
|
1328
1406
|
instances: {},
|
|
1407
|
+
enteredCbs: {},
|
|
1329
1408
|
name,
|
|
1330
1409
|
parent,
|
|
1331
1410
|
matchAs,
|
|
@@ -1355,7 +1434,7 @@ function addRouteRecord (
|
|
|
1355
1434
|
`Named Route '${route.name}' has a default child route. ` +
|
|
1356
1435
|
`When navigating to this named route (:to="{name: '${
|
|
1357
1436
|
route.name
|
|
1358
|
-
}'"), ` +
|
|
1437
|
+
}'}"), ` +
|
|
1359
1438
|
`the default child route will not be rendered. Remove the name from ` +
|
|
1360
1439
|
`this route and use the name of the default child route for named ` +
|
|
1361
1440
|
`links instead.`
|
|
@@ -1379,7 +1458,7 @@ function addRouteRecord (
|
|
|
1379
1458
|
const aliases = Array.isArray(route.alias) ? route.alias : [route.alias];
|
|
1380
1459
|
for (let i = 0; i < aliases.length; ++i) {
|
|
1381
1460
|
const alias = aliases[i];
|
|
1382
|
-
if (
|
|
1461
|
+
if (alias === path) {
|
|
1383
1462
|
warn(
|
|
1384
1463
|
false,
|
|
1385
1464
|
`Found an alias with the same value as the path: "${path}". You have to remove that alias. It will be ignored in development.`
|
|
@@ -1406,7 +1485,7 @@ function addRouteRecord (
|
|
|
1406
1485
|
if (name) {
|
|
1407
1486
|
if (!nameMap[name]) {
|
|
1408
1487
|
nameMap[name] = record;
|
|
1409
|
-
} else if (
|
|
1488
|
+
} else if (!matchAs) {
|
|
1410
1489
|
warn(
|
|
1411
1490
|
false,
|
|
1412
1491
|
`Duplicate named routes definition: ` +
|
|
@@ -1459,6 +1538,28 @@ function createMatcher (
|
|
|
1459
1538
|
createRouteMap(routes, pathList, pathMap, nameMap);
|
|
1460
1539
|
}
|
|
1461
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
|
+
|
|
1462
1563
|
function match (
|
|
1463
1564
|
raw,
|
|
1464
1565
|
currentRoute,
|
|
@@ -1602,6 +1703,8 @@ function createMatcher (
|
|
|
1602
1703
|
|
|
1603
1704
|
return {
|
|
1604
1705
|
match,
|
|
1706
|
+
addRoute,
|
|
1707
|
+
getRoutes,
|
|
1605
1708
|
addRoutes
|
|
1606
1709
|
}
|
|
1607
1710
|
}
|
|
@@ -1621,10 +1724,9 @@ function matchRoute (
|
|
|
1621
1724
|
|
|
1622
1725
|
for (let i = 1, len = m.length; i < len; ++i) {
|
|
1623
1726
|
const key = regex.keys[i - 1];
|
|
1624
|
-
const val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i];
|
|
1625
1727
|
if (key) {
|
|
1626
1728
|
// Fix #1994: using * with props: true generates a param named 0
|
|
1627
|
-
params[key.name || 'pathMatch'] =
|
|
1729
|
+
params[key.name || 'pathMatch'] = typeof m[i] === 'string' ? decode(m[i]) : m[i];
|
|
1628
1730
|
}
|
|
1629
1731
|
}
|
|
1630
1732
|
|
|
@@ -1662,6 +1764,10 @@ function setStateKey (key) {
|
|
|
1662
1764
|
const positionStore = Object.create(null);
|
|
1663
1765
|
|
|
1664
1766
|
function setupScroll () {
|
|
1767
|
+
// Prevent browser scroll behavior on History popstate
|
|
1768
|
+
if ('scrollRestoration' in window.history) {
|
|
1769
|
+
window.history.scrollRestoration = 'manual';
|
|
1770
|
+
}
|
|
1665
1771
|
// Fix for #1585 for Firefox
|
|
1666
1772
|
// Fix for #2195 Add optional third attribute to workaround a bug in safari https://bugs.webkit.org/show_bug.cgi?id=182678
|
|
1667
1773
|
// Fix for #2774 Support for apps loaded from Windows file shares not mapped to network drives: replaced location.origin with
|
|
@@ -1673,12 +1779,10 @@ function setupScroll () {
|
|
|
1673
1779
|
const stateCopy = extend({}, window.history.state);
|
|
1674
1780
|
stateCopy.key = getStateKey();
|
|
1675
1781
|
window.history.replaceState(stateCopy, '', absolutePath);
|
|
1676
|
-
window.addEventListener('popstate',
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
}
|
|
1681
|
-
});
|
|
1782
|
+
window.addEventListener('popstate', handlePopState);
|
|
1783
|
+
return () => {
|
|
1784
|
+
window.removeEventListener('popstate', handlePopState);
|
|
1785
|
+
}
|
|
1682
1786
|
}
|
|
1683
1787
|
|
|
1684
1788
|
function handleScroll (
|
|
@@ -1740,6 +1844,13 @@ function saveScrollPosition () {
|
|
|
1740
1844
|
}
|
|
1741
1845
|
}
|
|
1742
1846
|
|
|
1847
|
+
function handlePopState (e) {
|
|
1848
|
+
saveScrollPosition();
|
|
1849
|
+
if (e.state && e.state.key) {
|
|
1850
|
+
setStateKey(e.state.key);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1743
1854
|
function getScrollPosition () {
|
|
1744
1855
|
const key = getStateKey();
|
|
1745
1856
|
if (key) {
|
|
@@ -1805,7 +1916,17 @@ function scrollToPosition (shouldScroll, position) {
|
|
|
1805
1916
|
}
|
|
1806
1917
|
|
|
1807
1918
|
if (position) {
|
|
1808
|
-
|
|
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
|
+
}
|
|
1809
1930
|
}
|
|
1810
1931
|
}
|
|
1811
1932
|
|
|
@@ -1825,7 +1946,7 @@ const supportsPushState =
|
|
|
1825
1946
|
return false
|
|
1826
1947
|
}
|
|
1827
1948
|
|
|
1828
|
-
return window.history &&
|
|
1949
|
+
return window.history && typeof window.history.pushState === 'function'
|
|
1829
1950
|
})();
|
|
1830
1951
|
|
|
1831
1952
|
function pushState (url, replace) {
|
|
@@ -1870,6 +1991,93 @@ function runQueue (queue, fn, cb) {
|
|
|
1870
1991
|
step(0);
|
|
1871
1992
|
}
|
|
1872
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
|
+
|
|
1873
2081
|
/* */
|
|
1874
2082
|
|
|
1875
2083
|
function resolveAsyncComponents (matched) {
|
|
@@ -1905,7 +2113,7 @@ function resolveAsyncComponents (matched) {
|
|
|
1905
2113
|
|
|
1906
2114
|
const reject = once(reason => {
|
|
1907
2115
|
const msg = `Failed to resolve async component ${key}: ${reason}`;
|
|
1908
|
-
|
|
2116
|
+
warn(false, msg);
|
|
1909
2117
|
if (!error) {
|
|
1910
2118
|
error = isError(reason)
|
|
1911
2119
|
? reason
|
|
@@ -1976,29 +2184,6 @@ function once (fn) {
|
|
|
1976
2184
|
}
|
|
1977
2185
|
}
|
|
1978
2186
|
|
|
1979
|
-
class NavigationDuplicated extends Error {
|
|
1980
|
-
constructor (normalizedLocation) {
|
|
1981
|
-
super();
|
|
1982
|
-
this.name = this._name = 'NavigationDuplicated';
|
|
1983
|
-
// passing the message to super() doesn't seem to work in the transpiled version
|
|
1984
|
-
this.message = `Navigating to current location ("${
|
|
1985
|
-
normalizedLocation.fullPath
|
|
1986
|
-
}") is not allowed`;
|
|
1987
|
-
// add a stack property so services like Sentry can correctly display it
|
|
1988
|
-
Object.defineProperty(this, 'stack', {
|
|
1989
|
-
value: new Error().stack,
|
|
1990
|
-
writable: true,
|
|
1991
|
-
configurable: true
|
|
1992
|
-
});
|
|
1993
|
-
// we could also have used
|
|
1994
|
-
// Error.captureStackTrace(this, this.constructor)
|
|
1995
|
-
// but it only exists on node and chrome
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
|
|
1999
|
-
// support IE9
|
|
2000
|
-
NavigationDuplicated._name = 'NavigationDuplicated';
|
|
2001
|
-
|
|
2002
2187
|
/* */
|
|
2003
2188
|
|
|
2004
2189
|
class History {
|
|
@@ -2011,6 +2196,8 @@ class History {
|
|
|
2011
2196
|
|
|
2012
2197
|
|
|
2013
2198
|
|
|
2199
|
+
|
|
2200
|
+
|
|
2014
2201
|
|
|
2015
2202
|
// implemented by sub-classes
|
|
2016
2203
|
|
|
@@ -2018,6 +2205,7 @@ class History {
|
|
|
2018
2205
|
|
|
2019
2206
|
|
|
2020
2207
|
|
|
2208
|
+
|
|
2021
2209
|
|
|
2022
2210
|
constructor (router, base) {
|
|
2023
2211
|
this.router = router;
|
|
@@ -2029,6 +2217,7 @@ class History {
|
|
|
2029
2217
|
this.readyCbs = [];
|
|
2030
2218
|
this.readyErrorCbs = [];
|
|
2031
2219
|
this.errorCbs = [];
|
|
2220
|
+
this.listeners = [];
|
|
2032
2221
|
}
|
|
2033
2222
|
|
|
2034
2223
|
listen (cb) {
|
|
@@ -2055,13 +2244,27 @@ class History {
|
|
|
2055
2244
|
onComplete,
|
|
2056
2245
|
onAbort
|
|
2057
2246
|
) {
|
|
2058
|
-
|
|
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;
|
|
2059
2259
|
this.confirmTransition(
|
|
2060
2260
|
route,
|
|
2061
2261
|
() => {
|
|
2062
2262
|
this.updateRoute(route);
|
|
2063
2263
|
onComplete && onComplete(route);
|
|
2064
2264
|
this.ensureURL();
|
|
2265
|
+
this.router.afterHooks.forEach(hook => {
|
|
2266
|
+
hook && hook(route, prev);
|
|
2267
|
+
});
|
|
2065
2268
|
|
|
2066
2269
|
// fire ready cbs once
|
|
2067
2270
|
if (!this.ready) {
|
|
@@ -2076,10 +2279,14 @@ class History {
|
|
|
2076
2279
|
onAbort(err);
|
|
2077
2280
|
}
|
|
2078
2281
|
if (err && !this.ready) {
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
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
|
+
}
|
|
2083
2290
|
}
|
|
2084
2291
|
}
|
|
2085
2292
|
);
|
|
@@ -2087,29 +2294,37 @@ class History {
|
|
|
2087
2294
|
|
|
2088
2295
|
confirmTransition (route, onComplete, onAbort) {
|
|
2089
2296
|
const current = this.current;
|
|
2297
|
+
this.pending = route;
|
|
2090
2298
|
const abort = err => {
|
|
2091
|
-
//
|
|
2092
|
-
//
|
|
2093
|
-
|
|
2094
|
-
if (!isExtendedError(NavigationDuplicated, err) && isError(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)) {
|
|
2095
2302
|
if (this.errorCbs.length) {
|
|
2096
2303
|
this.errorCbs.forEach(cb => {
|
|
2097
2304
|
cb(err);
|
|
2098
2305
|
});
|
|
2099
2306
|
} else {
|
|
2100
|
-
|
|
2307
|
+
{
|
|
2308
|
+
warn(false, 'uncaught error during route navigation:');
|
|
2309
|
+
}
|
|
2101
2310
|
console.error(err);
|
|
2102
2311
|
}
|
|
2103
2312
|
}
|
|
2104
2313
|
onAbort && onAbort(err);
|
|
2105
2314
|
};
|
|
2315
|
+
const lastRouteIndex = route.matched.length - 1;
|
|
2316
|
+
const lastCurrentIndex = current.matched.length - 1;
|
|
2106
2317
|
if (
|
|
2107
2318
|
isSameRoute(route, current) &&
|
|
2108
2319
|
// in the case the route map has been dynamically appended to
|
|
2109
|
-
|
|
2320
|
+
lastRouteIndex === lastCurrentIndex &&
|
|
2321
|
+
route.matched[lastRouteIndex] === current.matched[lastCurrentIndex]
|
|
2110
2322
|
) {
|
|
2111
2323
|
this.ensureURL();
|
|
2112
|
-
|
|
2324
|
+
if (route.hash) {
|
|
2325
|
+
handleScroll(this.router, current, route, false);
|
|
2326
|
+
}
|
|
2327
|
+
return abort(createNavigationDuplicatedError(current, route))
|
|
2113
2328
|
}
|
|
2114
2329
|
|
|
2115
2330
|
const { updated, deactivated, activated } = resolveQueue(
|
|
@@ -2130,15 +2345,17 @@ class History {
|
|
|
2130
2345
|
resolveAsyncComponents(activated)
|
|
2131
2346
|
);
|
|
2132
2347
|
|
|
2133
|
-
this.pending = route;
|
|
2134
2348
|
const iterator = (hook, next) => {
|
|
2135
2349
|
if (this.pending !== route) {
|
|
2136
|
-
return abort()
|
|
2350
|
+
return abort(createNavigationCancelledError(current, route))
|
|
2137
2351
|
}
|
|
2138
2352
|
try {
|
|
2139
2353
|
hook(route, current, (to) => {
|
|
2140
|
-
if (to === false
|
|
2354
|
+
if (to === false) {
|
|
2141
2355
|
// next(false) -> abort navigation, ensure current URL
|
|
2356
|
+
this.ensureURL(true);
|
|
2357
|
+
abort(createNavigationAbortedError(current, route));
|
|
2358
|
+
} else if (isError(to)) {
|
|
2142
2359
|
this.ensureURL(true);
|
|
2143
2360
|
abort(to);
|
|
2144
2361
|
} else if (
|
|
@@ -2147,7 +2364,7 @@ class History {
|
|
|
2147
2364
|
(typeof to.path === 'string' || typeof to.name === 'string'))
|
|
2148
2365
|
) {
|
|
2149
2366
|
// next('/') or next({ path: '/' }) -> redirect
|
|
2150
|
-
abort();
|
|
2367
|
+
abort(createNavigationRedirectedError(current, route));
|
|
2151
2368
|
if (typeof to === 'object' && to.replace) {
|
|
2152
2369
|
this.replace(to);
|
|
2153
2370
|
} else {
|
|
@@ -2164,23 +2381,19 @@ class History {
|
|
|
2164
2381
|
};
|
|
2165
2382
|
|
|
2166
2383
|
runQueue(queue, iterator, () => {
|
|
2167
|
-
const postEnterCbs = [];
|
|
2168
|
-
const isValid = () => this.current === route;
|
|
2169
2384
|
// wait until async components are resolved before
|
|
2170
2385
|
// extracting in-component enter guards
|
|
2171
|
-
const enterGuards = extractEnterGuards(activated
|
|
2386
|
+
const enterGuards = extractEnterGuards(activated);
|
|
2172
2387
|
const queue = enterGuards.concat(this.router.resolveHooks);
|
|
2173
2388
|
runQueue(queue, iterator, () => {
|
|
2174
2389
|
if (this.pending !== route) {
|
|
2175
|
-
return abort()
|
|
2390
|
+
return abort(createNavigationCancelledError(current, route))
|
|
2176
2391
|
}
|
|
2177
2392
|
this.pending = null;
|
|
2178
2393
|
onComplete(route);
|
|
2179
2394
|
if (this.router.app) {
|
|
2180
2395
|
this.router.app.$nextTick(() => {
|
|
2181
|
-
|
|
2182
|
-
cb();
|
|
2183
|
-
});
|
|
2396
|
+
handleRouteEntered(route);
|
|
2184
2397
|
});
|
|
2185
2398
|
}
|
|
2186
2399
|
});
|
|
@@ -2188,12 +2401,24 @@ class History {
|
|
|
2188
2401
|
}
|
|
2189
2402
|
|
|
2190
2403
|
updateRoute (route) {
|
|
2191
|
-
const prev = this.current;
|
|
2192
2404
|
this.current = route;
|
|
2193
2405
|
this.cb && this.cb(route);
|
|
2194
|
-
|
|
2195
|
-
|
|
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();
|
|
2196
2416
|
});
|
|
2417
|
+
this.listeners = [];
|
|
2418
|
+
|
|
2419
|
+
// reset current history route
|
|
2420
|
+
this.current = START;
|
|
2421
|
+
this.pending = null;
|
|
2197
2422
|
}
|
|
2198
2423
|
}
|
|
2199
2424
|
|
|
@@ -2280,15 +2505,13 @@ function bindGuard (guard, instance) {
|
|
|
2280
2505
|
}
|
|
2281
2506
|
|
|
2282
2507
|
function extractEnterGuards (
|
|
2283
|
-
activated
|
|
2284
|
-
cbs,
|
|
2285
|
-
isValid
|
|
2508
|
+
activated
|
|
2286
2509
|
) {
|
|
2287
2510
|
return extractGuards(
|
|
2288
2511
|
activated,
|
|
2289
2512
|
'beforeRouteEnter',
|
|
2290
2513
|
(guard, _, match, key) => {
|
|
2291
|
-
return bindEnterGuard(guard, match, key
|
|
2514
|
+
return bindEnterGuard(guard, match, key)
|
|
2292
2515
|
}
|
|
2293
2516
|
)
|
|
2294
2517
|
}
|
|
@@ -2296,66 +2519,52 @@ function extractEnterGuards (
|
|
|
2296
2519
|
function bindEnterGuard (
|
|
2297
2520
|
guard,
|
|
2298
2521
|
match,
|
|
2299
|
-
key
|
|
2300
|
-
cbs,
|
|
2301
|
-
isValid
|
|
2522
|
+
key
|
|
2302
2523
|
) {
|
|
2303
2524
|
return function routeEnterGuard (to, from, next) {
|
|
2304
2525
|
return guard(to, from, cb => {
|
|
2305
2526
|
if (typeof cb === 'function') {
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
// we will need to poll for registration until current route
|
|
2311
|
-
// is no longer valid.
|
|
2312
|
-
poll(cb, match.instances, key, isValid);
|
|
2313
|
-
});
|
|
2527
|
+
if (!match.enteredCbs[key]) {
|
|
2528
|
+
match.enteredCbs[key] = [];
|
|
2529
|
+
}
|
|
2530
|
+
match.enteredCbs[key].push(cb);
|
|
2314
2531
|
}
|
|
2315
2532
|
next(cb);
|
|
2316
2533
|
})
|
|
2317
2534
|
}
|
|
2318
2535
|
}
|
|
2319
2536
|
|
|
2320
|
-
function poll (
|
|
2321
|
-
cb, // somehow flow cannot infer this is a function
|
|
2322
|
-
instances,
|
|
2323
|
-
key,
|
|
2324
|
-
isValid
|
|
2325
|
-
) {
|
|
2326
|
-
if (
|
|
2327
|
-
instances[key] &&
|
|
2328
|
-
!instances[key]._isBeingDestroyed // do not reuse being destroyed instance
|
|
2329
|
-
) {
|
|
2330
|
-
cb(instances[key]);
|
|
2331
|
-
} else if (isValid()) {
|
|
2332
|
-
setTimeout(() => {
|
|
2333
|
-
poll(cb, instances, key, isValid);
|
|
2334
|
-
}, 16);
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
2537
|
/* */
|
|
2339
2538
|
|
|
2340
2539
|
class HTML5History extends History {
|
|
2540
|
+
|
|
2541
|
+
|
|
2341
2542
|
constructor (router, base) {
|
|
2342
2543
|
super(router, base);
|
|
2343
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;
|
|
2344
2554
|
const expectScroll = router.options.scrollBehavior;
|
|
2345
2555
|
const supportsScroll = supportsPushState && expectScroll;
|
|
2346
2556
|
|
|
2347
2557
|
if (supportsScroll) {
|
|
2348
|
-
setupScroll();
|
|
2558
|
+
this.listeners.push(setupScroll());
|
|
2349
2559
|
}
|
|
2350
2560
|
|
|
2351
|
-
const
|
|
2352
|
-
window.addEventListener('popstate', e => {
|
|
2561
|
+
const handleRoutingEvent = () => {
|
|
2353
2562
|
const current = this.current;
|
|
2354
2563
|
|
|
2355
2564
|
// Avoiding first `popstate` event dispatched in some browsers but first
|
|
2356
2565
|
// history route not updated since async guard at the same time.
|
|
2357
2566
|
const location = getLocation(this.base);
|
|
2358
|
-
if (this.current === START && location ===
|
|
2567
|
+
if (this.current === START && location === this._startLocation) {
|
|
2359
2568
|
return
|
|
2360
2569
|
}
|
|
2361
2570
|
|
|
@@ -2364,6 +2573,10 @@ class HTML5History extends History {
|
|
|
2364
2573
|
handleScroll(router, route, current, true);
|
|
2365
2574
|
}
|
|
2366
2575
|
});
|
|
2576
|
+
};
|
|
2577
|
+
window.addEventListener('popstate', handleRoutingEvent);
|
|
2578
|
+
this.listeners.push(() => {
|
|
2579
|
+
window.removeEventListener('popstate', handleRoutingEvent);
|
|
2367
2580
|
});
|
|
2368
2581
|
}
|
|
2369
2582
|
|
|
@@ -2402,8 +2615,13 @@ class HTML5History extends History {
|
|
|
2402
2615
|
}
|
|
2403
2616
|
|
|
2404
2617
|
function getLocation (base) {
|
|
2405
|
-
let path =
|
|
2406
|
-
|
|
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))) {
|
|
2407
2625
|
path = path.slice(base.length);
|
|
2408
2626
|
}
|
|
2409
2627
|
return (path || '/') + window.location.search + window.location.hash
|
|
@@ -2424,31 +2642,40 @@ class HashHistory extends History {
|
|
|
2424
2642
|
// this is delayed until the app mounts
|
|
2425
2643
|
// to avoid the hashchange listener being fired too early
|
|
2426
2644
|
setupListeners () {
|
|
2645
|
+
if (this.listeners.length > 0) {
|
|
2646
|
+
return
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2427
2649
|
const router = this.router;
|
|
2428
2650
|
const expectScroll = router.options.scrollBehavior;
|
|
2429
2651
|
const supportsScroll = supportsPushState && expectScroll;
|
|
2430
2652
|
|
|
2431
2653
|
if (supportsScroll) {
|
|
2432
|
-
setupScroll();
|
|
2654
|
+
this.listeners.push(setupScroll());
|
|
2433
2655
|
}
|
|
2434
2656
|
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
()
|
|
2438
|
-
|
|
2439
|
-
if (!ensureSlash()) {
|
|
2440
|
-
return
|
|
2441
|
-
}
|
|
2442
|
-
this.transitionTo(getHash(), route => {
|
|
2443
|
-
if (supportsScroll) {
|
|
2444
|
-
handleScroll(this.router, route, current, true);
|
|
2445
|
-
}
|
|
2446
|
-
if (!supportsPushState) {
|
|
2447
|
-
replaceHash(route.fullPath);
|
|
2448
|
-
}
|
|
2449
|
-
});
|
|
2657
|
+
const handleRoutingEvent = () => {
|
|
2658
|
+
const current = this.current;
|
|
2659
|
+
if (!ensureSlash()) {
|
|
2660
|
+
return
|
|
2450
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
|
|
2451
2675
|
);
|
|
2676
|
+
this.listeners.push(() => {
|
|
2677
|
+
window.removeEventListener(eventType, handleRoutingEvent);
|
|
2678
|
+
});
|
|
2452
2679
|
}
|
|
2453
2680
|
|
|
2454
2681
|
push (location, onComplete, onAbort) {
|
|
@@ -2519,17 +2746,6 @@ function getHash () {
|
|
|
2519
2746
|
if (index < 0) return ''
|
|
2520
2747
|
|
|
2521
2748
|
href = href.slice(index + 1);
|
|
2522
|
-
// decode the hash but not the search or hash
|
|
2523
|
-
// as search(query) is already decoded
|
|
2524
|
-
const searchIndex = href.indexOf('?');
|
|
2525
|
-
if (searchIndex < 0) {
|
|
2526
|
-
const hashIndex = href.indexOf('#');
|
|
2527
|
-
if (hashIndex > -1) {
|
|
2528
|
-
href = decodeURI(href.slice(0, hashIndex)) + href.slice(hashIndex);
|
|
2529
|
-
} else href = decodeURI(href);
|
|
2530
|
-
} else {
|
|
2531
|
-
href = decodeURI(href.slice(0, searchIndex)) + href.slice(searchIndex);
|
|
2532
|
-
}
|
|
2533
2749
|
|
|
2534
2750
|
return href
|
|
2535
2751
|
}
|
|
@@ -2601,11 +2817,15 @@ class AbstractHistory extends History {
|
|
|
2601
2817
|
this.confirmTransition(
|
|
2602
2818
|
route,
|
|
2603
2819
|
() => {
|
|
2820
|
+
const prev = this.current;
|
|
2604
2821
|
this.index = targetIndex;
|
|
2605
2822
|
this.updateRoute(route);
|
|
2823
|
+
this.router.afterHooks.forEach(hook => {
|
|
2824
|
+
hook && hook(route, prev);
|
|
2825
|
+
});
|
|
2606
2826
|
},
|
|
2607
2827
|
err => {
|
|
2608
|
-
if (
|
|
2828
|
+
if (isNavigationFailure(err, NavigationFailureType.duplicated)) {
|
|
2609
2829
|
this.index = targetIndex;
|
|
2610
2830
|
}
|
|
2611
2831
|
}
|
|
@@ -2624,11 +2844,12 @@ class AbstractHistory extends History {
|
|
|
2624
2844
|
|
|
2625
2845
|
/* */
|
|
2626
2846
|
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
2847
|
class KduRouter {
|
|
2630
2848
|
|
|
2631
2849
|
|
|
2850
|
+
|
|
2851
|
+
|
|
2852
|
+
|
|
2632
2853
|
|
|
2633
2854
|
|
|
2634
2855
|
|
|
@@ -2644,6 +2865,9 @@ class KduRouter {
|
|
|
2644
2865
|
|
|
2645
2866
|
|
|
2646
2867
|
constructor (options = {}) {
|
|
2868
|
+
{
|
|
2869
|
+
warn(this instanceof KduRouter, `Router must be called with the new operator.`);
|
|
2870
|
+
}
|
|
2647
2871
|
this.app = null;
|
|
2648
2872
|
this.apps = [];
|
|
2649
2873
|
this.options = options;
|
|
@@ -2653,7 +2877,8 @@ class KduRouter {
|
|
|
2653
2877
|
this.matcher = createMatcher(options.routes || [], this);
|
|
2654
2878
|
|
|
2655
2879
|
let mode = options.mode || 'hash';
|
|
2656
|
-
this.fallback =
|
|
2880
|
+
this.fallback =
|
|
2881
|
+
mode === 'history' && !supportsPushState && options.fallback !== false;
|
|
2657
2882
|
if (this.fallback) {
|
|
2658
2883
|
mode = 'hash';
|
|
2659
2884
|
}
|
|
@@ -2679,11 +2904,7 @@ class KduRouter {
|
|
|
2679
2904
|
}
|
|
2680
2905
|
}
|
|
2681
2906
|
|
|
2682
|
-
match (
|
|
2683
|
-
raw,
|
|
2684
|
-
current,
|
|
2685
|
-
redirectedFrom
|
|
2686
|
-
) {
|
|
2907
|
+
match (raw, current, redirectedFrom) {
|
|
2687
2908
|
return this.matcher.match(raw, current, redirectedFrom)
|
|
2688
2909
|
}
|
|
2689
2910
|
|
|
@@ -2692,11 +2913,11 @@ class KduRouter {
|
|
|
2692
2913
|
}
|
|
2693
2914
|
|
|
2694
2915
|
init (app /* Kdu component instance */) {
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2916
|
+
assert(
|
|
2917
|
+
install.installed,
|
|
2918
|
+
`not installed. Make sure to call \`Kdu.use(KduRouter)\` ` +
|
|
2919
|
+
`before creating root instance.`
|
|
2920
|
+
);
|
|
2700
2921
|
|
|
2701
2922
|
this.apps.push(app);
|
|
2702
2923
|
|
|
@@ -2708,6 +2929,8 @@ class KduRouter {
|
|
|
2708
2929
|
// ensure we still have a main app or null if no apps
|
|
2709
2930
|
// we do not release the router so it can be reused
|
|
2710
2931
|
if (this.app === app) this.app = this.apps[0] || null;
|
|
2932
|
+
|
|
2933
|
+
if (!this.app) this.history.teardown();
|
|
2711
2934
|
});
|
|
2712
2935
|
|
|
2713
2936
|
// main app previously initialized
|
|
@@ -2720,21 +2943,29 @@ class KduRouter {
|
|
|
2720
2943
|
|
|
2721
2944
|
const history = this.history;
|
|
2722
2945
|
|
|
2723
|
-
if (history instanceof HTML5History) {
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
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 => {
|
|
2727
2957
|
history.setupListeners();
|
|
2958
|
+
handleInitialScroll(routeOrError);
|
|
2728
2959
|
};
|
|
2729
2960
|
history.transitionTo(
|
|
2730
2961
|
history.getCurrentLocation(),
|
|
2731
|
-
|
|
2732
|
-
|
|
2962
|
+
setupListeners,
|
|
2963
|
+
setupListeners
|
|
2733
2964
|
);
|
|
2734
2965
|
}
|
|
2735
2966
|
|
|
2736
2967
|
history.listen(route => {
|
|
2737
|
-
this.apps.forEach(
|
|
2968
|
+
this.apps.forEach(app => {
|
|
2738
2969
|
app._route = route;
|
|
2739
2970
|
});
|
|
2740
2971
|
});
|
|
@@ -2803,11 +3034,14 @@ class KduRouter {
|
|
|
2803
3034
|
if (!route) {
|
|
2804
3035
|
return []
|
|
2805
3036
|
}
|
|
2806
|
-
return [].concat.apply(
|
|
2807
|
-
|
|
2808
|
-
|
|
3037
|
+
return [].concat.apply(
|
|
3038
|
+
[],
|
|
3039
|
+
route.matched.map(m => {
|
|
3040
|
+
return Object.keys(m.components).map(key => {
|
|
3041
|
+
return m.components[key]
|
|
3042
|
+
})
|
|
2809
3043
|
})
|
|
2810
|
-
|
|
3044
|
+
)
|
|
2811
3045
|
}
|
|
2812
3046
|
|
|
2813
3047
|
resolve (
|
|
@@ -2816,12 +3050,7 @@ class KduRouter {
|
|
|
2816
3050
|
append
|
|
2817
3051
|
) {
|
|
2818
3052
|
current = current || this.history.current;
|
|
2819
|
-
const location = normalizeLocation(
|
|
2820
|
-
to,
|
|
2821
|
-
current,
|
|
2822
|
-
append,
|
|
2823
|
-
this
|
|
2824
|
-
);
|
|
3053
|
+
const location = normalizeLocation(to, current, append, this);
|
|
2825
3054
|
const route = this.match(location, current);
|
|
2826
3055
|
const fullPath = route.redirectedFrom || route.fullPath;
|
|
2827
3056
|
const base = this.history.base;
|
|
@@ -2836,7 +3065,21 @@ class KduRouter {
|
|
|
2836
3065
|
}
|
|
2837
3066
|
}
|
|
2838
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
|
+
|
|
2839
3079
|
addRoutes (routes) {
|
|
3080
|
+
{
|
|
3081
|
+
warn(false, 'router.addRoutes() is deprecated and has been removed in Kdu Router 4. Use router.addRoute() instead.');
|
|
3082
|
+
}
|
|
2840
3083
|
this.matcher.addRoutes(routes);
|
|
2841
3084
|
if (this.history.current !== START) {
|
|
2842
3085
|
this.history.transitionTo(this.history.getCurrentLocation());
|
|
@@ -2858,7 +3101,10 @@ function createHref (base, fullPath, mode) {
|
|
|
2858
3101
|
}
|
|
2859
3102
|
|
|
2860
3103
|
KduRouter.install = install;
|
|
2861
|
-
KduRouter.version = '3.
|
|
3104
|
+
KduRouter.version = '3.5.4';
|
|
3105
|
+
KduRouter.isNavigationFailure = isNavigationFailure;
|
|
3106
|
+
KduRouter.NavigationFailureType = NavigationFailureType;
|
|
3107
|
+
KduRouter.START_LOCATION = START;
|
|
2862
3108
|
|
|
2863
3109
|
if (inBrowser && window.Kdu) {
|
|
2864
3110
|
window.Kdu.use(KduRouter);
|