-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFoxyRouter.js
153 lines (124 loc) · 3.78 KB
/
FoxyRouter.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
* FoxyRouter
* Lightweight javascript routing library for single page applications
* https://github.com/WillBrock/FoxyRouter
*
* Copyright 2016 Will Brock
* Released under the GPL license
* http://www.gnu.org/copyleft/gpl.html
*/
((context) => {
`use strict`;
class FoxyRouter {
constructor() {
// Stores all the routes for the application
this.routes = [];
}
// Add new routes
add(route, ...callbacks) {
this.routes.push({
path : route,
callbacks : callbacks || []
});
return this;
}
// Main function to rendor the route data
render(active_route, search) {
// Store any route parameters, if any
let parameters = {};
// Regex that will match values like :id, :user_id
let route_regex = /:[^\s(/|\-|\.)]+/g;
let replace_regex = `[\\w-]+`;
if(active_route.lastIndexOf(`/`) === active_route.length -1) {
active_route = active_route.slice(0, -1);
}
// Find a matching route
for(let route of this.routes) {
let path = route.path;
let callbacks = route.callbacks;
// Replace any parameters with regex to match the path
let path_test = path.replace(route_regex, replace_regex);
let absolute_path = path.substring(0, active_route.length) === active_route;
let match = active_route.match(path_test) || absolute_path;
// If there is no match for the current route
if(!match) {
continue;
}
if(!absolute_path) {
// Get any keys and values that are in the route, e.g :id, :user_id, etc.
let active_offset = 0;
path.replace(route_regex, (match, offset) => {
let index = match.substr(1);
active_offset = active_offset === 0 ? offset : active_offset;
let route_match = active_route.substr(active_offset - 1).match(replace_regex)[0];
parameters[index] = route_match;
active_offset = active_offset === 0 ? offset + route_match.length : active_offset + route_match.length + 2;
});
}
// Check for any query parameters
search.replace(/([^?=&]+)(=([^&]*))?/g, (match, key, foo, value) => {
parameters[key] = decodeURIComponent(value);
});
// Trigger the callbacks
for(let callback of callbacks) {
callback(parameters);
}
return true;
}
// If we get here then no route was found
return false;
}
}
let Router = new FoxyRouter();
if(typeof module != `undefined` && module.exports) {
module.exports = Router;
}
else if(typeof define === `function` && define.amd) {
define(Router);
}
else {
context.FoxyRouter = Router;
}
// Handle history
window.addEventListener(`popstate`, (e) => {
render(window.location.pathname, window.location.search);
});
// Render the route on page load
// This will also handle bookmarked links
window.addEventListener(`load`, (e) => {
let route = window.location.pathname;
let search = window.location.search;
render(route, search);
e.preventDefault();
});
// Route all clicks on link tags except for external links
document.querySelector(`body`).addEventListener(`click`, (e) => {
let target = e.target;
// Only route on link tags
if(target.tagName.toLowerCase() !== `a`) {
return;
}
// Don't continue if it's an external link
if(target.host !== window.location.host) {
return;
}
// Render the route
let rendered = render(target.pathname, target.search);
// Don't refresh on internal links that have a route defined
if(rendered) {
e.preventDefault();
}
});
/**
* Helper function to render routes
* @param {String} Path of the route
* @param {String} Query parameters
* @return {Boolean}
*/
function render(route, search = ``) {
// Set the new path and search
window.history.pushState({}, ``, `${route}${search}`);
// Render the new route
return Router.render(route, search);
}
})(this);