UNPKG

lazyload-js

Version:

An ancient tiny JS and CSS loader from the days before everyone had written one, Fork from @github/rgrove/lazyload

330 lines (285 loc) 7.61 kB
/*! * Express - router * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var utils = require('../utils') , parse = require('url').parse , _methods = require('./methods') , Route = require('./route'); /** * Expose router. */ exports = module.exports = router; /** * Expose methods. */ exports.methods = _methods; /** * Provides Sinatra-like routing capabilities. * * @param {Function} fn * @return {Function} * @api private */ function router(fn, app){ var self = this , methods = {} , routes = {} , params = {}; if (!fn) throw new Error('router provider requires a callback function'); // Generate method functions _methods.forEach(function(method){ methods[method] = generateMethodFunction(method.toUpperCase()); }); // Alias del -> delete methods.del = methods.delete; // Apply callback to all methods methods.all = function(){ var args = arguments; _methods.forEach(function(name){ methods[name].apply(this, args); }); return self; }; // Register param callback methods.param = function(name, fn){ params[name] = fn; }; fn.call(this, methods); function generateMethodFunction(name) { var localRoutes = routes[name] = routes[name] || []; return function(path, fn){ var keys = [] , middleware = []; // slice middleware if (arguments.length > 2) { middleware = Array.prototype.slice.call(arguments, 1, arguments.length); fn = middleware.pop(); middleware = utils.flatten(middleware); } if (!path) throw new Error(name + ' route requires a path'); if (!fn) throw new Error(name + ' route ' + path + ' requires a callback'); var options = { sensitive: app.enabled('case sensitive routes') }; var route = new Route(name, path, fn, options); route.middleware = middleware; localRoutes.push(route); return self; }; } function router(req, res, next){ var route , self = this; (function pass(i){ if (route = match(req, routes, i)) { var i = 0 , keys = route.keys; req.params = route.params; // Param preconditions (function param(err) { try { var key = keys[i++] , val = req.params[key] , fn = params[key]; if ('route' == err) { pass(req._route_index + 1); // Error } else if (err) { next(err); // Param has callback } else if (fn) { // Return style if (1 == fn.length) { req.params[key] = fn(val); param(); // Middleware style } else { fn(req, res, param, val); } // Finished processing params } else if (!key) { // route middleware i = 0; (function nextMiddleware(err){ var fn = route.middleware[i++]; if ('route' == err) { pass(req._route_index + 1); } else if (err) { next(err); } else if (fn) { fn(req, res, nextMiddleware); } else { route.callback.call(self, req, res, function(err){ if (err) { next(err); } else { pass(req._route_index + 1); } }); } })(); // More params } else { param(); } } catch (err) { next(err); } })(); } else if ('OPTIONS' == req.method) { options(req, res, routes); } else { next(); } })(); }; router.remove = function(path, method, ret){ var ret = ret || [] , route; // method specific remove if (method) { method = method.toUpperCase(); if (routes[method]) { for (var i = 0; i < routes[method].length; ++i) { route = routes[method][i]; if (path == route.path) { route.index = i; routes[method].splice(i, 1); ret.push(route); --i; } } } // global remove } else { _methods.forEach(function(method){ router.remove(path, method, ret); }); } return ret; }; router.lookup = function(path, method, ret){ ret = ret || []; // method specific lookup if (method) { method = method.toUpperCase(); if (routes[method]) { routes[method].forEach(function(route, i){ if (path == route.path) { route.index = i; ret.push(route); } }); } // global lookup } else { _methods.forEach(function(method){ router.lookup(path, method, ret); }); } return ret; }; router.match = function(url, method, ret){ var ret = ret || [] , i = 0 , route , req; // method specific matches if (method) { method = method.toUpperCase(); req = { url: url, method: method }; while (route = match(req, routes, i)) { i = req._route_index + 1; route.index = i; ret.push(route); } // global matches } else { _methods.forEach(function(method){ router.match(url, method, ret); }); } return ret; }; return router; } /** * Respond to OPTIONS. * * @param {ServerRequest} req * @param {ServerResponse} req * @param {Array} routes * @api private */ function options(req, res, routes) { var pathname = parse(req.url).pathname , body = optionsFor(pathname, routes).join(','); res.send(body, { Allow: body }); } /** * Return OPTIONS array for the given `path`, matching `routes`. * * @param {String} path * @param {Array} routes * @return {Array} * @api private */ function optionsFor(path, routes) { return _methods.filter(function(method){ var arr = routes[method.toUpperCase()]; for (var i = 0, len = arr.length; i < len; ++i) { if (arr[i].regexp.test(path)) return true; } }).map(function(method){ return method.toUpperCase(); }); } /** * Attempt to match the given request to * one of the routes. When successful * a route function is returned. * * @param {ServerRequest} req * @param {Object} routes * @return {Function} * @api private */ function match(req, routes, i) { var captures , method = req.method , i = i || 0; // pass HEAD to GET routes if ('HEAD' == method) method = 'GET'; // routes for this method if (routes = routes[method]) { var url = parse(req.url) , pathname = url.pathname; // matching routes for (var len = routes.length; i < len; ++i) { var route = routes[i] , fn = route.callback , path = route.regexp , keys = route.keys; // match if (captures = path.exec(pathname)) { route.params = []; for (var j = 1, l = captures.length; j < l; ++j) { var key = keys[j-1], val = 'string' == typeof captures[j] ? decodeURIComponent(captures[j]) : captures[j]; if (key) { route.params[key] = val; } else { route.params.push(val); } } req._route_index = i; return route; } } } }