kdu-router 3.4.0-beta.0 → 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 +3 -1
- package/dist/kdu-router.common.js +381 -274
- package/dist/kdu-router.esm.browser.js +384 -276
- package/dist/kdu-router.esm.browser.min.js +3 -3
- package/dist/kdu-router.esm.js +381 -274
- package/dist/kdu-router.js +387 -281
- package/dist/kdu-router.min.js +3 -3
- package/package.json +15 -36
- package/src/components/link.js +30 -3
- package/src/components/view.js +6 -0
- package/src/create-matcher.js +28 -2
- package/src/create-route-map.js +18 -3
- package/src/history/abstract.js +4 -0
- package/src/history/base.js +32 -53
- package/src/history/hash.js +0 -11
- package/src/history/html5.js +7 -2
- package/src/index.js +21 -5
- package/src/util/errors.js +1 -0
- package/src/util/path.js +1 -1
- package/src/util/query.js +48 -31
- package/src/util/route.js +29 -10
- package/src/util/scroll.js +11 -1
- package/src/util/warn.js +1 -1
- package/types/index.d.ts +5 -1
- package/types/router.d.ts +52 -11
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* kdu-router v3.4
|
|
2
|
+
* kdu-router v3.5.4
|
|
3
3
|
* (c) 2022 NKDuy
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
@@ -12,7 +12,7 @@ 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
|
}
|
|
@@ -24,153 +24,6 @@ function extend (a, b) {
|
|
|
24
24
|
return a
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
var View = {
|
|
28
|
-
name: 'RouterView',
|
|
29
|
-
functional: true,
|
|
30
|
-
props: {
|
|
31
|
-
name: {
|
|
32
|
-
type: String,
|
|
33
|
-
default: 'default'
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
render (_, { props, children, parent, data }) {
|
|
37
|
-
// used by devtools to display a router-view badge
|
|
38
|
-
data.routerView = true;
|
|
39
|
-
|
|
40
|
-
// directly use parent context's createElement() function
|
|
41
|
-
// so that components rendered by router-view can resolve named slots
|
|
42
|
-
const h = parent.$createElement;
|
|
43
|
-
const name = props.name;
|
|
44
|
-
const route = parent.$route;
|
|
45
|
-
const cache = parent._routerViewCache || (parent._routerViewCache = {});
|
|
46
|
-
|
|
47
|
-
// determine current view depth, also check to see if the tree
|
|
48
|
-
// has been toggled inactive but kept-alive.
|
|
49
|
-
let depth = 0;
|
|
50
|
-
let inactive = false;
|
|
51
|
-
while (parent && parent._routerRoot !== parent) {
|
|
52
|
-
const knodeData = parent.$knode ? parent.$knode.data : {};
|
|
53
|
-
if (knodeData.routerView) {
|
|
54
|
-
depth++;
|
|
55
|
-
}
|
|
56
|
-
if (knodeData.keepAlive && parent._directInactive && parent._inactive) {
|
|
57
|
-
inactive = true;
|
|
58
|
-
}
|
|
59
|
-
parent = parent.$parent;
|
|
60
|
-
}
|
|
61
|
-
data.routerViewDepth = depth;
|
|
62
|
-
|
|
63
|
-
// render previous view if the tree is inactive and kept-alive
|
|
64
|
-
if (inactive) {
|
|
65
|
-
const cachedData = cache[name];
|
|
66
|
-
const cachedComponent = cachedData && cachedData.component;
|
|
67
|
-
if (cachedComponent) {
|
|
68
|
-
// #2301
|
|
69
|
-
// pass props
|
|
70
|
-
if (cachedData.configProps) {
|
|
71
|
-
fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps);
|
|
72
|
-
}
|
|
73
|
-
return h(cachedComponent, data, children)
|
|
74
|
-
} else {
|
|
75
|
-
// render previous empty view
|
|
76
|
-
return h()
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const matched = route.matched[depth];
|
|
81
|
-
const component = matched && matched.components[name];
|
|
82
|
-
|
|
83
|
-
// render empty node if no matched route or no config component
|
|
84
|
-
if (!matched || !component) {
|
|
85
|
-
cache[name] = null;
|
|
86
|
-
return h()
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// cache component
|
|
90
|
-
cache[name] = { component };
|
|
91
|
-
|
|
92
|
-
// attach instance registration hook
|
|
93
|
-
// this will be called in the instance's injected lifecycle hooks
|
|
94
|
-
data.registerRouteInstance = (vm, val) => {
|
|
95
|
-
// val could be undefined for unregistration
|
|
96
|
-
const current = matched.instances[name];
|
|
97
|
-
if (
|
|
98
|
-
(val && current !== vm) ||
|
|
99
|
-
(!val && current === vm)
|
|
100
|
-
) {
|
|
101
|
-
matched.instances[name] = val;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// also register instance in prepatch hook
|
|
106
|
-
// in case the same component instance is reused across different routes
|
|
107
|
-
;(data.hook || (data.hook = {})).prepatch = (_, knode) => {
|
|
108
|
-
matched.instances[name] = knode.componentInstance;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
// register instance in init hook
|
|
112
|
-
// in case kept-alive component be actived when routes changed
|
|
113
|
-
data.hook.init = (knode) => {
|
|
114
|
-
if (knode.data.keepAlive &&
|
|
115
|
-
knode.componentInstance &&
|
|
116
|
-
knode.componentInstance !== matched.instances[name]
|
|
117
|
-
) {
|
|
118
|
-
matched.instances[name] = knode.componentInstance;
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const configProps = matched.props && matched.props[name];
|
|
123
|
-
// save route and configProps in cache
|
|
124
|
-
if (configProps) {
|
|
125
|
-
extend(cache[name], {
|
|
126
|
-
route,
|
|
127
|
-
configProps
|
|
128
|
-
});
|
|
129
|
-
fillPropsinData(component, data, route, configProps);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return h(component, data, children)
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
function fillPropsinData (component, data, route, configProps) {
|
|
137
|
-
// resolve props
|
|
138
|
-
let propsToPass = data.props = resolveProps(route, configProps);
|
|
139
|
-
if (propsToPass) {
|
|
140
|
-
// clone to prevent mutation
|
|
141
|
-
propsToPass = data.props = extend({}, propsToPass);
|
|
142
|
-
// pass non-declared props as attrs
|
|
143
|
-
const attrs = data.attrs = data.attrs || {};
|
|
144
|
-
for (const key in propsToPass) {
|
|
145
|
-
if (!component.props || !(key in component.props)) {
|
|
146
|
-
attrs[key] = propsToPass[key];
|
|
147
|
-
delete propsToPass[key];
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function resolveProps (route, config) {
|
|
154
|
-
switch (typeof config) {
|
|
155
|
-
case 'undefined':
|
|
156
|
-
return
|
|
157
|
-
case 'object':
|
|
158
|
-
return config
|
|
159
|
-
case 'function':
|
|
160
|
-
return config(route)
|
|
161
|
-
case 'boolean':
|
|
162
|
-
return config ? route.params : undefined
|
|
163
|
-
default:
|
|
164
|
-
{
|
|
165
|
-
warn(
|
|
166
|
-
false,
|
|
167
|
-
`props in "${route.path}" is a ${typeof config}, ` +
|
|
168
|
-
`expecting an object, function or boolean.`
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
27
|
/* */
|
|
175
28
|
|
|
176
29
|
const encodeReserveRE = /[!'()*]/g;
|
|
@@ -180,11 +33,21 @@ const commaRE = /%2C/g;
|
|
|
180
33
|
// fixed encodeURIComponent which is more conformant to RFC3986:
|
|
181
34
|
// - escapes [!'()*]
|
|
182
35
|
// - preserve commas
|
|
183
|
-
const encode = str =>
|
|
184
|
-
|
|
185
|
-
|
|
36
|
+
const encode = str =>
|
|
37
|
+
encodeURIComponent(str)
|
|
38
|
+
.replace(encodeReserveRE, encodeReserveReplacer)
|
|
39
|
+
.replace(commaRE, ',');
|
|
186
40
|
|
|
187
|
-
|
|
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
|
+
}
|
|
188
51
|
|
|
189
52
|
function resolveQuery (
|
|
190
53
|
query,
|
|
@@ -196,16 +59,20 @@ function resolveQuery (
|
|
|
196
59
|
try {
|
|
197
60
|
parsedQuery = parse(query || '');
|
|
198
61
|
} catch (e) {
|
|
199
|
-
|
|
62
|
+
warn(false, e.message);
|
|
200
63
|
parsedQuery = {};
|
|
201
64
|
}
|
|
202
65
|
for (const key in extraQuery) {
|
|
203
66
|
const value = extraQuery[key];
|
|
204
|
-
parsedQuery[key] = Array.isArray(value)
|
|
67
|
+
parsedQuery[key] = Array.isArray(value)
|
|
68
|
+
? value.map(castQueryParamValue)
|
|
69
|
+
: castQueryParamValue(value);
|
|
205
70
|
}
|
|
206
71
|
return parsedQuery
|
|
207
72
|
}
|
|
208
73
|
|
|
74
|
+
const castQueryParamValue = value => (value == null || typeof value === 'object' ? value : String(value));
|
|
75
|
+
|
|
209
76
|
function parseQuery (query) {
|
|
210
77
|
const res = {};
|
|
211
78
|
|
|
@@ -218,9 +85,7 @@ function parseQuery (query) {
|
|
|
218
85
|
query.split('&').forEach(param => {
|
|
219
86
|
const parts = param.replace(/\+/g, ' ').split('=');
|
|
220
87
|
const key = decode(parts.shift());
|
|
221
|
-
const val = parts.length > 0
|
|
222
|
-
? decode(parts.join('='))
|
|
223
|
-
: null;
|
|
88
|
+
const val = parts.length > 0 ? decode(parts.join('=')) : null;
|
|
224
89
|
|
|
225
90
|
if (res[key] === undefined) {
|
|
226
91
|
res[key] = val;
|
|
@@ -235,34 +100,39 @@ function parseQuery (query) {
|
|
|
235
100
|
}
|
|
236
101
|
|
|
237
102
|
function stringifyQuery (obj) {
|
|
238
|
-
const res = obj
|
|
239
|
-
|
|
103
|
+
const res = obj
|
|
104
|
+
? Object.keys(obj)
|
|
105
|
+
.map(key => {
|
|
106
|
+
const val = obj[key];
|
|
240
107
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (val === null) {
|
|
246
|
-
return encode(key)
|
|
247
|
-
}
|
|
108
|
+
if (val === undefined) {
|
|
109
|
+
return ''
|
|
110
|
+
}
|
|
248
111
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
val.forEach(val2 => {
|
|
252
|
-
if (val2 === undefined) {
|
|
253
|
-
return
|
|
112
|
+
if (val === null) {
|
|
113
|
+
return encode(key)
|
|
254
114
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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('&')
|
|
259
129
|
}
|
|
260
|
-
});
|
|
261
|
-
return result.join('&')
|
|
262
|
-
}
|
|
263
130
|
|
|
264
|
-
|
|
265
|
-
|
|
131
|
+
return encode(key) + '=' + encode(val)
|
|
132
|
+
})
|
|
133
|
+
.filter(x => x.length > 0)
|
|
134
|
+
.join('&')
|
|
135
|
+
: null;
|
|
266
136
|
return res ? `?${res}` : ''
|
|
267
137
|
}
|
|
268
138
|
|
|
@@ -335,23 +205,23 @@ function getFullPath (
|
|
|
335
205
|
return (path || '/') + stringify(query) + hash
|
|
336
206
|
}
|
|
337
207
|
|
|
338
|
-
function isSameRoute (a, b) {
|
|
208
|
+
function isSameRoute (a, b, onlyPath) {
|
|
339
209
|
if (b === START) {
|
|
340
210
|
return a === b
|
|
341
211
|
} else if (!b) {
|
|
342
212
|
return false
|
|
343
213
|
} else if (a.path && b.path) {
|
|
344
|
-
return (
|
|
345
|
-
a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
|
|
214
|
+
return a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') && (onlyPath ||
|
|
346
215
|
a.hash === b.hash &&
|
|
347
|
-
isObjectEqual(a.query, b.query)
|
|
348
|
-
)
|
|
216
|
+
isObjectEqual(a.query, b.query))
|
|
349
217
|
} else if (a.name && b.name) {
|
|
350
218
|
return (
|
|
351
219
|
a.name === b.name &&
|
|
352
|
-
|
|
220
|
+
(onlyPath || (
|
|
221
|
+
a.hash === b.hash &&
|
|
353
222
|
isObjectEqual(a.query, b.query) &&
|
|
354
|
-
isObjectEqual(a.params, b.params)
|
|
223
|
+
isObjectEqual(a.params, b.params))
|
|
224
|
+
)
|
|
355
225
|
)
|
|
356
226
|
} else {
|
|
357
227
|
return false
|
|
@@ -361,14 +231,18 @@ function isSameRoute (a, b) {
|
|
|
361
231
|
function isObjectEqual (a = {}, b = {}) {
|
|
362
232
|
// handle null value #1566
|
|
363
233
|
if (!a || !b) return a === b
|
|
364
|
-
const aKeys = Object.keys(a);
|
|
365
|
-
const bKeys = Object.keys(b);
|
|
234
|
+
const aKeys = Object.keys(a).sort();
|
|
235
|
+
const bKeys = Object.keys(b).sort();
|
|
366
236
|
if (aKeys.length !== bKeys.length) {
|
|
367
237
|
return false
|
|
368
238
|
}
|
|
369
|
-
return aKeys.every(key => {
|
|
239
|
+
return aKeys.every((key, i) => {
|
|
370
240
|
const aVal = a[key];
|
|
241
|
+
const bKey = bKeys[i];
|
|
242
|
+
if (bKey !== key) return false
|
|
371
243
|
const bVal = b[key];
|
|
244
|
+
// query values can be null and undefined
|
|
245
|
+
if (aVal == null || bVal == null) return aVal === bVal
|
|
372
246
|
// check nested equality
|
|
373
247
|
if (typeof aVal === 'object' && typeof bVal === 'object') {
|
|
374
248
|
return isObjectEqual(aVal, bVal)
|
|
@@ -396,6 +270,173 @@ function queryIncludes (current, target) {
|
|
|
396
270
|
return true
|
|
397
271
|
}
|
|
398
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
|
+
|
|
399
440
|
/* */
|
|
400
441
|
|
|
401
442
|
function resolvePath (
|
|
@@ -464,7 +505,7 @@ function parsePath (path) {
|
|
|
464
505
|
}
|
|
465
506
|
|
|
466
507
|
function cleanPath (path) {
|
|
467
|
-
return path.replace(
|
|
508
|
+
return path.replace(/\/(?:\s*\/)+/g, '/')
|
|
468
509
|
}
|
|
469
510
|
|
|
470
511
|
var isarray = Array.isArray || function (arr) {
|
|
@@ -1004,6 +1045,10 @@ const eventTypes = [String, Array];
|
|
|
1004
1045
|
|
|
1005
1046
|
const noop = () => {};
|
|
1006
1047
|
|
|
1048
|
+
let warnedCustomSlot;
|
|
1049
|
+
let warnedTagProp;
|
|
1050
|
+
let warnedEventProp;
|
|
1051
|
+
|
|
1007
1052
|
var Link = {
|
|
1008
1053
|
name: 'RouterLink',
|
|
1009
1054
|
props: {
|
|
@@ -1015,7 +1060,9 @@ var Link = {
|
|
|
1015
1060
|
type: String,
|
|
1016
1061
|
default: 'a'
|
|
1017
1062
|
},
|
|
1063
|
+
custom: Boolean,
|
|
1018
1064
|
exact: Boolean,
|
|
1065
|
+
exactPath: Boolean,
|
|
1019
1066
|
append: Boolean,
|
|
1020
1067
|
replace: Boolean,
|
|
1021
1068
|
activeClass: String,
|
|
@@ -1059,8 +1106,8 @@ var Link = {
|
|
|
1059
1106
|
? createRoute(null, normalizeLocation(route.redirectedFrom), null, router)
|
|
1060
1107
|
: route;
|
|
1061
1108
|
|
|
1062
|
-
classes[exactActiveClass] = isSameRoute(current, compareTarget);
|
|
1063
|
-
classes[activeClass] = this.exact
|
|
1109
|
+
classes[exactActiveClass] = isSameRoute(current, compareTarget, this.exactPath);
|
|
1110
|
+
classes[activeClass] = this.exact || this.exactPath
|
|
1064
1111
|
? classes[exactActiveClass]
|
|
1065
1112
|
: isIncludedRoute(current, compareTarget);
|
|
1066
1113
|
|
|
@@ -1099,13 +1146,17 @@ var Link = {
|
|
|
1099
1146
|
});
|
|
1100
1147
|
|
|
1101
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
|
+
}
|
|
1102
1153
|
if (scopedSlot.length === 1) {
|
|
1103
1154
|
return scopedSlot[0]
|
|
1104
1155
|
} else if (scopedSlot.length > 1 || !scopedSlot.length) {
|
|
1105
1156
|
{
|
|
1106
1157
|
warn(
|
|
1107
1158
|
false,
|
|
1108
|
-
|
|
1159
|
+
`<router-link> with to="${
|
|
1109
1160
|
this.to
|
|
1110
1161
|
}" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element.`
|
|
1111
1162
|
);
|
|
@@ -1114,6 +1165,23 @@ var Link = {
|
|
|
1114
1165
|
}
|
|
1115
1166
|
}
|
|
1116
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
|
+
|
|
1117
1185
|
if (this.tag === 'a') {
|
|
1118
1186
|
data.on = on;
|
|
1119
1187
|
data.attrs = { href, 'aria-current': ariaCurrentValue };
|
|
@@ -1249,7 +1317,8 @@ function createRouteMap (
|
|
|
1249
1317
|
routes,
|
|
1250
1318
|
oldPathList,
|
|
1251
1319
|
oldPathMap,
|
|
1252
|
-
oldNameMap
|
|
1320
|
+
oldNameMap,
|
|
1321
|
+
parentRoute
|
|
1253
1322
|
) {
|
|
1254
1323
|
// the path list is used to control path matching priority
|
|
1255
1324
|
const pathList = oldPathList || [];
|
|
@@ -1259,7 +1328,7 @@ function createRouteMap (
|
|
|
1259
1328
|
const nameMap = oldNameMap || Object.create(null);
|
|
1260
1329
|
|
|
1261
1330
|
routes.forEach(route => {
|
|
1262
|
-
addRouteRecord(pathList, pathMap, nameMap, route);
|
|
1331
|
+
addRouteRecord(pathList, pathMap, nameMap, route, parentRoute);
|
|
1263
1332
|
});
|
|
1264
1333
|
|
|
1265
1334
|
// ensure wildcard routes are always at the end
|
|
@@ -1307,6 +1376,14 @@ function addRouteRecord (
|
|
|
1307
1376
|
path || name
|
|
1308
1377
|
)} cannot be a ` + `string id. Use an actual component instead.`
|
|
1309
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
|
+
);
|
|
1310
1387
|
}
|
|
1311
1388
|
|
|
1312
1389
|
const pathToRegexpOptions =
|
|
@@ -1321,7 +1398,13 @@ function addRouteRecord (
|
|
|
1321
1398
|
path: normalizedPath,
|
|
1322
1399
|
regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
|
|
1323
1400
|
components: route.components || { default: route.component },
|
|
1401
|
+
alias: route.alias
|
|
1402
|
+
? typeof route.alias === 'string'
|
|
1403
|
+
? [route.alias]
|
|
1404
|
+
: route.alias
|
|
1405
|
+
: [],
|
|
1324
1406
|
instances: {},
|
|
1407
|
+
enteredCbs: {},
|
|
1325
1408
|
name,
|
|
1326
1409
|
parent,
|
|
1327
1410
|
matchAs,
|
|
@@ -1351,7 +1434,7 @@ function addRouteRecord (
|
|
|
1351
1434
|
`Named Route '${route.name}' has a default child route. ` +
|
|
1352
1435
|
`When navigating to this named route (:to="{name: '${
|
|
1353
1436
|
route.name
|
|
1354
|
-
}'"), ` +
|
|
1437
|
+
}'}"), ` +
|
|
1355
1438
|
`the default child route will not be rendered. Remove the name from ` +
|
|
1356
1439
|
`this route and use the name of the default child route for named ` +
|
|
1357
1440
|
`links instead.`
|
|
@@ -1375,7 +1458,7 @@ function addRouteRecord (
|
|
|
1375
1458
|
const aliases = Array.isArray(route.alias) ? route.alias : [route.alias];
|
|
1376
1459
|
for (let i = 0; i < aliases.length; ++i) {
|
|
1377
1460
|
const alias = aliases[i];
|
|
1378
|
-
if (
|
|
1461
|
+
if (alias === path) {
|
|
1379
1462
|
warn(
|
|
1380
1463
|
false,
|
|
1381
1464
|
`Found an alias with the same value as the path: "${path}". You have to remove that alias. It will be ignored in development.`
|
|
@@ -1402,7 +1485,7 @@ function addRouteRecord (
|
|
|
1402
1485
|
if (name) {
|
|
1403
1486
|
if (!nameMap[name]) {
|
|
1404
1487
|
nameMap[name] = record;
|
|
1405
|
-
} else if (
|
|
1488
|
+
} else if (!matchAs) {
|
|
1406
1489
|
warn(
|
|
1407
1490
|
false,
|
|
1408
1491
|
`Duplicate named routes definition: ` +
|
|
@@ -1455,6 +1538,28 @@ function createMatcher (
|
|
|
1455
1538
|
createRouteMap(routes, pathList, pathMap, nameMap);
|
|
1456
1539
|
}
|
|
1457
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
|
+
|
|
1458
1563
|
function match (
|
|
1459
1564
|
raw,
|
|
1460
1565
|
currentRoute,
|
|
@@ -1598,6 +1703,8 @@ function createMatcher (
|
|
|
1598
1703
|
|
|
1599
1704
|
return {
|
|
1600
1705
|
match,
|
|
1706
|
+
addRoute,
|
|
1707
|
+
getRoutes,
|
|
1601
1708
|
addRoutes
|
|
1602
1709
|
}
|
|
1603
1710
|
}
|
|
@@ -1617,10 +1724,9 @@ function matchRoute (
|
|
|
1617
1724
|
|
|
1618
1725
|
for (let i = 1, len = m.length; i < len; ++i) {
|
|
1619
1726
|
const key = regex.keys[i - 1];
|
|
1620
|
-
const val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i];
|
|
1621
1727
|
if (key) {
|
|
1622
1728
|
// Fix #1994: using * with props: true generates a param named 0
|
|
1623
|
-
params[key.name || 'pathMatch'] =
|
|
1729
|
+
params[key.name || 'pathMatch'] = typeof m[i] === 'string' ? decode(m[i]) : m[i];
|
|
1624
1730
|
}
|
|
1625
1731
|
}
|
|
1626
1732
|
|
|
@@ -1810,7 +1916,17 @@ function scrollToPosition (shouldScroll, position) {
|
|
|
1810
1916
|
}
|
|
1811
1917
|
|
|
1812
1918
|
if (position) {
|
|
1813
|
-
|
|
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
|
+
}
|
|
1814
1930
|
}
|
|
1815
1931
|
}
|
|
1816
1932
|
|
|
@@ -1875,6 +1991,7 @@ function runQueue (queue, fn, cb) {
|
|
|
1875
1991
|
step(0);
|
|
1876
1992
|
}
|
|
1877
1993
|
|
|
1994
|
+
// When changing thing, also edit router.d.ts
|
|
1878
1995
|
const NavigationFailureType = {
|
|
1879
1996
|
redirected: 2,
|
|
1880
1997
|
aborted: 4,
|
|
@@ -1996,7 +2113,7 @@ function resolveAsyncComponents (matched) {
|
|
|
1996
2113
|
|
|
1997
2114
|
const reject = once(reason => {
|
|
1998
2115
|
const msg = `Failed to resolve async component ${key}: ${reason}`;
|
|
1999
|
-
|
|
2116
|
+
warn(false, msg);
|
|
2000
2117
|
if (!error) {
|
|
2001
2118
|
error = isError(reason)
|
|
2002
2119
|
? reason
|
|
@@ -2128,6 +2245,7 @@ class History {
|
|
|
2128
2245
|
onAbort
|
|
2129
2246
|
) {
|
|
2130
2247
|
let route;
|
|
2248
|
+
// catch redirect option
|
|
2131
2249
|
try {
|
|
2132
2250
|
route = this.router.match(location, this.current);
|
|
2133
2251
|
} catch (e) {
|
|
@@ -2137,10 +2255,10 @@ class History {
|
|
|
2137
2255
|
// Exception should still be thrown
|
|
2138
2256
|
throw e
|
|
2139
2257
|
}
|
|
2258
|
+
const prev = this.current;
|
|
2140
2259
|
this.confirmTransition(
|
|
2141
2260
|
route,
|
|
2142
2261
|
() => {
|
|
2143
|
-
const prev = this.current;
|
|
2144
2262
|
this.updateRoute(route);
|
|
2145
2263
|
onComplete && onComplete(route);
|
|
2146
2264
|
this.ensureURL();
|
|
@@ -2161,16 +2279,13 @@ class History {
|
|
|
2161
2279
|
onAbort(err);
|
|
2162
2280
|
}
|
|
2163
2281
|
if (err && !this.ready) {
|
|
2164
|
-
|
|
2165
|
-
//
|
|
2166
|
-
if (!isNavigationFailure(err, NavigationFailureType.redirected)) {
|
|
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;
|
|
2167
2286
|
this.readyErrorCbs.forEach(cb => {
|
|
2168
2287
|
cb(err);
|
|
2169
2288
|
});
|
|
2170
|
-
} else {
|
|
2171
|
-
this.readyCbs.forEach(cb => {
|
|
2172
|
-
cb(route);
|
|
2173
|
-
});
|
|
2174
2289
|
}
|
|
2175
2290
|
}
|
|
2176
2291
|
}
|
|
@@ -2179,16 +2294,19 @@ class History {
|
|
|
2179
2294
|
|
|
2180
2295
|
confirmTransition (route, onComplete, onAbort) {
|
|
2181
2296
|
const current = this.current;
|
|
2297
|
+
this.pending = route;
|
|
2182
2298
|
const abort = err => {
|
|
2183
|
-
// changed after adding errors
|
|
2184
|
-
// redirect and aborted navigation would produce an err == null
|
|
2299
|
+
// changed after adding errors
|
|
2300
|
+
// before that change, redirect and aborted navigation would produce an err == null
|
|
2185
2301
|
if (!isNavigationFailure(err) && isError(err)) {
|
|
2186
2302
|
if (this.errorCbs.length) {
|
|
2187
2303
|
this.errorCbs.forEach(cb => {
|
|
2188
2304
|
cb(err);
|
|
2189
2305
|
});
|
|
2190
2306
|
} else {
|
|
2191
|
-
|
|
2307
|
+
{
|
|
2308
|
+
warn(false, 'uncaught error during route navigation:');
|
|
2309
|
+
}
|
|
2192
2310
|
console.error(err);
|
|
2193
2311
|
}
|
|
2194
2312
|
}
|
|
@@ -2203,6 +2321,9 @@ class History {
|
|
|
2203
2321
|
route.matched[lastRouteIndex] === current.matched[lastCurrentIndex]
|
|
2204
2322
|
) {
|
|
2205
2323
|
this.ensureURL();
|
|
2324
|
+
if (route.hash) {
|
|
2325
|
+
handleScroll(this.router, current, route, false);
|
|
2326
|
+
}
|
|
2206
2327
|
return abort(createNavigationDuplicatedError(current, route))
|
|
2207
2328
|
}
|
|
2208
2329
|
|
|
@@ -2224,7 +2345,6 @@ class History {
|
|
|
2224
2345
|
resolveAsyncComponents(activated)
|
|
2225
2346
|
);
|
|
2226
2347
|
|
|
2227
|
-
this.pending = route;
|
|
2228
2348
|
const iterator = (hook, next) => {
|
|
2229
2349
|
if (this.pending !== route) {
|
|
2230
2350
|
return abort(createNavigationCancelledError(current, route))
|
|
@@ -2261,11 +2381,9 @@ class History {
|
|
|
2261
2381
|
};
|
|
2262
2382
|
|
|
2263
2383
|
runQueue(queue, iterator, () => {
|
|
2264
|
-
const postEnterCbs = [];
|
|
2265
|
-
const isValid = () => this.current === route;
|
|
2266
2384
|
// wait until async components are resolved before
|
|
2267
2385
|
// extracting in-component enter guards
|
|
2268
|
-
const enterGuards = extractEnterGuards(activated
|
|
2386
|
+
const enterGuards = extractEnterGuards(activated);
|
|
2269
2387
|
const queue = enterGuards.concat(this.router.resolveHooks);
|
|
2270
2388
|
runQueue(queue, iterator, () => {
|
|
2271
2389
|
if (this.pending !== route) {
|
|
@@ -2275,9 +2393,7 @@ class History {
|
|
|
2275
2393
|
onComplete(route);
|
|
2276
2394
|
if (this.router.app) {
|
|
2277
2395
|
this.router.app.$nextTick(() => {
|
|
2278
|
-
|
|
2279
|
-
cb();
|
|
2280
|
-
});
|
|
2396
|
+
handleRouteEntered(route);
|
|
2281
2397
|
});
|
|
2282
2398
|
}
|
|
2283
2399
|
});
|
|
@@ -2293,11 +2409,16 @@ class History {
|
|
|
2293
2409
|
// Default implementation is empty
|
|
2294
2410
|
}
|
|
2295
2411
|
|
|
2296
|
-
|
|
2412
|
+
teardown () {
|
|
2413
|
+
// clean up event listeners
|
|
2297
2414
|
this.listeners.forEach(cleanupListener => {
|
|
2298
2415
|
cleanupListener();
|
|
2299
2416
|
});
|
|
2300
2417
|
this.listeners = [];
|
|
2418
|
+
|
|
2419
|
+
// reset current history route
|
|
2420
|
+
this.current = START;
|
|
2421
|
+
this.pending = null;
|
|
2301
2422
|
}
|
|
2302
2423
|
}
|
|
2303
2424
|
|
|
@@ -2384,15 +2505,13 @@ function bindGuard (guard, instance) {
|
|
|
2384
2505
|
}
|
|
2385
2506
|
|
|
2386
2507
|
function extractEnterGuards (
|
|
2387
|
-
activated
|
|
2388
|
-
cbs,
|
|
2389
|
-
isValid
|
|
2508
|
+
activated
|
|
2390
2509
|
) {
|
|
2391
2510
|
return extractGuards(
|
|
2392
2511
|
activated,
|
|
2393
2512
|
'beforeRouteEnter',
|
|
2394
2513
|
(guard, _, match, key) => {
|
|
2395
|
-
return bindEnterGuard(guard, match, key
|
|
2514
|
+
return bindEnterGuard(guard, match, key)
|
|
2396
2515
|
}
|
|
2397
2516
|
)
|
|
2398
2517
|
}
|
|
@@ -2400,45 +2519,21 @@ function extractEnterGuards (
|
|
|
2400
2519
|
function bindEnterGuard (
|
|
2401
2520
|
guard,
|
|
2402
2521
|
match,
|
|
2403
|
-
key
|
|
2404
|
-
cbs,
|
|
2405
|
-
isValid
|
|
2522
|
+
key
|
|
2406
2523
|
) {
|
|
2407
2524
|
return function routeEnterGuard (to, from, next) {
|
|
2408
2525
|
return guard(to, from, cb => {
|
|
2409
2526
|
if (typeof cb === 'function') {
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
// we will need to poll for registration until current route
|
|
2415
|
-
// is no longer valid.
|
|
2416
|
-
poll(cb, match.instances, key, isValid);
|
|
2417
|
-
});
|
|
2527
|
+
if (!match.enteredCbs[key]) {
|
|
2528
|
+
match.enteredCbs[key] = [];
|
|
2529
|
+
}
|
|
2530
|
+
match.enteredCbs[key].push(cb);
|
|
2418
2531
|
}
|
|
2419
2532
|
next(cb);
|
|
2420
2533
|
})
|
|
2421
2534
|
}
|
|
2422
2535
|
}
|
|
2423
2536
|
|
|
2424
|
-
function poll (
|
|
2425
|
-
cb, // somehow flow cannot infer this is a function
|
|
2426
|
-
instances,
|
|
2427
|
-
key,
|
|
2428
|
-
isValid
|
|
2429
|
-
) {
|
|
2430
|
-
if (
|
|
2431
|
-
instances[key] &&
|
|
2432
|
-
!instances[key]._isBeingDestroyed // do not reuse being destroyed instance
|
|
2433
|
-
) {
|
|
2434
|
-
cb(instances[key]);
|
|
2435
|
-
} else if (isValid()) {
|
|
2436
|
-
setTimeout(() => {
|
|
2437
|
-
poll(cb, instances, key, isValid);
|
|
2438
|
-
}, 16);
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
|
|
2442
2537
|
/* */
|
|
2443
2538
|
|
|
2444
2539
|
class HTML5History extends History {
|
|
@@ -2520,8 +2615,13 @@ class HTML5History extends History {
|
|
|
2520
2615
|
}
|
|
2521
2616
|
|
|
2522
2617
|
function getLocation (base) {
|
|
2523
|
-
let path =
|
|
2524
|
-
|
|
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))) {
|
|
2525
2625
|
path = path.slice(base.length);
|
|
2526
2626
|
}
|
|
2527
2627
|
return (path || '/') + window.location.search + window.location.hash
|
|
@@ -2646,17 +2746,6 @@ function getHash () {
|
|
|
2646
2746
|
if (index < 0) return ''
|
|
2647
2747
|
|
|
2648
2748
|
href = href.slice(index + 1);
|
|
2649
|
-
// decode the hash but not the search or hash
|
|
2650
|
-
// as search(query) is already decoded
|
|
2651
|
-
const searchIndex = href.indexOf('?');
|
|
2652
|
-
if (searchIndex < 0) {
|
|
2653
|
-
const hashIndex = href.indexOf('#');
|
|
2654
|
-
if (hashIndex > -1) {
|
|
2655
|
-
href = decodeURI(href.slice(0, hashIndex)) + href.slice(hashIndex);
|
|
2656
|
-
} else href = decodeURI(href);
|
|
2657
|
-
} else {
|
|
2658
|
-
href = decodeURI(href.slice(0, searchIndex)) + href.slice(searchIndex);
|
|
2659
|
-
}
|
|
2660
2749
|
|
|
2661
2750
|
return href
|
|
2662
2751
|
}
|
|
@@ -2728,8 +2817,12 @@ class AbstractHistory extends History {
|
|
|
2728
2817
|
this.confirmTransition(
|
|
2729
2818
|
route,
|
|
2730
2819
|
() => {
|
|
2820
|
+
const prev = this.current;
|
|
2731
2821
|
this.index = targetIndex;
|
|
2732
2822
|
this.updateRoute(route);
|
|
2823
|
+
this.router.afterHooks.forEach(hook => {
|
|
2824
|
+
hook && hook(route, prev);
|
|
2825
|
+
});
|
|
2733
2826
|
},
|
|
2734
2827
|
err => {
|
|
2735
2828
|
if (isNavigationFailure(err, NavigationFailureType.duplicated)) {
|
|
@@ -2756,6 +2849,7 @@ class KduRouter {
|
|
|
2756
2849
|
|
|
2757
2850
|
|
|
2758
2851
|
|
|
2852
|
+
|
|
2759
2853
|
|
|
2760
2854
|
|
|
2761
2855
|
|
|
@@ -2771,6 +2865,9 @@ class KduRouter {
|
|
|
2771
2865
|
|
|
2772
2866
|
|
|
2773
2867
|
constructor (options = {}) {
|
|
2868
|
+
{
|
|
2869
|
+
warn(this instanceof KduRouter, `Router must be called with the new operator.`);
|
|
2870
|
+
}
|
|
2774
2871
|
this.app = null;
|
|
2775
2872
|
this.apps = [];
|
|
2776
2873
|
this.options = options;
|
|
@@ -2816,8 +2913,7 @@ class KduRouter {
|
|
|
2816
2913
|
}
|
|
2817
2914
|
|
|
2818
2915
|
init (app /* Kdu component instance */) {
|
|
2819
|
-
|
|
2820
|
-
assert(
|
|
2916
|
+
assert(
|
|
2821
2917
|
install.installed,
|
|
2822
2918
|
`not installed. Make sure to call \`Kdu.use(KduRouter)\` ` +
|
|
2823
2919
|
`before creating root instance.`
|
|
@@ -2834,10 +2930,7 @@ class KduRouter {
|
|
|
2834
2930
|
// we do not release the router so it can be reused
|
|
2835
2931
|
if (this.app === app) this.app = this.apps[0] || null;
|
|
2836
2932
|
|
|
2837
|
-
if (!this.app)
|
|
2838
|
-
// clean up event listeners
|
|
2839
|
-
this.history.teardownListeners();
|
|
2840
|
-
}
|
|
2933
|
+
if (!this.app) this.history.teardown();
|
|
2841
2934
|
});
|
|
2842
2935
|
|
|
2843
2936
|
// main app previously initialized
|
|
@@ -2972,7 +3065,21 @@ class KduRouter {
|
|
|
2972
3065
|
}
|
|
2973
3066
|
}
|
|
2974
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
|
+
|
|
2975
3079
|
addRoutes (routes) {
|
|
3080
|
+
{
|
|
3081
|
+
warn(false, 'router.addRoutes() is deprecated and has been removed in Kdu Router 4. Use router.addRoute() instead.');
|
|
3082
|
+
}
|
|
2976
3083
|
this.matcher.addRoutes(routes);
|
|
2977
3084
|
if (this.history.current !== START) {
|
|
2978
3085
|
this.history.transitionTo(this.history.getCurrentLocation());
|
|
@@ -2994,9 +3101,10 @@ function createHref (base, fullPath, mode) {
|
|
|
2994
3101
|
}
|
|
2995
3102
|
|
|
2996
3103
|
KduRouter.install = install;
|
|
2997
|
-
KduRouter.version = '3.4
|
|
3104
|
+
KduRouter.version = '3.5.4';
|
|
2998
3105
|
KduRouter.isNavigationFailure = isNavigationFailure;
|
|
2999
3106
|
KduRouter.NavigationFailureType = NavigationFailureType;
|
|
3107
|
+
KduRouter.START_LOCATION = START;
|
|
3000
3108
|
|
|
3001
3109
|
if (inBrowser && window.Kdu) {
|
|
3002
3110
|
window.Kdu.use(KduRouter);
|