kdu-router 2.7.0 → 3.1.3

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,59 +0,0 @@
1
- /* @flow */
2
-
3
- import { inBrowser } from './dom'
4
- import { saveScrollPosition } from './scroll'
5
-
6
- export const supportsPushState = inBrowser && (function () {
7
- const ua = window.navigator.userAgent
8
-
9
- if (
10
- (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
11
- ua.indexOf('Mobile Safari') !== -1 &&
12
- ua.indexOf('Chrome') === -1 &&
13
- ua.indexOf('Windows Phone') === -1
14
- ) {
15
- return false
16
- }
17
-
18
- return window.history && 'pushState' in window.history
19
- })()
20
-
21
- // use User Timing api (if present) for more accurate key precision
22
- const Time = inBrowser && window.performance && window.performance.now
23
- ? window.performance
24
- : Date
25
-
26
- let _key: string = genKey()
27
-
28
- function genKey (): string {
29
- return Time.now().toFixed(3)
30
- }
31
-
32
- export function getStateKey () {
33
- return _key
34
- }
35
-
36
- export function setStateKey (key: string) {
37
- _key = key
38
- }
39
-
40
- export function pushState (url?: string, replace?: boolean) {
41
- saveScrollPosition()
42
- // try...catch the pushState call to get around Safari
43
- // DOM Exception 18 where it limits to 100 pushState calls
44
- const history = window.history
45
- try {
46
- if (replace) {
47
- history.replaceState({ key: _key }, '', url)
48
- } else {
49
- _key = genKey()
50
- history.pushState({ key: _key }, '', url)
51
- }
52
- } catch (e) {
53
- window.location[replace ? 'replace' : 'assign'](url)
54
- }
55
- }
56
-
57
- export function replaceState (url?: string) {
58
- pushState(url, true)
59
- }
package/src/util/query.js DELETED
@@ -1,96 +0,0 @@
1
- /* @flow */
2
-
3
- import { warn } from './warn'
4
-
5
- const encodeReserveRE = /[!'()*]/g
6
- const encodeReserveReplacer = c => '%' + c.charCodeAt(0).toString(16)
7
- const commaRE = /%2C/g
8
-
9
- // fixed encodeURIComponent which is more conformant to RFC3986:
10
- // - escapes [!'()*]
11
- // - preserve commas
12
- const encode = str => encodeURIComponent(str)
13
- .replace(encodeReserveRE, encodeReserveReplacer)
14
- .replace(commaRE, ',')
15
-
16
- const decode = decodeURIComponent
17
-
18
- export function resolveQuery (
19
- query: ?string,
20
- extraQuery: Dictionary<string> = {},
21
- _parseQuery: ?Function
22
- ): Dictionary<string> {
23
- const parse = _parseQuery || parseQuery
24
- let parsedQuery
25
- try {
26
- parsedQuery = parse(query || '')
27
- } catch (e) {
28
- process.env.NODE_ENV !== 'production' && warn(false, e.message)
29
- parsedQuery = {}
30
- }
31
- for (const key in extraQuery) {
32
- const val = extraQuery[key]
33
- parsedQuery[key] = Array.isArray(val) ? val.slice() : val
34
- }
35
- return parsedQuery
36
- }
37
-
38
- function parseQuery (query: string): Dictionary<string> {
39
- const res = {}
40
-
41
- query = query.trim().replace(/^(\?|#|&)/, '')
42
-
43
- if (!query) {
44
- return res
45
- }
46
-
47
- query.split('&').forEach(param => {
48
- const parts = param.replace(/\+/g, ' ').split('=')
49
- const key = decode(parts.shift())
50
- const val = parts.length > 0
51
- ? decode(parts.join('='))
52
- : null
53
-
54
- if (res[key] === undefined) {
55
- res[key] = val
56
- } else if (Array.isArray(res[key])) {
57
- res[key].push(val)
58
- } else {
59
- res[key] = [res[key], val]
60
- }
61
- })
62
-
63
- return res
64
- }
65
-
66
- export function stringifyQuery (obj: Dictionary<string>): string {
67
- const res = obj ? Object.keys(obj).map(key => {
68
- const val = obj[key]
69
-
70
- if (val === undefined) {
71
- return ''
72
- }
73
-
74
- if (val === null) {
75
- return encode(key)
76
- }
77
-
78
- if (Array.isArray(val)) {
79
- const result = []
80
- val.forEach(val2 => {
81
- if (val2 === undefined) {
82
- return
83
- }
84
- if (val2 === null) {
85
- result.push(encode(key))
86
- } else {
87
- result.push(encode(key) + '=' + encode(val2))
88
- }
89
- })
90
- return result.join('&')
91
- }
92
-
93
- return encode(key) + '=' + encode(val)
94
- }).filter(x => x.length > 0).join('&') : null
95
- return res ? `?${res}` : ''
96
- }
@@ -1,100 +0,0 @@
1
- /* @flow */
2
-
3
- import { _Kdu } from '../install'
4
- import { warn, isError } from './warn'
5
-
6
- export function resolveAsyncComponents (matched: Array<RouteRecord>): Function {
7
- return (to, from, next) => {
8
- let hasAsync = false
9
- let pending = 0
10
- let error = null
11
-
12
- flatMapComponents(matched, (def, _, match, key) => {
13
- // if it's a function and doesn't have cid attached,
14
- // assume it's an async component resolve function.
15
- // we are not using Kdu's default async resolving mechanism because
16
- // we want to halt the navigation until the incoming component has been
17
- // resolved.
18
- if (typeof def === 'function' && def.cid === undefined) {
19
- hasAsync = true
20
- pending++
21
-
22
- const resolve = once(resolvedDef => {
23
- if (resolvedDef.__esModule && resolvedDef.default) {
24
- resolvedDef = resolvedDef.default
25
- }
26
- // save resolved on async factory in case it's used elsewhere
27
- def.resolved = typeof resolvedDef === 'function'
28
- ? resolvedDef
29
- : _Kdu.extend(resolvedDef)
30
- match.components[key] = resolvedDef
31
- pending--
32
- if (pending <= 0) {
33
- next()
34
- }
35
- })
36
-
37
- const reject = once(reason => {
38
- const msg = `Failed to resolve async component ${key}: ${reason}`
39
- process.env.NODE_ENV !== 'production' && warn(false, msg)
40
- if (!error) {
41
- error = isError(reason)
42
- ? reason
43
- : new Error(msg)
44
- next(error)
45
- }
46
- })
47
-
48
- let res
49
- try {
50
- res = def(resolve, reject)
51
- } catch (e) {
52
- reject(e)
53
- }
54
- if (res) {
55
- if (typeof res.then === 'function') {
56
- res.then(resolve, reject)
57
- } else {
58
- // new syntax in Kdu
59
- const comp = res.component
60
- if (comp && typeof comp.then === 'function') {
61
- comp.then(resolve, reject)
62
- }
63
- }
64
- }
65
- }
66
- })
67
-
68
- if (!hasAsync) next()
69
- }
70
- }
71
-
72
- export function flatMapComponents (
73
- matched: Array<RouteRecord>,
74
- fn: Function
75
- ): Array<?Function> {
76
- return flatten(matched.map(m => {
77
- return Object.keys(m.components).map(key => fn(
78
- m.components[key],
79
- m.instances[key],
80
- m, key
81
- ))
82
- }))
83
- }
84
-
85
- export function flatten (arr: Array<any>): Array<any> {
86
- return Array.prototype.concat.apply([], arr)
87
- }
88
-
89
- // in Webpack 2, require.ensure now also returns a Promise
90
- // so the resolve/reject functions may get called an extra time
91
- // if the user uses an arrow function shorthand that happens to
92
- // return that Promise.
93
- function once (fn) {
94
- let called = false
95
- return function (...args) {
96
- if (called) return
97
- called = true
98
- return fn.apply(this, args)
99
- }
100
- }
package/src/util/route.js DELETED
@@ -1,110 +0,0 @@
1
- /* @flow */
2
-
3
- import type KduRouter from '../index'
4
- import { stringifyQuery } from './query'
5
-
6
- const trailingSlashRE = /\/?$/
7
-
8
- export function createRoute (
9
- record: ?RouteRecord,
10
- location: Location,
11
- redirectedFrom?: ?Location,
12
- router?: KduRouter
13
- ): Route {
14
- const stringifyQuery = router && router.options.stringifyQuery
15
- const route: Route = {
16
- name: location.name || (record && record.name),
17
- meta: (record && record.meta) || {},
18
- path: location.path || '/',
19
- hash: location.hash || '',
20
- query: location.query || {},
21
- params: location.params || {},
22
- fullPath: getFullPath(location, stringifyQuery),
23
- matched: record ? formatMatch(record) : []
24
- }
25
- if (redirectedFrom) {
26
- route.redirectedFrom = getFullPath(redirectedFrom, stringifyQuery)
27
- }
28
- return Object.freeze(route)
29
- }
30
-
31
- // the starting route that represents the initial state
32
- export const START = createRoute(null, {
33
- path: '/'
34
- })
35
-
36
- function formatMatch (record: ?RouteRecord): Array<RouteRecord> {
37
- const res = []
38
- while (record) {
39
- res.unshift(record)
40
- record = record.parent
41
- }
42
- return res
43
- }
44
-
45
- function getFullPath (
46
- { path, query = {}, hash = '' },
47
- _stringifyQuery
48
- ): string {
49
- const stringify = _stringifyQuery || stringifyQuery
50
- return (path || '/') + stringify(query) + hash
51
- }
52
-
53
- export function isSameRoute (a: Route, b: ?Route): boolean {
54
- if (b === START) {
55
- return a === b
56
- } else if (!b) {
57
- return false
58
- } else if (a.path && b.path) {
59
- return (
60
- a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
61
- a.hash === b.hash &&
62
- isObjectEqual(a.query, b.query)
63
- )
64
- } else if (a.name && b.name) {
65
- return (
66
- a.name === b.name &&
67
- a.hash === b.hash &&
68
- isObjectEqual(a.query, b.query) &&
69
- isObjectEqual(a.params, b.params)
70
- )
71
- } else {
72
- return false
73
- }
74
- }
75
-
76
- function isObjectEqual (a = {}, b = {}): boolean {
77
- const aKeys = Object.keys(a)
78
- const bKeys = Object.keys(b)
79
- if (aKeys.length !== bKeys.length) {
80
- return false
81
- }
82
- return aKeys.every(key => {
83
- const aVal = a[key]
84
- const bVal = b[key]
85
- // check nested equality
86
- if (typeof aVal === 'object' && typeof bVal === 'object') {
87
- return isObjectEqual(aVal, bVal)
88
- }
89
- return String(aVal) === String(bVal)
90
- })
91
- }
92
-
93
- export function isIncludedRoute (current: Route, target: Route): boolean {
94
- return (
95
- current.path.replace(trailingSlashRE, '/').indexOf(
96
- target.path.replace(trailingSlashRE, '/')
97
- ) === 0 &&
98
- (!target.hash || current.hash === target.hash) &&
99
- queryIncludes(current.query, target.query)
100
- )
101
- }
102
-
103
- function queryIncludes (current: Dictionary<string>, target: Dictionary<string>): boolean {
104
- for (const key in target) {
105
- if (!(key in current)) {
106
- return false
107
- }
108
- }
109
- return true
110
- }
@@ -1,111 +0,0 @@
1
- /* @flow */
2
-
3
- import type Router from '../index'
4
- import { assert } from './warn'
5
- import { getStateKey, setStateKey } from './push-state'
6
-
7
- const positionStore = Object.create(null)
8
-
9
- export function setupScroll () {
10
- window.addEventListener('popstate', e => {
11
- saveScrollPosition()
12
- if (e.state && e.state.key) {
13
- setStateKey(e.state.key)
14
- }
15
- })
16
- }
17
-
18
- export function handleScroll (
19
- router: Router,
20
- to: Route,
21
- from: Route,
22
- isPop: boolean
23
- ) {
24
- if (!router.app) {
25
- return
26
- }
27
-
28
- const behavior = router.options.scrollBehavior
29
- if (!behavior) {
30
- return
31
- }
32
-
33
- if (process.env.NODE_ENV !== 'production') {
34
- assert(typeof behavior === 'function', `scrollBehavior must be a function`)
35
- }
36
-
37
- // wait until re-render finishes before scrolling
38
- router.app.$nextTick(() => {
39
- let position = getScrollPosition()
40
- const shouldScroll = behavior(to, from, isPop ? position : null)
41
- if (!shouldScroll) {
42
- return
43
- }
44
- const isObject = typeof shouldScroll === 'object'
45
- if (isObject && typeof shouldScroll.selector === 'string') {
46
- const el = document.querySelector(shouldScroll.selector)
47
- if (el) {
48
- let offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}
49
- offset = normalizeOffset(offset)
50
- position = getElementPosition(el, offset)
51
- } else if (isValidPosition(shouldScroll)) {
52
- position = normalizePosition(shouldScroll)
53
- }
54
- } else if (isObject && isValidPosition(shouldScroll)) {
55
- position = normalizePosition(shouldScroll)
56
- }
57
-
58
- if (position) {
59
- window.scrollTo(position.x, position.y)
60
- }
61
- })
62
- }
63
-
64
- export function saveScrollPosition () {
65
- const key = getStateKey()
66
- if (key) {
67
- positionStore[key] = {
68
- x: window.pageXOffset,
69
- y: window.pageYOffset
70
- }
71
- }
72
- }
73
-
74
- function getScrollPosition (): ?Object {
75
- const key = getStateKey()
76
- if (key) {
77
- return positionStore[key]
78
- }
79
- }
80
-
81
- function getElementPosition (el: Element, offset: Object): Object {
82
- const docEl: any = document.documentElement
83
- const docRect = docEl.getBoundingClientRect()
84
- const elRect = el.getBoundingClientRect()
85
- return {
86
- x: elRect.left - docRect.left - offset.x,
87
- y: elRect.top - docRect.top - offset.y
88
- }
89
- }
90
-
91
- function isValidPosition (obj: Object): boolean {
92
- return isNumber(obj.x) || isNumber(obj.y)
93
- }
94
-
95
- function normalizePosition (obj: Object): Object {
96
- return {
97
- x: isNumber(obj.x) ? obj.x : window.pageXOffset,
98
- y: isNumber(obj.y) ? obj.y : window.pageYOffset
99
- }
100
- }
101
-
102
- function normalizeOffset (obj: Object): Object {
103
- return {
104
- x: isNumber(obj.x) ? obj.x : 0,
105
- y: isNumber(obj.y) ? obj.y : 0
106
- }
107
- }
108
-
109
- function isNumber (v: any): boolean {
110
- return typeof v === 'number'
111
- }
package/src/util/warn.js DELETED
@@ -1,17 +0,0 @@
1
- /* @flow */
2
-
3
- export function assert (condition: any, message: string) {
4
- if (!condition) {
5
- throw new Error(`[kdu-router] ${message}`)
6
- }
7
- }
8
-
9
- export function warn (condition: any, message: string) {
10
- if (process.env.NODE_ENV !== 'production' && !condition) {
11
- typeof console !== 'undefined' && console.warn(`[kdu-router] ${message}`)
12
- }
13
- }
14
-
15
- export function isError (err: any): boolean {
16
- return Object.prototype.toString.call(err).indexOf('Error') > -1
17
- }