-
Notifications
You must be signed in to change notification settings - Fork 0
/
Router.js
110 lines (93 loc) · 2.5 KB
/
Router.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
import * as queryString from "query-string";
import {Channel} from "porter-client";
import RoutePath from "./RoutePath";
class Router {
// Broadcasts {path, search, hash, error, query, tokens}
constructor(channel) {
if (!(channel instanceof Channel)) throw "Router: a valid channel is required.";
this.channel = channel;
this.routes = [];
this.changeRoute = "changeRoute";
this.defaultRoute = null;
window.onload = e => this.connect();
this.middlewares = {};
}
addDefaultRoute(action) {
this.defaultRoute = new RoutePath(/([\s\S])+/, action); // matches anything
}
addRoute(pattern, action, options) {
this.routes.push(new RoutePath(pattern, action, options || {}));
}
parseHash(hash) {
let parts = hash.split("?");
return {
path: parts[0],
search: parts[1] || null,
hash: hash,
};
}
firstMatchingRoute(parts) {
let matchingRoutes = this.routes.filter(route => {
return parts.path.match(route.pattern);
});
return matchingRoutes[0];
}
pipeThroughMiddleWare(route) {
let pipes = route.options.middlewares;
if (pipes) {
for(var index in pipes) {
let callback = pipes[index];
route = callback(this, route);
}
}
return route;
}
match(hash) {
let parts = this.parseHash(hash);
let route = this.firstMatchingRoute(parts);
if (!route) {
return this.parseMessage(this.defaultRoute, parts, "NotFound");
}
let ok = this.pipeThroughMiddleWare(route);
if (!ok) return;
return this.parseMessage(route, parts);
}
parseMessage(route, parts, error) {
if (!route) {
console.warn("Router: unmatched route and no default", parts[1]);
return null;
}
return {
...parts,
error: error,
query: queryString.parse(parts.search),
tokens: route.parseTokens(parts.path),
action: route.action,
};
}
goTo(location) {
window.location.hash = location;
}
updateRoute() {
if (window.location.hash == "" || window.location.hash == "#") {
this.goTo("#/");
return;
}
let match = this.match(window.location.hash);
if (match) {
this.channel.publish(this.changeRoute, match);
}
}
connect(){
window.addEventListener("hashchange", event => {
this.updateRoute();
});
this.updateRoute();
}
}
if (typeof module != 'undefined') {
module.exports = {
Router: Router
};
}
export default Router;