-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
121 lines (98 loc) · 3.33 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
var pathToRegexp = require('path-to-regexp-with-reversible-keys')
var qs = require('query-string')
var xtend = require('xtend')
var browserHashLocation = require('./hash-location.js')
var EventEmitter = require('eventemitter3')
module.exports = function Router(opts, hashLocation) {
var emitter = new EventEmitter()
if (isHashLocation(opts)) {
hashLocation = opts
opts = null
}
opts = opts || {}
if (!hashLocation) {
hashLocation = browserHashLocation(window)
}
function onNotFound(path, queryStringParameters) {
emitter.emit('not found', path, queryStringParameters)
}
var routes = []
var onHashChange = evaluateCurrentPath.bind(null, routes, hashLocation, !!opts.reverse, onNotFound)
hashLocation.on('hashchange', onHashChange)
function stop() {
hashLocation.removeListener('hashchange', onHashChange)
}
emitter.add = add.bind(null, routes)
emitter.stop = stop
emitter.evaluateCurrent = evaluateCurrentPathOrGoToDefault.bind(null, routes, hashLocation, !!opts.reverse, onNotFound)
emitter.replace = hashLocation.replace
emitter.go = hashLocation.go
emitter.location = hashLocation
return emitter
}
function evaluateCurrentPath(routes, hashLocation, reverse, onNotFound) {
evaluatePath(routes, stripHashFragment(hashLocation.get()), reverse, onNotFound)
}
function getPathParts(path) {
var chunks = path.split('?')
return {
path: chunks.shift(),
queryString: qs.parse(chunks.join('')),
}
}
function evaluatePath(routes, path, reverse, onNotFound) {
var pathParts = getPathParts(path)
path = pathParts.path
var queryStringParameters = pathParts.queryString
var matchingRoute = find((reverse ? reverseArray(routes) : routes), path)
if (matchingRoute) {
var regexResult = matchingRoute.exec(path)
var routeParameters = makeParametersObjectFromRegexResult(matchingRoute.keys, regexResult)
var params = xtend(queryStringParameters, routeParameters)
matchingRoute.fn(params)
} else {
onNotFound(path, queryStringParameters)
}
}
function reverseArray(ary) {
return ary.slice().reverse()
}
function makeParametersObjectFromRegexResult(keys, regexResult) {
return keys.reduce(function(memo, urlKey, index) {
memo[urlKey.name] = regexResult[index + 1]
return memo
}, {})
}
function add(routes, routeString, routeFunction) {
if (typeof routeFunction !== 'function') {
throw new Error('The router add function must be passed a callback function')
}
var newRoute = pathToRegexp(routeString)
newRoute.fn = routeFunction
routes.push(newRoute)
}
function evaluateCurrentPathOrGoToDefault(routes, hashLocation, reverse, onNotFound, defaultPath) {
var currentLocation = stripHashFragment(hashLocation.get())
var canUseCurrentLocation = currentLocation && (currentLocation !== '/' || defaultPath === '/')
if (canUseCurrentLocation) {
var routesCopy = routes.slice()
evaluateCurrentPath(routesCopy, hashLocation, reverse, onNotFound)
} else {
hashLocation.go(defaultPath)
}
}
var urlWithoutHashFragmentRegex = /^([^#]*)(:?#.*)?$/
function stripHashFragment(url) {
var match = url.match(urlWithoutHashFragmentRegex)
return match ? match[1] : ''
}
function isHashLocation(hashLocation) {
return hashLocation && hashLocation.go && hashLocation.replace && hashLocation.on
}
function find(aryOfRegexes, str) {
for (var i = 0; i < aryOfRegexes.length; ++i) {
if (str.match(aryOfRegexes[i])) {
return aryOfRegexes[i]
}
}
}