kdu-router 2.7.0 → 4.0.16-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,51 +0,0 @@
1
- /* @flow */
2
-
3
- import type Router from '../index'
4
- import { History } from './base'
5
-
6
- export class AbstractHistory extends History {
7
- index: number;
8
- stack: Array<Route>;
9
-
10
- constructor (router: Router, base: ?string) {
11
- super(router, base)
12
- this.stack = []
13
- this.index = -1
14
- }
15
-
16
- push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
17
- this.transitionTo(location, route => {
18
- this.stack = this.stack.slice(0, this.index + 1).concat(route)
19
- this.index++
20
- onComplete && onComplete(route)
21
- }, onAbort)
22
- }
23
-
24
- replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
25
- this.transitionTo(location, route => {
26
- this.stack = this.stack.slice(0, this.index).concat(route)
27
- onComplete && onComplete(route)
28
- }, onAbort)
29
- }
30
-
31
- go (n: number) {
32
- const targetIndex = this.index + n
33
- if (targetIndex < 0 || targetIndex >= this.stack.length) {
34
- return
35
- }
36
- const route = this.stack[targetIndex]
37
- this.confirmTransition(route, () => {
38
- this.index = targetIndex
39
- this.updateRoute(route)
40
- })
41
- }
42
-
43
- getCurrentLocation () {
44
- const current = this.stack[this.stack.length - 1]
45
- return current ? current.fullPath : '/'
46
- }
47
-
48
- ensureURL () {
49
- // noop
50
- }
51
- }
@@ -1,328 +0,0 @@
1
- /* @flow */
2
-
3
- import { _Kdu } from '../install'
4
- import type Router from '../index'
5
- import { inBrowser } from '../util/dom'
6
- import { runQueue } from '../util/async'
7
- import { warn, isError } from '../util/warn'
8
- import { START, isSameRoute } from '../util/route'
9
- import {
10
- flatten,
11
- flatMapComponents,
12
- resolveAsyncComponents
13
- } from '../util/resolve-components'
14
-
15
- export class History {
16
- router: Router;
17
- base: string;
18
- current: Route;
19
- pending: ?Route;
20
- cb: (r: Route) => void;
21
- ready: boolean;
22
- readyCbs: Array<Function>;
23
- readyErrorCbs: Array<Function>;
24
- errorCbs: Array<Function>;
25
-
26
- // implemented by sub-classes
27
- +go: (n: number) => void;
28
- +push: (loc: RawLocation) => void;
29
- +replace: (loc: RawLocation) => void;
30
- +ensureURL: (push?: boolean) => void;
31
- +getCurrentLocation: () => string;
32
-
33
- constructor (router: Router, base: ?string) {
34
- this.router = router
35
- this.base = normalizeBase(base)
36
- // start with a route object that stands for "nowhere"
37
- this.current = START
38
- this.pending = null
39
- this.ready = false
40
- this.readyCbs = []
41
- this.readyErrorCbs = []
42
- this.errorCbs = []
43
- }
44
-
45
- listen (cb: Function) {
46
- this.cb = cb
47
- }
48
-
49
- onReady (cb: Function, errorCb: ?Function) {
50
- if (this.ready) {
51
- cb()
52
- } else {
53
- this.readyCbs.push(cb)
54
- if (errorCb) {
55
- this.readyErrorCbs.push(errorCb)
56
- }
57
- }
58
- }
59
-
60
- onError (errorCb: Function) {
61
- this.errorCbs.push(errorCb)
62
- }
63
-
64
- transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
65
- const route = this.router.match(location, this.current)
66
- this.confirmTransition(route, () => {
67
- this.updateRoute(route)
68
- onComplete && onComplete(route)
69
- this.ensureURL()
70
-
71
- // fire ready cbs once
72
- if (!this.ready) {
73
- this.ready = true
74
- this.readyCbs.forEach(cb => { cb(route) })
75
- }
76
- }, err => {
77
- if (onAbort) {
78
- onAbort(err)
79
- }
80
- if (err && !this.ready) {
81
- this.ready = true
82
- this.readyErrorCbs.forEach(cb => { cb(err) })
83
- }
84
- })
85
- }
86
-
87
- confirmTransition (route: Route, onComplete: Function, onAbort?: Function) {
88
- const current = this.current
89
- const abort = err => {
90
- if (isError(err)) {
91
- if (this.errorCbs.length) {
92
- this.errorCbs.forEach(cb => { cb(err) })
93
- } else {
94
- warn(false, 'uncaught error during route navigation:')
95
- console.error(err)
96
- }
97
- }
98
- onAbort && onAbort(err)
99
- }
100
- if (
101
- isSameRoute(route, current) &&
102
- // in the case the route map has been dynamically appended to
103
- route.matched.length === current.matched.length
104
- ) {
105
- this.ensureURL()
106
- return abort()
107
- }
108
-
109
- const {
110
- updated,
111
- deactivated,
112
- activated
113
- } = resolveQueue(this.current.matched, route.matched)
114
-
115
- const queue: Array<?NavigationGuard> = [].concat(
116
- // in-component leave guards
117
- extractLeaveGuards(deactivated),
118
- // global before hooks
119
- this.router.beforeHooks,
120
- // in-component update hooks
121
- extractUpdateHooks(updated),
122
- // in-config enter guards
123
- activated.map(m => m.beforeEnter),
124
- // async components
125
- resolveAsyncComponents(activated)
126
- )
127
-
128
- this.pending = route
129
- const iterator = (hook: NavigationGuard, next) => {
130
- if (this.pending !== route) {
131
- return abort()
132
- }
133
- try {
134
- hook(route, current, (to: any) => {
135
- if (to === false || isError(to)) {
136
- // next(false) -> abort navigation, ensure current URL
137
- this.ensureURL(true)
138
- abort(to)
139
- } else if (
140
- typeof to === 'string' ||
141
- (typeof to === 'object' && (
142
- typeof to.path === 'string' ||
143
- typeof to.name === 'string'
144
- ))
145
- ) {
146
- // next('/') or next({ path: '/' }) -> redirect
147
- abort()
148
- if (typeof to === 'object' && to.replace) {
149
- this.replace(to)
150
- } else {
151
- this.push(to)
152
- }
153
- } else {
154
- // confirm transition and pass on the value
155
- next(to)
156
- }
157
- })
158
- } catch (e) {
159
- abort(e)
160
- }
161
- }
162
-
163
- runQueue(queue, iterator, () => {
164
- const postEnterCbs = []
165
- const isValid = () => this.current === route
166
- // wait until async components are resolved before
167
- // extracting in-component enter guards
168
- const enterGuards = extractEnterGuards(activated, postEnterCbs, isValid)
169
- const queue = enterGuards.concat(this.router.resolveHooks)
170
- runQueue(queue, iterator, () => {
171
- if (this.pending !== route) {
172
- return abort()
173
- }
174
- this.pending = null
175
- onComplete(route)
176
- if (this.router.app) {
177
- this.router.app.$nextTick(() => {
178
- postEnterCbs.forEach(cb => { cb() })
179
- })
180
- }
181
- })
182
- })
183
- }
184
-
185
- updateRoute (route: Route) {
186
- const prev = this.current
187
- this.current = route
188
- this.cb && this.cb(route)
189
- this.router.afterHooks.forEach(hook => {
190
- hook && hook(route, prev)
191
- })
192
- }
193
- }
194
-
195
- function normalizeBase (base: ?string): string {
196
- if (!base) {
197
- if (inBrowser) {
198
- // respect <base> tag
199
- const baseEl = document.querySelector('base')
200
- base = (baseEl && baseEl.getAttribute('href')) || '/'
201
- // strip full URL origin
202
- base = base.replace(/^https?:\/\/[^\/]+/, '')
203
- } else {
204
- base = '/'
205
- }
206
- }
207
- // make sure there's the starting slash
208
- if (base.charAt(0) !== '/') {
209
- base = '/' + base
210
- }
211
- // remove trailing slash
212
- return base.replace(/\/$/, '')
213
- }
214
-
215
- function resolveQueue (
216
- current: Array<RouteRecord>,
217
- next: Array<RouteRecord>
218
- ): {
219
- updated: Array<RouteRecord>,
220
- activated: Array<RouteRecord>,
221
- deactivated: Array<RouteRecord>
222
- } {
223
- let i
224
- const max = Math.max(current.length, next.length)
225
- for (i = 0; i < max; i++) {
226
- if (current[i] !== next[i]) {
227
- break
228
- }
229
- }
230
- return {
231
- updated: next.slice(0, i),
232
- activated: next.slice(i),
233
- deactivated: current.slice(i)
234
- }
235
- }
236
-
237
- function extractGuards (
238
- records: Array<RouteRecord>,
239
- name: string,
240
- bind: Function,
241
- reverse?: boolean
242
- ): Array<?Function> {
243
- const guards = flatMapComponents(records, (def, instance, match, key) => {
244
- const guard = extractGuard(def, name)
245
- if (guard) {
246
- return Array.isArray(guard)
247
- ? guard.map(guard => bind(guard, instance, match, key))
248
- : bind(guard, instance, match, key)
249
- }
250
- })
251
- return flatten(reverse ? guards.reverse() : guards)
252
- }
253
-
254
- function extractGuard (
255
- def: Object | Function,
256
- key: string
257
- ): NavigationGuard | Array<NavigationGuard> {
258
- if (typeof def !== 'function') {
259
- // extend now so that global mixins are applied.
260
- def = _Kdu.extend(def)
261
- }
262
- return def.options[key]
263
- }
264
-
265
- function extractLeaveGuards (deactivated: Array<RouteRecord>): Array<?Function> {
266
- return extractGuards(deactivated, 'beforeRouteLeave', bindGuard, true)
267
- }
268
-
269
- function extractUpdateHooks (updated: Array<RouteRecord>): Array<?Function> {
270
- return extractGuards(updated, 'beforeRouteUpdate', bindGuard)
271
- }
272
-
273
- function bindGuard (guard: NavigationGuard, instance: ?_Kdu): ?NavigationGuard {
274
- if (instance) {
275
- return function boundRouteGuard () {
276
- return guard.apply(instance, arguments)
277
- }
278
- }
279
- }
280
-
281
- function extractEnterGuards (
282
- activated: Array<RouteRecord>,
283
- cbs: Array<Function>,
284
- isValid: () => boolean
285
- ): Array<?Function> {
286
- return extractGuards(activated, 'beforeRouteEnter', (guard, _, match, key) => {
287
- return bindEnterGuard(guard, match, key, cbs, isValid)
288
- })
289
- }
290
-
291
- function bindEnterGuard (
292
- guard: NavigationGuard,
293
- match: RouteRecord,
294
- key: string,
295
- cbs: Array<Function>,
296
- isValid: () => boolean
297
- ): NavigationGuard {
298
- return function routeEnterGuard (to, from, next) {
299
- return guard(to, from, cb => {
300
- next(cb)
301
- if (typeof cb === 'function') {
302
- cbs.push(() => {
303
- // #750
304
- // if a router-view is wrapped with an out-in transition,
305
- // the instance may not have been registered at this time.
306
- // we will need to poll for registration until current route
307
- // is no longer valid.
308
- poll(cb, match.instances, key, isValid)
309
- })
310
- }
311
- })
312
- }
313
- }
314
-
315
- function poll (
316
- cb: any, // somehow flow cannot infer this is a function
317
- instances: Object,
318
- key: string,
319
- isValid: () => boolean
320
- ) {
321
- if (instances[key]) {
322
- cb(instances[key])
323
- } else if (isValid()) {
324
- setTimeout(() => {
325
- poll(cb, instances, key, isValid)
326
- }, 16)
327
- }
328
- }
@@ -1,97 +0,0 @@
1
- /* @flow */
2
-
3
- import type Router from '../index'
4
- import { History } from './base'
5
- import { cleanPath } from '../util/path'
6
- import { getLocation } from './html5'
7
-
8
- export class HashHistory extends History {
9
- constructor (router: Router, base: ?string, fallback: boolean) {
10
- super(router, base)
11
- // check history fallback deeplinking
12
- if (fallback && checkFallback(this.base)) {
13
- return
14
- }
15
- ensureSlash()
16
- }
17
-
18
- // this is delayed until the app mounts
19
- // to avoid the hashchange listener being fired too early
20
- setupListeners () {
21
- window.addEventListener('hashchange', () => {
22
- if (!ensureSlash()) {
23
- return
24
- }
25
- this.transitionTo(getHash(), route => {
26
- replaceHash(route.fullPath)
27
- })
28
- })
29
- }
30
-
31
- push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
32
- this.transitionTo(location, route => {
33
- pushHash(route.fullPath)
34
- onComplete && onComplete(route)
35
- }, onAbort)
36
- }
37
-
38
- replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
39
- this.transitionTo(location, route => {
40
- replaceHash(route.fullPath)
41
- onComplete && onComplete(route)
42
- }, onAbort)
43
- }
44
-
45
- go (n: number) {
46
- window.history.go(n)
47
- }
48
-
49
- ensureURL (push?: boolean) {
50
- const current = this.current.fullPath
51
- if (getHash() !== current) {
52
- push ? pushHash(current) : replaceHash(current)
53
- }
54
- }
55
-
56
- getCurrentLocation () {
57
- return getHash()
58
- }
59
- }
60
-
61
- function checkFallback (base) {
62
- const location = getLocation(base)
63
- if (!/^\/#/.test(location)) {
64
- window.location.replace(
65
- cleanPath(base + '/#' + location)
66
- )
67
- return true
68
- }
69
- }
70
-
71
- function ensureSlash (): boolean {
72
- const path = getHash()
73
- if (path.charAt(0) === '/') {
74
- return true
75
- }
76
- replaceHash('/' + path)
77
- return false
78
- }
79
-
80
- export function getHash (): string {
81
- // We can't use window.location.hash here because it's not
82
- // consistent across browsers - Firefox will pre-decode it!
83
- const href = window.location.href
84
- const index = href.indexOf('#')
85
- return index === -1 ? '' : href.slice(index + 1)
86
- }
87
-
88
- function pushHash (path) {
89
- window.location.hash = path
90
- }
91
-
92
- function replaceHash (path) {
93
- const href = window.location.href
94
- const i = href.indexOf('#')
95
- const base = i >= 0 ? href.slice(0, i) : href
96
- window.location.replace(`${base}#${path}`)
97
- }
@@ -1,69 +0,0 @@
1
- /* @flow */
2
-
3
- import type Router from '../index'
4
- import { History } from './base'
5
- import { cleanPath } from '../util/path'
6
- import { setupScroll, handleScroll } from '../util/scroll'
7
- import { pushState, replaceState } from '../util/push-state'
8
-
9
- export class HTML5History extends History {
10
- constructor (router: Router, base: ?string) {
11
- super(router, base)
12
-
13
- const expectScroll = router.options.scrollBehavior
14
-
15
- if (expectScroll) {
16
- setupScroll()
17
- }
18
-
19
- window.addEventListener('popstate', e => {
20
- const current = this.current
21
- this.transitionTo(getLocation(this.base), route => {
22
- if (expectScroll) {
23
- handleScroll(router, route, current, true)
24
- }
25
- })
26
- })
27
- }
28
-
29
- go (n: number) {
30
- window.history.go(n)
31
- }
32
-
33
- push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
34
- const { current: fromRoute } = this
35
- this.transitionTo(location, route => {
36
- pushState(cleanPath(this.base + route.fullPath))
37
- handleScroll(this.router, route, fromRoute, false)
38
- onComplete && onComplete(route)
39
- }, onAbort)
40
- }
41
-
42
- replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
43
- const { current: fromRoute } = this
44
- this.transitionTo(location, route => {
45
- replaceState(cleanPath(this.base + route.fullPath))
46
- handleScroll(this.router, route, fromRoute, false)
47
- onComplete && onComplete(route)
48
- }, onAbort)
49
- }
50
-
51
- ensureURL (push?: boolean) {
52
- if (getLocation(this.base) !== this.current.fullPath) {
53
- const current = cleanPath(this.base + this.current.fullPath)
54
- push ? pushState(current) : replaceState(current)
55
- }
56
- }
57
-
58
- getCurrentLocation (): string {
59
- return getLocation(this.base)
60
- }
61
- }
62
-
63
- export function getLocation (base: string): string {
64
- let path = window.location.pathname
65
- if (base && path.indexOf(base) === 0) {
66
- path = path.slice(base.length)
67
- }
68
- return (path || '/') + window.location.search + window.location.hash
69
- }