]> git.cworth.org Git - obsolete/notmuch-web/blobdiff - node_modules/express/lib/router/index.js
Install the "express" node module via npm
[obsolete/notmuch-web] / node_modules / express / lib / router / index.js
diff --git a/node_modules/express/lib/router/index.js b/node_modules/express/lib/router/index.js
new file mode 100644 (file)
index 0000000..5a80b5c
--- /dev/null
@@ -0,0 +1,329 @@
+
+/*!
+ * 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;
+      }
+    }
+  }
+}