-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathchoreographer.js
96 lines (93 loc) · 3.86 KB
/
choreographer.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
/**
* The Choreographer
* Your server is my stage -- dirt simple URL routing for Node.js.
*
* by Han (slightly modified by Adrien Risser)
*
* http://github.com/laughinghan/choreographer
*
*/
var parse = require('url').parse;
GLOBAL.router = (function () {
//router, to be passed to `require('http').createServer()`
var router = function router(req, res) {
var path = parse(req.url).pathname,
_routes = routes[req.method];
if (_routes != undefined) {
len = _routes.length;
for (var i = 0; i < len; i += 1) {
//say '/foo/bar/baz' matches '/foo/*/*'
var route = _routes[i],
matches = route.exec(path);
if (matches) { //then matches would be ['/foo/bar/baz','bar','baz']
//so turn arguments from [req,res] into [req,res,'bar','baz']
Array.prototype.push.apply(arguments, matches.slice(1));
console.log("GOT", arguments[0].url);
return route.callback.apply(this, arguments);
}
}
}
//route not found: no route has matched and hence returned yet
router.notFound.apply(this, arguments);
};
//dictionary of arrays of routes
var routes = {};
//routing API
function getRoute(route, ignoreCase) {
if (route.constructor === RegExp) { //instanceof fails between modules
route = new RegExp(route); //if route is already a RegExp, just clone it
} else { //else stringify and interpret as regex where * matches URI segments
if (route.constructor === Array) {
route = route.join("/");
}
route = new RegExp('^' + //and everything else matches literally
String(route).replace(specialChars, '\\$&').replace(/\*\*/g, '(.*)').replace(/\*/g, '([^/]*)') + '$', ignoreCase ? 'i' : '');
}
return route;
}
['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'TRACE', 'OPTIONS', 'CONNECT'].forEach(function (method) {
routes[method] = [];
//e.g. router.get('/foo/*',function(req,res,bar){});
router[method.toLowerCase()] = {
"add": function (route, ignoreCase, callback) {
console.log("Adding route:", route);
if (arguments.length === 2) {
callback = ignoreCase;
ignoreCase = undefined;
}
route = getRoute(route, ignoreCase);
route.callback = callback;
routes[method].push(route);
return router;
},
"remove": function (route, ignoreCase) {
console.log("Removing route:", route);
var idx = routes[method].each(function (el, i) {
if (el.toString() === getRoute(route, ignoreCase).toString()) {
return i;
}
});
if (idx !== undefined) {
routes[method].splice(idx, 1);
}
return router;
}
};
});
//special characters that need to be escaped when passed to `RegExp()`, lest
//they be interpreted as pattern-matching:
var specialChars = /[|.+?{}()\[\]^$]/g;
//creating `get` routes automatically creates `head` routes:
routes.GET.push = function (route) { //as called by `router.get()`
Array.prototype.push.call(this, route);
routes.HEAD.push(route);
};
//404 is a route too
router.notFound = function defaultNotFound(req, res) {
res.writeHead(404, {
'Content-Type': 'text/html'
});
res.end('<html><head><title>Error 404: Not Found</title></head><body>\n' + '<h1>Error 404: Not Found</h1>\n' + '<p>Cannot ' + req.method + ' ' + req.url + '</body></html>\n');
};
return router;
}());