package/.npmignore000644 000765 000024 00000000137 11657257013 014240 0ustar00tjstaff000000 000000 *.markdown *.md .git* Makefile benchmarks/ docs/ examples/ install.sh support/ test/ .DS_Store package/index.js000644 000765 000024 00000000053 11657257013 013703 0ustar00tjstaff000000 000000 module.exports = require('./lib/connect');package/LICENSE000644 000765 000024 00000002161 11657257013 013245 0ustar00tjstaff000000 000000 (The MIT License) Copyright (c) 2010 Sencha Inc. Copyright (c) 2011 LearnBoost Copyright (c) 2011 TJ Holowaychuk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.package/package.json000644 000765 000024 00000001252 11657257013 014526 0ustar00tjstaff000000 000000 { "name": "connect", "version": "1.7.3", "description": "High performance middleware framework", "keywords": ["framework", "web", "middleware", "connect", "rack"], "repository": "git://github.com/senchalabs/connect.git", "author": "TJ Holowaychuk (http://tjholowaychuk.com)", "repository": "git://github.com/senchalabs/connect", "dependencies": { "qs": ">= 0.3.1", "mime": ">= 0.0.1" }, "devDependencies": { "expresso": "0.9.2", "koala": "0.1.2", "less": "1.1.1", "sass": "0.5.0", "markdown": "0.2.1", "ejs": "0.4.3", "should": "0.3.2" }, "main": "index", "engines": { "node": ">= 0.4.1 < 0.7.0" } }package/test.js000644 000765 000024 00000000277 11657257013 013563 0ustar00tjstaff000000 000000 /** * Module dependencies. */ var connect = require('./') , utils = connect.utils , http = require('http') , url = require('url') , path = require('path') , fs = require('fs'); package/lib/cache.js000644 000765 000024 00000002206 11657257013 014407 0ustar00tjstaff000000 000000 /*! * Connect - Cache * Copyright(c) 2011 Sencha Inc. * MIT Licensed */ /** * Expose `Cache`. */ module.exports = Cache; /** * LRU cache store. * * @param {Number} limit * @api private */ function Cache(limit) { this.store = {}; this.keys = []; this.limit = limit; } /** * Touch `key`, promoting the object. * * @param {String} key * @param {Number} i * @api private */ Cache.prototype.touch = function(key, i){ this.keys.splice(i,1); this.keys.push(key); }; /** * Remove `key`. * * @param {String} key * @api private */ Cache.prototype.remove = function(key){ delete this.store[key]; }; /** * Get the object stored for `key`. * * @param {String} key * @return {Array} * @api private */ Cache.prototype.get = function(key){ return this.store[key]; }; /** * Add a cache `key`. * * @param {String} key * @return {Array} * @api private */ Cache.prototype.add = function(key){ // initialize store var len = this.keys.push(key); // limit reached, invalid LRU if (len > this.limit) this.remove(this.keys.shift()); var arr = this.store[key] = []; arr.createdAt = new Date; return arr; }; package/lib/connect.js000644 000765 000024 00000004336 11657257013 015003 0ustar00tjstaff000000 000000 /*! * Connect * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var HTTPServer = require('./http').Server , HTTPSServer = require('./https').Server , fs = require('fs'); // node patches require('./patch'); // expose createServer() as the module exports = module.exports = createServer; /** * Framework version. */ exports.version = '1.7.3'; /** * Initialize a new `connect.HTTPServer` with the middleware * passed to this function. When an object is passed _first_, * we assume these are the tls options, and return a `connect.HTTPSServer`. * * Examples: * * An example HTTP server, accepting several middleware. * * var server = connect.createServer( * connect.logger() * , connect.static(__dirname + '/public') * ); * * An HTTPS server, utilizing the same middleware as above. * * var server = connect.createServer( * { key: key, cert: cert } * , connect.logger() * , connect.static(__dirname + '/public') * ); * * Alternatively with connect 1.0 we may omit `createServer()`. * * connect( * connect.logger() * , connect.static(__dirname + '/public') * ).listen(3000); * * @param {Object|Function} ... * @return {Server} * @api public */ function createServer() { if ('object' == typeof arguments[0]) { return new HTTPSServer(arguments[0], Array.prototype.slice.call(arguments, 1)); } else { return new HTTPServer(Array.prototype.slice.call(arguments)); } }; // support connect.createServer() exports.createServer = createServer; // auto-load getters exports.middleware = {}; /** * Auto-load bundled middleware with getters. */ fs.readdirSync(__dirname + '/middleware').forEach(function(filename){ if (/\.js$/.test(filename)) { var name = filename.substr(0, filename.lastIndexOf('.')); exports.middleware.__defineGetter__(name, function(){ return require('./middleware/' + name); }); } }); // expose utils exports.utils = require('./utils'); // expose getters as first-class exports exports.utils.merge(exports, exports.middleware); // expose constructors exports.HTTPServer = HTTPServer; exports.HTTPSServer = HTTPSServer; package/lib/http.js000644 000765 000024 00000012244 11657257013 014326 0ustar00tjstaff000000 000000 /*! * Connect - HTTPServer * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var http = require('http') , parse = require('url').parse , assert = require('assert'); // environment var env = process.env.NODE_ENV || 'development'; /** * Initialize a new `Server` with the given `middleware`. * * Examples: * * var server = connect.createServer( * connect.favicon() * , connect.logger() * , connect.static(__dirname + '/public') * ); * * @params {Array} middleware * @return {Server} * @api public */ var Server = exports.Server = function HTTPServer(middleware) { this.stack = []; middleware.forEach(function(fn){ this.use(fn); }, this); http.Server.call(this, this.handle); }; /** * Inherit from `http.Server.prototype`. */ Server.prototype.__proto__ = http.Server.prototype; /** * Utilize the given middleware `handle` to the given `route`, * defaulting to _/_. This "route" is the mount-point for the * middleware, when given a value other than _/_ the middleware * is only effective when that segment is present in the request's * pathname. * * For example if we were to mount a function at _/admin_, it would * be invoked on _/admin_, and _/admin/settings_, however it would * not be invoked for _/_, or _/posts_. * * This is effectively the same as passing middleware to `connect.createServer()`, * however provides a progressive api. * * Examples: * * var server = connect.createServer(); * server.use(connect.favicon()); * server.use(connect.logger()); * server.use(connect.static(__dirname + '/public')); * * If we wanted to prefix static files with _/public_, we could * "mount" the `static()` middleware: * * server.use('/public', connect.static(__dirname + '/public')); * * This api is chainable, meaning the following is valid: * * connect.createServer() * .use(connect.favicon()) * .use(connect.logger()) * .use(connect.static(__dirname + '/public')) * .listen(3000); * * @param {String|Function} route or handle * @param {Function} handle * @return {Server} * @api public */ Server.prototype.use = function(route, handle){ this.route = '/'; // default route to '/' if ('string' != typeof route) { handle = route; route = '/'; } // wrap sub-apps if ('function' == typeof handle.handle) { var server = handle; server.route = route; handle = function(req, res, next) { server.handle(req, res, next); }; } // wrap vanilla http.Servers if (handle instanceof http.Server) { handle = handle.listeners('request')[0]; } // normalize route to not trail with slash if ('/' == route[route.length - 1]) { route = route.substr(0, route.length - 1); } // add the middleware this.stack.push({ route: route, handle: handle }); // allow chaining return this; }; /** * Handle server requests, punting them down * the middleware stack. * * @api private */ Server.prototype.handle = function(req, res, out) { var writeHead = res.writeHead , stack = this.stack , removed = '' , index = 0; function next(err) { var layer, path, c; req.url = removed + req.url; req.originalUrl = req.originalUrl || req.url; removed = ''; layer = stack[index++]; // all done if (!layer || res.headerSent) { // but wait! we have a parent if (out) return out(err); // error if (err) { var msg = 'production' == env ? 'Internal Server Error' : err.stack || err.toString(); // output to stderr in a non-test env if ('test' != env) console.error(err.stack || err.toString()); // unable to respond if (res.headerSent) return req.socket.destroy(); res.statusCode = 500; res.setHeader('Content-Type', 'text/plain'); if ('HEAD' == req.method) return res.end(); res.end(msg); } else { res.statusCode = 404; res.setHeader('Content-Type', 'text/plain'); if ('HEAD' == req.method) return res.end(); res.end('Cannot ' + req.method + ' ' + req.url); } return; } try { path = parse(req.url).pathname; if (undefined == path) path = '/'; // skip this layer if the route doesn't match. if (0 != path.indexOf(layer.route)) return next(err); c = path[layer.route.length]; if (c && '/' != c && '.' != c) return next(err); // Call the layer handler // Trim off the part of the url that matches the route removed = layer.route; req.url = req.url.substr(removed.length); // Ensure leading slash if ('/' != req.url[0]) req.url = '/' + req.url; var arity = layer.handle.length; if (err) { if (arity === 4) { layer.handle(err, req, res, next); } else { next(err); } } else if (arity < 4) { layer.handle(req, res, next); } else { next(); } } catch (e) { if (e instanceof assert.AssertionError) { console.error(e.stack + '\n'); next(e); } else { next(e); } } } next(); };package/lib/https.js000644 000765 000024 00000001775 11657257013 014520 0ustar00tjstaff000000 000000 /*! * Connect - HTTPServer * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var HTTPServer = require('./http').Server , https = require('https'); /** * Initialize a new `Server` with the given *`options` and `middleware`. The HTTPS api * is identical to the [HTTP](http.html) server, * however TLS `options` must be provided before * passing in the optional middleware. * * @params {Object} options * @params {Array} middleawre * @return {Server} * @api public */ var Server = exports.Server = function HTTPSServer(options, middleware) { this.stack = []; middleware.forEach(function(fn){ this.use(fn); }, this); https.Server.call(this, options, this.handle); }; /** * Inherit from `http.Server.prototype`. */ Server.prototype.__proto__ = https.Server.prototype; // mixin HTTPServer methods Object.keys(HTTPServer.prototype).forEach(function(method){ Server.prototype[method] = HTTPServer.prototype[method]; });package/lib/index.js000644 000765 000024 00000004144 11657257013 014456 0ustar00tjstaff000000 000000 /** * # Connect * * Connect is a middleware framework for node, * shipping with over 11 bundled middleware and a rich choice of * [3rd-party middleware](https://github.com/senchalabs/connect/wiki). * * Installation: * * $ npm install connect * * API: * * - [connect](connect.html) general * - [http](http.html) http server * - [https](https.html) https server * * Middleware: * * - [logger](middleware-logger.html) request logger with custom format support * - [csrf](middleware-csrf.html) Cross-site request forgery protection * - [basicAuth](middleware-basicAuth.html) basic http authentication * - [bodyParser](middleware-bodyParser.html) extensible request body parser * - [cookieParser](middleware-cookieParser.html) cookie parser * - [session](middleware-session.html) session management support with bundled [MemoryStore](middleware-session-memory.html) * - [compiler](middleware-compiler.html) static asset compiler (sass, less, coffee-script, etc) * - [methodOverride](middleware-methodOverride.html) faux HTTP method support * - [responseTime](middleware-responseTime.html) calculates response-time and exposes via X-Response-Time * - [router](middleware-router.html) provides rich Sinatra / Express-like routing * - [staticCache](middleware-staticCache.html) memory cache layer for the static() middleware * - [static](middleware-static.html) streaming static file server supporting `Range` and more * - [directory](middleware-directory.html) directory listing middleware * - [vhost](middleware-vhost.html) virtual host sub-domain mapping middleware * - [favicon](middleware-favicon.html) efficient favicon server (with default icon) * - [limit](middleware-limit.html) limit the bytesize of request bodies * - [profiler](middleware-profiler.html) request profiler reporting response-time, memory usage, etc * - [query](middleware-query.html) automatic querystring parser, populating `req.query` * - [errorHandler](middleware-errorHandler.html) flexible error handler * * Internals: * * - connect [utilities](utils.html) * - node monkey [patches](patch.html) * */package/lib/patch.js000644 000765 000024 00000002745 11657257013 014453 0ustar00tjstaff000000 000000 /*! * Connect * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var http = require('http') , res = http.OutgoingMessage.prototype; // original setHeader() var setHeader = res.setHeader; // original _renderHeaders() var _renderHeaders = res._renderHeaders; if (res._hasConnectPatch) return; /** * Provide a public "header sent" flag * until node does. * * @return {Boolean} * @api public */ res.__defineGetter__('headerSent', function(){ return this._headerSent; }); /** * Set header `field` to `val`, special-casing * the `Set-Cookie` field for multiple support. * * @param {String} field * @param {String} val * @api public */ res.setHeader = function(field, val){ var key = field.toLowerCase() , prev; // special-case Set-Cookie if (this._headers && 'set-cookie' == key) { if (prev = this.getHeader(field)) { val = Array.isArray(prev) ? prev.concat(val) : [prev, val]; } // charset } else if ('content-type' == key && this.charset) { val += '; charset=' + this.charset; } return setHeader.call(this, field, val); }; /** * Proxy `res.end()` to expose a 'header' event, * allowing arbitrary augmentation before the header * fields are written to the socket. * * NOTE: this _only_ supports node's progressive header * field API aka `res.setHeader()`. */ res._renderHeaders = function(){ this.emit('header'); return _renderHeaders.call(this); }; res._hasConnectPatch = true; package/lib/utils.js000644 000765 000024 00000021613 11657257013 014507 0ustar00tjstaff000000 000000 /*! * Connect - utils * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var crypto = require('crypto') , Path = require('path') , fs = require('fs'); /** * 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; }; /** * Return md5 hash of the given string and optional encoding, * defaulting to hex. * * utils.md5('wahoo'); * // => "e493298061761236c96b02ea6aa8a2ad" * * @param {String} str * @param {String} encoding * @return {String} * @api public */ exports.md5 = function(str, encoding){ return crypto .createHash('md5') .update(str) .digest(encoding || 'hex'); }; /** * Merge object b with object a. * * var a = { foo: 'bar' } * , b = { bar: 'baz' }; * * utils.merge(a, b); * // => { foo: 'bar', bar: 'baz' } * * @param {Object} a * @param {Object} b * @return {Object} * @api public */ exports.merge = function(a, b){ if (a && b) { for (var key in b) { a[key] = b[key]; } } return a; }; /** * Escape the given string of `html`. * * @param {String} html * @return {String} * @api public */ exports.escape = function(html){ return String(html) .replace(/&(?!\w+;)/g, '&') .replace(//g, '>') .replace(/"/g, '"'); }; /** * Return a unique identifier with the given `len`. * * utils.uid(10); * // => "FDaS435D2z" * * @param {Number} len * @return {String} * @api public */ exports.uid = function(len) { var buf = [] , chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' , charlen = chars.length; for (var i = 0; i < len; ++i) { buf.push(chars[getRandomInt(0, charlen - 1)]); } return buf.join(''); }; /** * Parse the given cookie string into an object. * * @param {String} str * @return {Object} * @api public */ exports.parseCookie = function(str){ var obj = {} , pairs = str.split(/[;,] */); for (var i = 0, len = pairs.length; i < len; ++i) { var pair = pairs[i] , eqlIndex = pair.indexOf('=') , key = pair.substr(0, eqlIndex).trim().toLowerCase() , val = pair.substr(++eqlIndex, pair.length).trim(); // quoted values if ('"' == val[0]) val = val.slice(1, -1); // only assign once if (undefined == obj[key]) { val = val.replace(/\+/g, ' '); try { obj[key] = decodeURIComponent(val); } catch (err) { if (err instanceof URIError) { obj[key] = val; } else { throw err; } } } } return obj; }; /** * Serialize the given object into a cookie string. * * utils.serializeCookie('name', 'tj', { httpOnly: true }) * // => "name=tj; httpOnly" * * @param {String} name * @param {String} val * @param {Object} obj * @return {String} * @api public */ exports.serializeCookie = function(name, val, obj){ var pairs = [name + '=' + encodeURIComponent(val)] , obj = obj || {}; if (obj.domain) pairs.push('domain=' + obj.domain); if (obj.path) pairs.push('path=' + obj.path); if (obj.expires) pairs.push('expires=' + obj.expires.toUTCString()); if (obj.httpOnly) pairs.push('httpOnly'); if (obj.secure) pairs.push('secure'); return pairs.join('; '); }; /** * Pause `data` and `end` events on the given `obj`. * Middleware performing async tasks _should_ utilize * this utility (or similar), to re-emit data once * the async operation has completed, otherwise these * events may be lost. * * var pause = utils.pause(req); * fs.readFile(path, function(){ * next(); * pause.resume(); * }); * * @param {Object} obj * @return {Object} * @api public */ exports.pause = function(obj){ var onData , onEnd , events = []; // buffer data obj.on('data', onData = function(data, encoding){ events.push(['data', data, encoding]); }); // buffer end obj.on('end', onEnd = function(data, encoding){ events.push(['end', data, encoding]); }); return { end: function(){ obj.removeListener('data', onData); obj.removeListener('end', onEnd); }, resume: function(){ this.end(); for (var i = 0, len = events.length; i < len; ++i) { obj.emit.apply(obj, events[i]); } } }; }; /** * Check `req` and `res` to see if it has been modified. * * @param {IncomingMessage} req * @param {ServerResponse} res * @return {Boolean} * @api public */ exports.modified = function(req, res, headers) { var headers = headers || res._headers || {} , modifiedSince = req.headers['if-modified-since'] , lastModified = headers['last-modified'] , noneMatch = req.headers['if-none-match'] , etag = headers['etag']; if (noneMatch) noneMatch = noneMatch.split(/ *, */); // check If-None-Match if (noneMatch && etag && ~noneMatch.indexOf(etag)) { return false; } // check If-Modified-Since if (modifiedSince && lastModified) { modifiedSince = new Date(modifiedSince); lastModified = new Date(lastModified); // Ignore invalid dates if (!isNaN(modifiedSince.getTime())) { if (lastModified <= modifiedSince) return false; } } return true; }; /** * Strip `Content-*` headers from `res`. * * @param {ServerResponse} res * @api public */ exports.removeContentHeaders = function(res){ Object.keys(res._headers).forEach(function(field){ if (0 == field.indexOf('content')) { res.removeHeader(field); } }); }; /** * Check if `req` is a conditional GET request. * * @param {IncomingMessage} req * @return {Boolean} * @api public */ exports.conditionalGET = function(req) { return req.headers['if-modified-since'] || req.headers['if-none-match']; }; /** * Respond with 403 "Forbidden". * * @param {ServerResponse} res * @api public */ exports.forbidden = function(res) { var body = 'Forbidden'; res.setHeader('Content-Type', 'text/plain'); res.setHeader('Content-Length', body.length); res.statusCode = 403; res.end(body); }; /** * Respond with 401 "Unauthorized". * * @param {ServerResponse} res * @param {String} realm * @api public */ exports.unauthorized = function(res, realm) { res.statusCode = 401; res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"'); res.end('Unauthorized'); }; /** * Respond with 400 "Bad Request". * * @param {ServerResponse} res * @api public */ exports.badRequest = function(res) { res.statusCode = 400; res.end('Bad Request'); }; /** * Respond with 304 "Not Modified". * * @param {ServerResponse} res * @param {Object} headers * @api public */ exports.notModified = function(res) { exports.removeContentHeaders(res); res.statusCode = 304; res.end(); }; /** * Return an ETag in the form of `"-"` * from the given `stat`. * * @param {Object} stat * @return {String} * @api public */ exports.etag = function(stat) { return '"' + stat.size + '-' + Number(stat.mtime) + '"'; }; /** * Parse "Range" header `str` relative to the given file `size`. * * @param {Number} size * @param {String} str * @return {Array} * @api public */ 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; }; /** * Parse the given Cache-Control `str`. * * @param {String} str * @return {Object} * @api public */ exports.parseCacheControl = function(str){ var directives = str.split(',') , obj = {}; for(var i = 0, len = directives.length; i < len; i++) { var parts = directives[i].split('=') , key = parts.shift().trim() , val = parseInt(parts.shift(), 10); obj[key] = isNaN(val) ? true : val; } return obj; }; /** * Convert array-like object to an `Array`. * * node-bench measured "16.5 times faster than Array.prototype.slice.call()" * * @param {Object} obj * @return {Array} * @api public */ var toArray = exports.toArray = function(obj){ var len = obj.length , arr = new Array(len); for (var i = 0; i < len; ++i) { arr[i] = obj[i]; } return arr; }; /** * Retrun a random int, used by `utils.uid()` * * @param {Number} min * @param {Number} max * @return {Number} * @api private */ function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } package/lib/middleware/basicAuth.js000644 000765 000024 00000004513 11657257013 017367 0ustar00tjstaff000000 000000 /*! * Connect - basicAuth * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var utils = require('../utils') , unauthorized = utils.unauthorized , badRequest = utils.badRequest; /** * Enfore basic authentication by providing a `callback(user, pass)`, * which must return `true` in order to gain access. Alternatively an async * method is provided as well, invoking `callback(user, pass, callback)`. Populates * `req.remoteUser`. The final alternative is simply passing username / password * strings. * * Examples: * * connect(connect.basicAuth('username', 'password')); * * connect( * connect.basicAuth(function(user, pass){ * return 'tj' == user & 'wahoo' == pass; * }) * ); * * connect( * connect.basicAuth(function(user, pass, fn){ * User.authenticate({ user: user, pass: pass }, fn); * }) * ); * * @param {Function|String} callback or username * @param {String} realm * @api public */ module.exports = function basicAuth(callback, realm) { var username, password; // user / pass strings if ('string' == typeof callback) { username = callback; password = realm; if ('string' != typeof password) throw new Error('password argument required'); realm = arguments[2]; callback = function(user, pass){ return user == username && pass == password; } } realm = realm || 'Authorization Required'; return function(req, res, next) { var authorization = req.headers.authorization; if (req.remoteUser) return next(); if (!authorization) return unauthorized(res, realm); var parts = authorization.split(' ') , scheme = parts[0] , credentials = new Buffer(parts[1], 'base64').toString().split(':'); if ('Basic' != scheme) return badRequest(res); // async if (callback.length >= 3) { var pause = utils.pause(req); callback(credentials[0], credentials[1], function(err, user){ if (err || !user) return unauthorized(res, realm); req.remoteUser = user; next(); pause.resume(); }); // sync } else { if (callback(credentials[0], credentials[1])) { req.remoteUser = credentials[0]; next(); } else { unauthorized(res, realm); } } } }; package/lib/middleware/bodyParser.js000644 000765 000024 00000004112 11657257013 017571 0ustar00tjstaff000000 000000 /*! * Connect - bodyParser * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var qs = require('qs'); /** * Extract the mime type from the given request's * _Content-Type_ header. * * @param {IncomingMessage} req * @return {String} * @api private */ function mime(req) { var str = req.headers['content-type'] || ''; return str.split(';')[0]; } /** * Parse request bodies. * * By default _application/json_ and _application/x-www-form-urlencoded_ * are supported, however you may map `connect.bodyParser.parse[contentType]` * to a function of your choice to replace existing parsers, or implement * one for other content-types. * * Examples: * * connect.createServer( * connect.bodyParser() * , function(req, res) { * res.end('viewing user ' + req.body.user.name); * } * ); * * Since both _json_ and _x-www-form-urlencoded_ are supported by * default, either of the following requests would result in the response * of "viewing user tj". * * $ curl -d 'user[name]=tj' http://localhost/ * $ curl -d '{"user":{"name":"tj"}}' -H "Content-Type: application/json" http://localhost/ * * @return {Function} * @api public */ exports = module.exports = function bodyParser(){ return function bodyParser(req, res, next) { if (req.body) return next(); req.body = {}; if ('GET' == req.method || 'HEAD' == req.method) return next(); var parser = exports.parse[mime(req)]; if (parser) { var data = ''; req.setEncoding('utf8'); req.on('data', function(chunk) { data += chunk; }); req.on('end', function(){ try { req.body = data ? parser(data) : {}; } catch (err) { return next(err); } next(); }); } else { next(); } } }; /** * Supported decoders. * * - application/x-www-form-urlencoded * - application/json */ exports.parse = { 'application/x-www-form-urlencoded': qs.parse , 'application/json': JSON.parse };package/lib/middleware/compiler.js000644 000765 000024 00000007414 11657257013 017301 0ustar00tjstaff000000 000000 /*! * Connect - compiler * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var fs = require('fs') , path = require('path') , parse = require('url').parse; /** * Require cache. */ var cache = {}; /** * Setup compiler. * * Options: * * - `src` Source directory, defaults to **CWD**. * - `dest` Destination directory, defaults `src`. * - `enable` Array of enabled compilers. * * Compilers: * * - `sass` Compiles sass to css * - `less` Compiles less to css * - `coffeescript` Compiles coffee to js * * @param {Object} options * @api public */ exports = module.exports = function compiler(options){ options = options || {}; var srcDir = options.src || process.cwd() , destDir = options.dest || srcDir , enable = options.enable; if (!enable || enable.length === 0) { throw new Error('compiler\'s "enable" option is not set, nothing will be compiled.'); } return function compiler(req, res, next){ if ('GET' != req.method) return next(); var pathname = parse(req.url).pathname; for (var i = 0, len = enable.length; i < len; ++i) { var name = enable[i] , compiler = compilers[name]; if (compiler.match.test(pathname)) { var src = (srcDir + pathname).replace(compiler.match, compiler.ext) , dest = destDir + pathname; // Compare mtimes fs.stat(src, function(err, srcStats){ if (err) { if ('ENOENT' == err.code) { next(); } else { next(err); } } else { fs.stat(dest, function(err, destStats){ if (err) { // Oh snap! it does not exist, compile it if ('ENOENT' == err.code) { compile(); } else { next(err); } } else { // Source has changed, compile it if (srcStats.mtime > destStats.mtime) { compile(); } else { // Defer file serving next(); } } }); } }); // Compile to the destination function compile() { fs.readFile(src, 'utf8', function(err, str){ if (err) { next(err); } else { compiler.compile(str, function(err, str){ if (err) { next(err); } else { fs.writeFile(dest, str, 'utf8', function(err){ next(err); }); } }); } }); } return; } } next(); }; }; /** * Bundled compilers: * * - [sass](http://github.com/visionmedia/sass.js) to _css_ * - [less](http://github.com/cloudhead/less.js) to _css_ * - [coffee](http://github.com/jashkenas/coffee-script) to _js_ */ var compilers = exports.compilers = { sass: { match: /\.css$/, ext: '.sass', compile: function(str, fn){ var sass = cache.sass || (cache.sass = require('sass')); try { fn(null, sass.render(str)); } catch (err) { fn(err); } } }, less: { match: /\.css$/, ext: '.less', compile: function(str, fn){ var less = cache.less || (cache.less = require('less')); try { less.render(str, fn); } catch (err) { fn(err); } } }, coffeescript: { match: /\.js$/, ext: '.coffee', compile: function(str, fn){ var coffee = cache.coffee || (cache.coffee = require('coffee-script')); try { fn(null, coffee.compile(str)); } catch (err) { fn(err); } } } };package/lib/middleware/cookieParser.js000644 000765 000024 00000001547 11657257013 020116 0ustar00tjstaff000000 000000 /*! * Connect - cookieParser * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var utils = require('./../utils'); /** * Parse _Cookie_ header and populate `req.cookies` * with an object keyed by the cookie names. * * Examples: * * connect.createServer( * connect.cookieParser() * , function(req, res, next){ * res.end(JSON.stringify(req.cookies)); * } * ); * * @return {Function} * @api public */ module.exports = function cookieParser(){ return function cookieParser(req, res, next) { var cookie = req.headers.cookie; if (req.cookies) return next(); req.cookies = {}; if (cookie) { try { req.cookies = utils.parseCookie(cookie); } catch (err) { return next(err); } } next(); }; };package/lib/middleware/csrf.js000644 000765 000024 00000005317 11657257013 016424 0ustar00tjstaff000000 000000 /*! * Connect - csrf * Copyright(c) 2011 Sencha Inc. * MIT Licensed */ /** * Module dependencies. */ var utils = require('../utils') , crypto = require('crypto'); /** * CRSF protection middleware. * * By default this middleware generates a token named "_csrf" * which should be added to requests which mutate * state, within a hidden form field, query-string etc. This * token is validated against the visitor's `req.session._csrf` * property which is re-generated per request. * * The default `value` function checks `req.body` generated * by the `bodyParser()` middleware, `req.query` generated * by `query()`, and the "X-CSRF-Token" header field. * * This middleware requires session support, thus should be added * somewhere _below_ `session()` and `cookieParser()`. * * Examples: * * var form = '\n\ *
\n\ * \n\ * \n\ * \n\ * \n\ *
\n\ * '; * * connect( * connect.cookieParser() * , connect.session({ secret: 'keyboard cat' }) * , connect.bodyParser() * , connect.csrf() * * , function(req, res, next){ * if ('POST' != req.method) return next(); * req.session.user = req.body.user; * next(); * } * * , function(req, res){ * res.setHeader('Content-Type', 'text/html'); * var body = form * .replace('{token}', req.session._csrf) * .replace('{user}', req.session.user && req.session.user.name || ''); * res.end(body); * } * ).listen(3000); * * Options: * * - `value` a function accepting the request, returning the token * * @param {Object} options * @api public */ module.exports = function csrf(options) { var options = options || {} , value = options.value || defaultValue; return function(req, res, next){ // generate CSRF token var token = req.session._csrf || (req.session._csrf = utils.uid(24)); // ignore GET (for now) if ('GET' == req.method) return next(); // determine value var val = value(req); // check if (val != token) return utils.forbidden(res); next(); } }; /** * Default value function, checking the `req.body` * and `req.query` for the CSRF token. * * @param {IncomingMessage} req * @return {String} * @api private */ function defaultValue(req) { return (req.body && req.body._csrf) || (req.query && req.query._csrf) || (req.headers['x-csrf-token']); }package/lib/middleware/directory.js000644 000765 000024 00000012274 11657257013 017473 0ustar00tjstaff000000 000000 /*! * Connect - directory * Copyright(c) 2011 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ // TODO: icon / style for directories // TODO: arrow key navigation // TODO: make icons extensible /** * Module dependencies. */ var fs = require('fs') , parse = require('url').parse , utils = require('../utils') , path = require('path') , normalize = path.normalize , extname = path.extname , join = path.join; /** * Icon cache. */ var cache = {}; /** * Serve directory listings with the given `root` path. * * Options: * * - `hidden` display hidden (dot) files. Defaults to false. * - `icons` display icons. Defaults to false. * - `filter` Apply this filter function to files. Defaults to false. * * @param {String} root * @param {Object} options * @return {Function} * @api public */ exports = module.exports = function directory(root, options){ options = options || {}; // root required if (!root) throw new Error('directory() root path required'); var hidden = options.hidden , icons = options.icons , filter = options.filter , root = normalize(root); return function directory(req, res, next) { var accept = req.headers.accept || 'text/plain' , url = parse(req.url) , dir = decodeURIComponent(url.pathname) , path = normalize(join(root, dir)) , originalUrl = parse(req.originalUrl) , originalDir = decodeURIComponent(originalUrl.pathname) , showUp = path != root && path != root + '/'; // null byte(s) if (~path.indexOf('\0')) return utils.badRequest(res); // malicious path if (0 != path.indexOf(root)) return utils.forbidden(res); // check if we have a directory fs.stat(path, function(err, stat){ if (err) return 'ENOENT' == err.code ? next() : next(err); if (!stat.isDirectory()) return next(); // fetch files fs.readdir(path, function(err, files){ if (err) return next(err); if (!hidden) files = removeHidden(files); if (filter) files = files.filter(filter); files.sort(); // content-negotiation for (var key in exports) { if (~accept.indexOf(key) || ~accept.indexOf('*/*')) { exports[key](req, res, files, next, originalDir, showUp, icons); return; } } utils.notAcceptable(res); }); }); }; }; /** * Respond with text/html. */ exports.html = function(req, res, files, next, dir, showUp, icons){ fs.readFile(__dirname + '/../public/directory.html', 'utf8', function(err, str){ if (err) return next(err); fs.readFile(__dirname + '/../public/style.css', 'utf8', function(err, style){ if (err) return next(err); if (showUp) files.unshift('..'); str = str .replace('{style}', style) .replace('{files}', html(files, dir, icons)) .replace('{directory}', dir) .replace('{linked-path}', htmlPath(dir)); res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Length', str.length); res.end(str); }); }); }; /** * Respond with application/json. */ exports.json = function(req, res, files){ files = JSON.stringify(files); res.setHeader('Content-Type', 'application/json'); res.setHeader('Content-Length', files.length); res.end(files); }; /** * Respond with text/plain. */ exports.plain = function(req, res, files){ files = files.join('\n') + '\n'; res.setHeader('Content-Type', 'text/plain'); res.setHeader('Content-Length', files.length); res.end(files); }; /** * Map html `dir`, returning a linked path. */ function htmlPath(dir) { var curr = []; return dir.split('/').map(function(part){ curr.push(part); return '' + part + ''; }).join(' / '); } /** * Map html `files`, returning an html unordered list. */ function html(files, dir, useIcons) { return '
    ' + files.map(function(file){ var icon = '' , classes = []; if (useIcons && '..' != file) { icon = icons[extname(file)] || icons.default; icon = ''; classes.push('icon'); } return '
  • ' + icon + file + '
  • '; }).join('\n') + '
'; } /** * Load and cache the given `icon`. * * @param {String} icon * @return {String} * @api private */ function load(icon) { if (cache[icon]) return cache[icon]; return cache[icon] = fs.readFileSync(__dirname + '/../public/icons/' + icon, 'base64'); } /** * Filter "hidden" `files`, aka files * beginning with a `.`. * * @param {Array} files * @return {Array} * @api private */ function removeHidden(files) { return files.filter(function(file){ return '.' != file[0]; }); } /** * Icon map. */ var icons = { '.js': 'page_white_code_red.png' , '.c': 'page_white_c.png' , '.h': 'page_white_h.png' , '.cc': 'page_white_cplusplus.png' , '.php': 'page_white_php.png' , '.rb': 'page_white_ruby.png' , '.cpp': 'page_white_cplusplus.png' , '.swf': 'page_white_flash.png' , '.pdf': 'page_white_acrobat.png' , 'default': 'page_white.png' }; package/lib/middleware/errorHandler.js000644 000765 000024 00000005543 11657257013 020117 0ustar00tjstaff000000 000000 /*! * Connect - errorHandler * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var utils = require('../utils') , url = require('url') , fs = require('fs'); /** * Flexible error handler, providing (_optional_) stack traces * and error message responses for requests accepting text, html, * or json. * * Options: * * - `showStack`, `stack` respond with both the error message and stack trace. Defaults to `false` * - `showMessage`, `message`, respond with the exception message only. Defaults to `false` * - `dumpExceptions`, `dump`, dump exceptions to stderr (without terminating the process). Defaults to `false` * * Text: * * By default, and when _text/plain_ is accepted a simple stack trace * or error message will be returned. * * JSON: * * When _application/json_ is accepted, connect will respond with * an object in the form of `{ "error": error }`. * * HTML: * * When accepted connect will output a nice html stack trace. * * @param {Object} options * @return {Function} * @api public */ exports = module.exports = function errorHandler(options){ options = options || {}; // defaults var showStack = options.showStack || options.stack , showMessage = options.showMessage || options.message , dumpExceptions = options.dumpExceptions || options.dump , formatUrl = options.formatUrl; return function errorHandler(err, req, res, next){ res.statusCode = 500; if (dumpExceptions) console.error(err.stack); if (showStack) { var accept = req.headers.accept || ''; // html if (~accept.indexOf('html')) { fs.readFile(__dirname + '/../public/style.css', 'utf8', function(e, style){ fs.readFile(__dirname + '/../public/error.html', 'utf8', function(e, html){ var stack = (err.stack || '') .split('\n').slice(1) .map(function(v){ return '
  • ' + v + '
  • '; }).join(''); html = html .replace('{style}', style) .replace('{stack}', stack) .replace('{title}', exports.title) .replace(/\{error\}/g, utils.escape(err.toString())); res.setHeader('Content-Type', 'text/html'); res.end(html); }); }); // json } else if (~accept.indexOf('json')) { var json = JSON.stringify({ error: err }); res.setHeader('Content-Type', 'application/json'); res.end(json); // plain text } else { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end(err.stack); } } else { var body = showMessage ? err.toString() : 'Internal Server Error'; res.setHeader('Content-Type', 'text/plain'); res.end(body); } }; }; /** * Template title. */ exports.title = 'Connect';package/lib/middleware/favicon.js000644 000765 000024 00000003103 11657257013 017103 0ustar00tjstaff000000 000000 /*! * Connect - favicon * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var fs = require('fs') , utils = require('../utils'); /** * Favicon cache. */ var icon; /** * By default serves the connect favicon, or the favicon * located by the given `path`. * * Options: * * - `maxAge` cache-control max-age directive, defaulting to 1 day * * Examples: * * connect.createServer( * connect.favicon() * ); * * connect.createServer( * connect.favicon(__dirname + '/public/favicon.ico') * ); * * @param {String} path * @param {Object} options * @return {Function} * @api public */ module.exports = function favicon(path, options){ var options = options || {} , path = path || __dirname + '/../public/favicon.ico' , maxAge = options.maxAge || 86400000; return function favicon(req, res, next){ if ('/favicon.ico' == req.url) { if (icon) { res.writeHead(200, icon.headers); res.end(icon.body); } else { fs.readFile(path, function(err, buf){ if (err) return next(err); icon = { headers: { 'Content-Type': 'image/x-icon' , 'Content-Length': buf.length , 'ETag': '"' + utils.md5(buf) + '"' , 'Cache-Control': 'public, max-age=' + (maxAge / 1000) }, body: buf }; res.writeHead(200, icon.headers); res.end(icon.body); }); } } else { next(); } }; };package/lib/middleware/limit.js000644 000765 000024 00000003103 11657257013 016574 0ustar00tjstaff000000 000000 /*! * Connect - limit * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Limit request bodies to the given size in `bytes`. * * A string representation of the bytesize may also be passed, * for example "5mb", "200kb", "1gb", etc. * * Examples: * * var server = connect( * connect.limit('5.5mb') * ).listen(3000); * * TODO: pause EV_READ * * @param {Number|String} bytes * @return {Function} * @api public */ module.exports = function limit(bytes){ if ('string' == typeof bytes) bytes = parse(bytes); if ('number' != typeof bytes) throw new Error('limit() bytes required'); return function limit(req, res, next){ var received = 0 , len = req.headers['content-length'] ? parseInt(req.headers['content-length'], 10) : null; // deny the request function deny() { req.destroy(); } // self-awareness if (req._limit) return next(); req._limit = true; // limit by content-length if (len && len > bytes) { res.statusCode = 413; res.end('Request Entity Too Large'); return; } // limit req.on('data', function(chunk){ received += chunk.length; if (received > bytes) deny(); }); next(); }; }; /** * Parse byte `size` string. * * @param {String} size * @return {Number} * @api private */ function parse(size) { var parts = size.match(/^(\d+(?:\.\d+)?) *(kb|mb|gb)$/) , n = parseFloat(parts[1]) , type = parts[2]; var map = { kb: 1024 , mb: 1024 * 1024 , gb: 1024 * 1024 * 1024 }; return map[type] * n; }package/lib/middleware/logger.js000644 000765 000024 00000015366 11657257013 016753 0ustar00tjstaff000000 000000 /*! * Connect - logger * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Log buffer. */ var buf = []; /** * Default log buffer duration. */ var defaultBufferDuration = 1000; /** * Log requests with the given `options` or a `format` string. * * Options: * * - `format` Format string, see below for tokens * - `stream` Output stream, defaults to _stdout_ * - `buffer` Buffer duration, defaults to 1000ms when _true_ * - `immediate` Write log line on request instead of response (for response times) * * Tokens: * * - `:req[header]` ex: `:req[Accept]` * - `:res[header]` ex: `:res[Content-Length]` * - `:http-version` * - `:response-time` * - `:remote-addr` * - `:date` * - `:method` * - `:url` * - `:referrer` * - `:user-agent` * - `:status` * * Formats: * * Pre-defined formats that ship with connect: * * - `default` ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"' * - `short` ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms' * - `tiny` ':method :url :status :res[content-length] - :response-time ms' * - `dev` concise output colored by response status for development use * * Examples: * * connect.logger() // default * connect.logger('short') * connect.logger('tiny') * connect.logger('dev') * connect.logger(':method :url - :referrer') * connect.logger(':req[content-type] -> :res[content-type]') * connect.logger(function(req, res){ return 'some format string' }) * * Defining Tokens: * * To define a token, simply invoke `connect.logger.token()` with the * name and a callback function. The value returned is then available * as ":type" in this case. * * connect.logger.token('type', function(req, res){ return req.headers['content-type']; }) * * Defining Formats: * * All default formats are defined this way, however it's public API as well: * * connect.logger.format('name', 'string or function') * * @param {String|Function|Object} format or options * @return {Function} * @api public */ exports = module.exports = function logger(options) { if ('object' == typeof options) { options = options || {}; } else if (options) { options = { format: options }; } else { options = {}; } // output on request instead of response var immediate = options.immediate; // format name var fmt = exports[options.format] || options.format || exports.default; // compile format if ('function' != typeof fmt) fmt = compile(fmt); // options var stream = options.stream || process.stdout , buffer = options.buffer; // buffering support if (buffer) { var realStream = stream , interval = 'number' == typeof buffer ? buffer : defaultBufferDuration; // flush interval setInterval(function(){ if (buf.length) { realStream.write(buf.join(''), 'ascii'); buf.length = 0; } }, interval); // swap the stream stream = { write: function(str){ buf.push(str); } }; } return function logger(req, res, next) { req._startTime = new Date; // mount safety if (req._logging) return next(); // flag as logging req._logging = true; // immediate if (immediate) { var line = fmt(exports, req, res); if (null == line) return; stream.write(line + '\n', 'ascii'); } else { // proxy end to output loggging var end = res.end; res.end = function(chunk, encoding){ res.end = end; res.end(chunk, encoding); var line = fmt(exports, req, res); if (null == line) return; stream.write(line + '\n', 'ascii'); }; } next(); }; }; /** * Compile `fmt` into a function. * * @param {String} fmt * @return {Function} * @api private */ function compile(fmt) { fmt = fmt.replace(/"/g, '\\"'); var js = ' return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){ return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "'; }) + '";' return new Function('tokens, req, res', js); }; /** * Define a token function with the given `name`, * and callback `fn(req, res)`. * * @param {String} name * @param {Function} fn * @return {Object} exports for chaining * @api public */ exports.token = function(name, fn) { exports[name] = fn; return this; }; /** * Define a `fmt` with the given `name`. * * @param {String} name * @param {String|Function} fmt * @return {Object} exports for chaining * @api public */ exports.format = function(name, str){ exports[name] = str; return this; }; // default format exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'); // short format exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms'); // tiny format exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms'); // dev (colored) exports.format('dev', function(tokens, req, res){ var status = res.statusCode , color = 32; if (status >= 500) color = 31 else if (status >= 400) color = 33 else if (status >= 300) color = 36; return '\033[90m' + req.method + ' ' + req.originalUrl + ' ' + '\033[' + color + 'm' + res.statusCode + ' \033[90m' + (new Date - req._startTime) + 'ms\033[0m'; }); // request url exports.token('url', function(req){ return req.originalUrl; }); // request method exports.token('method', function(req){ return req.method; }); // response time in milliseconds exports.token('response-time', function(req){ return new Date - req._startTime; }); // UTC date exports.token('date', function(){ return new Date().toUTCString(); }); // response status code exports.token('status', function(req, res){ return res.statusCode; }); // normalized referrer exports.token('referrer', function(req){ return req.headers['referer'] || req.headers['referrer']; }); // remote address exports.token('remote-addr', function(req){ return req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)); }); // HTTP version exports.token('http-version', function(req){ return req.httpVersionMajor + '.' + req.httpVersionMinor; }); // UA string exports.token('user-agent', function(req){ return req.headers['user-agent']; }); // request header exports.token('req', function(req, res, field){ return req.headers[field.toLowerCase()]; }); // response header exports.token('res', function(req, res, field){ return (res._headers || {})[field.toLowerCase()]; }); package/lib/middleware/methodOverride.js000644 000765 000024 00000001624 11657257013 020444 0ustar00tjstaff000000 000000 /*! * Connect - methodOverride * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Provides faux HTTP method support. * * Pass an optional `key` to use when checking for * a method override, othewise defaults to _\_method_. * The original method is available via `req.originalMethod`. * * @param {String} key * @return {Function} * @api public */ module.exports = function methodOverride(key){ key = key || "_method"; return function methodOverride(req, res, next) { req.originalMethod = req.originalMethod || req.method; // req.body if (req.body && key in req.body) { req.method = req.body[key].toUpperCase(); delete req.body[key]; // check X-HTTP-Method-Override } else if (req.headers['x-http-method-override']) { req.method = req.headers['x-http-method-override'].toUpperCase(); } next(); }; }; package/lib/middleware/profiler.js000644 000765 000024 00000004153 11657257013 017306 0ustar00tjstaff000000 000000 /*! * Connect - profiler * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Profile the duration of a request. * * Typically this middleware should be utilized * _above_ all others, as it proxies the `res.end()` * method, being first allows it to encapsulate all * other middleware. * * Example Output: * * GET / * response time 2ms * memory rss 52.00kb * memory vsize 2.07mb * heap before 3.76mb / 8.15mb * heap after 3.80mb / 8.15mb * * @api public */ module.exports = function profiler(){ return function(req, res, next){ var end = res.end , start = snapshot(); // state snapshot function snapshot() { return { mem: process.memoryUsage() , time: new Date }; } // proxy res.end() res.end = function(data, encoding){ res.end = end; res.end(data, encoding); compare(req, start, snapshot()) }; next(); } }; /** * Compare `start` / `end` snapshots. * * @param {IncomingRequest} req * @param {Object} start * @param {Object} end * @api private */ function compare(req, start, end) { console.log(); row(req.method, req.url); row('response time:', (end.time - start.time) + 'ms'); row('memory rss:', formatBytes(end.mem.rss - start.mem.rss)); row('memory vsize:', formatBytes(end.mem.vsize - start.mem.vsize)); row('heap before:', formatBytes(start.mem.heapUsed) + ' / ' + formatBytes(start.mem.heapTotal)); row('heap after:', formatBytes(end.mem.heapUsed) + ' / ' + formatBytes(end.mem.heapTotal)); console.log(); } /** * Row helper * * @param {String} key * @param {String} val * @api private */ function row(key, val) { console.log(' \033[90m%s\033[0m \033[36m%s\033[0m', key, val); } /** * Format byte-size. * * @param {Number} bytes * @return {String} * @api private */ function formatBytes(bytes) { var kb = 1024 , mb = 1024 * kb , gb = 1024 * mb; if (bytes < kb) return bytes + 'b'; if (bytes < mb) return (bytes / kb).toFixed(2) + 'kb'; if (bytes < gb) return (bytes / mb).toFixed(2) + 'mb'; return (bytes / gb).toFixed(2) + 'gb'; }; package/lib/middleware/query.js000644 000765 000024 00000001303 11657257013 016623 0ustar00tjstaff000000 000000 /*! * Connect - query * Copyright(c) 2011 TJ Holowaychuk * Copyright(c) 2011 Sencha Inc. * MIT Licensed */ /** * Module dependencies. */ var qs = require('qs') , parse = require('url').parse; /** * Automatically parse the query-string when available, * populating the `req.query` object. * * Examples: * * connect( * connect.query() * , function(req, res){ * res.end(JSON.stringify(req.query)); * } * ).listen(3000); * * @return {Function} * @api public */ module.exports = function query(){ return function query(req, res, next){ req.query = ~req.url.indexOf('?') ? qs.parse(parse(req.url).query) : {}; next(); }; }; package/lib/middleware/responseTime.js000644 000765 000024 00000001325 11657257013 020137 0ustar00tjstaff000000 000000 /*! * Connect - responseTime * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Adds the `X-Response-Time` header displaying the response * duration in milliseconds. * * @return {Function} * @api public */ module.exports = function responseTime(){ return function(req, res, next){ var writeHead = res.writeHead , start = new Date; if (res._responseTime) return next(); res._responseTime = true; // proxy writeHead to calculate duration res.writeHead = function(status, headers){ var duration = new Date - start; res.setHeader('X-Response-Time', duration + 'ms'); res.writeHead = writeHead; res.writeHead(status, headers); }; next(); }; }; package/lib/middleware/router.js000644 000765 000024 00000020757 11657257013 017014 0ustar00tjstaff000000 000000 /*! * Connect - router * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var utils = require('../utils') , parse = require('url').parse; /** * Expose router. */ exports = module.exports = router; /** * Supported HTTP / WebDAV methods. */ var _methods = exports.methods = [ 'get' , 'post' , 'put' , 'delete' , 'connect' , 'options' , 'trace' , 'copy' , 'lock' , 'mkcol' , 'move' , 'propfind' , 'proppatch' , 'unlock' , 'report' , 'mkactivity' , 'checkout' , 'merge' ]; /** * Provides Sinatra and Express-like routing capabilities. * * Examples: * * connect.router(function(app){ * app.get('/user/:id', function(req, res, next){ * // populates req.params.id * }); * app.put('/user/:id', function(req, res, next){ * // populates req.params.id * }); * }) * * @param {Function} fn * @return {Function} * @api public */ function router(fn){ 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); } fn.middleware = middleware; if (!path) throw new Error(name + ' route requires a path'); if (!fn) throw new Error(name + ' route ' + path + ' requires a callback'); var regexp = path instanceof RegExp ? path : normalizePath(path, keys); localRoutes.push({ fn: fn , path: regexp , keys: keys , orig: path , method: name }); 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.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){ var fns = router.lookup(path, method); fns.forEach(function(fn){ routes[fn.method].splice(fn.index, 1); }); }; 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.orig) { var fn = route.fn; fn.regexp = route.path; fn.keys = route.keys; fn.path = route.orig; fn.method = route.method; fn.index = i; ret.push(fn); } }); } // 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 , fn , req; // method specific matches if (method) { method = method.toUpperCase(); req = { url: url, method: method }; while (fn = match(req, routes, i)) { i = req._route_index + 1; ret.push(fn); } // 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.writeHead(200, { 'Content-Length': body.length , 'Allow': body }); res.end(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].path.test(path)) return true; } }).map(function(method){ return method.toUpperCase(); }); } /** * 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} path * @param {Array} keys * @return {RegExp} * @api private */ function normalizePath(path, keys) { path = path .concat('/?') .replace(/\/\(/g, '(?:/') .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){ keys.push(key); slash = slash || ''; return '' + (optional ? '' : slash) + '(?:' + (optional ? slash : '') + (format || '') + (capture || '([^/]+?)') + ')' + (optional || ''); }) .replace(/([\/.])/g, '\\$1') .replace(/\*/g, '(.+)'); return new RegExp('^' + path + '$', 'i'); } /** * 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; if ('HEAD' == method) method = 'GET'; if (routes = routes[method]) { var url = parse(req.url) , pathname = url.pathname; for (var len = routes.length; i < len; ++i) { var route = routes[i] , fn = route.fn , path = route.path , keys = fn.keys = route.keys; if (captures = path.exec(pathname)) { fn.method = method; fn.params = []; for (var j = 1, len = captures.length; j < len; ++j) { var key = keys[j-1], val = typeof captures[j] === 'string' ? decodeURIComponent(captures[j]) : captures[j]; if (key) { fn.params[key] = val; } else { fn.params.push(val); } } req._route_index = i; return fn; } } } } package/lib/middleware/session.js000644 000765 000024 00000022456 11657257013 017155 0ustar00tjstaff000000 000000 /*! * Connect - session * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var Session = require('./session/session') , MemoryStore = require('./session/memory') , Cookie = require('./session/cookie') , Store = require('./session/store') , utils = require('./../utils') , parse = require('url').parse , crypto = require('crypto'); // environment var env = process.env.NODE_ENV; /** * Expose the middleware. */ exports = module.exports = session; /** * Expose constructors. */ exports.Store = Store; exports.Cookie = Cookie; exports.Session = Session; exports.MemoryStore = MemoryStore; /** * Warning message for `MemoryStore` usage in production. */ var warning = 'Warning: connection.session() MemoryStore is not\n' + 'designed for a production environment, as it will leak\n' + 'memory, and obviously only work within a single process.'; /** * Default finger-printing function. */ function defaultFingerprint(req) { var ua = req.headers['user-agent'] || ''; return ua.replace(/;?\schromeframe\/[\d\.]+/, ''); }; /** * Paths to ignore. */ exports.ignore = []; /** * Setup session store with the given `options`. * * Session data is _not_ saved in the cookie itself, however * cookies are used, so we must use the [cookieParser()](middleware-cookieParser.html) * middleware _before_ `session()`. * * Examples: * * connect.createServer( * connect.cookieParser() * , connect.session({ secret: 'keyboard cat' }) * ); * * Options: * * - `key` cookie name defaulting to `connect.sid` * - `store` Session store instance * - `fingerprint` Custom fingerprint generating function * - `cookie` Session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: 14400000 }` * - `secret` Secret string used to compute hash * * Ignore Paths: * * By default `/favicon.ico` is the only ignored path, all others * will utilize sessions, to manipulate the paths ignored, use * `connect.session.ignore.push('/my/path')`. This works for _full_ * pathnames only, not segments nor substrings. * * connect.session.ignore.push('/robots.txt'); * * ## req.session * * To store or access session data, simply use the request property `req.session`, * which is (generally) serialized as JSON by the store, so nested objects * are typically fine. For example below is a user-specific view counter: * * connect( * connect.cookieParser() * , connect.session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}) * , connect.favicon() * , function(req, res, next){ * var sess = req.session; * if (sess.views) { * res.setHeader('Content-Type', 'text/html'); * res.write('

    views: ' + sess.views + '

    '); * res.write('

    expires in: ' + (sess.cookie.maxAge / 1000) + 's

    '); * res.end(); * sess.views++; * } else { * sess.views = 1; * res.end('welcome to the session demo. refresh!'); * } * } * ).listen(3000); * * ## Session#regenerate() * * To regenerate the session simply invoke the method, once complete * a new SID and `Session` instance will be initialized at `req.session`. * * req.session.regenerate(function(err){ * // will have a new session here * }); * * ## Session#destroy() * * Destroys the session, removing `req.session`, will be re-generated next request. * * req.session.destroy(function(err){ * // cannot access session here * }); * * ## Session#reload() * * Reloads the session data. * * req.session.reload(function(err){ * // session updated * }); * * ## Session#save() * * Save the session. * * req.session.save(function(err){ * // session saved * }); * * ## Session#touch() * * Updates the `.maxAge`, and `.lastAccess` properties. Typically this is * not necessary to call, as the session middleware does this for you. * * ## Session#cookie * * Each session has a unique cookie object accompany it. This allows * you to alter the session cookie per visitor. For example we can * set `req.session.cookie.expires` to `false` to enable the cookie * to remain for only the duration of the user-agent. * * ## Session#maxAge * * Alternatively `req.session.cookie.maxAge` will return the time * remaining in milliseconds, which we may also re-assign a new value * to adjust the `.expires` property appropriately. The following * are essentially equivalent * * var hour = 3600000; * req.session.cookie.expires = new Date(Date.now() + hour); * req.session.cookie.maxAge = hour; * * For example when `maxAge` is set to `60000` (one minute), and 30 seconds * has elapsed it will return `30000` until the current request has completed, * at which time `req.session.touch()` is called to update `req.session.lastAccess`, * and reset `req.session.maxAge` to its original value. * * req.session.cookie.maxAge; * // => 30000 * * Session Store Implementation: * * Every session store _must_ implement the following methods * * - `.get(sid, callback)` * - `.set(sid, session, callback)` * - `.destroy(sid, callback)` * * Recommended methods include, but are not limited to: * * - `.length(callback)` * - `.clear(callback)` * * For an example implementation view the [connect-redis](http://github.com/visionmedia/connect-redis) repo. * * @param {Object} options * @return {Function} * @api public */ function session(options){ var options = options || {} , key = options.key || 'connect.sid' , secret = options.secret , store = options.store || new MemoryStore , fingerprint = options.fingerprint || defaultFingerprint , cookie = options.cookie; // notify user that this store is not // meant for a production environment if ('production' == env && store instanceof MemoryStore) { console.warn(warning); } // ensure secret is present if (!secret) { throw new Error('connect.session({ secret: "string" }) required for security'); } // session hashing function store.hash = function(req, base) { return crypto .createHmac('sha256', secret) .update(base + fingerprint(req)) .digest('base64') .replace(/=*$/, ''); }; // generates the new session store.generate = function(req){ var base = utils.uid(24); var sessionID = base + '.' + store.hash(req, base); req.sessionID = sessionID; req.session = new Session(req); req.session.cookie = new Cookie(cookie); }; return function session(req, res, next) { // self-awareness if (req.session) return next(); // parse url var url = parse(req.url) , path = url.pathname; // ignorable paths if (~exports.ignore.indexOf(path)) return next(); // expose store req.sessionStore = store; // proxy writeHead() to Set-Cookie var writeHead = res.writeHead; res.writeHead = function(status, headers){ if (req.session) { var cookie = req.session.cookie; // only send secure session cookies when there is a secure connection. // proxySecure is a custom attribute to allow for a reverse proxy // to handle SSL connections and to communicate to connect over HTTP that // the incoming connection is secure. var secured = cookie.secure && (req.connection.encrypted || req.connection.proxySecure); if (secured || !cookie.secure) { res.setHeader('Set-Cookie', cookie.serialize(key, req.sessionID)); } } res.writeHead = writeHead; return res.writeHead(status, headers); }; // proxy end() to commit the session var end = res.end; res.end = function(data, encoding){ res.end = end; if (req.session) { // HACK: ensure Set-Cookie for implicit writeHead() if (!res._header) res._implicitHeader(); req.session.resetMaxAge(); req.session.save(function(){ res.end(data, encoding); }); } else { res.end(data, encoding); } }; // session hashing function hash(base) { return store.hash(req, base); } // generate the session function generate() { store.generate(req); } // get the sessionID from the cookie req.sessionID = req.cookies[key]; // make a new session if the browser doesn't send a sessionID if (!req.sessionID) { generate(); next(); return; } // check the fingerprint var parts = req.sessionID.split('.'); if (parts[1] != hash(parts[0])) { generate(); next(); return; } // generate the session object var pause = utils.pause(req); store.get(req.sessionID, function(err, sess){ // proxy to resume() events var _next = next; next = function(err){ _next(err); pause.resume(); } // error handling if (err) { if ('ENOENT' == err.code) { generate(); next(); } else { next(err); } // no session } else if (!sess) { generate(); next(); // populate req.session } else { store.createSession(req, sess); next(); } }); }; }; package/lib/middleware/static.js000644 000765 000024 00000013073 11657257013 016754 0ustar00tjstaff000000 000000 /*! * Connect - staticProvider * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var fs = require('fs') , path = require('path') , join = path.join , basename = path.basename , normalize = path.normalize , utils = require('../utils') , Buffer = require('buffer').Buffer , parse = require('url').parse , mime = require('mime'); /** * Static file server with the given `root` path. * * Examples: * * var oneDay = 86400000; * * connect( * connect.static(__dirname + '/public') * ).listen(3000); * * connect( * connect.static(__dirname + '/public', { maxAge: oneDay }) * ).listen(3000); * * Options: * * - `maxAge` Browser cache maxAge in milliseconds. defaults to 0 * - `hidden` Allow transfer of hidden files. defaults to false * - `redirect` Redirect to trailing "/" when the pathname is a dir * * @param {String} root * @param {Object} options * @return {Function} * @api public */ exports = module.exports = function static(root, options){ options = options || {}; // root required if (!root) throw new Error('static() root path required'); options.root = root; return function static(req, res, next) { options.path = req.url; options.getOnly = true; send(req, res, next, options); }; }; /** * Expose mime module. */ exports.mime = mime; /** * Respond with 416 "Requested Range Not Satisfiable" * * @param {ServerResponse} res * @api private */ function invalidRange(res) { var body = 'Requested Range Not Satisfiable'; res.setHeader('Content-Type', 'text/plain'); res.setHeader('Content-Length', body.length); res.statusCode = 416; res.end(body); } /** * Attempt to tranfer the requseted file to `res`. * * @param {ServerRequest} * @param {ServerResponse} * @param {Function} next * @param {Object} options * @api private */ var send = exports.send = function(req, res, next, options){ options = options || {}; if (!options.path) throw new Error('path required'); // setup var maxAge = options.maxAge || 0 , ranges = req.headers.range , head = 'HEAD' == req.method , get = 'GET' == req.method , root = options.root ? normalize(options.root) : null , redirect = false === options.redirect ? false : true , getOnly = options.getOnly , fn = options.callback , hidden = options.hidden , done; // replace next() with callback when available if (fn) next = fn; // ignore non-GET requests if (getOnly && !get && !head) return next(); // parse url var url = parse(options.path) , path = decodeURIComponent(url.pathname) , type; // null byte(s) if (~path.indexOf('\0')) return utils.badRequest(res); // when root is not given, consider .. malicious if (!root && ~path.indexOf('..')) return utils.forbidden(res); // join / normalize from optional root dir path = normalize(join(root, path)); // malicious path if (root && 0 != path.indexOf(root)) return fn ? fn(new Error('Forbidden')) : utils.forbidden(res); // index.html support if ('/' == path[path.length - 1]) path += 'index.html'; // "hidden" file if (!hidden && '.' == basename(path)[0]) return next(); fs.stat(path, function(err, stat){ // mime type type = mime.lookup(path); // ignore ENOENT if (err) { if (fn) return fn(err); return 'ENOENT' == err.code ? next() : next(err); // redirect directory in case index.html is present } else if (stat.isDirectory()) { if (!redirect) return next(); res.statusCode = 301; res.setHeader('Location', url.pathname + '/'); res.end('Redirecting to ' + url.pathname + '/'); return; } // header fields if (!res.getHeader('Date')) res.setHeader('Date', new Date().toUTCString()); if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (maxAge / 1000)); if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', stat.mtime.toUTCString()); if (!res.getHeader('ETag')) res.setHeader('ETag', utils.etag(stat)); if (!res.getHeader('content-type')) { var charset = mime.charsets.lookup(type); res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : '')); } res.setHeader('Accept-Ranges', 'bytes'); // conditional GET support if (utils.conditionalGET(req)) { if (!utils.modified(req, res)) { req.emit('static'); return utils.notModified(res); } } var opts = {}; var chunkSize = stat.size; // we have a Range request if (ranges) { ranges = utils.parseRange(stat.size, ranges); // valid if (ranges) { // TODO: stream options // TODO: multiple support opts.start = ranges[0].start; opts.end = ranges[0].end; chunkSize = opts.end - opts.start + 1; res.statusCode = 206; res.setHeader('Content-Range', 'bytes ' + opts.start + '-' + opts.end + '/' + stat.size); // invalid } else { return fn ? fn(new Error('Requested Range Not Satisfiable')) : invalidRange(res); } } res.setHeader('Content-Length', chunkSize); // transfer if (head) return res.end(); // stream var stream = fs.createReadStream(path, opts); req.emit('static', stream); stream.pipe(res); // callback if (fn) { function callback(err) { done || fn(err); done = true } req.on('close', callback); stream.on('end', callback); } }); }; package/lib/middleware/staticCache.js000644 000765 000024 00000010165 11657257013 017677 0ustar00tjstaff000000 000000 /*! * Connect - staticCache * Copyright(c) 2011 Sencha Inc. * MIT Licensed */ /** * Module dependencies. */ var http = require('http') , utils = require('../utils') , Cache = require('../cache') , url = require('url') , fs = require('fs'); /** * Enables a memory cache layer on top of * the `static()` middleware, serving popular * static files. * * By default a maximum of 128 objects are * held in cache, with a max of 256k each, * totalling ~32mb. * * A Least-Recently-Used (LRU) cache algo * is implemented through the `Cache` object, * simply rotating cache objects as they are * hit. This means that increasingly popular * objects maintain their positions while * others get shoved out of the stack and * garbage collected. * * Benchmarks: * * static(): 2700 rps * node-static: 5300 rps * static() + staticCache(): 7500 rps * * Options: * * - `maxObjects` max cache objects [128] * - `maxLength` max cache object length 256kb * * @param {Type} name * @return {Type} * @api public */ module.exports = function staticCache(options){ var options = options || {} , cache = new Cache(options.maxObjects || 128) , maxlen = options.maxLength || 1024 * 256; return function staticCache(req, res, next){ var path = url.parse(req.url).pathname , ranges = req.headers.range , hit = cache.get(path) , hitCC , uaCC , header , age; // cache static req.on('static', function(stream){ var headers = res._headers , cc = utils.parseCacheControl(headers['cache-control'] || '') , contentLength = headers['content-length'] , hit; // ignore larger files if (!contentLength || contentLength > maxlen) return; // dont cache items we shouldn't be if ( cc['no-cache'] || cc['no-store'] || cc['private'] || cc['must-revalidate']) return; // if already in cache then validate if (hit = cache.get(path)){ if (headers.etag == hit[0].etag) { hit[0].date = new Date; return; } else { cache.remove(path); } } // validation notifiactions don't contain a steam if (null == stream) return; // add the cache object var arr = cache.add(path); arr.push(headers); // store the chunks stream.on('data', function(chunk){ arr.push(chunk); }); // flag it as complete stream.on('end', function(){ arr.complete = true; }); }); // cache hit, doesnt support range requests if (hit && hit.complete && !ranges) { header = utils.merge({}, hit[0]); header.Age = age = (new Date - new Date(header.date)) / 1000 | 0; header.date = new Date().toUTCString(); // parse cache-controls hitCC = utils.parseCacheControl(header['cache-control'] || ''); uaCC = utils.parseCacheControl(req.headers['cache-control'] || ''); // check if we must revalidate(bypass) if (hitCC['no-cache'] || uaCC['no-cache']) return next(); // check freshness of entity if (isStale(hitCC, age) || isStale(uaCC, age)) return next(); // conditional GET support if (utils.conditionalGET(req)) { if (!utils.modified(req, res, header)) { header['content-length'] = 0; res.writeHead(304, header); return res.end(); } } // HEAD support if ('HEAD' == req.method) { header['content-length'] = 0; res.writeHead(200, header); return res.end(); } // respond with cache res.writeHead(200, header); // backpressure function write(i) { var buf = hit[i]; if (!buf) return res.end(); if (false === res.write(buf)) { res.once('drain', function(){ write(++i); }); } else { write(++i); } } return write(1); } next(); } }; /** * Check if cache item is stale * * @param {Object} cc * @param {Number} age * @return {Boolean} * @api private */ function isStale(cc, age) { return cc['max-age'] && cc['max-age'] <= age; }package/lib/middleware/vhost.js000644 000765 000024 00000002141 11657257013 016622 0ustar00tjstaff000000 000000 /*! * Connect - vhost * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Setup vhost for the given `hostname` and `server`. * * Examples: * * connect( * connect.vhost('foo.com', * connect.createServer(...middleware...) * ), * connect.vhost('bar.com', * connect.createServer(...middleware...) * ) * ); * * @param {String} hostname * @param {Server} server * @return {Function} * @api public */ module.exports = function vhost(hostname, server){ if (!hostname) throw new Error('vhost hostname required'); if (!server) throw new Error('vhost server required'); var regexp = new RegExp('^' + hostname.replace(/[*]/g, '(.*?)') + '$'); if (server.onvhost) server.onvhost(hostname); return function vhost(req, res, next){ if (!req.headers.host) return next(); var host = req.headers.host.split(':')[0]; if (req.subdomains = regexp.exec(host)) { req.subdomains = req.subdomains[0].split('.').slice(0, -1); server.emit("request", req, res); } else { next(); } }; }; package/lib/middleware/session/memory.js000644 000765 000024 00000004514 11657257013 020460 0ustar00tjstaff000000 000000 /*! * Connect - session - MemoryStore * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var Store = require('./store') , utils = require('../../utils') , Session = require('./session'); /** * Initialize a new `MemoryStore`. * * @api public */ var MemoryStore = module.exports = function MemoryStore() { this.sessions = {}; }; /** * Inherit from `Store.prototype`. */ MemoryStore.prototype.__proto__ = Store.prototype; /** * Attempt to fetch session by the given `sid`. * * @param {String} sid * @param {Function} fn * @api public */ MemoryStore.prototype.get = function(sid, fn){ var self = this; process.nextTick(function(){ var expires , sess = self.sessions[sid]; if (sess) { sess = JSON.parse(sess); expires = 'string' == typeof sess.cookie.expires ? new Date(sess.cookie.expires) : sess.cookie.expires; if (!expires || new Date < expires) { fn(null, sess); } else { self.destroy(sid, fn); } } else { fn(); } }); }; /** * Commit the given `sess` object associated with the given `sid`. * * @param {String} sid * @param {Session} sess * @param {Function} fn * @api public */ MemoryStore.prototype.set = function(sid, sess, fn){ var self = this; process.nextTick(function(){ self.sessions[sid] = JSON.stringify(sess); fn && fn(); }); }; /** * Destroy the session associated with the given `sid`. * * @param {String} sid * @api public */ MemoryStore.prototype.destroy = function(sid, fn){ var self = this; process.nextTick(function(){ delete self.sessions[sid]; fn && fn(); }); }; /** * Invoke the given callback `fn` with all active sessions. * * @param {Function} fn * @api public */ MemoryStore.prototype.all = function(fn){ var arr = [] , keys = Object.keys(this.sessions); for (var i = 0, len = keys.length; i < len; ++i) { arr.push(this.sessions[keys[i]]); } fn(null, arr); }; /** * Clear all sessions. * * @param {Function} fn * @api public */ MemoryStore.prototype.clear = function(fn){ this.sessions = {}; fn && fn(); }; /** * Fetch number of sessions. * * @param {Function} fn * @api public */ MemoryStore.prototype.length = function(fn){ fn(null, Object.keys(this.sessions).length); }; package/lib/middleware/session/cookie.js000644 000765 000024 00000003764 11657257013 020427 0ustar00tjstaff000000 000000 /*! * Connect - session - Cookie * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var utils = require('../../utils'); /** * Initialize a new `Cookie` with the given `options`. * * @param {Object} options * @api private */ var Cookie = module.exports = function Cookie(options) { this.path = '/'; this.httpOnly = true; this.maxAge = 14400000; if (options) utils.merge(this, options); this.originalMaxAge = undefined == this.originalMaxAge ? this.maxAge : this.originalMaxAge; }; /** * Prototype. */ Cookie.prototype = { /** * Set expires `date`. * * @param {Date} date * @api public */ set expires(date) { this._expires = date; this.originalMaxAge = this.maxAge; }, /** * Get expires `date`. * * @return {Date} * @api public */ get expires() { return this._expires; }, /** * Set expires via max-age in `ms`. * * @param {Number} ms * @api public */ set maxAge(ms) { this.expires = 'number' == typeof ms ? new Date(Date.now() + ms) : ms; }, /** * Get expires max-age in `ms`. * * @return {Number} * @api public */ get maxAge() { return this.expires instanceof Date ? this.expires.valueOf() - Date.now() : this.expires; }, /** * Return cookie data object. * * @return {Object} * @api private */ get data() { return { originalMaxAge: this.originalMaxAge , expires: this._expires , secure: this.secure , httpOnly: this.httpOnly , domain: this.domain , path: this.path } }, /** * Return a serialized cookie string. * * @return {String} * @api public */ serialize: function(name, val){ return utils.serializeCookie(name, val, this.data); }, /** * Return JSON representation of this cookie. * * @return {Object} * @api private */ toJSON: function(){ return this.data; } }; package/lib/middleware/session/session.js000644 000765 000024 00000005351 11657257013 020633 0ustar00tjstaff000000 000000 /*! * Connect - session - Session * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var utils = require('../../utils') , Cookie = require('./cookie'); /** * Create a new `Session` with the given request and `data`. * * @param {IncomingRequest} req * @param {Object} data * @api private */ var Session = module.exports = function Session(req, data) { Object.defineProperty(this, 'req', { value: req }); Object.defineProperty(this, 'id', { value: req.sessionID }); if ('object' == typeof data) { utils.merge(this, data); } else { this.lastAccess = Date.now(); } }; /** * Update `.lastAccess` timestamp, * and reset `.cookie.maxAge` to prevent * the cookie from expiring when the * session is still active. * * @return {Session} for chaining * @api public */ Session.prototype.touch = function(){ return this .resetLastAccess() .resetMaxAge(); }; /** * Update `.lastAccess` timestamp. * * @return {Session} for chaining * @api public */ Session.prototype.resetLastAccess = function(){ this.lastAccess = Date.now(); return this; }; /** * Reset `.maxAge` to `.originalMaxAge`. * * @return {Session} for chaining * @api public */ Session.prototype.resetMaxAge = function(){ this.cookie.maxAge = this.cookie.originalMaxAge; return this; }; /** * Save the session data with optional callback `fn(err)`. * * @param {Function} fn * @return {Session} for chaining * @api public */ Session.prototype.save = function(fn){ this.req.sessionStore.set(this.id, this, fn || function(){}); return this; }; /** * Re-loads the session data _without_ altering * the maxAge or lastAccess properties. Invokes the * callback `fn(err)`, after which time if no exception * has occurred the `req.session` property will be * a new `Session` object, although representing the * same session. * * @param {Function} fn * @return {Session} for chaining * @api public */ Session.prototype.reload = function(fn){ var req = this.req , store = this.req.sessionStore; store.get(this.id, function(err, sess){ if (err) return fn(err); if (!sess) return fn(new Error('failed to load session')); store.createSession(req, sess); fn(); }); return this; }; /** * Destroy `this` session. * * @param {Function} fn * @return {Session} for chaining * @api public */ Session.prototype.destroy = function(fn){ delete this.req.session; this.req.sessionStore.destroy(this.id, fn); return this; }; /** * Regenerate this request's session. * * @param {Function} fn * @return {Session} for chaining * @api public */ Session.prototype.regenerate = function(fn){ this.req.sessionStore.regenerate(this.req, fn); return this; }; package/lib/middleware/session/store.js000644 000765 000024 00000003513 11657257013 020302 0ustar00tjstaff000000 000000 /*! * Connect - session - Store * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var EventEmitter = require('events').EventEmitter , Session = require('./session') , Cookie = require('./cookie') , utils = require('../../utils'); /** * Initialize abstract `Store`. * * @api private */ var Store = module.exports = function Store(options){}; /** * Inherit from `EventEmitter.prototype`. */ Store.prototype.__proto__ = EventEmitter.prototype; /** * Re-generate the given requests's session. * * @param {IncomingRequest} req * @return {Function} fn * @api public */ Store.prototype.regenerate = function(req, fn){ var self = this; this.destroy(req.sessionID, function(err){ self.generate(req); fn(err); }); }; /** * Load a `Session` instance via the given `sid` * and invoke the callback `fn(err, sess)`. * * @param {String} sid * @param {Function} fn * @api public */ Store.prototype.load = function(sid, fn){ var self = this; this.get(sid, function(err, sess){ if (err) return fn(err); if (!sess) return fn(); var req = { sessionID: sid, sessionStore: self }; sess = self.createSession(req, sess, false); fn(null, sess); }); }; /** * Create session from JSON `sess` data. * * @param {IncomingRequest} req * @param {Object} sess * @return {Session} * @api private */ Store.prototype.createSession = function(req, sess, update){ var expires = sess.cookie.expires , orig = sess.cookie.originalMaxAge , update = null == update ? true : false; sess.cookie = new Cookie(sess.cookie); if ('string' == typeof expires) sess.cookie.expires = new Date(expires); sess.cookie.originalMaxAge = orig; req.session = new Session(req, sess); if (update) req.session.resetLastAccess(); return req.session; };package/lib/public/error.html000644 000765 000024 00000000356 11657257013 016307 0ustar00tjstaff000000 000000 {error}

    {title}

    500 {error}

      {stack}
    package/lib/public/directory.html000644 000765 000024 00000003560 11657257013 017162 0ustar00tjstaff000000 000000 listing directory {directory}

    {linked-path}

    {files}
    package/lib/public/favicon.ico000644 000765 000024 00000002576 11657257013 016417 0ustar00tjstaff000000 000000 h( }@_UC˩~6Gcc<ep›lu4ơu            package/lib/public/style.css000644 000765 000024 00000005247 11657257013 016146 0ustar00tjstaff000000 000000 body { margin: 0; padding: 80px 100px; font: 13px "Helvetica Neue", "Lucida Grande", "Arial"; background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9)); background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9); background-repeat: no-repeat; color: #555; -webkit-font-smoothing: antialiased; } h1, h2, h3 { margin: 0; font-size: 22px; color: #343434; } h1 em, h2 em { padding: 0 5px; font-weight: normal; } h1 { font-size: 60px; } h2 { margin-top: 10px; } h3 { margin: 5px 0 10px 0; padding-bottom: 5px; border-bottom: 1px solid #eee; font-size: 18px; } ul { margin: 0; padding: 0; } ul li { margin: 5px 0; padding: 3px 8px; list-style: none; } ul li:hover { cursor: pointer; color: #2e2e2e; } ul li .path { padding-left: 5px; font-weight: bold; } ul li .line { padding-right: 5px; font-style: italic; } ul li:first-child .path { padding-left: 0; } p { line-height: 1.5; } a { color: #555; text-decoration: none; } a:hover { color: #303030; } #stacktrace { margin-top: 15px; } .directory h1 { margin-bottom: 15px; font-size: 18px; } ul#files { width: 100%; height: 500px; } ul#files li { padding: 0; } ul#files li img { position: absolute; top: 5px; left: 5px; } ul#files li a { position: relative; display: block; margin: 1px; width: 30%; height: 25px; line-height: 25px; text-indent: 8px; float: left; border: 1px solid transparent; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; overflow: hidden; text-overflow: ellipsis; } ul#files li a.icon { text-indent: 25px; } ul#files li a:focus, ul#files li a:hover { outline: none; background: rgba(255,255,255,0.65); border: 1px solid #ececec; } ul#files li a.highlight { -webkit-transition: background .4s ease-in-out; background: #ffff4f; border-color: #E9DC51; } #search { display: block; position: fixed; top: 20px; right: 20px; width: 90px; -webkit-transition: width ease 0.2s, opacity ease 0.4s; -moz-transition: width ease 0.2s, opacity ease 0.4s; -webkit-border-radius: 32px; -moz-border-radius: 32px; -webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); -moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); -webkit-font-smoothing: antialiased; text-align: left; font: 13px "Helvetica Neue", Arial, sans-serif; padding: 4px 10px; border: none; background: transparent; margin-bottom: 0; outline: none; opacity: 0.7; color: #888; } #search:focus { width: 120px; opacity: 1.0; } package/lib/public/icons/page.png000755 000765 000024 00000001173 11657257013 017026 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe< IDATs>{SG3;1Ј.bc#61lIAKD"$X$ TDyUIޥwO69DBכY}o*>'o{t:& ﱋoGgpfS=3Og~jUiִe[ǫ;kҏxOT$4ls1 w}؝>``ʨ2*c*u˽=v̝* D)ke{Oy,0CB0(4;.ݓ3*J 4 PC%# 0C/$4 I2O!E6k3tGBT%`YCam)6Ӱl0C/щ*ER4\g ˲ZW7{谆$$|ṵu/ٮ+Jt(ʅt% }6q2=V}.vDžw}z᷶/ bPfvf`džY:LI@A JD(JΜ#w o^7=  BFh}RBdq\ֆX=)jCL5QB ZBP!AD6&:@ч,!dLP Z]ABP$)H ~bԜ/G&FdHВ:}soUkZq7+qj^a(d4'?/g:x#m;+ǽgvf}7->hgߞ1+qkKENݯ_4K'S5 XcZ*JJ#~0[@jӲ^~18--&;T߶řlw^~p=,+2tD] "(669]f߅I@A JD(JwɆ^߹V7=  BFh}RBd4jcS1Xe }O$hɍ#N}ྦྷ3K?}bB8L A##=}-`xU6o_ qu]ђ>郖[6༮ akrԋoZ%Wλ1G_4\::]*,/_w4E+;}V IL}oG)g$[?/`/} -%nQP`IENDB`package/lib/public/icons/page_edit.png000755 000765 000024 00000001447 11657257013 020037 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATOhuyw9ge&"2&A:uVt.AA]9롢H낆 Ҋ?/}_'֏ jC E#m*Ưi)LYUھ}ظxq`u#RkRJj4$! dD!iOn9C/im&N{ A DJD)u\~'ו:nһaK A I"%wyĖU־zRWO؄wRD(SHIF^5{/rYAib`=vVS"eնizu[{s^?y׳-g Dlb BDPA6[P ((AP"%Bi_^ݙ:Z4|^D㻏N3',_fGf}'rs(3Ǐl'(sw|i|0"6\ښ)kZeMYk5Kmdw+ROIENDB`package/lib/public/icons/page_excel.png000755 000765 000024 00000001461 11657257013 020206 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATMUU{ S# r -MRTڄK EaBD!ѢH!jM dI ecyw9}_9[,G?ȁ)v|8\|؞!w&}%1YB g.^w⊿Mw{OA b19䤮N™VQ:.xWv!9碯H("E dp36(>ԎgS5Be㏻|o~+UФںeKZUݝ齓WU%N[;?~S'S۲njvG2j|ӎ4mm-5Dl yаEwSA^nw\[ּoxhD_EBM'{_Ld`ݢ kwZ2+ 2Jn.]Mq璻M&㐲*2e5&7nzZjrGGTW?VK&y p!mo~r`@Tß_C*Rd uuzp;0V'"cfkD@@x 8Ԛ{yшˡܪowFw_w./,7_t?=S ϋ)fʊE~jήO::5I`.;þX;\fY])V.T (xg8`O­ U|v *`hwLs; k'M}9CP ^ (( 08 w\OE5 BIp>Yt@#tDBK,b+[ޅ*Ϝk"򤢭((((”4LIENDB`package/lib/public/icons/page_gear.png000755 000765 000024 00000001501 11657257013 020017 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATMhe~|ϗHF!E^ "<:tAt)."#{@"R+Tmn9=>`E^t!HD.XN7owtY^_amiqP " \5qCoOuLO߆r謫rJ$Dn3-_˵(K|v҂:#S'LH,X=0^4+$ȉ\%WOku}ɛO.UuT/C A]$9+:#g,̖RfmgT6ANY(DM]s A H@ы&G.XQ]:Zȡ6-J 0qի4}VeŊç ܫ՞={zj(%DA=}588ۺuLMMͫOv:)4ABS8eRrYvo+˖-޽{%‡JBI옘PU~VG)ZI.]ry5ۍa[Y /|!k֬oKfJcccҪ}v)1H\nB+]khh133… z{{UanYݱƗ:por 6=94,76)"luhW{.v FFFz}r"s/0Wjk;vUIENDB`package/lib/public/icons/page_go.png000755 000765 000024 00000001413 11657257013 017510 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATKVeΙGm$d7fe"EIѢUPmMv"ڹ+6.v]2»68fޞ'2Ӷw>>1J 1d+y M}xώ@d>8z77-%' 䍡'tfȞsBiѶYۿA>K3n:9?SϾ?|~@D,f)F m "P㩕lX $%(JD(Mߌ{=4|vH2 !d'% YSc-I"DJ6QJ 6R! (D(J0҄8ώn6@ڑIE"3JDh溡UJ_jMIv+Π\\}A }OR]kګ֭҅`O{hvfz灐j:}/а6w1?Gf[^߳rbĆEM\;&^3.\٣y:gZZsЋc{5ҭnc l7fZOGd|rS??p`Yκ,/`x ^{(&Orv<-Doz)"H9@HsS_]}rm aS/IENDB`package/lib/public/icons/page_green.png000755 000765 000024 00000001155 11657257013 020206 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT?s~~TI!!bF ,H8b1a5l NQB b`^8MA~y*޿\)(0kJiΆIH q~{СMкOgԪ(2X2s,}O>};x3o}~Wtܠ$SjbjLEMa8y޿ww>JҪ*m*u=g6=W|vjz :@B(%UZ4\1mfj"jAƚ6hT*J VZJZ $@10PHZ(} )Be  @)zcԊe 02FHd#W?9Wi!Gc{sގl6IENDB`package/lib/public/icons/page_key.png000755 000765 000024 00000001441 11657257013 017674 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT_swΙ2-!I(vaŅ\p"nȕ;qjf$!q眝>NYoN=0|i\ZsЉo:5c>~'k ?~K=ܷsp;,{,F4SybFoݓ^~~ЍmqCʅmy}#sthno?N#=ƙ?IENDB`package/lib/public/icons/page_lightning.png000755 000765 000024 00000001507 11657257013 021072 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATKhU9ߙ[&IzPR5 Ġ.Z+fƅFp!" BwݹЅp!`ml%hSm &:|3}' 'Zr;&*pȈf }붷޺xf$.~63ynH!ڝٟ[,vtf|!};dd("qbk}^zW84-Ɗd(%,D8rljrR\4l&LJ}/}ݨ^N5Xe7˛9cR`##\<赬Tv|KN/~rḲ6R >GJ8a .[&07|Y?K6F+Qtp}Χ[|U{ @_W#2Qt"`Πk!9!&p;9Bp\}ѫK $aNі$N? =;"@]0?c|5$#IENDB`package/lib/public/icons/page_link.png000755 000765 000024 00000001476 11657257013 020051 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT_he}o;ͣ-cZFTV$D"QWfPB$hn $`EA $DНT7e )ѝMIlsyRD/ 6vd-@ [?7Ǐ鿙wܱ"rڇsN˂JlW4u}믳'hC Jjٹ(vNϓ,ڢ(͌OigH)-2uTv[`ddDNINININI֍V-xd8y;U} PDPW$I}=,Rl!չ 5DSB $! Qe%)2))$@*@R/ "J "䌒U $B5"`}۹?x!SW[NK;|]C~14;X۬W#ֈY/mnn˗6UוVj(P"1]Ҟ]sN{ ZWu))ׯ7iK.l^o~c׮]6l`۶mnV7K6jKytuth\z5M077'bʕ+5t{jk_|v4O|ԋci .h4VZeddDĉSɩao'X^^},KF3s;ݴ(,?)n/Vkz̖0F[v`IENDB`package/lib/public/icons/page_paintbrush.png000755 000765 000024 00000001455 11657257013 021270 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATKeZYL ( Z).$"ZDTAD(Tavݴ215o3x9?ĝ>d%lBdUWSqڱm}OG/%% ҉v~wg/ohKkGfٯ5% +ì.] ݾb^Xuԛ/޼vX2{rr2i^ BDP%#~ v$(RJD dS]y6>?KGO̵ο,.W1޶;d"]wWEuVei-Cp-^}1[|nm{hg?67[1 ila}>:2΍鏍uivC;?Ҥ[6OD:ƹM/&}8\vN^|b:bq.k-RX$bq\7My| CgsQAT}%E$;z+ &M&j#EdLE)E&ԱjcDKCGW:.0a@f UM&kHТb(\ޞNOڷ*kNT x/?ݍp,2JQ Pwwrnܢ AQJdYZ?5I83IENDB`package/lib/public/icons/page_red.png000755 000765 000024 00000001201 11657257013 017650 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATs>מ*v"6iD @"DLb5Dh2HDDIY$⥪ ;ހ:dNf0`* Աrvc€P00QeTUF1鞣9Q^}NfzfyT52Ol3t5{ѽy,0CB0(4[FI jFA֒$` t"DY# !@R*̃ 0ڬ!M6AiX0C/щo"W*Ҁ |` ˲ZWw{4*% *qpk3,{Kw׍o -*5bG/k/0t_od }g ;Jow]W*?!y!!IENDB`package/lib/public/icons/page_refresh.png000755 000765 000024 00000001532 11657257013 020543 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATmheu?9|b*VI+R$he!A!AeSB?D-!}r$sJZf6u;<}]~ǽoJu۝X)Au|;3SyrѝuN  cL^UYR>^.>i|ĉ)DSB0Yx3S<{Zp"̒|9D@(Jy+gmsK#@N'%¿U,̘N&%x3 Ѹ1\0 ia5Y~ /0`6︯~ /mGMQT*n/\ifo" `Y &%KMWCdJg0 03\bN-gN{)nMW GX𪆙aXw3Pポl[+9'9ȹOW|TAS_q:v\O `/ #!M |4Yxi-۷ȟ!DbU; {K6-X:E"W+_!T-{! ,BW׵݋7dپִhfmwܙ+"h!E}ȓ``&19ky&w%ńevqIENDB`package/lib/public/icons/page_save.png000755 000765 000024 00000001406 11657257013 020043 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATMVe78bDfai*QT@h妍P;]B@hQTP6Rd%XAT(T0sxM(a6/AV]-_cgNʞ| #[/?_8s|qmibGm)%deUץOl]z^té̦m "PmYܻٶ'׵s3@I JD(J.][s} ÷h&L8)me4ZkR6DE'.X?갳a+k>+4;uʚ/~{=ov:_ >rIqԎL*2V#nt+"֜LI)IFءjMFn VnaG5^n(#e?y?Wu\%2dzjnl-[6әF:;=2?9oՑUXU/F/ˮIENDB`package/lib/public/icons/page_white.png000755 000765 000024 00000000446 11657257013 020230 0ustar00tjstaff000000 000000 PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(u[ E&}O(@_֖9a.і?v9\ bBWҴJ/\Y+DIO)n,T(pbeF8D`ñ_9υN8s]蔊d!4i/&~b%BB *t!ЕBiU| Qd&d+3D iνs9U0Oƨq es]>G99|>BIuE  ÀT $q( &]05wLӄiHӈb\d:X`6߸TUE</)X@;mknD"3}@3GH(,˕+!::? C=!QE8/0/a fyHBؒP/HvAmn)+/ 8ĿB"nƆ@Z[)j~Uh^N3QOR6]n^SݑI1Oc c4esKxCA fw5>VD{; XL" uS 2dƻ07>,D<G6iBFYUs0uOcR dB8V(}X u WmY$Hn1) t|?>76'@XG"r9-y8]"L?y[ӿحYQIENDB`package/lib/public/icons/page_white_add.png000755 000765 000024 00000001000 11657257013 021023 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕KP[8V"Apj[JS,889U`(ZRǜKSӷpr/wk`PkRYeUѣ+J^7 >,4E(R.C&A<X,I ,#P.QVJ dY$ (JKthD |dkf&6:Fk96J*Ȳ)=0t:q A#Z&r!.Bo8 ذwٛ4_q`ST»ʼn-_Z?^D(z }=~H@ a\a0m4e5eE0<#̕"&`AGya{4)l}O/!&RIENDB`package/lib/public/icons/page_white_c.png000755 000765 000024 00000001113 11657257013 020522 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍKK` I]t^RYU8LV7*ځʠ0^tJEQT*RIS&~S/\'+PBaTA9㣓s TUEP/OJ%b18N8DQ P5GnGXD B(_-W|@لjE\bt<(~|}ܝBXqVx!! R<eɡf÷.0f9.D.d,]]@r4v邛S=P4 zO48J ܢZJ!'rrg}鬪;2QTh4Ղs^WV# K@NSj3^f<H5sƎ *'麾1]PsVY9]<@IENDB`package/lib/public/icons/page_white_cd.png000755 000765 000024 00000001232 11657257013 020670 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<,IDAT8ˍ_kPg?dGJ:bE/du@ .ZA7 ԭеf]6iҦi=?9ڬ <9yw ^)p rC q4^D"sÒa$ 4:6$Izh4H $f6V>O+xiB(vidY˲6b%;v#B6}T$HeEZfyH'4<ֈnI@&px}Y7QEᕈx*TEviPCՃf#:"]y?TU=,+|BbGEbro5 ӱ 0Q Xxߨ~ٚWPu&O3<r #̭EG"|g.6\l-x2 V/H@5tFGs5^[.>O/@86-*:/E@0  ~+g"n-dqc3s̝GqFnC9A?\l.]D\cn?/ 8FNq$ɷAIP0Hɦi²,0iDQdAJ|mTU&2t]$ILPT@IMIP[5AJ(Je%6O(~0[dāa(*yUtOлM̾_ ^I P%4UUoVNcc_E{v_B|NuXתA"*kfB"_0-K?6љc.oal7.`Z:g|)bbܬ*āG å2Da.a2A$tCʘ?IENDB`package/lib/public/icons/page_white_code_red.png000755 000765 000024 00000001113 11657257013 022044 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍOkAk//9ւG/"!GE Ԇ4M[ҮD!@??='%n6I97&.<,̻ggޙ0GdER%: 6Gf!h6@ }xn$ijiIC "qބ1X~v=dCp (9mSKR=I AuA@z)㨙t:INjSX h@ fBp]yKo$*&K*h/C=!5_=Fo8ܶo'^޼W+a %!v泈{pMW?33eJG\:=8+˝ U$58j{$QPxJ@|~z_U_SEw8\2%Hҿ9n3ւ?lPHۄBތ`$)t~{Ff:IENDB`package/lib/public/icons/page_white_coldfusion.png000755 000765 000024 00000001120 11657257013 022443 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍ_OPo?}"Je&^xO h ?WqdR0BvvsԕnM~iӞwz `E +Cr!eY8=G<7 Pՠ*J $@V%ό4 rY &xd2Kn(K^cIn-#׊jUV`x~ nO+'.+ GZLf?}Y(y,{0?h3}㹋b^_ ~n7/!0 V&4|.:&dɔ[#B?X%N5[å@gTP|{±FL۶%ea(Y1*xFU7 vqs4hʳ)YG)ZE}/ܣ ~p0 Xr܆?V^֫TIENDB`package/lib/public/icons/page_white_compressed.png000755 000765 000024 00000001324 11657257013 022450 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<fIDAT8˅MKQ̼3NNba`TB65M_jS֢HD~V̌;眧699p澹g""a r8r$9'8edd hiMWUjT*R*d~~^s2;;+ gP<(ӛf拉Š5@ZMkn) B`ll5ޑ:_Z $hBCC(MfXk\>*tv33G͓/39v >!2eB8\($Yazeӵ @&JQ`ZceEl\&- 81hQJ67f7wn  u[^?1֓5BPJ`iVP+~|&V:͙k1sK]>#JXm\_MeKsf\O?Ż:|5K*DMrp{*KQkΉxzɗxiWz/M0YYZZTٜbN: Τ#6 /ֵH^0dA#~9NXF~{TB|v9IENDB`package/lib/public/icons/page_white_copy.png000755 000765 000024 00000000465 11657257013 021263 0ustar00tjstaff000000 000000 PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(uMn0E9GBꂫtP kBH$4]8&瑟J&ѨF%@ds3W$q&гݨyFz\MMIENDB`package/lib/public/icons/page_white_cplusplus.png000755 000765 000024 00000001155 11657257013 022340 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍkQū]w%R.ZV (HWJەJQqFnHSBmPA7 EII$wdFځüown0vAq\ j6[V !$$ Zbٶ 4*Ȳ QcHt}a$uaY4MzH6 !C2s*2s fjq8]wX59q}=5QP  RqZ^}GV T*6(峋]z?.h}۩QVۀY7YCƓ ʴ3\7xMR x!copBpcrWIvpTJ3@ɼsX^sxC"K1ѫ+8s|)0S7q^˫6Yu6^;Xlv:lWW{sE[10+G{ fg]> @/A0Αh{=ҧIENDB`package/lib/public/icons/page_white_csharp.png000755 000765 000024 00000001274 11657257013 021570 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<NIDAT8ˍkAƫ@"G_,>X, 5Rx Mچ4i4!RTkђ ňmsivlS?g6&3f9sQHE4%)ZVh6ۭV DaXLJ0@͒$\.wU,!2 I` wQ=PVQ噪T*P(8PT@/Zm nyL9re5QwWQ ,[99nn=79ؗ1琀20¸8_#m6c,j|zo ZgcOMGqv23Lȳr8xb2M#Mϲ08&0j p.[0"CLa!L0V>m; ,JOG#:dgfqн98zcC3Õ5&A(a%f,f0\,' e<j/LEQW Wmm,Hɼ N# U\&f5F!@#_/bH -e/~A/\;1%{~ A;wD['V~.mIENDB`package/lib/public/icons/page_white_cup.png000755 000765 000024 00000001177 11657257013 021101 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍkSQk .Wq:΍݋\!X%Ym7A.B"Đ@@9y!SyϱyMb|ws{{7l.lrݚ`k:rEyh4wu6cH$3 h4XRՠje  jM4E$A8F0d4" & hNGb2|l6zjKU*q\..}-_?o3 >,i6Vgшp>G.çOm5~}Byt:3f9'L\ەinh4eIRÁrZ.C"q*b(Db/N~ /J铠nl6|6q@RŽL& ڥHB$z=*>'5RyWb1hZ, Bp:Z! NOBKXVgd h >=Yp*%h2#kM\:{F`kIENDB`package/lib/public/icons/page_white_database.png000755 000765 000024 00000001103 11657257013 022043 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍj"AMbV 22/bP$jlu!q'FkN.\ qƺd]GSUg\Vvv;LNNb\xXrTN4 RnqT$e2u!sL&` [zш5 )h4H$`6`0Izŵ ojPi&(p'N|'mjZT(  Є  kal6eR0l4!ptipgj*R$*ff<Fc:fsyx^ :߱Q%Xr,!@(>Oqf tׇ|p;*Ji vqD"sNh:x/xO_OW7On )sy9 xΦ_K<IENDB`package/lib/public/icons/page_white_delete.png000755 000765 000024 00000001030 11657257013 021540 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕;KBaգnBCMMBh("Zڂ38PC.(8TK 4u∷=^#~y Qa$CCUM`$)Y**r`rZ%Hl6 QR)$ aY |!ϣP(O" L"Ȃ\.:v˒6H@t:h4Rm&&kީoX,&W"BwA+/hP;4 $ Ţ< ['^f2~^PH!9Ǜ݄)^׻lV|fN_: iCϾ4fmQP6qd/*2[|VG sl"2}ݜ 9 GdO 56mkIz]˥a T _#>U)ےIENDB`package/lib/public/icons/page_white_dvd.png000755 000765 000024 00000001176 11657257013 021066 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍn@EKJCQo<%A  U%R0uT!DI\mdlǹ߯f΀ME~g:X!6tk=.9[Z,1 \u:Z-TU1M>υ8[|T*!N#L"Buz=mضTr9O&yJfo$Sр$It:婜.m$N80,3|!Db( Fa78vcq5jױ_ %D?(z{/=xq\B.Ç!d-@ 6#^8rΏ`6 ,48݃kt~AZۻX% #?y"CupXIENDB`package/lib/public/icons/page_white_edit.png000755 000765 000024 00000001152 11657257013 021230 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍKSaWEDBԅЍ}a? P3B ]t +$aA IX^ns\[sKp;;gɦps6qt w`԰U]|^)JrL+:Nsb&\.u,Ld2iIYBӗ)v>)iR(D4E!J߰EYyZ,ǽM!a*?fH>>ƧG9Q7&h޳|}+ n $jcM}w+U;}92XͶ$jxw?Y=“g~Z\-32L>vEdi/ e{;^D tT߈]v5Jh.Az{G߀WN_}$GbBpIij&{-aI?@>L'n "$>Z ?zL^IENDB`package/lib/public/icons/page_white_excel.png000755 000765 000024 00000001227 11657257013 021406 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<)IDAT8ˍOHTQ;D:L&DD)-7AQZb3c8o޻6T|;)@)H>WP E5ȿv4QR$rY^^E1‚!jpWֲ9'u8'8+Xpaq$a#ˍ epW6R) d\/-$4E%Ȥq"91R)֎&-JC4cyZZʋ~>|4¼Мf5W Š}hb0|3Y=jK<~ ~TT$՚F021LW'$d0(}m+gHliZÞ>Ͼ珄Q "(p5 }iap!i|gVSMƘbsrZ^P8kmp֮VV]:{L`/G*_>IENDB`package/lib/public/icons/page_white_find.png000755 000765 000024 00000001244 11657257013 021225 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<6IDAT8ˍkaW@%t\# &jPɉlkhkE$XZl:2X#0Z/v]=zgh/\/sx$d3I]L_ @\VFcl9^VH+@JbR$(! sH, pzZG5e~`px CKT*( fH$P.AEo3nqϺf@s?AΈsx4y$FLL[alb #ZLҚ`w=[h0J*,//$CS:枾qt!4?eB 2.qKLp,Fl<NzZCH`U}mCx lo?&Hԍ0Xyu8䪣_ nö;: /@RQ5 !+L&AN4a6oamPP,%0 Pl6{i>8vn* r T*If paμ_[13 \.Nr=am'HBPJ==9z/bBk&AmO #a͔7826Qe sX@]Ȁ|ės4}"UDOށ~y-BW {iJlx&U@?oqQ6>ǘ]MnMGsSv/IENDB`package/lib/public/icons/page_white_freehand.png000755 000765 000024 00000001177 11657257013 022066 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍkSaƛ8bAcJE':tp!KEpiC6iC6چބ4779Mz sxbM,v}`aY:<;' sلiuj5e)JzH|'T*5IX<l7([X E'y4x-ɣZh%>r_s wG]C@d0hLKW1x@ӠD9"N49pd< )M+$EY5Y;_!oƐ\HIENDB`package/lib/public/icons/page_white_get.png000755 000765 000024 00000001004 11657257013 021056 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕MKQM!Z M [.PladdFqѢ@ ms5?0ssaka`0%0Xl4f# %Hj ۔J% iYJ :$$ Z $ORX,"A@D"Y# (P.f{ZjD!`m7;JdA7j UB jfB z.υ$ĿT C] (@Np>]P8c6 #֍+ޥ/>?>/'nqgջ7lQ+LGՂAsXzߋkf껰Ȇij}/bg<7G!Z#ɠlrs`%Co4tQЛ1IENDB`package/lib/public/icons/page_white_go.png000755 000765 000024 00000001144 11657257013 020711 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕KQ(RJ}B.FSkJ7-"RQUPFQHP4UĂTJqBJB^s֯ h1so0|ܠTXPD"o<?J$(9z,T !I|><vl6 S-!#WJ P(zhsB~8s-%8@w%jFI{Hj-8Pb\kD="#XrѿK:-?JǗs+zڍ>viQ|,;-=|z;֚1s[x w߱DZeoqoq5fnC}RX <-A.ӷQ5W @Y2dU,OiI1v M(5y@( lYPR49T}vsIENDB`package/lib/public/icons/page_white_h.png000755 000765 000024 00000001133 11657257013 020531 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍKKPǵ|R7B7nD0BR:  tDG) υ}hT&1ɤyg{n%H =w9pid41!ȋJUVk`$i(* y|ض 4*(L&#$ar= (X,\.7.,˂i\l6W ZRRj- /$8t]I(xb$5i1Ëxa+BW"m}m= s;&fs&L,$T 0x_Y /iZ+@ZE%̈́DF_yW*>S{'.,VH/H/t&5.}fް/ "P%<";8ZZ$#L>hn2 ˲4 JBT*AmOa`T*9ht:CvduryW;hzAUUȲЬ0D~fzyx2q쾑_s(e'`kPx|~qQp8$X64Z联07?a篃ub? KХ!S7aaw\Wl9N4ogcXI˽~޽͗!ayj <׏}Ƚk0V#O Gg cLkpXyݳ^=t$\ݹ[SG'b'n5UMu+IENDB`package/lib/public/icons/page_white_lightning.png000755 000765 000024 00000001235 11657257013 022270 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe</IDAT8ˍkQ;tWh+A[>t& 5*&BPEiAX-Z[-i4iiLf&yf?zijZ}s.@|[U mΕJr %Ղ&6 i: EQ <`0H*u;ڳӐ8/>>B XZ$ PA _\OD'd P (O BMKl&Tɳq6c 9?i/f(>Idqܒ@߿13v}h0~v!jr^Qil,/ Q+qhs~wM0?8q awqDr3L> =#[P^ߜ@jG!N"o(lGwurT 74r|6Z) Ҳ!gbyǐxh R"Wڐ|qT= WS#_1eo]Ku)K#+Edҋɿ:*A=a+կ _ZMHyBIENDB`package/lib/public/icons/page_white_link.png000755 000765 000024 00000001146 11657257013 021243 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕;bA Y5DQl3ED1QEM|  тo~gl8v%$l|`zdz>1IFr:+l6p8~T*uqGڰn\.!Fl6O2 X`X`<ߴԷd2'z%NB~L\B@  AH˕Jh\HN{ k2 ,8.:_CfczVp8 łD"AnpbB@kZTUZ-4  pp:Vh`4hN1&jo3syz|lhP.Q,NrxbR]vJ_d2Wv̞l8T*\.wbt?JOB >IENDB`package/lib/public/icons/page_white_magnify.png000755 000765 000024 00000001052 11657257013 021734 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍIK@C7/= ŋIm\ E*q[A\jU64S:_4U1 dg2..1TR!gi n{\b ):cr9d2qK( <7 0P('i,%E^%! (*JgRM j|NwmWIRNdY[PW #N;fP 5&1_/L}~`7 ]cVi3$QA4)°+E8! _j8C=L=pUU^X}Lwr }u=RY8yƂ8]cw!6iMwֶwA}LL6xG)>_o$qp-7Z7ZIENDB`package/lib/public/icons/page_white_medal.png000755 000765 000024 00000001302 11657257013 021362 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<TIDAT8˅KTQ?8O3J@Z[7]HX B~mF A@EBE.*(yj8E{| O (z{"z7u8H=­fx qG^(6pΑ)B-VVV/ 2~rF8)t^BJ!a233sssKk*1G:HbMR7uR,J)-L&C.cvvv֘Q9x!ω>iVRA҃U5u")ym}/.::;fdit8[uwZ[)R T3@f"sÏOoɏ^myhY0XE\w)]:z ]m|R ΋6N$IZ)>tZ6.0ܨ~XXҡ3IENDB`package/lib/public/icons/page_white_office.png000755 000765 000024 00000001413 11657257013 021536 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍKSQGg$TCaYQhEHY eIŌ( !)Ji~1  _p]~1uv{wv݋~;^.|~:rPr)y`+eS.Mi8=dJlgKy1WuĦ,!# BEx^&yIa2s;O*~ PU@$B!ߒoDzr\>~! ^FZD#D72gcV~YB B|ܢe/.Yf@;'&:m<»+*#ƽx NȓbȽ-PF:y_pCSDIfȲ(-ㅙwi!QcBd&"|ªBI 4q: b(0Ua-y OU- >RJ4ȟaoKd5 J;y2zo{ h WDa.u6FfP`ea7W؝.@&w<:OjntuN~a(=+h  D@?5&H[}4~eJ/wi ]@__ 8O pIENDB`package/lib/public/icons/page_white_paint.png000755 000765 000024 00000001260 11657257013 021416 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<BIDAT8ˍOA/8&b&('ś1$41PK($DiP)VmR i~l62s=3ym6mAA8`t]VTB5}8NG5 ]4,2EE<#JrLnopon MӐ硨E$It zþ٫11YApBNXde\.L&c `>{(Qc~S0'[BnDy iF}]uLKxCSQ "肞}ԗ xҾPg5,Xj,p")W$@&K :0D$ OayW.M;' bDp8ES~|8$a0/*mav_C_Yx ӈ=)Hcxn=\[L"ŮP(Pn?@k.u>CK(ZlfZ_ [4S9 G_Ѝ턍JA#lco, ~Nč3IENDB`package/lib/public/icons/page_white_paintbrush.png000755 000765 000024 00000001152 11657257013 022462 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍkQ.qEǪuƝ+7Ս.,؅E[(XhUS@P/DH(}aEJؚF8ɴ${.$1sf F?cTՔdb|P(lvVJBdhh4 EQDe8.&Z+Ёt:l6˯D"@,C8T*z.iÊn (l@ qD"Oъ:xWk3 SF)"t q٨؁:tO])b*02GsPī޳;OME`!;E\p˝(8TAZоfMkD8 ˞)x>*vo_֗n,@(q5f]=(;^Ĩg&}>y(r5<Ρ7@׃%{}SxߧIlX@~!/\bfIENDB`package/lib/public/icons/page_white_paste.png000755 000765 000024 00000001154 11657257013 021421 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8u;hTQ{qF%ш ]*k_r]!IJ+!A X)6.I@Hv̙#Ks3qf7ƣhpBnrzGkt۟'xl-71u4W̌`f,^g7ocKi~ *47`m85{!1Hs 94`"L̜E`hwvZ=ι`40UT=V{k!h<[YY;vscDqADޓ)Je5cjZ}"T<&  isEQ I*0cA1QT EQ?ONNt!`YUE%`jX#w"2}D1 -d3!A=zgPXυ-NτBɷozxa{{;K'B]89pd0sa,IENDB`package/lib/public/icons/page_white_php.png000755 000765 000024 00000001032 11657257013 021067 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍKKQ [զ}mJ"&zl4EiE$nfԔytϭwsO8\?:FjRh5MP(d%Ś,(P(DÖ,C&TU0 IE*T*?8Gl qRe4d)5!I n,@<&ѫ'< v3DW8/P.m;>%X߸ w(6A6&f2Ek*lƳ 0A |{-XV0Jpl$|=HD7A=@J&ֱb(:w~⭞)IENDB`package/lib/public/icons/page_white_picture.png000755 000765 000024 00000001212 11657257013 021753 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍOSasZbMJPAqh$?p1ͅفфEcTĄE +h^n8Pj$7ssDUPUz&4mcVSU26ƸvNVFCժZku{{[&2ëRxNFb8xMq2Ygeek^,uq ˟^AVBP(ʏ]>mDzM;7,!a03i&"a= q_R xa`_|~vM,|NIENDB`package/lib/public/icons/page_white_put.png000755 000765 000024 00000001013 11657257013 021107 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕KKaE"!hQAIe1- J \ jBA Rh-\HhA(::ۜ309g822P h0,0@ YU8.U**rv%@C\.eki$ID"Qx$tI@ Ţx',RѨ(0 #JZ 2 bXKKՙBL}KqP(YL*y$jfB\HB-*vױ}o9 ӭBT%h (L<ѧǨvdx:ٿ {Њ7'\;TS;s`ۄ#\0?vaV2+:.ŷw - ZeR:ؕߓֱ+(\ҴphIENDB`package/lib/public/icons/page_white_ruby.png000755 000765 000024 00000001162 11657257013 021265 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍOOABHV&&$$ Hmr3 "'. I?v f~̼N0*#bVB  6Y%qA?7MضaժT* H_u]x'FeYuZMDQ.jɝ(} KK/ZX;SwO\IԄdQgo_A?mVh<\y0:|+PUDkk@ vI`cf&ߒj:ss('S݇iA9|a :8}d{,# _0M#hdd.ghf*E9 ctn"rN .'׽B~F'!pXXqw>k%Q"42&ۙ}\MvlwqI)Yz;B I|"a7& <6vjIENDB`package/lib/public/icons/page_white_stack.png000755 000765 000024 00000000475 11657257013 021417 0ustar00tjstaff000000 000000 PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(uM 0FG=RP3:E#qC("ڟ9i y0x4! TBA}LkZ݉6G 7(L{P  أoX#bxG @)yxp0—X,X <4i0Hȧ<.m{b4O:a"נJF@zt 7uNxx?,?X.YwIENDB`package/lib/public/icons/page_white_star.png000755 000765 000024 00000001065 11657257013 021257 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍKQX?Tpib$Kq.JEMQ .J7dMq)n}7""hq E|5{ofDt0y7瞙;`$R | 97}߿ $#|!: If۶8NnNz Z l6aN, ih(뺠rw l:v#QZJRVon.,;O{74ώA( nurNZ/w8vX:! 8Ovy|<\c /qW,1-`Nc&6Yo4*D#1z N#h!D@B `@'F6~'z*B DF25bd@%|<[SYu(1hp 4M6.V3u]T \ n1| I P35}te}(U\;r̶12Qka& }\Ž^h]cCrkު6eŦTěnePB*z*,H;s=v7m|rx΁ ~P%S!ӂIENDB`package/lib/public/icons/page_white_text.png000755 000765 000024 00000000526 11657257013 021273 0ustar00tjstaff000000 000000 PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT1nSAǞ4A(ISp.KPp"QA7%1Ӣ;+Z######DWk=C?|Xj9昇SekMs9NNVG@kD)4hn.Q@nJ)1]:;1@ T:ti:I$fM-+g]^LvIENDB`package/lib/public/icons/page_white_text_width.png000755 000765 000024 00000000473 11657257013 022473 0ustar00tjstaff000000 000000 PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(υM 0'ZqJ[A=q/ ܺtc+Z[t|I[l?pF\ 5n`˸X(C»˻zoÛV})&vl'Ɖ@Xz{{`aJ8ސwxqˌl43-yK7w0;#v Cx͏ڍO9I Rhoˤ/&wl{u:oѤbuIENDB`package/lib/public/icons/page_white_vector.png000755 000765 000024 00000001204 11657257013 021603 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˅OoQšF.ąw5{;w~ao 1!EBZ W@iպP Q a@0Ϳz|wZȔ:ټ{\I|ׄVVBK3bȶ#qHh4BU2O&Vh@efO9i`P(@4u(hI&ǐy骥R MlUl6ϴf2, Jw-s#7$i1@d`0Ϟ#!ˑ,Z֚-@U kraeSDode0=XzZ,f_G|Zq-3,h!S٫YO$WZlv-`s^5-*f⼙8 p-Bҩ bNL^y/]04q `IENDB`package/lib/public/icons/page_white_visualstudio.png000755 000765 000024 00000001276 11657257013 023045 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<PIDAT8ˍoHqǗ8 -7FDhBȔJ0ތeь\zFQ"‘V ^ LbcsMvw]Om~Щ(IQj*"Ac:$If2C9yT,)p*˲y$HD,$A%U<A 0( x\p!>$'ޖ = kneTM,nɒOz 㗏D\d V\o|gܣx$S6YT?@ s7v"nFZch a$D xg%zEL@R²D6,3a{O邠g̹z xxK`/a20AUAxGf5@.y}4N A cV0m +m/{ʊ<Ď!Wǝ>ؚ0T  f`J).]*Pか?? ^~V Z!Z03 ^`!F 7HLgYkAy~eX`=aѶH`V%XyIENDB`package/lib/public/icons/page_white_width.png000755 000765 000024 00000000465 11657257013 021430 0ustar00tjstaff000000 000000 PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(υA0E֕n\xqob♼ Rw(gIu2U5S6VXʲJ6YhlOe<Ƥj56S&5n2 ,be %,r[;zֳ X`<ƐdzB7|!A/n2=VY ro vZdIENDB`package/lib/public/icons/page_white_word.png000755 000765 000024 00000001213 11657257013 021254 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍkQ}ɢ7.ĤScR+SY`+"6_F,]fǽ3Y7Q[̜{cTci`p7UTȅ,˺eY,tii50XUC`,˸~^NG[lH{B7zuNB ACZ(VVV,..y2ȡln !qo?"7L$qڗ]biJED!P\IИ ',hTiJ$Vb{+k]Bh|^p6(9xId_ {,s.Fi$*Zs# `sJr$S1ڃ ™On,q [1Ts?> ,ܨx)} y6>ۘAT5# 3fK!Up4J19Jsq@8#:/nyIENDB`package/lib/public/icons/page_white_world.png000755 000765 000024 00000001336 11657257013 021436 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<pIDAT8ˍORaǭ#+/ڢ2+u6SQHeUV"5LKsa ž=)>;}{+PB!B%xV> P*/(kkkd2dx+++$CpB'W3N: Ӌɻz,,H$DXA6V)ZD+ ި`O`<{+:quLI[鐨nW )^}[)GcAt[1مh4$  `FaгKv/jlq{>OIRl=bضdF؀$O?9 q * v~L#:fE|w}@og!qی:>Q!1r+l-<ǘPy͎C*;Ιpz+T@ M?a_p C/zNWfƀ 6wAEܠ c|y5FNԡ2Hw'(AFle7>ųI\H"^LiEn5ۙ`t;'%G ;-?|f'(XtK2,B*t:!.2 èT*pݐ$ @Xl%4B`0EQx`ۑH$`Xx%'#NS'IXVޞIN[CȲh4ʳ*l65M4'v/O!c{+d2FdʇB$M,mߟuD~؊kXyLFD03o/] ]ǧ_Unk0py*tw<~kFUT,m@_"g7J%\_^q9c!6 S4BsY@mZm5rqF OźXIENDB`package/lib/public/icons/page_white_zip.png000755 000765 000024 00000000602 11657257013 021104 0ustar00tjstaff000000 000000 PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(]MJ+"/$BׂQBT)"Vt D*ǦI% $117 tAȎ&鰲Or/7"olܐ_jPCMU5(M_Ǚr L[nIENDB`package/lib/public/icons/page_word.png000755 000765 000024 00000001411 11657257013 020054 0ustar00tjstaff000000 000000 PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATMUeιuqIWh"(²l6Ԧ(Eh]dlآEhSZQ34%-glc{9_O^n!HDєk]މ# Co~ 4TGz?+gO[ÑԐ[WgrJ$F4Ψn,kl_~ͯg} CJi""nu2uTɮ=s6mϠE3}ܱ3Ӷ̮sDשmzxֱz/fL9~i0(4WPM,4zMꇕp׮O^J~'9W8}Z3t7;)PFՌl0ݫg.yn|nsTlrb1xɵHwg?? -ynos Ha>чJ&b]% K93ԣtP,/My"`gGi#cZR/me GDA8Hݝ)®:Һ-,f.޺Fe-)(>$Vй*8 *&Œ%V; ٞqHO\s/^シyDCM EVygD@mZ qbwvA(Y9s:TD+SM5.n[hh(KhI;D +I+k6!A@ι;FQx- 1IENDB`