diff options
Diffstat (limited to 'node_modules/express/lib')
| -rw-r--r-- | node_modules/express/lib/express.js | 79 | ||||
| -rw-r--r-- | node_modules/express/lib/http.js | 582 | ||||
| -rw-r--r-- | node_modules/express/lib/https.js | 52 | ||||
| -rw-r--r-- | node_modules/express/lib/request.js | 323 | ||||
| -rw-r--r-- | node_modules/express/lib/response.js | 460 | ||||
| -rw-r--r-- | node_modules/express/lib/router/collection.js | 53 | ||||
| -rw-r--r-- | node_modules/express/lib/router/index.js | 398 | ||||
| -rw-r--r-- | node_modules/express/lib/router/methods.js | 70 | ||||
| -rw-r--r-- | node_modules/express/lib/router/route.js | 88 | ||||
| -rw-r--r-- | node_modules/express/lib/utils.js | 152 | ||||
| -rw-r--r-- | node_modules/express/lib/view.js | 460 | ||||
| -rw-r--r-- | node_modules/express/lib/view/partial.js | 40 | ||||
| -rw-r--r-- | node_modules/express/lib/view/view.js | 210 |
13 files changed, 2967 insertions, 0 deletions
diff --git a/node_modules/express/lib/express.js b/node_modules/express/lib/express.js new file mode 100644 index 0000000..0a0d5ad --- /dev/null +++ b/node_modules/express/lib/express.js @@ -0,0 +1,79 @@ + +/*! + * Express + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var connect = require('connect') + , HTTPSServer = require('./https') + , HTTPServer = require('./http') + , Route = require('./router/route') + +/** + * Re-export connect auto-loaders. + * + * This prevents the need to `require('connect')` in order + * to access core middleware, so for example `express.logger()` instead + * of `require('connect').logger()`. + */ + +var exports = module.exports = connect.middleware; + +/** + * Framework version. + */ + +exports.version = '2.5.8'; + +/** + * Shortcut for `new Server(...)`. + * + * @param {Function} ... + * @return {Server} + * @api public + */ + +exports.createServer = function(options){ + if ('object' == typeof options) { + return new HTTPSServer(options, Array.prototype.slice.call(arguments, 1)); + } else { + return new HTTPServer(Array.prototype.slice.call(arguments)); + } +}; + +/** + * Expose constructors. + */ + +exports.HTTPServer = HTTPServer; +exports.HTTPSServer = HTTPSServer; +exports.Route = Route; + +/** + * View extensions. + */ + +exports.View = +exports.view = require('./view'); + +/** + * Response extensions. + */ + +require('./response'); + +/** + * Request extensions. + */ + +require('./request'); + +// Error handler title + +exports.errorHandler.title = 'Express'; + diff --git a/node_modules/express/lib/http.js b/node_modules/express/lib/http.js new file mode 100644 index 0000000..da2158f --- /dev/null +++ b/node_modules/express/lib/http.js @@ -0,0 +1,582 @@ +/*! + * Express - HTTPServer + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var qs = require('qs') + , connect = require('connect') + , router = require('./router') + , Router = require('./router') + , view = require('./view') + , toArray = require('./utils').toArray + , methods = router.methods.concat('del', 'all') + , url = require('url') + , utils = connect.utils; + +/** + * Expose `HTTPServer`. + */ + +exports = module.exports = HTTPServer; + +/** + * Server proto. + */ + +var app = HTTPServer.prototype; + +/** + * Initialize a new `HTTPServer` with optional `middleware`. + * + * @param {Array} middleware + * @api public + */ + +function HTTPServer(middleware){ + connect.HTTPServer.call(this, []); + this.init(middleware); +}; + +/** + * Inherit from `connect.HTTPServer`. + */ + +app.__proto__ = connect.HTTPServer.prototype; + +/** + * Initialize the server. + * + * @param {Array} middleware + * @api private + */ + +app.init = function(middleware){ + var self = this; + this.cache = {}; + this.settings = {}; + this.redirects = {}; + this.isCallbacks = {}; + this._locals = {}; + this.dynamicViewHelpers = {}; + this.errorHandlers = []; + + this.set('env', process.env.NODE_ENV || 'development'); + + // expose objects to each other + this.use(function(req, res, next){ + req.query = req.query || {}; + res.setHeader('X-Powered-By', 'Express'); + req.app = res.app = self; + req.res = res; + res.req = req; + req.next = next; + // assign req.query + if (req.url.indexOf('?') > 0) { + var query = url.parse(req.url).query; + req.query = qs.parse(query); + } + next(); + }); + + // apply middleware + if (middleware) middleware.forEach(self.use.bind(self)); + + // router + this.routes = new Router(this); + this.__defineGetter__('router', function(){ + this.__usedRouter = true; + return self.routes.middleware; + }); + + // default locals + this.locals({ + settings: this.settings + , app: this + }); + + // default development configuration + this.configure('development', function(){ + this.enable('hints'); + }); + + // default production configuration + this.configure('production', function(){ + this.enable('view cache'); + }); + + // register error handlers on "listening" + // so that they disregard definition position. + this.on('listening', this.registerErrorHandlers.bind(this)); + + // route manipulation methods + methods.forEach(function(method){ + self.lookup[method] = function(path){ + return self.routes.lookup(method, path); + }; + + self.match[method] = function(path){ + return self.routes.match(method, path); + }; + + self.remove[method] = function(path){ + return self.routes.lookup(method, path).remove(); + }; + }); + + // del -> delete + self.lookup.del = self.lookup.delete; + self.match.del = self.match.delete; + self.remove.del = self.remove.delete; +}; + +/** + * Remove routes matching the given `path`. + * + * @param {Route} path + * @return {Boolean} + * @api public + */ + +app.remove = function(path){ + return this.routes.lookup('all', path).remove(); +}; + +/** + * Lookup routes defined with a path + * equivalent to `path`. + * + * @param {Stirng} path + * @return {Array} + * @api public + */ + +app.lookup = function(path){ + return this.routes.lookup('all', path); +}; + +/** + * Lookup routes matching the given `url`. + * + * @param {Stirng} url + * @return {Array} + * @api public + */ + +app.match = function(url){ + return this.routes.match('all', url); +}; + +/** + * When using the vhost() middleware register error handlers. + */ + +app.onvhost = function(){ + this.registerErrorHandlers(); +}; + +/** + * Register error handlers. + * + * @return {Server} for chaining + * @api public + */ + +app.registerErrorHandlers = function(){ + this.errorHandlers.forEach(function(fn){ + this.use(function(err, req, res, next){ + fn.apply(this, arguments); + }); + }, this); + return this; +}; + +/** + * Proxy `connect.HTTPServer#use()` to apply settings to + * mounted applications. + * + * @param {String|Function|Server} route + * @param {Function|Server} middleware + * @return {Server} for chaining + * @api public + */ + +app.use = function(route, middleware){ + var app, base, handle; + + if ('string' != typeof route) { + middleware = route, route = '/'; + } + + // express app + if (middleware.handle && middleware.set) app = middleware; + + // restore .app property on req and res + if (app) { + app.route = route; + middleware = function(req, res, next) { + var orig = req.app; + app.handle(req, res, function(err){ + req.app = res.app = orig; + next(err); + }); + }; + } + + connect.HTTPServer.prototype.use.call(this, route, middleware); + + // mounted an app, invoke the hook + // and adjust some settings + if (app) { + base = this.set('basepath') || this.route; + if ('/' == base) base = ''; + base = base + (app.set('basepath') || app.route); + app.set('basepath', base); + app.parent = this; + if (app.__mounted) app.__mounted.call(app, this); + } + + return this; +}; + +/** + * Assign a callback `fn` which is called + * when this `Server` is passed to `Server#use()`. + * + * Examples: + * + * var app = express.createServer() + * , blog = express.createServer(); + * + * blog.mounted(function(parent){ + * // parent is app + * // "this" is blog + * }); + * + * app.use(blog); + * + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.mounted = function(fn){ + this.__mounted = fn; + return this; +}; + +/** + * See: view.register. + * + * @return {Server} for chaining + * @api public + */ + +app.register = function(){ + view.register.apply(this, arguments); + return this; +}; + +/** + * Register the given view helpers `obj`. This method + * can be called several times to apply additional helpers. + * + * @param {Object} obj + * @return {Server} for chaining + * @api public + */ + +app.helpers = +app.locals = function(obj){ + utils.merge(this._locals, obj); + return this; +}; + +/** + * Register the given dynamic view helpers `obj`. This method + * can be called several times to apply additional helpers. + * + * @param {Object} obj + * @return {Server} for chaining + * @api public + */ + +app.dynamicHelpers = function(obj){ + utils.merge(this.dynamicViewHelpers, obj); + return this; +}; + +/** + * Map the given param placeholder `name`(s) to the given callback(s). + * + * Param mapping is used to provide pre-conditions to routes + * which us normalized placeholders. This callback has the same + * signature as regular middleware, for example below when ":userId" + * is used this function will be invoked in an attempt to load the user. + * + * app.param('userId', function(req, res, next, id){ + * User.find(id, function(err, user){ + * if (err) { + * next(err); + * } else if (user) { + * req.user = user; + * next(); + * } else { + * next(new Error('failed to load user')); + * } + * }); + * }); + * + * Passing a single function allows you to map logic + * to the values passed to `app.param()`, for example + * this is useful to provide coercion support in a concise manner. + * + * The following example maps regular expressions to param values + * ensuring that they match, otherwise passing control to the next + * route: + * + * app.param(function(name, regexp){ + * if (regexp instanceof RegExp) { + * return function(req, res, next, val){ + * var captures; + * if (captures = regexp.exec(String(val))) { + * req.params[name] = captures; + * next(); + * } else { + * next('route'); + * } + * } + * } + * }); + * + * We can now use it as shown below, where "/commit/:commit" expects + * that the value for ":commit" is at 5 or more digits. The capture + * groups are then available as `req.params.commit` as we defined + * in the function above. + * + * app.param('commit', /^\d{5,}$/); + * + * For more of this useful functionality take a look + * at [express-params](http://github.com/visionmedia/express-params). + * + * @param {String|Array|Function} name + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.param = function(name, fn){ + var self = this + , fns = [].slice.call(arguments, 1); + + // array + if (Array.isArray(name)) { + name.forEach(function(name){ + fns.forEach(function(fn){ + self.param(name, fn); + }); + }); + // param logic + } else if ('function' == typeof name) { + this.routes.param(name); + // single + } else { + if (':' == name[0]) name = name.substr(1); + fns.forEach(function(fn){ + self.routes.param(name, fn); + }); + } + + return this; +}; + +/** + * Assign a custom exception handler callback `fn`. + * These handlers are always _last_ in the middleware stack. + * + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.error = function(fn){ + this.errorHandlers.push(fn); + return this; +}; + +/** + * Register the given callback `fn` for the given `type`. + * + * @param {String} type + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.is = function(type, fn){ + if (!fn) return this.isCallbacks[type]; + this.isCallbacks[type] = fn; + return this; +}; + +/** + * Assign `setting` to `val`, or return `setting`'s value. + * Mounted servers inherit their parent server's settings. + * + * @param {String} setting + * @param {String} val + * @return {Server|Mixed} for chaining, or the setting value + * @api public + */ + +app.set = function(setting, val){ + if (val === undefined) { + if (this.settings.hasOwnProperty(setting)) { + return this.settings[setting]; + } else if (this.parent) { + return this.parent.set(setting); + } + } else { + this.settings[setting] = val; + return this; + } +}; + +/** + * Check if `setting` is enabled. + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.enabled = function(setting){ + return !!this.set(setting); +}; + +/** + * Check if `setting` is disabled. + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.disabled = function(setting){ + return !this.set(setting); +}; + +/** + * Enable `setting`. + * + * @param {String} setting + * @return {Server} for chaining + * @api public + */ + +app.enable = function(setting){ + return this.set(setting, true); +}; + +/** + * Disable `setting`. + * + * @param {String} setting + * @return {Server} for chaining + * @api public + */ + +app.disable = function(setting){ + return this.set(setting, false); +}; + +/** + * Redirect `key` to `url`. + * + * @param {String} key + * @param {String} url + * @return {Server} for chaining + * @api public + */ + +app.redirect = function(key, url){ + this.redirects[key] = url; + return this; +}; + +/** + * Configure callback for zero or more envs, + * when no env is specified that callback will + * be invoked for all environments. Any combination + * can be used multiple times, in any order desired. + * + * Examples: + * + * app.configure(function(){ + * // executed for all envs + * }); + * + * app.configure('stage', function(){ + * // executed staging env + * }); + * + * app.configure('stage', 'production', function(){ + * // executed for stage and production + * }); + * + * @param {String} env... + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.configure = function(env, fn){ + var envs = 'all' + , args = toArray(arguments); + fn = args.pop(); + if (args.length) envs = args; + if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this); + return this; +}; + +/** + * Delegate `.VERB(...)` calls to `.route(VERB, ...)`. + */ + +methods.forEach(function(method){ + app[method] = function(path){ + if (1 == arguments.length) return this.routes.lookup(method, path); + var args = [method].concat(toArray(arguments)); + if (!this.__usedRouter) this.use(this.router); + return this.routes._route.apply(this.routes, args); + } +}); + +/** + * Special-cased "all" method, applying the given route `path`, + * middleware, and callback to _every_ HTTP method. + * + * @param {String} path + * @param {Function} ... + * @return {Server} for chaining + * @api public + */ + +app.all = function(path){ + var args = arguments; + if (1 == args.length) return this.routes.lookup('all', path); + methods.forEach(function(method){ + if ('all' == method || 'del' == method) return; + app[method].apply(this, args); + }, this); + return this; +}; + +// del -> delete alias + +app.del = app.delete; + diff --git a/node_modules/express/lib/https.js b/node_modules/express/lib/https.js new file mode 100644 index 0000000..8a8c008 --- /dev/null +++ b/node_modules/express/lib/https.js @@ -0,0 +1,52 @@ + +/*! + * Express - HTTPSServer + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var connect = require('connect') + , HTTPServer = require('./http') + , https = require('https'); + +/** + * Expose `HTTPSServer`. + */ + +exports = module.exports = HTTPSServer; + +/** + * Server proto. + */ + +var app = HTTPSServer.prototype; + +/** + * Initialize a new `HTTPSServer` with the + * given `options`, and optional `middleware`. + * + * @param {Object} options + * @param {Array} middleware + * @api public + */ + +function HTTPSServer(options, middleware){ + connect.HTTPSServer.call(this, options, []); + this.init(middleware); +}; + +/** + * Inherit from `connect.HTTPSServer`. + */ + +app.__proto__ = connect.HTTPSServer.prototype; + +// mixin HTTPServer methods + +Object.keys(HTTPServer.prototype).forEach(function(method){ + app[method] = HTTPServer.prototype[method]; +}); diff --git a/node_modules/express/lib/request.js b/node_modules/express/lib/request.js new file mode 100644 index 0000000..1d5ab40 --- /dev/null +++ b/node_modules/express/lib/request.js @@ -0,0 +1,323 @@ + +/*! + * Express - request + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , req = http.IncomingMessage.prototype + , utils = require('./utils') + , parse = require('url').parse + , mime = require('mime'); + +/** + * Default flash formatters. + * + * @type Object + */ + +var flashFormatters = exports.flashFormatters = { + s: function(val){ + return String(val); + } +}; + +/** + * Return request header or optional default. + * + * The `Referrer` header field is special-cased, + * both `Referrer` and `Referer` will yield are + * interchangeable. + * + * Examples: + * + * req.header('Content-Type'); + * // => "text/plain" + * + * req.header('content-type'); + * // => "text/plain" + * + * req.header('Accept'); + * // => undefined + * + * req.header('Accept', 'text/html'); + * // => "text/html" + * + * @param {String} name + * @param {String} defaultValue + * @return {String} + * @api public + */ + +req.header = function(name, defaultValue){ + switch (name = name.toLowerCase()) { + case 'referer': + case 'referrer': + return this.headers.referrer + || this.headers.referer + || defaultValue; + default: + return this.headers[name] || defaultValue; + } +}; + +/** + * Get `field`'s `param` value, defaulting to ''. + * + * Examples: + * + * req.get('content-disposition', 'filename'); + * // => "something.png" + * + * @param {String} field + * @param {String} param + * @return {String} + * @api public + */ + +req.get = function(field, param){ + var val = this.header(field); + if (!val) return ''; + var regexp = new RegExp(param + ' *= *(?:"([^"]+)"|([^;]+))', 'i'); + if (!regexp.exec(val)) return ''; + return RegExp.$1 || RegExp.$2; +}; + +/** + * Short-hand for `require('url').parse(req.url).pathname`. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('path', function(){ + return parse(this.url).pathname; +}); + +/** + * Check if the _Accept_ header is present, and includes the given `type`. + * + * When the _Accept_ header is not present `true` is returned. Otherwise + * the given `type` is matched by an exact match, and then subtypes. You + * may pass the subtype such as "html" which is then converted internally + * to "text/html" using the mime lookup table. + * + * Examples: + * + * // Accept: text/html + * req.accepts('html'); + * // => true + * + * // Accept: text/*; application/json + * req.accepts('html'); + * req.accepts('text/html'); + * req.accepts('text/plain'); + * req.accepts('application/json'); + * // => true + * + * req.accepts('image/png'); + * req.accepts('png'); + * // => false + * + * @param {String} type + * @return {Boolean} + * @api public + */ + +req.accepts = function(type){ + var accept = this.header('Accept'); + + // normalize extensions ".json" -> "json" + if (type && '.' == type[0]) type = type.substr(1); + + // when Accept does not exist, or is '*/*' return true + if (!accept || '*/*' == accept) { + return true; + } else if (type) { + // allow "html" vs "text/html" etc + if (!~type.indexOf('/')) type = mime.lookup(type); + + // check if we have a direct match + if (~accept.indexOf(type)) return true; + + // check if we have type/* + type = type.split('/')[0] + '/*'; + return !!~accept.indexOf(type); + } else { + return false; + } +}; + +/** + * Return the value of param `name` when present or `defaultValue`. + * + * - Checks route placeholders, ex: _/user/:id_ + * - Checks query string params, ex: ?id=12 + * - Checks urlencoded body params, ex: id=12 + * + * To utilize urlencoded request bodies, `req.body` + * should be an object. This can be done by using + * the `connect.bodyParser` middleware. + * + * @param {String} name + * @param {Mixed} defaultValue + * @return {String} + * @api public + */ + +req.param = function(name, defaultValue){ + // route params like /user/:id + if (this.params && this.params.hasOwnProperty(name) && undefined !== this.params[name]) { + return this.params[name]; + } + // query string params + if (undefined !== this.query[name]) { + return this.query[name]; + } + // request body params via connect.bodyParser + if (this.body && undefined !== this.body[name]) { + return this.body[name]; + } + return defaultValue; +}; + +/** + * Queue flash `msg` of the given `type`. + * + * Examples: + * + * req.flash('info', 'email sent'); + * req.flash('error', 'email delivery failed'); + * req.flash('info', 'email re-sent'); + * // => 2 + * + * req.flash('info'); + * // => ['email sent', 'email re-sent'] + * + * req.flash('info'); + * // => [] + * + * req.flash(); + * // => { error: ['email delivery failed'], info: [] } + * + * Formatting: + * + * Flash notifications also support arbitrary formatting support. + * For example you may pass variable arguments to `req.flash()` + * and use the %s specifier to be replaced by the associated argument: + * + * req.flash('info', 'email has been sent to %s.', userName); + * + * To add custom formatters use the `exports.flashFormatters` object. + * + * @param {String} type + * @param {String} msg + * @return {Array|Object|Number} + * @api public + */ + +req.flash = function(type, msg){ + if (this.session === undefined) throw Error('req.flash() requires sessions'); + var msgs = this.session.flash = this.session.flash || {}; + if (type && msg) { + var i = 2 + , args = arguments + , formatters = this.app.flashFormatters || {}; + formatters.__proto__ = flashFormatters; + msg = utils.miniMarkdown(msg); + msg = msg.replace(/%([a-zA-Z])/g, function(_, format){ + var formatter = formatters[format]; + if (formatter) return formatter(utils.escape(args[i++])); + }); + return (msgs[type] = msgs[type] || []).push(msg); + } else if (type) { + var arr = msgs[type]; + delete msgs[type]; + return arr || []; + } else { + this.session.flash = {}; + return msgs; + } +}; + +/** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains the give mime `type`. + * + * Examples: + * + * // With Content-Type: text/html; charset=utf-8 + * req.is('html'); + * req.is('text/html'); + * // => true + * + * // When Content-Type is application/json + * req.is('json'); + * req.is('application/json'); + * // => true + * + * req.is('html'); + * // => false + * + * Ad-hoc callbacks can also be registered with Express, to perform + * assertions again the request, for example if we need an expressive + * way to check if our incoming request is an image, we can register "an image" + * callback: + * + * app.is('an image', function(req){ + * return 0 == req.headers['content-type'].indexOf('image'); + * }); + * + * Now within our route callbacks, we can use to to assert content types + * such as "image/jpeg", "image/png", etc. + * + * app.post('/image/upload', function(req, res, next){ + * if (req.is('an image')) { + * // do something + * } else { + * next(); + * } + * }); + * + * @param {String} type + * @return {Boolean} + * @api public + */ + +req.is = function(type){ + var fn = this.app.is(type); + if (fn) return fn(this); + var ct = this.headers['content-type']; + if (!ct) return false; + ct = ct.split(';')[0]; + if (!~type.indexOf('/')) type = mime.lookup(type); + if (~type.indexOf('*')) { + type = type.split('/'); + ct = ct.split('/'); + if ('*' == type[0] && type[1] == ct[1]) return true; + if ('*' == type[1] && type[0] == ct[0]) return true; + return false; + } + return !! ~ct.indexOf(type); +}; + +// Callback for isXMLHttpRequest / xhr + +function isxhr() { + return this.header('X-Requested-With', '').toLowerCase() === 'xmlhttprequest'; +} + +/** + * Check if the request was an _XMLHttpRequest_. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('isXMLHttpRequest', isxhr); +req.__defineGetter__('xhr', isxhr); diff --git a/node_modules/express/lib/response.js b/node_modules/express/lib/response.js new file mode 100644 index 0000000..a671771 --- /dev/null +++ b/node_modules/express/lib/response.js @@ -0,0 +1,460 @@ + +/*! + * Express - response + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , http = require('http') + , path = require('path') + , connect = require('connect') + , utils = connect.utils + , parseRange = require('./utils').parseRange + , res = http.ServerResponse.prototype + , send = connect.static.send + , mime = require('mime') + , basename = path.basename + , join = path.join; + +/** + * Send a response with the given `body` and optional `headers` and `status` code. + * + * Examples: + * + * res.send(); + * res.send(new Buffer('wahoo')); + * res.send({ some: 'json' }); + * res.send('<p>some html</p>'); + * res.send('Sorry, cant find that', 404); + * res.send('text', { 'Content-Type': 'text/plain' }, 201); + * res.send(404); + * + * @param {String|Object|Number|Buffer} body or status + * @param {Object|Number} headers or status + * @param {Number} status + * @return {ServerResponse} + * @api public + */ + +res.send = function(body, headers, status){ + // allow status as second arg + if ('number' == typeof headers) { + status = headers, + headers = null; + } + + // default status + status = status || this.statusCode; + + // allow 0 args as 204 + if (!arguments.length || undefined === body) status = 204; + + // determine content type + switch (typeof body) { + case 'number': + if (!this.header('Content-Type')) { + this.contentType('.txt'); + } + body = http.STATUS_CODES[status = body]; + break; + case 'string': + if (!this.header('Content-Type')) { + this.charset = this.charset || 'utf-8'; + this.contentType('.html'); + } + break; + case 'boolean': + case 'object': + if (Buffer.isBuffer(body)) { + if (!this.header('Content-Type')) { + this.contentType('.bin'); + } + } else { + return this.json(body, headers, status); + } + break; + } + + // populate Content-Length + if (undefined !== body && !this.header('Content-Length')) { + this.header('Content-Length', Buffer.isBuffer(body) + ? body.length + : Buffer.byteLength(body)); + } + + // merge headers passed + if (headers) { + var fields = Object.keys(headers); + for (var i = 0, len = fields.length; i < len; ++i) { + var field = fields[i]; + this.header(field, headers[field]); + } + } + + // strip irrelevant headers + if (204 == status || 304 == status) { + this.removeHeader('Content-Type'); + this.removeHeader('Content-Length'); + body = ''; + } + + // respond + this.statusCode = status; + this.end('HEAD' == this.req.method ? null : body); + return this; +}; + +/** + * Send JSON response with `obj`, optional `headers`, and optional `status`. + * + * Examples: + * + * res.json(null); + * res.json({ user: 'tj' }); + * res.json('oh noes!', 500); + * res.json('I dont have that', 404); + * + * @param {Mixed} obj + * @param {Object|Number} headers or status + * @param {Number} status + * @return {ServerResponse} + * @api public + */ + +res.json = function(obj, headers, status){ + var body = JSON.stringify(obj) + , callback = this.req.query.callback + , jsonp = this.app.enabled('jsonp callback'); + + this.charset = this.charset || 'utf-8'; + this.header('Content-Type', 'application/json'); + + if (callback && jsonp) { + this.header('Content-Type', 'text/javascript'); + body = callback.replace(/[^\w$.]/g, '') + '(' + body + ');'; + } + + return this.send(body, headers, status); +}; + +/** + * Set status `code`. + * + * @param {Number} code + * @return {ServerResponse} + * @api public + */ + +res.status = function(code){ + this.statusCode = code; + return this; +}; + +/** + * Transfer the file at the given `path`. Automatically sets + * the _Content-Type_ response header field. `next()` is called + * when `path` is a directory, or when an error occurs. + * + * Options: + * + * - `maxAge` defaulting to 0 + * - `root` root directory for relative filenames + * + * @param {String} path + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +res.sendfile = function(path, options, fn){ + var next = this.req.next; + options = options || {}; + + // support function as second arg + if ('function' == typeof options) { + fn = options; + options = {}; + } + + options.path = encodeURIComponent(path); + options.callback = fn; + send(this.req, this, next, options); +}; + +/** + * Set _Content-Type_ response header passed through `mime.lookup()`. + * + * Examples: + * + * var filename = 'path/to/image.png'; + * res.contentType(filename); + * // res.headers['Content-Type'] is now "image/png" + * + * res.contentType('.html'); + * res.contentType('html'); + * res.contentType('json'); + * res.contentType('png'); + * + * @param {String} type + * @return {String} the resolved mime type + * @api public + */ + +res.contentType = function(type){ + return this.header('Content-Type', mime.lookup(type)); +}; + +/** + * Set _Content-Disposition_ header to _attachment_ with optional `filename`. + * + * @param {String} filename + * @return {ServerResponse} + * @api public + */ + +res.attachment = function(filename){ + if (filename) this.contentType(filename); + this.header('Content-Disposition', filename + ? 'attachment; filename="' + basename(filename) + '"' + : 'attachment'); + return this; +}; + +/** + * Transfer the file at the given `path`, with optional + * `filename` as an attachment and optional callback `fn(err)`, + * and optional `fn2(err)` which is invoked when an error has + * occurred after header has been sent. + * + * @param {String} path + * @param {String|Function} filename or fn + * @param {Function} fn + * @param {Function} fn2 + * @api public + */ + +res.download = function(path, filename, fn, fn2){ + var self = this; + + // support callback as second arg + if ('function' == typeof filename) { + fn2 = fn; + fn = filename; + filename = null; + } + + // transfer the file + this.attachment(filename || path).sendfile(path, function(err){ + var sentHeader = self._header; + if (err) { + if (!sentHeader) self.removeHeader('Content-Disposition'); + if (sentHeader) { + fn2 && fn2(err); + } else if (fn) { + fn(err); + } else { + self.req.next(err); + } + } else if (fn) { + fn(); + } + }); +}; + +/** + * Set or get response header `name` with optional `val`. + * + * @param {String} name + * @param {String} val + * @return {ServerResponse} for chaining + * @api public + */ + +res.header = function(name, val){ + if (1 == arguments.length) return this.getHeader(name); + this.setHeader(name, val); + return this; +}; + +/** + * Clear cookie `name`. + * + * @param {String} name + * @param {Object} options + * @api public + */ + +res.clearCookie = function(name, options){ + var opts = { expires: new Date(1) }; + this.cookie(name, '', options + ? utils.merge(options, opts) + : opts); +}; + +/** + * Set cookie `name` to `val`, with the given `options`. + * + * Options: + * + * - `maxAge` max-age in milliseconds, converted to `expires` + * - `path` defaults to the "basepath" setting which is typically "/" + * + * Examples: + * + * // "Remember Me" for 15 minutes + * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); + * + * // save as above + * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) + * + * @param {String} name + * @param {String} val + * @param {Options} options + * @api public + */ + +res.cookie = function(name, val, options){ + options = options || {}; + if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge); + if (undefined === options.path) options.path = this.app.set('basepath'); + var cookie = utils.serializeCookie(name, val, options); + this.header('Set-Cookie', cookie); +}; + +/** + * Redirect to the given `url` with optional response `status` + * defauling to 302. + * + * The given `url` can also be the name of a mapped url, for + * example by default express supports "back" which redirects + * to the _Referrer_ or _Referer_ headers or the application's + * "basepath" setting. Express also supports "basepath" out of the box, + * which can be set via `app.set('basepath', '/blog');`, and defaults + * to '/'. + * + * Redirect Mapping: + * + * To extend the redirect mapping capabilities that Express provides, + * we may use the `app.redirect()` method: + * + * app.redirect('google', 'http://google.com'); + * + * Now in a route we may call: + * + * res.redirect('google'); + * + * We may also map dynamic redirects: + * + * app.redirect('comments', function(req, res){ + * return '/post/' + req.params.id + '/comments'; + * }); + * + * So now we may do the following, and the redirect will dynamically adjust to + * the context of the request. If we called this route with _GET /post/12_ our + * redirect _Location_ would be _/post/12/comments_. + * + * app.get('/post/:id', function(req, res){ + * res.redirect('comments'); + * }); + * + * Unless an absolute `url` is given, the app's mount-point + * will be respected. For example if we redirect to `/posts`, + * and our app is mounted at `/blog` we will redirect to `/blog/posts`. + * + * @param {String} url + * @param {Number} code + * @api public + */ + +res.redirect = function(url, status){ + var app = this.app + , req = this.req + , base = app.set('basepath') || app.route + , status = status || 302 + , head = 'HEAD' == req.method + , body; + + // Setup redirect map + var map = { + back: req.header('Referrer', base) + , home: base + }; + + // Support custom redirect map + map.__proto__ = app.redirects; + + // Attempt mapped redirect + var mapped = 'function' == typeof map[url] + ? map[url](req, this) + : map[url]; + + // Perform redirect + url = mapped || url; + + // Relative + if (!~url.indexOf('://')) { + // Respect mount-point + if ('/' != base && 0 != url.indexOf(base)) url = base + url; + + // Absolute + var host = req.headers.host + , tls = req.connection.encrypted; + url = 'http' + (tls ? 's' : '') + '://' + host + url; + } + + // Support text/{plain,html} by default + if (req.accepts('html')) { + body = '<p>' + http.STATUS_CODES[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>'; + this.header('Content-Type', 'text/html'); + } else { + body = http.STATUS_CODES[status] + '. Redirecting to ' + url; + this.header('Content-Type', 'text/plain'); + } + + // Respond + this.statusCode = status; + this.header('Location', url); + this.end(head ? null : body); +}; + +/** + * Assign the view local variable `name` to `val` or return the + * local previously assigned to `name`. + * + * @param {String} name + * @param {Mixed} val + * @return {Mixed} val + * @api public + */ + +res.local = function(name, val){ + this._locals = this._locals || {}; + return undefined === val + ? this._locals[name] + : this._locals[name] = val; +}; + +/** + * Assign several locals with the given `obj`, + * or return the locals. + * + * @param {Object} obj + * @return {Object|Undefined} + * @api public + */ + +res.locals = +res.helpers = function(obj){ + if (obj) { + for (var key in obj) { + this.local(key, obj[key]); + } + } else { + return this._locals; + } +}; diff --git a/node_modules/express/lib/router/collection.js b/node_modules/express/lib/router/collection.js new file mode 100644 index 0000000..991a9a2 --- /dev/null +++ b/node_modules/express/lib/router/collection.js @@ -0,0 +1,53 @@ + +/*! + * Express - router - Collection + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Expose `Collection`. + */ + +module.exports = Collection; + +/** + * Initialize a new route `Collection` + * with the given `router`. + * + * @param {Router} router + * @api private + */ + +function Collection(router) { + Array.apply(this, arguments); + this.router = router; +} + +/** + * Inherit from `Array.prototype`. + */ + +Collection.prototype.__proto__ = Array.prototype; + +/** + * Remove the routes in this collection. + * + * @return {Collection} of routes removed + * @api public + */ + +Collection.prototype.remove = function(){ + var router = this.router + , len = this.length + , ret = new Collection(this.router); + + for (var i = 0; i < len; ++i) { + if (router.remove(this[i])) { + ret.push(this[i]); + } + } + + return ret; +}; + diff --git a/node_modules/express/lib/router/index.js b/node_modules/express/lib/router/index.js new file mode 100644 index 0000000..ff1498b --- /dev/null +++ b/node_modules/express/lib/router/index.js @@ -0,0 +1,398 @@ + +/*! + * Express - Router + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Route = require('./route') + , Collection = require('./collection') + , utils = require('../utils') + , parse = require('url').parse + , toArray = utils.toArray; + +/** + * Expose `Router` constructor. + */ + +exports = module.exports = Router; + +/** + * Expose HTTP methods. + */ + +var methods = exports.methods = require('./methods'); + +/** + * Initialize a new `Router` with the given `app`. + * + * @param {express.HTTPServer} app + * @api private + */ + +function Router(app) { + var self = this; + this.app = app; + this.routes = {}; + this.params = {}; + this._params = []; + + this.middleware = function(req, res, next){ + self._dispatch(req, res, next); + }; +} + +/** + * Register a param callback `fn` for the given `name`. + * + * @param {String|Function} name + * @param {Function} fn + * @return {Router} for chaining + * @api public + */ + +Router.prototype.param = function(name, fn){ + // param logic + if ('function' == typeof name) { + this._params.push(name); + return; + } + + // apply param functions + var params = this._params + , len = params.length + , ret; + + for (var i = 0; i < len; ++i) { + if (ret = params[i](name, fn)) { + fn = ret; + } + } + + // ensure we end up with a + // middleware function + if ('function' != typeof fn) { + throw new Error('invalid param() call for ' + name + ', got ' + fn); + } + + (this.params[name] = this.params[name] || []).push(fn); + return this; +}; + +/** + * Return a `Collection` of all routes defined. + * + * @return {Collection} + * @api public + */ + +Router.prototype.all = function(){ + return this.find(function(){ + return true; + }); +}; + +/** + * Remove the given `route`, returns + * a bool indicating if the route was present + * or not. + * + * @param {Route} route + * @return {Boolean} + * @api public + */ + +Router.prototype.remove = function(route){ + var routes = this.routes[route.method] + , len = routes.length; + + for (var i = 0; i < len; ++i) { + if (route == routes[i]) { + routes.splice(i, 1); + return true; + } + } +}; + +/** + * Return routes with route paths matching `path`. + * + * @param {String} method + * @param {String} path + * @return {Collection} + * @api public + */ + +Router.prototype.lookup = function(method, path){ + return this.find(function(route){ + return path == route.path + && (route.method == method + || method == 'all'); + }); +}; + +/** + * Return routes with regexps that match the given `url`. + * + * @param {String} method + * @param {String} url + * @return {Collection} + * @api public + */ + +Router.prototype.match = function(method, url){ + return this.find(function(route){ + return route.match(url) + && (route.method == method + || method == 'all'); + }); +}; + +/** + * Find routes based on the return value of `fn` + * which is invoked once per route. + * + * @param {Function} fn + * @return {Collection} + * @api public + */ + +Router.prototype.find = function(fn){ + var len = methods.length + , ret = new Collection(this) + , method + , routes + , route; + + for (var i = 0; i < len; ++i) { + method = methods[i]; + routes = this.routes[method]; + if (!routes) continue; + for (var j = 0, jlen = routes.length; j < jlen; ++j) { + route = routes[j]; + if (fn(route)) ret.push(route); + } + } + + return ret; +}; + +/** + * Route dispatcher aka the route "middleware". + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @param {Function} next + * @api private + */ + +Router.prototype._dispatch = function(req, res, next){ + var params = this.params + , self = this; + + // route dispatch + (function pass(i, err){ + var paramCallbacks + , paramIndex = 0 + , paramVal + , route + , keys + , key + , ret; + + // match next route + function nextRoute(err) { + pass(req._route_index + 1, err); + } + + // match route + req.route = route = self._match(req, i); + + // implied OPTIONS + if (!route && 'OPTIONS' == req.method) return self._options(req, res); + + // no route + if (!route) return next(err); + + // we have a route + // start at param 0 + req.params = route.params; + keys = route.keys; + i = 0; + + // param callbacks + function param(err) { + paramIndex = 0; + key = keys[i++]; + paramVal = key && req.params[key.name]; + paramCallbacks = key && params[key.name]; + + try { + if ('route' == err) { + nextRoute(); + } else if (err) { + i = 0; + callbacks(err); + } else if (paramCallbacks && undefined !== paramVal) { + paramCallback(); + } else if (key) { + param(); + } else { + i = 0; + callbacks(); + } + } catch (err) { + param(err); + } + }; + + param(err); + + // single param callbacks + function paramCallback(err) { + var fn = paramCallbacks[paramIndex++]; + if (err || !fn) return param(err); + fn(req, res, paramCallback, paramVal, key.name); + } + + // invoke route callbacks + function callbacks(err) { + var fn = route.callbacks[i++]; + try { + if ('route' == err) { + nextRoute(); + } else if (err && fn) { + if (fn.length < 4) return callbacks(err); + fn(err, req, res, callbacks); + } else if (fn) { + fn(req, res, callbacks); + } else { + nextRoute(err); + } + } catch (err) { + callbacks(err); + } + } + })(0); +}; + +/** + * Respond to __OPTIONS__ method. + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @api private + */ + +Router.prototype._options = function(req, res){ + var path = parse(req.url).pathname + , body = this._optionsFor(path).join(','); + res.send(body, { Allow: body }); +}; + +/** + * Return an array of HTTP verbs or "options" for `path`. + * + * @param {String} path + * @return {Array} + * @api private + */ + +Router.prototype._optionsFor = function(path){ + var self = this; + return methods.filter(function(method){ + var routes = self.routes[method]; + if (!routes || 'options' == method) return; + for (var i = 0, len = routes.length; i < len; ++i) { + if (routes[i].match(path)) return true; + } + }).map(function(method){ + return method.toUpperCase(); + }); +}; + +/** + * Attempt to match a route for `req` + * starting from offset `i`. + * + * @param {IncomingMessage} req + * @param {Number} i + * @return {Route} + * @api private + */ + +Router.prototype._match = function(req, i){ + var method = req.method.toLowerCase() + , url = parse(req.url) + , path = url.pathname + , routes = this.routes + , captures + , route + , keys; + + // pass HEAD to GET routes + if ('head' == method) method = 'get'; + + // routes for this method + if (routes = routes[method]) { + + // matching routes + for (var len = routes.length; i < len; ++i) { + route = routes[i]; + if (captures = route.match(path)) { + keys = route.keys; + route.params = []; + + // params from capture groups + for (var j = 1, jlen = captures.length; j < jlen; ++j) { + var key = keys[j-1] + , val = 'string' == typeof captures[j] + ? decodeURIComponent(captures[j]) + : captures[j]; + if (key) { + route.params[key.name] = val; + } else { + route.params.push(val); + } + } + + // all done + req._route_index = i; + return route; + } + } + } +}; + +/** + * Route `method`, `path`, and one or more callbacks. + * + * @param {String} method + * @param {String} path + * @param {Function} callback... + * @return {Router} for chaining + * @api private + */ + +Router.prototype._route = function(method, path, callbacks){ + var app = this.app + , callbacks = utils.flatten(toArray(arguments, 2)); + + // ensure path was given + if (!path) throw new Error('app.' + method + '() requires a path'); + + // create the route + var route = new Route(method, path, callbacks, { + sensitive: app.enabled('case sensitive routes') + , strict: app.enabled('strict routing') + }); + + // add it + (this.routes[method] = this.routes[method] || []) + .push(route); + return this; +}; diff --git a/node_modules/express/lib/router/methods.js b/node_modules/express/lib/router/methods.js new file mode 100644 index 0000000..e19787b --- /dev/null +++ b/node_modules/express/lib/router/methods.js @@ -0,0 +1,70 @@ + +/*! + * Express - router - methods + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Hypertext Transfer Protocol -- HTTP/1.1 + * http://www.ietf.org/rfc/rfc2616.txt + */ + +var RFC2616 = ['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT']; + +/** + * HTTP Extensions for Distributed Authoring -- WEBDAV + * http://www.ietf.org/rfc/rfc2518.txt + */ + +var RFC2518 = ['PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK']; + +/** + * Versioning Extensions to WebDAV + * http://www.ietf.org/rfc/rfc3253.txt + */ + +var RFC3253 = ['VERSION-CONTROL', 'REPORT', 'CHECKOUT', 'CHECKIN', 'UNCHECKOUT', 'MKWORKSPACE', 'UPDATE', 'LABEL', 'MERGE', 'BASELINE-CONTROL', 'MKACTIVITY']; + +/** + * Ordered Collections Protocol (WebDAV) + * http://www.ietf.org/rfc/rfc3648.txt + */ + +var RFC3648 = ['ORDERPATCH']; + +/** + * Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol + * http://www.ietf.org/rfc/rfc3744.txt + */ + +var RFC3744 = ['ACL']; + +/** + * Web Distributed Authoring and Versioning (WebDAV) SEARCH + * http://www.ietf.org/rfc/rfc5323.txt + */ + +var RFC5323 = ['SEARCH']; + +/** + * PATCH Method for HTTP + * http://www.ietf.org/rfc/rfc5789.txt + */ + +var RFC5789 = ['PATCH']; + +/** + * Expose the methods. + */ + +module.exports = [].concat( + RFC2616 + , RFC2518 + , RFC3253 + , RFC3648 + , RFC3744 + , RFC5323 + , RFC5789).map(function(method){ + return method.toLowerCase(); + }); diff --git a/node_modules/express/lib/router/route.js b/node_modules/express/lib/router/route.js new file mode 100644 index 0000000..7f2965c --- /dev/null +++ b/node_modules/express/lib/router/route.js @@ -0,0 +1,88 @@ + +/*! + * Express - router - Route + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Expose `Route`. + */ + +module.exports = Route; + +/** + * Initialize `Route` with the given HTTP `method`, `path`, + * and an array of `callbacks` and `options`. + * + * Options: + * + * - `sensitive` enable case-sensitive routes + * - `strict` enable strict matching for trailing slashes + * + * @param {String} method + * @param {String} path + * @param {Array} callbacks + * @param {Object} options. + * @api private + */ + +function Route(method, path, callbacks, options) { + options = options || {}; + this.path = path; + this.method = method; + this.callbacks = callbacks; + this.regexp = normalize(path + , this.keys = [] + , options.sensitive + , options.strict); +} + +/** + * Check if this route matches `path` and return captures made. + * + * @param {String} path + * @return {Array} + * @api private + */ + +Route.prototype.match = function(path){ + return this.regexp.exec(path); +}; + +/** + * Normalize the given path string, + * returning a regular expression. + * + * An empty array should be passed, + * which will contain the placeholder + * key names. For example "/user/:id" will + * then contain ["id"]. + * + * @param {String|RegExp} path + * @param {Array} keys + * @param {Boolean} sensitive + * @param {Boolean} strict + * @return {RegExp} + * @api private + */ + +function normalize(path, keys, sensitive, strict) { + if (path instanceof RegExp) return path; + path = path + .concat(strict ? '' : '/?') + .replace(/\/\(/g, '(?:/') + .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){ + keys.push({ name: key, optional: !! optional }); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' + + (optional || ''); + }) + .replace(/([\/.])/g, '\\$1') + .replace(/\*/g, '(.*)'); + return new RegExp('^' + path + '$', sensitive ? '' : 'i'); +} diff --git a/node_modules/express/lib/utils.js b/node_modules/express/lib/utils.js new file mode 100644 index 0000000..d579f7c --- /dev/null +++ b/node_modules/express/lib/utils.js @@ -0,0 +1,152 @@ + +/*! + * Express - Utils + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Check if `path` looks absolute. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +exports.isAbsolute = function(path){ + if ('/' == path[0]) return true; + if (':' == path[1] && '\\' == path[2]) return true; +}; + +/** + * Merge object `b` with `a` giving precedence to + * values in object `a`. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.union = function(a, b){ + if (a && b) { + var keys = Object.keys(b) + , len = keys.length + , key; + for (var i = 0; i < len; ++i) { + key = keys[i]; + if (!a.hasOwnProperty(key)) { + a[key] = b[key]; + } + } + } + return a; +}; + +/** + * Flatten the given `arr`. + * + * @param {Array} arr + * @return {Array} + * @api private + */ + +exports.flatten = function(arr, ret){ + var ret = ret || [] + , len = arr.length; + for (var i = 0; i < len; ++i) { + if (Array.isArray(arr[i])) { + exports.flatten(arr[i], ret); + } else { + ret.push(arr[i]); + } + } + return ret; +}; + +/** + * Parse mini markdown implementation. + * The following conversions are supported, + * primarily for the "flash" middleware: + * + * _foo_ or *foo* become <em>foo</em> + * __foo__ or **foo** become <strong>foo</strong> + * [A](B) becomes <a href="B">A</a> + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.miniMarkdown = function(str){ + return String(str) + .replace(/(__|\*\*)(.*?)\1/g, '<strong>$2</strong>') + .replace(/(_|\*)(.*?)\1/g, '<em>$2</em>') + .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>'); +}; + +/** + * Escape special characters in the given string of html. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html) { + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/</g, '<') + .replace(/>/g, '>'); +}; + +/** + * Parse "Range" header `str` relative to the given file `size`. + * + * @param {Number} size + * @param {String} str + * @return {Array} + * @api private + */ + +exports.parseRange = function(size, str){ + var valid = true; + var arr = str.substr(6).split(',').map(function(range){ + var range = range.split('-') + , start = parseInt(range[0], 10) + , end = parseInt(range[1], 10); + + // -500 + if (isNaN(start)) { + start = size - end; + end = size - 1; + // 500- + } else if (isNaN(end)) { + end = size - 1; + } + + // Invalid + if (isNaN(start) || isNaN(end) || start > end) valid = false; + + return { start: start, end: end }; + }); + return valid ? arr : undefined; +}; + +/** + * Fast alternative to `Array.prototype.slice.call()`. + * + * @param {Arguments} args + * @param {Number} n + * @return {Array} + * @api public + */ + +exports.toArray = function(args, i){ + var arr = [] + , len = args.length + , i = i || 0; + for (; i < len; ++i) arr.push(args[i]); + return arr; +}; diff --git a/node_modules/express/lib/view.js b/node_modules/express/lib/view.js new file mode 100644 index 0000000..5258249 --- /dev/null +++ b/node_modules/express/lib/view.js @@ -0,0 +1,460 @@ + +/*! + * Express - view + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var path = require('path') + , extname = path.extname + , dirname = path.dirname + , basename = path.basename + , utils = require('connect').utils + , View = require('./view/view') + , partial = require('./view/partial') + , union = require('./utils').union + , merge = utils.merge + , http = require('http') + , res = http.ServerResponse.prototype; + +/** + * Expose constructors. + */ + +exports = module.exports = View; + +/** + * Export template engine registrar. + */ + +exports.register = View.register; + +/** + * Lookup and compile `view` with cache support by supplying + * both the `cache` object and `cid` string, + * followed by `options` passed to `exports.lookup()`. + * + * @param {String} view + * @param {Object} cache + * @param {Object} cid + * @param {Object} options + * @return {View} + * @api private + */ + +exports.compile = function(view, cache, cid, options){ + if (cache && cid && cache[cid]){ + options.filename = cache[cid].path; + return cache[cid]; + } + + // lookup + view = exports.lookup(view, options); + + // hints + if (!view.exists) { + if (options.hint) hintAtViewPaths(view.original, options); + var err = new Error('failed to locate view "' + view.original.view + '"'); + err.view = view.original; + throw err; + } + + // compile + options.filename = view.path; + view.fn = view.templateEngine.compile(view.contents, options); + cache[cid] = view; + + return view; +}; + +/** + * Lookup `view`, returning an instanceof `View`. + * + * Options: + * + * - `root` root directory path + * - `defaultEngine` default template engine + * - `parentView` parent `View` object + * - `cache` cache object + * - `cacheid` optional cache id + * + * Lookup: + * + * - partial `_<name>` + * - any `<name>/index` + * - non-layout `../<name>/index` + * - any `<root>/<name>` + * - partial `<root>/_<name>` + * + * @param {String} view + * @param {Object} options + * @return {View} + * @api private + */ + +exports.lookup = function(view, options){ + var orig = view = new View(view, options) + , partial = options.isPartial + , layout = options.isLayout; + + // Try _ prefix ex: ./views/_<name>.jade + // taking precedence over the direct path + if (partial) { + view = new View(orig.prefixPath, options); + if (!view.exists) view = orig; + } + + // Try index ex: ./views/user/index.jade + if (!layout && !view.exists) view = new View(orig.indexPath, options); + + // Try ../<name>/index ex: ../user/index.jade + // when calling partial('user') within the same dir + if (!layout && !view.exists) view = new View(orig.upIndexPath, options); + + // Try root ex: <root>/user.jade + if (!view.exists) view = new View(orig.rootPath, options); + + // Try root _ prefix ex: <root>/_user.jade + if (!view.exists && partial) view = new View(view.prefixPath, options); + + view.original = orig; + return view; +}; + +/** + * Partial render helper. + * + * @api private + */ + +function renderPartial(res, view, options, parentLocals, parent){ + var collection, object, locals; + + if (options) { + // collection + if (options.collection) { + collection = options.collection; + delete options.collection; + } else if ('length' in options) { + collection = options; + options = {}; + } + + // locals + if (options.locals) { + locals = options.locals; + delete options.locals; + } + + // object + if ('Object' != options.constructor.name) { + object = options; + options = {}; + } else if (undefined != options.object) { + object = options.object; + delete options.object; + } + } else { + options = {}; + } + + // Inherit locals from parent + union(options, parentLocals); + + // Merge locals + if (locals) merge(options, locals); + + // Partials dont need layouts + options.isPartial = true; + options.layout = false; + + // Deduce name from view path + var name = options.as || partial.resolveObjectName(view); + + // Render partial + function render(){ + if (object) { + if ('string' == typeof name) { + options[name] = object; + } else if (name === global) { + merge(options, object); + } + } + return res.render(view, options, null, parent, true); + } + + // Collection support + if (collection) { + var len = collection.length + , buf = '' + , keys + , key + , val; + + options.collectionLength = len; + + if ('number' == typeof len || Array.isArray(collection)) { + for (var i = 0; i < len; ++i) { + val = collection[i]; + options.firstInCollection = i == 0; + options.indexInCollection = i; + options.lastInCollection = i == len - 1; + object = val; + buf += render(); + } + } else { + keys = Object.keys(collection); + len = keys.length; + options.collectionLength = len; + options.collectionKeys = keys; + for (var i = 0; i < len; ++i) { + key = keys[i]; + val = collection[key]; + options.keyInCollection = key; + options.firstInCollection = i == 0; + options.indexInCollection = i; + options.lastInCollection = i == len - 1; + object = val; + buf += render(); + } + } + + return buf; + } else { + return render(); + } +}; + +/** + * Render `view` partial with the given `options`. Optionally a + * callback `fn(err, str)` may be passed instead of writing to + * the socket. + * + * Options: + * + * - `object` Single object with name derived from the view (unless `as` is present) + * + * - `as` Variable name for each `collection` value, defaults to the view name. + * * as: 'something' will add the `something` local variable + * * as: this will use the collection value as the template context + * * as: global will merge the collection value's properties with `locals` + * + * - `collection` Array of objects, the name is derived from the view name itself. + * For example _video.html_ will have a object _video_ available to it. + * + * @param {String} view + * @param {Object|Array|Function} options, collection, callback, or object + * @param {Function} fn + * @return {String} + * @api public + */ + +res.partial = function(view, options, fn){ + var app = this.app + , options = options || {} + , viewEngine = app.set('view engine') + , parent = {}; + + // accept callback as second argument + if ('function' == typeof options) { + fn = options; + options = {}; + } + + // root "views" option + parent.dirname = app.set('views') || process.cwd() + '/views'; + + // utilize "view engine" option + if (viewEngine) parent.engine = viewEngine; + + // render the partial + try { + var str = renderPartial(this, view, options, null, parent); + } catch (err) { + if (fn) { + fn(err); + } else { + this.req.next(err); + } + return; + } + + // callback or transfer + if (fn) { + fn(null, str); + } else { + this.send(str); + } +}; + +/** + * Render `view` with the given `options` and optional callback `fn`. + * When a callback function is given a response will _not_ be made + * automatically, however otherwise a response of _200_ and _text/html_ is given. + * + * Options: + * + * - `scope` Template evaluation context (the value of `this`) + * - `debug` Output debugging information + * - `status` Response status code + * + * @param {String} view + * @param {Object|Function} options or callback function + * @param {Function} fn + * @api public + */ + +res.render = function(view, opts, fn, parent, sub){ + // support callback function as second arg + if ('function' == typeof opts) { + fn = opts, opts = null; + } + + try { + return this._render(view, opts, fn, parent, sub); + } catch (err) { + // callback given + if (fn) { + fn(err); + // unwind to root call to prevent multiple callbacks + } else if (sub) { + throw err; + // root template, next(err) + } else { + this.req.next(err); + } + } +}; + +// private render() + +res._render = function(view, opts, fn, parent, sub){ + var options = {} + , self = this + , app = this.app + , helpers = app._locals + , dynamicHelpers = app.dynamicViewHelpers + , viewOptions = app.set('view options') + , root = app.set('views') || process.cwd() + '/views'; + + // cache id + var cid = app.enabled('view cache') + ? view + (parent ? ':' + parent.path : '') + : false; + + // merge "view options" + if (viewOptions) merge(options, viewOptions); + + // merge res._locals + if (this._locals) merge(options, this._locals); + + // merge render() options + if (opts) merge(options, opts); + + // merge render() .locals + if (opts && opts.locals) merge(options, opts.locals); + + // status support + if (options.status) this.statusCode = options.status; + + // capture attempts + options.attempts = []; + + var partial = options.isPartial + , layout = options.layout; + + // Layout support + if (true === layout || undefined === layout) { + layout = 'layout'; + } + + // Default execution scope to a plain object + options.scope = options.scope || {}; + + // Populate view + options.parentView = parent; + + // "views" setting + options.root = root; + + // "view engine" setting + options.defaultEngine = app.set('view engine'); + + // charset option + if (options.charset) this.charset = options.charset; + + // Dynamic helper support + if (false !== options.dynamicHelpers) { + // cache + if (!this.__dynamicHelpers) { + this.__dynamicHelpers = {}; + for (var key in dynamicHelpers) { + this.__dynamicHelpers[key] = dynamicHelpers[key].call( + this.app + , this.req + , this); + } + } + + // apply + merge(options, this.__dynamicHelpers); + } + + // Merge view helpers + union(options, helpers); + + // Always expose partial() as a local + options.partial = function(path, opts){ + return renderPartial(self, path, opts, options, view); + }; + + // View lookup + options.hint = app.enabled('hints'); + view = exports.compile(view, app.cache, cid, options); + + // layout helper + options.layout = function(path){ + layout = path; + }; + + // render + var str = view.fn.call(options.scope, options); + + // layout expected + if (layout) { + options.isLayout = true; + options.layout = false; + options.body = str; + this.render(layout, options, fn, view, true); + // partial return + } else if (partial) { + return str; + // render complete, and + // callback given + } else if (fn) { + fn(null, str); + // respond + } else { + this.send(str); + } +} + +/** + * Hint at view path resolution, outputting the + * paths that Express has tried. + * + * @api private + */ + +function hintAtViewPaths(view, options) { + console.error(); + console.error('failed to locate view "' + view.view + '", tried:'); + options.attempts.forEach(function(path){ + console.error(' - %s', path); + }); + console.error(); +} diff --git a/node_modules/express/lib/view/partial.js b/node_modules/express/lib/view/partial.js new file mode 100644 index 0000000..7d2f69b --- /dev/null +++ b/node_modules/express/lib/view/partial.js @@ -0,0 +1,40 @@ + +/*! + * Express - view - Partial + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Memory cache. + */ + +var cache = {}; + +/** + * Resolve partial object name from the view path. + * + * Examples: + * + * "user.ejs" becomes "user" + * "forum thread.ejs" becomes "forumThread" + * "forum/thread/post.ejs" becomes "post" + * "blog-post.ejs" becomes "blogPost" + * + * @return {String} + * @api private + */ + +exports.resolveObjectName = function(view){ + return cache[view] || (cache[view] = view + .split('/') + .slice(-1)[0] + .split('.')[0] + .replace(/^_/, '') + .replace(/[^a-zA-Z0-9 ]+/g, ' ') + .split(/ +/).map(function(word, i){ + return i + ? word[0].toUpperCase() + word.substr(1) + : word; + }).join('')); +};
\ No newline at end of file diff --git a/node_modules/express/lib/view/view.js b/node_modules/express/lib/view/view.js new file mode 100644 index 0000000..7d9392c --- /dev/null +++ b/node_modules/express/lib/view/view.js @@ -0,0 +1,210 @@ + +/*! + * Express - View + * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var path = require('path') + , utils = require('../utils') + , extname = path.extname + , dirname = path.dirname + , basename = path.basename + , fs = require('fs') + , stat = fs.statSync; + +/** + * Expose `View`. + */ + +exports = module.exports = View; + +/** + * Require cache. + */ + +var cache = {}; + +/** + * Initialize a new `View` with the given `view` path and `options`. + * + * @param {String} view + * @param {Object} options + * @api private + */ + +function View(view, options) { + options = options || {}; + this.view = view; + this.root = options.root; + this.relative = false !== options.relative; + this.defaultEngine = options.defaultEngine; + this.parent = options.parentView; + this.basename = basename(view); + this.engine = this.resolveEngine(); + this.extension = '.' + this.engine; + this.name = this.basename.replace(this.extension, ''); + this.path = this.resolvePath(); + this.dirname = dirname(this.path); + if (options.attempts) { + if (!~options.attempts.indexOf(this.path)) + options.attempts.push(this.path); + } +}; + +/** + * Check if the view path exists. + * + * @return {Boolean} + * @api public + */ + +View.prototype.__defineGetter__('exists', function(){ + try { + stat(this.path); + return true; + } catch (err) { + return false; + } +}); + +/** + * Resolve view engine. + * + * @return {String} + * @api private + */ + +View.prototype.resolveEngine = function(){ + // Explicit + if (~this.basename.indexOf('.')) return extname(this.basename).substr(1); + // Inherit from parent + if (this.parent) return this.parent.engine; + // Default + return this.defaultEngine; +}; + +/** + * Resolve view path. + * + * @return {String} + * @api private + */ + +View.prototype.resolvePath = function(){ + var path = this.view; + // Implicit engine + if (!~this.basename.indexOf('.')) path += this.extension; + // Absolute + if (utils.isAbsolute(path)) return path; + // Relative to parent + if (this.relative && this.parent) return this.parent.dirname + '/' + path; + // Relative to root + return this.root + ? this.root + '/' + path + : path; +}; + +/** + * Get view contents. This is a one-time hit, so we + * can afford to be sync. + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('contents', function(){ + return fs.readFileSync(this.path, 'utf8'); +}); + +/** + * Get template engine api, cache exports to reduce + * require() calls. + * + * @return {Object} + * @api public + */ + +View.prototype.__defineGetter__('templateEngine', function(){ + var ext = this.extension; + return cache[ext] || (cache[ext] = require(this.engine)); +}); + +/** + * Return root path alternative. + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('rootPath', function(){ + this.relative = false; + return this.resolvePath(); +}); + +/** + * Return index path alternative. + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('indexPath', function(){ + return this.dirname + + '/' + this.basename.replace(this.extension, '') + + '/index' + this.extension; +}); + +/** + * Return ../<name>/index path alternative. + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('upIndexPath', function(){ + return this.dirname + '/../' + this.name + '/index' + this.extension; +}); + +/** + * Return _ prefix path alternative + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('prefixPath', function(){ + return this.dirname + '/_' + this.basename; +}); + +/** + * Register the given template engine `exports` + * as `ext`. For example we may wish to map ".html" + * files to jade: + * + * app.register('.html', require('jade')); + * + * or + * + * app.register('html', require('jade')); + * + * This is also useful for libraries that may not + * match extensions correctly. For example my haml.js + * library is installed from npm as "hamljs" so instead + * of layout.hamljs, we can register the engine as ".haml": + * + * app.register('.haml', require('haml-js')); + * + * @param {String} ext + * @param {Object} obj + * @api public + */ + +exports.register = function(ext, exports) { + if ('.' != ext[0]) ext = '.' + ext; + cache[ext] = exports; +}; |
