-
Notifications
You must be signed in to change notification settings - Fork 0
/
jouter.js
102 lines (82 loc) · 2.22 KB
/
jouter.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
const TOKEN_RE = /:[^\/]+/g
const ANY_RE = /\*/g
const SUBPATH_RE = /\/\.\.\./g
const regexify = x =>
x.replace(TOKEN_RE, '([^/]+)')
.replace(ANY_RE, '.*')
.replace(SUBPATH_RE, '(\/.*)')
// parseRoute :: String | RegExp -> RegExp
const routeRe = x =>
x instanceof RegExp ?
new RegExp(x.source)
: new RegExp(`^${regexify(x)}$`)
// route :: (* -> *), String -> (String -> _)
const route = (f, r) => {
const re = routeRe(r)
return (path) => {
const match = re.exec(path)
if (!match) return
f(...match.slice(1))
return true
}
}
// Unsafe functions
const pathHandler = {
// get :: _ -> String
get: () => window.location.pathname,
// set :: (String, String) -> _
set: (path, title) =>
window.history.pushState(undefined, title, path),
// swap :: (String, String) -> _
swap: (path, title) =>
window.history.replaceState(undefined, title, path),
// listen :: (* -> *) -> _
listen: f => window.onpopstate = f,
// decorate :: (* -> *) -> (* -> *)
decorate: f => f,
// onNoMatch :: (String -> _) -> _
onNoMatch: f => undefined,
}
export const createRouter = (myPathHandler = {}) => {
let routes = []
const path = {
...pathHandler,
...myPathHandler
}
const add = (f, r) =>
routes.push(route(path.decorate(f), r))
const dispatch = p => {
const hadMatch = routes.reduce((result, f) => {
const match = f(p)
return result || match
}, false)
if (!hadMatch) path.onNoMatch(p)
return hadMatch
}
const go = (p, t) => {
path.set(p, t)
dispatch(path.get())
}
const replace = (p, t) => {
path.swap(p, t)
dispatch(path.get())
}
const handleEvent = e => {
e.preventDefault()
const target = e.currentTarget
go(target.href, target.title)
}
const start = () => {
path.listen(() => dispatch(path.get()))
dispatch(path.get())
}
const dispatchRoutes = subpath =>
dispatch(subpath)
dispatchRoutes.add = add
dispatchRoutes.go = go
dispatchRoutes.replace = replace
dispatchRoutes.handleEvent = handleEvent
dispatchRoutes.start = start
return dispatchRoutes
}
export default createRouter