-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathrlite.js
124 lines (100 loc) · 4.04 KB
/
rlite.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
// This library started as an experiment to see how small I could make
// a functional router. It has since been optimized (and thus grown).
// The redundancy and inelegance here is for the sake of either size
// or speed.
//
// That's why router params are marked with a single char: `~` and named params are denoted `:`
(function (root, factory) {
var define = root && root.define;
if (define && define.amd) {
define('rlite', [], factory);
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = factory();
} else {
root.Rlite = factory();
}
}(this, function () {
return function (notFound, routeDefinitions) {
var routes = {};
var decode = decodeURIComponent;
init();
return run;
function init() {
for (var key in routeDefinitions) {
add(key, routeDefinitions[key]);
}
};
function noop(s) { return s; }
function sanitize(url) {
~url.indexOf('/?') && (url = url.replace('/?', '?'));
url[0] == '/' && (url = url.slice(1));
url[url.length - 1] == '/' && (url = url.slice(0, -1));
return url;
}
// Recursively searches the route tree for a matching route
// pieces: an array of url parts, ['users', '1', 'edit']
// esc: the function used to url escape values
// i: the index of the piece being processed
// rules: the route tree
// params: the computed route parameters (this is mutated), and is a stack since we don't have fast immutable datatypes
//
// This attempts to match the most specific route, but may end int a dead-end. We then attempt a less specific
// route, following named route parameters. In searching this secondary branch, we need to make sure to clear
// any route params that were generated during the search of the dead-end branch.
function recurseUrl(pieces, esc, i, rules, params) {
if (!rules) {
return;
}
if (i >= pieces.length) {
var cb = rules['@'];
return cb && {
cb: cb,
params: params.reduce(function(h, kv) { h[kv[0]] = kv[1]; return h; }, {}),
};
}
var piece = esc(pieces[i]);
var paramLen = params.length;
return recurseUrl(pieces, esc, i + 1, rules[piece.toLowerCase()], params)
|| recurseNamedUrl(pieces, esc, i + 1, rules, ':', piece, params, paramLen)
|| recurseNamedUrl(pieces, esc, pieces.length, rules, '*', pieces.slice(i).join('/'), params, paramLen);
}
// Recurses for a named route, where the name is looked up via key and associated with val
function recurseNamedUrl(pieces, esc, i, rules, key, val, params, paramLen) {
params.length = paramLen; // Reset any params generated in the unsuccessful search branch
var subRules = rules[key];
subRules && params.push([subRules['~'], val]);
return recurseUrl(pieces, esc, i, subRules, params);
}
function processQuery(url, ctx, esc) {
if (url && ctx.cb) {
var hash = url.indexOf('#'),
query = (hash < 0 ? url : url.slice(0, hash)).split('&');
for (var i = 0; i < query.length; ++i) {
var nameValue = query[i].split('=');
ctx.params[nameValue[0]] = esc(nameValue[1]);
}
}
return ctx;
}
function lookup(url) {
var querySplit = sanitize(url).split('?');
var esc = ~url.indexOf('%') ? decode : noop;
return processQuery(querySplit[1], recurseUrl(querySplit[0].split('/'), esc, 0, routes, []) || {}, esc);
}
function add(route, handler) {
var pieces = route.split('/');
var rules = routes;
for (var i = +(route[0] === '/'); i < pieces.length; ++i) {
var piece = pieces[i];
var name = piece[0] == ':' ? ':' : piece[0] == '*' ? '*' : piece.toLowerCase();
rules = rules[name] || (rules[name] = {});
(name == ':' || name == '*') && (rules['~'] = piece.slice(1));
}
rules['@'] = handler;
}
function run(url, arg) {
var result = lookup(url);
return (result.cb || notFound)(result.params, arg, url);
};
};
}));