[
  {
    "path": "README.md",
    "content": "# Hello, Ping.\n\n## 静态文件服务器部分\n[用Node.js打造你的静态文件服务器](http://cnodejs.org/blog/?p=3904)\n\n## 动态文件服务器部分\n[用Node.js构建动态服务器基础](http://cnodejs.org/blog/?p=4520)\n"
  },
  {
    "path": "app.js",
    "content": "var PORT = process.argv[2] || 8000;\nvar http = require(\"http\");\nvar url = require(\"url\");\nvar fs = require(\"fs\");\nvar path = require(\"path\");\nvar mime = require(\"./mime\").types;\nvar config = require(\"./config\");\nvar utils = require(\"./utils\");\nvar zlib = require(\"zlib\");\n\nvar server = http.createServer(function(request, response) {\n    response.setHeader(\"Server\", \"Node/V5\");\n    response.setHeader('Accept-Ranges', 'bytes');\n    var pathname = url.parse(request.url).pathname;\n    if (pathname.slice(-1) === \"/\") {\n        pathname = pathname + config.Welcome.file;\n    }\n    var realPath = path.join(\"assets\", path.normalize(pathname.replace(/\\.\\./g, \"\")));\n\n    var pathHandle = function (realPath) {\n        fs.stat(realPath, function (err, stats) {\n            if (err) {\n                response.writeHead(404, \"Not Found\", {'Content-Type': 'text/plain'});\n                response.write(\"This request URL \" + pathname + \" was not found on this server.\");\n                response.end();\n            } else {\n                if (stats.isDirectory()) {\n                    realPath = path.join(realPath, \"/\", config.Welcome.file);\n                    pathHandle(realPath);\n                } else {\n                    var ext = path.extname(realPath);\n                    ext = ext ? ext.slice(1) : 'unknown';\n                    var contentType = mime[ext] || \"text/plain\";\n                    response.setHeader(\"Content-Type\", contentType);\n\n                    var lastModified = stats.mtime.toUTCString();\n                    var ifModifiedSince = \"If-Modified-Since\".toLowerCase();\n                    response.setHeader(\"Last-Modified\", lastModified);\n\n                    if (ext.match(config.Expires.fileMatch)) {\n                        var expires = new Date();\n                        expires.setTime(expires.getTime() + config.Expires.maxAge * 1000);\n                        response.setHeader(\"Expires\", expires.toUTCString());\n                        response.setHeader(\"Cache-Control\", \"max-age=\" + config.Expires.maxAge);\n                    }\n\n                    if (request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]) {\n                        response.writeHead(304, \"Not Modified\");\n                        response.end();\n                    } else {\n                        var compressHandle = function (raw, statusCode, reasonPhrase,contentLength) {\n                                var stream = raw;\n                                var acceptEncoding = request.headers['accept-encoding'] || \"\";\n                                var matched = ext.match(config.Compress.match);\n\n                                if (matched && acceptEncoding.match(/\\bgzip\\b/)) {\n                                    response.setHeader(\"Content-Encoding\", \"gzip\");\n                                    stream = raw.pipe(zlib.createGzip());\n                                } else if (matched && acceptEncoding.match(/\\bdeflate\\b/)) {\n                                    response.setHeader(\"Content-Encoding\", \"deflate\");\n                                    stream = raw.pipe(zlib.createDeflate());\n                                }\n                                else{\n                                    response.setHeader('Content-Length',contentLength);\n                                }\n                                response.writeHead(statusCode, reasonPhrase);\n                                stream.pipe(response);\n                            };\n\n                        if (request.headers[\"range\"]) {\n                            var range = utils.parseRange(request.headers[\"range\"], stats.size);\n                            if (range) {\n                                response.setHeader(\"Content-Range\", \"bytes \" + range.start + \"-\" + range.end + \"/\" + stats.size);\n                                var raw = fs.createReadStream(realPath, {\"start\": range.start, \"end\": range.end});\n                                compressHandle(raw, 206, \"Partial Content\",(range.end - range.start + 1));\n                            } else {\n                                response.writeHead(416, \"Request Range Not Satisfiable\");\n                                response.end();\n                            }\n                        } else {\n                            var raw = fs.createReadStream(realPath);\n                            compressHandle(raw, 200, \"Ok\",stats.size);\n                        }\n                    }\n                }\n            }\n        });\n    };\n\n    pathHandle(realPath);\n});\n\nserver.listen(PORT);\nconsole.log(\"Server running at port: \" + PORT + \".\");\n"
  },
  {
    "path": "asset.js",
    "content": "var url = require(\"url\");\nvar fs = require(\"fs\");\nvar path = require(\"path\");\nvar mime = require(\"./mime\").types;\nvar config = require(\"./config\");\nvar utils = require(\"./utils\");\nvar zlib = require(\"zlib\");\n\nvar Asset = function () {};\n\nAsset.prototype.dispatch = function (request, response) {\n    response.setHeader(\"Server\", \"Node/V5\");\n    response.setHeader('Accept-Ranges', 'bytes');\n    var pathname = url.parse(request.url).pathname;\n    if (pathname.slice(-1) === \"/\") {\n        pathname = pathname + config.Welcome.file;\n    }\n    var realPath = path.join(\"assets\", path.normalize(pathname.replace(/\\.\\./g, \"\")));\n\n    var pathHandle = function (realPath) {\n        fs.stat(realPath, function (err, stats) {\n            if (err) {\n                response.writeHead(404, \"Not Found\", {'Content-Type': 'text/plain'});\n                response.write(\"This request URL \" + pathname + \" was not found on this server.\");\n                response.end();\n            } else {\n                if (stats.isDirectory()) {\n                    realPath = path.join(realPath, \"/\", config.Welcome.file);\n                    pathHandle(realPath);\n                } else {\n                    var ext = path.extname(realPath);\n                    ext = ext ? ext.slice(1) : 'unknown';\n                    var contentType = mime[ext] || \"text/plain\";\n                    response.setHeader(\"Content-Type\", contentType);\n                    response.setHeader('Content-Length', stats.size);\n\n                    var lastModified = stats.mtime.toUTCString();\n                    var ifModifiedSince = \"If-Modified-Since\".toLowerCase();\n                    response.setHeader(\"Last-Modified\", lastModified);\n\n                    if (ext.match(config.Expires.fileMatch)) {\n                        var expires = new Date();\n                        expires.setTime(expires.getTime() + config.Expires.maxAge * 1000);\n                        response.setHeader(\"Expires\", expires.toUTCString());\n                        response.setHeader(\"Cache-Control\", \"max-age=\" + config.Expires.maxAge);\n                    }\n\n                    if (request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]) {\n                        response.writeHead(304, \"Not Modified\");\n                        response.end();\n                    } else {\n                        var compressHandle = function (raw, statusCode, reasonPhrase) {\n                                var stream = raw;\n                                var acceptEncoding = request.headers['accept-encoding'] || \"\";\n                                var matched = ext.match(config.Compress.match);\n\n                                if (matched && acceptEncoding.match(/\\bgzip\\b/)) {\n                                    response.setHeader(\"Content-Encoding\", \"gzip\");\n                                    stream = raw.pipe(zlib.createGzip());\n                                } else if (matched && acceptEncoding.match(/\\bdeflate\\b/)) {\n                                    response.setHeader(\"Content-Encoding\", \"deflate\");\n                                    stream = raw.pipe(zlib.createDeflate());\n                                }\n                                response.writeHead(statusCode, reasonPhrase);\n                                stream.pipe(response);\n                            };\n\n                        if (request.headers[\"range\"]) {\n                            var range = utils.parseRange(request.headers[\"range\"], stats.size);\n                            if (range) {\n                                response.setHeader(\"Content-Range\", \"bytes \" + range.start + \"-\" + range.end + \"/\" + stats.size);\n                                response.setHeader(\"Content-Length\", (range.end - range.start + 1));\n                                var raw = fs.createReadStream(realPath, {\"start\": range.start, \"end\": range.end});\n                                compressHandle(raw, 206);\n                            } else {\n                                response.removeHeader(\"Content-Length\");\n                                response.writeHead(416);\n                                response.end();\n                            }\n                        } else {\n                            var raw = fs.createReadStream(realPath);\n                            compressHandle(raw, 200);\n                        }\n                    }\n                }\n            }\n        });\n    };\n\n    pathHandle(realPath);\n};\n\nexports.Asset = Asset;\n"
  },
  {
    "path": "assets/eventproxy.js",
    "content": "/*global exports */\n/**\n * @fileoverview This file is used for define the EventProxy library.\n * @author <a href=\"mailto:shyvo1987@gmail.com\">Jackson Tian</a>\n * @version 0.1.0\n */\n(function () {\n    /**\n     * @description EventProxy. A module that can be mixed in to *any object* in order to provide it with\n     * custom events. You may `bind` or `unbind` a callback function to an event;\n     * `trigger`-ing an event fires all callbacks in succession.\n     * @constructor\n     * @name EventProxy\n     * @class EventProxy. An implementation of task/event based asynchronous pattern.\n     * @example\n     * var render = function (template, resources) {};\n     * var proxy = new EventProxy();\n     * proxy.assign(\"template\", \"l10n\", render);\n     * proxy.trigger(\"template\", template);\n     * proxy.trigger(\"l10n\", resources);\n     */\n    var EventProxy = function () {\n        if (!(this instanceof EventProxy)) {\n            return new EventProxy();\n        }\n        this._callbacks = {};\n        this._fired = {};\n    };\n\n    /**\n     * @description Bind an event, specified by a string name, `ev`, to a `callback` function.\n     * Passing `\"all\"` will bind the callback to all events fired.\n     * @memberOf EventProxy#\n     * @param {string} eventName Event name.\n     * @param {function} callback Callback.\n     */\n    EventProxy.prototype.addListener = function (ev, callback) {\n        this._callbacks = this._callbacks || {};\n        this._callbacks[ev] = this._callbacks[ev] || [];\n        this._callbacks[ev].push(callback);\n        return this;\n    };\n    EventProxy.prototype.bind = EventProxy.prototype.addListener;\n    EventProxy.prototype.on = EventProxy.prototype.addListener;\n    EventProxy.prototype.await = EventProxy.prototype.addListener;\n\n    /**\n     * @description Remove one or many callbacks. If `callback` is null, removes all\n     * callbacks for the event. If `ev` is null, removes all bound callbacks\n     * for all events.\n     * @memberOf EventProxy#\n     * @param {string} eventName Event name.\n     * @param {function} callback Callback.\n     */\n    EventProxy.prototype.removeListener = function (ev, callback) {\n        var calls = this._callbacks, i, l;\n        if (!ev) {\n            this._callbacks = {};\n        } else if (calls) {\n            if (!callback) {\n                calls[ev] = [];\n            } else {\n                var list = calls[ev];\n                if (!list) {\n                    return this;\n                }\n                l = list.length;\n                for (i = 0; i < l; i++) {\n                    if (callback === list[i]) {\n                        list[i] = null;\n                        break;\n                    }\n                }\n            }\n        }\n        return this;\n    };\n    EventProxy.prototype.unbind = EventProxy.prototype.removeListener;\n\n    /**\n     * @description Remove all listeners.\n     * It equals unbind(); Just add this API for as same as Event.Emitter.\n     * @memberOf EventProxy#\n     * @param {string} event Event name.\n     */\n    EventProxy.prototype.removeAllListeners = function (event) {\n        return this.unbind(event);\n    };\n\n    /**\n     * @description Trigger an event, firing all bound callbacks. Callbacks are passed the\n     * same arguments as `trigger` is, apart from the event name.\n     * Listening for `\"all\"` passes the true event name as the first argument.\n     * @param {string} eventName Event name.\n     * @param {mix} data Pass in data. \n     */\n    EventProxy.prototype.trigger = function (eventName, data) {\n        var list, calls, ev, callback, args, i, l;\n        var both = 2;\n        if (!(calls = this._callbacks)) {\n            return this;\n        }\n        while (both--) {\n            ev = both ? eventName : 'all';\n            list = calls[ev];\n            if (list) {\n                for (i = 0, l = list.length; i < l; i++) {\n                    if (!(callback = list[i])) {\n                        list.splice(i, 1); i--; l--;\n                    } else {\n                        args = both ? Array.prototype.slice.call(arguments, 1) : arguments;\n                        callback.apply(this, args);\n                    }\n                }\n            }\n        }\n        return this;\n    };\n    EventProxy.prototype.emit = EventProxy.prototype.trigger;\n    EventProxy.prototype.fire = EventProxy.prototype.trigger;\n\n    /**\n     * @description Bind an event like the bind method, but will remove the listener after it was fired.\n     * @param {string} ev Event name.\n     * @param {function} callback Callback.\n     */\n    EventProxy.prototype.once = function (ev, callback) {\n        var self = this,\n            wrapper = function () {\n                callback.apply(self, arguments);\n                self.unbind(ev, wrapper);\n            };\n        this.bind(ev, wrapper);\n        return this;\n    };\n    \n    /**\n     * @description Bind an event, and trigger it immediately.\n     * @param {string} ev Event name.\n     * @param {function} callback Callback.\n     * @param {mix} data The data that will be passed to calback as arguments.\n     */\n    EventProxy.prototype.immediate = function (ev, callback, data) {\n        this.bind(ev, callback);\n        this.trigger(ev, data);\n        return this;\n    };\n\n    var _assign = function (eventname1, eventname2, cb, once) {\n        var proxy = this, length, index = 0, argsLength = arguments.length,\n            bind, _all,\n            callback, events, isOnce, times = 0, flag = {};\n\n        // Check the arguments length.\n        if (argsLength < 3) {\n            return this;\n        }\n\n        events = Array.prototype.slice.apply(arguments, [0, argsLength - 2]);\n        callback = arguments[argsLength - 2];\n        isOnce = arguments[argsLength - 1];\n\n        // Check the callback type.\n        if (typeof callback !== \"function\") {\n            return this;\n        }\n\n        length = events.length;\n        bind = function (key) {\n            var method = isOnce ? \"once\" : \"bind\";\n            proxy[method](key, function (data) {\n                proxy._fired[key] = proxy._fired[key] || {};\n                proxy._fired[key].data = data;\n                if (!flag[key]) {\n                    flag[key] = true;\n                    times++;\n                }\n            });\n        };\n\n        for (index = 0; index < length; index++) {\n            bind(events[index]);\n        }\n\n        _all = function () {\n            if (times < length) {\n                return;\n            }\n            var data = [];\n            for (index = 0; index < length; index++) {\n                data.push(proxy._fired[events[index]].data);\n            }\n            if (isOnce) {\n                proxy.unbind(\"all\", _all);\n            }\n            callback.apply(null, data);\n        };\n        proxy.bind(\"all\", _all);\n    };\n\n    /**\n     * @description Assign some events, after all events were fired, the callback will be executed once.\n     * @example\n     * proxy.all(ev1, ev2, callback);\n     * proxy.all([ev1, ev2], callback);\n     * proxy.all(ev1, [ev2, ev3], callback);\n     * @param {string} eventName1 First event name.\n     * @param {string} eventName2 Second event name.\n     * @param {function} callback Callback, that will be called after predefined events were fired.\n     */\n    EventProxy.prototype.all = function (eventname1, eventname2, cb) {\n        var args = Array.prototype.concat.apply([], arguments);\n        args.push(true);\n        _assign.apply(this, args);\n        return this;\n    };\n    EventProxy.prototype.assign = EventProxy.prototype.all;\n\n    /**\n     * @description Assign some events, after all events were fired, the callback will be executed first time.\n     * then any event that predefined be fired again, the callback will executed with the newest data.\n     * @example\n     * proxy.tail(ev1, ev2, callback);\n     * proxy.tail([ev1, ev2], callback);\n     * proxy.tail(ev1, [ev2, ev3], callback);\n     * @memberOf EventProxy#\n     * @param {string} eventName1 First event name.\n     * @param {string} eventName2 Second event name.\n     * @param {function} callback Callback, that will be called after predefined events were fired.\n     */\n    EventProxy.prototype.tail = function () {\n        var args = Array.prototype.concat.apply([], arguments);\n        args.push(false);\n        _assign.apply(this, args);\n        return this;\n    };\n    EventProxy.prototype.assignAll = EventProxy.prototype.tail;\n    EventProxy.prototype.assignAlways = EventProxy.prototype.tail;\n\n    /**\n     * @description The callback will be executed after the event be fired N times.\n     * @memberOf EventProxy#\n     * @param {string} eventName Event name.\n     * @param {number} times N times.\n     * @param {function} callback Callback, that will be called after event was fired N times.\n     */\n    EventProxy.prototype.after = function (eventName, times, callback) {\n        var proxy = this,\n            firedData = [],\n            all;\n        all = function (name, data) {\n            if (name === eventName) {\n                times--;\n                firedData.push(data);\n                if (times < 1) {\n                    proxy.unbind(\"all\", all);\n                    callback.apply(null, [firedData]);\n                }\n            }\n        };\n        proxy.bind(\"all\", all);\n        return this;\n    };\n\n    /**\n     * @description The callback will be executed after any registered event was fired. It only executed once.\n     * @memberOf EventProxy#\n     * @param {string} eventName1 Event name.\n     * @param {string} eventName2 Event name.\n     * @param {function} callback The callback will get a map that has data and eventName attributes.\n     */\n    EventProxy.prototype.any = function () {\n        var proxy = this,\n            index,\n            _bind,\n            len = arguments.length,\n            callback = arguments[len - 1],\n            events = Array.prototype.slice.apply(arguments, [0, len - 1]),\n            count = events.length,\n            _eventName = events.join(\"_\");\n\n        proxy.once(_eventName, callback);\n\n        _bind = function (key) {\n            proxy.bind(key, function (data) {\n                proxy.trigger(_eventName, {\"data\": data, eventName: key});\n            });\n        };\n\n        for (index = 0; index < count; index++) {\n            _bind(events[index]);\n        }\n    };\n\n    /**\n     * @description The callback will be executed when the evnet name not equals with assigned evnet.\n     * @memberOf EventProxy#\n     * @param {string} eventName Event name.\n     * @param {function} callback Callback.\n     */\n    EventProxy.prototype.not = function (eventName, callback) {\n        var proxy = this;\n        proxy.bind(\"all\", function (name, data) {\n            if (name !== eventName) {\n                callback(data);\n            }\n        });\n    };\n    \n    /**\n     * Create a new EventProxy\n     * @example\n     *     var ep = EventProxy.create();\n     *     ep.assign('user', 'articles', function(user, articles) {\n     *       // do something...\n     *     });\n     * \n     *     // or one line ways: Create EventProxy and Assign\n     *     \n     *     var ep = EventProxy.create('user', 'articles', function(user, articles) {\n     *       // do something...\n     *     });\n     * \n     * @returns {EventProxy}\n     */\n    EventProxy.create = function () {\n        var ep = new EventProxy();\n        if (arguments.length) {\n            ep.assign.apply(ep, Array.prototype.slice.call(arguments));\n        }\n        return ep;\n    };\n\n    // Event proxy can be used in browser and Nodejs both.\n    if (typeof exports !== \"undefined\") {\n        exports.EventProxy = EventProxy;\n    } else {\n        this.EventProxy = EventProxy;\n    }\n\n}());\n"
  },
  {
    "path": "assets/index.html",
    "content": "<html>\r\n<body>\r\n<h1>It works!</h1>\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "assets/portal_client.js",
    "content": "(function (global) {\r\n    var Portal = new EventProxy();\r\n    Portal.bigPipe = function (id, html) {\r\n        document.getElementById(id).innerHTML = html;\r\n    };\r\n\r\n    global.Portal = Portal;\r\n}(this));"
  },
  {
    "path": "assets/post/index.js",
    "content": "Portal.on(\"onlineUsers\", function (data) {\r\n    setTimeout(function () {\r\n        document.getElementById(\"onlineUsers\").innerHTML = \"888人\";\r\n    }, 100);\r\n});\r\n"
  },
  {
    "path": "certs/ping-csr.pem",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIB1TCCAT4CAQAwfjELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFNoYW5naGFpMREw\nDwYDVQQHDAhTaGFuZ2hhaTEOMAwGA1UECgwFQ05vZGUxFTATBgNVBAMMDEphY2tz\nb24gVGlhbjEiMCAGCSqGSIb3DQEJARYTc2h5dm8xOTg3QGdtYWlsLmNvbTCBnzAN\nBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0mGIYebaRufKjh5sPbeAMi2Y8kuDUfsk\nue7KWdnjQd7ZPsUXEhDaN8uljXuHqMCE9cotjGoMK1uwlIsCiFrzv4zj03VABrkc\nYHBIBW9LW7kssNfzM3Qs4smvqBwJ2+zVUN8itFf1PFGwEpyLV+JB6rO25q7Wbkq6\nJEmHWAb1Un0CAwEAAaAXMBUGCSqGSIb3DQEJBzEIDAYxMjM0NTYwDQYJKoZIhvcN\nAQEFBQADgYEAnkabG4W2k0F0TPAvJtezI/n9qt+2krBG8Zl91l30Ov7wKtv2dimF\nK1ET00SxJCpj2ED6wXr2THw6GNHjfc8JcTbLxJtcsiUBbmtUhPt02J1SeRqJfYWm\nssmWlNGhP/JE/+bNvncEX2D0rwuB0wijxbllN8ULQFBJCxhNo6BpaMM=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "certs/ping-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQDSYYhh5tpG58qOHmw9t4AyLZjyS4NR+yS57spZ2eNB3tk+xRcS\nENo3y6WNe4eowIT1yi2MagwrW7CUiwKIWvO/jOPTdUAGuRxgcEgFb0tbuSyw1/Mz\ndCziya+oHAnb7NVQ3yK0V/U8UbASnItX4kHqs7bmrtZuSrokSYdYBvVSfQIDAQAB\nAoGBAJo7oq6Tfcapu9fA+f2s/7DJuO046wc5JU2igHqqwG7UH1RjTWyTKkfpZm94\n9pWCvncrR4U/YbiPub7MwoiQlUytDdAwEu5i+ME0Pb0LCiSGA6XT+XMOaHXQu8Pp\no320FUUQ19jD4sIC4ZAYw9JIQqnrRRyL5Ldzz7QWWFEpaN5BAkEA/sKC0oCr0KOc\nN4M7PmT54p67lCrjp2Q9D8XwGwnD5+dUeEPfa4vIGsoYN5VSkwH8QbbVBK4C7kKW\nGbaApZKoZQJBANNnty7y0Ata2lbYy7itm18YQiR3jTa+VKq4FpVHBtUphzDei8zh\nzD/UqrQjj5Od0xG5cGJxofkDy348Bs2XRDkCQErvUeWzHVawWUnm2u0+bFYhVJF/\nkBjznhZepYJ+e9Zhr/H0HOqYYhKnMTpgPLqrEdUOf1fyC0Cj61zC1tJc8hUCQFQC\nBAZRESFiAh++2P3TZ0mbvzT0mRYm/kg1DSxW5D0y2nkuBontNJgs74TUGMsFTYne\nke1c0Iu+2U+ZlO5/7OkCQDmCHHoZy2b2DUaweOdqNIkyqcNLAadJDlcZvN35tvj0\nbPRvezY2G7LTcg9j/9jhEMGi9ELEQDzYvwIsNKXO2qk=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "config.js",
    "content": "exports.Expires = {\n    fileMatch: /^(gif|png|jpg|js|css)$/ig,\n    maxAge: 60*60*24*365\n};\nexports.Compress = {\n    match: /css|html/ig\n};\nexports.Welcome = {\n    file: \"index.html\"\n};\nexports.Timeout = 20 * 60 * 1000;\nexports.Secure = null;\n"
  },
  {
    "path": "context.js",
    "content": "var path = require(\"path\");\r\nvar fs = require(\"fs\");\r\nvar footprint = require(\"footprint\");\r\n\r\nvar Context = function (request, response, session, framework) {\r\n    this.request = request;\r\n    this.response = response;\r\n    this.session = session;\r\n    this.framework = framework;\r\n};\r\nContext.prototype.none = function () {\r\n    this.response.writeHead(204);\r\n    this.response.end();\r\n};\r\nContext.prototype.renderJSON = function (jsonObj) {\r\n    this.response.setHeader(\"Content-Type\", \"application/json\");\r\n    this.response.writeHead(200);\r\n    this.response.end(JSON.stringify(jsonObj));\r\n};\r\nContext.prototype.redirect = function (url) {\r\n    this.response.setHeader(\"Location\", url);\r\n    this.response.writeHead(301);\r\n    this.response.end();\r\n};\r\nContext.prototype._renderView = function (viewEngine, template, data) {\r\n    var framework = this.framework,\r\n        request = this.request,\r\n        response = this.response;\r\n\r\n    try {\r\n        response.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});\r\n        response.write(viewEngine.template(template, data));\r\n        response.end();\r\n    } catch (ex) {\r\n        console.log(ex.message);\r\n        console.log(ex.stack);\r\n        framework.handler500(request, response, \"Parse template error.\");\r\n    }\r\n};\r\n\r\nContext.prototype.renderView = function (view, data) {\r\n    var context = this,\r\n        framework = context.framework,\r\n        request = context.request,\r\n        response = context.response;\r\n\r\n    // Get engine.\r\n    var viewEngine = footprint;\r\n\r\n    // Check cache.\r\n    viewEngine._cache = viewEngine._cache || {};\r\n    var template = viewEngine._cache[view];\r\n    if (template) {\r\n        context._renderView(viewEngine, template, data);\r\n    } else {\r\n        var filePath = path.join(__dirname, \"views/\", view);\r\n\r\n        path.exists(filePath, function (exists) {\r\n            if(!exists) {\r\n                framework.handler500(request, response, \"This template file doesn't exist.\");  \r\n            } else {\r\n                fs.readFile(filePath, \"utf8\", function(err, file) {  \r\n                    if (err) {\r\n                        framework.handler500(request, response, err);\r\n                    } else {\r\n                        viewEngine._cache[view] = file;\r\n                        context._renderView(viewEngine, file, data);\r\n                    }\r\n                });\r\n            }\r\n        });\r\n    }\r\n};\r\nContext.prototype.renderPartial = Context.prototype.renderView;\r\n\r\nexports.Context = Context;"
  },
  {
    "path": "controllers/index.js",
    "content": "var get = exports.get = {};\r\nget.index = function () {\r\n    var response = this.response;\r\n    response.setHeader(\"Content-Type\", \"text/html\");\r\n    response.writeHead(\"200\");\r\n    response.end(\"<h1>Hello NodeV5.</h1>\\n\");\r\n};\r\nget.none = function () {\r\n    this.none();\r\n};\r\nget.json = function () {\r\n    var obj = {\"Hello\": \"world!\"};\r\n    this.renderJSON(obj);\r\n};\r\nget.redirect = function () {\r\n    this.redirect(\"https://github.com/JacksonTian/nodev5\");\r\n};\r\nget.render = function () {\r\n    var obj = {\"title\": \"NodeV5\"};\r\n    this.renderView(\"index.html\", obj);\r\n};\r\n"
  },
  {
    "path": "cookie.js",
    "content": "exports.parse = function (cookies) {\n    var map = {};\n    var pairs = cookies.split(\";\");\n    pairs.forEach(function (pair) {\n        var kv = pair.split(\"=\");\n        map[kv[0].trim()] = kv[1] || \"\";\n    });\n    return map;\n};\nexports.stringify = function (cookie) {\n    var buffer = [cookie.key, \"=\", cookie.value];\n    if (cookie.expires) {\n        buffer.push(\"; expires=\", (new Date(cookie.expires)).toUTCString());\n    }\n    if (cookie.path) {\n        buffer.push(\"; path=\", cookie.path);\n    }\n    if (cookie.domain) {\n        buffer.push(\"; domain=\", cookie.domain);\n    }\n    if (cookie.secure) {\n        buffer.push(\"; secure\");\n    }\n    if (cookie.httpOnly) {\n        buffer.push(\"; httponly\");\n    }\n\n    return buffer.join(\"\");\n};\n"
  },
  {
    "path": "framework.js",
    "content": "var http = require(\"http\");\r\nvar url = require(\"url\");\r\nvar cookie = require(\"./cookie\");\r\nvar session = require(\"./session\");\r\nvar config = require(\"./config\");\r\nvar path = require(\"path\");\r\nvar Context = require(\"./context\").Context;\r\n\r\nvar Framework = function () {\r\n    this.sessionManager = new session.SessionManager(config.Timeout);\r\n};\r\n\r\nFramework.prototype.dispatch = function (request, response) {\r\n    if (request.url == \"/favicon.ico\") {\r\n        response.writeHead(404, \"Not Found\");\r\n        response.end();\r\n        return;\r\n    }\r\n\r\n    var routeInfo = this.route(request.url);\r\n    var controller;\r\n    try {\r\n        controller = require('./controllers/' + routeInfo.controller);\r\n        var method = request.method.toLowerCase() || 'get';\r\n        var action = controller[method] ? controller[method][routeInfo.action] : null;\r\n        if (action) {\r\n            this.enableGet(request, response);\r\n            this.enableCookie(request, response);\r\n            this.enablePost(request, response);\r\n            var curSession = this.enableSession(request, response);\r\n            // Pass request response session and framework into context object.\r\n            var context = new Context(request, response, curSession, this);\r\n            request.on(\"end\", function () {\r\n                action.apply(context, routeInfo.args);\r\n            });\r\n        } else {\r\n            this.handler500(request, response, 'Error: Controller \"' + routeInfo.controller + '\" without action \"' + routeInfo.action + '\" for \"' + request.method + '\" request.');\r\n        }\r\n    } catch (ex) {\r\n        console.log(ex.message);\r\n        console.log(ex.stack);\r\n        this.handler500(request, response, 'Error: Controller \"' + routeInfo.controller + '\" dosen\\'t exsit.');\r\n    }\r\n};\r\n\r\n\r\n// Add get parse supports\r\nFramework.prototype.enableGet = function () {\r\n    http.IncomingMessage.prototype.get = function (key) {\r\n        if (!this._urlMap) {\r\n            this._urlMap = url.parse(this.url, true);\r\n        }\r\n        return this._urlMap.query[key];\r\n    };\r\n};\r\n\r\n// Add cookie parse and set supports\r\nFramework.prototype.enableCookie = function (request, response) {\r\n    http.IncomingMessage.prototype.cookie = function () {\r\n        this.cookie = function (key) {\r\n            if (!this._cookieMap) {\r\n                this._cookieMap = cookie.parse(this.headers.cookie || \"\");\r\n            }\r\n            return this._cookieMap[key];\r\n        };\r\n    };\r\n\r\n    http.ServerResponse.prototype.setCookie = function (cookieObj) {\r\n        if (!this._setCookieMap) {\r\n            this._setCookieMap = {};\r\n        }\r\n        this._setCookieMap[cookieObj.key] = cookie.stringify(cookieObj);\r\n        var returnVal = [];\r\n        for(var key in this._setCookieMap) {\r\n            returnVal.push(this._setCookieMap[key]);\r\n        }\r\n\r\n        this.setHeader(\"Set-Cookie\", returnVal.join(\", \"));\r\n    };\r\n};\r\n\r\n// Add post parse supports\r\nFramework.prototype.enablePost = function () {\r\n    http.IncomingMessage.prototype.post = function () {\r\n        if (!this._postMap) {\r\n            this._postMap = qs.parse(this.postData);\r\n        }\r\n        return this._postMap[key];\r\n    };\r\n};\r\n\r\n// Recept post body.\r\nFramework.prototype.recept = function (request) {\r\n    if (request.method === \"POST\") {\r\n        var _postData = \"\";\r\n\r\n        this.on('data', function (chunk) {\r\n            _postData += chunk;\r\n        })\r\n        .on(\"end\", function () {\r\n            request.postData = _postData;\r\n        });\r\n    }\r\n};\r\n\r\n// Add session supports\r\nFramework.prototype.enableSession = function (request, response) {\r\n    var sessionManager = this.sessionManager;\r\n    var sessionId = request.cookie(session.SESSIONID_KEY);\r\n\r\n    var curSession;\r\n    if (sessionId && (curSession = sessionManager.get(sessionId))) {\r\n        if (sessionManager.isTimeout(curSession)) {\r\n            sessionManager.remove(sessionId);\r\n            curSession = sessionManager.renew(response);\r\n        } else {\r\n            curSession.updateTime();\r\n        }\r\n    } else {\r\n        curSession = sessionManager.renew(response);\r\n    }\r\n\r\n    return curSession;\r\n};\r\n\r\nFramework.prototype.handler500 = function (request, response, err) {\r\n    response.writeHead(500, {'Content-Type': 'text/plain'});\r\n    response.end(err);\r\n};\r\n\r\nFramework.prototype.route = function (requestUrl) {\r\n    // /controller/action/parameter1/parameter2\r\n    var pathname = url.parse(requestUrl).pathname;\r\n\r\n    var path = pathname.split(\"/\");\r\n    path.shift(); // Remove the first \"/\"\r\n\r\n    return {\r\n        controller: path[0] || \"index\",\r\n        action: path[1] || \"index\",\r\n        args: path.slice(2) || [],\r\n    };\r\n};\r\n\r\nexports.Framework = Framework;\r\n"
  },
  {
    "path": "mime.js",
    "content": "exports.types =  {\n  \"css\": \"text/css\",\n  \"gif\": \"image/gif\",\n  \"html\": \"text/html\",\n  \"ico\": \"image/x-icon\",\n  \"jpeg\": \"image/jpeg\",\n  \"jpg\": \"image/jpeg\",\n  \"js\": \"text/javascript\",\n  \"json\": \"application/json\",\n  \"pdf\": \"application/pdf\",\n  \"png\": \"image/png\",\n  \"svg\": \"image/svg+xml\",\n  \"swf\": \"application/x-shockwave-flash\",\n  \"tiff\": \"image/tiff\",\n  \"txt\": \"text/plain\",\n  \"wav\": \"audio/x-wav\",\n  \"wma\": \"audio/x-ms-wma\",\n  \"wmv\": \"video/x-ms-wmv\",\n  \"xml\": \"text/xml\"\n};\n"
  },
  {
    "path": "model.js",
    "content": "var Model = function () {\r\n    this.keys = {};\r\n};\r\nModel.prototype.get = function (key, callback) {\r\n    var val = this.keys[key];\r\n    if (val) {\r\n        \r\n    } else {\r\n        if (this.state[key] === \"pending\") {\r\n            this.on(key, callback);\r\n        } else {\r\n            this.state[key] = \"pending\";\r\n            // TODO get it.\r\n            this.state[key] = \"gotit\";\r\n        }\r\n    }\r\n};\r\nModel.prototype.register = function (key, callback) {\r\n    this.keys[key] = callback;\r\n};\r\n"
  },
  {
    "path": "models/index.js",
    "content": "module.exports = model = function () {};\r\nmodel.before = function (callback) {\r\n    setTimeout(function () {\r\n        var data = {\r\n            \"title\": \"用Nodejs打造你的静态文件服务器\",\r\n            \"content\": 'The concept is come from <a href=\"http://weibo.com/otakustay\">otakustay</a>. You can find it <a href=\"http://weibo.com/2087024342/xADWZc3eX\">here</a>.',\r\n            \"author\": \"Jackson Tian\",\r\n            \"time\": \"2011-12-12\"\r\n        };\r\n        callback(data);\r\n    }, 100);\r\n};\r\n"
  },
  {
    "path": "models/post.js",
    "content": "module.exports = model = function () {};\r\nmodel.before = function (callback) {\r\n    setTimeout(function () {\r\n        var data = {\r\n            \"title\": \"用Nodejs打造你的静态文件服务器\",\r\n            \"content\": '<p>在《The Node Beginner Book》的中文版（<a href=\"http://nodebeginner.org/index-zh-cn.html\">http://nodebeginner.org/index-zh-cn.html</a>）发布之后，获得国内的好评。也有同学觉得这本书略薄，没有包含进阶式的例子。<a href=\"http://www.weibo.com/n/otakustay\">@otakustay</a>同学说：“确实，我的想法是在这之上补一个简单的MVC框架和一个StaticFile+Mimetype+CacheControl机制，可以成为一个更全面的教程”。正巧的是目前我手里的V5项目有一些特殊性：</p>',\r\n            \"author\": \"Jackson\",\r\n            \"time\": '<abbr title=\"星期五, 十一月 11th, 2011, 5:03 下午\">2011 年 11 月 11 日</abbr>',\r\n            \"who\": \"Jackson Tian\",\r\n            \"user\": {\"name\": \"Jackson Tian\", \"email\": \"shyvo1987@gmail.com\"}\r\n        };\r\n        callback(data);\r\n    }, 100);\r\n};\r\nmodel.pipe = function (data, callback) {\r\n    setTimeout(function () {\r\n        callback(data.viewName + \"'s Pipe Content \" + Math.random());\r\n    }, Math.random() * 1000);\r\n};\r\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"author\": \"Jackson Tian <shyvo1987@gmail.com> (http://weibo.com/shyvo)\",\n  \"name\": \"node-ping\",\n  \"description\": \"Web Framework based on Nodejs.\",\n  \"version\": \"0.1.0\",\n  \"homepage\": \"https://github.com/JacksonTian/ping\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/JacksonTian/ping.git\"\n  },\n  \"main\": \"server.js\",\n  \"scripts\": {\n    \"test\": \"expresso test/test.js\"\n  },\n  \"engines\": {\n    \"node\": \"0.6.6\"\n  },\n  \"dependencies\": {},\n  \"devDependencies\": {}\n}\n"
  },
  {
    "path": "partials/comments.view",
    "content": "Comments View\n<%=who%>"
  },
  {
    "path": "partials/friends.view",
    "content": "Friends view."
  },
  {
    "path": "partials/recentPosts.view",
    "content": "Recent Posts view."
  },
  {
    "path": "ping.js",
    "content": "var http = require(\"http\");\r\nvar https = require(\"https\");\r\nvar config = require(\"./config\");\r\n\r\nexports.createServer = function (framework, options) {\r\n    options = options || config.secure;\r\n    var server = options ? https.createServer(options) : http.createServer();\r\n    server.on(\"request\", function (request, response) {\r\n        framework.dispatch(request, response);\r\n    });\r\n\r\n    return server;\r\n};\r\n"
  },
  {
    "path": "portal.js",
    "content": "var url = require(\"url\");\r\nvar path = require(\"path\");\r\nvar fs = require(\"fs\");\r\nvar vm = require(\"vm\");\r\nvar PortalView = require(\"./portalview\").PortalView;\r\n\r\nvar Portal = function () {\r\n};\r\nPortal.extension = \".portal\";\r\nPortal.templateSettings = {\r\n    evaluate    : /<%([\\s\\S]+?)%>/g,\r\n    interpolate : /<%=([\\s\\S]+?)%>/g\r\n};\r\n\r\nPortal.preprocess = function (str, sandbox, settings) {\r\n    var c  = settings || Portal.templateSettings;\r\n    try {\r\n        var temp = str.replace(c.interpolate, function (match, code) {\r\n            var script = \"view.viewData.\" + code;\r\n            return vm.runInNewContext(script, sandbox);\r\n            //return sandbox.view.viewData[code] ? sandbox.view.viewData[code] : \"The key \" + code + \" is undefined.\";\r\n        }).replace(c.evaluate, function (match, code) {\r\n            console.log(code);\r\n            var result = vm.runInNewContext(code, sandbox);\r\n            //console.log(result);\r\n            return result;\r\n        });\r\n    } catch (ex) {\r\n        return ex.stack;\r\n    }\r\n\r\n    return temp;\r\n};\r\n\r\nPortal.postprocess = function (str, sandbox, settings) {\r\n    var c  = settings || Portal.templateSettings;\r\n    var temp = str.replace(c.interpolate, function (match, code) {\r\n        return sandbox[code] ? sandbox[code] : \"The key \" + code + \" is undefined.\";\r\n    });\r\n\r\n    return temp;\r\n};\r\nPortal.prototype.route = function (requestUrl) {\r\n    var portal = requestUrl.split(\"/\")[1];\r\n    var pathname = portal + Portal.extension;\r\n    var realPath = path.join(\"portals\", path.normalize(pathname.replace(/\\.\\./g, \"\")));\r\n    return {\r\n        \"portal\": portal,\r\n        \"path\": realPath\r\n    };\r\n};\r\nPortal.prototype.dispatch = function (request, response) {\r\n    if (request.url == \"/favicon.ico\") {\r\n        response.writeHead(404);\r\n        response.end();\r\n        return;\r\n    }\r\n\r\n    var routeInfo = this.route(request.url);\r\n\r\n    var portalView = new PortalView();\r\n    portalView.all(\"before\", \"file\", function (viewData, file) {\r\n        console.log(\"Portal ready.\");\r\n        portalView.viewData = viewData;\r\n        var sandbox = {\r\n                \"view\": portalView\r\n            };\r\n\r\n        var preprocessed = Portal.preprocess(file, sandbox);\r\n\r\n        portalView.after(\"partial_end\", portalView.partialSeq.length, function () {\r\n            response.writeHead(200);\r\n            response.write(Portal.postprocess(preprocessed, portalView.partialViews));\r\n            console.log(\"Postprocess done.\");\r\n            portalView.fire(\"postprocess_done\");\r\n        });\r\n\r\n        portalView.getPartials();\r\n        portalView.getPipes();\r\n\r\n        console.log(\"Preprocess done.\");\r\n    });\r\n\r\n    portalView.on(\"postprocess_done\", function () {\r\n        portalView.processAjax(response);\r\n        portalView.processPipes(response);\r\n    });\r\n\r\n    try {\r\n        var model = new require(\"./models/\" + routeInfo.portal);\r\n        portalView.model = model;\r\n        model.before(function (viewData) {\r\n            portalView.fire(\"before\", viewData);\r\n        });\r\n    } catch (ex) {\r\n        response.writeHead(500);\r\n        response.write(ex.stack);\r\n        response.end(\"\\n\");\r\n    }\r\n\r\n    fs.readFile(routeInfo.path, function (err, file) {\r\n        if (err) {\r\n            response.writeHead(404, {'Content-Type': 'text/plain'});\r\n            response.write(\"This request URL \" + pathname + \" was not found on this server.\\n\");\r\n            response.end();\r\n        } else {\r\n            portalView.fire(\"file\", file.toString(\"utf-8\"));\r\n        }\r\n    });\r\n\r\n};\r\n\r\nexports.Portal = Portal;\r\n"
  },
  {
    "path": "portals/index.portal",
    "content": "<!DOCTYPE html>\r\n<html>\r\n<head>\r\n    <meta charset=\"utf-8\" />\r\n    <title><%=title%></title>\r\n</head>\r\n<body>\r\n    <div id=\"main\">\r\n        <h2><%=title%></h2>\r\n        <section>\r\n            <%=content%>\r\n        </section>\r\n        <aside><%=author%> posted on <%=time%></aside>\r\n        <div id=\"comments\">\r\n            <%view.partial(\"comments\");%>\r\n        </div>\r\n    </div>\r\n    <aside id=\"sidebar\">\r\n        <h3>好友</h3>\r\n        <div id=\"friends\">\r\n            <%view.partial(\"friends\");%>\r\n        </div>\r\n        <h3>最近博客</h3>\r\n        <div id=\"posts\">\r\n            <%view.partial(\"recentPosts\");%>\r\n        </div>\r\n        <h3>最近访客</h3>\r\n        <div id=\"visitors\">\r\n            <%view.bigPipe(\"recentVisitors\");%>\r\n        </div>\r\n    </aside>\r\n    <footer>\r\n        当前在线: <%view.ajax(\"onlineUsers\");%>\r\n    </footer>\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "portals/post.portal",
    "content": "<!DOCTYPE html>\r\n<html>\r\n<head>\r\n    <meta charset=\"utf-8\" />\r\n    <title><%=title%></title>\r\n    <script src=\"http://localhost:8001/eventproxy.js\"></script>\r\n    <script src=\"http://localhost:8001/portal_client.js\"></script>\r\n    <script src=\"http://localhost:8001/post/index.js\"></script>\r\n</head>\r\n<body>\r\n    <div id=\"main\">\r\n        <h2><%=title%></h2>\r\n        <section>\r\n            <%=user.name%>\r\n            <%=user.email%>\r\n            <%=content%>\r\n        </section>\r\n        <aside>By <%=author%> on <%=time%></aside>\r\n        <div id=\"comments\">\r\n            <%view.partial(\"comments\");%>\r\n        </div>\r\n    </div>\r\n    <aside id=\"sidebar\">\r\n        <h3>好友</h3>\r\n        <div id=\"friends\">\r\n            <%view.partial(\"friends\");%>\r\n        </div>\r\n        <h3>最近博客</h3>\r\n        <div id=\"recentPosts\">\r\n            <%view.bigPipe(\"recentPosts\");%>\r\n        </div>\r\n        <h3>最近访客</h3>\r\n        <div id=\"visitors\">\r\n            <%view.bigPipe(\"visitors\");%>\r\n        </div>\r\n    </aside>\r\n    <footer>\r\n        当前在线: <%view.ajax(\"onlineUsers\");%><span id=\"onlineUsers\">loading</span>\r\n    </footer>\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "portalview.js",
    "content": "var util = require(\"util\");\r\nvar path = require(\"path\");\r\nvar fs = require(\"fs\");\r\nvar EventProxy = require(\"eventproxy\").EventProxy;\r\nvar footprint = require(\"footprint\");\r\n\r\nvar PortalView = function () {\r\n    EventProxy.call(this);\r\n    this.ajaxSeq = [];\r\n    this.partialViews = {};\r\n    this.partialSeq = [];\r\n    this.pipeSeq = [];\r\n    this.pipes = [];\r\n};\r\nutil.inherits(PortalView, EventProxy);\r\n\r\n/**\r\n * Put partial view into sequence. Replace call with view name in template.\r\n * After preprocess phase, portal will get all partial views through this sequence.\r\n * The view name will be replaced in postprocess phase.\r\n */\r\nPortalView.prototype.partial = function (viewName, data) {\r\n    data = data || this.viewData;\r\n    this.partialSeq.push({\"viewName\": viewName, \"viewData\": data});\r\n    return \"<%=\" + viewName + \"%>\";\r\n};\r\n\r\n/**\r\n * Put ajax call into sequence. These scripts will were outputed after all partial views outputed.\r\n */\r\nPortalView.prototype.ajax = function (viewName, data) {\r\n    var script = '<script>\\nPortal.fire(\"' + viewName + '\", ' + JSON.stringify(data) + ');\\n</script>';\r\n    this.ajaxSeq.push(script);\r\n    return \"\";\r\n};\r\n\r\n/**\r\n * Put pipe call into sequence. Trigger these calls after preprocess phase.\r\n */\r\nPortalView.prototype.bigPipe = function (viewName, data) {\r\n    this.pipeSeq.push({\"viewName\": viewName, \"viewData\": data});\r\n    return \"\";\r\n};\r\n\r\n/**\r\n * \r\n */\r\nPortalView.prototype.getPartial = function (data) {\r\n    var that = this;\r\n    var viewPath = path.join(\"partials\", data.viewName + \".view\");\r\n    fs.readFile(viewPath, function (err, view) {\r\n        if (err) {\r\n            throw err;\r\n        } else {\r\n            console.log(data.viewData);\r\n            var html = footprint.template(view.toString(\"utf-8\"), data.viewData);\r\n            that.partialViews[data.viewName] = html;\r\n            that.fire(\"partial_end\", html);\r\n        }\r\n    });\r\n};\r\n\r\nPortalView.prototype.getPartials = function () {\r\n    var that = this;\r\n    this.partialSeq.forEach(function (val) {\r\n        that.getPartial(val);\r\n    });\r\n};\r\n\r\n\r\nPortalView.prototype.getPipe = function (data) {\r\n    var that = this;\r\n    this.model.pipe(data, function (content) {\r\n        var script = '<script>\\nPortal.bigPipe(\"' + data.viewName + '\", \"' + content + '\");\\n</script>';\r\n        that.pipes.push(script);\r\n        that.fire(\"pipe_end\", script);\r\n    });\r\n};\r\n\r\nPortalView.prototype.getPipes = function () {\r\n    var that = this;\r\n    this.pipeSeq.forEach(function (val) {\r\n        that.getPipe(val);\r\n    });\r\n};\r\n\r\nPortalView.prototype.processAjax = function (response) {\r\n    this.ajaxSeq.forEach(function (script) {\r\n        response.write(script);\r\n    });\r\n};\r\n/**\r\n * @description \r\n */\r\nPortalView.prototype.processPipes = function (response) {\r\n    // Process sequnences\r\n    this.pipes.forEach(function (script) {\r\n        response.write(script);\r\n    });\r\n\r\n    // Process coming pipes.\r\n    var times = this.pipeSeq.length - this.pipes.length;\r\n    this.on(\"pipe_end\", function (script) {\r\n        response.write(script);\r\n    });\r\n\r\n    this.after(\"pipe_end\", times, function () {\r\n        console.log(\"Complete.\");\r\n        response.end();\r\n    });\r\n};\r\n\r\nexports.PortalView = PortalView;"
  },
  {
    "path": "proxy.js",
    "content": "var http = require(\"http\");\nvar cp = require(\"child_process\");\n\nvar map = {\n    \"www.nodev5.com\": \"www\",\n    \"assets.nodev5.com\": \"assets\"\n};\n\nvar fork = function () {\n    var worker = cp.fork.apply(cp, arguments);\n    worker.on('message', function (message) {\n        \n    });\n    return worker;\n};\n\nvar workers = {\n    www: cp.fork(__dirname + \"/asset_app.js\", [8001]),\n    assets: cp.fork(__dirname + \"/v5_app.js\", [8002]),\n};\n\nvar server = http.createServer(function(request, response) {\n    // TODO\n    switch()\n        \n    var worker = workers[map[request.headers.host]] || workers[\"assets\"];\n    //worker.send(\"\", server._handle);\n    // console.log(server);\n\n    // response.writeHead(200);\n    // response.end();\n});\n\nserver.on(\"connection\", function (socket) {\n    console.log(socket);\n});\n\nserver.listen(8080);\nconsole.log(\"Server runing at port: 80.\");\n"
  },
  {
    "path": "server.js",
    "content": "var http = require(\"http\");\nvar ping = require(\"./ping\");\nvar Framework = require(\"./framework\").Framework;\nvar Asset = require(\"./asset\").Asset;\nvar Portal = require(\"./portal\").Portal;\n\nvar portal = new Portal();\nping.createServer(portal).listen(8000);\nconsole.log(\"Portal server is running at 8000 port.\");\n\n// Static file server.\nvar asset = new Asset();\nping.createServer(asset).listen(8001);\nconsole.log(\"Static file server is running at 8001 port.\");\n\n// Dynamic handle.\nvar framework = new Framework();\nping.createServer(framework).listen(8002);\nconsole.log(\"Dynamic server is running at 8002 port.\");"
  },
  {
    "path": "session.js",
    "content": "var Session = function (sessionId) {\n    this.sessionId = sessionId;\n    this._map = {};\n};\nSession.prototype.set = function (name, value) {\n    this._map[name] = value;\n};\nSession.prototype.get = function (name) {\n    return this._map[name];\n};\nSession.prototype.remove = function (key) {\n    delete this._map[key];\n};\nSession.prototype.removeAll = function () {\n    delete this._map;\n    this._map = {};\n};\nSession.prototype.updateTime = function () {\n    this._updateTime = new Date().getTime();\n};\n\nvar SESSIONID_KEY = exports.SESSIONID_KEY = \"session_id\";\n\nvar SessionManager = function (timeout) {\n    this.timeout = timeout;\n    this._sessions = {};\n};\nSessionManager.prototype.renew = function (response) {\n    var that = this;\n    var sessionId = [new Date().getTime(), Math.round(Math.random() * 1000)].join(\"\");\n    var session = new Session(sessionId);\n    session.updateTime();\n    this._sessions[sessionId] = session;\n    var clientTimeout = 30 * 24 * 60 * 60 * 1000;\n    var cookie = {key: SESSIONID_KEY, value: sessionId, path: \"/\", expires: new Date().getTime() + clientTimeout};\n    response.setCookie(cookie);\n    return session;\n};\nSessionManager.prototype.get = function (sessionId) {\n    return this._sessions[sessionId];\n};\nSessionManager.prototype.remove = function (sessionId) {\n    delete this._sessions[sessionId];\n};\nSessionManager.prototype.isTimeout = function (session) {\n    return (session._updateTime + this.timeout) < new Date().getTime();\n};\nexports.Session = Session;\nexports.SessionManager = SessionManager;\n"
  },
  {
    "path": "utils.js",
    "content": "exports.parseRange = function (str, size) {\r\n    if (str.indexOf(\",\") != -1) {\r\n        return;\r\n    }\r\n\r\n    var range = str.split(\"-\"),\r\n        start = parseInt(range[0].match(/\\d+/)[0], 10),\r\n        end = parseInt(range[1], 10);\r\n\r\n    // Case: 100-\r\n    if (isNaN(end)) {\r\n        end = size - 1;\r\n    }\r\n\r\n    // Invalid\r\n    if (isNaN(start) || isNaN(end) || start > end || end > size) {\r\n        return;\r\n    }\r\n\r\n    return {start: start, end: end};\r\n};\r\n"
  },
  {
    "path": "views/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\" />\n    <title><%=title%></title>\n</head>\n<body>\n<h1><%=title%></h1>\n</body>\n</html>\n\n"
  }
]