[
  {
    "path": ".gitignore",
    "content": ".project\n*/node-log.log\nlogs/*.log\n*.log\n!.gitignore\nnode_modules/*\nnode_modules_back/*\n.project\n.settings/\n**/*.svn\n*.svn\n*.swp\n*.sublime-project\n*.sublime-workspace\nlib/doc/\nlib-cov/\ncoverage.html\n.DS_Store\n.idea/*"
  },
  {
    "path": ".jshintrc",
    "content": "{\n  \"node\": true,\n  \"camelcase\": true,\n  \"eqeqeq\": true,\n  \"undef\": true,\n  \"unused\": true,\n  \"curly\": true,\n  \"newcap\": true,\n  \"nonew\": true,\n  \"trailing\": true,\n  \"smarttabs\": true,\n  \"noarg\": true\n}"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"10.15.0\"\nbefore_script:\n  - npm install -g grunt-cli\n"
  },
  {
    "path": "AUTHORS",
    "content": "* Charlie Crane  <xieccy@gmail.com> <twitter:@xiecc> <weibo:@圈圈套圈圈> <github:xiecc>\n* Chang chang <changchang005@gmail.com> <twitter:@changchang005> <weibo:@郁闷的武昌鱼> <github:changchang>\n* piaohai <piaohai@gmail.com> <twitter:@piaohai> <weibo:@飘Hai> <github:piaohai>\n* py8765  <pengyang633@126.com> <twitter:@py8765> <weibo:@py8765> <github:py8765>\n* Demon <zhhang0925@gmail.com> <weibo:@demon0925> <github:demon0925>\n* numbcoder <wzhao23@gmail.com> <twitter: @numbcoder> <weibo: @Seekr> <github: numbcoder>\n* halfblood <halfblood369@gmail.com> <twitter: @halfblo97338394> <weibo: @衣兜里的东西> <github: halfblood369>\n* fantasyni <fantasyni@163.com> <github:fantasyni>\n"
  },
  {
    "path": "History.md",
    "content": "2.2.7 / 2019-11-8\n=================\n   * [#1150](https://github.com/NetEase/pomelo/pull/1150)\n2.2.6 / 2019-7-10\n=================\n   * [NEW] upgrade pomelo-admin to 1.0.1\n2.2.5 / 2017-1-22\n=================\n  * [#815](https://github.com/NetEase/pomelo/pull/815)\n  * [#901](https://github.com/NetEase/pomelo/pull/901)\n\n2.2.4 / 2017-1-20\n=================\n  * [NEW] upgrade pomelo-rpc to 1.0.7\n\n2.2.3 / 2017-1-20\n=================\n  * [NEW] upgrade socket.io to 1.7.x\n\n2.2.2 / 2017-1-20\n=================\n  * [#899](https://github.com/NetEase/pomelo/pull/899)\n\n2.2.1 / 2017-1-20\n=================\n  * [FIX] fix push message with no array\n\n2.2.0 / 2017-1-19\n=================\n  * [NEW] upgrade pomelo-rpc to 1.0.6, rpc protocol moved to MQTT\n  * [NEW] upgrade pomelo-admin to 1.0.0\n  * [NEW] pure javaScript without need to install c++ addons \n\n1.2.1 / 2015-12-31\n================\n  * [NEW] upgrade ws to 0.8.0\n  * [#771](https://github.com/NetEase/pomelo/pull/771)\n  * [#774](https://github.com/NetEase/pomelo/pull/774)\n  * [FIX] tls: destory connection when clientError\n\n1.2.0 / 2015-09-18\n=================\n  * [NEW] upgrade pomelo-admin to 0.4.5\n  * [#751](https://github.com/NetEase/pomelo/pull/751)\n  * [#741](https://github.com/NetEase/pomelo/pull/741)\n  * [#740](https://github.com/NetEase/pomelo/pull/740)\n  * [FIX] fix wrong variable in protobuf\n\n1.1.9 / 2015-06-05\n=================\n  * [NEW] upgrade pomelo-rpc to 0.4.10\n  * [NEW] upgrade pomelo-admin to 0.4.4\n  * [NEW] upgrade pomelo-logger to 0.1.7\n\n1.1.8 / 2015-05-29\n=================\n  * fix bug on verison calculating when update proto files\n  * avoid modules loading error on windows\n  * [NEW] upgrade pomelo-protocol to 0.1.6\n\n1.1.7 / 2015-05-12\n=================\n  * [#706](https://github.com/NetEase/pomelo/pull/706)\n  * [#707](https://github.com/NetEase/pomelo/pull/707)\n  * [#443](https://github.com/NetEase/pomelo/pull/443)\n  * [#444](https://github.com/NetEase/pomelo/pull/444)\n  * [#713](https://github.com/NetEase/pomelo/pull/713)\n  * [NEW] upgrade pomelo-rpc to 0.4.9\n  * [NEW] upgrade pomelo-admin to 0.4.3\n\n1.1.6 / 2015-03-12\n=================\n  * [NEW] add configure file automatically reload feature\n  * [NEW] add mqtt connector heartbeat timeout option\n  * [NEW] upgrade pomelo-rpc to 0.4.8\n\n1.1.5 / 2015-02-26\n=================\n  * [NEW] upgrade pomelo-rpc to 0.4.7\n\n1.1.4 / 2015-01-23\n=================\n  [#670](https://github.com/NetEase/pomelo/pull/670)\n  [#669](https://github.com/NetEase/pomelo/pull/669)\n  [#666](https://github.com/NetEase/pomelo/pull/666)\n  [#665](https://github.com/NetEase/pomelo/pull/665)\n  [#662](https://github.com/NetEase/pomelo/pull/662)\n  [#659](https://github.com/NetEase/pomelo/pull/659)\n  [#657](https://github.com/NetEase/pomelo/pull/657)\n  [#653](https://github.com/NetEase/pomelo/pull/653)\n\n1.1.2 / 2014-11-12\n=================\n* [NEW] introduce updateUserInfo for connectionService\n  [#637](https://github.com/NetEase/pomelo/pull/637)\n* [FIX] fix wrong variable err using \n  [#642](https://github.com/NetEase/pomelo/pull/642)\n* [NEW] introduce cancelShutdownHook\n  [#644](https://github.com/NetEase/pomelo/pull/644)\n* [FIX] revert PR #613, which should not be accepted\n  [#649](https://github.com/NetEase/pomelo/pull/649)\n\n1.1.1 / 2014-10-10\n=================\n* [NEW] upgrade pomelo-protocol to 0.1.4\n  [#616](https://github.com/NetEase/pomelo/pull/616)\n* [FIX] incorrect this scope\n  [#622](https://github.com/NetEase/pomelo/pull/622)\n* [FIX] fix bug on arg parse\n  [#623](https://github.com/NetEase/pomelo/pull/623)\n* [FIX] connection without communication bug\n\n1.1.0 / 2014-09-12\n=================\n* [NEW] fit for libpomelo2\n* [NEW] upgrade pomelo-rpc to 0.4.5\n  [#612](https://github.com/NetEase/pomelo/pull/612)\n* [FIX] close http server after WebSocketServer.close\n  [#613](https://github.com/NetEase/pomelo/pull/613)\n* [FIX] update timeout.js\n  [#614](https://github.com/NetEase/pomelo/pull/614)\n* [FIX] fix typo\n\n1.0.4 / 2014-08-26\n=================\n* [NEW] upgrade pomelo-rpc to 0.4.3\n* [NEW] upgrade pomelo-logger to 0.1.6\n* [FIX] pomelo-masterha-plugin reconnect bug miss parameter env\n* [#582](https://github.com/NetEase/pomelo/pull/582)\n\n1.0.3 / 2014-07-18\n=================\n* [NEW] dictVersion : Similar to `protoVersion`, add `dictVersion` and skip sending dict when handshaking if possible.\n  [#572](https://github.com/NetEase/pomelo/pull/572)\n* [FIX] CRON : Upgrade pomelo-scheduler to v0.3.9. Fix a bug that will loss tasks if The number of days next month is greater than this month.\n  [#560](https://github.com/NetEase/pomelo/pull/560)\n  [pomelo-scheduler#4](https://github.com/NetEase/pomelo-scheduler/pull/4)\n* [FIX] hot update : Remove the error logging if a server doesn't have any handler.\n  [#562](https://github.com/NetEase/pomelo/pull/562)\n* [NEW] protobuf : Add `protobuf cache` into libpomelo, and add `useProto` handshake option to tell the client whether to use the protobuf.\n  [#564](https://github.com/NetEase/pomelo/pull/564)\n  [libpomelo#58](https://github.com/NetEase/libpomelo/pull/58)\n  [discuss(chinese)](http://nodejs.netease.com/topic/53c6c126898634292c8157a2)\n\n1.0.2 / 2014-07-10\n=================\n* fix server reconnect bug\n\n1.0.1 / 2014-07-03\n=================\n* merge pull request #538 #541 #545 #546 #547\n* update master watchdog notify method\n* upgrade pomelo-rpc to 0.4.2\n\n1.0.0 / 2014-06-19\n=================\n* mqtt connector\n* support ie6,7,8 with sioconnector\n* support hot update partially\n\n1.0.0-pre / 2014-05-16\n=================\n* add udpconnector\n* pomelo-rpc load balancing and fault tolerance\n* connector wss & tls support\n* pomelo-zookeeper-plugin\n* pomelo-scale-plugin\n* environment directory configuration support\n* pomelo-cli dynamic script\n\n0.9.10 / 2014-05-15\n=================\n* merge pull request #505\n* merge pull request #506\n* merge pull request from kaisatec\n* add getClientAddressBySessionId in sessionService\n\n0.9.9 / 2014-05-06\n=================\n* merge pull request #495\n* merge pull request #499\n* merge pull request #501\n* upgrade pomelo-admin to 0.3.4\n\n0.9.8 / 2014-05-04\n=================\n* remove unused module\n* update constants definition\n\n0.9.7 / 2014-04-25\n=================\n* merge pull request #486\n* add channelservice rpc error info\n\n0.9.6 / 2014-04-16\n=================\n* merge pull request #472\n* merge pull request #475\n* merge pull request #485\n* update ssh config\n\n0.9.5 / 2014-04-02\n=================\n* add support for different env\n* add session count method in sessionService\n* emit start_all_event\n* better prompt for init project\n\n0.9.4 / 2014-03-20\n=================\n* merge pull request from zhaohaojie\n* merge pull request from wuxian\n* merge pull request from roytan883\n* fix history.md year bug\n\n0.9.3 / 2014-03-11\n=================\n* upgrade pomelo-admin to 0.3.2 for npm reason\n\n0.9.2 / 2014-03-10\n=================\n* merge pull request sshPort\n* merge pull request #438\n* fix hybridconnector dict bug\n* upgrade pomelo-rpc to 0.3.2\n\n0.9.1 / 2014-03-03\n=================\n* fix pomelo stop auto-restart bug\n* add restart-force option\n* add application.require method\n* export constants.js\n\n0.9.0 / 2014-02-26\n=================\n* rpc support for zmq\n* rpc requests callback timeout\n* rpc support for hot restart\n* optimize for command line\n* support for connection blacklist\n* protobuf support for decodeIO-protobuf.js\n* channel serialization interface\n\n0.8.9 / 2014-02-21\n=================\n* fix fin_wait2 caused by socket.end bug\n\n0.8.8 / 2014-02-19\n=================\n* fix some typos in comment\n\n0.8.7 / 2014-01-28\n=================\n* refactor pomelo command, report remained servers if kill failed \n\n0.8.6 / 2014-01-22\n=================\n* upgrade pomelo-rpc 0.2.9\n* upgrade pomelo-admin 0.2.9\n\n0.8.5 / 2014-01-22\n=================\n* upgrade pomelo-rpc 0.2.8\n* upgrade pomelo-scheduler 0.3.8\n\n0.8.4 / 2014-01-20\n=================\n* fix bin/pomelo spell bug\n\n0.8.3 / 2014-01-16\n=================\n* add tcp socket close option\n* upgrade pomelo-rpc 0.2.7\n* upgrade pomelo-admin 0.2.8\n* upgrade pomelo-schedule 0.3.7\n\n0.8.2 / 2014-01-03\n=================\n* fix session kick bug issue #355\n* fix add rpc filter bug\n\n0.8.1 / 2013-12-31\n=================\n* upgrade pomelo-rpc to 0.2.6\n* handle rpc filter error\n* add test cases\n\n0.8.0 / 2013-12-24\n=================\n* refactor bin/pomelo\n* pushScheduler add option\n* add rpc invoke method\n* lifecycle callback feature\n* add rcp filter interface\n* simplify servers.json configuration\n* pomelo-logger dynamic log level\n* pomelo-rpc & pomelo-admin white list\n* pomelo-data-plugin\n\n0.7.7 / 2013-12-16\n=================\n* upgrade pomelo-loader to 0.0.6\n* upgrade pomelo-logger to 0.1.2(add dynamic change logger level feature)\n\n0.7.6 / 2013-12-3\n=================\n* upgrade pomelo-rpc to 0.2.4\n* upgrade pomelo-admin to 0.2.6(fix reconnect bug)\n\n0.7.5 / 2013-11-27\n=================\n* fix pomelo kill bug\n* fix rpc toobusy filter bug\n\n0.7.4 / 2013-11-20\n=================\n* fix pomelo add command\n* master start servers in 2 mode, detached in production, no detched in development\n\n0.7.3 / 2013-11-15\n=================\n* add heartbeat timeout option\n\n0.7.2 / 2013-11-14\n=================\n* add start server detached mode\n* add masterha for pomelo stop&list\n* fix auto-restart disconnect bug\n* update pomelo start for different envs\n\n0.7.1 / 2013-11-11\n=================\n* fix errorHandler bug\n* compatible for schedulerConfig\n\n0.7.0 / 2013-11-6\n=================\n* crontab\n* global filter\n* transaction\n* pomelo-cli auto-complete\n* some components rename\n\n0.6.8 / 2013-11-4\n=================\n* update pomelo-admin version\n\n0.6.7 / 2013-10-14\n=================\n* fix masterha monitor reconnect bug\n\n0.6.6 / 2013-10-12\n=================\n* merge pull request #303 replace tab & remove session get value argument\n* upgrade pomelo-admin to 0.2.4\n* upgrade pomelo-monitor to 0.3.7\n* upgrade pomelo-rpc to 0.2.2\n\n0.6.5 / 2013-9-30\n=================\n* fix server reconnect bug\n* upgrade pomelo-admin to 0.2.3\n\n0.6.4 / 2013-9-27\n=================\n* update logger config && test log4js config\n* update require pomelo path & unuse module\n* merge pull request update readme #295\n\n0.6.3 / 2013-9-10\n=================\n* fix tcp socket package bug\n* update filter parameters\n* merge pull request localSession unbind #289\n\n0.6.2 / 2013-9-5\n=================\n* upgrade pomelo-admin to 0.2.2\n* update test cases\n* fix socket.on end bug\n\n0.6.1 / 2013-9-2\n=================\n* update pomelo-admin & pomelo-rpc to 0.2.1\n* add rpcDebug module in master\n\n0.6.0 / 2013-8-26\n=================\n* interactive command line tool\n* plugin mechanism\n* data signature\n* handle invalid connections\n* rpc debug log\n* overload protection\n* servers reconnect mechanism\n* daemon start mode\n* packages upgrade\n\n0.5.5 / 2013-8-9\n=================\n* fix sioconnector bug\n* fix localSession bug\n* merge pull request\n\n0.5.4 / 2013-7-25\n=================\n* update pomelo-protocol version\n\n0.5.3 / 2013-7-25\n=================\n* update check forever method\n* update socket.io transport\n* remove redis dependency for test cases\n\n0.5.2 / 2013-7-23\n=================\n* fix hybridsocket send message bug\n* fix globalChannel nextTick bug\n* add some test cases\n\n0.5.1 / 2013-7-19\n=================\n* update pomelo-protobuf version\n* receive servers console data event in production environment\n\n\n0.5.0 / 2013-7-16\n=================\n\n* high availability for master(with zookeeper)\n* support global channel(with redis)\n* server bind to CPU\n* server auto-restart when server does not work(configurable) \n* add beforeStop hook for application\n\n0.4.6 / 2013-7-15\n=================\n\n* fix pomelo-protocol bug, which will lose message when requestId is 128 multiple\n\n0.4.5 / 2013-7-3\n=================\n\n* fix load scheduler component bug\n* fix hybridconnector check useDict bug\n* add keywords, issues, contributor infos to npm\n\n0.4.3 / 2013-6-13\n==================\n\n* fix client heartbeat timeout bug\n* fix command line debug argument bug\n\n0.4.2 / 2013-6-5\n==================\n\n* fix duplicated bind session bug\n* add `disconnectOnTimeout` option for hybridconnector\n* fix empty group push bug in channel\n* fix protobuf encode bug\n\n0.4.1 / 2013-5-28\n==================\n\n* refactor protocol layers\n* support multiple sessions of the same user\n\n0.3.10 / 2013-5-20\n==================\n\n* `pomelo-protocol` upgrades to 0.3.4\n* fix session bind bug in backend server\n* replace `childprocess.exec` with `spawn` in `starter.js`\n\n* fix configure bug\n\n0.3.9 / 2013-5-8\n==================\n\n* fix configure bug\n\n0.3.8 / 2013-5-6\n==================\n\n* fix tcpsocket close event bug\n* fix error handler bug\n\n0.3.7 / 2013-4-16\n==================\n\n* update templates\n* sioconnector supports flashsocket\n* add `distinctHost` to hybridconnector\n* fix rpc `cacheMsg` configure bugs\n\n0.3.6 / 2013-4-9\n==================\n\n* compatible with node 0.10 version\n* fix daemon forever bugs\n* add some unit test case\n\n0.3.5 / 2013-3-25\n==================\n\n* fix log4js not compatible bug\n* fix function redefined in localSessionServie\n\n0.3.4 / 2013-3-19\n==================\n\n* fix server not verifing useDict, useProtobuf bug\n* fix can not start pomelo from ide bug\n* add host param in listen for hybridconnector, which is important for some load balance strategy\n\n0.3.3 / 2013-3-12\n==================\n\n* fix double string decode bug when not compressing route\n\n0.3.2 / 2013-3-11\n==================\n\n* fix init template bug\n* modify command line help, version to --help, --version\n\n0.3.1 / 2013-3-7\n==================\n\n* add hybridconnector to support socket and websocket\n* add route dictionary and protobuf for binary protocol\n* add localSession query interfaces\n* add broadcast method for ChannelService\n\n0.2.5 / 2013-2-28\n==================\n\n* dynamic add and remove servers (watchdog module)\n* fix filterService before filter bug\n* fix connector component bug\n\n0.2.4 / 2013-1-4\n==================\n\n* fix stop components bug\n* add windows install .bat\n* add comand line windows compatible feature\n\n\n0.2.3 / 2012-12-25\n==================\n\n* add mkdirp, update pomelo-admin version\n* solve windows comptaible problem\n\n0.2.2 / 2012-12-9\n==================\n\n* add fail ids for channel push method\n* code format standardize\n\n\n0.2.0 / 2012-11-20\n==================\n\n* establish project on github\n\n\n0.1.x / before 2012-11\n==================\n\n* internal development for 11 months\n"
  },
  {
    "path": "LICENSE",
    "content": "(The MIT License)\n\nCopyright (c) 2012-2014 Netease, Inc. and other pomelo contributors\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "## Pomelo -- a fast, scalable game server framework for node.js\n\nPomelo is a fast, scalable game server framework for [node.js](http://nodejs.org).\nIt provides the basic development framework and many related components, including libraries and tools.\nPomelo is also suitable for real-time web applications; its distributed architecture makes pomelo scale better than other real-time web frameworks.\n\n[![Build Status](https://travis-ci.org/NetEase/pomelo.svg?branch=master)](https://travis-ci.org/NetEase/pomelo)\n\n * Homepage: <http://pomelo.netease.com/>\n * Mailing list: <https://groups.google.com/group/pomelo>\n * Documentation: <http://github.com/NetEase/pomelo>\n * Wiki: <https://github.com/NetEase/pomelo/wiki/>\n * Issues: <https://github.com/NetEase/pomelo/issues/>\n * Tags: game, nodejs\n\n\n## Features\n\n### Complete support of game server and realtime application server architecture\n\n* Multiple-player game: mobile, social, web, MMO rpg(middle size)\n* Realtime application: chat,  message push, etc.\n\n### Fast, scalable\n\n* Distributed (multi-process) architecture, can be easily scale up\n* Flexible server extension\n* Full performance optimization and test\n\n### Easy\n\n* Simple API: request, response, broadcast, etc.\n* Lightweight: high development efficiency based on node.js\n* Convention over configuration: almost zero config\n\n### Powerful\n\n* Many clients support, including javascript, flash, android, iOS, cocos2d-x, C\n* Many libraries and tools, including command line tool, admin tool, performance test tool, AI, path finding etc.\n* Good reference materials: full docs, many examples and [an open-source MMO RPG demo](https://github.com/NetEase/pomelo/wiki/Introduction-to--Lord-of-Pomelo)\n\n### Extensible\n\n* Support plugin architecture, easy to add new features through plugins. We also provide many plugins like online status, master high availability.\n* Custom features, users can define their own network protocol, custom components very easy.\n\n## Why should I use pomelo?\nFast, scalable, real-time game server development is not an easy job, and a good container or framework can reduce its complexity.\nUnfortunately, unlike web, finding a game server framework solution is difficult, especially an open source solution. Pomelo fills this gap, providing a full solution for building game server frameworks.\nPomelo has the following advantages:\n* The architecture is scalable. It uses a multi-process, single thread runtime architecture, which has been proven in the industry and is especially suited to the node.js thread model.\n* Easy to use, the development model is quite similar to web, using convention over configuration, with almost zero config. The [API](http://pomelo.netease.com/api.html) is also easy to use.\n* The framework is extensible. Based on the node.js micro module principle, the core of pomelo is small. All of the components, libraries and tools are individual npm modules, and anyone can create their own module to extend the framework.\n* The reference materials and documentation are quite complete. In addition to the documentation, we also provide [an open-source MMO RPG demo](https://github.com/NetEase/pomelo/wiki/Introduction-to--Lord-of-Pomelo) (HTML5 client), which is a far better reference material than any book.\n\n## How can I develop with pomelo?\nWith the following references, you can quickly familiarize yourself with the pomelo development process:\n* [Pomelo documents](https://github.com/NetEase/pomelo/wiki)\n* [Getting started](https://github.com/NetEase/pomelo/wiki/Welcome-to-Pomelo)\n* [Tutorial](https://github.com/NetEase/pomelo/wiki/Preface)\n\n\n## Contributors\n* NetEase, Inc. (@NetEase)\n* Peter Johnson(@missinglink)\n* Aaron Yoshitake \n* @D-Deo \n* Eduard Gotwig\n* Eric Muyser(@stokegames)\n* @GeforceLee\n* Harold Jiang(@jzsues)\n* @ETiV\n* [kaisatec](https://github.com/kaisatec)\n* [roytan883](https://github.com/roytan883)\n* [wuxian](https://github.com/wuxian)\n* [zxc122333](https://github.com/zxc122333)\n* [newebug](https://github.com/newebug)\n* [jiangzhuo](https://github.com/jiangzhuo)\n* [youxiachai](https://github.com/youxiachai)\n* [qiankanglai](https://github.com/qiankanglai)\n* [xieren58](https://github.com/xieren58)\n* [prim](https://github.com/prim)\n* [Akaleth](https://github.com/Akaleth)\n* [pipi32167](https://github.com/pipi32167)\n* [ljhsai](https://github.com/ljhsai)\n* [zhanghaojie](https://github.com/zhanghaojie)\n* [airandfingers](https://github.com/airandfingers)\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2012-2017 NetEase, Inc. and other contributors\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "bin/pomelo",
    "content": "#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\nvar fs = require('fs'),\n  os = require('os'),\n  path = require('path'),\n  util = require('util'),\n  cliff = require('cliff'),\n  mkdirp = require('mkdirp'),\n  co = require('../lib/modules/console'),\n  utils = require('../lib/util/utils'),\n  starter = require('../lib/master/starter'),\n  exec = require('child_process').exec,\n  spawn = require('child_process').spawn,\n  version = require('../package.json').version,\n  adminClient = require('pomelo-admin').adminClient,\n  constants = require('../lib/util/constants'),\n  program = require('commander');\n\n/**\n *  Constant Variables\n */\nvar TIME_INIT = 1 * 1000;\nvar TIME_KILL_WAIT = 5 * 1000;\nvar KILL_CMD_LUX = 'kill -9 `ps -ef|grep node|awk \\'{print $2}\\'`';\nvar KILL_CMD_WIN = 'taskkill /im node.exe /f';\n\nvar CUR_DIR = process.cwd();\nvar DEFAULT_GAME_SERVER_DIR = CUR_DIR;\nvar DEFAULT_USERNAME = 'admin';\nvar DEFAULT_PWD = 'admin';\nvar DEFAULT_ENV = 'development';\nvar DEFAULT_MASTER_HOST = '127.0.0.1';\nvar DEFAULT_MASTER_PORT = 3005;\n\nvar CONNECT_ERROR = 'Fail to connect to admin console server.';\nvar FILEREAD_ERROR = 'Fail to read the file, please check if the application is started legally.';\nvar CLOSEAPP_INFO = 'Closing the application......\\nPlease wait......';\nvar ADD_SERVER_INFO = 'Successfully add server.';\nvar RESTART_SERVER_INFO = 'Successfully restart server.';\nvar INIT_PROJ_NOTICE = '\\nThe default admin user is: \\n\\n'+ '  username'.green + ': admin\\n  ' + 'password'.green+ ': admin\\n\\nYou can configure admin users by editing adminUser.json later.\\n ';\nvar SCRIPT_NOT_FOUND = 'Fail to find an appropriate script to run,\\nplease check the current work directory or the directory specified by option `--directory`.\\n'.red;\nvar MASTER_HA_NOT_FOUND = 'Fail to find an appropriate masterha config file, \\nplease check the current work directory or the arguments passed to.\\n'.red;\nvar COMMAND_ERROR = 'Illegal command format. Use `pomelo --help` to get more info.\\n'.red;\nvar DAEMON_INFO = 'The application is running in the background now.\\n';\n\nprogram.version(version);\n\nprogram.command('init [path]')\n  .description('create a new application')\n  .action(function(path) {\n    init(path || CUR_DIR);\n  });\n\nprogram.command('start')\n  .description('start the application')\n  .option('-e, --env <env>', 'the used environment', DEFAULT_ENV)\n  .option('-D, --daemon', 'enable the daemon start')\n  .option('-d, --directory, <directory>', 'the code directory', DEFAULT_GAME_SERVER_DIR)\n  .option('-t, --type <server-type>,', 'start server type')\n  .option('-i, --id <server-id>', 'start server id')\n  .action(function(opts) {\n    start(opts);\n  });\n\nprogram.command('list')\n  .description('list the servers')\n  .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)\n  .option('-p, --password <password>', 'administration password', DEFAULT_PWD)\n  .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)\n  .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)\n  .action(function(opts) {\n    list(opts);\n  });\n\nprogram.command('add')\n  .description('add a new server')\n  .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)\n  .option('-p, --password <password>', 'administration password', DEFAULT_PWD)\n  .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)\n  .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)\n  .action(function() {\n    var args = [].slice.call(arguments, 0);\n    var opts = args[args.length - 1];\n    opts.args = args.slice(0, -1);\n    add(opts);\n  });\n\nprogram.command('stop')\n  .description('stop the servers, for multiple servers, use `pomelo stop server-id-1 server-id-2`')\n  .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)\n  .option('-p, --password <password>', 'administration password', DEFAULT_PWD)\n  .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)\n  .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)\n  .action(function() {\n    var args = [].slice.call(arguments, 0);\n    var opts = args[args.length - 1];\n    opts.serverIds = args.slice(0, -1);\n    terminal('stop', opts);\n  });\n\nprogram.command('kill')\n  .description('kill the application')\n  .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)\n  .option('-p, --password <password>', 'administration password', DEFAULT_PWD)\n  .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)\n  .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)\n  .option('-f, --force', 'using this option would kill all the node processes')\n  .action(function() {\n    var args = [].slice.call(arguments, 0);\n    var opts = args[args.length - 1];\n    opts.serverIds = args.slice(0, -1);\n    terminal('kill', opts);\n  });\n\nprogram.command('restart')\n  .description('restart the servers, for multiple servers, use `pomelo restart server-id-1 server-id-2`')\n  .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)\n  .option('-p, --password <password>', 'administration password', DEFAULT_PWD)\n  .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)\n  .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)\n  .option('-t, --type <server-type>,', 'start server type')\n  .option('-i, --id <server-id>', 'start server id')\n  .action(function(opts) {\n    restart(opts);\n  });\n\nprogram.command('masterha')\n  .description('start all the slaves of the master')\n  .option('-d, --directory <directory>', 'the code directory', DEFAULT_GAME_SERVER_DIR)\n  .action(function(opts) {\n    startMasterha(opts);\n  });\n\nprogram.command('*')\n  .action(function() {\n    console.log(COMMAND_ERROR);\n  });\n\nprogram.parse(process.argv);\n\n/**\n * Init application at the given directory `path`.\n *\n * @param {String} path\n */\nfunction init(path) {\n  console.log(INIT_PROJ_NOTICE);\n  connectorType(function(type) {\n    emptyDirectory(path, function(empty) {\n      if(empty) {\n        process.stdin.destroy();\n        createApplicationAt(path, type);\n      } else {\n        confirm('Destination is not empty, continue? (y/n) [no] ', function(force) {\n          process.stdin.destroy();\n          if(force) {\n            createApplicationAt(path, type);\n          } else {\n            abort('Fail to init a project'.red);\n          }\n        });\n      }\n    });\n  });\n}\n\n/**\n * Create directory and files at the given directory `path`.\n *\n * @param {String} ph\n */\nfunction createApplicationAt(ph, type) {\n  var name = path.basename(path.resolve(CUR_DIR, ph));\n  copy(path.join(__dirname, '../template/'), ph);\n  mkdir(path.join(ph, 'game-server/logs'));\n  mkdir(path.join(ph, 'shared'));\n  // rmdir -r\n  var rmdir = function(dir) {\n    var list = fs.readdirSync(dir);\n    for(var i = 0; i < list.length; i++) {\n      var filename = path.join(dir, list[i]);\n      var stat = fs.statSync(filename);\n      if(filename === \".\" || filename === \"..\") {\n      } else if(stat.isDirectory()) {\n        rmdir(filename);\n      } else {\n        fs.unlinkSync(filename);\n      }\n    }\n    fs.rmdirSync(dir);\n  };\n  setTimeout(function() {\n    switch(type) {\n      case '1':\n         // use websocket\n         var unlinkFiles = ['game-server/app.js.sio',\n         'game-server/app.js.wss',\n         'game-server/app.js.mqtt',\n         'game-server/app.js.sio.wss',\n         'game-server/app.js.udp',\n         'web-server/app.js.https',\n         'web-server/public/index.html.sio',\n         'web-server/public/js/lib/pomeloclient.js',\n         'web-server/public/js/lib/pomeloclient.js.wss',\n         'web-server/public/js/lib/build/build.js.wss',\n         'web-server/public/js/lib/socket.io.js'];\n         for(var i = 0; i < unlinkFiles.length; ++i) {\n            fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));\n         }\n        break;\n        case '2':\n          // use socket.io\n          var unlinkFiles = ['game-server/app.js',\n          'game-server/app.js.wss',\n          'game-server/app.js.udp',\n          'game-server/app.js.mqtt',\n          'game-server/app.js.sio.wss',\n          'web-server/app.js.https',\n          'web-server/public/index.html',\n          'web-server/public/js/lib/component.json',\n          'web-server/public/js/lib/pomeloclient.js.wss'];\n          for(var i = 0; i < unlinkFiles.length; ++i) {\n            fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));\n          }\n\n          fs.renameSync(path.resolve(ph, 'game-server/app.js.sio'), path.resolve(ph, 'game-server/app.js'));\n          fs.renameSync(path.resolve(ph, 'web-server/public/index.html.sio'), path.resolve(ph, 'web-server/public/index.html'));\n\n          rmdir(path.resolve(ph, 'web-server/public/js/lib/build'));\n          rmdir(path.resolve(ph, 'web-server/public/js/lib/local'));\n          break;\n        case '3':\n          // use websocket wss\n          var unlinkFiles = ['game-server/app.js.sio',\n          'game-server/app.js',\n          'game-server/app.js.udp',\n          'game-server/app.js.sio.wss',\n          'game-server/app.js.mqtt',\n          'web-server/app.js',\n          'web-server/public/index.html.sio',\n          'web-server/public/js/lib/pomeloclient.js',\n          'web-server/public/js/lib/pomeloclient.js.wss',\n          'web-server/public/js/lib/build/build.js',\n          'web-server/public/js/lib/socket.io.js'];\n          for(var i = 0; i < unlinkFiles.length; ++i) {\n            fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));\n          }\n\n          fs.renameSync(path.resolve(ph, 'game-server/app.js.wss'), path.resolve(ph, 'game-server/app.js'));\n          fs.renameSync(path.resolve(ph, 'web-server/app.js.https'), path.resolve(ph, 'web-server/app.js'));\n          fs.renameSync(path.resolve(ph, 'web-server/public/js/lib/build/build.js.wss'), path.resolve(ph, 'web-server/public/js/lib/build/build.js'));\n          break;\n        case '4':\n          // use socket.io wss\n           var unlinkFiles = ['game-server/app.js.sio',\n          'game-server/app.js',\n          'game-server/app.js.udp',\n          'game-server/app.js.wss',\n          'game-server/app.js.mqtt',\n          'web-server/app.js',\n          'web-server/public/index.html',\n          'web-server/public/js/lib/pomeloclient.js'];\n          for(var i = 0; i < unlinkFiles.length; ++i) {\n            fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));\n          }\n\n          fs.renameSync(path.resolve(ph, 'game-server/app.js.sio.wss'), path.resolve(ph, 'game-server/app.js'));\n          fs.renameSync(path.resolve(ph, 'web-server/app.js.https'), path.resolve(ph, 'web-server/app.js'));\n          fs.renameSync(path.resolve(ph, 'web-server/public/index.html.sio'), path.resolve(ph, 'web-server/public/index.html'));\n          fs.renameSync(path.resolve(ph, 'web-server/public/js/lib/pomeloclient.js.wss'), path.resolve(ph, 'web-server/public/js/lib/pomeloclient.js'));\n\n          rmdir(path.resolve(ph, 'web-server/public/js/lib/build'));\n          rmdir(path.resolve(ph, 'web-server/public/js/lib/local'));\n          fs.unlinkSync(path.resolve(ph, 'web-server/public/js/lib/component.json'));\n          break;\n        case '5':\n          // use socket.io wss\n           var unlinkFiles = ['game-server/app.js.sio',\n          'game-server/app.js',\n          'game-server/app.js.wss',\n          'game-server/app.js.mqtt',\n          'game-server/app.js.sio.wss',\n          'web-server/app.js.https',\n          'web-server/public/index.html',\n          'web-server/public/js/lib/component.json',\n          'web-server/public/js/lib/pomeloclient.js.wss'];\n          for(var i = 0; i < unlinkFiles.length; ++i) {\n            fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));\n          }\n          \n          fs.renameSync(path.resolve(ph, 'game-server/app.js.udp'), path.resolve(ph, 'game-server/app.js'));\n          rmdir(path.resolve(ph, 'web-server/public/js/lib/build'));\n          rmdir(path.resolve(ph, 'web-server/public/js/lib/local'));\n          break;\n        case '6':\n          // use socket.io\n          var unlinkFiles = ['game-server/app.js',\n          'game-server/app.js.wss',\n          'game-server/app.js.udp',\n          'game-server/app.js.sio',\n          'game-server/app.js.sio.wss',\n          'web-server/app.js.https',\n          'web-server/public/index.html',\n          'web-server/public/js/lib/component.json',\n          'web-server/public/js/lib/pomeloclient.js.wss'];\n          for(var i = 0; i < unlinkFiles.length; ++i) {\n            fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));\n          }\n\n          fs.renameSync(path.resolve(ph, 'game-server/app.js.mqtt'), path.resolve(ph, 'game-server/app.js'));\n          fs.renameSync(path.resolve(ph, 'web-server/public/index.html.sio'), path.resolve(ph, 'web-server/public/index.html'));\n\n          rmdir(path.resolve(ph, 'web-server/public/js/lib/build'));\n          rmdir(path.resolve(ph, 'web-server/public/js/lib/local'));\n          break;\n        }\n        var replaceFiles = ['game-server/app.js',\n        'game-server/package.json',\n        'web-server/package.json'];\n        for(var j = 0; j < replaceFiles.length; j++) {\n          var str = fs.readFileSync(path.resolve(ph, replaceFiles[j])).toString();\n          fs.writeFileSync(path.resolve(ph, replaceFiles[j]), str.replace('$', name));\n        }\n        var f = path.resolve(ph, 'game-server/package.json');\n        var content = fs.readFileSync(f).toString();\n        fs.writeFileSync(f, content.replace('#', version));\n      }, TIME_INIT);\n}\n\n\n/**\n * Start application.\n *\n * @param {Object} opts options for `start` operation\n */\nfunction start(opts) {\n  var absScript = path.resolve(opts.directory, 'app.js');\n  if (!fs.existsSync(absScript)) {\n    abort(SCRIPT_NOT_FOUND);\n  }\n\n  var logDir = path.resolve(opts.directory, 'logs');\n  if (!fs.existsSync(logDir)) {\n    mkdir(logDir);\n  }\n  \n  var ls;\n  var type = opts.type || constants.RESERVED.ALL;\n  var params = [absScript, 'env=' + opts.env, 'type=' + type];\n  if(!!opts.id) {\n    params.push('startId=' + opts.id);\n  }\n  if (opts.daemon) {\n    ls = spawn(process.execPath, params, {detached: true, stdio: 'ignore'});\n    ls.unref();\n    console.log(DAEMON_INFO);\n    process.exit(0);\n  } else {\n    ls = spawn(process.execPath, params);\n    ls.stdout.on('data', function(data) {\n      console.log(data.toString());\n    });\n    ls.stderr.on('data', function(data) {\n      console.log(data.toString());\n    });\n  }\n}\n\n/**\n * List pomelo processes.\n *\n * @param {Object} opts options for `list` operation\n */\nfunction list(opts) {\n  var id = 'pomelo_list_' + Date.now();\n  connectToMaster(id, opts, function(client) {\n    client.request(co.moduleId, {signal: 'list'}, function(err, data) {\n      if(err) {\n        console.error(err);\n      }\n      var servers = [];\n      for(var key in data.msg) {\n        servers.push(data.msg[key]);\n      }\n      var comparer = function(a, b) {\n        if (a.serverType < b.serverType) {\n          return -1;\n        } else if (a.serverType > b.serverType) {\n          return 1;\n        } else if (a.serverId < b.serverId) {\n          return -1;\n        } else if (a.serverId > b.serverId) {\n          return 1;\n        } else {\n          return 0;\n        }\n      };\n      servers.sort(comparer);\n      var rows = [];\n      rows.push(['serverId', 'serverType', 'pid', 'rss(M)', 'heapTotal(M)', 'heapUsed(M)', 'uptime(m)']);\n      servers.forEach(function(server) {\n        rows.push([server.serverId, server.serverType, server.pid, server.rss, server.heapTotal, server.heapUsed, server.uptime]);\n      });\n      console.log(cliff.stringifyRows(rows, ['red', 'blue', 'green', 'cyan', 'magenta', 'white', 'yellow']));\n      process.exit(0);\n    });\n  });\n}\n\n/**\n * Add server to application.\n *\n * @param {Object} opts options for `add` operation\n */\nfunction add(opts) {\n  var id = 'pomelo_add_' + Date.now();\n  connectToMaster(id, opts, function(client) {\n    client.request(co.moduleId, { signal: 'add', args: opts.args }, function(err) {\n      if(err) {\n        console.error(err);\n      }\n      else {\n        console.info(ADD_SERVER_INFO);\n      }\n      process.exit(0);\n    });\n  });\n}\n\n/**\n * Terminal application.\n *\n * @param {String} signal stop/kill\n * @param {Object} opts options for `stop/kill` operation\n */\nfunction terminal(signal, opts) {\n  console.info(CLOSEAPP_INFO);\n  // option force just for `kill`\n  if(opts.force) {\n    if (os.platform() === constants.PLATFORM.WIN) {\n      exec(KILL_CMD_WIN);\n    } else {\n      exec(KILL_CMD_LUX);\n    }\n    process.exit(1);\n    return;\n  }\n  var id = 'pomelo_terminal_' + Date.now();\n  connectToMaster(id, opts, function(client) {\n    client.request(co.moduleId, {\n      signal: signal, ids: opts.serverIds\n    }, function(err, msg) {\n      if(err) {\n        console.error(err);\n      }\n      if(signal === 'kill') {\n        if(msg.code === 'ok') {\n          console.log('All the servers have been terminated!');\n        } else {\n          console.log('There may be some servers remained:', msg.serverIds);\n        }\n      }\n      process.exit(0);\n    });\n  });\n}\n\nfunction restart(opts) {\n  var id = 'pomelo_restart_' + Date.now();\n  var serverIds = [];\n  var type = null;\n  if(!!opts.id) {\n    serverIds.push(opts.id);\n  }\n  if(!!opts.type) {\n    type = opts.type;\n  }\n  connectToMaster(id, opts, function(client) {\n    client.request(co.moduleId, { signal: 'restart', ids: serverIds, type: type}, function(err, fails) {\n      if(!!err) {\n        console.error(err);\n      } else if(!!fails.length) {\n        console.info('restart fails server ids: %j', fails);\n      } else {\n        console.info(RESTART_SERVER_INFO);\n      }\n      process.exit(0);\n    });\n  });\n}\n\nfunction connectToMaster(id, opts, cb) {\n  var client = new adminClient({username: opts.username, password: opts.password, md5: true});\n  client.connect(id, opts.host, opts.port, function(err) {\n    if(err) {\n      abort(CONNECT_ERROR + err.red);\n    }\n    if(typeof cb === 'function') {\n      cb(client);\n    }\n  });\n}\n\n/**\n * Start master slaves.\n *\n * @param {String} option for `startMasterha` operation\n */\nfunction startMasterha(opts) {\n  var configFile = path.join(opts.directory, constants.FILEPATH.MASTER_HA);\n  if(!fs.existsSync(configFile)) {\n    abort(MASTER_HA_NOT_FOUND);\n  }\n  var masterha = require(configFile).masterha;\n  for(var i=0; i<masterha.length; i++) {\n    var server = masterha[i];\n    server.mode = constants.RESERVED.STAND_ALONE;\n    server.masterha = 'true';\n    server.home = opts.directory;\n    runServer(server);\n  }\n}\n\n/**\n * Check if the given directory `path` is empty.\n *\n * @param {String} path\n * @param {Function} fn\n */\nfunction emptyDirectory(path, fn) {\n  fs.readdir(path, function(err, files) {\n    if(err && 'ENOENT' !== err.code) {\n      abort(FILEREAD_ERROR);\n    }\n    fn(!files || !files.length);\n  });\n}\n\n/**\n * Prompt confirmation with the given `msg`.\n *\n * @param {String} msg\n * @param {Function} fn\n */\nfunction confirm(msg, fn) {\n  prompt(msg, function(val) {\n    fn(/^ *y(es)?/i.test(val));\n  });\n}\n\n/**\n * Prompt input with the given `msg` and callback `fn`.\n *\n * @param {String} msg\n * @param {Function} fn\n */\nfunction prompt(msg, fn) {\n  if(' ' === msg[msg.length - 1]) {\n    process.stdout.write(msg);\n  } else {\n    console.log(msg);\n  }\n  process.stdin.setEncoding('ascii');\n  process.stdin.once('data', function(data) {\n    fn(data);\n  }).resume();\n}\n\n/**\n * Exit with the given `str`.\n *\n * @param {String} str\n */\nfunction abort(str) {\n  console.error(str);\n  process.exit(1);\n}\n\n/**\n * Copy template files to project.\n *\n * @param {String} origin\n * @param {String} target\n */\nfunction copy(origin, target) {\n  if(!fs.existsSync(origin)) {\n    abort(origin + 'does not exist.');\n  }\n  if(!fs.existsSync(target)) {\n    mkdir(target);\n    console.log('   create : '.green + target);\n  }\n  fs.readdir(origin, function(err, datalist) {\n    if(err) {\n      abort(FILEREAD_ERROR);\n    }\n    for(var i = 0; i < datalist.length; i++) {\n      var oCurrent = path.resolve(origin, datalist[i]);\n      var tCurrent = path.resolve(target, datalist[i]);\n      if(fs.statSync(oCurrent).isFile()) {\n        fs.writeFileSync(tCurrent, fs.readFileSync(oCurrent, ''), '');\n        console.log('   create : '.green + tCurrent);\n      } else if(fs.statSync(oCurrent).isDirectory()) {\n        copy(oCurrent, tCurrent);\n      }\n    }\n  });\n}\n\n/**\n * Mkdir -p.\n *\n * @param {String} path\n * @param {Function} fn\n */\nfunction mkdir(path, fn) {\n  mkdirp(path, 0755, function(err){\n    if(err) {\n      throw err;\n    }\n    console.log('   create : '.green + path);\n    if(typeof fn === 'function') {\n      fn();\n    }\n  });\n}\n\n/**\n * Get user's choice on connector selecting\n * \n * @param {Function} cb\n */\nfunction connectorType(cb) {\n  prompt('Please select underly connector, 1 for websocket(native socket), 2 for socket.io, 3 for wss, 4 for socket.io(wss), 5 for udp, 6 for mqtt: [1]', function(msg) {\n    switch(msg.trim()) {\n      case '':\n         cb(1);\n         break;\n      case '1':\n      case '2':\n      case '3':\n      case '4':\n      case '5':\n      case '6':\n         cb(msg.trim());\n         break;\n      default:\n         console.log('Invalid choice! Please input 1 - 5.'.red + '\\n');\n         connectorType(cb);\n         break;\n    }\n  });\n}\n\n/**\n * Run server.\n * \n * @param {Object} server server information\n */\nfunction runServer(server) {\n  var cmd, key;\n  var main = path.resolve(server.home, 'app.js');\n  if(utils.isLocal(server.host)) {\n    var options = [];\n    options.push(main);\n    for(key in server) {\n      options.push(util.format('%s=%s', key, server[key]));\n    }\n    starter.localrun(process.execPath, null, options);\n  } else {\n    cmd = util.format('cd \"%s\" && \"%s\"', server.home, process.execPath);\n    cmd += util.format(' \"%s\" ', main);\n    for(key in server) {\n      cmd += util.format(' %s=%s ', key, server[key]);\n    }\n    starter.sshrun(cmd, server.host);\n  }\n}"
  },
  {
    "path": "coverage/blanket.js",
    "content": "var path = require('path');\nvar srcDir = path.join(__dirname, '..', 'lib');\n\nrequire('blanket')({\n  pattern: srcDir\n});"
  },
  {
    "path": "gruntfile.js",
    "content": "'use strict';\n\nmodule.exports = function(grunt) {\n\n  grunt.loadNpmTasks('grunt-mocha-test');\n  grunt.loadNpmTasks('grunt-contrib-clean');\n  grunt.loadNpmTasks('grunt-contrib-jshint');\n\n  var src = ['test/manager/taskManager.js', 'test/filters/*.js', \n  'test/remote/*.js', 'test/service/*.js', 'test/modules/*.js', 'test/util/*.js', 'test/*.js'];\n\n  // Project configuration.\n  grunt.initConfig({\n    mochaTest: {\n       test: {\n        options: {\n          reporter: 'spec',\n          timeout: 5000,\n          require: 'coverage/blanket'\n        },\n        src: src\n      },\n      coverage: {\n        options: {\n          reporter: 'html-cov',\n          quiet: true,\n          captureFile: 'coverage.html'\n        },\n        src: src\n      }\n    },\n    clean: {\n      \"coverage.html\" : {\n        src: ['coverage.html']\n      }\n    },\n    jshint: {\n      all: ['lib/*']\n    }\n  });\n\n  // Default task.\n  grunt.registerTask('default', ['clean', 'mochaTest', 'jshint']);\n};"
  },
  {
    "path": "index.js",
    "content": "module.exports = require('./lib/pomelo');"
  },
  {
    "path": "lib/application.js",
    "content": "/*!\n * Pomelo -- proto\n * Copyright(c) 2012 xiechengchao <xiecc@163.com>\n * MIT Licensed\n */\n\n/**\n * Module dependencies.\n */\nvar utils = require('./util/utils');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar EventEmitter = require('events').EventEmitter;\nvar events = require('./util/events');\nvar appUtil = require('./util/appUtil');\nvar Constants = require('./util/constants');\nvar appManager = require('./common/manager/appManager');\nvar fs = require('fs');\nvar path = require('path');\n\n/**\n * Application prototype.\n *\n * @module\n */\nvar Application = module.exports = {};\n\n/**\n * Application states\n */\nvar STATE_INITED  = 1;  // app has inited\nvar STATE_START = 2;  // app start\nvar STATE_STARTED = 3;  // app has started\nvar STATE_STOPED  = 4;  // app has stoped\n\n/**\n * Initialize the server.\n *\n *   - setup default configuration\n */\nApplication.init = function(opts) {\n  opts = opts || {};\n  this.loaded = [];       // loaded component list\n  this.components = {};   // name -> component map\n  this.settings = {};     // collection keep set/get\n  var base = opts.base || path.dirname(require.main.filename);\n  this.set(Constants.RESERVED.BASE, base, true);\n  this.event = new EventEmitter();  // event object to sub/pub events\n\n  // current server info\n  this.serverId = null;   // current server id\n  this.serverType = null; // current server type\n  this.curServer = null;  // current server info\n  this.startTime = null; // current server start time\n\n  // global server infos\n  this.master = null;         // master server info\n  this.servers = {};          // current global server info maps, id -> info\n  this.serverTypeMaps = {};   // current global type maps, type -> [info]\n  this.serverTypes = [];      // current global server type list\n  this.lifecycleCbs = {};     // current server custom lifecycle callbacks\n  this.clusterSeq = {};       // cluster id seqence\n\n  appUtil.defaultConfiguration(this);\n\n  this.state = STATE_INITED;\n  logger.info('application inited: %j', this.getServerId());\n};\n\n/**\n * Get application base path\n *\n *  // cwd: /home/game/\n *  pomelo start\n *  // app.getBase() -> /home/game\n *\n * @return {String} application base path\n *\n * @memberOf Application\n */\nApplication.getBase = function() {\n  return this.get(Constants.RESERVED.BASE);\n};\n\n/**\n * Override require method in application\n *\n * @param {String} relative path of file\n *\n * @memberOf Application\n */\nApplication.require = function(ph) {\n  return require(path.join(Application.getBase(), ph));\n};\n\n/**\n * Configure logger with {$base}/config/log4js.json\n * \n * @param {Object} logger pomelo-logger instance without configuration\n *\n * @memberOf Application\n */\nApplication.configureLogger = function(logger) {\n  if (process.env.POMELO_LOGGER !== 'off') {\n    var base = this.getBase();\n    var env = this.get(Constants.RESERVED.ENV);\n    var originPath = path.join(base, Constants.FILEPATH.LOG);\n    var presentPath = path.join(base, Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.LOG));\n    if(fs.existsSync(originPath)) {\n      logger.configure(originPath, {serverId: this.serverId, base: base});\n    } else if(fs.existsSync(presentPath)) {\n      logger.configure(presentPath, {serverId: this.serverId, base: base});\n    } else {\n      logger.error('logger file path configuration is error.');\n    }\n  }\n};\n\n/**\n * add a filter to before and after filter\n *\n * @param {Object} filter provide before and after filter method.\n *                        A filter should have two methods: before and after.\n * @memberOf Application\n */\nApplication.filter = function (filter) {\n  this.before(filter);\n  this.after(filter);\n};\n\n/**\n * Add before filter.\n *\n * @param {Object|Function} bf before fileter, bf(msg, session, next)\n * @memberOf Application\n */\nApplication.before = function (bf) {\n  addFilter(this, Constants.KEYWORDS.BEFORE_FILTER, bf);\n};\n\n/**\n * Add after filter.\n *\n * @param {Object|Function} af after filter, `af(err, msg, session, resp, next)`\n * @memberOf Application\n */\nApplication.after = function (af) {\n  addFilter(this, Constants.KEYWORDS.AFTER_FILTER, af);\n};\n\n/**\n * add a global filter to before and after global filter\n *\n * @param {Object} filter provide before and after filter method.\n *                        A filter should have two methods: before and after.\n * @memberOf Application\n */\nApplication.globalFilter = function (filter) {\n  this.globalBefore(filter);\n  this.globalAfter(filter);\n};\n\n/**\n * Add global before filter.\n *\n * @param {Object|Function} bf before fileter, bf(msg, session, next)\n * @memberOf Application\n */\nApplication.globalBefore = function (bf) {\n  addFilter(this, Constants.KEYWORDS.GLOBAL_BEFORE_FILTER, bf);\n};\n\n/**\n * Add global after filter.\n *\n * @param {Object|Function} af after filter, `af(err, msg, session, resp, next)`\n * @memberOf Application\n */\nApplication.globalAfter = function (af) {\n  addFilter(this, Constants.KEYWORDS.GLOBAL_AFTER_FILTER, af);\n};\n\n/**\n * Add rpc before filter.\n *\n * @param {Object|Function} bf before fileter, bf(serverId, msg, opts, next)\n * @memberOf Application\n */\nApplication.rpcBefore = function(bf) {\n  addFilter(this, Constants.KEYWORDS.RPC_BEFORE_FILTER, bf);\n};\n\n/**\n * Add rpc after filter.\n *\n * @param {Object|Function} af after filter, `af(serverId, msg, opts, next)`\n * @memberOf Application\n */\nApplication.rpcAfter = function(af) {\n  addFilter(this, Constants.KEYWORDS.RPC_AFTER_FILTER, af);\n};\n\n/**\n * add a rpc filter to before and after rpc filter\n *\n * @param {Object} filter provide before and after filter method.\n *                        A filter should have two methods: before and after.\n * @memberOf Application\n */\nApplication.rpcFilter = function(filter) {\n  this.rpcBefore(filter);\n  this.rpcAfter(filter);\n};\n\n/**\n * Load component\n *\n * @param  {String} name    (optional) name of the component\n * @param  {Object} component component instance or factory function of the component\n * @param  {[type]} opts    (optional) construct parameters for the factory function\n * @return {Object}     app instance for chain invoke\n * @memberOf Application\n */\nApplication.load = function(name, component, opts) {\n  if(typeof name !== 'string') {\n    opts = component;\n    component = name;\n    name = null;\n    if(typeof component.name === 'string') {\n      name = component.name;\n    }\n  }\n\n  if(typeof component === 'function') {\n    component = component(this, opts);\n  }\n\n  if(!name && typeof component.name === 'string') {\n    name = component.name;\n  }\n\n  if(name && this.components[name]) {\n    // ignore duplicat component\n    logger.warn('ignore duplicate component: %j', name);\n    return;\n  }\n\n  this.loaded.push(component);\n  if(name) {\n    // components with a name would get by name throught app.components later.\n    this.components[name] = component;\n  }\n\n  return this;\n};\n\n/**\n * Load Configure json file to settings.(support different enviroment directory & compatible for old path)\n *\n * @param {String} key environment key\n * @param {String} val environment value\n * @param {Boolean} reload whether reload after change default false\n * @return {Server|Mixed} for chaining, or the setting value\n * @memberOf Application\n */\nApplication.loadConfigBaseApp = function (key, val, reload) {\n  var self = this;\n  var env = this.get(Constants.RESERVED.ENV);\n  var originPath = path.join(Application.getBase(), val);\n  var presentPath = path.join(Application.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(val));\n  var realPath;\n  if(fs.existsSync(originPath)) {\n     realPath = originPath;\n     var file = require(originPath);\n     if (file[env]) {\n       file = file[env];\n     }\n     this.set(key, file);\n  } else if(fs.existsSync(presentPath)) {\n    realPath = presentPath;\n    var pfile = require(presentPath);\n    this.set(key, pfile);\n  } else {\n    logger.error('invalid configuration with file path: %s', key);\n  }\n\n  if(!!realPath && !!reload) {\n    fs.watch(realPath, function (event, filename) {\n      if(event === 'change') {\n        delete require.cache[require.resolve(realPath)];\n        self.loadConfigBaseApp(key, val);\n      }\n    });\n  }\n};\n\n/**\n * Load Configure json file to settings.\n *\n * @param {String} key environment key\n * @param {String} val environment value\n * @return {Server|Mixed} for chaining, or the setting value\n * @memberOf Application\n */\nApplication.loadConfig = function(key, val) {\n  var env = this.get(Constants.RESERVED.ENV);\n  val = require(val);\n  if (val[env]) {\n    val = val[env];\n  }\n  this.set(key, val);\n};\n\n/**\n * Set the route function for the specified server type.\n *\n * Examples:\n *\n *  app.route('area', routeFunc);\n *\n *  var routeFunc = function(session, msg, app, cb) {\n *    // all request to area would be route to the first area server\n *    var areas = app.getServersByType('area');\n *    cb(null, areas[0].id);\n *  };\n *\n * @param  {String} serverType server type string\n * @param  {Function} routeFunc  route function. routeFunc(session, msg, app, cb)\n * @return {Object}     current application instance for chain invoking\n * @memberOf Application\n */\nApplication.route = function(serverType, routeFunc) {\n  var routes = this.get(Constants.KEYWORDS.ROUTE);\n  if(!routes) {\n    routes = {};\n    this.set(Constants.KEYWORDS.ROUTE, routes);\n  }\n  routes[serverType] = routeFunc;\n  return this;\n};\n\n/**\n * Set before stop function. It would perform before servers stop.\n *\n * @param  {Function} fun before close function\n * @return {Void}\n * @memberOf Application\n */\nApplication.beforeStopHook = function(fun) {\n  logger.warn('this method was deprecated in pomelo 0.8');\n  if(!!fun && typeof fun === 'function') {\n    this.set(Constants.KEYWORDS.BEFORE_STOP_HOOK, fun);\n  }\n};\n\n/**\n * Start application. It would load the default components and start all the loaded components.\n *\n * @param  {Function} cb callback function\n * @memberOf Application\n */\n Application.start = function(cb) {\n  this.startTime = Date.now();\n  if(this.state > STATE_INITED) {\n    utils.invokeCallback(cb, new Error('application has already start.'));\n    return;\n  }\n  \n  var self = this;\n  appUtil.startByType(self, function() {\n    appUtil.loadDefaultComponents(self);\n    var startUp = function() {\n      appUtil.optComponents(self.loaded, Constants.RESERVED.START, function(err) {\n        self.state = STATE_START;\n        if(err) {\n          utils.invokeCallback(cb, err);\n        } else {\n          logger.info('%j enter after start...', self.getServerId());\n          self.afterStart(cb);\n        }\n      });\n    };\n    var beforeFun = self.lifecycleCbs[Constants.LIFECYCLE.BEFORE_STARTUP];\n    if(!!beforeFun) {\n      beforeFun.call(null, self, startUp);\n    } else {\n      startUp();\n    }\n  });\n};\n\n/**\n * Lifecycle callback for after start.\n *\n * @param  {Function} cb callback function\n * @return {Void}\n */\nApplication.afterStart = function(cb) {\n  if(this.state !== STATE_START) {\n    utils.invokeCallback(cb, new Error('application is not running now.'));\n    return;\n  }\n\n  var afterFun = this.lifecycleCbs[Constants.LIFECYCLE.AFTER_STARTUP];\n  var self = this;\n  appUtil.optComponents(this.loaded, Constants.RESERVED.AFTER_START, function(err) {\n    self.state = STATE_STARTED;\n    var id = self.getServerId();\n    if(!err) {\n      logger.info('%j finish start', id);\n    }\n    if(!!afterFun) {\n      afterFun.call(null, self, function() {\n        utils.invokeCallback(cb, err);\n      });\n    } else {\n      utils.invokeCallback(cb, err);\n    }\n    var usedTime = Date.now() - self.startTime;\n    logger.info('%j startup in %s ms', id, usedTime);\n    self.event.emit(events.START_SERVER, id);\n  });\n};\n\n/**\n * Stop components.\n *\n * @param  {Boolean} force whether stop the app immediately\n */\nApplication.stop = function(force) {\n  if(this.state > STATE_STARTED) {\n    logger.warn('[pomelo application] application is not running now.');\n    return;\n  }\n  this.state = STATE_STOPED;\n  var self = this;\n\n  this.stopTimer = setTimeout(function() {\n    process.exit(0);\n  }, Constants.TIME.TIME_WAIT_STOP);\n\n  var cancelShutDownTimer =function(){\n      if(!!self.stopTimer) {\n        clearTimeout(self.stopTimer);\n      }\n  };\n  var shutDown = function() {\n    appUtil.stopComps(self.loaded, 0, force, function() {\n      cancelShutDownTimer();\n      if(force) {\n        process.exit(0);\n      }\n    });\n  };\n  var fun = this.get(Constants.KEYWORDS.BEFORE_STOP_HOOK);\n  var stopFun = this.lifecycleCbs[Constants.LIFECYCLE.BEFORE_SHUTDOWN];\n  if(!!stopFun) {\n    stopFun.call(null, this, shutDown, cancelShutDownTimer);\n  } else if(!!fun) {\n    utils.invokeCallback(fun, self, shutDown, cancelShutDownTimer);\n  } else {\n    shutDown();\n  }\n};\n\n/**\n * Assign `setting` to `val`, or return `setting`'s value.\n *\n * Example:\n *\n *  app.set('key1', 'value1');\n *  app.get('key1');  // 'value1'\n *  app.key1;         // undefined\n *\n *  app.set('key2', 'value2', true);\n *  app.get('key2');  // 'value2'\n *  app.key2;         // 'value2'\n *\n * @param {String} setting the setting of application\n * @param {String} val the setting's value\n * @param {Boolean} attach whether attach the settings to application\n * @return {Server|Mixed} for chaining, or the setting value\n * @memberOf Application\n */\nApplication.set = function (setting, val, attach) {\n  if (arguments.length === 1) {\n    return this.settings[setting];\n  }\n  this.settings[setting] = val;\n  if(attach) {\n    this[setting] = val;\n  }\n  return this;\n};\n\n/**\n * Get property from setting\n *\n * @param {String} setting application setting\n * @return {String} val\n * @memberOf Application\n */\nApplication.get = function (setting) {\n  return this.settings[setting];\n};\n\n/**\n * Check if `setting` is enabled.\n *\n * @param {String} setting application setting\n * @return {Boolean}\n * @memberOf Application\n */\nApplication.enabled = function (setting) {\n  return !!this.get(setting);\n};\n\n/**\n * Check if `setting` is disabled.\n *\n * @param {String} setting application setting\n * @return {Boolean}\n * @memberOf Application\n */\nApplication.disabled = function (setting) {\n  return !this.get(setting);\n};\n\n/**\n * Enable `setting`.\n *\n * @param {String} setting application setting\n * @return {app} for chaining\n * @memberOf Application\n */\nApplication.enable = function (setting) {\n  return this.set(setting, true);\n};\n\n/**\n * Disable `setting`.\n *\n * @param {String} setting application setting\n * @return {app} for chaining\n * @memberOf Application\n */\nApplication.disable = function (setting) {\n  return this.set(setting, false);\n};\n\n/**\n * Configure callback for the specified env and server type.\n * When no env is specified that callback will\n * be invoked for all environments and when no type is specified\n * that callback will be invoked for all server types.\n *\n * Examples:\n *\n *  app.configure(function(){\n *    // executed for all envs and server types\n *  });\n *\n *  app.configure('development', function(){\n *    // executed development env\n *  });\n *\n *  app.configure('development', 'connector', function(){\n *    // executed for development env and connector server type\n *  });\n *\n * @param {String} env application environment\n * @param {Function} fn callback function\n * @param {String} type server type\n * @return {Application} for chaining\n * @memberOf Application\n */\nApplication.configure = function (env, type, fn) {\n  var args = [].slice.call(arguments);\n  fn = args.pop();\n  env = type = Constants.RESERVED.ALL;\n\n  if(args.length > 0) {\n    env = args[0];\n  }\n  if(args.length > 1) {\n    type = args[1];\n  }\n\n  if (env === Constants.RESERVED.ALL || contains(this.settings.env, env)) {\n    if (type === Constants.RESERVED.ALL || contains(this.settings.serverType, type)) {\n      fn.call(this);\n    }\n  }\n  return this;\n};\n\n/**\n * Register admin modules. Admin modules is the extends point of the monitor system.\n *\n * @param {String} module (optional) module id or provoided by module.moduleId\n * @param {Object} module module object or factory function for module\n * @param {Object} opts construct parameter for module\n * @memberOf Application\n */\nApplication.registerAdmin = function(moduleId, module, opts) {\n  var modules = this.get(Constants.KEYWORDS.MODULE);\n  if(!modules) {\n    modules = {};\n    this.set(Constants.KEYWORDS.MODULE, modules);\n  }\n\n  if(typeof moduleId !== 'string') {\n    opts = module;\n    module = moduleId;\n    if(module) {\n      moduleId = module.moduleId;\n    }\n  }\n\n  if(!moduleId){\n    return;\n  }\n\n  modules[moduleId] = {\n    moduleId: moduleId,\n    module: module,\n    opts: opts\n  };\n};\n\n/**\n * Use plugin.\n *\n * @param  {Object} plugin plugin instance\n * @param  {[type]} opts    (optional) construct parameters for the factory function\n * @memberOf Application\n */\nApplication.use = function(plugin, opts) {\n  if(!plugin.components) {\n    logger.error('invalid components, no components exist');\n    return;\n  }\n\n  var self = this;\n  opts = opts || {};\n  var dir = path.dirname(plugin.components);\n\n  if(!fs.existsSync(plugin.components)) {\n    logger.error('fail to find components, find path: %s', plugin.components);\n    return;\n  }\n\n  fs.readdirSync(plugin.components).forEach(function (filename) {\n    if (!/\\.js$/.test(filename)) {\n      return;\n    }\n    var name = path.basename(filename, '.js');\n    var param = opts[name] || {};\n    var absolutePath = path.join(dir, Constants.DIR.COMPONENT, filename);\n    if(!fs.existsSync(absolutePath)) {\n      logger.error('component %s not exist at %s', name, absolutePath);\n    } else {\n      self.load(require(absolutePath), param);\n    }\n  });\n\n  // load events\n  if(!plugin.events) {\n    return;\n  } else {\n    if(!fs.existsSync(plugin.events)) {\n      logger.error('fail to find events, find path: %s', plugin.events);\n      return;\n    }\n\n    fs.readdirSync(plugin.events).forEach(function (filename) {\n      if (!/\\.js$/.test(filename)) {\n        return;\n      }\n      var absolutePath = path.join(dir, Constants.DIR.EVENT, filename);\n      if(!fs.existsSync(absolutePath)) {\n        logger.error('events %s not exist at %s', filename, absolutePath);\n      } else {\n        bindEvents(require(absolutePath), self);\n      }\n    });\n  }\n};\n\n/**\n * Application transaction. Transcation includes conditions and handlers, if conditions are satisfied, handlers would be executed.\n * And you can set retry times to execute handlers. The transaction log is in file logs/transaction.log.\n *\n * @param {String} name transaction name\n * @param {Object} conditions functions which are called before transaction\n * @param {Object} handlers functions which are called during transaction\n * @param {Number} retry retry times to execute handlers if conditions are successfully executed\n * @memberOf Application\n */\nApplication.transaction = function(name, conditions, handlers, retry) {\n  appManager.transaction(name, conditions, handlers, retry);\n};\n\n/**\n * Get master server info.\n *\n * @return {Object} master server info, {id, host, port}\n * @memberOf Application\n */\nApplication.getMaster = function() {\n  return this.master;\n};\n\n/**\n * Get current server info.\n *\n * @return {Object} current server info, {id, serverType, host, port}\n * @memberOf Application\n */\nApplication.getCurServer = function() {\n  return this.curServer;\n};\n\n/**\n * Get current server id.\n *\n * @return {String|Number} current server id from servers.json\n * @memberOf Application\n */\nApplication.getServerId = function() {\n  return this.serverId;\n};\n\n/**\n * Get current server type.\n *\n * @return {String|Number} current server type from servers.json\n * @memberOf Application\n */\nApplication.getServerType = function() {\n  return this.serverType;\n};\n\n/**\n * Get all the current server infos.\n *\n * @return {Object} server info map, key: server id, value: server info\n * @memberOf Application\n */\nApplication.getServers = function() {\n  return this.servers;\n};\n\n/**\n * Get all server infos from servers.json.\n *\n * @return {Object} server info map, key: server id, value: server info\n * @memberOf Application\n */\nApplication.getServersFromConfig = function() {\n  return this.get(Constants.KEYWORDS.SERVER_MAP);\n};\n\n/**\n * Get all the server type.\n *\n * @return {Array} server type list\n * @memberOf Application\n */\nApplication.getServerTypes = function() {\n  return this.serverTypes;\n};\n\n/**\n * Get server info by server id from current server cluster.\n *\n * @param  {String} serverId server id\n * @return {Object} server info or undefined\n * @memberOf Application\n */\nApplication.getServerById = function(serverId) {\n  return this.servers[serverId];\n};\n\n/**\n * Get server info by server id from servers.json.\n *\n * @param  {String} serverId server id\n * @return {Object} server info or undefined\n * @memberOf Application\n */\n\nApplication.getServerFromConfig = function(serverId) {\n  return this.get(Constants.KEYWORDS.SERVER_MAP)[serverId];\n};\n\n/**\n * Get server infos by server type.\n *\n * @param  {String} serverType server type\n * @return {Array}      server info list\n * @memberOf Application\n */\nApplication.getServersByType = function(serverType) {\n  return this.serverTypeMaps[serverType];\n};\n\n/**\n * Check the server whether is a frontend server\n *\n * @param  {server}  server server info. it would check current server\n *            if server not specified\n * @return {Boolean}\n *\n * @memberOf Application\n */\nApplication.isFrontend = function(server) {\n  server = server || this.getCurServer();\n  return !!server && server.frontend === 'true';\n};\n\n/**\n * Check the server whether is a backend server\n *\n * @param  {server}  server server info. it would check current server\n *            if server not specified\n * @return {Boolean}\n * @memberOf Application\n */\nApplication.isBackend = function(server) {\n  server = server || this.getCurServer();\n  return !!server && !server.frontend;\n};\n\n/**\n * Check whether current server is a master server\n *\n * @return {Boolean}\n * @memberOf Application\n */\nApplication.isMaster = function() {\n  return this.serverType === Constants.RESERVED.MASTER;\n};\n\n/**\n * Add new server info to current application in runtime.\n *\n * @param {Array} servers new server info list\n * @memberOf Application\n */\nApplication.addServers = function(servers) {\n  if(!servers || !servers.length) {\n    return;\n  }\n\n  var item, slist;\n  for(var i=0, l=servers.length; i<l; i++) {\n    item = servers[i];\n    // update global server map\n    this.servers[item.id] = item;\n\n    // update global server type map\n    slist = this.serverTypeMaps[item.serverType];\n    if(!slist) {\n      this.serverTypeMaps[item.serverType] = slist = [];\n    }\n    replaceServer(slist, item);\n\n    // update global server type list\n    if(this.serverTypes.indexOf(item.serverType) < 0) {\n      this.serverTypes.push(item.serverType);\n    }\n  }\n  this.event.emit(events.ADD_SERVERS, servers);\n};\n\n/**\n * Remove server info from current application at runtime.\n *\n * @param  {Array} ids server id list\n * @memberOf Application\n */\nApplication.removeServers = function(ids) {\n  if(!ids || !ids.length) {\n    return;\n  }\n\n  var id, item, slist;\n  for(var i=0, l=ids.length; i<l; i++) {\n    id = ids[i];\n    item = this.servers[id];\n    if(!item) {\n      continue;\n    }\n    // clean global server map\n    delete this.servers[id];\n\n    // clean global server type map\n    slist = this.serverTypeMaps[item.serverType];\n    removeServer(slist, id);\n    // TODO: should remove the server type if the slist is empty?\n  }\n  this.event.emit(events.REMOVE_SERVERS, ids);\n};\n\n/**\n * Replace server info from current application at runtime.\n *\n * @param  {Object} server id map\n * @memberOf Application\n */\nApplication.replaceServers = function(servers) {\n  if(!servers){\n    return;\n  }\n\n  this.servers = servers;\n  this.serverTypeMaps = {};\n  this.serverTypes = [];\n  var serverArray = [];\n  for(var id in servers){\n    var server = servers[id];\n    var serverType = server[Constants.RESERVED.SERVER_TYPE];\n    var slist = this.serverTypeMaps[serverType];\n    if(!slist) {\n      this.serverTypeMaps[serverType] = slist = [];\n    }\n    this.serverTypeMaps[serverType].push(server);\n    // update global server type list\n    if(this.serverTypes.indexOf(serverType) < 0) {\n      this.serverTypes.push(serverType);\n    }\n    serverArray.push(server);\n  }\n  this.event.emit(events.REPLACE_SERVERS, serverArray);\n};\n\n/**\n * Add crons from current application at runtime.\n *\n * @param  {Array} crons new crons would be added in application\n * @memberOf Application\n */\nApplication.addCrons = function(crons) {\n  if(!crons || !crons.length) {\n    logger.warn('crons is not defined.');\n    return;\n  }\n  this.event.emit(events.ADD_CRONS, crons);\n};\n\n/**\n * Remove crons from current application at runtime.\n *\n * @param  {Array} crons old crons would be removed in application\n * @memberOf Application\n */\nApplication.removeCrons = function(crons) {\n  if(!crons || !crons.length) {\n    logger.warn('ids is not defined.');\n    return;\n  }\n  this.event.emit(events.REMOVE_CRONS, crons);\n};\n\nvar replaceServer = function(slist, serverInfo) {\n  for(var i=0, l=slist.length; i<l; i++) {\n    if(slist[i].id === serverInfo.id) {\n      slist[i] = serverInfo;\n      return;\n    }\n  }\n  slist.push(serverInfo);\n};\n\nvar removeServer = function(slist, id) {\n  if(!slist || !slist.length) {\n    return;\n  }\n\n  for(var i=0, l=slist.length; i<l; i++) {\n    if(slist[i].id === id) {\n      slist.splice(i, 1);\n      return;\n    }\n  }\n};\n\nvar contains = function(str, settings) {\n  if(!settings) {\n    return false;\n  }\n\n  var ts = settings.split(\"|\");\n  for(var i=0, l=ts.length; i<l; i++) {\n    if(str === ts[i]) {\n      return true;\n    }\n  }\n  return false;\n};\n\nvar bindEvents = function(Event, app) {\n  var emethods = new Event(app);\n  for(var m in emethods) {\n    if(typeof emethods[m] === 'function') {\n      app.event.on(m, emethods[m].bind(emethods));\n    }\n  }\n};\n\nvar addFilter = function(app, type, filter) {\n var filters = app.get(type);\n  if(!filters) {\n    filters = [];\n    app.set(type, filters);\n  }\n  filters.push(filter);\n};\n"
  },
  {
    "path": "lib/common/manager/appManager.js",
    "content": "var async = require('async');\nvar utils = require('../../util/utils');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar transactionLogger = require('pomelo-logger').getLogger('transaction-log', __filename);\nvar transactionErrorLogger = require('pomelo-logger').getLogger('transaction-error-log', __filename);\n\nvar manager = module.exports;\n\nmanager.transaction = function(name, conditions, handlers, retry) {\n\tif(!retry) {\n    retry = 1;\n  }\n  if(typeof name !== 'string') {\n    logger.error('transaction name is error format, name: %s.', name);\n    return;\n  }\n  if(typeof conditions !== 'object' || typeof handlers !== 'object') {\n    logger.error('transaction conditions parameter is error format, conditions: %j, handlers: %j.', conditions, handlers);\n    return;\n  }\n\n  var cmethods=[] ,dmethods=[], cnames=[], dnames=[];\n  for(var key in conditions) {\n    if(typeof key !== 'string' || typeof conditions[key] !== 'function') {\n      logger.error('transaction conditions parameter is error format, condition name: %s, condition function: %j.', key, conditions[key]);\n      return;\n    }\n    cnames.push(key);\n    cmethods.push(conditions[key]);\n  }\n\n  var i = 0;\n  // execute conditions\n  async.forEachSeries(cmethods, function(method, cb) {\n    method(cb);\n    transactionLogger.info('[%s]:[%s] condition is executed.', name, cnames[i]);\n    i++;\n  }, function(err) {\n    if(err) {\n      process.nextTick(function() {\n        transactionLogger.error('[%s]:[%s] condition is executed with err: %j.', name, cnames[--i], err.stack);\n        var log = {\n          name: name,\n          method: cnames[i],\n          time: Date.now(),\n          type: 'condition',\n          description: err.stack\n        };\n        transactionErrorLogger.error(JSON.stringify(log));\n      });\n      return;\n    } else {\n      // execute handlers\n      process.nextTick(function() {\n        for(var key in handlers) {\n          if(typeof key !== 'string' || typeof handlers[key] !== 'function') {\n            logger.error('transcation handlers parameter is error format, handler name: %s, handler function: %j.', key, handlers[key]);\n            return;\n          }\n          dnames.push(key);\n          dmethods.push(handlers[key]);\n        }\n\n        var flag = true;\n        var times = retry;\n        \n        // do retry if failed util retry times\n        async.whilst(\n          function() {\n            return retry > 0 && flag;\n          },\n          function(callback) {\n            var j = 0;\n            retry--;\n            async.forEachSeries(dmethods, function(method, cb) {\n              method(cb);\n              transactionLogger.info('[%s]:[%s] handler is executed.', name, dnames[j]);\n              j++;\n            }, function(err) {\n              if(err) {\n                process.nextTick(function() {\n                  transactionLogger.error('[%s]:[%s]:[%s] handler is executed with err: %j.', name, dnames[--j], times-retry, err.stack);\n                  var log = {\n                    name: name,\n                    method: dnames[j],\n                    retry: times-retry,\n                    time: Date.now(),\n                    type: 'handler',\n                    description: err.stack\n                  };\n                  transactionErrorLogger.error(JSON.stringify(log));\n                  utils.invokeCallback(callback);\n                });\n                return;\n              }\n              flag = false;\n              utils.invokeCallback(callback);\n              process.nextTick(function() {\n                transactionLogger.info('[%s] all conditions and handlers are executed successfully.', name);\n              });\n            });\n          },\n          function(err) {\n            if(err) {\n              logger.error('transaction process is executed with error: %j', err);\n            }\n            // callback will not pass error\n          }\n        );\n      });\n    }\n  });\n};"
  },
  {
    "path": "lib/common/manager/taskManager.js",
    "content": "var sequeue = require('seq-queue');\n\nvar manager = module.exports;\n\nvar queues = {};\n\nmanager.timeout = 3000;\n\n/**\n * Add tasks into task group. Create the task group if it dose not exist.\n *\n * @param {String}   key       task key\n * @param {Function} fn        task callback\n * @param {Function} ontimeout task timeout callback\n * @param {Number}   timeout   timeout for task\n */\nmanager.addTask = function(key, fn, ontimeout, timeout) {\n  var queue = queues[key];\n  if(!queue) {\n    queue = sequeue.createQueue(manager.timeout);\n    queues[key] = queue;\n  }\n\n  return queue.push(fn, ontimeout, timeout);\n};\n\n/**\n * Destroy task group\n *\n * @param  {String} key   task key\n * @param  {Boolean} force whether close task group directly\n */\nmanager.closeQueue = function(key, force) {\n  if(!queues[key]) {\n    // ignore illeagle key\n    return;\n  }\n\n  queues[key].close(force);\n  delete queues[key];\n};\n"
  },
  {
    "path": "lib/common/remote/backend/msgRemote.js",
    "content": "var utils = require('../../../util/utils');\nvar logger = require('pomelo-logger').getLogger('forward-log', __filename);\n/**\n * Remote service for backend servers.\n * Receive and handle request message forwarded from frontend server.\n */\nmodule.exports = function(app) {\n  return new Remote(app);\n};\n\nvar Remote = function(app) {\n  this.app = app;\n};\n\n/**\n * Forward message from frontend server to other server's handlers\n *\n * @param msg {Object} request message\n * @param session {Object} session object for current request\n * @param cb {Function} callback function\n */\nRemote.prototype.forwardMessage = function(msg, session, cb) {\n  var server = this.app.components.__server__;\n  var sessionService = this.app.components.__backendSession__;\n\n  if(!server) {\n    logger.error('server component not enable on %s', this.app.serverId);\n    utils.invokeCallback(cb, new Error('server component not enable'));\n    return;\n  }\n\n  if(!sessionService) {\n    logger.error('backend session component not enable on %s', this.app.serverId);\n    utils.invokeCallback(cb, new Error('backend sesssion component not enable'));\n    return;\n  }\n\n  // generate backend session for current request\n  var backendSession = sessionService.create(session);\n\n  // handle the request\n\n  logger.debug('backend server [%s] handle message: %j', this.app.serverId, msg);\n\n  server.handle(msg, backendSession, function(err, resp, opts) {\n    // cb && cb(err, resp, opts);\n    utils.invokeCallback(cb, err, resp, opts);\n  });\n};\n\nRemote.prototype.forwardMessage2 = function(route, body, aesPassword, compressGzip, session, cb) {\n  var server = this.app.components.__server__;\n  var sessionService = this.app.components.__backendSession__;\n\n  if(!server) {\n    logger.error('server component not enable on %s', this.app.serverId);\n    utils.invokeCallback(cb, new Error('server component not enable'));\n    return;\n  }\n\n  if(!sessionService) {\n    logger.error('backend session component not enable on %s', this.app.serverId);\n    utils.invokeCallback(cb, new Error('backend sesssion component not enable'));\n    return;\n  }\n\n  // generate backend session for current request\n  var backendSession = sessionService.create(session);\n\n  // handle the request\n\n  // logger.debug('backend server [%s] handle message: %j', this.app.serverId, msg);\n\n  var dmsg = {\n    route: route,\n    body: body,\n    compressGzip: compressGzip\n  }\n\n  var socket = {\n    aesPassword: aesPassword\n  }\n\n  var connector = this.app.components.__connector__.connector;\n  connector.runDecode(dmsg, socket, function(err, msg) {\n    if(err) {\n      return cb(err);\n    }\n    \n    server.handle(msg, backendSession, function(err, resp, opts) {\n      utils.invokeCallback(cb, err, resp, opts);\n    });\n  });\n};"
  },
  {
    "path": "lib/common/remote/frontend/channelRemote.js",
    "content": "/**\n * Remote channel service for frontend server.\n * Receive push request from backend servers and push it to clients.\n */\nvar utils = require('../../../util/utils');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\nmodule.exports = function(app) {\n  return new Remote(app);\n};\n\nvar Remote = function(app) {\n  this.app = app;\n};\n\n/**\n * Push message to client by uids.\n *\n * @param  {String}   route route string of message\n * @param  {Object}   msg   message\n * @param  {Array}    uids  user ids that would receive the message\n * @param  {Object}   opts  push options\n * @param  {Function} cb    callback function\n */\nRemote.prototype.pushMessage = function(route, msg, uids, opts, cb) {\n  if(!msg){\n    logger.error('Can not send empty message! route : %j, compressed msg : %j',\n        route, msg);\n    utils.invokeCallback(cb, new Error('can not send empty message.'));\n    return;\n  }\n  \n  var connector = this.app.components.__connector__;\n\n  var sessionService = this.app.get('sessionService');\n  var fails = [], sids = [], sessions, j, k;\n  for(var i=0, l=uids.length; i<l; i++) {\n    sessions = sessionService.getByUid(uids[i]);\n    if(!sessions) {\n      fails.push(uids[i]);\n    } else {\n      for(j=0, k=sessions.length; j<k; j++) {\n        sids.push(sessions[j].id);\n      }\n    }\n  }\n  logger.debug('[%s] pushMessage uids: %j, msg: %j, sids: %j', this.app.serverId, uids, msg, sids);\n  connector.send(null, route, msg, sids, opts, function(err) {\n    utils.invokeCallback(cb, err, fails);\n  });\n};\n\n/**\n * Broadcast to all the client connectd with current frontend server.\n *\n * @param  {String}    route  route string\n * @param  {Object}    msg    message\n * @param  {Boolean}   opts   broadcast options. \n * @param  {Function}  cb     callback function\n */\nRemote.prototype.broadcast = function(route, msg, opts, cb) {\n  var connector = this.app.components.__connector__;\n\n  connector.send(null, route, msg, null, opts, cb);\n};\n"
  },
  {
    "path": "lib/common/remote/frontend/sessionRemote.js",
    "content": "/**\n * Remote session service for frontend server.\n * Set session info for backend servers.\n */\nvar utils = require('../../../util/utils');\n\nmodule.exports = function(app) {\n  return new Remote(app);\n};\n\nvar Remote = function(app) {\n  this.app = app;\n};\n\nRemote.prototype.bind = function(sid, uid, cb) {\n  this.app.get('sessionService').bind(sid, uid, cb);\n};\n\nRemote.prototype.unbind = function(sid, uid, cb) {\n  this.app.get('sessionService').unbind(sid, uid, cb);\n};\n\nRemote.prototype.push = function(sid, key, value, cb) {\n  this.app.get('sessionService').import(sid, key, value, cb);\n};\n\nRemote.prototype.pushAll = function(sid, settings, cb) {\n  this.app.get('sessionService').importAll(sid, settings, cb);\n};\n\n/**\n * Get session informations with session id.\n *\n * @param  {String}   sid session id binded with the session\n * @param  {Function} cb(err, sinfo)  callback funtion, sinfo would be null if the session not exist.\n */\nRemote.prototype.getBackendSessionBySid = function(sid, cb) {\n  var session = this.app.get('sessionService').get(sid);\n  if(!session) {\n    utils.invokeCallback(cb);\n    return;\n  }\n  utils.invokeCallback(cb, null, session.toFrontendSession().export());\n};\n\n/**\n * Get all the session informations with the specified user id.\n *\n * @param  {String}   uid user id binded with the session\n * @param  {Function} cb(err, sinfo)  callback funtion, sinfo would be null if the session does not exist.\n */\nRemote.prototype.getBackendSessionsByUid = function(uid, cb) {\n  var sessions = this.app.get('sessionService').getByUid(uid);\n  if(!sessions) {\n    utils.invokeCallback(cb);\n    return;\n  }\n\n  var res = [];\n  for(var i=0, l=sessions.length; i<l; i++) {\n    res.push(sessions[i].toFrontendSession().export());\n  }\n  utils.invokeCallback(cb, null, res);\n};\n\n/**\n * Kick a session by session id.\n *\n * @param  {Number}   sid session id\n * @param  {String}   reason  kick reason\n * @param  {Function} cb  callback function\n */\nRemote.prototype.kickBySid = function(sid, reason, cb) {\n  this.app.get('sessionService').kickBySessionId(sid, reason, cb);\n};\n\n/**\n * Kick sessions by user id.\n *\n * @param  {Number|String}   uid user id\n * @param  {String}          reason     kick reason\n * @param  {Function} cb     callback function\n */\nRemote.prototype.kickByUid = function(uid, reason, cb) {\n  this.app.get('sessionService').kick(uid, reason, cb);\n};\n"
  },
  {
    "path": "lib/common/service/backendSessionService.js",
    "content": "/**\n * backend session service for backend session\n */\nvar utils = require('../../util/utils');\n\nvar EXPORTED_FIELDS = ['id', 'frontendId', 'uid', 'settings'];\n\n/**\n * Service that maintains backend sessions and the communication with frontend\n * servers.\n *\n * BackendSessionService would be created in each server process and maintains\n * backend sessions for current process and communicates with the relative\n * frontend servers.\n *\n * BackendSessionService instance could be accessed by\n * `app.get('backendSessionService')` or app.backendSessionService.\n *\n * @class\n * @constructor\n */\nvar BackendSessionService = function(app) {\n  this.app = app;\n};\n\nmodule.exports = BackendSessionService;\n\nBackendSessionService.prototype.create = function(opts) {\n  if(!opts) {\n    throw new Error('opts should not be empty.');\n  }\n  return new BackendSession(opts, this);\n};\n\n/**\n * Get backend session by frontend server id and session id.\n *\n * @param  {String}   frontendId frontend server id that session attached\n * @param  {String}   sid        session id\n * @param  {Function} cb         callback function. args: cb(err, BackendSession)\n *\n * @memberOf BackendSessionService\n */\nBackendSessionService.prototype.get = function(frontendId, sid, cb) {\n  var namespace = 'sys';\n  var service = 'sessionRemote';\n  var method = 'getBackendSessionBySid';\n  var args = [sid];\n  rpcInvoke(this.app, frontendId, namespace, service, method,\n            args, BackendSessionCB.bind(null, this, cb));\n};\n\n/**\n * Get backend sessions by frontend server id and user id.\n *\n * @param  {String}   frontendId frontend server id that session attached\n * @param  {String}   uid        user id binded with the session\n * @param  {Function} cb         callback function. args: cb(err, BackendSessions)\n *\n * @memberOf BackendSessionService\n */\nBackendSessionService.prototype.getByUid = function(frontendId, uid, cb) {\n  var namespace = 'sys';\n  var service = 'sessionRemote';\n  var method = 'getBackendSessionsByUid';\n  var args = [uid];\n  rpcInvoke(this.app, frontendId, namespace, service, method,\n            args, BackendSessionCB.bind(null, this, cb));\n};\n\n/**\n * Kick a session by session id.\n *\n * @param  {String}   frontendId cooperating frontend server id\n * @param  {Number}   sid        session id\n * @param  {Function} cb         callback function\n *\n * @memberOf BackendSessionService\n */\nBackendSessionService.prototype.kickBySid = function(frontendId, sid, reason, cb) {\n  var namespace = 'sys';\n  var service = 'sessionRemote';\n  var method = 'kickBySid';\n  var args = [sid];\n  if(typeof reason === 'function') {\n    cb = reason;\n  }else{\n    args.push(reason);\n  }\n  rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);\n};\n\n/**\n * Kick sessions by user id.\n *\n * @param  {String}          frontendId cooperating frontend server id\n * @param  {Number|String}   uid        user id\n * @param  {String}          reason     kick reason\n * @param  {Function}        cb         callback function\n *\n * @memberOf BackendSessionService\n */\nBackendSessionService.prototype.kickByUid = function(frontendId, uid, reason, cb) {\n  var namespace = 'sys';\n  var service = 'sessionRemote';\n  var method = 'kickByUid';\n  var args = [uid];\n  if(typeof reason === 'function') {\n    cb = reason;\n  }else{\n    args.push(reason);\n  }\n  rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);\n};\n\n/**\n * Bind the session with the specified user id. It would finally invoke the\n * the sessionService.bind in the cooperating frontend server.\n *\n * @param  {String}   frontendId cooperating frontend server id\n * @param  {Number}   sid        session id\n * @param  {String}   uid        user id\n * @param  {Function} cb         callback function\n *\n * @memberOf BackendSessionService\n * @api private\n */\nBackendSessionService.prototype.bind = function(frontendId, sid, uid, cb) {\n  var namespace = 'sys';\n  var service = 'sessionRemote';\n  var method = 'bind';\n  var args = [sid, uid];\n  rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);\n};\n\n/**\n * Unbind the session with the specified user id. It would finally invoke the\n * the sessionService.unbind in the cooperating frontend server.\n *\n * @param  {String}   frontendId cooperating frontend server id\n * @param  {Number}   sid        session id\n * @param  {String}   uid        user id\n * @param  {Function} cb         callback function\n *\n * @memberOf BackendSessionService\n * @api private\n */\nBackendSessionService.prototype.unbind = function(frontendId, sid, uid, cb) {\n  var namespace = 'sys';\n  var service = 'sessionRemote';\n  var method = 'unbind';\n  var args = [sid, uid];\n  rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);\n};\n\n/**\n * Push the specified customized change to the frontend internal session.\n *\n * @param  {String}   frontendId cooperating frontend server id\n * @param  {Number}   sid        session id\n * @param  {String}   key        key in session that should be push\n * @param  {Object}   value      value in session, primitive js object\n * @param  {Function} cb         callback function\n *\n * @memberOf BackendSessionService\n * @api private\n */\nBackendSessionService.prototype.push = function(frontendId, sid, key, value, cb) {\n  var namespace = 'sys';\n  var service = 'sessionRemote';\n  var method = 'push';\n  var args = [sid, key, value];\n  rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);\n};\n\n/**\n * Push all the customized changes to the frontend internal session.\n *\n * @param  {String}   frontendId cooperating frontend server id\n * @param  {Number}   sid        session id\n * @param  {Object}   settings   key/values in session that should be push\n * @param  {Function} cb         callback function\n *\n * @memberOf BackendSessionService\n * @api private\n */\nBackendSessionService.prototype.pushAll = function(frontendId, sid, settings, cb) {\n  var namespace = 'sys';\n  var service = 'sessionRemote';\n  var method = 'pushAll';\n  var args = [sid, settings];\n  rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);\n};\n\nvar rpcInvoke = function(app, sid, namespace, service, method, args, cb) {\n  app.rpcInvoke(sid, {namespace: namespace, service: service, method: method, args: args}, cb);\n};\n\n/**\n * BackendSession is the proxy for the frontend internal session passed to handlers and\n * it helps to keep the key/value pairs for the server locally.\n * Internal session locates in frontend server and should not be accessed directly.\n *\n * The mainly operation on backend session should be read and any changes happen in backend\n * session is local and would be discarded in next request. You have to push the\n * changes to the frontend manually if necessary. Any push would overwrite the last push\n * of the same key silently and the changes would be saw in next request.\n * And you have to make sure the transaction outside if you would push the session\n * concurrently in different processes.\n *\n * See the api below for more details.\n *\n * @class\n * @constructor\n */\nvar BackendSession = function(opts, service) {\n  for(var f in opts) {\n    this[f] = opts[f];\n  }\n  this.__sessionService__ = service;\n};\n\n/**\n * Bind current session with the user id. It would push the uid to frontend\n * server and bind  uid to the frontend internal session.\n *\n * @param  {Number|String}   uid user id\n * @param  {Function} cb  callback function\n *\n * @memberOf BackendSession\n */\nBackendSession.prototype.bind = function(uid, cb) {\n  var self = this;\n  this.__sessionService__.bind(this.frontendId, this.id, uid, function(err) {\n    if(!err) {\n      self.uid = uid;\n    }\n    utils.invokeCallback(cb, err);\n  });\n};\n\n/**\n * Unbind current session with the user id. It would push the uid to frontend\n * server and unbind uid from the frontend internal session.\n *\n * @param  {Number|String}   uid user id\n * @param  {Function} cb  callback function\n *\n * @memberOf BackendSession\n */\nBackendSession.prototype.unbind = function(uid, cb) {\n  var self = this;\n  this.__sessionService__.unbind(this.frontendId, this.id, uid, function(err) {\n    if(!err) {\n      self.uid = null;\n    }\n    utils.invokeCallback(cb, err);\n  });\n};\n\n/**\n * Set the key/value into backend session.\n *\n * @param {String} key   key\n * @param {Object} value value\n */\nBackendSession.prototype.set = function(key, value) {\n  this.settings[key] = value;\n};\n\n/**\n * Get the value from backend session by key.\n *\n * @param  {String} key key\n * @return {Object}     value\n */\nBackendSession.prototype.get = function(key) {\n  return this.settings[key];\n};\n\n/**\n * Push the key/value in backend session to the front internal session.\n *\n * @param  {String}   key key\n * @param  {Function} cb  callback function\n */\nBackendSession.prototype.push = function(key, cb) {\n  this.__sessionService__.push(this.frontendId, this.id, key, this.get(key), cb);\n};\n\n/**\n * Push all the key/values in backend session to the frontend internal session.\n *\n * @param  {Function} cb callback function\n */\nBackendSession.prototype.pushAll = function(cb) {\n  this.__sessionService__.pushAll(this.frontendId, this.id, this.settings, cb);\n};\n\n/**\n * Export the key/values for serialization.\n *\n * @api private\n */\nBackendSession.prototype.export = function() {\n  var res = {};\n  EXPORTED_FIELDS.forEach(function(field) {\n    res[field] = this[field];\n  });\n  return res;\n};\n\nvar BackendSessionCB = function(service, cb, err, sinfo) {\n  if(err) {\n    utils.invokeCallback(cb, err);\n    return;\n  }\n\n  if(!sinfo) {\n    utils.invokeCallback(cb);\n    return;\n  }\n  var sessions = [];\n  if(Array.isArray(sinfo)){\n      // #getByUid\n      for(var i = 0,k = sinfo.length;i<k;i++){\n          sessions.push(service.create(sinfo[i]));\n      }\n  }\n  else{\n      // #get\n      sessions = service.create(sinfo);\n  }\n  utils.invokeCallback(cb, null, sessions);\n};\n"
  },
  {
    "path": "lib/common/service/channelService.js",
    "content": "var countDownLatch = require('../../util/countDownLatch');\nvar utils = require('../../util/utils');\nvar ChannelRemote = require('../remote/frontend/channelRemote');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\n/**\n * constant\n */\nvar ST_INITED = 0;\nvar ST_DESTROYED = 1;\n\n/**\n * Create and maintain channels for server local.\n *\n * ChannelService is created by channel component which is a default loaded\n * component of pomelo and channel service would be accessed by `app.get('channelService')`.\n *\n * @class\n * @constructor\n */\nvar ChannelService = function(app, opts) {\n  opts = opts || {};\n  this.app = app;\n  this.channels = {};\n  this.prefix = opts.prefix;\n  this.store = opts.store;\n  this.broadcastFilter = opts.broadcastFilter;\n  this.channelRemote = new ChannelRemote(app);\n};\n\nmodule.exports = ChannelService;\n\n\nChannelService.prototype.start = function(cb) {\n  restoreChannel(this, cb);\n};\n\n\n\n/**\n * Create channel with name.\n *\n * @param {String} name channel's name\n * @memberOf ChannelService\n */\nChannelService.prototype.createChannel = function(name) {\n  if(this.channels[name]) {\n    return this.channels[name];\n  }\n\n  var c = new Channel(name, this);\n  addToStore(this, genKey(this), genKey(this, name));\n  this.channels[name] = c;\n  return c;\n};\n\n/**\n * Get channel by name.\n *\n * @param {String} name channel's name\n * @param {Boolean} create if true, create channel\n * @return {Channel}\n * @memberOf ChannelService\n */\nChannelService.prototype.getChannel = function(name, create) {\n  var channel = this.channels[name];\n  if(!channel && !!create) {\n    channel = this.channels[name] = new Channel(name, this);\n    addToStore(this, genKey(this), genKey(this, name));\n  }\n  return channel;\n};\n\n/**\n * Destroy channel by name.\n *\n * @param {String} name channel name\n * @memberOf ChannelService\n */\nChannelService.prototype.destroyChannel = function(name) {\n  delete this.channels[name];\n  removeFromStore(this, genKey(this), genKey(this, name));\n  removeAllFromStore(this, genKey(this, name));\n};\n\n/**\n * Push message by uids.\n * Group the uids by group. ignore any uid if sid not specified.\n *\n * @param {String} route message route\n * @param {Object} msg message that would be sent to client\n * @param {Array} uids the receiver info list, [{uid: userId, sid: frontendServerId}]\n * @param {Object} opts user-defined push options, optional \n * @param {Function} cb cb(err)\n * @memberOf ChannelService\n */\nChannelService.prototype.pushMessageByUids = function(route, msg, uids, opts, cb) {\n  if(typeof route !== 'string') {\n    cb = opts;\n    opts = uids;\n    uids = msg;\n    msg = route;\n    route = msg.route;\n  }\n\n  if(!cb && typeof opts === 'function') {\n    cb = opts;\n    opts = {};\n  }\n\n  if(!uids || uids.length === 0) {\n    utils.invokeCallback(cb, new Error('uids should not be empty'));\n    return;\n  }\n  var groups = {}, record;\n  for(var i=0, l=uids.length; i<l; i++) {\n    record = uids[i];\n    add(record.uid, record.sid, groups);\n  }\n\n  sendMessageByGroup(this, route, msg, groups, opts, cb);\n};\n\n/**\n * Broadcast message to all the connected clients.\n *\n * @param  {String}   stype      frontend server type string\n * @param  {String}   route      route string\n * @param  {Object}   msg        message\n * @param  {Object}   opts       user-defined broadcast options, optional\n *                               opts.binded: push to binded sessions or all the sessions\n *                               opts.filterParam: parameters for broadcast filter.\n * @param  {Function} cb         callback\n * @memberOf ChannelService\n */\nChannelService.prototype.broadcast = function(stype, route, msg, opts, cb) {\n  var app = this.app;\n  var namespace = 'sys';\n  var service = 'channelRemote';\n  var method = 'broadcast';\n  var servers = app.getServersByType(stype);\n\n  if(!servers || servers.length === 0) {\n    // server list is empty\n    utils.invokeCallback(cb);\n    return;\n  }\n\n  var count = servers.length;\n  var successFlag = false;\n\n  var latch = countDownLatch.createCountDownLatch(count, function() {\n    if(!successFlag) {\n      utils.invokeCallback(cb, new Error('broadcast fails'));\n      return;\n    }\n    utils.invokeCallback(cb, null);\n  });\n\n  var genCB = function(serverId) {\n    return function(err) {\n      if(err) {\n        logger.error('[broadcast] fail to push message to serverId: ' + serverId + ', err:' + err.stack);\n        latch.done();\n        return;\n      }\n      successFlag = true;\n      latch.done();\n    };\n  };\n\n  var self = this;\n  var sendMessage = function(serverId) {\n    return (function() {\n      if(serverId === app.serverId) {\n        self.channelRemote[method](route, msg, opts, genCB());\n      } else {\n        app.rpcInvoke(serverId, {namespace: namespace, service: service,\n          method: method, args: [route, msg, opts]}, genCB(serverId));\n      }\n    }());\n  };\n\n  opts = {type: 'broadcast', userOptions: opts || {}};\n\n  // for compatiblity \n  opts.isBroadcast = true;\n  if(opts.userOptions) {\n    opts.binded = opts.userOptions.binded;\n    opts.filterParam = opts.userOptions.filterParam;\n  }\n\n  for(var i=0, l=count; i<l; i++) {\n    sendMessage(servers[i].id);\n  }\n};\n\n/**\n * Channel maintains the receiver collection for a subject. You can\n * add users into a channel and then broadcast message to them by channel.\n *\n * @class channel\n * @constructor\n */\nvar Channel = function(name, service) {\n  this.name = name;\n  this.groups = {};       // group map for uids. key: sid, value: [uid]\n  this.records = {};      // member records. key: uid\n  this.__channelService__ = service;\n  this.state = ST_INITED;\n  this.userAmount =0;\n};\n\n/**\n * Add user to channel.\n *\n * @param {Number} uid user id\n * @param {String} sid frontend server id which user has connected to\n */\nChannel.prototype.add = function(uid, sid) {\n  if(this.state > ST_INITED) {\n    return false;\n  } else {\n    var res = add(uid, sid, this.groups);\n    if(res) {\n      this.records[uid] = {sid: sid, uid: uid};\n      this.userAmount =this.userAmount+1;\n    }\n    addToStore(this.__channelService__, genKey(this.__channelService__, this.name), genValue(sid, uid));\n    return res;\n  }\n};\n\n/**\n * Remove user from channel.\n *\n * @param {Number} uid user id\n * @param {String} sid frontend server id which user has connected to.\n * @return [Boolean] true if success or false if fail\n */\nChannel.prototype.leave = function(uid, sid) {\n  if(!uid || !sid) {\n    return false;\n  }\n  var res = deleteFrom(uid, sid, this.groups[sid]);\n  if(res){\n    delete this.records[uid];\n    this.userAmount = this.userAmount-1;\n  }\n  if(this.userAmount<0) this.userAmount=0;//robust\n  removeFromStore(this.__channelService__, genKey(this.__channelService__, this.name), genValue(sid, uid));\n  if(this.groups[sid] && this.groups[sid].length === 0) {\n    delete this.groups[sid];\n  }\n  return res;\n};\n/**\n * Get channel UserAmount in a channel.\n\n *\n * @return {number } channel member amount\n */\nChannel.prototype.getUserAmount = function() {\n  return this.userAmount;\n};\n\n/**\n * Get channel members.\n *\n * <b>Notice:</b> Heavy operation.\n *\n * @return {Array} channel member uid list\n */\nChannel.prototype.getMembers = function() {\n  var res = [], groups = this.groups;\n  var group, i, l;\n  for(var sid in groups) {\n    group = groups[sid];\n    for(i=0, l=group.length; i<l; i++) {\n      res.push(group[i]);\n    }\n  }\n  return res;\n};\n\n/**\n * Get Member info.\n *\n * @param  {String} uid user id\n * @return {Object} member info\n */\nChannel.prototype.getMember = function(uid) {\n  return this.records[uid];\n};\n\n/**\n * Destroy channel.\n */\nChannel.prototype.destroy = function() {\n  this.state = ST_DESTROYED;\n  this.__channelService__.destroyChannel(this.name);\n};\n\n/**\n * Push message to all the members in the channel\n *\n * @param {String} route message route\n * @param {Object} msg message that would be sent to client\n * @param {Object} opts user-defined push options, optional\n * @param {Function} cb callback function\n */\nChannel.prototype.pushMessage = function(route, msg, opts, cb) {\n  if(this.state !== ST_INITED) {\n    utils.invokeCallback(new Error('channel is not running now'));\n    return;\n  }\n\n  if(typeof route !== 'string') {\n    cb = opts;\n    opts = msg;\n    msg = route;\n    route = msg.route;\n  }\n\n  if(!cb && typeof opts === 'function') {\n    cb = opts;\n    opts = {};\n  }\n\n  sendMessageByGroup(this.__channelService__, route, msg, this.groups, opts, cb);\n};\n\n/**\n * add uid and sid into group. ignore any uid that uid not specified.\n *\n * @param uid user id\n * @param sid server id\n * @param groups {Object} grouped uids, , key: sid, value: [uid]\n */\nvar add = function(uid, sid, groups) {\n  if(!sid) {\n    logger.warn('ignore uid %j for sid not specified.', uid);\n    return false;\n  }\n\n  var group = groups[sid];\n  if(!group) {\n    group = [];\n    groups[sid] = group;\n  }\n\n  group.push(uid);\n  return true;\n};\n\n/**\n * delete element from array\n */\nvar deleteFrom = function(uid, sid, group) {\n  if(!uid || !sid || !group) {\n    return false;\n  }\n\n  for(var i=0, l=group.length; i<l; i++) {\n    if(group[i] === uid) {\n      group.splice(i, 1);\n      return true;\n    }\n  }\n\n  return false;\n};\n\n/**\n * push message by group\n *\n * @param route {String} route route message\n * @param msg {Object} message that would be sent to client\n * @param groups {Object} grouped uids, , key: sid, value: [uid]\n * @param opts {Object} push options\n * @param cb {Function} cb(err)\n *\n * @api private\n */\nvar sendMessageByGroup = function(channelService, route, msg, groups, opts, cb) {\n  var app = channelService.app;\n  var namespace = 'sys';\n  var service = 'channelRemote';\n  var method = 'pushMessage';\n  var count = utils.size(groups);\n  var successFlag = false;\n  var failIds = [];\n\n  logger.debug('[%s] channelService sendMessageByGroup route: %s, msg: %j, groups: %j, opts: %j', app.serverId, route, msg, groups, opts);\n  if(count === 0) {\n    // group is empty\n    utils.invokeCallback(cb);\n    return;\n  }\n\n  var latch = countDownLatch.createCountDownLatch(count, function() {\n    if(!successFlag) {\n      utils.invokeCallback(cb, new Error('all uids push message fail'));\n      return;\n    }\n    utils.invokeCallback(cb, null, failIds);\n  });\n\n  var rpcCB = function(serverId) {\n    return function(err, fails) {\n      if(err) {\n        logger.error('[pushMessage] fail to dispatch msg to serverId: ' + serverId + ', err:' + err.stack);\n        latch.done();\n        return;\n      }\n      if(fails) {\n        failIds = failIds.concat(fails);\n      }\n      successFlag = true;\n      latch.done();\n    };\n  };\n\n  opts = {type: 'push', userOptions: opts || {}};\n  // for compatiblity\n  opts.isPush = true;\n  \n  var sendMessage = function(sid) {\n    return (function() {\n      if(sid === app.serverId) {\n        channelService.channelRemote[method](route, msg, groups[sid], opts, rpcCB(sid));\n      } else {\n        app.rpcInvoke(sid, {namespace: namespace, service: service,\n          method: method, args: [route, msg, groups[sid], opts]}, rpcCB(sid));\n      }\n    })();\n  };\n\n  var group;\n  for(var sid in groups) {\n    group = groups[sid];\n    if(group && group.length > 0) {\n      sendMessage(sid);\n    } else {\n      // empty group\n      process.nextTick(rpcCB(sid));\n    }\n  }\n};\n\nvar restoreChannel = function(self, cb) {\n  if(!self.store) {\n    utils.invokeCallback(cb);\n    return;\n  } else {\n    loadAllFromStore(self, genKey(self), function(err, list) {\n      if(!!err) {\n        utils.invokeCallback(cb, err);\n        return;\n      } else {\n        if(!list.length || !Array.isArray(list)) {\n          utils.invokeCallback(cb);\n          return;\n        }\n        var load = function(key) {\n          return (function() {\n            loadAllFromStore(self, key, function(err, items) {\n              for(var j=0; j<items.length; j++) {\n                var array = items[j].split(':');\n                var sid = array[0];\n                var uid = array[1];\n                var channel = self.channels[name];\n                var res = add(uid, sid, channel.groups);\n                if(res) {\n                  channel.records[uid] = {sid: sid, uid: uid};\n                }\n              }\n            });\n          })();\n        };\n\n       for(var i=0; i<list.length; i++) {\n        var name = list[i].slice(genKey(self).length + 1);\n        self.channels[name] = new Channel(name, self);\n        load(list[i]);\n      }\n      utils.invokeCallback(cb);\n    }\n  });\n}\n};\n\nvar addToStore = function(self, key, value) {\n  if(!!self.store) {\n    self.store.add(key, value, function(err) {\n      if(!!err) {\n        logger.error('add key: %s value: %s to store, with err: %j', key, value, err.stack);\n      }\n    });\n  }\n};\n\nvar removeFromStore = function(self, key, value) {\n  if(!!self.store) {\n    self.store.remove(key, value, function(err) {\n      if(!!err) {\n        logger.error('remove key: %s value: %s from store, with err: %j', key, value, err.stack);\n      }\n    });\n  }\n};\n\nvar loadAllFromStore = function(self, key, cb) {\n  if(!!self.store) {\n    self.store.load(key, function(err, list) {\n      if(!!err) {\n        logger.error('load key: %s from store, with err: %j', key, err.stack);\n        utils.invokeCallback(cb, err);\n      } else {\n        utils.invokeCallback(cb, null, list);\n      }\n    });\n  }\n};\n\nvar removeAllFromStore = function(self, key) {\n  if(!!self.store) {\n    self.store.removeAll(key, function(err) {\n      if(!!err) {\n        logger.error('remove key: %s all members from store, with err: %j', key, err.stack);\n      }\n    });\n  }\n};\n\nvar genKey = function(self, name) {\n  if(!!name) {\n    return self.prefix + ':' + self.app.serverId + ':' + name;\n  } else {\n    return self.prefix + ':' + self.app.serverId;\n  }\n};\n\nvar genValue = function(sid, uid) {\n  return sid + ':' + uid;\n};\n"
  },
  {
    "path": "lib/common/service/connectionService.js",
    "content": "/**\n * connection statistics service\n * record connection, login count and list\n */\nvar Service = function(app) {\n  this.serverId = app.getServerId();\n  this.connCount = 0;\n  this.loginedCount = 0;\n  this.logined = {};\n};\n\nmodule.exports = Service;\n\nvar pro = Service.prototype;\n\n\n/**\n * Add logined user.\n *\n * @param uid {String} user id\n * @param info {Object} record for logined user\n */\npro.addLoginedUser = function(uid, info) {\n  if(!this.logined[uid]) {\n    this.loginedCount++;\n  }\n  info.uid = uid;\n  this.logined[uid] = info;\n};\n\n/**\n * Update user info.\n * @param uid {String} user id\n * @param info {Object} info for update.\n */\npro.updateUserInfo = function(uid, info) {\n    var user = this.logined[uid];\n    if (!user) {\n        return;\n    }\n\n    for (var p in info) {\n        if (info.hasOwnProperty(p) && typeof info[p] !== 'function') {\n            user[p] = info[p];\n        }\n    }\n};\n\n/**\n * Increase connection count\n */\npro.increaseConnectionCount = function() {\n  this.connCount++;\n};\n\n/**\n * Remote logined user\n *\n * @param uid {String} user id\n */\npro.removeLoginedUser = function(uid) {\n  if(!!this.logined[uid]) {\n    this.loginedCount--;\n  }\n  delete this.logined[uid];\n};\n\n/**\n * Decrease connection count\n *\n * @param uid {String} uid\n */\npro.decreaseConnectionCount = function(uid) {\n  if(this.connCount) {\n    this.connCount--;\n  }\n  if(!!uid) {\n    this.removeLoginedUser(uid);\n  }\n};\n\n/**\n * Get statistics info\n *\n * @return {Object} statistics info\n */\npro.getStatisticsInfo = function() {\n  var list = [];\n  for(var uid in this.logined) {\n    list.push(this.logined[uid]);\n  }\n\n  return {serverId: this.serverId, totalConnCount: this.connCount, loginedCount: this.loginedCount, loginedList: list};\n};\n"
  },
  {
    "path": "lib/common/service/filterService.js",
    "content": "var logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\n/**\n * Filter service.\n * Register and fire before and after filters.\n */\nvar Service = function() {\n  this.befores = [];    // before filters\n  this.afters = [];     // after filters\n};\n\nmodule.exports = Service;\n\nService.prototype.name = 'filter';\n\n/**\n * Add before filter into the filter chain.\n *\n * @param filter {Object|Function} filter instance or filter function.\n */\nService.prototype.before = function(filter){\n  this.befores.push(filter);\n};\n\n/**\n * Add after filter into the filter chain.\n *\n * @param filter {Object|Function} filter instance or filter function.\n */\nService.prototype.after = function(filter){\n  this.afters.unshift(filter);\n};\n\n/**\n * TODO: other insert method for filter? such as unshift\n */\n\n/**\n * Do the before filter.\n * Fail over if any filter pass err parameter to the next function.\n *\n * @param msg {Object} clienet request msg\n * @param session {Object} a session object for current request\n * @param cb {Function} cb(err) callback function to invoke next chain node\n */\nService.prototype.beforeFilter = function(msg, session, cb) {\n  var index = 0, self = this;\n  var next = function(err, resp, opts) {\n    if(err || index >= self.befores.length) {\n      cb(err, resp, opts);\n      return;\n    }\n\n    var handler = self.befores[index++];\n    if(typeof handler === 'function') {\n      handler(msg, session, next);\n    } else if(typeof handler.before === 'function') {\n      handler.before(msg, session, next);\n    } else {\n      logger.error('meet invalid before filter, handler or handler.before should be function.');\n      next(new Error('invalid before filter.'));\n    }\n  }; //end of next\n\n  next();\n};\n\n/**\n * Do after filter chain.\n * Give server a chance to do clean up jobs after request responsed.\n * After filter can not change the request flow before.\n * After filter should call the next callback to let the request pass to next after filter.\n *\n * @param err {Object} error object\n * @param session {Object} session object for current request\n * @param {Object} resp response object send to client\n * @param cb {Function} cb(err) callback function to invoke next chain node\n */\nService.prototype.afterFilter = function(err, msg, session, resp, cb) {\n  var index = 0, self = this;\n  function next(err) {\n    //if done\n    if(index >= self.afters.length) {\n      cb(err);\n      return;\n    }\n\n    var handler = self.afters[index++];\n    if(typeof handler === 'function') {\n      handler(err, msg, session, resp, next);\n    } else if(typeof handler.after === 'function') {\n      handler.after(err, msg, session, resp, next);\n    } else {\n      logger.error('meet invalid after filter, handler or handler.after should be function.');\n      next(new Error('invalid after filter.'));\n    }\n  } //end of next\n\n  next(err);\n};\n"
  },
  {
    "path": "lib/common/service/handlerService.js",
    "content": "var fs = require('fs');\nvar utils = require('../../util/utils');\nvar Loader = require('pomelo-loader');\nvar pathUtil = require('../../util/pathUtil');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar forwardLogger = require('pomelo-logger').getLogger('forward-log', __filename);\n/**\n * Handler service.\n * Dispatch request to the relactive handler.\n *\n * @param {Object} app      current application context\n */\nvar Service = function(app, opts) {\n  this.app = app;\n  this.handlerMap = {};\n  if(!!opts.reloadHandlers) {\n    watchHandlers(app, this.handlerMap);\n  }\n\n  this.enableForwardLog = opts.enableForwardLog || false;\n};\n\nmodule.exports = Service;\n\nService.prototype.name = 'handler';\n\n/**\n * Handler the request.\n */\nService.prototype.handle = function(routeRecord, msg, session, cb) {\n  // the request should be processed by current server\n  var handler = this.getHandler(routeRecord);\n  if(!handler) {\n    logger.error('[handleManager]: fail to find handler for %j', msg.__route__);\n    utils.invokeCallback(cb, new Error('fail to find handler for ' + msg.__route__));\n    return;\n  }\n  var start = Date.now();\n  var self = this;\n\n  var callback = function(err, resp, opts) {\n    if(self.enableForwardLog) {\n      var log = {\n        route : msg.__route__,\n        args : msg,\n        time : utils.format(new Date(start)),\n        timeUsed : new Date() - start\n      };\n      forwardLogger.info(JSON.stringify(log));\n    }\n\n    // resp = getResp(arguments);\n    utils.invokeCallback(cb, err, resp, opts);\n  }\n\n  var method = routeRecord.method;\n\n  if(!Array.isArray(msg)) {\n    handler[method](msg, session, callback);\n  } else {\n    msg.push(session);\n    msg.push(callback);\n    handler[method].apply(handler, msg);\n  }\n  return;\n};\n\n/**\n * Get handler instance by routeRecord.\n *\n * @param  {Object} handlers    handler map\n * @param  {Object} routeRecord route record parsed from route string\n * @return {Object}             handler instance if any matchs or null for match fail\n */\nService.prototype.getHandler = function(routeRecord) {\n  var serverType = routeRecord.serverType;\n  if(!this.handlerMap[serverType]) {\n    loadHandlers(this.app, serverType, this.handlerMap);\n  }\n  var handlers = this.handlerMap[serverType] || {};\n  var handler = handlers[routeRecord.handler];\n  if(!handler) {\n    logger.warn('could not find handler for routeRecord: %j', routeRecord);\n    return null;\n  }\n  if(typeof handler[routeRecord.method] !== 'function') {\n    logger.warn('could not find the method %s in handler: %s', routeRecord.method, routeRecord.handler);\n    return null;\n  }\n  return handler;\n};\n\n/**\n * Load handlers from current application\n */\nvar loadHandlers = function(app, serverType, handlerMap) {\n  var p = pathUtil.getHandlerPath(app.getBase(), serverType);\n  if(p) {\n    handlerMap[serverType] = Loader.load(p, app);\n  }\n};\n\nvar watchHandlers = function(app, handlerMap) {\n  var p = pathUtil.getHandlerPath(app.getBase(), app.serverType);\n  if (!!p){\n    fs.watch(p, function(event, name) {\n      if(event === 'change') {\n        handlerMap[app.serverType] = Loader.load(p, app);\n      }\n    });\n  }\n};\n\nvar getResp = function(args) {\n  var len = args.length;\n  if(len == 1) {\n    return [];\n  }\n\n  if(len == 2) {\n    return [args[1]];\n  }\n\n  if(len == 3) {\n    return [args[1], args[2]];\n  }\n\n  if(len == 4) {\n    return [args[1], args[2], args[3]];\n  }\n\n  var r = new Array(len);\n  for (var i = 1; i < len; i++) {\n    r[i] = args[i];\n  }\n\n  return r;\n}"
  },
  {
    "path": "lib/common/service/sessionService.js",
    "content": "var EventEmitter = require('events').EventEmitter;\nvar util = require('util');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar utils = require('../../util/utils');\n\nvar FRONTEND_SESSION_FIELDS = ['id', 'frontendId', 'uid', '__sessionService__'];\nvar EXPORTED_SESSION_FIELDS = ['id', 'frontendId', 'uid', 'settings'];\n\nvar ST_INITED = 0;\nvar ST_CLOSED = 1;\n\n/**\n * Session service maintains the internal session for each client connection.\n *\n * Session service is created by session component and is only\n * <b>available</b> in frontend servers. You can access the service by\n * `app.get('sessionService')` or `app.sessionService` in frontend servers.\n *\n * @param {Object} opts constructor parameters\n * @class\n * @constructor\n */\nvar SessionService = function(opts) {\n  opts = opts || {};\n  this.singleSession = opts.singleSession;\n  this.sessions = {};     // sid -> session\n  this.uidMap = {};       // uid -> sessions\n};\n\nmodule.exports = SessionService;\n\n/**\n * Create and return internal session.\n *\n * @param {Integer} sid uniqe id for the internal session \n * @param {String} frontendId frontend server in which the internal session is created \n * @param {Object} socket the underlying socket would be held by the internal session  \n *\n * @return {Session}\n *\n * @memberOf SessionService\n * @api private\n */\nSessionService.prototype.create = function(sid, frontendId, socket) {\n  var session = new Session(sid, frontendId, socket, this);\n  this.sessions[session.id] = session;\n\n  return session;\n};\n\n/**\n * Bind the session with a user id.\n *\n * @memberOf SessionService\n * @api private\n */\nSessionService.prototype.bind = function(sid, uid, cb) {\n  var session = this.sessions[sid];\n\n  if(!session) {\n    process.nextTick(function() {\n      cb(new Error('session does not exist, sid: ' + sid));\n    });\n    return;\n  }\n\n  if(session.uid) {\n    if(session.uid === uid) {\n      // already bound with the same uid\n      cb();\n      return;\n    }\n\n    // already bound with other uid\n    process.nextTick(function() {\n      cb(new Error('session has already bind with ' + session.uid));\n    });\n    return;\n  }\n\n  var sessions = this.uidMap[uid];\n\n  if(!!this.singleSession && !!sessions) {\n    process.nextTick(function() {\n      cb(new Error('singleSession is enabled, and session has already bind with uid: ' + uid));\n    });\n    return;\n  }\n\n  if(!sessions) {\n    sessions = this.uidMap[uid] = [];\n  }\n\n  for(var i=0, l=sessions.length; i<l; i++) {\n    // session has binded with the uid\n    if(sessions[i].id === session.id) {\n      process.nextTick(cb);\n      return;\n    }\n  }\n  sessions.push(session);\n\n  session.bind(uid);\n\n  if(cb) {\n    process.nextTick(cb);\n  }\n};\n\n/**\n * Unbind a session with the user id.\n *\n * @memberOf SessionService\n * @api private\n */\nSessionService.prototype.unbind = function(sid, uid, cb) {\n  var session = this.sessions[sid];\n\n  if(!session) {\n    process.nextTick(function() {\n      cb(new Error('session does not exist, sid: ' + sid));\n    });\n    return;\n  }\n\n  if(!session.uid || session.uid !== uid) {\n    process.nextTick(function() {\n      cb(new Error('session has not bind with ' + session.uid));\n    });\n    return;\n  }\n\n  var sessions = this.uidMap[uid], sess;\n  if(sessions) {\n    for(var i=0, l=sessions.length; i<l; i++) {\n      sess = sessions[i];\n      if(sess.id === sid) {\n        sessions.splice(i, 1);\n        break;\n      }\n    }\n\n    if(sessions.length === 0) {\n      delete this.uidMap[uid];\n    }\n  }\n  session.unbind(uid);\n\n  if(cb) {\n    process.nextTick(cb);\n  }\n};\n\n/**\n * Get session by id.\n *\n * @param {Number} id The session id\n * @return {Session}\n *\n * @memberOf SessionService\n * @api private\n */\nSessionService.prototype.get = function(sid) {\n  return this.sessions[sid];\n};\n\n/**\n * Get sessions by userId.\n *\n * @param {Number} uid User id associated with the session\n * @return {Array} list of session binded with the uid\n *\n * @memberOf SessionService\n * @api private\n */\nSessionService.prototype.getByUid = function(uid) {\n  return this.uidMap[uid];\n};\n\n/**\n * Remove session by key.\n *\n * @param {Number} sid The session id\n *\n * @memberOf SessionService\n * @api private\n */\nSessionService.prototype.remove = function(sid) {\n  var session = this.sessions[sid];\n  if(session) {\n    var uid = session.uid;\n    delete this.sessions[session.id];\n\n    var sessions = this.uidMap[uid];\n    if(!sessions) {\n      return;\n    }\n\n    for(var i=0, l=sessions.length; i<l; i++) {\n      if(sessions[i].id === sid) {\n        sessions.splice(i, 1);\n        if(sessions.length === 0) {\n          delete this.uidMap[uid];\n        }\n        break;\n      }\n    }\n  }\n};\n\n/**\n * Import the key/value into session.\n *\n * @api private\n */\nSessionService.prototype.import = function(sid, key, value, cb) {\n  var session = this.sessions[sid];\n  if(!session) {\n    utils.invokeCallback(cb, new Error('session does not exist, sid: ' + sid));\n    return;\n  }\n  session.set(key, value);\n  utils.invokeCallback(cb);\n};\n\n/**\n * Import new value for the existed session.\n *\n * @memberOf SessionService\n * @api private\n */\nSessionService.prototype.importAll = function(sid, settings, cb) {\n  var session = this.sessions[sid];\n  if(!session) {\n    utils.invokeCallback(cb, new Error('session does not exist, sid: ' + sid));\n    return;\n  }\n\n  for(var f in settings) {\n    session.set(f, settings[f]);\n  }\n  utils.invokeCallback(cb);\n};\n\n/**\n * Kick all the session offline under the user id.\n *\n * @param {Number}   uid user id asscociated with the session\n * @param {Function} cb  callback function\n *\n * @memberOf SessionService\n */\nSessionService.prototype.kick = function(uid, reason, cb) {\n  // compatible for old kick(uid, cb);\n  if(typeof reason === 'function') {\n    cb = reason;\n    reason = 'kick';\n  }\n  var sessions = this.getByUid(uid);\n\n  if(sessions) {\n    // notify client\n    var sids = [];\n    var self = this;\n    sessions.forEach(function(session) {\n      sids.push(session.id);\n    });\n\n    sids.forEach(function(sid) {\n      self.sessions[sid].closed(reason);\n    });\n\n    process.nextTick(function() {\n      utils.invokeCallback(cb);\n    });\n  } else {\n    process.nextTick(function() {\n      utils.invokeCallback(cb);\n    });\n  }\n};\n\n/**\n * Kick a user offline by session id.\n *\n * @param {Number}   sid session id\n * @param {Function} cb  callback function\n *\n * @memberOf SessionService\n */\nSessionService.prototype.kickBySessionId = function(sid, reason, cb) {\n  if(typeof reason === 'function') {\n    cb = reason;\n    reason = 'kick';\n  }\n\n  var session = this.get(sid);\n\n  if(session) {\n    // notify client\n    session.closed(reason);\n    process.nextTick(function() {\n      utils.invokeCallback(cb);\n    });\n  } else {\n    process.nextTick(function() {\n      utils.invokeCallback(cb);\n    });\n  }\n};\n\n/**\n * Get client remote address by session id.\n *\n * @param {Number}   sid session id\n * @return {Object} remote address of client\n *\n * @memberOf SessionService\n */\n SessionService.prototype.getClientAddressBySessionId = function(sid) {\n   var session = this.get(sid);\n   if(session) {\n      var socket = session.__socket__;\n      return socket.remoteAddress;\n   } else {\n      return null;\n   }\n };\n\n/**\n * Send message to the client by session id.\n *\n * @param {String} sid session id\n * @param {Object} msg message to send\n *\n * @memberOf SessionService\n * @api private\n */\nSessionService.prototype.sendMessage = function(sid, msg) {\n  var session = this.get(sid);\n\n  if(!session) {\n    logger.debug('Fail to send message for non-existing session, sid: ' + sid + ' msg: ' + msg);\n    return false;\n  }\n\n  return send(this, session, msg);\n};\n\n/**\n * Send message to the client by user id.\n *\n * @param {String} uid userId\n * @param {Object} msg message to send\n *\n * @memberOf SessionService\n * @api private\n */\nSessionService.prototype.sendMessageByUid = function(uid, msg) {\n  var sessions = this.getByUid(uid);\n\n  if(!sessions) {\n    logger.debug('fail to send message by uid for non-existing session. uid: %j',\n        uid);\n    return false;\n  }\n\n  for(var i=0, l=sessions.length; i<l; i++) {\n    send(this, sessions[i], msg);\n  }\n\n  return true\n};\n\n/**\n * Iterate all the session in the session service.\n *\n * @param  {Function} cb callback function to fetch session\n * @api private\n */\nSessionService.prototype.forEachSession = function(cb) {\n  for(var sid in this.sessions) {\n    cb(this.sessions[sid]);\n  }\n};\n\n/**\n * Iterate all the binded session in the session service.\n *\n * @param  {Function} cb callback function to fetch session\n * @api private\n */\nSessionService.prototype.forEachBindedSession = function(cb) {\n  var i, l, sessions;\n  for(var uid in this.uidMap) {\n    sessions = this.uidMap[uid];\n    for(i=0, l=sessions.length; i<l; i++) {\n      cb(sessions[i]);\n    }\n  }\n};\n\n/**\n * Get sessions' quantity in specified server.\n *\n */\nSessionService.prototype.getSessionsCount = function() {\n  return utils.size(this.sessions);\n};\n\n/**\n * Send message to the client that associated with the session.\n *\n * @api private\n */\nvar send = function(service, session, msg) {\n  session.send(msg);\n\n  return true;\n};\n\n/**\n * Session maintains the relationship between client connection and user information.\n * There is a session associated with each client connection. And it should bind to a\n * user id after the client passes the identification.\n *\n * Session is created in frontend server and should not be accessed in handler.\n * There is a proxy class called BackendSession in backend servers and FrontendSession \n * in frontend servers.\n */\nvar Session = function(sid, frontendId, socket, service) {\n  EventEmitter.call(this);\n  this.id = sid;          // r\n  this.frontendId = frontendId; // r\n  this.uid = null;        // r\n  this.settings = {};\n\n  // private\n  this.__socket__ = socket;\n  this.__sessionService__ = service;\n  this.__state__ = ST_INITED;\n};\n\nutil.inherits(Session, EventEmitter);\n\n/*\n * Export current session as frontend session.\n */\nSession.prototype.toFrontendSession = function() {\n  return new FrontendSession(this);\n};\n\n/**\n * Bind the session with the the uid.\n *\n * @param {Number} uid User id\n * @api public\n */\nSession.prototype.bind = function(uid) {\n  this.uid = uid;\n  this.emit('bind', uid);\n};\n\n/**\n * Unbind the session with the the uid.\n *\n * @param {Number} uid User id\n * @api private\n */\nSession.prototype.unbind = function(uid) {\n  this.uid = null;\n  this.emit('unbind', uid);\n};\n\n/**\n * Set values (one or many) for the session.\n *\n * @param {String|Object} key session key\n * @param {Object} value session value\n * @api public\n */\nSession.prototype.set = function(key, value) {\n  if (utils.isObject(key)) {\n    for (var i in key) {\n      this.settings[i] = key[i];\n    }\n  } else {\n    this.settings[key] = value;\n  }\n};\n\n/**\n * Remove value from the session.\n *\n * @param {String} key session key\n * @api public\n */\n Session.prototype.remove = function(key) {\n  delete this[key];\n};\n\n/**\n * Get value from the session.\n *\n * @param {String} key session key\n * @return {Object} value associated with session key\n * @api public\n */\nSession.prototype.get = function(key) {\n  return this.settings[key];\n};\n\n/**\n * Send message to the session.\n *\n * @param  {Object} msg final message sent to client\n */\nSession.prototype.send = function(msg) {\n  this.__socket__.send(msg);\n};\n\n/**\n * Send message to the session in batch.\n *\n * @param  {Array} msgs list of message\n */\nSession.prototype.sendBatch = function(msgs) {\n  this.__socket__.sendBatch(msgs);\n};\n\n/**\n * Closed callback for the session which would disconnect client in next tick.\n *\n * @api public\n */\nSession.prototype.closed = function(reason) {\n  logger.debug('session on [%s] is closed with session id: %s', this.frontendId, this.id);\n  if(this.__state__ === ST_CLOSED) {\n    return;\n  }\n  this.__state__ = ST_CLOSED;\n  this.__sessionService__.remove(this.id);\n  this.emit('closed', this.toFrontendSession(), reason);\n  this.__socket__.emit('closing', reason);\n\n  var self = this;\n  // give a chance to send disconnect message to client\n\n  process.nextTick(function() {\n    self.__socket__.disconnect();\n  });\n};\n\n/**\n * Frontend session for frontend server.\n */\nvar FrontendSession = function(session) {\n  EventEmitter.call(this);\n  clone(session, this, FRONTEND_SESSION_FIELDS);\n  // deep copy for settings\n  this.settings = dclone(session.settings);\n  this.__session__ = session;\n};\n\nutil.inherits(FrontendSession, EventEmitter);\n\nFrontendSession.prototype.bind = function(uid, cb) {\n  var self = this;\n  this.__sessionService__.bind(this.id, uid, function(err) {\n    if(!err) {\n      self.uid = uid;\n    }\n    utils.invokeCallback(cb, err);\n  });\n};\n\nFrontendSession.prototype.unbind = function(uid, cb) {\n  var self = this;\n  this.__sessionService__.unbind(this.id, uid, function(err) {\n    if(!err) {\n      self.uid = null;\n    }\n    utils.invokeCallback(cb, err);\n  });\n};\n\nFrontendSession.prototype.set = function(key, value) {\n  this.settings[key] = value;\n};\n\nFrontendSession.prototype.get = function(key) {\n  return this.settings[key];\n};\n\nFrontendSession.prototype.push = function(key, cb) {\n  this.__sessionService__.import(this.id, key, this.get(key), cb);\n};\n\nFrontendSession.prototype.pushAll = function(cb) {\n  this.__sessionService__.importAll(this.id, this.settings, cb);\n};\n\nFrontendSession.prototype.on = function(event, listener) {\n  EventEmitter.prototype.on.call(this, event, listener);\n  this.__session__.on(event, listener);\n};\n\n/**\n * Export the key/values for serialization.\n *\n * @api private\n */\nFrontendSession.prototype.export = function() {\n  var res = {};\n  clone(this, res, EXPORTED_SESSION_FIELDS);\n  return res;\n};\n\nvar clone = function(src, dest, includes) {\n  var f;\n  for(var i=0, l=includes.length; i<l; i++) {\n    f = includes[i];\n    dest[f] = src[f];\n  }\n};\n\nvar dclone = function(src) {\n  var res = {};\n  for(var f in src) {\n    res[f] = src[f];\n  }\n  return res;\n};\n"
  },
  {
    "path": "lib/components/backendSession.js",
    "content": "var BackendSessionService = require('../common/service/backendSessionService');\n\nmodule.exports = function(app) {\n  var service = new BackendSessionService(app);\n  service.name = '__backendSession__';\n  // export backend session service to the application context.\n  app.set('backendSessionService', service, true);\n\n  // for compatibility as `LocalSession` is renamed to `BackendSession` \n  app.set('localSessionService', service, true);\n\n  return service;\n};\n"
  },
  {
    "path": "lib/components/channel.js",
    "content": "var ChannelService = require('../common/service/channelService');\n\nmodule.exports = function(app, opts) {\n  var service = new ChannelService(app, opts);\n  app.set('channelService', service, true);\n  service.name = '__channel__';\n  return service;\n};"
  },
  {
    "path": "lib/components/connection.js",
    "content": "var ConnectionService = require('../common/service/connectionService');\n\n/**\n * Connection component for statistics connection status of frontend servers\n */\nmodule.exports = function(app) {\n  return new Component(app);\n};\n\nvar Component = function(app) {\n  this.app = app;\n  this.service = new ConnectionService(app);\n\n  // proxy the service methods except the lifecycle interfaces of component\n  var method, self = this;\n\n  var getFun = function(m) {\n    return (function() {\n          return function() {\n            return self.service[m].apply(self.service, arguments);\n          };\n    })();\n  };\n\n  for(var m in this.service) {\n    if(m !== 'start' && m !== 'stop') {\n      method = this.service[m];\n      if(typeof method === 'function') {\n        this[m] = getFun(m);\n      }\n    }\n  }\n};\n\nComponent.prototype.name = '__connection__';\n"
  },
  {
    "path": "lib/components/connector.js",
    "content": "var logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar taskManager = require('../common/manager/taskManager');\nvar pomelo = require('../pomelo');\nvar rsa = require(\"node-bignumber\");\nvar events = require('../util/events');\nvar utils = require('../util/utils');\n\nmodule.exports = function(app, opts) {\n  return new Component(app, opts);\n};\n\n/**\n * Connector component. Receive client requests and attach session with socket.\n *\n * @param {Object} app  current application context\n * @param {Object} opts attach parameters\n *                      opts.connector {Object} provides low level network and protocol details implementation between server and clients.\n */\nvar Component = function(app, opts) {\n  opts = opts || {};\n  this.app = app;\n  this.connector = getConnector(app, opts);\n  this.encode = opts.encode;\n  this.decode = opts.decode;\n  this.useCrypto = opts.useCrypto;\n  this.useHostFilter = opts.useHostFilter;\n  this.useAsyncCoder = opts.useAsyncCoder;\n  this.blacklistFun = opts.blacklistFun;\n  this.keys = {};\n  this.blacklist = [];\n\n  if (opts.useDict) {\n    app.load(pomelo.dictionary, app.get('dictionaryConfig'));\n  }\n\n  if (opts.useProtobuf) {\n    app.load(pomelo.protobuf, app.get('protobufConfig'));\n  }\n\n  // component dependencies\n  this.server = null;\n  this.session = null;\n  this.connection = null;\n};\n\nvar pro = Component.prototype;\n\npro.name = '__connector__';\n\npro.start = function(cb) {\n  this.server = this.app.components.__server__;\n  this.session = this.app.components.__session__;\n  this.connection = this.app.components.__connection__;\n\n  // check component dependencies\n  if (!this.server) {\n    process.nextTick(function() {\n      utils.invokeCallback(cb, new Error('fail to start connector component for no server component loaded'));\n    });\n    return;\n  }\n\n  if (!this.session) {\n    process.nextTick(function() {\n      utils.invokeCallback(cb, new Error('fail to start connector component for no session component loaded'));\n    });\n    return;\n  }\n\n  process.nextTick(cb);\n};\n\npro.afterStart = function(cb) {\n  this.connector.start(cb);\n  this.connector.on('connection', hostFilter.bind(this, bindEvents));\n};\n\npro.stop = function(force, cb) {\n  if (this.connector) {\n    this.connector.stop(force, cb);\n    this.connector = null;\n    return;\n  } else {\n    process.nextTick(cb);\n  }\n};\n\npro.send = function(reqId, route, msg, recvs, opts, cb) {\n  logger.debug('[%s] send message reqId: %s, route: %s, msg: %j, receivers: %j, opts: %j', this.app.serverId, reqId, route, msg, recvs, opts);\n  if (this.useAsyncCoder) {\n    return this.sendAsync(reqId, route, msg, recvs, opts, cb);\n  }\n\n  var emsg = msg;\n  if (this.encode) {\n    // use costumized encode\n    emsg = this.encode.call(this, reqId, route, msg);\n  } else if (this.connector.encode) {\n    // use connector default encode\n    emsg = this.connector.encode(reqId, route, msg);\n  }\n\n  this.doSend(reqId, route, emsg, recvs, opts, cb);\n};\n\npro.sendAsync = function(reqId, route, msg, recvs, opts, cb) {\n  var emsg = msg;\n  var self = this;\n\n  if (this.encode) {\n    // use costumized encode\n    this.encode(reqId, route, msg, function(err, encodeMsg) {\n      if (err) {\n        return cb(err);\n      }\n\n      emsg = encodeMsg;\n      self.doSend(reqId, route, emsg, recvs, opts, cb);\n    });\n  } else if (this.connector.encode) {\n    // use connector default encode\n    this.connector.encode(reqId, route, msg, function(err, encodeMsg) {\n      if (err) {\n        return cb(err);\n      }\n\n      emsg = encodeMsg;\n      self.doSend(reqId, route, emsg, recvs, opts, cb);\n    });\n  }\n}\n\npro.doSend = function(reqId, route, emsg, recvs, opts, cb) {\n  if (!emsg) {\n    process.nextTick(function() {\n      return cb && cb(new Error('fail to send message for encode result is empty.'));\n    });\n  }\n\n  this.app.components.__pushScheduler__.schedule(reqId, route, emsg,\n    recvs, opts, cb);\n}\n\npro.setPubKey = function(id, key) {\n  var pubKey = new rsa.Key();\n  pubKey.n = new rsa.BigInteger(key.rsa_n, 16);\n  pubKey.e = key.rsa_e;\n  this.keys[id] = pubKey;\n};\n\npro.getPubKey = function(id) {\n  return this.keys[id];\n};\n\nvar getConnector = function(app, opts) {\n  var connector = opts.connector;\n  if (!connector) {\n    return getDefaultConnector(app, opts);\n  }\n\n  if (typeof connector !== 'function') {\n    return connector;\n  }\n\n  var curServer = app.getCurServer();\n  return connector(curServer.clientPort, curServer.host, opts);\n};\n\nvar getDefaultConnector = function(app, opts) {\n  var DefaultConnector = require('../connectors/sioconnector');\n  var curServer = app.getCurServer();\n  return new DefaultConnector(curServer.clientPort, curServer.host, opts);\n};\n\nvar hostFilter = function(cb, socket) {\n  if(!this.useHostFilter) {\n    return cb(this, socket);\n  }\n\n  var ip = socket.remoteAddress.ip;\n  var check = function(list) {\n    for (var address in list) {\n      var exp = new RegExp(list[address]);\n      if (exp.test(ip)) {\n        socket.disconnect();\n        return true;\n      }\n    }\n    return false;\n  };\n  // dynamical check\n  if (this.blacklist.length !== 0 && !!check(this.blacklist)) {\n    return;\n  }\n  // static check\n  if (!!this.blacklistFun && typeof this.blacklistFun === 'function') {\n    var self = this;\n    self.blacklistFun(function(err, list) {\n      if (!!err) {\n        logger.error('connector blacklist error: %j', err.stack);\n        utils.invokeCallback(cb, self, socket);\n        return;\n      }\n      if (!Array.isArray(list)) {\n        logger.error('connector blacklist is not array: %j', list);\n        utils.invokeCallback(cb, self, socket);\n        return;\n      }\n      if (!!check(list)) {\n        return;\n      } else {\n        utils.invokeCallback(cb, self, socket);\n        return;\n      }\n    });\n  } else {\n    utils.invokeCallback(cb, this, socket);\n  }\n};\n\nvar bindEvents = function(self, socket) {\n  var curServer = self.app.getCurServer();\n  var maxConnections = curServer['max-connections'];\n  if (self.connection && maxConnections) {\n    self.connection.increaseConnectionCount();\n    var statisticInfo = self.connection.getStatisticsInfo();\n    if (statisticInfo.totalConnCount > maxConnections) {\n      logger.warn('the server %s has reached the max connections %s', curServer.id, maxConnections);\n      socket.disconnect();\n      return;\n    }\n  }\n\n  //create session for connection\n  var session = getSession(self, socket);\n  var closed = false;\n\n  socket.on('disconnect', function() {\n    if (closed) {\n      return;\n    }\n    closed = true;\n    if (self.connection) {\n      self.connection.decreaseConnectionCount(session.uid);\n    }\n  });\n\n  socket.on('error', function() {\n    if (closed) {\n      return;\n    }\n    closed = true;\n    if (self.connection) {\n      self.connection.decreaseConnectionCount(session.uid);\n    }\n  });\n\n  // new message\n  socket.on('message', function(msg) {\n    var dmsg = msg;\n    if (self.useAsyncCoder) {\n      return handleMessageAsync(self, msg, session, socket);\n    }\n\n    if (self.decode) {\n      dmsg = self.decode(msg, session);\n    } else if (self.connector.decode) {\n      dmsg = self.connector.decode(msg, socket);\n    }\n    if (!dmsg) {\n      // discard invalid message\n      return;\n    }\n\n    // use rsa crypto\n    if (self.useCrypto) {\n      var verified = verifyMessage(self, session, dmsg);\n      if (!verified) {\n        logger.error('fail to verify the data received from client.');\n        return;\n      }\n    }\n\n    handleMessage(self, session, dmsg);\n  }); //on message end\n};\n\nvar handleMessageAsync = function(self, msg, session, socket) {\n  if (self.decode) {\n    self.decode(msg, session, function(err, dmsg) {\n      if (err) {\n        logger.error('fail to decode message from client %s .', err.stack);\n        return;\n      }\n\n      doHandleMessage(self, dmsg, session);\n    });\n  } else if (self.connector.decode) {\n    self.connector.decode(msg, socket, function(err, dmsg) {\n      if (err) {\n        logger.error('fail to decode message from client %s .', err.stack);\n        return;\n      }\n\n      doHandleMessage(self, dmsg, session);\n    });\n  }\n}\n\nvar doHandleMessage = function(self, dmsg, session) {\n  if (!dmsg) {\n    // discard invalid message\n    return;\n  }\n\n  // use rsa crypto\n  if (self.useCrypto) {\n    var verified = verifyMessage(self, session, dmsg);\n    if (!verified) {\n      logger.error('fail to verify the data received from client.');\n      return;\n    }\n  }\n\n  handleMessage(self, session, dmsg);\n}\n\n/**\n * get session for current connection\n */\nvar getSession = function(self, socket) {\n  var app = self.app,\n    sid = socket.id;\n  var session = self.session.get(sid);\n  if (session) {\n    return session;\n  }\n\n  session = self.session.create(sid, app.getServerId(), socket);\n  logger.debug('[%s] getSession session is created with session id: %s', app.getServerId(), sid);\n\n  // bind events for session\n  socket.on('disconnect', session.closed.bind(session));\n  socket.on('error', session.closed.bind(session));\n  session.on('closed', onSessionClose.bind(null, app));\n  session.on('bind', function(uid) {\n    logger.debug('session on [%s] bind with uid: %s', self.app.serverId, uid);\n    // update connection statistics if necessary\n    if (self.connection) {\n      self.connection.addLoginedUser(uid, {\n        loginTime: Date.now(),\n        uid: uid,\n        address: socket.remoteAddress.ip + ':' + socket.remoteAddress.port\n      });\n    }\n    self.app.event.emit(events.BIND_SESSION, session);\n  });\n\n  session.on('unbind', function(uid) {\n    if (self.connection) {\n      self.connection.removeLoginedUser(uid);\n    }\n    self.app.event.emit(events.UNBIND_SESSION, session);\n  });\n\n  return session;\n};\n\nvar onSessionClose = function(app, session, reason) {\n  taskManager.closeQueue(session.id, true);\n  app.event.emit(events.CLOSE_SESSION, session);\n};\n\nvar handleMessage = function(self, session, msg) {\n  logger.debug('[%s] handleMessage session id: %s, msg: %j', self.app.serverId, session.id, msg);\n  var type = checkServerType(msg.route);\n  if (!type) {\n    logger.error('invalid route string. route : %j', msg.route);\n    return;\n  }\n  self.server.globalHandle(msg, session.toFrontendSession(), function(err, resp, opts) {\n    if (resp && !msg.id) {\n      logger.warn('try to response to a notify: %j', msg.route);\n      return;\n    }\n    if (!msg.id && !resp) return;\n    if (!resp) resp = {};\n    if (!!err && !resp.code) {\n      resp.code = 500;\n    }\n    opts = {\n      type: 'response',\n      userOptions: opts || {}\n    };\n    // for compatiablity\n    opts.isResponse = true;\n\n    self.send(msg.id, msg.route, resp, [session.id], opts,\n      function() {});\n  });\n};\n\n/**\n * Get server type form request message.\n */\nvar checkServerType = function(route) {\n  if (!route) {\n    return null;\n  }\n  var idx = route.indexOf('.');\n  if (idx < 0) {\n    return null;\n  }\n  return route.substring(0, idx);\n};\n\nvar verifyMessage = function(self, session, msg) {\n  var sig = msg.body.__crypto__;\n  if (!sig) {\n    logger.error('receive data from client has no signature [%s]', self.app.serverId);\n    return false;\n  }\n\n  var pubKey;\n\n  if (!session) {\n    logger.error('could not find session.');\n    return false;\n  }\n\n  if (!session.get('pubKey')) {\n    pubKey = self.getPubKey(session.id);\n    if (!!pubKey) {\n      delete self.keys[session.id];\n      session.set('pubKey', pubKey);\n    } else {\n      logger.error('could not get public key, session id is %s', session.id);\n      return false;\n    }\n  } else {\n    pubKey = session.get('pubKey');\n  }\n\n  if (!pubKey.n || !pubKey.e) {\n    logger.error('could not verify message without public key [%s]', self.app.serverId);\n    return false;\n  }\n\n  delete msg.body.__crypto__;\n\n  var message = JSON.stringify(msg.body);\n  if (utils.hasChineseChar(message))\n    message = utils.unicodeToUtf8(message);\n\n  return pubKey.verifyString(message, sig);\n};\n"
  },
  {
    "path": "lib/components/dictionary.js",
    "content": "var fs = require('fs');\nvar path = require('path');\nvar utils = require('../util/utils');\nvar Loader = require('pomelo-loader');\nvar pathUtil = require('../util/pathUtil');\nvar crypto = require('crypto');\n\nmodule.exports = function(app, opts) {\n  return new Component(app, opts);\n};\n\nvar Component = function(app, opts) {\n  this.app = app;\n  this.dict = {};\n  this.abbrs = {};\n  this.userDicPath = null;\n  this.version = \"\";\n\n  //Set user dictionary\n  var p = path.join(app.getBase(), '/config/dictionary.json');\n  if(!!opts && !!opts.dict) {\n    p = opts.dict;\n  }\n  if(fs.existsSync(p)) {\n    this.userDicPath = p;\n  }\n};\n\nvar pro = Component.prototype;\n\npro.name = '__dictionary__';\n\npro.start = function(cb) {\n  var servers = this.app.get('servers');\n  var routes = [];\n\n  //Load all the handler files\n  for(var serverType in servers) {\n    var p = pathUtil.getHandlerPath(this.app.getBase(), serverType);\n    if(!p) {\n      continue;\n    }\n\n    var handlers = Loader.load(p, this.app);\n\n    for(var name in handlers) {\n      var handler = handlers[name];\n      for(var key in handler) {\n        if(typeof(handler[key]) === 'function') {\n          routes.push(serverType + '.' + name + '.' + key);\n        }\n      }\n    }\n  }\n\n  //Sort the route to make sure all the routers abbr are the same in all the servers\n  routes.sort();\n  var abbr;\n  var i;\n  for(i = 0; i < routes.length; i++) {\n    abbr = i + 1;\n    this.abbrs[abbr] = routes[i];\n    this.dict[routes[i]] = abbr;\n  }\n\n  //Load user dictionary\n  if(!!this.userDicPath) {\n    var userDic = require(this.userDicPath);\n\n    abbr = routes.length + 1;\n    for(i = 0; i < userDic.length; i++) {\n      var route = userDic[i];\n\n      this.abbrs[abbr] = route;\n      this.dict[route] = abbr;\n      abbr++;\n    }\n  }\n  \n  this.version = crypto.createHash('md5').update(JSON.stringify(this.dict)).digest('base64');\n\n  utils.invokeCallback(cb);\n};\n\npro.getDict = function() {\n  return this.dict;\n};\n\npro.getAbbrs = function() {\n  return this.abbrs;\n};\n\npro.getVersion = function() {\n  return this.version;\n};\n"
  },
  {
    "path": "lib/components/master.js",
    "content": "/**\n * Component for master.\n */\nvar Master = require('../master/master');\n\n/**\n * Component factory function\n *\n * @param  {Object} app  current application context\n * @return {Object}      component instances\n */\nmodule.exports = function (app, opts) {\n\treturn new Component(app, opts);\n};\n\n/**\n* Master component class\n*\n* @param {Object} app  current application context\n*/\nvar Component = function (app, opts) {\n\tthis.master = new Master(app, opts);\n};\n\nvar pro = Component.prototype;\n\npro.name = '__master__';\n\n/**\n * Component lifecycle function\n *\n * @param  {Function} cb\n * @return {Void}\n */\npro.start = function (cb) {\n  this.master.start(cb);\n};\n\n/**\n * Component lifecycle function\n *\n * @param  {Boolean}   force whether stop the component immediately\n * @param  {Function}  cb\n * @return {Void}\n */\npro.stop = function (force, cb) {\n  this.master.stop(cb);\n};\n"
  },
  {
    "path": "lib/components/monitor.js",
    "content": "/**\n * Component for monitor.\n * Load and start monitor client.\n */\nvar Monitor = require('../monitor/monitor');\n\n\n\n/**\n * Component factory function\n *\n * @param  {Object} app  current application context\n * @return {Object}      component instances\n */\nmodule.exports = function(app, opts) {\n  return new Component(app, opts);\n};\n\nvar Component = function(app, opts) {\n  this.monitor = new Monitor(app, opts);\n};\n\nvar pro = Component.prototype;\n\npro.name = '__monitor__';\n\npro.start = function(cb) {\n  this.monitor.start(cb);\n};\n\npro.stop = function(force, cb) {\n  this.monitor.stop(cb);\n};\n\npro.reconnect = function(masterInfo) {\n  this.monitor.reconnect(masterInfo);\n};\n"
  },
  {
    "path": "lib/components/protobuf.js",
    "content": "var fs = require('fs');\nvar path = require('path');\nvar protobuf = require('pomelo-protobuf');\nvar Constants = require('../util/constants');\nvar crypto = require('crypto');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\nmodule.exports = function(app, opts) {\n  return new Component(app, opts);\n};\n\nvar Component = function(app, opts) {\n  this.app = app;\n  opts = opts || {};\n  this.watchers = {};\n  this.serverProtos = {};\n  this.clientProtos = {};\n  this.version = \"\";\n  \n  var env = app.get(Constants.RESERVED.ENV);\n  var originServerPath = path.join(app.getBase(), Constants.FILEPATH.SERVER_PROTOS);\n  var presentServerPath = path.join(Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.SERVER_PROTOS));\n  var originClientPath = path.join(app.getBase(), Constants.FILEPATH.CLIENT_PROTOS);\n  var presentClientPath = path.join(Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.CLIENT_PROTOS));\n\n  this.serverProtosPath = opts.serverProtos || (fs.existsSync(originServerPath) ? Constants.FILEPATH.SERVER_PROTOS : presentServerPath);\n  this.clientProtosPath = opts.clientProtos || (fs.existsSync(originClientPath) ? Constants.FILEPATH.CLIENT_PROTOS : presentClientPath);\n\n  this.setProtos(Constants.RESERVED.SERVER, path.join(app.getBase(), this.serverProtosPath));\n  this.setProtos(Constants.RESERVED.CLIENT, path.join(app.getBase(), this.clientProtosPath));\n\n  protobuf.init({encoderProtos:this.serverProtos, decoderProtos:this.clientProtos});\n};\n\nvar pro = Component.prototype;\n\npro.name = '__protobuf__';\n\npro.encode = function(key, msg) {\n  return protobuf.encode(key, msg);\n};\n\npro.encode2Bytes = function(key, msg) {\n  return protobuf.encode2Bytes(key, msg);\n};\n\npro.decode = function(key, msg) {\n  return protobuf.decode(key, msg);\n};\n\npro.getProtos = function() {\n  return {\n    server : this.serverProtos,\n    client : this.clientProtos,\n    version : this.version\n  };\n};\n\npro.getVersion = function() {\n  return this.version;\n};\n\npro.setProtos = function(type, path) {\n  if(!fs.existsSync(path)) {\n    return;\n  }\n\n  if(type === Constants.RESERVED.SERVER) {\n    this.serverProtos = protobuf.parse(require(path));\n  }\n\n  if(type === Constants.RESERVED.CLIENT) {\n    this.clientProtos = protobuf.parse(require(path));\n  }\n\n  var protoStr = JSON.stringify(this.clientProtos) + JSON.stringify(this.serverProtos);\n  this.version = crypto.createHash('md5').update(protoStr).digest('base64');\n\n  //Watch file\n  var watcher = fs.watch(path, this.onUpdate.bind(this, type, path));\n  if (this.watchers[type]) {\n    this.watchers[type].close();\n  }\n  this.watchers[type] = watcher;\n};\n\npro.onUpdate = function(type, path, event) {\n  if(event !== 'change') {\n    return;\n  }\n\n  var self = this;\n  fs.readFile(path, 'utf8' ,function(err, data) {\n    try {\n      var protos = protobuf.parse(JSON.parse(data));\n      if(type === Constants.RESERVED.SERVER) {\n        protobuf.setEncoderProtos(protos);\n        self.serverProtos = protos;\n      } else {\n        protobuf.setDecoderProtos(protos);\n        self.clientProtos = protos;\n      }\n\n      var protoStr = JSON.stringify(self.clientProtos) + JSON.stringify(self.serverProtos);\n      self.version = crypto.createHash('md5').update(protoStr).digest('base64');\n      logger.info('change proto file , type : %j, path : %j, version : %j', type, path, self.version);\n    } catch(e) {\n      logger.warn(\"change proto file error! path : %j\", path);\n      logger.warn(e);\n    }\n  });\n};\n\npro.stop = function(force, cb) {\n  for (var type in this.watchers) {\n    this.watchers[type].close();\n  }\n  this.watchers = {};\n  process.nextTick(cb);\n};\n"
  },
  {
    "path": "lib/components/proxy.js",
    "content": "/**\n * Component for proxy.\n * Generate proxies for rpc client.\n */\nvar crc = require('crc');\nvar utils = require('../util/utils');\nvar events = require('../util/events');\nvar Client = require('pomelo-rpc').client;\nvar pathUtil = require('../util/pathUtil');\nvar Constants = require('../util/constants');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\n/**\n * Component factory function\n *\n * @param {Object} app  current application context\n * @param {Object} opts construct parameters\n *                      opts.router: (optional) rpc message route function, route(routeParam, msg, cb),\n *                      opts.mailBoxFactory: (optional) mail box factory instance.\n * @return {Object}     component instance\n */\nmodule.exports = function(app, opts) {\n  opts = opts || {};\n  // proxy default config\n  // cacheMsg is deprecated, just for compatibility here.\n  opts.bufferMsg = opts.bufferMsg || opts.cacheMsg || false;\n  opts.interval = opts.interval || 30;\n  opts.router = genRouteFun();\n  opts.context = app;\n  opts.routeContext = app;\n  if (app.enabled('rpcDebugLog')) {\n    opts.rpcDebugLog = true;\n    opts.rpcLogger = require('pomelo-logger').getLogger('rpc-debug', __filename);\n  }\n\n  return new Component(app, opts);\n};\n\n/**\n * Proxy component class\n *\n * @param {Object} app  current application context\n * @param {Object} opts construct parameters\n */\nvar Component = function(app, opts) {\n  this.app = app;\n  this.opts = opts;\n  this.client = genRpcClient(this.app, opts);\n  this.app.event.on(events.ADD_SERVERS, this.addServers.bind(this));\n  this.app.event.on(events.REMOVE_SERVERS, this.removeServers.bind(this));\n  this.app.event.on(events.REPLACE_SERVERS, this.replaceServers.bind(this));\n};\n\nvar pro = Component.prototype;\n\npro.name = '__proxy__';\n\n/**\n * Proxy component lifecycle function\n *\n * @param {Function} cb\n * @return {Void}\n */\npro.start = function(cb) {\n  if(this.opts.enableRpcLog) {\n    logger.warn('enableRpcLog is deprecated in 0.8.0, please use app.rpcFilter(pomelo.rpcFilters.rpcLog())');\n  }\n  var rpcBefores = this.app.get(Constants.KEYWORDS.RPC_BEFORE_FILTER);\n  var rpcAfters = this.app.get(Constants.KEYWORDS.RPC_AFTER_FILTER);\n  var rpcErrorHandler = this.app.get(Constants.RESERVED.RPC_ERROR_HANDLER);\n\n  if(!!rpcBefores) {\n    this.client.before(rpcBefores);\n  } \n  if(!!rpcAfters) {\n    this.client.after(rpcAfters);\n  }\n  if(!!rpcErrorHandler) {\n    this.client.setErrorHandler(rpcErrorHandler);\n  }\n  process.nextTick(cb);\n};\n\n/**\n * Component lifecycle callback\n *\n * @param {Function} cb\n * @return {Void}\n */\npro.afterStart = function(cb) {\n  var self = this;\n  this.app.__defineGetter__('rpc', function() {\n    return self.client.proxies.user;\n  });\n  this.app.__defineGetter__('sysrpc', function() {\n    return self.client.proxies.sys;\n  });\n  this.app.set('rpcInvoke', this.client.rpcInvoke.bind(this.client), true);\n  this.client.start(cb);\n};\n\n/**\n * Add remote server to the rpc client.\n *\n * @param {Array} servers server info list, {id, serverType, host, port}\n */\npro.addServers = function(servers) {\n  if (!servers || !servers.length) {\n    return;\n  }\n\n  genProxies(this.client, this.app, servers);\n  this.client.addServers(servers);\n};\n\n/**\n * Remove remote server from the rpc client.\n *\n * @param  {Array} ids server id list\n */\npro.removeServers = function(ids) {\n  this.client.removeServers(ids);\n};\n\n/**\n * Replace remote servers from the rpc client.\n *\n * @param  {Array} ids server id list\n */\npro.replaceServers = function(servers) {\n  if (!servers || !servers.length) {\n    return;\n  }\n\n  // update proxies\n  this.client.proxies = {};\n  genProxies(this.client, this.app, servers);\n\n  this.client.replaceServers(servers);\n};\n\n/**\n * Proxy for rpc client rpcInvoke.\n *\n * @param {String}   serverId remote server id\n * @param {Object}   msg      rpc message: {serverType: serverType, service: serviceName, method: methodName, args: arguments}\n * @param {Function} cb      callback function\n */\npro.rpcInvoke = function(serverId, msg, cb) {\n  this.client.rpcInvoke(serverId, msg, cb);\n};\n\n/**\n * Generate rpc client\n *\n * @param {Object} app current application context\n * @param {Object} opts contructor parameters for rpc client\n * @return {Object} rpc client\n */\nvar genRpcClient = function(app, opts) {\n  opts.context = app;\n  opts.routeContext = app;\n  if(!!opts.rpcClient) {\n    return opts.rpcClient.create(opts);\n  } else {\n    return Client.create(opts);\n  }\n};\n\n/**\n * Generate proxy for the server infos.\n *\n * @param  {Object} client rpc client instance\n * @param  {Object} app    application context\n * @param  {Array} sinfos server info list\n */\nvar genProxies = function(client, app, sinfos) {\n  var item;\n  for (var i = 0, l = sinfos.length; i < l; i++) {\n    item = sinfos[i];\n    if (hasProxy(client, item)) {\n      continue;\n    }\n    client.addProxies(getProxyRecords(app, item));\n  }\n};\n\n/**\n * Check a server whether has generated proxy before\n *\n * @param  {Object}  client rpc client instance\n * @param  {Object}  sinfo  server info\n * @return {Boolean}        true or false\n */\nvar hasProxy = function(client, sinfo) {\n  var proxy = client.proxies;\n  return !!proxy.sys && !! proxy.sys[sinfo.serverType];\n};\n\n/**\n * Get proxy path for rpc client.\n * Iterate all the remote service path and create remote path record.\n *\n * @param {Object} app current application context\n * @param {Object} sinfo server info, format: {id, serverType, host, port}\n * @return {Array}     remote path record array\n */\nvar getProxyRecords = function(app, sinfo) {\n  var records = [],\n    appBase = app.getBase(),\n    record;\n  // sys remote service path record\n  if (app.isFrontend(sinfo)) {\n    record = pathUtil.getSysRemotePath('frontend');\n  } else {\n    record = pathUtil.getSysRemotePath('backend');\n  }\n  if (record) {\n    records.push(pathUtil.remotePathRecord('sys', sinfo.serverType, record));\n  }\n\n  // user remote service path record\n  record = pathUtil.getUserRemotePath(appBase, sinfo.serverType);\n  if (record) {\n    records.push(pathUtil.remotePathRecord('user', sinfo.serverType, record));\n  }\n\n  return records;\n};\n\nvar genRouteFun = function() {\n  return function(session, msg, app, cb) {\n    var routes = app.get('__routes__');\n\n    if (!routes) {\n      defaultRoute(session, msg, app, cb);\n      return;\n    }\n\n    var type = msg.serverType,\n      route = routes[type] || routes['default'];\n\n    if (route) {\n      route(session, msg, app, cb);\n    } else {\n      defaultRoute(session, msg, app, cb);\n    }\n  };\n};\n\nvar defaultRoute = function(session, msg, app, cb) {\n  var list = app.getServersByType(msg.serverType);\n  if (!list || !list.length) {\n    cb(new Error('can not find server info for type:' + msg.serverType));\n    return;\n  }\n\n  var uid = session ? (session.uid || '') : '';\n  var index = Math.abs(crc.crc32(uid.toString())) % list.length;\n  utils.invokeCallback(cb, null, list[index].id);\n};\n"
  },
  {
    "path": "lib/components/pushScheduler.js",
    "content": "/**\n * Scheduler component to schedule message sending.\n */\n\nvar DefaultScheduler = require('../pushSchedulers/direct');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\nmodule.exports = function(app, opts) {\n  return new PushScheduler(app, opts);\n};\n\nvar PushScheduler = function(app, opts) {\n  this.app = app;\n  opts = opts || {};\n  this.scheduler = getScheduler(this, app, opts);\n};\n\nPushScheduler.prototype.name = '__pushScheduler__';\n\n/**\n * Component lifecycle callback\n *\n * @param {Function} cb\n * @return {Void}\n */\nPushScheduler.prototype.afterStart = function(cb) {\n  if(this.isSelectable) {\n    for (var k in this.scheduler) {\n      var sch = this.scheduler[k];\n      if(typeof sch.start === 'function') {\n        sch.start();\n      }\n    }\n    process.nextTick(cb);\n  } else if(typeof this.scheduler.start === 'function') {\n    this.scheduler.start(cb);\n  } else {\n    process.nextTick(cb);\n  }\n};\n\n/**\n * Component lifecycle callback\n *\n * @param {Function} cb\n * @return {Void}\n */\nPushScheduler.prototype.stop = function(force, cb) {\n  if(this.isSelectable) {\n    for (var k in this.scheduler) {\n      var sch = this.scheduler[k];\n      if(typeof sch.stop === 'function') {\n        sch.stop();\n      }\n    }\n    process.nextTick(cb);\n  } else if(typeof this.scheduler.stop === 'function') {\n    this.scheduler.stop(cb);\n  } else {\n    process.nextTick(cb);\n  }\n};\n\n/**\n * Schedule how the message to send.\n *\n * @param  {Number}   reqId request id\n * @param  {String}   route route string of the message\n * @param  {Object}   msg   message content after encoded\n * @param  {Array}    recvs array of receiver's session id\n * @param  {Object}   opts  options\n * @param  {Function} cb\n */\n\nPushScheduler.prototype.schedule = function(reqId, route, msg, recvs, opts, cb) {\n  var self = this;\n  if(self.isSelectable) {\n    if(typeof self.selector === 'function') {\n      self.selector(reqId, route, msg, recvs, opts, function(id) {\n        if(self.scheduler[id] && typeof self.scheduler[id].schedule === 'function') {\n          self.scheduler[id].schedule(reqId, route, msg, recvs, opts, cb);\n        } else {\n          logger.error('invalid pushScheduler id, id: %j', id);\n        }\n      });\n    } else {\n      logger.error('the selector for pushScheduler is not a function, selector: %j', self.selector);\n    }\n  } else {\n    if (typeof self.scheduler.schedule === 'function') {\n      self.scheduler.schedule(reqId, route, msg, recvs, opts, cb);\n    } else {\n      logger.error('the scheduler does not have a schedule function, scheduler: %j', self.scheduler);\n    }\n  }\n};\n\nvar getScheduler = function(pushSchedulerComp, app, opts) {\n  var scheduler = opts.scheduler || DefaultScheduler;\n  if(typeof scheduler === 'function') {\n    return scheduler(app, opts);\n  }\n\n  if(Array.isArray(scheduler)) {\n    var res = {};\n    scheduler.forEach(function(sch) {\n      if(typeof sch.scheduler === 'function') {\n        res[sch.id] = sch.scheduler(app, sch.options);\n      } else {\n        res[sch.id] = sch.scheduler;\n      }\n    });\n    pushSchedulerComp.isSelectable = true;\n    pushSchedulerComp.selector = opts.selector;\n    return res; \n  }\n\n  return scheduler;\n};\n"
  },
  {
    "path": "lib/components/remote.js",
    "content": "/**\n * Component for remote service.\n * Load remote service and add to global context.\n */\nvar fs = require('fs');\nvar pathUtil = require('../util/pathUtil');\nvar RemoteServer = require('pomelo-rpc').server;\n\n/**\n * Remote component factory function\n *\n * @param {Object} app  current application context\n * @param {Object} opts construct parameters\n *                       opts.acceptorFactory {Object}: acceptorFactory.create(opts, cb)\n * @return {Object}     remote component instances\n */\nmodule.exports = function(app, opts) {\n  opts = opts || {};\n\n  // cacheMsg is deprecated, just for compatibility here.\n  opts.bufferMsg = opts.bufferMsg || opts.cacheMsg || false;\n  opts.interval = opts.interval || 30;\n  if(app.enabled('rpcDebugLog')) {\n    opts.rpcDebugLog = true;\n    opts.rpcLogger = require('pomelo-logger').getLogger('rpc-debug', __filename);\n  }\n  return new Component(app, opts);\n};\n\n/**\n * Remote component class\n *\n * @param {Object} app  current application context\n * @param {Object} opts construct parameters\n */\nvar Component = function(app, opts) {\n  this.app = app;\n  this.opts = opts;\n};\n\nvar pro = Component.prototype;\n\npro.name = '__remote__';\n\n/**\n * Remote component lifecycle function\n *\n * @param {Function} cb\n * @return {Void}\n */\npro.start = function(cb) {\n  this.opts.port = this.app.getCurServer().port;\n  this.remote = genRemote(this.app, this.opts);\n  this.remote.start();\n  process.nextTick(cb);\n};\n\n/**\n * Remote component lifecycle function\n *\n * @param {Boolean}  force whether stop the component immediately\n * @param {Function}  cb\n * @return {Void}\n */\npro.stop = function(force, cb) {\n  this.remote.stop(force);\n  process.nextTick(cb);\n};\n\n/**\n * Get remote paths from application\n *\n * @param {Object} app current application context\n * @return {Array} paths\n *\n */\nvar getRemotePaths = function(app) {\n  var paths = [];\n\n  var role;\n  // master server should not come here\n  if(app.isFrontend()) {\n    role = 'frontend';\n  } else {\n    role = 'backend';\n  }\n\n  var sysPath = pathUtil.getSysRemotePath(role), serverType = app.getServerType();\n  if(fs.existsSync(sysPath)) {\n    paths.push(pathUtil.remotePathRecord('sys', serverType, sysPath));\n  }\n  var userPath = pathUtil.getUserRemotePath(app.getBase(), serverType);\n  if(fs.existsSync(userPath)) {\n    paths.push(pathUtil.remotePathRecord('user', serverType, userPath));\n  }\n\n  return paths;\n};\n\n/**\n * Generate remote server instance\n *\n * @param {Object} app current application context\n * @param {Object} opts contructor parameters for rpc Server\n * @return {Object} remote server instance\n */\nvar genRemote = function(app, opts) {\n  opts.paths = getRemotePaths(app);\n  opts.context = app;\n  if(!!opts.rpcServer) {\n    return opts.rpcServer.create(opts);\n  } else {\n    return RemoteServer.create(opts);\n  }\n};\n"
  },
  {
    "path": "lib/components/server.js",
    "content": "/**\n * Component for server starup.\n */\nvar Server = require('../server/server');\n\n/**\n * Component factory function\n *\n * @param {Object} app  current application context\n * @return {Object}     component instance\n */\nmodule.exports = function(app, opts) {\n\treturn new Component(app, opts);\n};\n\n/**\n * Server component class\n *\n * @param {Object} app  current application context\n */\nvar Component = function(app, opts) {\n\tthis.server = Server.create(app, opts);\n};\n\nvar pro = Component.prototype;\n\npro.name = '__server__';\n\n/**\n * Component lifecycle callback\n *\n * @param {Function} cb\n * @return {Void}\n */\npro.start = function(cb) {\n\tthis.server.start();\n\tprocess.nextTick(cb);\n};\n\n/**\n * Component lifecycle callback\n *\n * @param {Function} cb\n * @return {Void}\n */\npro.afterStart = function(cb) {\n\tthis.server.afterStart();\n\tprocess.nextTick(cb);\n};\n\n/**\n * Component lifecycle function\n *\n * @param {Boolean}  force whether stop the component immediately\n * @param {Function}  cb\n * @return {Void}\n */\npro.stop = function(force, cb) {\n\tthis.server.stop();\n\tprocess.nextTick(cb);\n};\n\n/**\n * Proxy server handle\n */\npro.handle = function(msg, session, cb) {\n\tthis.server.handle(msg, session, cb);\n};\n\n/**\n * Proxy server global handle\n */\npro.globalHandle = function(msg, session, cb) {\n\tthis.server.globalHandle(msg, session, cb);\n};"
  },
  {
    "path": "lib/components/session.js",
    "content": "var SessionService = require('../common/service/sessionService');\n\nmodule.exports = function(app, opts) {\n  var cmp = new Component(app, opts);\n  app.set('sessionService', cmp, true);\n  return cmp;\n};\n\n/**\n * Session component. Manage sessions.\n *\n * @param {Object} app  current application context\n * @param {Object} opts attach parameters\n */\nvar Component = function(app, opts) {\n  opts = opts || {};\n  this.app = app;\n  this.service = new SessionService(opts);\n\n  var getFun = function(m) {\n    return (function() {\n          return function() {\n            return self.service[m].apply(self.service, arguments);\n          };\n    })();\n  };\n  // proxy the service methods except the lifecycle interfaces of component\n  var method, self = this;\n  for(var m in this.service) {\n    if(m !== 'start' && m !== 'stop') {\n      method = this.service[m];\n      if(typeof method === 'function') {\n        this[m] = getFun(m);\n      }\n    }\n  }\n};\n\nComponent.prototype.name = '__session__';\n"
  },
  {
    "path": "lib/connectors/commands/handshake.js",
    "content": "var pomelo = require('../../pomelo');\nvar Package = require('pomelo-protocol').Package;\n\nvar CODE_OK = 200;\nvar CODE_USE_ERROR = 500;\nvar CODE_OLD_CLIENT = 501;\n\n/**\n * Process the handshake request.\n *\n * @param {Object} opts option parameters\n *                      opts.handshake(msg, cb(err, resp)) handshake callback. msg is the handshake message from client.\n *                      opts.hearbeat heartbeat interval (level?)\n *                      opts.version required client level\n */\nvar Command = function(opts) {\n  opts = opts || {};\n  this.userHandshake = opts.handshake;\n\n  if(opts.heartbeat) {\n    this.heartbeatSec = opts.heartbeat;\n    this.heartbeat = opts.heartbeat * 1000;\n  }\n\n  this.checkClient = opts.checkClient;\n\n  this.useDict = opts.useDict;\n  this.useProtobuf = opts.useProtobuf;\n  this.useCrypto = opts.useCrypto;\n};\n\nmodule.exports = Command;\n\nCommand.prototype.handle = function(socket, msg) {\n  if(!msg.sys) {\n    processError(socket, CODE_USE_ERROR);\n    return;\n  }\n\n  if(typeof this.checkClient === 'function') {\n    if(!msg || !msg.sys || !this.checkClient(msg.sys.type, msg.sys.version)) {\n      processError(socket, CODE_OLD_CLIENT);\n      return;\n    }\n  }\n\n  var opts = {\n    heartbeat : setupHeartbeat(this)\n  };\n\n  if(this.useDict) {\n    var dictVersion = pomelo.app.components.__dictionary__.getVersion();\n    if(!msg.sys.dictVersion || msg.sys.dictVersion !== dictVersion){\n\n      // may be deprecated in future\n      opts.dict = pomelo.app.components.__dictionary__.getDict();\n\n      opts.routeToCode = pomelo.app.components.__dictionary__.getDict();\n      opts.codeToRoute = pomelo.app.components.__dictionary__.getAbbrs();\n      opts.dictVersion = dictVersion; \n    }\n    opts.useDict = true;\n  }\n\n  if(this.useProtobuf) {\n    var protoVersion = pomelo.app.components.__protobuf__.getVersion();\n    if(!msg.sys.protoVersion || msg.sys.protoVersion !== protoVersion){\n      opts.protos = pomelo.app.components.__protobuf__.getProtos();\n    }\n    opts.useProto = true;\n  }\n\n  if(!!pomelo.app.components.__decodeIO__protobuf__) {\n    if(!!this.useProtobuf) {\n      throw new Error('protobuf can not be both used in the same project.');\n    }\n    var version = pomelo.app.components.__decodeIO__protobuf__.getVersion();\n    if(!msg.sys.protoVersion || msg.sys.protoVersion < version) {\n      opts.protos = pomelo.app.components.__decodeIO__protobuf__.getProtos();\n    }\n    opts.useProto = true;\n  }\n\n  if(this.useCrypto) {\n    pomelo.app.components.__connector__.setPubKey(socket.id, msg.sys.rsa);\n  }\n\n  if(typeof this.userHandshake === 'function') {\n    this.userHandshake(msg, function(err, resp) {\n      if(err) {\n        process.nextTick(function() {\n          processError(socket, CODE_USE_ERROR);\n        });\n        return;\n      }\n      process.nextTick(function() {\n        response(socket, opts, resp);\n      });\n    }, socket);\n    return;\n  }\n\n  process.nextTick(function() {\n    response(socket, opts);\n  });\n};\n\nvar setupHeartbeat = function(self) {\n  return self.heartbeatSec;\n};\n\nvar response = function(socket, sys, resp) {\n  var res = {\n    code: CODE_OK,\n    sys: sys\n  };\n  if(resp) {\n    res.user = resp;\n  }\n  socket.handshakeResponse(Package.encode(Package.TYPE_HANDSHAKE, new Buffer(JSON.stringify(res))));\n};\n\nvar processError = function(socket, code) {\n  var res = {\n    code: code\n  };\n  socket.sendForce(Package.encode(Package.TYPE_HANDSHAKE, new Buffer(JSON.stringify(res))));\n  process.nextTick(function() {\n    socket.disconnect();\n  });\n};\n"
  },
  {
    "path": "lib/connectors/commands/heartbeat.js",
    "content": "var Package = require('pomelo-protocol').Package;\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\n/**\n * Process heartbeat request.\n *\n * @param {Object} opts option request\n *                      opts.heartbeat heartbeat interval\n */\nvar Command = function(opts) {\n  opts = opts || {};\n  this.heartbeat = null;\n  this.timeout = null;\n  this.disconnectOnTimeout = opts.disconnectOnTimeout;\n\n  if(opts.heartbeat) {\n    this.heartbeat = opts.heartbeat * 1000; // heartbeat interval\n    this.timeout = opts.timeout * 1000 || this.heartbeat * 2;      // max heartbeat message timeout\n    this.disconnectOnTimeout = true;\n  }\n\n  this.timeouts = {};\n  this.clients = {};\n};\n\nmodule.exports = Command;\n\nCommand.prototype.handle = function(socket) {\n  if(!this.heartbeat) {\n    // no heartbeat setting\n    return;\n  }\n\n  var self = this;\n\n  if(!this.clients[socket.id]) {\n    // clear timers when socket disconnect or error\n    this.clients[socket.id] = 1;\n    socket.once('disconnect', clearTimers.bind(null, this, socket.id));\n    socket.once('error', clearTimers.bind(null, this, socket.id));\n  }\n\n  // clear timeout timer\n  if(self.disconnectOnTimeout) {\n    this.clear(socket.id);\n  }\n\n  socket.sendRaw(Package.encode(Package.TYPE_HEARTBEAT));\n\n  if(self.disconnectOnTimeout) {\n    self.timeouts[socket.id] = setTimeout(function() {\n      logger.info('client %j heartbeat timeout.', socket.id);\n      socket.disconnect();\n    }, self.timeout);\n  }\n};\n\nCommand.prototype.clear = function(id) {\n  var tid = this.timeouts[id];\n  if(tid) {\n    clearTimeout(tid);\n    delete this.timeouts[id];\n  }\n};\n\nvar clearTimers = function(self, id) {\n  delete self.clients[id];\n  var tid = self.timeouts[id];\n  if(tid) {\n    clearTimeout(tid);\n    delete self.timeouts[id];\n  }\n};\n"
  },
  {
    "path": "lib/connectors/commands/kick.js",
    "content": "var Package = require('pomelo-protocol').Package;\n\nmodule.exports.handle = function(socket, reason) {\n// websocket close code 1000 would emit when client close the connection\n  if(typeof reason === 'string') {\n    var res = {\n      reason: reason\n    };\n    socket.sendRaw(Package.encode(Package.TYPE_KICK, new Buffer(JSON.stringify(res))));\n  }\n};\n"
  },
  {
    "path": "lib/connectors/common/coder.js",
    "content": "var Message = require('pomelo-protocol').Message;\nvar Constants = require('../../util/constants');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\nvar encode = function(reqId, route, msg) {\n  if(!!reqId) {\n    return composeResponse(this, reqId, route, msg);\n  } else {\n    return composePush(this, route, msg);\n  }\n};\n\nvar decode = function(msg) {\n  msg = Message.decode(msg.body);\n  var route = msg.route;\n\n  // decode use dictionary\n  if(!!msg.compressRoute) {\n    if(!!this.connector.useDict) {\n      var abbrs = this.dictionary.getAbbrs();\n      if(!abbrs[route]) {\n        logger.error('dictionary error! no abbrs for route : %s', route);\n        return null;\n      }\n      route = msg.route = abbrs[route];\n    } else {\n      logger.error('fail to uncompress route code for msg: %j, server not enable dictionary.', msg);\n      return null;\n    }\n  }\n\n  // decode use protobuf\n  if(!!this.protobuf && !!this.protobuf.getProtos().client[route]) {\n    msg.body = this.protobuf.decode(route, msg.body);\n  } else if(!!this.decodeIO_protobuf && !!this.decodeIO_protobuf.check(Constants.RESERVED.CLIENT, route)) {\n    msg.body = this.decodeIO_protobuf.decode(route, msg.body);\n  } else {\n    try {\n      msg.body = JSON.parse(msg.body.toString('utf8'));\n    } catch (ex) {\n      msg.body = {};\n    }\n  }\n\n  return msg;\n};\n\nvar composeResponse = function(server, msgId, route, msgBody) {\n  if(!msgId || !route || !msgBody) {\n    return null;\n  }\n  msgBody = encodeBody(server, route, msgBody);\n  return Message.encode(msgId, Message.TYPE_RESPONSE, 0, null, msgBody);\n};\n\nvar composePush = function(server, route, msgBody) {\n  if(!route || !msgBody){\n    return null;\n  }\n  msgBody = encodeBody(server, route, msgBody);\n  // encode use dictionary\n  var compressRoute = 0;\n  if(!!server.dictionary) {\n    var dict = server.dictionary.getDict();\n    if(!!server.connector.useDict && !!dict[route]) {\n      route = dict[route];\n      compressRoute = 1;\n    }\n  }\n  return Message.encode(0, Message.TYPE_PUSH, compressRoute, route, msgBody);\n};\n\nvar encodeBody = function(server, route, msgBody) {\n    // encode use protobuf\n  if(!!server.protobuf && !!server.protobuf.getProtos().server[route]) {\n    msgBody = server.protobuf.encode(route, msgBody);\n  } else if(!!server.decodeIO_protobuf && !!server.decodeIO_protobuf.check(Constants.RESERVED.SERVER, route)) {\n     msgBody = server.decodeIO_protobuf.encode(route, msgBody);\n  } else {\n    msgBody = new Buffer(JSON.stringify(msgBody), 'utf8');\n  }\n  return msgBody;\n};\n\nmodule.exports = {\n  encode: encode,\n  decode: decode\n};"
  },
  {
    "path": "lib/connectors/common/handler.js",
    "content": "var protocol = require('pomelo-protocol');\nvar Package = protocol.Package;\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\nvar handlers = {};\n\nvar ST_INITED = 0;\nvar ST_WAIT_ACK = 1;\nvar ST_WORKING = 2;\nvar ST_CLOSED = 3;\n\nvar handleHandshake = function(socket, pkg) {\n  if(socket.state !== ST_INITED) {\n    return;\n  }\n  try {\n    socket.emit('handshake', JSON.parse(protocol.strdecode(pkg.body)));\n  } catch (ex) {\n    socket.emit('handshake', {});\n  }\n};\n\nvar handleHandshakeAck = function(socket, pkg) {\n  if(socket.state !== ST_WAIT_ACK) {\n    return;\n  }\n  socket.state = ST_WORKING;\n  socket.emit('heartbeat');\n};\n\nvar handleHeartbeat = function(socket, pkg) {\n  if(socket.state !== ST_WORKING) {\n    return;\n  }\n  socket.emit('heartbeat');\n};\n\nvar handleData = function(socket, pkg) {\n  if(socket.state !== ST_WORKING) {\n    return;\n  }\n  socket.emit('message', pkg);\n};\n\nhandlers[Package.TYPE_HANDSHAKE] = handleHandshake;\nhandlers[Package.TYPE_HANDSHAKE_ACK] = handleHandshakeAck;\nhandlers[Package.TYPE_HEARTBEAT] = handleHeartbeat;\nhandlers[Package.TYPE_DATA] = handleData;\n\nvar handle = function(socket, pkg) {\n  var handler = handlers[pkg.type];\n  if(!!handler) {\n    handler(socket, pkg);\n  } else {\n    logger.error('could not find handle invalid data package.');\n    socket.disconnect();\n  }\n};\n\nmodule.exports = handle;\n"
  },
  {
    "path": "lib/connectors/hybrid/switcher.js",
    "content": "var EventEmitter = require('events').EventEmitter;\nvar util = require('util');\nvar WSProcessor = require('./wsprocessor');\nvar TCPProcessor = require('./tcpprocessor');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\nvar HTTP_METHODS = [\n  'GET', 'POST', 'DELETE', 'PUT', 'HEAD'\n];\n\nvar ST_STARTED = 1;\nvar ST_CLOSED = 2;\n\nvar DEFAULT_TIMEOUT = 90;\n\n/**\n * Switcher for tcp and websocket protocol\n *\n * @param {Object} server tcp server instance from node.js net module\n */\nvar Switcher = function(server, opts) {\n  EventEmitter.call(this);\n  this.server = server;\n  this.wsprocessor = new WSProcessor();\n  this.tcpprocessor = new TCPProcessor(opts.closeMethod);\n  this.id = 1;\n  this.timeout = (opts.timeout || DEFAULT_TIMEOUT) * 1000;\n  this.setNoDelay = opts.setNoDelay;\n\n  if (!opts.ssl) {\n    this.server.on('connection', this.newSocket.bind(this));\n  } else {\n    this.server.on('secureConnection', this.newSocket.bind(this));\n    this.server.on('clientError', function(e, tlsSo) {\n      logger.warn('an ssl error occured before handshake established: ', e);\n      tlsSo.destroy();\n    });\n  }\n\n  this.wsprocessor.on('connection', this.emit.bind(this, 'connection'));\n  this.tcpprocessor.on('connection', this.emit.bind(this, 'connection'));\n\n  this.state = ST_STARTED;\n};\nutil.inherits(Switcher, EventEmitter);\n\nmodule.exports = Switcher;\n\nSwitcher.prototype.newSocket = function(socket) {\n  if(this.state !== ST_STARTED) {\n    return;\n  }\n\n  socket.setTimeout(this.timeout, function() {\n     logger.warn('connection is timeout without communication, the remote ip is %s && port is %s',\n       socket.remoteAddress, socket.remotePort);\n     socket.destroy();\n  });\n\n  var self = this;\n\n  socket.once('data', function(data) {\n    // FIXME: handle incomplete HTTP method\n    if(isHttp(data)) {\n      processHttp(self, self.wsprocessor, socket, data);\n    } else {\n      if(!!self.setNoDelay) {\n        socket.setNoDelay(true);\n      }\n      processTcp(self, self.tcpprocessor, socket, data);\n    }\n  });\n};\n\nSwitcher.prototype.close = function() {\n  if(this.state !== ST_STARTED) {\n    return;\n  }\n\n  this.state = ST_CLOSED;\n  this.wsprocessor.close();\n  this.tcpprocessor.close();\n};\n\nvar isHttp = function(data) {\n  var head = data.toString('utf8', 0, 4);\n\n  for(var i=0, l=HTTP_METHODS.length; i<l; i++) {\n    if(head.indexOf(HTTP_METHODS[i]) === 0) {\n      return true;\n    }\n  }\n\n  return false;\n};\n\nvar processHttp = function(switcher, processor, socket, data) {\n  processor.add(socket, data);\n};\n\nvar processTcp = function(switcher, processor, socket, data) {\n  processor.add(socket, data);\n};\n"
  },
  {
    "path": "lib/connectors/hybrid/tcpprocessor.js",
    "content": "var EventEmitter = require('events').EventEmitter;\nvar util = require('util');\nvar utils = require('../../util/utils');\nvar TcpSocket = require('./tcpsocket');\n\nvar ST_STARTED = 1;\nvar ST_CLOSED = 2;\n\n// private protocol, no need exports\nvar HEAD_SIZE = 4;\n\n/**\n * websocket protocol processor\n */\nvar Processor = function(closeMethod) {\n  EventEmitter.call(this);\n  this.closeMethod = closeMethod;\n  this.state = ST_STARTED;\n};\nutil.inherits(Processor, EventEmitter);\n\nmodule.exports = Processor;\n\nProcessor.prototype.add = function(socket, data) {\n  if(this.state !== ST_STARTED) {\n    return;\n  }\n  var tcpsocket = new TcpSocket(socket, {headSize: HEAD_SIZE,\n                                         headHandler: utils.headHandler,\n                                         closeMethod: this.closeMethod});\n  this.emit('connection', tcpsocket);\n  socket.emit('data', data);\n};\n\nProcessor.prototype.close = function() {\n  if(this.state !== ST_STARTED) {\n    return;\n  }\n  this.state = ST_CLOSED;\n};"
  },
  {
    "path": "lib/connectors/hybrid/tcpsocket.js",
    "content": "var Stream = require('stream');\nvar util = require('util');\nvar protocol = require('pomelo-protocol');\nvar Package = protocol.Package;\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\n/**\n * Work states\n */\nvar ST_HEAD = 1;      // wait for head\nvar ST_BODY = 2;      // wait for body\nvar ST_CLOSED = 3;    // closed\n\n/**\n * Tcp socket wrapper with package compositing.\n * Collect the package from socket and emit a completed package with 'data' event.\n * Uniform with ws.WebSocket interfaces.\n *\n * @param {Object} socket origin socket from node.js net module\n * @param {Object} opts   options parameter.\n *                        opts.headSize size of package head\n *                        opts.headHandler(headBuffer) handler for package head. caculate and return body size from head data.\n */\nvar Socket = function(socket, opts) {\n  if(!(this instanceof Socket)) {\n    return new Socket(socket, opts);\n  }\n\n  if(!socket || !opts) {\n    throw new Error('invalid socket or opts');\n  }\n\n  if(!opts.headSize || typeof opts.headHandler !== 'function') {\n    throw new Error('invalid opts.headSize or opts.headHandler');\n  }\n\n  // stream style interfaces.\n  // TODO: need to port to stream2 after node 0.9\n  Stream.call(this);\n  this.readable = true;\n  this.writeable = true;\n\n  this._socket = socket;\n  this.headSize = opts.headSize;\n  this.closeMethod = opts.closeMethod;\n  this.headBuffer = new Buffer(opts.headSize);\n  this.headHandler = opts.headHandler;\n\n  this.headOffset = 0;\n  this.packageOffset = 0;\n  this.packageSize = 0;\n  this.packageBuffer = null;\n\n  // bind event form the origin socket\n  this._socket.on('data', ondata.bind(null, this));\n  this._socket.on('end', onend.bind(null, this));\n  this._socket.on('error', this.emit.bind(this, 'error'));\n  this._socket.on('close', this.emit.bind(this, 'close'));\n\n  this.state = ST_HEAD;\n};\n\nutil.inherits(Socket, Stream);\n\nmodule.exports = Socket;\n\nSocket.prototype.send = function(msg, encode, cb) {\n  this._socket.write(msg, encode, cb);\n};\n\nSocket.prototype.close = function() {\n  if(!!this.closeMethod && this.closeMethod === 'end') {\n    this._socket.end();\n  } else {\n    try { \n      this._socket.destroy(); \n    } catch (e) {\n      logger.error('socket close with destroy error: %j', e.stack);\n    }\n  }\n};\n\nvar ondata = function(socket, chunk) {\n  if(socket.state === ST_CLOSED) {\n    throw new Error('socket has closed');\n  }\n\n  if(typeof chunk !== 'string' && !Buffer.isBuffer(chunk)) {\n    throw new Error('invalid data');\n  }\n\n  if(typeof chunk === 'string') {\n    chunk = new Buffer(chunk, 'utf8');\n  }\n\n  var offset = 0, end = chunk.length;\n\n  while(offset < end && socket.state !== ST_CLOSED) {\n    if(socket.state === ST_HEAD) {\n      offset = readHead(socket, chunk, offset);\n    }\n\n    if(socket.state === ST_BODY) {\n      offset = readBody(socket, chunk, offset);\n    }\n  }\n\n  return true;\n};\n\nvar onend = function(socket, chunk) {\n  if(chunk) {\n    socket._socket.write(chunk);\n  }\n\n  socket.state = ST_CLOSED;\n  reset(socket);\n  socket.emit('end');\n};\n\n/**\n * Read head segment from data to socket.headBuffer.\n *\n * @param  {Object} socket Socket instance\n * @param  {Object} data   Buffer instance\n * @param  {Number} offset offset read star from data\n * @return {Number}        new offset of data after read\n */\nvar readHead = function(socket, data, offset) {\n  var hlen = socket.headSize - socket.headOffset;\n  var dlen = data.length - offset;\n  var len = Math.min(hlen, dlen);\n  var dend = offset + len;\n\n  data.copy(socket.headBuffer, socket.headOffset, offset, dend);\n  socket.headOffset += len;\n\n  if(socket.headOffset === socket.headSize) {\n    // if head segment finished\n    var size = socket.headHandler(socket.headBuffer);\n    if(size < 0) {\n      throw new Error('invalid body size: ' + size);\n    }\n    // check if header contains a valid type\n    if(checkTypeData(socket.headBuffer[0])) {\n      socket.packageSize = size + socket.headSize;\n      socket.packageBuffer = new Buffer(socket.packageSize);\n      socket.headBuffer.copy(socket.packageBuffer, 0, 0, socket.headSize);\n      socket.packageOffset = socket.headSize;\n      socket.state = ST_BODY;\n    } else {\n      dend = data.length;\n      logger.error('close the connection with invalid head message, the remote ip is %s && port is %s && message is %j', socket._socket.remoteAddress, socket._socket.remotePort, data);\n      socket.close();\n    }\n\n  }\n\n  return dend;\n};\n\n/**\n * Read body segment from data buffer to socket.packageBuffer;\n *\n * @param  {Object} socket Socket instance\n * @param  {Object} data   Buffer instance\n * @param  {Number} offset offset read star from data\n * @return {Number}        new offset of data after read\n */\nvar readBody = function(socket, data, offset) {\n  var blen = socket.packageSize - socket.packageOffset;\n  var dlen = data.length - offset;\n  var len = Math.min(blen, dlen);\n  var dend = offset + len;\n\n  data.copy(socket.packageBuffer, socket.packageOffset, offset, dend);\n\n  socket.packageOffset += len;\n\n  if(socket.packageOffset === socket.packageSize) {\n    // if all the package finished\n    var buffer = socket.packageBuffer;\n    socket.emit('message', buffer);\n    reset(socket);\n  }\n\n  return dend;\n};\n\nvar reset = function(socket) {\n  socket.headOffset = 0;\n  socket.packageOffset = 0;\n  socket.packageSize = 0;\n  socket.packageBuffer = null;\n  socket.state = ST_HEAD;\n};\n\nvar checkTypeData = function(data) {\n  return data === Package.TYPE_HANDSHAKE || data === Package.TYPE_HANDSHAKE_ACK || data === Package.TYPE_HEARTBEAT || data === Package.TYPE_DATA || data === Package.TYPE_KICK;\n};\n"
  },
  {
    "path": "lib/connectors/hybrid/wsprocessor.js",
    "content": "var HttpServer = require('http').Server;\nvar EventEmitter = require('events').EventEmitter;\nvar util = require('util');\nvar WebSocketServer = require('ws').Server;\n\nvar ST_STARTED = 1;\nvar ST_CLOSED = 2;\n\n/**\n * websocket protocol processor\n */\nvar Processor = function() {\n  EventEmitter.call(this);\n  this.httpServer = new HttpServer();\n\n  var self = this;\n  this.wsServer = new WebSocketServer({server: this.httpServer});\n\n  this.wsServer.on('connection', function(socket) {\n    // emit socket to outside\n    self.emit('connection', socket);\n  });\n\n  this.state = ST_STARTED;\n};\nutil.inherits(Processor, EventEmitter);\n\nmodule.exports = Processor;\n\nProcessor.prototype.add = function(socket, data) {\n  if(this.state !== ST_STARTED) {\n    return;\n  }\n  this.httpServer.emit('connection', socket);\n  if(typeof socket.ondata === 'function') {\n    // compatible with stream2\n    socket.ondata(data, 0, data.length);\n  } else {\n    // compatible with old stream\n    socket.emit('data', data);\n  }\n};\n\nProcessor.prototype.close = function() {\n  if(this.state !== ST_STARTED) {\n    return;\n  }\n  this.state = ST_CLOSED;\n  this.wsServer.close();\n  this.wsServer = null;\n  this.httpServer = null;\n};\n"
  },
  {
    "path": "lib/connectors/hybridconnector.js",
    "content": "var net = require('net');\nvar tls = require('tls');\nvar util = require('util');\nvar EventEmitter = require('events').EventEmitter;\n\nvar HybridSocket = require('./hybridsocket');\nvar Switcher = require('./hybrid/switcher');\nvar Handshake = require('./commands/handshake');\nvar Heartbeat = require('./commands/heartbeat');\nvar Kick = require('./commands/kick');\nvar coder = require('./common/coder');\n\nvar curId = 1;\n\n/**\n * Connector that manager low level connection and protocol bewteen server and client.\n * Develper can provide their own connector to switch the low level prototol, such as tcp or probuf.\n */\nvar Connector = function(port, host, opts) {\n  if (!(this instanceof Connector)) {\n    return new Connector(port, host, opts);\n  }\n\n  EventEmitter.call(this);\n\n  this.opts = opts || {};\n  this.port = port;\n  this.host = host;\n  this.useDict = opts.useDict;\n  this.useProtobuf = opts.useProtobuf;\n  this.handshake = new Handshake(opts);\n  this.heartbeat = new Heartbeat(opts);\n  this.distinctHost = opts.distinctHost;\n  this.ssl = opts.ssl;\n\n  this.switcher = null;\n};\n\nutil.inherits(Connector, EventEmitter);\n\nmodule.exports = Connector;\n\n/**\n * Start connector to listen the specified port\n */\nConnector.prototype.start = function(cb) {\n  var app = require('../pomelo').app;\n  var self = this;\n\n  var gensocket = function(socket) {\n    var hybridsocket = new HybridSocket(curId++, socket);\n    hybridsocket.on('handshake', self.handshake.handle.bind(self.handshake, hybridsocket));\n    hybridsocket.on('heartbeat', self.heartbeat.handle.bind(self.heartbeat, hybridsocket));\n    hybridsocket.on('disconnect', self.heartbeat.clear.bind(self.heartbeat, hybridsocket.id));\n    hybridsocket.on('closing', Kick.handle.bind(null, hybridsocket));\n    self.emit('connection', hybridsocket);\n  };\n\n  this.connector = app.components.__connector__.connector;\n  this.dictionary = app.components.__dictionary__;\n  this.protobuf = app.components.__protobuf__;\n  this.decodeIO_protobuf = app.components.__decodeIO__protobuf__;\n\n  if(!this.ssl) {\n    this.listeningServer = net.createServer();\n  } else {\n    this.listeningServer = tls.createServer(this.ssl);\n  }\n  this.switcher = new Switcher(this.listeningServer, self.opts);\n\n  this.switcher.on('connection', function(socket) {\n    gensocket(socket);\n  });\n\n  if(!!this.distinctHost) {\n    this.listeningServer.listen(this.port, this.host);\n  } else {\n    this.listeningServer.listen(this.port);\n  }\n\n  process.nextTick(cb);\n};\n\nConnector.prototype.stop = function(force, cb) {\n  this.switcher.close();\n  this.listeningServer.close();\n\n  process.nextTick(cb);\n};\n\nConnector.decode = Connector.prototype.decode = coder.decode;\n\nConnector.encode = Connector.prototype.encode = coder.encode;\n"
  },
  {
    "path": "lib/connectors/hybridsocket.js",
    "content": "var util = require('util');\nvar EventEmitter = require('events').EventEmitter;\nvar handler = require('./common/handler');\nvar protocol = require('pomelo-protocol');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar Package = protocol.Package;\n\nvar ST_INITED = 0;\nvar ST_WAIT_ACK = 1;\nvar ST_WORKING = 2;\nvar ST_CLOSED = 3;\n\n/**\n * Socket class that wraps socket and websocket to provide unified interface for up level.\n */\nvar Socket = function(id, socket) {\n  EventEmitter.call(this);\n  this.id = id;\n  this.socket = socket;\n\n  if(!socket._socket) {\n    this.remoteAddress = {\n      ip: socket.address().address,\n      port: socket.address().port\n    };\n  } else {\n    this.remoteAddress = {\n      ip: socket._socket.remoteAddress,\n      port: socket._socket.remotePort\n    };\n  }\n\n  var self = this;\n\n  socket.once('close', this.emit.bind(this, 'disconnect'));\n  socket.on('error', this.emit.bind(this, 'error'));\n\n  socket.on('message', function(msg) {\n    if(msg) {\n      msg = Package.decode(msg);\n      handler(self, msg);\n    }\n  });\n\n  this.state = ST_INITED;\n\n  // TODO: any other events?\n};\n\nutil.inherits(Socket, EventEmitter);\n\nmodule.exports = Socket;\n\n/**\n * Send raw byte data.\n *\n * @api private\n */\nSocket.prototype.sendRaw = function(msg) {\n  if(this.state !== ST_WORKING) {\n    return;\n  }\n  var self = this;\n\n  this.socket.send(msg, {binary: true}, function(err) {\n    if(!!err) {\n      logger.error('websocket send binary data failed: %j', err.stack);\n      return;\n    }\n  });\n};\n\n/**\n * Send byte data package to client.\n *\n * @param  {Buffer} msg byte data\n */\nSocket.prototype.send = function(msg) {\n  if(msg instanceof String) {\n    msg = new Buffer(msg);\n  } else if(!(msg instanceof Buffer)) {\n    msg = new Buffer(JSON.stringify(msg));\n  }\n  this.sendRaw(Package.encode(Package.TYPE_DATA, msg));\n};\n\n/**\n * Send byte data packages to client in batch.\n *\n * @param  {Buffer} msgs byte data\n */\nSocket.prototype.sendBatch = function(msgs) {\n  var rs = [];\n  for(var i=0; i<msgs.length; i++) {\n    var src = Package.encode(Package.TYPE_DATA, msgs[i]);\n    rs.push(src);\n  }\n  this.sendRaw(Buffer.concat(rs));\n};\n\n/**\n * Send message to client no matter whether handshake.\n *\n * @api private\n */\nSocket.prototype.sendForce = function(msg) {\n  if(this.state === ST_CLOSED) {\n    return;\n  }\n  this.socket.send(msg, {binary: true});\n};\n\n/**\n * Response handshake request\n *\n * @api private\n */\nSocket.prototype.handshakeResponse = function(resp) {\n  if(this.state !== ST_INITED) {\n    return;\n  }\n\n  this.socket.send(resp, {binary: true});\n  this.state = ST_WAIT_ACK;\n};\n\n/**\n * Close the connection.\n *\n * @api private\n */\nSocket.prototype.disconnect = function() {\n  if(this.state === ST_CLOSED) {\n    return;\n  }\n\n  this.state = ST_CLOSED;\n  this.socket.emit('close');\n  this.socket.close();\n};"
  },
  {
    "path": "lib/connectors/mqtt/generate.js",
    "content": "var protocol = require('./protocol');\nvar crypto = require('crypto');\n\n/* TODO: consider rewriting these functions using buffers instead\n * of arrays\n */\n\n/* Publish */\nmodule.exports.publish = function(opts) {\n  opts = opts || {};\n  var dup = opts.dup ? protocol.DUP_MASK : 0;\n  var qos = opts.qos || 0;\n  var retain = opts.retain ? protocol.RETAIN_MASK : 0;\n  var topic = opts.topic;\n  var payload = opts.payload || new Buffer(0);\n  var id = (typeof opts.messageId === 'undefined') ? randint() : opts.messageId;\n  var packet = {header: 0, payload: []};\n\n  /* Check required fields */\n  if (typeof topic !== 'string' || topic.length <= 0) return null;\n  /* if payload is a string, we'll convert it into a buffer */\n  if(typeof payload == 'string') {\n    payload = new Buffer(payload);\n  }\n  /* accepting only a buffer for payload */\n  if (!Buffer.isBuffer(payload)) return null;\n  if (typeof qos !== 'number' || qos < 0 || qos > 2) return null;\n  if (typeof id !== 'number' || id < 0 || id > 0xFFFF) return null;\n\n  /* Generate header */\n  packet.header = protocol.codes.publish << protocol.CMD_SHIFT | dup | qos << protocol.QOS_SHIFT | retain;\n\n  /* Topic name */\n  packet.payload = packet.payload.concat(gen_string(topic));\n\n  /* Message ID */\n  if (qos > 0) packet.payload = packet.payload.concat(gen_number(id));\n\n\n  var buf = new Buffer([packet.header]\n      .concat(gen_length(packet.payload.length + payload.length))\n      .concat(packet.payload));\n\n  return Buffer.concat([buf, payload]);\n};\n\n/* Requires length be a number > 0 */\nvar gen_length = function(length) {\n  if(typeof length !== \"number\") return null;\n  if(length < 0) return null;\n\n  var len = [];\n  var digit = 0;\n\n  do {\n    digit = length % 128 | 0;\n    length = length / 128 | 0;\n    if (length > 0) {\n        digit = digit | 0x80;\n    }\n    len.push(digit);\n  } while (length > 0);\n\n  return len;\n};\n\nvar gen_string = function(str, without_length) { /* based on code in (from http://farhadi.ir/downloads/utf8.js) */\n  if(arguments.length < 2) without_length = false;\n  if(typeof str !== \"string\") return null;\n  if(typeof without_length !== \"boolean\") return null;\n\n  var string = [];\n  var length = 0;\n  for(var i = 0; i < str.length; i++) {\n    var code = str.charCodeAt(i);\n    if (code < 128) {\n      string.push(code);                      ++length;\n\n    } else if (code < 2048) {\n      string.push(192 + ((code >> 6 )   )); ++length;\n      string.push(128 + ((code    ) & 63)); ++length;\n    } else if (code < 65536) {\n      string.push(224 + ((code >> 12)   )); ++length;\n      string.push(128 + ((code >> 6 ) & 63)); ++length;\n      string.push(128 + ((code    ) & 63)); ++length;\n    } else if (code < 2097152) {\n      string.push(240 + ((code >> 18)   )); ++length;\n      string.push(128 + ((code >> 12) & 63)); ++length;\n      string.push(128 + ((code >> 6 ) & 63)); ++length;\n      string.push(128 + ((code    ) & 63)); ++length;\n    } else {\n      throw new Error(\"Can't encode character with code \" + code);\n    }\n  }\n  return without_length ? string : gen_number(length).concat(string);\n};\n\nvar gen_number = function(num) {\n  var number = [num >> 8, num & 0x00FF];\n  return number;\n};\n\nvar randint = function() { return Math.floor(Math.random() * 0xFFFF); };"
  },
  {
    "path": "lib/connectors/mqtt/mqttadaptor.js",
    "content": "var Adaptor = function(opts) {\n  opts = opts || {};\n  this.subReqs = {};\n  this.publishRoute = opts.publishRoute;\n  this.subscribeRoute = opts.subscribeRoute;\n};\n\nmodule.exports = Adaptor;\n\nAdaptor.prototype.onPublish = function(client, packet) {\n  var route = this.publishRoute;\n\n  if(!route) {\n    throw new Error('unspecified publish route.');\n  }\n\n  var payload = packet.payload;\n  if(payload instanceof Buffer) {\n    payload = payload.toString('utf8');\n  }\n\n  var req = {\n    id: packet.messageId,\n    route: route,\n    body: packet\n  };\n\n  client.emit('message', req);\n\n  if(packet.qos === 1) {\n    client.socket.puback({messageId: packet.messageId});\n  }\n};\n\nAdaptor.prototype.onSubscribe = function(client, packet) {\n  var route = this.subscribeRoute;\n\n  if(!route) {\n    throw new Error('unspecified subscribe route.');\n  }\n\n  var req = {\n    id: packet.messageId,\n    route: route,\n    body: {\n      subscriptions: packet.subscriptions\n    }\n  };\n\n  this.subReqs[packet.messageId] = packet;\n\n  client.emit('message', req);\n};\n\nAdaptor.prototype.onPubAck = function(client, packet) {\n  var req = {\n    id: packet.messageId,\n    route: 'connector.mqttHandler.pubAck',\n    body: {\n      mid: packet.messageId\n    }\n  };\n\n  this.subReqs[packet.messageId] = packet;\n\n  client.emit('message', req);\n};\n\n/**\n * Publish message or subscription ack.\n *\n * if packet.id exist and this.subReqs[packet.id] exist then packet is a suback.\n * Subscription is request/response mode.\n * packet.id is pass from client in packet.messageId and record in Pomelo context and attached to the subscribe response packet.\n * packet.body is the context that returned by subscribe next callback.\n *\n * if packet.id not exist then packet is a publish message.\n *\n * otherwise packet is a illegal packet.\n */\nAdaptor.prototype.publish = function(client, packet) {\n  var mid = packet.id;\n  var subreq = this.subReqs[mid];\n  if(subreq) {\n    // is suback\n    client.socket.suback({messageId: mid, granted: packet.body});\n    delete this.subReqs[mid];\n    return;\n  }\n\n  client.socket.publish(packet.body);\n};\n"
  },
  {
    "path": "lib/connectors/mqtt/protocol.js",
    "content": "/* Protocol - protocol constants */\n\n/* Command code => mnemonic */\nmodule.exports.types = {\n  0: 'reserved',\n  1: 'connect',\n  2: 'connack',\n  3: 'publish',\n  4: 'puback',\n  5: 'pubrec',\n  6: 'pubrel',\n  7: 'pubcomp',\n  8: 'subscribe',\n  9: 'suback',\n  10: 'unsubscribe',\n  11: 'unsuback',\n  12: 'pingreq',\n  13: 'pingresp',\n  14: 'disconnect',\n  15: 'reserved'\n};\n\n/* Mnemonic => Command code */\nmodule.exports.codes = {};\nfor(var k in module.exports.types) {\n  var v = module.exports.types[k];\n  module.exports.codes[v] = k;\n}\n\n/* Header */\nmodule.exports.CMD_SHIFT = 4;\nmodule.exports.CMD_MASK = 0xF0;\nmodule.exports.DUP_MASK = 0x08;\nmodule.exports.QOS_MASK = 0x03;\nmodule.exports.QOS_SHIFT = 1;\nmodule.exports.RETAIN_MASK = 0x01;\n\n/* Length */\nmodule.exports.LENGTH_MASK = 0x7F;\nmodule.exports.LENGTH_FIN_MASK = 0x80;\n\n/* Connect */\nmodule.exports.USERNAME_MASK = 0x80;\nmodule.exports.PASSWORD_MASK = 0x40;\nmodule.exports.WILL_RETAIN_MASK = 0x20;\nmodule.exports.WILL_QOS_MASK = 0x18;\nmodule.exports.WILL_QOS_SHIFT = 3;\nmodule.exports.WILL_FLAG_MASK = 0x04;\nmodule.exports.CLEAN_SESSION_MASK = 0x02;\n"
  },
  {
    "path": "lib/connectors/mqttconnector.js",
    "content": "var util = require('util');\nvar EventEmitter = require('events').EventEmitter;\nvar mqtt = require('mqtt');\nvar constants = require('../util/constants');\nvar MQTTSocket = require('./mqttsocket');\nvar Adaptor = require('./mqtt/mqttadaptor');\nvar generate = require('./mqtt/generate');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\nvar curId = 1;\n/**\n * Connector that manager low level connection and protocol bewteen server and client.\n * Develper can provide their own connector to switch the low level prototol, such as tcp or probuf.\n */\nvar Connector = function(port, host, opts) {\n  if (!(this instanceof Connector)) {\n    return new Connector(port, host, opts);\n  }\n\n  EventEmitter.call(this);\n  this.port = port;\n  this.host = host;\n  this.opts = opts || {};\n\n  this.adaptor = new Adaptor(this.opts);\n};\nutil.inherits(Connector, EventEmitter);\n\nmodule.exports = Connector;\n/**\n * Start connector to listen the specified port\n */\nConnector.prototype.start = function(cb) {\n  var self = this;\n  this.mqttServer = mqtt.createServer();\n  this.mqttServer.on('client', function(client) {\n\t\tclient.on('error', function(err) {\n\t\t\tclient.stream.destroy();\n\t\t});\n\t\t\n    client.on('close', function() {\n\t\t\tclient.stream.destroy();\n\t\t});\n\t\t\n    client.on('disconnect', function(packet) {\n\t\t\tclient.stream.destroy();\n\t\t});\n    \n    if(self.opts.disconnectOnTimeout) {\n      var timeout = self.opts.timeout * 1000 || constants.TIME.DEFAULT_MQTT_HEARTBEAT_TIMEOUT;\n      client.stream.setTimeout(timeout,function() {\n        client.emit('close');\n      });\n    }\n    \n    client.on('connect', function(packet) {\n      client.connack({returnCode: 0});\n      var mqttsocket = new MQTTSocket(curId++, client, self.adaptor);\n      self.emit('connection', mqttsocket);\n    });\n  });\n\n  this.mqttServer.listen(this.port);\n\n  process.nextTick(cb);\n};\n\nConnector.prototype.stop = function() {\n\tthis.mqttServer.close();\n\tprocess.exit(0);\n};\n\nvar composeResponse = function(msgId, route, msgBody) {\n  return {\n    id: msgId,\n    body: msgBody\n  };\n};\n\nvar composePush = function(route, msgBody) {\n  var msg = generate.publish(msgBody);\n  if(!msg) {\n    logger.error('invalid mqtt publish message: %j', msgBody);\n  }\n\n  return msg;\n};\n\nConnector.prototype.encode = function(reqId, route, msgBody) {\n\tif (!!reqId) {\n\t\treturn composeResponse(reqId, route, msgBody);\n\t} else {\n\t\treturn composePush(route, msgBody);\n\t}\n};\n\nConnector.prototype.close = function() {\n  this.mqttServer.close();\n};"
  },
  {
    "path": "lib/connectors/mqttsocket.js",
    "content": "var util = require('util');\nvar EventEmitter = require('events').EventEmitter;\n\nvar ST_INITED = 1;\nvar ST_CLOSED = 2;\n\n/**\n * Socket class that wraps socket and websocket to provide unified interface for up level.\n */\nvar Socket = function(id, socket, adaptor) {\n  EventEmitter.call(this);\n  this.id = id;\n  this.socket = socket;\n  this.remoteAddress = {\n    ip: socket.stream.remoteAddress,\n    port: socket.stream.remotePort\n  };\n  this.adaptor = adaptor;\n\n  var self = this;\n\n  socket.on('close', this.emit.bind(this, 'disconnect'));\n  socket.on('error', this.emit.bind(this, 'disconnect'));\n  socket.on('disconnect', this.emit.bind(this, 'disconnect'));\n\n  socket.on('pingreq', function(packet) {\n    socket.pingresp();\n  });\n\n  socket.on('subscribe', this.adaptor.onSubscribe.bind(this.adaptor, this));\n\n  socket.on('publish', this.adaptor.onPublish.bind(this.adaptor, this));\n\n  this.state = ST_INITED;\n\n  // TODO: any other events?\n};\n\nutil.inherits(Socket, EventEmitter);\n\nmodule.exports = Socket;\n\nSocket.prototype.send = function(msg) {\n  if(this.state !== ST_INITED) {\n    return;\n  }\n  if(msg instanceof Buffer) {\n    // if encoded, send directly\n    this.socket.stream.write(msg);\n  } else {\n    this.adaptor.publish(this, msg);\n  }\n};\n\nSocket.prototype.sendBatch = function(msgs) {\n  for(var i = 0, l = msgs.length; i<l; i++) {\n    this.send(msgs[i]);\n  }\n};\n\nSocket.prototype.disconnect = function() {\n  if(this.state === ST_CLOSED) {\n    return;\n  }\n\n  this.state = ST_CLOSED;\n  this.socket.stream.destroy();\n};"
  },
  {
    "path": "lib/connectors/sioconnector.js",
    "content": "var util = require('util');\nvar EventEmitter = require('events').EventEmitter;\nvar httpServer = require('http').createServer();\nvar SioSocket = require('./siosocket');\n\nvar PKG_ID_BYTES = 4;\nvar PKG_ROUTE_LENGTH_BYTES = 1;\nvar PKG_HEAD_BYTES = PKG_ID_BYTES + PKG_ROUTE_LENGTH_BYTES;\n\nvar curId = 1;\n\n/**\n * Connector that manager low level connection and protocol bewteen server and client.\n * Develper can provide their own connector to switch the low level prototol, such as tcp or probuf.\n */\nvar Connector = function(port, host, opts) {\n  if (!(this instanceof Connector)) {\n    return new Connector(port, host, opts);\n  }\n\n  EventEmitter.call(this);\n  this.port = port;\n  this.host = host;\n  this.opts = opts;\n  this.heartbeats = opts.heartbeats || true;\n  this.closeTimeout = opts.closeTimeout || 60;\n  this.heartbeatTimeout = opts.heartbeatTimeout || 60;\n  this.heartbeatInterval = opts.heartbeatInterval || 25;\n};\n\nutil.inherits(Connector, EventEmitter);\n\nmodule.exports = Connector;\n\n/**\n * Start connector to listen the specified port\n */\nConnector.prototype.start = function(cb) {\n  var self = this;\n  // issue https://github.com/NetEase/pomelo-cn/issues/174\n  var opts = {}\n  if(!!this.opts) {\n    opts = this.opts;\n  }\n  else {\n    opts = {\n      transports: [\n      'websocket', 'polling-xhr', 'polling-jsonp', 'polling'\n      ]\n    };\n  }\n\n  var sio = require('socket.io')(httpServer, opts);\n\n  var port = this.port;\n  httpServer.listen(port, function () {\n    console.log('sio Server listening at port %d', port);\n  });\n  sio.set('resource', '/socket.io');\n  sio.set('transports', this.opts.transports);\n  sio.set('heartbeat timeout', this.heartbeatTimeout);\n  sio.set('heartbeat interval', this.heartbeatInterval);\n\n  sio.on('connection', function (socket) {\n    var siosocket = new SioSocket(curId++, socket);\n    self.emit('connection', siosocket);\n    siosocket.on('closing', function(reason) {\n      siosocket.send({route: 'onKick', reason: reason});\n    });\n  });\n\n  process.nextTick(cb);\n};\n\n/**\n * Stop connector\n */\nConnector.prototype.stop = function(force, cb) {\n  this.wsocket.server.close();\n  process.nextTick(cb);\n};\n\nConnector.encode = Connector.prototype.encode = function(reqId, route, msg) {\n  if(reqId) {\n    return composeResponse(reqId, route, msg);\n  } else {\n    return composePush(route, msg);\n  }\n};\n\n/**\n * Decode client message package.\n *\n * Package format:\n *   message id: 4bytes big-endian integer\n *   route length: 1byte\n *   route: route length bytes\n *   body: the rest bytes\n *\n * @param  {String} data socket.io package from client\n * @return {Object}      message object\n */\nConnector.decode = Connector.prototype.decode = function(msg) {\n  var index = 0;\n\n  var id = parseIntField(msg, index, PKG_ID_BYTES);\n  index += PKG_ID_BYTES;\n\n  var routeLen = parseIntField(msg, index, PKG_ROUTE_LENGTH_BYTES);\n\n  var route = msg.substr(PKG_HEAD_BYTES, routeLen);\n  var body = msg.substr(PKG_HEAD_BYTES + routeLen);\n\n  return {\n    id: id,\n    route: route,\n    body: JSON.parse(body)\n  };\n};\n\nvar composeResponse = function(msgId, route, msgBody) {\n  return {\n    id: msgId,\n    body: msgBody\n  };\n};\n\nvar composePush = function(route, msgBody) {\n  return JSON.stringify({route: route, body: msgBody});\n};\n\nvar parseIntField = function(str, offset, len) {\n  var res = 0;\n  for(var i=0; i<len; i++) {\n    if(i > 0) {\n      res <<= 8;\n    }\n    res |= str.charCodeAt(offset + i) & 0xff;\n  }\n\n  return res;\n};"
  },
  {
    "path": "lib/connectors/siosocket.js",
    "content": "var util = require('util');\nvar EventEmitter = require('events').EventEmitter;\n\nvar ST_INITED = 0;\nvar ST_CLOSED = 1;\n\n/**\n * Socket class that wraps socket.io socket to provide unified interface for up level.\n */\nvar Socket = function(id, socket) {\n  EventEmitter.call(this);\n  this.id = id;\n  this.socket = socket;\n  this.remoteAddress = {\n    ip: socket.handshake.address.address,\n    port: socket.handshake.address.port\n  };\n\n  var self = this;\n\n  socket.on('disconnect', this.emit.bind(this, 'disconnect'));\n\n  socket.on('error', this.emit.bind(this, 'error'));\n\n  socket.on('message', function(msg) {\n    self.emit('message', msg);\n  });\n\n  this.state = ST_INITED;\n\n  // TODO: any other events?\n};\n\nutil.inherits(Socket, EventEmitter);\n\nmodule.exports = Socket;\n\nSocket.prototype.send = function(msg) {\n  if(this.state !== ST_INITED) {\n    return;\n  }\n  if(typeof msg !== 'string') {\n    msg = JSON.stringify(msg);\n  }\n  this.socket.send(msg);\n};\n\nSocket.prototype.disconnect = function() {\n  if(this.state === ST_CLOSED) {\n    return;\n  }\n\n  this.state = ST_CLOSED;\n  this.socket.disconnect();\n};\n\nSocket.prototype.sendBatch = function(msgs) {\n  this.send(encodeBatch(msgs));\n};\n\n/**\n * Encode batch msg to client\n */\nvar encodeBatch = function(msgs){\n  var res = '[', msg;\n  for(var i=0, l=msgs.length; i<l; i++) {\n    if(i > 0) {\n      res += ',';\n    }\n    msg = msgs[i];\n    if(typeof msg === 'string') {\n      res += msg;\n    } else {\n      res += JSON.stringify(msg);\n    }\n  }\n  res += ']';\n  return res;\n};\n"
  },
  {
    "path": "lib/connectors/udpconnector.js",
    "content": "var net = require('net');\nvar util = require('util');\nvar dgram = require(\"dgram\");\nvar utils = require('../util/utils');\nvar Constants = require('../util/constants');\nvar UdpSocket = require('./udpsocket');\nvar Kick = require('./commands/kick');\nvar Handshake = require('./commands/handshake');\nvar Heartbeat = require('./commands/heartbeat');\nvar protocol = require('pomelo-protocol');\nvar Package = protocol.Package;\nvar Message = protocol.Message;\nvar coder = require('./common/coder');\nvar EventEmitter = require('events').EventEmitter;\n\nvar curId = 1;\n\nvar Connector = function(port, host, opts) {\n  if (!(this instanceof Connector)) {\n    return new Connector(port, host, opts);\n  }\n\n  EventEmitter.call(this);\n  this.opts = opts || {};\n  this.type = opts.udpType || 'udp4';\n  this.handshake = new Handshake(opts);\n  if(!opts.heartbeat) {\n    opts.heartbeat = Constants.TIME.DEFAULT_UDP_HEARTBEAT_TIME;\n    opts.timeout = Constants.TIME.DEFAULT_UDP_HEARTBEAT_TIMEOUT;\n  }\n  this.heartbeat = new Heartbeat(utils.extends(opts, {disconnectOnTimeout: true}));\n  this.clients = {};\n  this.host = host;\n  this.port = port;\n};\n\nutil.inherits(Connector, EventEmitter);\n\nmodule.exports = Connector;\n\nConnector.prototype.start = function(cb) {\n  var self = this;\n  this.tcpServer = net.createServer();\n  this.socket = dgram.createSocket(this.type, function(msg, peer) {\n    var key = genKey(peer);\n    if(!self.clients[key]) {\n      var udpsocket = new UdpSocket(curId++, self.socket, peer);\n      self.clients[key] = udpsocket;\n\n      udpsocket.on('handshake',\n      self.handshake.handle.bind(self.handshake, udpsocket));\n\n      udpsocket.on('heartbeat',\n      self.heartbeat.handle.bind(self.heartbeat, udpsocket));\n\n      udpsocket.on('disconnect',\n      self.heartbeat.clear.bind(self.heartbeat, udpsocket.id));\n\n      udpsocket.on('disconnect', function() {\n        delete self.clients[genKey(udpsocket.peer)];\n      });\n\n      udpsocket.on('closing', Kick.handle.bind(null, udpsocket));\n\n      self.emit('connection', udpsocket);\n    }\n  });\n\n  this.socket.on('message', function(data, peer) {\n    var socket = self.clients[genKey(peer)];\n    if(!!socket) {\n      socket.emit('package', data);\n    }\n  });\n\n  this.socket.on('error', function(err) {\n    logger.error('udp socket encounters with error: %j', err.stack);\n    return;\n  });\n\n  this.socket.bind(this.port, this.host);\n  this.tcpServer.listen(this.port);\n  process.nextTick(cb);\n};\n\nConnector.decode = Connector.prototype.decode = coder.decode;\n\nConnector.encode = Connector.prototype.encode = coder.encode;\n\nConnector.prototype.stop = function(force, cb) {\n  this.socket.close();\n  process.nextTick(cb);\n};\n\nvar genKey = function(peer) {\n  return peer.address + \":\" + peer.port;\n};"
  },
  {
    "path": "lib/connectors/udpsocket.js",
    "content": "var util = require('util');\nvar handler = require('./common/handler');\nvar protocol = require('pomelo-protocol');\nvar Package = protocol.Package;\nvar EventEmitter = require('events').EventEmitter;\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\nvar ST_INITED = 0;\nvar ST_WAIT_ACK = 1;\nvar ST_WORKING = 2;\nvar ST_CLOSED = 3;\n\nvar Socket = function(id, socket, peer) {\n\tEventEmitter.call(this);\n\t\n  this.id = id;\n\tthis.socket = socket;\n  this.peer = peer;\n\tthis.host = peer.address;\n\tthis.port = peer.port;\n\tthis.remoteAddress = {\n    ip: this.host,\n    port: this.port\n  };\n\n  var self = this;\n  this.on('package', function(pkg) {\n    if(!!pkg) {\n      pkg = Package.decode(pkg);\n      handler(self, pkg);\n    }\n  });\n\n  this.state = ST_INITED;\n};\n\nutil.inherits(Socket, EventEmitter);\n\nmodule.exports = Socket;\n\n/**\n * Send byte data package to client.\n *\n * @param  {Buffer} msg byte data\n */\nSocket.prototype.send = function(msg) {\n  if(this.state !== ST_WORKING) {\n    return;\n  }\n  if(msg instanceof String) {\n    msg = new Buffer(msg);\n  } else if(!(msg instanceof Buffer)) {\n    msg = new Buffer(JSON.stringify(msg));\n  }\n  this.sendRaw(Package.encode(Package.TYPE_DATA, msg));\n};\n\nSocket.prototype.sendRaw = function(msg) {\n\tthis.socket.send(msg, 0, msg.length, this.port, this.host, function(err, bytes) {\n    if(!!err)\t{\n      logger.error('send msg to remote with err: %j', err.stack);\n      return;\n    }\n  });\n};\n\nSocket.prototype.sendForce = function(msg) {\n  if(this.state === ST_CLOSED) {\n    return;\n  }\n  this.sendRaw(msg);\n};\n\nSocket.prototype.handshakeResponse = function(resp) {\n  if(this.state !== ST_INITED) {\n    return;\n  }\n  this.sendRaw(resp);\n  this.state = ST_WAIT_ACK;\n};\n\nSocket.prototype.sendBatch = function(msgs) {\n  if(this.state !== ST_WORKING) {\n    return;\n  }\n  var rs = [];\n  for(var i=0; i<msgs.length; i++) {\n    var src = Package.encode(Package.TYPE_DATA, msgs[i]);\n    rs.push(src);\n  }\n  this.sendRaw(Buffer.concat(rs));\n};\n\nSocket.prototype.disconnect = function() {\n  if(this.state === ST_CLOSED) {\n    return;\n  }\n  this.state = ST_CLOSED;\n  this.emit('disconnect', 'the connection is disconnected.');\n};"
  },
  {
    "path": "lib/filters/handler/serial.js",
    "content": "/**\n * Filter to keep request sequence.\n */\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar taskManager = require('../../common/manager/taskManager');\n\nmodule.exports = function(timeout) {\n  return new Filter(timeout);\n};\n\nvar Filter = function(timeout) {\n  this.timeout = timeout;\n};\n\n/**\n * request serialization after filter\n */\nFilter.prototype.before = function(msg, session, next) {\n  taskManager.addTask(session.id, function(task) {\n    session.__serialTask__ = task;\n    next();\n  }, function() {\n    logger.error('[serial filter] msg timeout, msg:' + JSON.stringify(msg));\n  }, this.timeout);\n};\n\n/**\n * request serialization after filter\n */\nFilter.prototype.after = function(err, msg, session, resp, next) {\n  var task = session.__serialTask__;\n  if(task) {\n    if(!task.done() && !err) {\n      err = new Error('task time out. msg:' + JSON.stringify(msg));\n    }\n  }\n  next(err);\n};\n"
  },
  {
    "path": "lib/filters/handler/time.js",
    "content": "/**\n * Filter for statistics.\n * Record used time for each request.\n */\nvar conLogger = require('pomelo-logger').getLogger('con-log', __filename);\nvar utils = require('../../util/utils');\n\nmodule.exports = function() {\n  return new Filter();\n};\n\nvar Filter = function() {\n};\n\nFilter.prototype.before = function(msg, session, next) {\n  session.__startTime__ = Date.now();\n  next();\n};\n\nFilter.prototype.after = function(err, msg, session, resp, next) {\n  var start = session.__startTime__;\n  if(typeof start === 'number') {\n    var timeUsed = Date.now() - start;\n    var log = {\n      route : msg.__route__,\n      args : msg,\n      time : utils.format(new Date(start)),\n      timeUsed : timeUsed\n    };\n    conLogger.info(JSON.stringify(log));\n  }\n  next(err);\n};\n"
  },
  {
    "path": "lib/filters/handler/timeout.js",
    "content": "/**\n * Filter for timeout.\n * Print a warn information when request timeout.\n */\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar utils = require('../../util/utils');\n\nvar DEFAULT_TIMEOUT = 3000;\nvar DEFAULT_SIZE = 500;\n\nmodule.exports = function(timeout, maxSize) {\n  return new Filter(timeout || DEFAULT_TIMEOUT, maxSize || DEFAULT_SIZE);\n};\n\nvar Filter = function(timeout, maxSize) {\n  this.timeout = timeout;\n  this.maxSize = maxSize;\n  this.timeouts = {};\n  this.curId = 0;\n};\n\nFilter.prototype.before = function(msg, session, next) {\n  var count = utils.size(this.timeouts);\n  if(count > this.maxSize) {\n    logger.warn('timeout filter is out of range, current size is %s, max size is %s', count, this.maxSize);\n    next();\n    return;\n  }\n  this.curId++;\n  this.timeouts[this.curId] = setTimeout(function() {\n    logger.error('request %j timeout.', msg.__route__);\n  }, this.timeout);\n  session.__timeout__ = this.curId;\n  next();\n};\n\nFilter.prototype.after = function(err, msg, session, resp, next) {\n  var timeout = this.timeouts[session.__timeout__];\n  if(timeout) {\n    clearTimeout(timeout);\n    delete this.timeouts[session.__timeout__];\n  }\n  next(err);\n};\n"
  },
  {
    "path": "lib/filters/handler/toobusy.js",
    "content": "/**\n * Filter for toobusy.\n * if the process is toobusy, just skip the new request\n */\nvar conLogger = require('pomelo-logger').getLogger('con-log', __filename);\nvar toobusy = null;\nvar DEFAULT_MAXLAG = 70;\n\n\nmodule.exports = function(maxLag) {\n  return new Filter(maxLag || DEFAULT_MAXLAG);\n};\n\nvar Filter = function(maxLag) {\n  try {\n    toobusy = require('toobusy');\n  } catch(e) {\n  }\n  if(!!toobusy) {\n    toobusy.maxLag(maxLag);\n  }\n};\n\nFilter.prototype.before = function(msg, session, next) {\n  if (!!toobusy && toobusy()) {\n    conLogger.warn('[toobusy] reject request msg: ' + msg);\n    var err = new Error('Server toobusy!');\n    err.code = 500;\n    next(err);\n  } else {\n    next();\n  }\n};"
  },
  {
    "path": "lib/filters/rpc/rpcLog.js",
    "content": "/**\n * Filter for rpc log.\n * Record used time for remote process call.\n */\nvar rpcLogger = require('pomelo-logger').getLogger('rpc-log', __filename);\nvar utils = require('../../util/utils');\n\nmodule.exports = function() {\n  return new Filter();\n};\n\nvar Filter = function () {\n}; \n\nFilter.prototype.name = 'rpcLog';\n\n/**\n * Before filter for rpc\n */\n\nFilter.prototype.before = function(serverId, msg, opts, next) {\n  opts = opts||{};\n  opts.__start_time__ = Date.now();\n  next();\n};\n\n/**\n * After filter for rpc\n */\nFilter.prototype.after = function(serverId, msg, opts, next) {\n  if(!!opts && !!opts.__start_time__) {\n    var start = opts.__start_time__;\n    var end = Date.now();\n    var timeUsed = end - start;\n    var log = {\n      route: msg.service,\n      args: msg.args,\n      time: utils.format(new Date(start)),\n      timeUsed: timeUsed\n    };\n    rpcLogger.info(JSON.stringify(log));\n  }\n  next();\n};\n"
  },
  {
    "path": "lib/filters/rpc/toobusy.js",
    "content": "/**\n * Filter for rpc log.\n * Reject rpc request when toobusy\n */\nvar rpcLogger = require('pomelo-logger').getLogger('rpc-log', __filename);\nvar toobusy = null;\n\nvar DEFAULT_MAXLAG = 70;\n\nmodule.exports = function(maxLag) {\n  return new Filter(maxLag || DEFAULT_MAXLAG);\n};\n\nvar Filter = function(maxLag) {\n  try {\n    toobusy = require('toobusy');\n  } catch(e) {\n  }\n  if(!!toobusy) {\n    toobusy.maxLag(maxLag);\n  }\n};\n\nFilter.prototype.name = 'toobusy';\n\n/**\n * Before filter for rpc\n */\n Filter.prototype.before = function(serverId, msg, opts, next) {\n  opts = opts||{};\n  if (!!toobusy && toobusy()) {\n    rpcLogger.warn('Server too busy for rpc request, serverId:' + serverId + ' msg: ' + msg);\n    var err =  new Error('Backend server ' + serverId + ' is too busy now!');\n    err.code = 500;\n    next(err);\n  } else {\n    next();\n  }\n};\n"
  },
  {
    "path": "lib/index.js",
    "content": "module.exports = require('./pomelo');"
  },
  {
    "path": "lib/master/master.js",
    "content": "var starter = require('./starter');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar crashLogger = require('pomelo-logger').getLogger('crash-log', __filename);\nvar adminLogger = require('pomelo-logger').getLogger('admin-log', __filename);\nvar admin = require('pomelo-admin');\nvar util = require('util');\nvar utils = require('../util/utils');\nvar moduleUtil = require('../util/moduleUtil');\nvar Constants = require('../util/constants');\n\nvar Server = function(app, opts) {\n  this.app = app;\n  this.masterInfo = app.getMaster();\n  this.registered = {};\n  this.modules = [];\n  opts = opts || {};\n  \n  opts.port = this.masterInfo.port;\n  opts.env = this.app.get(Constants.RESERVED.ENV);\n  this.closeWatcher = opts.closeWatcher;\n  this.masterConsole = admin.createMasterConsole(opts);\n};\n\nmodule.exports = Server;\n\nServer.prototype.start = function(cb) {\n  moduleUtil.registerDefaultModules(true, this.app, this.closeWatcher);\n  moduleUtil.loadModules(this, this.masterConsole);\n\n  var self = this;\n  // start master console\n  this.masterConsole.start(function(err) {\n    if(err) {\n      process.exit(0);\n    }\n    moduleUtil.startModules(self.modules, function(err) {\n      if(err) {\n        utils.invokeCallback(cb, err);\n        return;\n      }\n\n      if(self.app.get(Constants.RESERVED.MODE) !== Constants.RESERVED.STAND_ALONE) {\n        starter.runServers(self.app);\n      }\n      utils.invokeCallback(cb);\n    });\n  });\n  \n  this.masterConsole.on('error', function(err) {\n    if(!!err) {\n      logger.error('masterConsole encounters with error: ' + err.stack);\n      return;\n    }\n  });\n  \n  this.masterConsole.on('reconnect', function(info){\n    self.app.addServers([info]);\n  });\n\n  // monitor servers disconnect event\n  this.masterConsole.on('disconnect', function(id, type, info, reason) {\n    crashLogger.info(util.format('[%s],[%s],[%s],[%s]', type, id, Date.now(), reason || 'disconnect'));\n    var count = 0;\n    var time = 0;\n    var pingTimer = null;\n    var server = self.app.getServerById(id);\n    var stopFlags = self.app.get(Constants.RESERVED.STOP_SERVERS) || [];\n    if(!!server && (server[Constants.RESERVED.AUTO_RESTART] === 'true' || server[Constants.RESERVED.RESTART_FORCE] === 'true') && stopFlags.indexOf(id) < 0) {\n      var setTimer = function(time) {\n        pingTimer = setTimeout(function() {\n          utils.ping(server.host, function(flag) {\n            if(flag)  {\n              handle();\n            } else {\n              count++;\n              if(count > 3) {\n                time = Constants.TIME.TIME_WAIT_MAX_PING;\n              } else {\n                time = Constants.TIME.TIME_WAIT_PING * count;\n              }\n              setTimer(time);\n            }\n          });\n        }, time);\n      };\n      setTimer(time);\n      var handle = function() {\n        clearTimeout(pingTimer);\n        utils.checkPort(server, function(status) {\n          if(status === 'error') {\n            utils.invokeCallback(cb, new Error('Check port command executed with error.'));\n            return;\n          } else if(status === 'busy') {\n            if(!!server[Constants.RESERVED.RESTART_FORCE]) {\n              starter.kill([info.pid], [server]);\n            } else {\n              utils.invokeCallback(cb, new Error('Port occupied already, check your server to add.'));\n              return;\n            }\n          }\n          setTimeout(function() {\n            starter.run(self.app, server, null);\n          }, Constants.TIME.TIME_WAIT_STOP);\n        });\n      };\n    }\n  });\n\n  // monitor servers register event\n  this.masterConsole.on('register', function(record) {\n    starter.bindCpu(record.id, record.pid, record.host);\n  });\n\n  this.masterConsole.on('admin-log', function(log, error) {\n    if(error) {\n      adminLogger.error(JSON.stringify(log));\n    } else {\n      adminLogger.info(JSON.stringify(log));\n    }\n  });\n};\n\nServer.prototype.stop = function(cb) {\n  this.masterConsole.stop();\n  process.nextTick(cb);\n};\n"
  },
  {
    "path": "lib/master/starter.js",
    "content": "var cp = require('child_process');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar starter = module.exports;\nvar util = require('util');\nvar utils = require('../util/utils');\nvar Constants = require('../util/constants');\nvar env = Constants.RESERVED.ENV_DEV;\nvar os=require('os');\nvar cpus = {};\nvar pomelo = require('../pomelo');\n\n/**\n * Run all servers\n *\n * @param {Object} app current application  context\n * @return {Void}\n */\n starter.runServers = function(app) {\n  var server, servers;\n  var condition = app.startId || app.type;\n  switch(condition) {\n    case Constants.RESERVED.MASTER:\n    break;\n    case Constants.RESERVED.ALL:\n    servers = app.getServersFromConfig();\n    for (var serverId in servers) {\n      this.run(app, servers[serverId]);\n    }\n    break;\n    default:\n    server = app.getServerFromConfig(condition);\n    if(!!server) {\n      this.run(app, server);\n    } else {\n      servers = app.get(Constants.RESERVED.SERVERS)[condition];\n      for(var i=0; i<servers.length; i++) {\n        this.run(app, servers[i]);\n      }\n    }\n  }\n};\n\n/**\n * Run server\n *\n * @param {Object} app current application context\n * @param {Object} server\n * @return {Void}\n */\nstarter.run = function(app, server, cb) {\n  env = app.get(Constants.RESERVED.ENV);\n  var cmd, key;\n  if (utils.isLocal(server.host)) {\n    var options = [];\n    if (!!server.args) {\n      if(typeof server.args === 'string') {\n        options.push(server.args.trim());\n      } else {\n        options = options.concat(server.args);\n      }\n    }\n    cmd = app.get(Constants.RESERVED.MAIN);\n    options.push(cmd);\n    options.push(util.format('env=%s',  env));\n    for(key in server) {\n      if(key === Constants.RESERVED.CPU) {\n        cpus[server.id] = server[key];\n      }\n      options.push(util.format('%s=%s', key, server[key]));\n    }\n    starter.localrun(process.execPath, null, options, cb);\n  } else {\n    cmd = util.format('cd \"%s\" && \"%s\"', app.getBase(), process.execPath);\n    var arg = server.args;\n    if (arg !== undefined) {\n      cmd += arg;\n    }\n    cmd += util.format(' \"%s\" env=%s ', app.get(Constants.RESERVED.MAIN), env);\n    for(key in server) {\n      if(key === Constants.RESERVED.CPU) {\n        cpus[server.id] = server[key];\n      }\n      cmd += util.format(' %s=%s ', key, server[key]);\n    }\n    starter.sshrun(cmd, server.host, cb);\n  }\n};\n\n/**\n * Bind process with cpu\n *\n * @param {String} sid server id\n * @param {String} pid process id\n * @param {String} host server host\n * @return {Void}\n */\nstarter.bindCpu = function(sid, pid, host) {\n  if(os.platform() === Constants.PLATFORM.LINUX && cpus[sid] !== undefined) {\n    if (utils.isLocal(host)) {\n      var options = [];\n      options.push('-pc');\n      options.push(cpus[sid]);\n      options.push(pid);\n      starter.localrun(Constants.COMMAND.TASKSET, null, options);\n    }\n    else {\n      var cmd = util.format('taskset -pc \"%s\" \"%s\"', cpus[sid], pid);\n      starter.sshrun(cmd, host, null);\n    }\n  }\n};\n\n/**\n * Kill application in all servers\n *\n * @param {String} pids  array of server's pid\n * @param {String} serverIds array of serverId\n */\nstarter.kill = function(pids, servers) {\n  var cmd;\n  for(var i = 0; i < servers.length; i++) {\n    var server = servers[i];\n    if(utils.isLocal(server.host)) {\n      var options = [];\n      if(os.platform() === Constants.PLATFORM.WIN) {\n        cmd = Constants.COMMAND.TASKKILL;\n        options.push('/pid');\n        options.push('/f');\n      } else {\n        cmd = Constants.COMMAND.KILL;\n        options.push(-9);\n      }\n      options.push(pids[i]);\n      starter.localrun(cmd,null,options);\n    } else {\n      if(os.platform() === Constants.PLATFORM.WIN) {\n        cmd = util.format('taskkill /pid %s /f', pids[i]);\n      } else {\n        cmd = util.format('kill -9 %s', pids[i]);\n      }\n      starter.sshrun(cmd, server.host);\n    }\n  }\n};\n\n/**\n * Use ssh to run command.\n *\n * @param {String} cmd command that would be executed in the remote server\n * @param {String} host remote server host\n * @param {Function} cb callback function\n *\n */\nstarter.sshrun = function(cmd, host, cb) {\n  var args = [];\n  args.push(host);\n  var ssh_params = pomelo.app.get(Constants.RESERVED.SSH_CONFIG_PARAMS);\n  if(!!ssh_params && Array.isArray(ssh_params)) {\n    args = args.concat(ssh_params);\n  }\n  args.push(cmd);\n\n  logger.info('Executing ' + cmd + ' on ' + host + ':22');\n  spawnProcess(Constants.COMMAND.SSH, host, args, cb);\n  return;\n};\n\n/**\n * Run local command.\n *\n * @param {String} cmd\n * @param {Callback} callback\n *\n */\nstarter.localrun = function (cmd, host, options, callback) {\n  logger.info('Executing ' + cmd + ' ' + options + ' locally');\n  spawnProcess(cmd, host, options, callback);\n};\n\n/**\n * Fork child process to run command.\n *\n * @param {String} command\n * @param {Object} options\n * @param {Callback} callback\n *\n */\nvar spawnProcess = function(command, host, options, cb) {\n  var child = null;\n\n  if(env === Constants.RESERVED.ENV_DEV) {\n    child = cp.spawn(command, options);\n    var prefix = command === Constants.COMMAND.SSH ? '[' + host + '] ' : '';\n\n    child.stderr.on('data', function (chunk) {\n      var msg = chunk.toString();\n      process.stderr.write(msg);\n      if(!!cb) {\n        cb(msg);\n      }\n    });\n\n    child.stdout.on('data', function (chunk) {\n      var msg = prefix + chunk.toString();\n      process.stdout.write(msg);\n    });\n  } else {\n    child = cp.spawn(command, options, {detached: true, stdio: 'inherit'});\n    child.unref();\n  }\n\n  child.on('exit', function (code) {\n    if(code !== 0) {\n      logger.warn('child process exit with error, error code: %s, executed command: %s', code,  command);\n    }\n    if (typeof cb === 'function') {\n      cb(code === 0 ? null : code);\n    }\n  });\n};\n"
  },
  {
    "path": "lib/master/watchdog.js",
    "content": "var logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar utils = require('../util/utils');\nvar Constants = require('../util/constants');\nvar countDownLatch = require('../util/countDownLatch');\nvar EventEmitter = require('events').EventEmitter;\nvar util = require('util');\n\nvar Watchdog = function(app, service) {\n  EventEmitter.call(this);\n\n  this.app = app;\n  this.service = service;\n  this.isStarted = false;\n  this.count = utils.size(app.getServersFromConfig());\n\n  this.servers = {};\n  this.listeners = {};\n};\nutil.inherits(Watchdog, EventEmitter);\n\nmodule.exports = Watchdog;\n\nWatchdog.prototype.addServer = function(server) {\n  if(!server) {\n    return;\n  }\n  this.servers[server.id] = server;\n  this.notify({action: 'addServer', server: server});\n};\n\nWatchdog.prototype.removeServer = function(id) {\n  if(!id) {\n    return;\n  }\n  this.unsubscribe(id);\n  delete this.servers[id];\n  this.notify({action: 'removeServer', id: id});\n};\n\nWatchdog.prototype.reconnectServer = function(server) {\n  var self = this;\n  if(!server) {\n    return;\n  }\n  if(!this.servers[server.id]) {\n    this.servers[server.id] = server;\n  }\n  //replace server in reconnect server\n  this.notifyById(server.id, {action: 'replaceServer', servers: self.servers});\n  // notify other server to add server\n  this.notify({action: 'addServer', server: server});\n  // add server in listener\n  this.subscribe(server.id);\n};\n\nWatchdog.prototype.subscribe = function(id) {\n  this.listeners[id] = 1;\n};\n\nWatchdog.prototype.unsubscribe = function(id) {\n  delete this.listeners[id];\n};\n\nWatchdog.prototype.query = function() {\n  return this.servers;\n};\n\nWatchdog.prototype.record = function(id) {\n  if(!this.isStarted && --this.count < 0) {\n    var usedTime = Date.now() - this.app.startTime;\n    logger.info('all servers startup in %s ms', usedTime);\n    this.notify({action: 'startOver'});\n    this.isStarted = true;\n  }\n};\n\nWatchdog.prototype.notifyById = function(id, msg) {\n  this.service.agent.request(id, Constants.KEYWORDS.MONITOR_WATCHER, msg, function(signal) {\n    if(signal !== Constants.SIGNAL.OK) {\n      logger.error('master watchdog fail to notify to monitor, id: %s, msg: %j', id, msg);\n    } else {\n      logger.debug('master watchdog notify to monitor success, id: %s, msg: %j', id, msg);\n    }\n  });\n};\n\nWatchdog.prototype.notify = function(msg) {\n  var listeners = this.listeners;\n  var success = true;\n  var fails = [];\n  var timeouts = [];\n  var requests = {};\n  var count = utils.size(listeners);\n  if(count === 0) {\n    logger.warn('master watchdog listeners is none, msg: %j', msg);\n    return;\n  }\n  var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function(isTimeout) {\n    if(!!isTimeout) {\n      for(var key in requests) {\n        if(!requests[key])  {\n          timeouts.push(key);\n        }\n      }\n      logger.error('master watchdog request timeout message: %j, timeouts: %j, fails: %j', msg, timeouts, fails);\n    }\n    if(!success) {\n      logger.error('master watchdog request fail message: %j, fails: %j', msg, fails);\n    }\n  });\n\n  var moduleRequest = function(self, id) {\n    return (function() {\n      self.service.agent.request(id, Constants.KEYWORDS.MONITOR_WATCHER, msg, function(signal) {\n        if(signal !== Constants.SIGNAL.OK) {\n          fails.push(id);\n          success = false;\n        }\n        requests[id] = 1;\n        latch.done();\n      });\n    })();\n  };\n\n  for(var id in listeners) {\n    requests[id] = 0;\n    moduleRequest(this, id);\n  }\n};"
  },
  {
    "path": "lib/modules/console.js",
    "content": "/*!\n * Pomelo -- consoleModule serverStop stop/kill\n * Copyright(c) 2012 fantasyni <fantasyni@163.com>\n * MIT Licensed\n */\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar countDownLatch = require('../util/countDownLatch');\nvar utils = require('../util/utils');\nvar Constants = require('../util/constants');\nvar starter = require('../master/starter');\nvar exec = require('child_process').exec;\n\nmodule.exports = function(opts) {\n  return new Module(opts);\n};\n\nmodule.exports.moduleId = '__console__';\n\nvar Module = function(opts) {\n  opts = opts || {};\n  this.app = opts.app;\n  this.starter = opts.starter;\n};\n\nModule.prototype.monitorHandler = function(agent, msg, cb) {\n  var serverId = agent.id;\n  switch(msg.signal) {\n    case 'stop':\n      if(agent.type === Constants.RESERVED.MASTER) {\n        return;\n      }\n      this.app.stop(true);\n      break;\n    case 'list':\n      var serverType = agent.type;\n      var pid = process.pid;\n      var heapUsed = (process.memoryUsage().heapUsed/(1024 * 1024)).toFixed(2);\n      var rss = (process.memoryUsage().rss/(1024 * 1024)).toFixed(2);\n      var heapTotal = (process.memoryUsage().heapTotal/(1024 * 1024)).toFixed(2);\n      var uptime = (process.uptime()/60).toFixed(2);\n      utils.invokeCallback(cb, {\n        serverId: serverId,\n        body: {serverId:serverId, serverType: serverType, pid:pid, rss: rss, heapTotal: heapTotal, heapUsed:heapUsed, uptime:uptime}\n      });\n      break;\n    case 'kill':\n      utils.invokeCallback(cb, serverId);\n      if (agent.type !== 'master') {\n        setTimeout(function() {\n          process.exit(-1);\n        }, Constants.TIME.TIME_WAIT_MONITOR_KILL);\n      }\n      break;\n    case 'addCron':\n      this.app.addCrons([msg.cron]);\n      break;\n    case 'removeCron':\n      this.app.removeCrons([msg.cron]);\n      break;\n    case 'blacklist':\n      if(this.app.isFrontend()) {\n        var connector = this.app.components.__connector__;\n        connector.blacklist = connector.blacklist.concat(msg.blacklist);\n      }\n      break;\n    case 'restart':\n      if(agent.type === Constants.RESERVED.MASTER) {\n        return;\n      }\n      var self = this;\n      var server = this.app.get(Constants.RESERVED.CURRENT_SERVER);\n      utils.invokeCallback(cb, server);\n      process.nextTick(function() {\n        self.app.stop(true);\n      });\n      break;\n    default:\n      logger.error('receive error signal: %j', msg);\n      break;\n  }\n};\n\nModule.prototype.clientHandler = function(agent, msg, cb) {\n  var app = this.app;\n  switch(msg.signal) {\n    case 'kill':\n      kill(app, agent, msg, cb);\n      break;\n    case 'stop':\n      stop(app, agent, msg, cb);\n      break;\n    case 'list':\n      list(agent, msg, cb);\n      break;\n    case 'add':\n      add(app, msg, cb);\n      break;\n    case 'addCron':\n      addCron(app, agent, msg, cb);\n      break;\n    case 'removeCron':\n      removeCron(app, agent, msg, cb);\n      break;\n    case 'blacklist':\n      blacklist(agent, msg, cb);\n      break;\n    case 'restart':\n      restart(app, agent, msg, cb);\n      break;\n    default:\n      utils.invokeCallback(cb, new Error('The command cannot be recognized, please check.'), null);\n      break;\n  }\n};\n\nvar kill = function(app, agent, msg, cb) {\n  var sid, record;\n  var serverIds = [];\n  var count = utils.size(agent.idMap);\n  var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_MASTER_KILL}, function(isTimeout) {\n    if (!isTimeout) {\n      utils.invokeCallback(cb, null, {code: 'ok'});\n    } else {\n      utils.invokeCallback(cb, null, {code: 'remained', serverIds: serverIds});\n    }\n    setTimeout(function() {\n      process.exit(-1);\n    }, Constants.TIME.TIME_WAIT_MONITOR_KILL);\n  });\n\n  var agentRequestCallback = function(msg) {\n      for (var i = 0; i < serverIds.length; ++i) {\n        if (serverIds[i] === msg) {\n          serverIds.splice(i,1);\n          latch.done();\n          break;\n        }\n      }\n  };\n\n  for(sid in agent.idMap) {\n    record = agent.idMap[sid];\n    serverIds.push(record.id);\n    agent.request(record.id, module.exports.moduleId, { signal: msg.signal }, agentRequestCallback);\n  }\n};\n\nvar stop = function(app, agent, msg, cb) {\n  var serverIds = msg.ids;\n  if(!!serverIds.length) {\n    var servers = app.getServers();\n    app.set(Constants.RESERVED.STOP_SERVERS, serverIds);\n    for(var i=0; i<serverIds.length; i++) {\n      var serverId = serverIds[i];\n      if(!servers[serverId]) {\n        utils.invokeCallback(cb, new Error('Cannot find the server to stop.'), null);\n      } else {\n        agent.notifyById(serverId, module.exports.moduleId, { signal: msg.signal });\n      }\n    }\n    utils.invokeCallback(cb, null, { status: \"part\" });\n  } else {\n    var servers = app.getServers();\n    var serverIds = [];\n    for(var i in servers){\n        serverIds.push(i)\n    }\n    app.set(Constants.RESERVED.STOP_SERVERS, serverIds);\n    agent.notifyAll(module.exports.moduleId, { signal: msg.signal });\n    setTimeout(function() {\n      app.stop(true);\n      utils.invokeCallback(cb, null, { status: \"all\" });\n    }, Constants.TIME.TIME_WAIT_STOP);\n  }\n};\n\nvar restart = function(app, agent, msg, cb) {\n  var successFlag;\n  var successIds = [];\n  var serverIds = msg.ids;\n  var type = msg.type;\n  var servers;\n  if(!serverIds.length && !!type) {\n    servers = app.getServersByType(type);\n    if(!servers) {\n      utils.invokeCallback(cb, new Error('restart servers with unknown server type: ' + type));\n      return;\n    }\n    for(var i=0; i<servers.length; i++) {\n      serverIds.push(servers[i].id);\n    }\n  } else if(!serverIds.length) {\n    servers = app.getServers();\n    for(var key in servers) {\n      serverIds.push(key);\n    }\n  }  \n  var count = serverIds.length;\n  var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function() {\n    if(!successFlag) {\n      utils.invokeCallback(cb, new Error('all servers start failed.'));\n      return;\n    }\n    utils.invokeCallback(cb, null, utils.arrayDiff(serverIds, successIds));\n  });\n\n  var request = function(id) {\n    return (function() {\n      agent.request(id, module.exports.moduleId, { signal: msg.signal }, function(msg) {\n        if(!utils.size(msg)) {\n          latch.done();\n          return;\n        }\n        setTimeout(function() {\n         runServer(app, msg, function(err, status) {\n          if(!!err) {\n            logger.error('restart ' + id + ' failed.');\n          } else {\n            successIds.push(id);\n            successFlag = true;\n          }\n          latch.done();\n        });\n       }, Constants.TIME.TIME_WAIT_RESTART);\n      });\n    })();\n  };\n\n  for(var j=0; j<serverIds.length; j++) {\n    request(serverIds[j]);\n  }\n};\n\nvar list = function(agent, msg, cb) {\n  var sid, record;\n  var serverInfo = {};\n  var count = utils.size(agent.idMap);\n  var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function() {\n    utils.invokeCallback(cb, null, { msg: serverInfo });\n  });\n\n  var callback = function(msg) {\n    serverInfo[msg.serverId] = msg.body;\n    latch.done();\n  };\n  for(sid in agent.idMap) {\n    record = agent.idMap[sid];\n    agent.request(record.id, module.exports.moduleId, { signal: msg.signal }, callback);\n  }\n};\n\nvar add = function(app, msg, cb) {\n  if(checkCluster(msg)) {\n    startCluster(app, msg, cb);\n  } else {\n    startServer(app, msg, cb);\n  }\n  reset(ServerInfo);\n};\n\nvar addCron = function(app, agent, msg, cb) {\n  var cron = parseArgs(msg, CronInfo, cb);\n  sendCronInfo(cron, agent, msg, CronInfo, cb);\n};\n\nvar removeCron = function(app, agent, msg, cb) {\n  var cron = parseArgs(msg, RemoveCron, cb);\n  sendCronInfo(cron, agent, msg, RemoveCron, cb);\n};\n\nvar blacklist = function(agent, msg, cb) {\n  var ips = msg.args;\n  for(var i=0; i<ips.length; i++) {\n    if(!(new RegExp(/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)/g).test(ips[i]))) {\n      utils.invokeCallback(cb, new Error('blacklist ip: ' + ips[i] + ' is error format.'), null);\n      return;\n    }\n  }\n  agent.notifyAll(module.exports.moduleId, { signal: msg.signal, blacklist: msg.args });\n  process.nextTick(function() {\n    cb(null, { status: \"ok\" });\n  });\n};\n\nvar checkPort = function(server, cb) {\n  if (!server.port && !server.clientPort) {\n    utils.invokeCallback(cb, 'leisure');\n    return;\n  }\n\n  var p = server.port || server.clientPort;\n  var host = server.host;\n  var cmd = 'netstat -tln | grep ';\n  if (!utils.isLocal(host)) {\n    cmd = 'ssh ' + host + ' ' + cmd;\n  }\n\n  exec(cmd + p, function(err, stdout, stderr) {\n    if (stdout || stderr) {\n      utils.invokeCallback(cb, 'busy');\n    } else {\n      p = server.clientPort;\n      exec(cmd + p, function(err, stdout, stderr) {\n        if (stdout || stderr) {\n          utils.invokeCallback(cb, 'busy');\n        } else {\n          utils.invokeCallback(cb, 'leisure');\n        }\n      });\n    }\n  });\n};\n\nvar parseArgs = function(msg, info, cb) {\n  var rs = {};\n  var args = msg.args;\n  for(var i =0; i<args.length; i++) {\n    if(args[i].indexOf('=') < 0) {\n      cb(new Error('Error server parameters format.'), null);\n      return;\n    }\n    var pairs = args[i].split('=');\n    var key = pairs[0];\n    if(!!info[key]) {\n      info[key] = 1;\n    }\n    rs[pairs[0]] = pairs[1];\n  }\n  return rs;\n};\n\nvar sendCronInfo = function(cron, agent, msg, info, cb) {\n  if(isReady(info) && (cron.serverId || cron.serverType)) {\n    if(!!cron.serverId) {\n      agent.notifyById(cron.serverId, module.exports.moduleId, { signal: msg.signal, cron: cron });\n    } else {\n      agent.notifyByType(cron.serverType, module.exports.moduleId, { signal: msg.signal, cron: cron });\n    }\n    process.nextTick(function() {\n      cb(null, { status: \"ok\" });\n    });\n  } else {\n    cb(new Error('Miss necessary server parameters.'), null);\n  }\n  reset(info);\n};\n\nvar startServer = function(app, msg, cb) {\n  var server = parseArgs(msg, ServerInfo, cb);\n  if(isReady(ServerInfo)) {\n    runServer(app, server, cb);\n  } else {\n    cb(new Error('Miss necessary server parameters.'), null);\n  }\n};\n\nvar runServer = function(app, server, cb) {\n  checkPort(server, function(status) {\n    if(status === 'busy') {\n      utils.invokeCallback(cb, new Error('Port occupied already, check your server to add.'));\n    } else {\n      starter.run(app, server, function(err) {\n        if(err) {\n          utils.invokeCallback(cb, new Error(err), null);\n          return;\n        }\n      });\n      process.nextTick(function() {\n        utils.invokeCallback(cb, null, { status: \"ok\" });\n      });\n    }\n  });\n};\n\nvar startCluster = function(app, msg, cb) {\n  var serverMap = {};\n  var fails = [];\n  var successFlag;\n  var serverInfo = parseArgs(msg, ClusterInfo, cb);\n  utils.loadCluster(app, serverInfo, serverMap);\n  var count = utils.size(serverMap);\n  var latch = countDownLatch.createCountDownLatch(count, function() {\n    if(!successFlag) {\n      utils.invokeCallback(cb, new Error('all servers start failed.'));\n      return;\n    }\n    utils.invokeCallback(cb, null, fails);\n  });\n\n  var start = function(server) {\n    return (function() {\n      checkPort(server, function(status) {\n        if(status === 'busy') {\n          fails.push(server);\n          latch.done();\n        } else {\n          starter.run(app, server, function(err) {\n            if(err) {\n              fails.push(server);\n              latch.done();\n            }\n          });\n          process.nextTick(function() {\n            successFlag = true;\n            latch.done();\n          });\n        }\n      });\n    })();\n  };\n  for(var key in serverMap) {\n    var server = serverMap[key];\n    start(server);\n  }\n};\n\nvar checkCluster = function(msg) {\n  var flag = false;\n  var args = msg.args;\n  for(var i=0; i < args.length; i++) {\n    if(utils.startsWith(args[i], Constants.RESERVED.CLUSTER_COUNT)) {\n      flag = true;\n    }\n  }\n  return flag;\n};\n\nvar isReady = function(info) {\n  for(var key in info) {\n    if(info[key]) {\n      return false;\n    }\n  }\n  return true;\n};\n\nvar reset = function(info) {\n  for(var key in info) {\n    info[key] = 0;\n  }\n};\n\nvar ServerInfo = {\n  host: 0,\n  port: 0,\n  id:   0,\n  serverType: 0\n};\n\nvar CronInfo = {\n  id: 0,\n  action: 0,\n  time: 0\n};\n\nvar RemoveCron = {\n  id: 0\n};\n\nvar ClusterInfo = {\n  host: 0,\n  port: 0,\n  clusterCount: 0\n};"
  },
  {
    "path": "lib/modules/masterwatcher.js",
    "content": "var logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar utils = require('../util/utils');\nvar Constants = require('../util/constants');\nvar MasterWatchdog = require('../master/watchdog');\n\nmodule.exports = function(opts, consoleService) {\n  return new Module(opts, consoleService);\n};\n\nmodule.exports.moduleId = Constants.KEYWORDS.MASTER_WATCHER;\n\nvar Module = function(opts, consoleService) {\n  this.app = opts.app;\n  this.service = consoleService;\n  this.id = this.app.getServerId();\n\n  this.watchdog = new MasterWatchdog(this.app, this.service);\n  this.service.on('register', onServerAdd.bind(null, this));\n  this.service.on('disconnect', onServerLeave.bind(null, this));\n  this.service.on('reconnect', onServerReconnect.bind(null, this));\n};\n\n// ----------------- bind methods -------------------------\n\nvar onServerAdd = function(module, record) {\n  logger.debug('masterwatcher receive add server event, with server: %j', record);\n  if(!record || record.type === 'client' || !record.serverType) {\n    return;\n  }\n  module.watchdog.addServer(record);\n};\n\nvar onServerReconnect = function(module, record) {\n  logger.debug('masterwatcher receive reconnect server event, with server: %j', record);\n  if(!record || record.type === 'client' || !record.serverType) {\n    logger.warn('onServerReconnect receive wrong message: %j', record);\n    return;\n  }\n  module.watchdog.reconnectServer(record);\n};\n\nvar onServerLeave = function(module, id, type) {\n  logger.debug('masterwatcher receive remove server event, with server: %s, type: %s', id, type);\n  if(!id) {\n    logger.warn('onServerLeave receive server id is empty.');\n    return;\n  }\n  if(type !== 'client') {\n    module.watchdog.removeServer(id);\n  }\n};\n\n// ----------------- module methods -------------------------\n\nModule.prototype.start = function(cb) {\n  utils.invokeCallback(cb);\n};\n\nModule.prototype.masterHandler = function(agent, msg, cb) {\n  if(!msg) {\n    logger.warn('masterwatcher receive empty message.');\n    return;\n  }\n  var func = masterMethods[msg.action];\n  if(!func) {\n    logger.info('masterwatcher unknown action: %j', msg.action);\n    return;\n  }\n  func(this, agent, msg, cb);\n};\n\n// ----------------- monitor request methods -------------------------\n\nvar subscribe = function(module, agent, msg, cb) {\n  if(!msg) {\n    utils.invokeCallback(cb, new Error('masterwatcher subscribe empty message.'));\n    return;\n  }\n\n  module.watchdog.subscribe(msg.id);\n  utils.invokeCallback(cb, null, module.watchdog.query());\n};\n\nvar unsubscribe = function(module, agent, msg, cb) {\n  if(!msg) {\n    utils.invokeCallback(cb, new Error('masterwatcher unsubscribe empty message.'));\n    return;\n  }\n  module.watchdog.unsubscribe(msg.id);\n  utils.invokeCallback(cb);\n};\n\nvar query = function(module, agent, msg, cb) {\n  utils.invokeCallback(cb, null, module.watchdog.query());\n};\n\nvar record = function(module, agent, msg) {\n  if(!msg) {\n    utils.invokeCallback(cb, new Error('masterwatcher record empty message.'));\n    return;\n  }\n  module.watchdog.record(msg.id);\n};\n\nvar masterMethods = {\n  'subscribe': subscribe,\n  'unsubscribe': unsubscribe,\n  'query': query,\n  'record': record\n};"
  },
  {
    "path": "lib/modules/monitorwatcher.js",
    "content": "var logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar utils = require('../util/utils');\nvar events = require('../util/events');\nvar Constants = require('../util/constants');\nvar util = require('util');\n\nmodule.exports = function(opts, consoleService) {\n  return new Module(opts, consoleService);\n};\n\nmodule.exports.moduleId = Constants.KEYWORDS.MONITOR_WATCHER;\n\nvar Module = function(opts, consoleService) {\n  this.app = opts.app;\n  this.service = consoleService;\n  this.id = this.app.getServerId();\n\n  this.app.event.on(events.START_SERVER, finishStart.bind(null, this));\n};\n\nModule.prototype.start = function(cb) {\n  subscribeRequest(this, this.service.agent, this.id, cb);\n};\n\nModule.prototype.monitorHandler = function(agent, msg, cb) {\n  if(!msg || !msg.action) {\n    return;\n  }\n  var func = monitorMethods[msg.action];\n  if(!func) {\n    logger.info('monitorwatcher unknown action: %j', msg.action);\n    return;\n  }\n  func(this, agent, msg, cb);\n};\n\n// ----------------- monitor start method -------------------------\n\nvar subscribeRequest = function(self, agent, id, cb) {\n  var msg = {action: 'subscribe', id: id};\n  agent.request(Constants.KEYWORDS.MASTER_WATCHER, msg, function(err, servers) {\n    if(err) {\n      logger.error('subscribeRequest request to master with error: %j', err.stack);\n      utils.invokeCallback(cb, err);\n    }\n    var res = [];\n    for(var id in servers) {\n      res.push(servers[id]);\n    }\n    addServers(self, res);\n    utils.invokeCallback(cb);\n  });\n};\n\n// ----------------- monitor request methods -------------------------\n\nvar addServer = function(self, agent, msg, cb) {\n  logger.debug('[%s] receive addServer signal: %j', self.app.serverId, msg);\n  if(!msg || !msg.server) {\n    logger.warn('monitorwatcher addServer receive empty message: %j', msg);\n    utils.invokeCallback(cb, Constants.SIGNAL.FAIL);\n    return;\n  }\n  addServers(self, [msg.server]);\n  utils.invokeCallback(cb, Constants.SIGNAL.OK);\n};\n\nvar removeServer = function(self, agent, msg, cb) {\n  logger.debug('%s receive removeServer signal: %j', self.app.serverId, msg);\n  if(!msg || !msg.id) {\n    logger.warn('monitorwatcher removeServer receive empty message: %j', msg);\n    utils.invokeCallback(cb, Constants.SIGNAL.FAIL);\n    return;\n  }\n  removeServers(self, [msg.id]);\n  utils.invokeCallback(cb, Constants.SIGNAL.OK);\n};\n\nvar replaceServer = function(self, agent, msg, cb) {\n  logger.debug('%s receive replaceServer signal: %j', self.app.serverId, msg);\n  if(!msg || !msg.servers) {\n    logger.warn('monitorwatcher replaceServer receive empty message: %j', msg);\n    utils.invokeCallback(cb, Constants.SIGNAL.FAIL);\n    return;\n  }\n  replaceServers(self, msg.servers);\n  utils.invokeCallback(cb, Constants.SIGNAL.OK);\n};\n\nvar startOver = function(self, agent, msg, cb) {\n  var fun = self.app.lifecycleCbs[Constants.LIFECYCLE.AFTER_STARTALL];\n  if(!!fun) {\n    fun.call(null, self.app);\n  }\n  self.app.event.emit(events.START_ALL);\n  utils.invokeCallback(cb, Constants.SIGNAL.OK);\n};\n\n// ----------------- common methods -------------------------\n\nvar addServers = function(self, servers) {\n  if(!servers || !servers.length) {\n    return;\n  }\n  self.app.addServers(servers);\n};\n\nvar removeServers = function(self, ids) {\n  if(!ids || !ids.length) {\n    return;\n  }\n  self.app.removeServers(ids);\n};\n\nvar replaceServers = function(self, servers) {\n  self.app.replaceServers(servers);\n};\n\n// ----------------- bind methods -------------------------\n\nvar finishStart = function(self, id) {\n  var msg = {action: 'record', id: id};\n  self.service.agent.notify(Constants.KEYWORDS.MASTER_WATCHER, msg);\n};\n\nvar monitorMethods = {\n  'addServer': addServer,\n  'removeServer': removeServer,\n  'replaceServer': replaceServer,\n  'startOver': startOver\n};\n"
  },
  {
    "path": "lib/monitor/monitor.js",
    "content": "/**\n * Component for monitor.\n * Load and start monitor client.\n */\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar admin = require('pomelo-admin');\nvar moduleUtil = require('../util/moduleUtil');\nvar utils = require('../util/utils');\nvar Constants = require('../util/constants');\n\nvar Monitor = function(app, opts) {\n  opts = opts || {};\n  this.app = app;\n  this.serverInfo = app.getCurServer();\n  this.masterInfo = app.getMaster();\n  this.modules = [];\n  this.closeWatcher = opts.closeWatcher;\n\n  this.monitorConsole = admin.createMonitorConsole({\n    id: this.serverInfo.id,\n    type: this.app.getServerType(),\n    host: this.masterInfo.host,\n    port: this.masterInfo.port,\n    info: this.serverInfo,\n    env: this.app.get(Constants.RESERVED.ENV),\n    authServer: app.get('adminAuthServerMonitor') // auth server function\n  });\n};\n\nmodule.exports = Monitor;\n\nMonitor.prototype.start = function(cb) {\n  moduleUtil.registerDefaultModules(false, this.app, this.closeWatcher);\n  this.startConsole(cb);\n};\n\nMonitor.prototype.startConsole = function(cb) {\n  moduleUtil.loadModules(this, this.monitorConsole);\n\n  var self = this;\n  this.monitorConsole.start(function(err) {\n    if (err) {\n      utils.invokeCallback(cb, err);\n      return;\n    }\n    moduleUtil.startModules(self.modules, function(err) {\n      utils.invokeCallback(cb, err);\n      return;\n    });\n  });\n\n  this.monitorConsole.on('error', function(err) {\n    if(!!err) {\n      logger.error('monitorConsole encounters with error: %j', err.stack);\n      return;\n    }\n  });\n};\n\nMonitor.prototype.stop = function(cb) {\n  this.monitorConsole.stop();\n  this.modules = [];\n  process.nextTick(function() {\n    utils.invokeCallback(cb);\n  });\n};\n\n// monitor reconnect to master\nMonitor.prototype.reconnect = function(masterInfo) {\n  var self = this;\n  this.stop(function() {\n    self.monitorConsole = admin.createMonitorConsole({\n      id: self.serverInfo.id,\n      type: self.app.getServerType(),\n      host: masterInfo.host,\n      port: masterInfo.port,\n      info: self.serverInfo,\n      env: self.app.get(Constants.RESERVED.ENV)\n    });\n    self.startConsole(function() {\n      logger.info('restart modules for server : %j finish.', self.app.serverId);\n    });\n  });\n};"
  },
  {
    "path": "lib/pomelo.js",
    "content": "/*!\n * Pomelo\n * Copyright(c) 2012 xiechengchao <xiecc@163.com>\n * MIT Licensed\n */\n\n/**\n * Module dependencies.\n */\nvar fs = require('fs');\nvar path = require('path');\nvar application = require('./application');\nvar Package = require('../package');\n\n/**\n * Expose `createApplication()`.\n *\n * @module\n */\n\nvar Pomelo = module.exports = {};\n\n/**\n * Framework version.\n */\n\nPomelo.version = Package.version;\n\n/**\n * Event definitions that would be emitted by app.event\n */\nPomelo.events = require('./util/events');\n\n/**\n * auto loaded components\n */\nPomelo.components = {};\n\n/**\n * auto loaded filters\n */\nPomelo.filters = {};\n\n/**\n * auto loaded rpc filters\n */\nPomelo.rpcFilters = {};\n\n/**\n * connectors\n */\nPomelo.connectors = {};\nPomelo.connectors.__defineGetter__('sioconnector', load.bind(null, './connectors/sioconnector'));\nPomelo.connectors.__defineGetter__('hybridconnector', load.bind(null, './connectors/hybridconnector'));\nPomelo.connectors.__defineGetter__('udpconnector', load.bind(null, './connectors/udpconnector'));\nPomelo.connectors.__defineGetter__('mqttconnector', load.bind(null, './connectors/mqttconnector'));\n\n/**\n * pushSchedulers\n */\nPomelo.pushSchedulers = {};\nPomelo.pushSchedulers.__defineGetter__('direct', load.bind(null, './pushSchedulers/direct'));\nPomelo.pushSchedulers.__defineGetter__('buffer', load.bind(null, './pushSchedulers/buffer'));\n\nvar self = this;\n\n/**\n * Create an pomelo application.\n *\n * @return {Application}\n * @memberOf Pomelo\n * @api public\n */\nPomelo.createApp = function (opts) {\n  var app = application;\n  app.init(opts);\n  self.app = app;\n  return app;\n};\n\n/**\n * Get application\n */\nObject.defineProperty(Pomelo, 'app', {\n  get:function () {\n    return self.app;\n  }\n});\n\n/**\n * Auto-load bundled components with getters.\n */\nfs.readdirSync(__dirname + '/components').forEach(function (filename) {\n  if (!/\\.js$/.test(filename)) {\n    return;\n  }\n  var name = path.basename(filename, '.js');\n  var _load = load.bind(null, './components/', name);\n  \n  Pomelo.components.__defineGetter__(name, _load);\n  Pomelo.__defineGetter__(name, _load);\n});\n\nfs.readdirSync(__dirname + '/filters/handler').forEach(function (filename) {\n  if (!/\\.js$/.test(filename)) {\n    return;\n  }\n  var name = path.basename(filename, '.js');\n  var _load = load.bind(null, './filters/handler/', name);\n  \n  Pomelo.filters.__defineGetter__(name, _load);\n  Pomelo.__defineGetter__(name, _load);\n});\n\nfs.readdirSync(__dirname + '/filters/rpc').forEach(function (filename) {\n  if (!/\\.js$/.test(filename)) {\n    return;\n  }\n  var name = path.basename(filename, '.js');\n  var _load = load.bind(null, './filters/rpc/', name);\n  \n  Pomelo.rpcFilters.__defineGetter__(name, _load);\n});\n\nfunction load(path, name) {\n  if (name) {\n    return require(path + name);\n  }\n  return require(path);\n}\n"
  },
  {
    "path": "lib/pushSchedulers/buffer.js",
    "content": "var utils = require('../util/utils');\nvar DEFAULT_FLUSH_INTERVAL = 20;\n\nvar Service = function(app, opts) {\n  if (!(this instanceof Service)) {\n    return new Service(app, opts);\n  }\n\n  opts = opts || {};\n  this.app = app;\n  this.flushInterval = opts.flushInterval || DEFAULT_FLUSH_INTERVAL;\n  this.sessions = {};   // sid -> msg queue\n  this.tid = null;\n};\n\nmodule.exports = Service;\n\nService.prototype.start = function(cb) {\n  this.tid = setInterval(flush.bind(null, this), this.flushInterval);\n  process.nextTick(function() {\n    utils.invokeCallback(cb);\n  });\n};\n\nService.prototype.stop = function(force, cb) {\n  if(this.tid) {\n    clearInterval(this.tid);\n    this.tid = null;\n  }\n  process.nextTick(function() {\n    utils.invokeCallback(cb);\n  });\n};\n\nService.prototype.schedule = function(reqId, route, msg, recvs, opts, cb) {\n  opts = opts || {};\n  if(opts.type === 'broadcast') {\n    doBroadcast(this, msg, opts.userOptions);\n  } else {\n    doBatchPush(this, msg, recvs);\n  }\n\n  process.nextTick(function() {\n    utils.invokeCallback(cb);\n  });\n};\n\nvar doBroadcast = function(self, msg, opts) {\n  var channelService = self.app.get('channelService');\n  var sessionService = self.app.get('sessionService');\n\n  if(opts.binded) {\n    sessionService.forEachBindedSession(function(session) {\n      if(channelService.broadcastFilter &&\n         !channelService.broadcastFilter(session, msg, opts.filterParam)) {\n        return;\n      }\n\n      enqueue(self, session, msg);\n    });\n  } else {\n    sessionService.forEachSession(function(session) {\n      if(channelService.broadcastFilter &&\n          !channelService.broadcastFilter(session, msg, opts.filterParam)) {\n        return;\n      }\n\n      enqueue(self, session, msg);\n    });\n  }\n};\n\nvar doBatchPush = function(self, msg, recvs) {\n  var sessionService = self.app.get('sessionService');\n  var session;\n  for(var i=0, l=recvs.length; i<l; i++) {\n    session = sessionService.get(recvs[i]);\n    if(session) {\n      enqueue(self, session, msg);\n    }\n  }\n};\n\nvar enqueue = function(self, session, msg) {\n  var queue = self.sessions[session.id];\n  if(!queue) {\n    queue = self.sessions[session.id] = [];\n    session.once('closed', onClose.bind(null, self));\n  }\n\n  queue.push(msg);\n};\n\nvar onClose = function(self, session) {\n  delete self.sessions[session.id];\n};\n\nvar flush = function(self) {\n  var sessionService = self.app.get('sessionService');\n  var queue, session;\n  for(var sid in self.sessions) {\n    session = sessionService.get(sid);\n    if(!session) {\n      continue;\n    }\n\n    queue = self.sessions[sid];\n    if(!queue || queue.length === 0) {\n      continue;\n    }\n\n    session.sendBatch(queue);\n    self.sessions[sid] = [];\n  }\n};\n"
  },
  {
    "path": "lib/pushSchedulers/direct.js",
    "content": "var utils = require('../util/utils');\n\nvar Service = function(app, opts) {\n  if (!(this instanceof Service)) {\n    return new Service(app, opts);\n  }\n\n  opts = opts || {};\n  this.app = app;\n};\n\nmodule.exports = Service;\n\nService.prototype.schedule = function(reqId, route, msg, recvs, opts, cb) {\n  opts = opts || {};\n  if(opts.type === 'broadcast') {\n    doBroadcast(this, msg, opts.userOptions);\n  } else {\n    doBatchPush(this, msg, recvs);\n  }\n\n  if(cb) {\n    process.nextTick(function() {\n      utils.invokeCallback(cb);\n    });\n  }\n};\n\nvar doBroadcast = function(self, msg, opts) {\n  var channelService = self.app.get('channelService');\n  var sessionService = self.app.get('sessionService');\n\n  if(opts.binded) {\n    sessionService.forEachBindedSession(function(session) {\n      if(channelService.broadcastFilter &&\n         !channelService.broadcastFilter(session, msg, opts.filterParam)) {\n        return;\n      }\n\n      sessionService.sendMessageByUid(session.uid, msg);\n    });\n  } else {\n    sessionService.forEachSession(function(session) {\n      if(channelService.broadcastFilter &&\n         !channelService.broadcastFilter(session, msg, opts.filterParam)) {\n        return;\n      }\n\n      sessionService.sendMessage(session.id, msg);\n    });\n  }\n};\n\nvar doBatchPush = function(self, msg, recvs) {\n  var sessionService = self.app.get('sessionService');\n  for(var i=0, l=recvs.length; i<l; i++) {\n    sessionService.sendMessage(recvs[i], msg);\n  }\n};\n"
  },
  {
    "path": "lib/server/server.js",
    "content": "/**\n * Implementation of server component.\n * Init and start server instance.\n */\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar fs = require('fs');\nvar path = require('path');\nvar pathUtil = require('../util/pathUtil');\nvar Loader = require('pomelo-loader');\nvar utils = require('../util/utils');\nvar schedule = require('pomelo-scheduler');\nvar events = require('../util/events');\nvar Constants = require('../util/constants');\nvar FilterService = require('../common/service/filterService');\nvar HandlerService = require('../common/service/handlerService');\n\nvar ST_INITED = 0;    // server inited\nvar ST_STARTED = 1;   // server started\nvar ST_STOPED = 2;    // server stoped\n\n/**\n * Server factory function.\n *\n * @param {Object} app  current application context\n * @return {Object} erver instance\n */\nmodule.exports.create = function(app, opts) {\n  return new Server(app, opts);\n};\n\nvar Server = function (app, opts) {\n  this.opts = opts || {};\n  this.app = app;\n  this.globalFilterService = null;\n  this.filterService = null;\n  this.handlerService = null;\n  this.crons = [];\n  this.jobs = {};\n  this.state = ST_INITED;\n\n  app.event.on(events.ADD_CRONS, this.addCrons.bind(this));\n  app.event.on(events.REMOVE_CRONS, this.removeCrons.bind(this));\n};\n\nvar pro = Server.prototype;\n\n/**\n * Server lifecycle callback\n */\npro.start = function() {\n  if(this.state > ST_INITED) {\n    return;\n  }\n\n  this.globalFilterService = initFilter(true, this.app);\n  this.filterService = initFilter(false, this.app);\n  this.handlerService = initHandler(this.app, this.opts);\n  this.cronHandlers = loadCronHandlers(this.app);\n  loadCrons(this, this.app);\n  this.state = ST_STARTED;\n};\n\npro.afterStart = function() {\n  scheduleCrons(this, this.crons);\n};\n\n/**\n * Stop server\n */\npro.stop = function() {\n  this.state = ST_STOPED;\n};\n\n/**\n * Global handler.\n *\n * @param  {Object} msg request message\n * @param  {Object} session session object\n * @param  {Callback} callback function \n */\npro.globalHandle = function(msg, session, cb) {\n  if(this.state !== ST_STARTED) {\n    utils.invokeCallback(cb, new Error('server not started'));\n    return;\n  }\n\n  var routeRecord = parseRoute(msg.route);\n  if(!routeRecord) {\n    utils.invokeCallback(cb, new Error('meet unknown route message %j', msg.route));\n    return;\n  }\n  if (routeRecord.method === 'constructor') {\n      logger.warn('attack session:', session, msg);\n      this.app.sessionService.kickBySessionId(session.id, 'attack');\n      return;\n  }\n  var self = this;\n  var dispatch = function(err, resp, opts) {\n    if(err) {\n      handleError(true, self, err, msg, session, resp, opts, function(err, resp, opts) {\n        response(true, self, err, msg, session, resp, opts, cb);\n      });\n      return;\n    }\n\n    if(self.app.getServerType() !== routeRecord.serverType) {\n      doForward(self.app, msg, session, routeRecord, function(err, resp, opts) {\n        response(true, self, err, msg, session, resp, opts, cb);\n      });\n    } else {\n      doHandle(self, msg, session, routeRecord, function(err, resp, opts) {\n        response(true, self, err, msg, session, resp, opts, cb);\n      });\n    }\n  };\n  beforeFilter(true, self, msg, session, dispatch);\n};\n\n/**\n * Handle request\n */\npro.handle = function(msg, session, cb) {\n   if(this.state !== ST_STARTED) {\n    cb(new Error('server not started'));\n    return;\n  }\n\n  var routeRecord = parseRoute(msg.route);\n  doHandle(this, msg, session, routeRecord, cb);\n};\n\n/**\n * Add crons at runtime.\n *\n * @param {Array} crons would be added in application\n */\npro.addCrons = function(crons) {\n  this.cronHandlers = loadCronHandlers(this.app);\n  for(var i=0, l=crons.length; i<l; i++) {\n    var cron = crons[i];\n    checkAndAdd(cron, this.crons, this);\n  }\n  scheduleCrons(this, crons);\n};\n\n/**\n * Remove crons at runtime.\n *\n * @param {Array} crons would be removed in application\n */\npro.removeCrons = function(crons) {\n  for(var i=0, l=crons.length; i<l; i++) {\n    var cron = crons[i];\n    var id = parseInt(cron.id);\n    if(!!this.jobs[id]) {\n      schedule.cancelJob(this.jobs[id]);\n    } else {\n      logger.warn('cron is not in application: %j', cron);\n    }\n  }\n};\n\nvar initFilter = function(isGlobal, app) {\n  var service = new FilterService();\n  var befores, afters;\n\n  if(isGlobal) {\n    befores = app.get(Constants.KEYWORDS.GLOBAL_BEFORE_FILTER);\n    afters = app.get(Constants.KEYWORDS.GLOBAL_AFTER_FILTER);\n  } else {\n    befores = app.get(Constants.KEYWORDS.BEFORE_FILTER);\n    afters = app.get(Constants.KEYWORDS.AFTER_FILTER);\n  }\n\n  var i, l;\n  if(befores) {\n    for(i=0, l=befores.length; i<l; i++) {\n      service.before(befores[i]);\n    }\n  }\n\n  if(afters) {\n    for(i=0, l=afters.length; i<l; i++) {\n      service.after(afters[i]);\n    }\n  }\n\n  return service;\n};\n\nvar initHandler = function(app, opts) {\n  return new HandlerService(app, opts);\n};\n\n/**\n * Load cron handlers from current application\n */\nvar loadCronHandlers = function(app) {\n  var p = pathUtil.getCronPath(app.getBase(), app.getServerType());\n  if(p) {\n    return Loader.load(p, app);\n  }\n};\n\n/**\n * Load crons from configure file\n */\nvar loadCrons = function(server, app) {\n  var env = app.get(Constants.RESERVED.ENV);\n  var p = path.join(app.getBase(), Constants.FILEPATH.CRON);\n  if(!fs.existsSync(p)) {\n    p = path.join(app.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.CRON));\n    if (!fs.existsSync(p)) {\n      return;\n    }\n  }\n  app.loadConfigBaseApp(Constants.RESERVED.CRONS, Constants.FILEPATH.CRON);\n  var crons = app.get(Constants.RESERVED.CRONS);\n  for(var serverType in crons) {\n    if(app.serverType === serverType) {\n      var list = crons[serverType];\n      for(var i = 0; i<list.length; i++) {\n        if(!list[i].serverId) {\n          checkAndAdd(list[i], server.crons, server);\n        } else {\n          if(app.serverId === list[i].serverId) {\n            checkAndAdd(list[i], server.crons, server);\n          }\n        }\n      }\n    }\n  }\n};\n\n/**\n * Fire before filter chain if any\n */\nvar beforeFilter = function(isGlobal, server, msg, session, cb) {\n  var fm;\n  if(isGlobal) {\n    fm = server.globalFilterService;\n  } else {\n    fm = server.filterService;\n  }\n  if(fm) {\n    fm.beforeFilter(msg, session, cb);\n  } else {\n    utils.invokeCallback(cb);\n  }\n};\n\n/**\n * Fire after filter chain if have\n */\nvar afterFilter = function(isGlobal, server, err, msg, session, resp, opts, cb) {\n  var fm;\n  if(isGlobal) {\n    fm = server.globalFilterService;\n  } else {\n    fm = server.filterService;\n  }\n  if(fm) {\n    if(isGlobal) {\n      fm.afterFilter(err, msg, session, resp, function() {\n        // do nothing\n      });\n    } else {\n      fm.afterFilter(err, msg, session, resp, function(err) {\n        cb(err, resp, opts);\n      });\n    }\n  }\n};\n\n/**\n * pass err to the global error handler if specified\n */\nvar handleError = function(isGlobal, server, err, msg, session, resp, opts, cb) {\n  var handler;\n  if(isGlobal) {\n    handler = server.app.get(Constants.RESERVED.GLOBAL_ERROR_HANDLER);\n  } else {\n    handler = server.app.get(Constants.RESERVED.ERROR_HANDLER);\n  }\n  if(!handler) {\n    logger.debug('no default error handler to resolve unknown exception. ' + err.stack);\n    utils.invokeCallback(cb, err, resp, opts);\n  } else {\n    if(handler.length === 5) {\n      handler(err, msg, resp, session, cb);\n    } else {\n       handler(err, msg, resp, session, opts, cb);     \n    }\n  }\n};\n\n/**\n * Send response to client and fire after filter chain if any.\n */\n\nvar response = function(isGlobal, server, err, msg, session, resp, opts, cb) {\n  if(isGlobal) {\n    cb(err, resp, opts);\n    // after filter should not interfere response\n    afterFilter(isGlobal, server, err, msg, session, resp, opts, cb);\n  } else {\n    afterFilter(isGlobal, server, err, msg, session, resp, opts, cb);\n  }\n};\n\n/**\n * Parse route string.\n *\n * @param  {String} route route string, such as: serverName.handlerName.methodName\n * @return {Object}       parse result object or null for illeagle route string\n */\nvar parseRoute = function(route) {\n  if(!route) {\n    return null;\n  }\n  var ts = route.split('.');\n  if(ts.length !== 3) {\n    return null;\n  }\n\n  return {\n    route: route,\n    serverType: ts[0],\n    handler: ts[1],\n    method: ts[2]\n  };\n};\n\nvar doForward = function(app, msg, session, routeRecord, cb) {\n  var finished = false;\n  //should route to other servers\n  try {\n    app.sysrpc[routeRecord.serverType].msgRemote.forwardMessage(\n    // app.sysrpc[routeRecord.serverType].msgRemote.forwardMessage2(\n      session,\n      msg,\n      // msg.oldRoute || msg.route,\n      // msg.body,\n      // msg.aesPassword,\n      // msg.compressGzip,\n      session.export(),\n      function(err, resp, opts) {\n        if(err) {\n          logger.error('fail to process remote message:' + err.stack);\n        }\n        finished = true;\n        utils.invokeCallback(cb, err, resp, opts);\n      }\n    );\n  } catch(err) {\n    if(!finished) {\n      logger.error('fail to forward message:' + err.stack);\n      utils.invokeCallback(cb, err);\n    }\n  }\n};\n\nvar doHandle = function(server, msg, session, routeRecord, cb) {\n  var originMsg = msg;\n  msg = msg.body || {};\n  msg.__route__ = originMsg.route;\n\n  var self = server;\n\n  var handle = function(err, resp, opts) {\n    if(err) {\n      // error from before filter\n      handleError(false, self, err, msg, session, resp, opts, function(err, resp, opts) {\n        response(false, self, err, msg, session, resp, opts, cb);\n      });\n      return;\n    }\n\n    self.handlerService.handle(routeRecord, msg, session, function(err, resp, opts) {\n      if(err) {\n        //error from handler\n        handleError(false, self, err, msg, session, resp, opts, function(err, resp, opts) {\n          response(false, self, err, msg, session, resp, opts, cb);\n        });\n        return;\n      }\n\n      response(false, self, err, msg, session, resp, opts, cb);\n    });\n  };  //end of handle\n\n  beforeFilter(false, server, msg, session, handle);\n};\n\n/**\n * Schedule crons\n */\nvar scheduleCrons = function(server, crons) {\n  var handlers = server.cronHandlers;\n  for(var i = 0; i<crons.length; i++) {\n    var cronInfo = crons[i];\n    var time = cronInfo.time;\n    var action = cronInfo.action;\n    var jobId = cronInfo.id;\n\n    if(!time || !action || !jobId) {\n      logger.error('cron miss necessary parameters: %j', cronInfo);\n      continue;\n    }\n\n    if(action.indexOf('.') < 0) {\n      logger.error('cron action is error format: %j', cronInfo);\n      continue;\n    }\n    \n    var cron = action.split('.')[0];\n    var job = action.split('.')[1];\n    var handler = handlers[cron];\n    \n    if(!handler) {\n      logger.error('could not find cron: %j', cronInfo);\n      continue;\n    }\n      \n    if(typeof handler[job] !== 'function') {\n      logger.error('could not find cron job: %j, %s', cronInfo, job);\n      continue;\n    }\n      \n    var id = schedule.scheduleJob(time, handler[job].bind(handler));\n    server.jobs[jobId] = id;\n  }\n};\n\n/**\n * If cron is not in crons then put it in the array.\n */\nvar checkAndAdd = function(cron, crons, server) {\n  if(!containCron(cron.id, crons)) {\n    server.crons.push(cron);\n  } else {\n    logger.warn('cron is duplicated: %j', cron);\n  }\n};\n\n/**\n * Check if cron is in crons.\n */\nvar containCron = function(id, crons) {\n  for(var i=0, l=crons.length; i<l; i++) {\n    if(id === crons[i].id) {\n      return true;\n    }\n  }\n  return false;\n};\n"
  },
  {
    "path": "lib/util/appUtil.js",
    "content": "var async = require('async');\nvar log = require('./log');\nvar utils = require('./utils');\nvar path = require('path');\nvar fs = require('fs');\nvar Constants = require('./constants');\nvar starter = require('../master/starter');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\n\n/**\n * Initialize application configuration.\n */\nmodule.exports.defaultConfiguration = function(app) {\n  var args = parseArgs(process.argv);\n  setupEnv(app, args);\n  loadMaster(app);\n  loadServers(app);\n  processArgs(app, args);\n  configLogger(app);\n  loadLifecycle(app);\n};\n\n/**\n * Start servers by type.\n */\nmodule.exports.startByType = function(app, cb) {\n  if(!!app.startId) {\n    if(app.startId === Constants.RESERVED.MASTER) {\n      utils.invokeCallback(cb);\n    } else {\n      starter.runServers(app);\n    }\n  } else {\n    if(!!app.type && app.type !== Constants.RESERVED.ALL && app.type !== Constants.RESERVED.MASTER) {\n      starter.runServers(app);\n    } else {\n      utils.invokeCallback(cb);\n    }\n  }\n};\n\n/**\n * Load default components for application.\n */\nmodule.exports.loadDefaultComponents = function(app) {\n  var pomelo = require('../pomelo');\n  // load system default components\n  if (app.serverType === Constants.RESERVED.MASTER) {\n    app.load(pomelo.master, app.get('masterConfig'));\n  } else {\n    app.load(pomelo.proxy, app.get('proxyConfig'));\n    if (app.getCurServer().port) {\n      app.load(pomelo.remote, app.get('remoteConfig'));\n    }\n    if (app.isFrontend()) {\n      app.load(pomelo.connection, app.get('connectionConfig'));\n      app.load(pomelo.connector, app.get('connectorConfig'));\n      app.load(pomelo.session, app.get('sessionConfig'));\n      // compatible for schedulerConfig\n      if(app.get('schedulerConfig')) {\n        app.load(pomelo.pushScheduler, app.get('schedulerConfig'));\n      } else {\n        app.load(pomelo.pushScheduler, app.get('pushSchedulerConfig'));\n      }\n    }\n    app.load(pomelo.backendSession, app.get('backendSessionConfig'));\n    app.load(pomelo.channel, app.get('channelConfig'));\n    app.load(pomelo.server, app.get('serverConfig'));\n  }\n  app.load(pomelo.monitor, app.get('monitorConfig'));\n};\n\n/**\n * Stop components.\n *\n * @param  {Array}  comps component list\n * @param  {Number}   index current component index\n * @param  {Boolean}  force whether stop component immediately\n * @param  {Function} cb\n */\nmodule.exports.stopComps = function(comps, index, force, cb) {\n  if (index >= comps.length) {\n    utils.invokeCallback(cb);\n    return;\n  }\n  var comp = comps[index];\n  if (typeof comp.stop === 'function') {\n    comp.stop(force, function() {\n      // ignore any error\n      module.exports.stopComps(comps, index + 1, force, cb);\n    });\n  } else {\n    module.exports.stopComps(comps, index + 1, force, cb);\n  }\n};\n\n/**\n * Apply command to loaded components.\n * This method would invoke the component {method} in series.\n * Any component {method} return err, it would return err directly.\n *\n * @param {Array} comps loaded component list\n * @param {String} method component lifecycle method name, such as: start, stop\n * @param {Function} cb\n */\nmodule.exports.optComponents = function(comps, method, cb) {\n  var i = 0;\n  async.forEachSeries(comps, function(comp, done) {\n    i++;\n    if (typeof comp[method] === 'function') {\n      comp[method](done);\n    } else {\n      done();\n    }\n  }, function(err) {\n    if (err) {\n      if(typeof err === 'string') {\n        logger.error('fail to operate component, method: %s, err: %j', method, err);\n      } else {\n        logger.error('fail to operate component, method: %s, err: %j',  method, err.stack);\n      }\n    }\n    utils.invokeCallback(cb, err);\n  });\n};\n\n/**\n * Load server info from config/servers.json.\n */\nvar loadServers = function(app) {\n  app.loadConfigBaseApp(Constants.RESERVED.SERVERS, Constants.FILEPATH.SERVER);\n  var servers = app.get(Constants.RESERVED.SERVERS);\n  var serverMap = {}, slist, i, l, server;\n  for (var serverType in servers) {\n    slist = servers[serverType];\n    for (i = 0, l = slist.length; i < l; i++) {\n      server = slist[i];\n      server.serverType = serverType;\n      if(server[Constants.RESERVED.CLUSTER_COUNT]) {\n        utils.loadCluster(app, server, serverMap);\n        continue;\n      }\n      serverMap[server.id] = server;\n      if (server.wsPort) {\n        logger.warn('wsPort is deprecated, use clientPort in frontend server instead, server: %j', server);\n      }\n    }\n  }\n  app.set(Constants.KEYWORDS.SERVER_MAP, serverMap);\n};\n\n/**\n * Load master info from config/master.json.\n */\nvar loadMaster = function(app) {\n  app.loadConfigBaseApp(Constants.RESERVED.MASTER, Constants.FILEPATH.MASTER);\n  app.master = app.get(Constants.RESERVED.MASTER);\n};\n\n/**\n * Process server start command\n */\nvar processArgs = function(app, args) {\n  var serverType = args.serverType || Constants.RESERVED.MASTER;\n  var serverId = args.id || app.getMaster().id;\n  var mode = args.mode || Constants.RESERVED.CLUSTER;\n  var masterha = args.masterha || 'false';\n  var type = args.type || Constants.RESERVED.ALL;\n  var startId = args.startId;\n\n  app.set(Constants.RESERVED.MAIN, args.main, true);\n  app.set(Constants.RESERVED.SERVER_TYPE, serverType, true);\n  app.set(Constants.RESERVED.SERVER_ID, serverId, true);\n  app.set(Constants.RESERVED.MODE, mode, true);\n  app.set(Constants.RESERVED.TYPE, type, true);\n  if(!!startId) {\n    app.set(Constants.RESERVED.STARTID, startId, true);\n  }\n\n  if (masterha === 'true') {\n    app.master = args;\n    app.set(Constants.RESERVED.CURRENT_SERVER, args, true);\n  } else if (serverType !== Constants.RESERVED.MASTER) {\n    app.set(Constants.RESERVED.CURRENT_SERVER, args, true);\n  } else {\n    app.set(Constants.RESERVED.CURRENT_SERVER, app.getMaster(), true);\n  }\n};\n\n/**\n * Setup enviroment.\n */\nvar setupEnv = function(app, args) {\n  app.set(Constants.RESERVED.ENV, args.env || process.env.NODE_ENV || Constants.RESERVED.ENV_DEV, true);\n};\n\n/**\n * Configure custom logger.\n */\nvar configLogger = function(app) {\n  if (process.env.POMELO_LOGGER !== 'off') {\n    var env = app.get(Constants.RESERVED.ENV);\n    var originPath = path.join(app.getBase(), Constants.FILEPATH.LOG);\n    var presentPath = path.join(app.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.LOG));\n    if(fs.existsSync(originPath)) {\n      log.configure(app, originPath);\n    } else if(fs.existsSync(presentPath)) {\n      log.configure(app, presentPath);\n    } else {\n      logger.error('logger file path configuration is error.');\n    }\n  }\n};\n\n/**\n * Parse command line arguments.\n *\n * @param args command line arguments\n *\n * @return Object argsMap map of arguments\n */\nvar parseArgs = function(args) {\n  var argsMap = {};\n  var mainPos = 1;\n\n  while (args[mainPos].indexOf('--') > 0) {\n    mainPos++;\n  }\n  argsMap.main = args[mainPos];\n\n  for (var i = (mainPos + 1); i < args.length; i++) {\n    var arg = args[i];\n    var sep = arg.indexOf('=');\n    var key = arg.slice(0, sep);\n    var value = arg.slice(sep + 1);\n    if (!isNaN(Number(value)) && (value.indexOf('.') < 0)) {\n      value = Number(value);\n    }\n    argsMap[key] = value;\n  }\n\n  return argsMap;\n};\n\n/**\n * Load lifecycle file.\n *\n */\nvar loadLifecycle = function(app) {\n  var filePath = path.join(app.getBase(), Constants.FILEPATH.SERVER_DIR, app.serverType, Constants.FILEPATH.LIFECYCLE);\n  if(!fs.existsSync(filePath)) {\n    return;\n  }\n  var lifecycle = require(filePath);\n  for(var key in lifecycle) {\n    if(typeof lifecycle[key] === 'function') {\n      app.lifecycleCbs[key] = lifecycle[key];\n    } else {\n      logger.warn('lifecycle.js in %s is error format.', filePath);\n    }\n  }\n};\n"
  },
  {
    "path": "lib/util/constants.js",
    "content": "module.exports = {\n  KEYWORDS: {\n    BEFORE_FILTER: '__befores__',\n    AFTER_FILTER: '__afters__',\n    GLOBAL_BEFORE_FILTER: '__globalBefores__',\n    GLOBAL_AFTER_FILTER: '__globalAfters__',\n    ROUTE: '__routes__',\n    BEFORE_STOP_HOOK: '__beforeStopHook__',\n    MODULE: '__modules__',\n    SERVER_MAP: '__serverMap__',\n    RPC_BEFORE_FILTER: '__rpcBefores__',\n    RPC_AFTER_FILTER: '__rpcAfters__',\n    MASTER_WATCHER: '__masterwatcher__',\n    MONITOR_WATCHER: '__monitorwatcher__'\n },\n\n  FILEPATH: {\n    MASTER: '/config/master.json',\n    SERVER: '/config/servers.json',\n    CRON: '/config/crons.json',\n    LOG: '/config/log4js.json',\n    SERVER_PROTOS: '/config/serverProtos.json',\n    CLIENT_PROTOS: '/config/clientProtos.json',\n    MASTER_HA: '/config/masterha.json',\n    LIFECYCLE: '/lifecycle.js',\n    SERVER_DIR: '/app/servers/',\n    CONFIG_DIR: '/config'\n  },\n\n  DIR: {\n    HANDLER: 'handler',\n    REMOTE: 'remote',\n    CRON: 'cron',\n    LOG: 'logs',\n    SCRIPT: 'scripts',\n    EVENT: 'events',\n    COMPONENT: 'components'\n  },\n\n  RESERVED: {\n    BASE: 'base',\n    MAIN: 'main',\n    MASTER: 'master',\n    SERVERS: 'servers',\n    ENV: 'env',\n    CPU: 'cpu',\n    ENV_DEV: 'development',\n    ENV_PRO: 'production',\n    ALL: 'all',\n    SERVER_TYPE: 'serverType',\n    SERVER_ID: 'serverId',\n    CURRENT_SERVER: 'curServer',\n    MODE: 'mode',\n    TYPE: 'type',\n    CLUSTER: 'clusters',\n    STAND_ALONE: 'stand-alone',\n    START: 'start',\n    AFTER_START: 'afterStart',\n    CRONS: 'crons',\n    ERROR_HANDLER: 'errorHandler',\n    GLOBAL_ERROR_HANDLER: 'globalErrorHandler',\n    AUTO_RESTART: 'auto-restart',\n    RESTART_FORCE: 'restart-force',\n    CLUSTER_COUNT: 'clusterCount',\n    CLUSTER_PREFIX: 'cluster-server-',\n    CLUSTER_SIGNAL: '++',\n    RPC_ERROR_HANDLER: 'rpcErrorHandler',\n    SERVER: 'server',\n    CLIENT: 'client',\n    STARTID: 'startId',\n    STOP_SERVERS: 'stop_servers',\n    SSH_CONFIG_PARAMS: 'ssh_config_params'\n  },\n\n  COMMAND: {\n    TASKSET: 'taskset',\n    KILL: 'kill',\n    TASKKILL: 'taskkill',\n    SSH: 'ssh'\n  },\n\n  PLATFORM: {\n    WIN: 'win32',\n    LINUX: 'linux'\n  },\n\n  LIFECYCLE: {\n    BEFORE_STARTUP: 'beforeStartup',\n    BEFORE_SHUTDOWN: 'beforeShutdown',\n    AFTER_STARTUP: 'afterStartup',\n    AFTER_STARTALL: 'afterStartAll'\n  },\n\n  SIGNAL: {\n    FAIL: 0,\n    OK: 1\n  },\n\n TIME: {\n   TIME_WAIT_STOP: 3 * 1000,\n   TIME_WAIT_KILL: 5 * 1000,\n   TIME_WAIT_RESTART: 5 * 1000,\n   TIME_WAIT_COUNTDOWN: 10 * 1000,\n   TIME_WAIT_MASTER_KILL: 2 * 60 * 1000,\n   TIME_WAIT_MONITOR_KILL: 2 * 1000,\n   TIME_WAIT_PING: 30 * 1000,\n   TIME_WAIT_MAX_PING: 5 * 60 * 1000,\n   DEFAULT_UDP_HEARTBEAT_TIME: 20 * 1000,\n   DEFAULT_UDP_HEARTBEAT_TIMEOUT: 100 * 1000,\n   DEFAULT_MQTT_HEARTBEAT_TIMEOUT: 90 * 1000\n }\n};"
  },
  {
    "path": "lib/util/countDownLatch.js",
    "content": "var exp = module.exports;\n\n/**\n * Count down to zero or timeout and invoke cb finally.\n */\nvar CountDownLatch = function(count, opts, cb) {\n  this.count = count;\n  this.cb = cb;\n  var self = this;\n  if (opts.timeout) {\n    this.timerId = setTimeout(function() {\n      self.cb(true);\n    }, opts.timeout);\n  }\n};\n\n/**\n * Call when a task finish to count down.\n *\n * @api public\n */\nCountDownLatch.prototype.done = function() {\n  if(this.count <= 0) {\n    throw new Error('illegal state.');\n  }\n\n  this.count--;\n  if (this.count === 0) {\n    if (this.timerId) {\n      clearTimeout(this.timerId);\n    }\n    this.cb();\n  }\n};\n\n/**\n * Create a count down latch\n *\n * @param {Integer} count\n * @param {Object} opts, opts.timeout indicates timeout, optional param\n * @param {Function} cb, cb(isTimeout)\n *\n * @api public\n */\nexp.createCountDownLatch = function(count, opts, cb) {\n  if(!count || count <= 0) {\n    throw new Error('count should be positive.');\n  }\n\n  if (!cb && typeof opts === 'function') {\n    cb = opts;\n    opts = {};\n  }\n\n  if(typeof cb !== 'function') {\n    throw new Error('cb should be a function.');\n  }\n\n  return new CountDownLatch(count, opts, cb);\n};\n"
  },
  {
    "path": "lib/util/events.js",
    "content": "module.exports = {\n\tADD_SERVERS: 'add_servers',\n\tREMOVE_SERVERS: 'remove_servers',\n  REPLACE_SERVERS: 'replace_servers',\n  BIND_SESSION: 'bind_session',\n  UNBIND_SESSION:'unbind_session',\n\tCLOSE_SESSION: 'close_session',\n\tADD_CRONS: 'add_crons',\n\tREMOVE_CRONS: 'remove_crons',\n\tSTART_SERVER: 'start_server',\n\tSTART_ALL: 'start_all'\n};\n"
  },
  {
    "path": "lib/util/log.js",
    "content": "var logger = require('pomelo-logger');\n\n/**\n * Configure pomelo logger\n */\nmodule.exports.configure = function(app, filename) {\n  var serverId = app.getServerId();\n  var base = app.getBase();\n  logger.configure(filename, {serverId: serverId, base: base});\n};\n"
  },
  {
    "path": "lib/util/moduleUtil.js",
    "content": "var os = require('os');\nvar admin = require('pomelo-admin');\nvar utils = require('./utils');\nvar Constants = require('./constants');\nvar pathUtil = require('./pathUtil');\nvar starter = require('../master/starter');\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar pro = module.exports;\n\n/**\n * Load admin modules\n */\npro.loadModules = function(self, consoleService) {\n  // load app register modules\n  var _modules = self.app.get(Constants.KEYWORDS.MODULE);\n\n  if(!_modules) {\n    return;\n  }\n\n  var modules = [];\n  for(var m in _modules){\n    modules.push(_modules[m]);\n  }\n  \n  var record, moduleId, module;\n  for(var i=0, l=modules.length; i<l; i++) {\n    record = modules[i];\n    if(typeof record.module === 'function') {\n      module = record.module(record.opts, consoleService);\n    } else {\n      module = record.module;\n    }\n\n    moduleId = record.moduleId || module.moduleId;\n\n    if(!moduleId) {\n      logger.warn('ignore an unknown module.');\n      continue;\n    }\n\n    consoleService.register(moduleId, module);\n    self.modules.push(module);\n  }\n};\n\npro.startModules = function(modules, cb) {\n  // invoke the start lifecycle method of modules\n\n  if(!modules) {\n    return;\n  }\n  startModule(null, modules, 0, cb);\n};\n\n/**\n * Append the default system admin modules\n */\npro.registerDefaultModules = function(isMaster, app, closeWatcher) {\n  if(!closeWatcher) {\n    if(isMaster) {\n      app.registerAdmin(require('../modules/masterwatcher'), {app: app});\n    } else {\n      app.registerAdmin(require('../modules/monitorwatcher'), {app: app});\n    }\n  }\n  app.registerAdmin(admin.modules.watchServer,{app:app});\n  app.registerAdmin(require('../modules/console'), {app: app, starter: starter});\n  if(app.enabled('systemMonitor')) {\n    if(os.platform() !== Constants.PLATFORM.WIN) {\n      app.registerAdmin(admin.modules.systemInfo);\n      app.registerAdmin(admin.modules.nodeInfo);\n    }\n    app.registerAdmin(admin.modules.monitorLog, {path: pathUtil.getLogPath(app.getBase())});\n    app.registerAdmin(admin.modules.scripts, {app:app, path: pathUtil.getScriptPath(app.getBase())});\n    if(os.platform() !== Constants.PLATFORM.WIN) {\n      app.registerAdmin(admin.modules.profiler);\n    }\n  }\n};\n\nvar startModule = function(err, modules, index, cb) {\n  if(err || index >= modules.length) {\n    utils.invokeCallback(cb, err);\n    return;\n  }\n\n  var module = modules[index];\n  if(module && typeof module.start === 'function') {\n    module.start(function(err) {\n      startModule(err, modules, index + 1, cb);\n    });\n  } else {\n    startModule(err, modules, index + 1, cb);\n  }\n};\n"
  },
  {
    "path": "lib/util/pathUtil.js",
    "content": "var fs = require('fs');\nvar path = require('path');\nvar Constants = require('./constants');\nvar exp = module.exports;\n\n/**\n * Get system remote service path\n *\n * @param  {String} role server role: frontend, backend\n * @return {String}      path string if the path exist else null\n */\nexp.getSysRemotePath = function(role) {\n  var p = path.join(__dirname, '/../common/remote/', role);\n  return fs.existsSync(p) ? p : null;\n};\n\n/**\n * Get user remote service path\n *\n * @param  {String} appBase    application base path\n * @param  {String} serverType server type\n * @return {String}            path string if the path exist else null\n */\nexp.getUserRemotePath = function(appBase, serverType) {\n  var p = path.join(appBase, '/app/servers/', serverType, Constants.DIR.REMOTE);\n  return fs.existsSync(p) ? p : null;\n};\n\n/**\n * Get user remote cron path\n * \n * @param  {String} appBase    application base path\n * @param  {String} serverType server type\n * @return {String}            path string if the path exist else null\n */\nexp.getCronPath = function(appBase, serverType) {\n  var p = path.join(appBase, '/app/servers/', serverType, Constants.DIR.CRON);\n  return fs.existsSync(p) ? p : null;\n};\n\n/**\n * List all the subdirectory names of user remote directory\n * which hold the codes for all the server types.\n *\n * @param  {String} appBase application base path\n * @return {Array}         all the subdiretory name under servers/\n */\nexp.listUserRemoteDir = function(appBase) {\n  var base = path.join(appBase, '/app/servers/');\n  var files = fs.readdirSync(base);\n  return files.filter(function(fn) {\n    if(fn.charAt(0) === '.') {\n      return false;\n    }\n\n    return fs.statSync(path.join(base, fn)).isDirectory();\n  });\n};\n\n/**\n * Compose remote path record\n *\n * @param  {String} namespace  remote path namespace, such as: 'sys', 'user'\n * @param  {String} serverType\n * @param  {String} path       remote service source path\n * @return {Object}            remote path record\n */\nexp.remotePathRecord = function(namespace, serverType, path) {\n  return {namespace: namespace, serverType: serverType, path: path};\n};\n\n/**\n * Get handler path\n *\n * @param  {String} appBase    application base path\n * @param  {String} serverType server type\n * @return {String}            path string if the path exist else null\n */\nexp.getHandlerPath = function(appBase, serverType) {\n  var p = path.join(appBase, '/app/servers/', serverType, Constants.DIR.HANDLER);\n  return fs.existsSync(p) ? p : null;\n};\n\n/**\n * Get admin script root path.\n *\n * @param  {String} appBase application base path\n * @return {String}         script path string\n */\nexp.getScriptPath = function(appBase) {\n  return path.join(appBase, Constants.DIR.SCRIPT);\n};\n\n/**\n * Get logs path.\n *\n * @param  {String} appBase application base path\n * @return {String}         logs path string\n */\nexp.getLogPath = function(appBase) {\n  return path.join(appBase, Constants.DIR.LOG);\n};"
  },
  {
    "path": "lib/util/utils.js",
    "content": "var os = require('os');\nvar util = require('util');\nvar exec = require('child_process').exec;\nvar logger = require('pomelo-logger').getLogger('pomelo', __filename);\nvar Constants = require('./constants');\nvar pomelo = require('../pomelo');\n\nvar utils = module.exports;\n\n/**\n * Invoke callback with check\n */\nutils.invokeCallback = function(cb) {\n  if (typeof cb === 'function') {\n    var len = arguments.length;\n    if(len == 1) {\n      return cb();\n    }\n\n    if(len == 2) {\n      return cb(arguments[1]);\n    }\n\n    if(len == 3) {\n      return cb(arguments[1], arguments[2]);\n    }\n\n    if(len == 4) {\n      return cb(arguments[1], arguments[2], arguments[3]);\n    }\n\n    var args = Array(len - 1);\n    for (i = 1; i < len; i++)\n        args[i - 1] = arguments[i];\n    cb.apply(null, args);\n    // cb.apply(null, Array.prototype.slice.call(arguments, 1));\n  }\n};\n\n/**\n * Get the count of elements of object\n */\nutils.size = function(obj) {\n  var count = 0;\n  for (var i in obj) {\n    if (obj.hasOwnProperty(i) && typeof obj[i] !== 'function') {\n      count++;\n    }\n  }\n  return count;\n};\n\n/**\n * Check a string whether ends with another string\n */\nutils.endsWith = function(str, suffix) {\n  if (typeof str !== 'string' || typeof suffix !== 'string' ||\n    suffix.length > str.length) {\n    return false;\n  }\n  return str.indexOf(suffix, str.length - suffix.length) !== -1;\n};\n\n/**\n * Check a string whether starts with another string\n */\nutils.startsWith = function(str, prefix) {\n  if (typeof str !== 'string' || typeof prefix !== 'string' ||\n    prefix.length > str.length) {\n    return false;\n  }\n\n  return str.indexOf(prefix) === 0;\n};\n\n/**\n * Compare the two arrays and return the difference.\n */\nutils.arrayDiff = function(array1, array2) {\n  var o = {};\n  for(var i = 0, len = array2.length; i < len; i++) {\n    o[array2[i]] = true;\n  }\n\n  var result = [];\n  for(i = 0, len = array1.length; i < len; i++) {\n    var v = array1[i];\n    if(o[v]) continue;\n    result.push(v);\n  }\n  return result;\n};\n\n/*\n * Date format\n */\nutils.format = function(date, format) {\n  format = format || 'MMddhhmm';\n  var o = {\n    \"M+\": date.getMonth() + 1, //month\n    \"d+\": date.getDate(), //day\n    \"h+\": date.getHours(), //hour\n    \"m+\": date.getMinutes(), //minute\n    \"s+\": date.getSeconds(), //second\n    \"q+\": Math.floor((date.getMonth() + 3) / 3), //quarter\n    \"S\": date.getMilliseconds() //millisecond\n  };\n\n  if (/(y+)/.test(format)) {\n    format = format.replace(RegExp.$1, (date.getFullYear() + \"\").substr(4 - RegExp.$1.length));\n  }\n\n  for (var k in o) {\n    if (new RegExp(\"(\" + k + \")\").test(format)) {\n      format = format.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] :\n        (\"00\" + o[k]).substr((\"\" + o[k]).length));\n    }\n  }\n  return format;\n};\n\n/**\n * check if has Chinese characters.\n */\nutils.hasChineseChar = function(str) {\n  if (/.*[\\u4e00-\\u9fa5]+.*$/.test(str)) {\n    return true;\n  } else {\n    return false;\n  }\n};\n\n/**\n * transform unicode to utf8\n */\nutils.unicodeToUtf8 = function(str) {\n  var i, len, ch;\n  var utf8Str = \"\";\n  len = str.length;\n  for (i = 0; i < len; i++) {\n    ch = str.charCodeAt(i);\n\n    if ((ch >= 0x0) && (ch <= 0x7F)) {\n      utf8Str += str.charAt(i);\n\n    } else if ((ch >= 0x80) && (ch <= 0x7FF)) {\n      utf8Str += String.fromCharCode(0xc0 | ((ch >> 6) & 0x1F));\n      utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));\n\n    } else if ((ch >= 0x800) && (ch <= 0xFFFF)) {\n      utf8Str += String.fromCharCode(0xe0 | ((ch >> 12) & 0xF));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 6) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));\n\n    } else if ((ch >= 0x10000) && (ch <= 0x1FFFFF)) {\n      utf8Str += String.fromCharCode(0xF0 | ((ch >> 18) & 0x7));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 12) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 6) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));\n\n    } else if ((ch >= 0x200000) && (ch <= 0x3FFFFFF)) {\n      utf8Str += String.fromCharCode(0xF8 | ((ch >> 24) & 0x3));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 18) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 12) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 6) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));\n\n    } else if ((ch >= 0x4000000) && (ch <= 0x7FFFFFFF)) {\n      utf8Str += String.fromCharCode(0xFC | ((ch >> 30) & 0x1));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 24) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 18) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 12) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | ((ch >> 6) & 0x3F));\n      utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));\n\n    }\n\n  }\n  return utf8Str;\n};\n\n/**\n * Ping server to check if network is available\n *\n */\nutils.ping = function(host, cb) {\n  if(!module.exports.isLocal(host)) {\n    var cmd = 'ping -w 15 ' + host;\n    exec(cmd, function(err, stdout, stderr) {\n      if(!!err) {\n        cb(false);\n        return;\n      }\n      cb(true);\n    });\n  } else {\n    cb(true);\n  }\n};\n\n/**\n * Check if server is exsit. \n *\n */\nutils.checkPort = function(server, cb) {\n  if (!server.port && !server.clientPort) {\n    this.invokeCallback(cb, 'leisure');\n    return;\n  }\n  var self = this;\n  var port = server.port || server.clientPort;\n  var host = server.host;\n  var generateCommand = function(self, host, port) {\n    var cmd;\n    var ssh_params = pomelo.app.get(Constants.RESERVED.SSH_CONFIG_PARAMS);\n    if(!!ssh_params && Array.isArray(ssh_params)) {\n      ssh_params = ssh_params.join(' ');\n    }\n    else {\n      ssh_params = \"\";\n    }\n    if (!self.isLocal(host)) {\n      cmd = util.format('ssh %s %s \"netstat -an|awk \\'{print $4}\\'|grep %s|wc -l\"', host, ssh_params, port);\n    } else {\n      cmd = util.format('netstat -an|awk \\'{print $4}\\'|grep %s|wc -l', port);\n    }\n    return cmd;\n  };\n  var cmd1 = generateCommand(self, host, port);\n  var child = exec(cmd1, function(err, stdout, stderr) {\n    if(err) {\n      logger.error('command %s execute with error: %j', cmd1, err.stack);\n      self.invokeCallback(cb, 'error');\n    } else if(stdout.trim() !== '0') {\n      self.invokeCallback(cb, 'busy');\n    } else {\n      port = server.clientPort;\n      var cmd2 = generateCommand(self, host, port);\n      exec(cmd2, function(err, stdout, stderr) {\n        if(err) {\n          logger.error('command %s execute with error: %j', cmd2, err.stack);\n          self.invokeCallback(cb, 'error');\n        } else if (stdout.trim() !== '0') {\n          self.invokeCallback(cb, 'busy');\n        } else {\n          self.invokeCallback(cb, 'leisure');\n        }\n      });\n    }\n  });\n};\n\nutils.isLocal = function(host) {\n  var app = require('../pomelo').app;\n  if(!app) {\n    return host === '127.0.0.1' || host === 'localhost' || host === '0.0.0.0' || inLocal(host);\n  } else {\n    return host === '127.0.0.1' || host === 'localhost' || host === '0.0.0.0' || inLocal(host) || host === app.master.host;\n  }\n};\n\n/**\n * Load cluster server.\n *\n */\nutils.loadCluster = function(app, server, serverMap) {\n  var increaseFields = {};\n  var host = server.host;\n  var count = parseInt(server[Constants.RESERVED.CLUSTER_COUNT]);\n  var seq = app.clusterSeq[server.serverType];\n  if(!seq) {\n    seq = 0;\n    app.clusterSeq[server.serverType] = count;\n  } else {\n    app.clusterSeq[server.serverType] = seq + count;\n  }\n\n  for(var key in server) {\n    var value = server[key].toString();\n    if(value.indexOf(Constants.RESERVED.CLUSTER_SIGNAL) > 0) {\n      var base = server[key].slice(0, -2);\n      increaseFields[key] = base;\n    }\n  }\n\n  var clone = function(src) {\n    var rs = {};\n    for(var key in src) {\n      rs[key] = src[key];\n    }\n    return rs;\n  };\n  for(var i=0, l=seq; i<count; i++,l++) {\n    var cserver = clone(server);\n    cserver.id = Constants.RESERVED.CLUSTER_PREFIX + server.serverType + '-' + l;\n    for(var k in increaseFields) {\n      var v = parseInt(increaseFields[k]);\n      cserver[k] = v + i;\n    }\n    serverMap[cserver.id] = cserver;\n  }\n};\n\nutils.extends = function(origin, add) {\n  if (!add || !this.isObject(add)) return origin;\n\n  var keys = Object.keys(add);\n  var i = keys.length;\n  while (i--) {\n    origin[keys[i]] = add[keys[i]];\n  }\n  return origin;\n};\n\nutils.headHandler = function(headBuffer) {\n  var len = 0;\n  for(var i=1; i<4; i++) {\n    if(i > 1) {\n      len <<= 8;\n    }\n    len += headBuffer.readUInt8(i);\n  }\n  return len;\n};\n\nvar inLocal = function(host) {\n  for (var index in localIps) {\n    if (host === localIps[index]) {\n      return true;\n    }\n  }\n  return false;\n};\n\nvar localIps = function() {\n  var ifaces = os.networkInterfaces();\n  var ips = [];\n  var func = function(details) {\n    if (details.family === 'IPv4') {\n      ips.push(details.address);\n    }\n  };\n  for (var dev in ifaces) {\n    ifaces[dev].forEach(func);\n  }\n  return ips;\n}();\n\nutils.isObject = function(arg) {\n  return typeof arg === 'object' && arg !== null;\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"pomelo\",\n  \"version\": \"2.2.7\",\n  \"homepage\": \"https://github.com/NetEase/pomelo\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/NetEase/pomelo.git\"\n  },\n  \"scripts\": {\n    \"test\": \"grunt\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/NetEase/pomelo/issues\"\n  },\n  \"licenses\": [{\n    \"type\": \"MIT\",\n    \"url\": \"https://github.com/NetEase/pomelo#license\"\n  }],\n  \"keywords\": [\n    \"pomelo\",\n    \"framework\",\n    \"game\",\n    \"web\",\n    \"realtime\",\n    \"server\"\n  ],\n  \"dependencies\": {\n    \"socket.io\": \"1.7.2\",\n    \"async\": \"0.2.5\",\n    \"seq-queue\": \"0.0.5\",\n    \"crc\": \"0.2.0\",\n    \"cliff\": \"0.1.8\",\n    \"mkdirp\": \"0.3.3\",\n    \"pomelo-loader\": \"0.0.6\",\n    \"pomelo-rpc\": \"1.0.7\",\n    \"pomelo-protocol\": \"0.1.6\",\n    \"pomelo-admin\": \"1.0.1\",\n    \"pomelo-logger\": \"0.1.7\",\n    \"pomelo-scheduler\": \"0.3.9\",\n    \"ws\": \"1.1.1\",\n    \"pomelo-protobuf\": \"0.4.0\",\n    \"node-bignumber\": \"1.2.1\",\n    \"commander\": \"2.0.0\",\n    \"mqtt\": \"0.3.9\"\n  },\n  \"bin\": {\n    \"pomelo\": \"./bin/pomelo\"\n  },\n  \"devDependencies\": {\n    \"should\": \"3.3.1\",\n    \"mocha\": \">=0.0.1\",\n    \"muk\": \">=0.0.1\",\n    \"grunt\": \"~0.4.2\",\n    \"grunt-mocha-test\": \"0.8.x\",\n    \"grunt-contrib-clean\": \"0.5.x\",\n    \"grunt-contrib-jshint\": \"~0.8.0\",\n    \"blanket\": \"~1.1.6\"\n  }\n}"
  },
  {
    "path": "template/game-server/app/servers/connector/handler/entryHandler.js",
    "content": "module.exports = function(app) {\n  return new Handler(app);\n};\n\nvar Handler = function(app) {\n  this.app = app;\n};\n\n/**\n * New client entry.\n *\n * @param  {Object}   msg     request message\n * @param  {Object}   session current session object\n * @param  {Function} next    next step callback\n * @return {Void}\n */\nHandler.prototype.entry = function(msg, session, next) {\n  next(null, {code: 200, msg: 'game server is ok.'});\n};\n\n/**\n * Publish route for mqtt connector.\n *\n * @param  {Object}   msg     request message\n * @param  {Object}   session current session object\n * @param  {Function} next    next step callback\n * @return {Void}\n */\nHandler.prototype.publish = function(msg, session, next) {\n\tvar result = {\n\t\ttopic: 'publish',\n\t\tpayload: JSON.stringify({code: 200, msg: 'publish message is ok.'})\n\t};\n  next(null, result);\n};\n\n/**\n * Subscribe route for mqtt connector.\n *\n * @param  {Object}   msg     request message\n * @param  {Object}   session current session object\n * @param  {Function} next    next step callback\n * @return {Void}\n */\nHandler.prototype.subscribe = function(msg, session, next) {\n\tvar result = {\n\t\ttopic: 'subscribe',\n\t\tpayload: JSON.stringify({code: 200, msg: 'subscribe message is ok.'})\n\t};\n  next(null, result);\n};\n"
  },
  {
    "path": "template/game-server/app.js",
    "content": "var pomelo = require('pomelo');\n\n/**\n * Init app for client.\n */\nvar app = pomelo.createApp();\napp.set('name', '$');\n\n// app configuration\napp.configure('production|development', 'connector', function(){\n  app.set('connectorConfig',\n    {\n      connector : pomelo.connectors.hybridconnector,\n      heartbeat : 3,\n      useDict : true,\n      useProtobuf : true\n    });\n});\n\n// start app\napp.start();\n\nprocess.on('uncaughtException', function (err) {\n  console.error(' Caught exception: ' + err.stack);\n});\n"
  },
  {
    "path": "template/game-server/app.js.mqtt",
    "content": "var pomelo = require('pomelo');\n\n/**\n * Init app for client.\n */\nvar app = pomelo.createApp();\napp.set('name', '$');\n\n// app configuration\napp.configure('production|development', 'connector', function(){\n  app.set('connectorConfig',\n    {\n      connector : pomelo.connectors.mqttconnector,\n      publishRoute: 'connector.entryHandler.publish',\n      subscribeRoute: 'connector.entryHandler.subscribe'\n    });\n});\n\n// start app\napp.start();\n\nprocess.on('uncaughtException', function (err) {\n  console.error(' Caught exception: ' + err.stack);\n});"
  },
  {
    "path": "template/game-server/app.js.sio",
    "content": "var pomelo = require('pomelo');\n\n/**\n * Init app for client.\n */\nvar app = pomelo.createApp();\napp.set('name', '$');\n\n// app configuration\napp.configure('production|development', 'connector', function(){\n  app.set('connectorConfig',\n    {\n      connector : pomelo.connectors.sioconnector,\n      // 'websocket', 'polling-xhr', 'polling-jsonp', 'polling'\n      transports : ['websocket', 'polling'],\n      heartbeats : true,\n      closeTimeout : 60 * 1000,\n      heartbeatTimeout : 60 * 1000,\n      heartbeatInterval : 25 * 1000\n    });\n});\n\n// start app\napp.start();\n\nprocess.on('uncaughtException', function (err) {\n  console.error(' Caught exception: ' + err.stack);\n});\n"
  },
  {
    "path": "template/game-server/app.js.sio.wss",
    "content": "var fs = require('fs');\nvar pomelo = require('pomelo');\n\n/**\n * Init app for client.\n */\nvar app = pomelo.createApp();\napp.set('name', '$');\n\n// app configuration\napp.configure('production|development', function(){\n  app.set('connectorConfig',\n    {\n      connector : pomelo.connectors.sioconnector,\n      key: fs.readFileSync('../shared/server.key'),\n  \t\tcert: fs.readFileSync('../shared/server.crt')\n    });\n});\n\n// start app\napp.start();\n\nprocess.on('uncaughtException', function (err) {\n  console.error(' Caught exception: ' + err.stack);\n});"
  },
  {
    "path": "template/game-server/app.js.udp",
    "content": "var pomelo = require('pomelo');\n\n/**\n * Init app for client.\n */\nvar app = pomelo.createApp();\napp.set('name', '$');\n\n// app configuration\napp.configure('production|development', function(){\n  app.set('connectorConfig',\n    {\n      connector : pomelo.connectors.udpconnector,\n     \theartbeat : 3\n    });\n});\n\n// start app\napp.start();\n\nprocess.on('uncaughtException', function (err) {\n  console.error(' Caught exception: ' + err.stack);\n});"
  },
  {
    "path": "template/game-server/app.js.wss",
    "content": "var fs = require('fs');\nvar pomelo = require('pomelo');\n\n/**\n * Init app for client.\n */\nvar app = pomelo.createApp();\napp.set('name', '$');\n\n// app configuration\napp.configure('production|development', 'connector', function(){\n  app.set('connectorConfig',\n    {\n      connector : pomelo.connectors.hybridconnector,\n      heartbeat : 3,\n      useDict : true,\n      useProtobuf : true,\n      ssl: {\n        type: 'wss',\n      \tkey: fs.readFileSync('../shared/server.key'),\n  \t\t\tcert: fs.readFileSync('../shared/server.crt')\n      }\n    });\n});\n\n// start app\napp.start();\n\nprocess.on('uncaughtException', function (err) {\n  console.error(' Caught exception: ' + err.stack);\n});"
  },
  {
    "path": "template/game-server/config/adminServer.json",
    "content": "[{\n    \"type\": \"connector\",\n    \"token\": \"agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn\"\n}\n]"
  },
  {
    "path": "template/game-server/config/adminUser.json",
    "content": "[\n  {\n    \"id\": \"user-1\",\n    \"username\": \"admin\",\n    \"password\": \"admin\",\n    \"level\": 1\n  },\n  \n  {\n    \"id\": \"user-2\",\n    \"username\": \"monitor\",\n    \"password\": \"monitor\",\n    \"level\": 2\n  },\n\n  {\n    \"id\": \"user-3\",\n    \"username\": \"test\",\n    \"password\": \"test\",\n    \"level\": 2\n  }\n]\n"
  },
  {
    "path": "template/game-server/config/clientProtos.json",
    "content": "{}"
  },
  {
    "path": "template/game-server/config/dictionary.json",
    "content": "{}"
  },
  {
    "path": "template/game-server/config/log4js.json",
    "content": "{\n  \"appenders\": [\n    {\n      \"type\": \"console\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"${opts:base}/logs/con-log-${opts:serverId}.log\",\n      \"pattern\": \"connector\",\n      \"maxLogSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\": \"con-log\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"${opts:base}/logs/rpc-log-${opts:serverId}.log\",\n      \"maxLogSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\": \"rpc-log\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"${opts:base}/logs/forward-log-${opts:serverId}.log\",\n      \"maxLogSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\": \"forward-log\"\n    },\n    {\n     \"type\": \"file\",\n     \"filename\": \"${opts:base}/logs/rpc-debug-${opts:serverId}.log\",\n     \"maxLogSize\": 1048576,\n     \"layout\": {\n      \"type\": \"basic\"\n     },\n     \"backups\": 5,\n     \"category\": \"rpc-debug\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"${opts:base}/logs/crash.log\",\n      \"maxLogSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\":\"crash-log\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"${opts:base}/logs/admin.log\",\n      \"maxLogSize\": 1048576,\n      \"layout\": {\n          \"type\": \"basic\"\n        }\n      ,\"backups\": 5,\n      \"category\":\"admin-log\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"${opts:base}/logs/pomelo-${opts:serverId}.log\",\n      \"maxLogSize\": 1048576,\n      \"layout\": {\n          \"type\": \"basic\"\n        }\n      ,\"backups\": 5,\n      \"category\":\"pomelo\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"${opts:base}/logs/pomelo-admin.log\",\n      \"maxLogSize\": 1048576,\n      \"layout\": {\n          \"type\": \"basic\"\n        }\n      ,\"backups\": 5,\n      \"category\":\"pomelo-admin\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"${opts:base}/logs/pomelo-rpc-${opts:serverId}.log\",\n      \"maxLogSize\": 1048576,\n      \"layout\": {\n          \"type\": \"basic\"\n        }\n      ,\"backups\": 5,\n      \"category\":\"pomelo-rpc\"\n    }\n  ],\n\n  \"levels\": {\n    \"rpc-log\" : \"ERROR\",\n    \"forward-log\": \"ERROR\"\n  },\n\n  \"replaceConsole\": true,\n\n  \"lineDebug\": false\n}\n"
  },
  {
    "path": "template/game-server/config/master.json",
    "content": "{\n  \"development\": {\n    \"id\": \"master-server-1\", \"host\": \"127.0.0.1\", \"port\": 3005\n  },\n  \"production\": {\n    \"id\": \"master-server-1\", \"host\": \"127.0.0.1\", \"port\": 3005\n  }\n}\n"
  },
  {
    "path": "template/game-server/config/serverProtos.json",
    "content": "{}"
  },
  {
    "path": "template/game-server/config/servers.json",
    "content": "{\n  \"development\":{\n    \"connector\": [\n    {\"id\": \"connector-server-1\", \"host\": \"127.0.0.1\", \"port\": 3150, \"clientHost\": \"127.0.0.1\", \"clientPort\": 3010, \"frontend\": true}\n    ]\n  },\n  \"production\":{\n    \"connector\": [\n    {\"id\": \"connector-server-1\", \"host\": \"127.0.0.1\", \"port\": 3150, \"clientHost\": \"127.0.0.1\", \"clientPort\": 3010, \"frontend\": true}\n    ]\n  }\n}\n"
  },
  {
    "path": "template/game-server/package.json",
    "content": "{\n    \"name\":\"$\",\n    \"version\":\"0.0.1\",\n    \"private\":false,\n    \"dependencies\":{\n        \"pomelo\":\"#\"\n    }\n}\n\n"
  },
  {
    "path": "template/npm-install.bat",
    "content": "::npm-install.bat\n@echo off\n::install web server dependencies && game server dependencies\ncd web-server && npm install -d && cd .. && cd game-server && npm install -d"
  },
  {
    "path": "template/npm-install.sh",
    "content": "cd ./game-server && npm install -d\necho '============   game-server npm installed ============'\ncd ..\ncd ./web-server && npm install -d\necho '============   web-server npm installed ============'\n"
  },
  {
    "path": "template/shared/server.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIICSzCCAbQCCQCQVN8rD6MylDANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJD\nTjERMA8GA1UECAwIemhlamlhbmcxETAPBgNVBAcMCGhhbmd6aG91MRAwDgYDVQQK\nDAdOZXRFYXNlMQ8wDQYDVQQLDAZwb21lbG8xEjAQBgNVBAMMCWxvY2FsaG9zdDAe\nFw0xNDA0MjIwNjEwMDJaFw0xNDA1MjIwNjEwMDJaMGoxCzAJBgNVBAYTAkNOMREw\nDwYDVQQIDAh6aGVqaWFuZzERMA8GA1UEBwwIaGFuZ3pob3UxEDAOBgNVBAoMB05l\ndEVhc2UxDzANBgNVBAsMBnBvbWVsbzESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0G\nCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMPe8oscKpTlQFZRrpbWmSO1UE+H65nq50\nl5+ptOVPMK3wgEj+YRyGWhBjugj9teVmLXY9ImWdZkBlvdAiQj7/S/1MxRbRtwEF\nGRE5ul/X1M6I+F0UyTGYA1Mo0jIlQaBDXAAyDujCWi+qlyZ28efNDUlO2KBY1H4r\nXobm9hoEFQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAMIuL8KqEEtjbfL/tR2+5dQ5\n958gtDtA62L7bMosl4hmuzdyWADu3IcKSaXAESLhIuIClt2Pwc14iFf9qRyB/cjY\n4kLgwDGhK5EJw1kQS+Hs9NNSGxJTXUkoms3kEdRGy4hrZpTheJJNaKuv3oXrdvYQ\n85yoc/P5OnJapB3huYL9\n-----END CERTIFICATE-----"
  },
  {
    "path": "template/shared/server.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQDMPe8oscKpTlQFZRrpbWmSO1UE+H65nq50l5+ptOVPMK3wgEj+\nYRyGWhBjugj9teVmLXY9ImWdZkBlvdAiQj7/S/1MxRbRtwEFGRE5ul/X1M6I+F0U\nyTGYA1Mo0jIlQaBDXAAyDujCWi+qlyZ28efNDUlO2KBY1H4rXobm9hoEFQIDAQAB\nAoGAXhaeCUIyqeoynLWh+yzzOHFqzjpnrr0iIwYCgJycEqobRzLh7YXxLRdqe3al\nU7Oq9TI2SR2CcEs9mWEi89VOzVvfu+4zRlvJLMzNjG8ncdvzmzWR288ORq6qmYVU\n3KAEz/tbNaQMLrD43hkIb9BrSIb/cnwekl3pANo9dwytU5UCQQD4V6vTyzs/ob21\n+fO98tFkPtoHbt43S/1kDBSUyh6WWbS1KIQgtUSr2P5Ddtl6/vD3DW+XHCAhxyfV\nvuDvaP/fAkEA0oomFfmlpvzYejYNKPOz2PR+M0oRFVwn7lYyNwbRtUK1JYOMHwJ/\n3gwQEgAcYEkvgRlsxX0T5vHNmoR3U3OqiwJAIWkiG9devDvVWxMqoKZ3V0ZBbPiU\netoFWB1r82yR2uZssmamCAR7HaeO5aKqtapw3rv3BFxrUkAJ8u7AMlVs/wJAVnpm\nMGqNjyyWIoSnHSYUvk2WtKx8neBvimcfUxja9HAFBfaljGszaFpeE3a2MRp+h7GQ\nywGYNikmAYzdkoqVBwJAcOm/6u863pD2xA1mSFnmm3TulAMBfCULLdcY40w9m38b\nD89R1ISEy//N1fWa4KTsM0GpVOowEyluc53XNRUghw==\n-----END RSA PRIVATE KEY-----"
  },
  {
    "path": "template/web-server/app.js",
    "content": "var express = require('express');\nvar app = express.createServer();\n\napp.configure(function(){\n  app.use(express.methodOverride());\n  app.use(express.bodyParser());\n  app.use(app.router);\n  app.set('view engine', 'jade');\n  app.set('views', __dirname + '/public');\n  app.set('view options', {layout: false});\n  app.set('basepath',__dirname + '/public');\n});\n\napp.configure('development', function(){\n  app.use(express.static(__dirname + '/public'));\n  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));\n});\n\napp.configure('production', function(){\n  var oneYear = 31557600000;\n  app.use(express.static(__dirname + '/public', { maxAge: oneYear }));\n  app.use(express.errorHandler());\n});\n\nconsole.log(\"Web server has started.\\nPlease log on http://127.0.0.1:3001/index.html\");\n\napp.listen(3001);\n"
  },
  {
    "path": "template/web-server/app.js.https",
    "content": "var https = require('https');\nvar express = require('express');\n\nvar fs = require('fs');\n\nvar options = {\n  key: fs.readFileSync('../shared/server.key'),\n  cert: fs.readFileSync('../shared/server.crt')\n};\n\nvar app = express();\n\napp.configure(function(){\n  app.use(express.methodOverride());\n  app.use(express.bodyParser());\n  app.use(app.router);\n  app.set('view engine', 'jade');\n  app.set('views', __dirname + '/public');\n  app.set('view options', {layout: false});\n  app.set('basepath',__dirname + '/public');\n});\n\napp.configure('development', function(){\n  app.use(express.static(__dirname + '/public'));\n  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));\n});\n\napp.configure('production', function(){\n  var oneYear = 31557600000;\n  app.use(express.static(__dirname + '/public', { maxAge: oneYear }));\n  app.use(express.errorHandler());\n});\n\nconsole.log(\"Web server has started.\\nPlease log on http://127.0.0.1:3001/index.html\");\n\nvar httpsServer = https.createServer(options, app);\n\nhttpsServer.listen(3001);"
  },
  {
    "path": "template/web-server/bin/component.bat",
    "content": "cd public/js/lib && component install -f && component build -v"
  },
  {
    "path": "template/web-server/bin/component.sh",
    "content": "cd public/js/lib && component install -f && component build -v"
  },
  {
    "path": "template/web-server/package.json",
    "content": "{\n  \"name\": \"$\",\n  \"version\": \"0.0.1\",\n  \"private\": false,\n  \"dependencies\": {\n    \"express\": \"3.4.8\"\n  }\n}"
  },
  {
    "path": "template/web-server/public/css/base.css",
    "content": "﻿.g-doc {\n  width: 1000px;\n  margin: 0 auto;\n  text-align: left;\n  line-height: 18px;\n  font-size: 12px;\n  color: #555;\n  font-family: arial;\n}\n\n.g-banner {\n  position: relative;\n  width: 1000px;\n  height: 90px;\n  margin: 0 auto;\n}\n\n.g-banner .logo {\n  position: absolute;\n  left: 20px;\n  top: 13px;\n  width: 153px;\n  height: 60px;\n  z-index: 101;\n}\n\n.g-banner .logo .img {\n  width: 153px;\n  height: 60px;\n  background: url(../image/logo.png) no-repeat 0 0;\n}\n\n.g-background {\n  height: 350px;\n  background: url(../image/sp.png) no-repeat 326px 0;\n}\n\n.g-content {\n  padding: 150px;\n  width: 800px;\n  margin-left: 50px;\n  font-size: 50pt;\n  font-style: italic;\n}\n\n.g-link {\n  height: 100px;\n  margin-top: 30px;\n  margin-left: 300px;\n  font-size: 15pt;\n}\n\n.g-button {\n  margin-top: 10px;\n  text-align: center;\n}\n\na:link {\n  color: #006699;\n  text-decoration: none;\n}\n\na:visited {\n  color: #006699;\n  text-decoration: none;\n}\n\na:hover {\n  color: #FF0000;\n  text-decoration: underline;\n}\n\na:active {\n  color: #006699;\n  text-decoration: underline;\n}\n"
  },
  {
    "path": "template/web-server/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>\n      Pomelo\n    </title>\n    <meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\" />\n    <meta http-equiv=\"content-style-type\" content=\"text/css\" />\n    <meta http-equiv=\"content-scripte-type\" content=\"text/javascript\" />\n    <meta name=\"author\" content=\"netease\" />\n    <meta name=\"version\" content=\"1.0\" />\n    <meta name=\"keywords\" content=\"pomelo\" />\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"css/base.css\" />\n    <script src=\"js/lib/build/build.js\" type=\"text/javascript\"></script>\n    <script type=\"text/javascript\">\n      require('boot');\n    </script> \n    <script type=\"text/javascript\">\n      var pomelo = window.pomelo;\n      var host = \"127.0.0.1\";\n      var port = \"3010\";\n      function show() {\n        pomelo.init({\n          host: host,\n          port: port,\n          log: true\n        }, function() {\n        pomelo.request(\"connector.entryHandler.entry\", \"hello pomelo\", function(data) {\n            alert(data.msg);\n          });\n        });\n      }\n    </script>\n  </head>\n  <body>\n    <div class=\"g-doc\">\n      <div class=\"g-banner\" style=\"border:none\">\n        <div class=\"logo\">\n          <div class=\"img\"></div>\n        </div>\n      </div>\n      <div class=\"g-background\">\n        <div class=\"g-content\">\n          Welcome to Pomelo\n        </div>\n      </div>\n      <div class=\"g-link\">\n        Home:\n        <a href=\"https://github.com/NetEase/pomelo\">https://github.com/NetEase/pomelo</a>\n      </div>\n      <div class=\"g-button\">\n        <input id=\"test\" type=\"button\" value=\"Test Game Server\" onclick=\"show()\"/>\n      </div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "template/web-server/public/index.html.sio",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>\n      Pomelo\n    </title>\n    <meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\" />\n    <meta http-equiv=\"content-style-type\" content=\"text/css\" />\n    <meta http-equiv=\"content-scripte-type\" content=\"text/javascript\" />\n    <meta name=\"author\" content=\"netease\" />\n    <meta name=\"version\" content=\"1.0\" />\n    <meta name=\"keywords\" content=\"pomelo\" />\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"css/base.css\" />\n    <script src=\"js/lib/socket.io.js\">\n    </script>\n    <script src=\"js/lib/pomeloclient.js\">\n    </script>\n    <script type=\"text/javascript\">\n      var pomelo = window.pomelo;\n      var host = \"127.0.0.1\";\n      var port = \"3010\";\n      function show() {\n        pomelo.init({\n          host: host,\n          port: port,\n          log: true\n        }, function() {\n        pomelo.request(\"connector.entryHandler.entry\", \"hello pomelo\", function(data) {\n            alert(data.msg);\n          });\n        });\n      }\n    </script>\n \n  </head>\n  <body>\n    <div class=\"g-doc\">\n      <div class=\"g-banner\" style=\"border:none\">\n        <div class=\"logo\">\n          <div class=\"img\"></div>\n        </div>\n      </div>\n      <div class=\"g-background\">\n        <div class=\"g-content\">\n          Welcome to Pomelo\n        </div>\n      </div>\n      <div class=\"g-link\">\n        Home:\n        <a href=\"https://github.com/NetEase/pomelo\">https://github.com/NetEase/pomelo</a>\n      </div>\n      <div class=\"g-button\">\n        <input id=\"test\" type=\"button\" value=\"Test Game Server\" onclick=\"show()\"/>\n      </div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "template/web-server/public/js/lib/build/build.js",
    "content": "\n\n/**\n * hasOwnProperty.\n */\n\nvar has = Object.prototype.hasOwnProperty;\n\n/* Refer to https://github.com/componentjs/require/blob/master/lib/require.js */\n/**\n * Require the given path.\n *\n * @param {String} path\n * @return {Object} exports\n * @api public\n */\n\nfunction require(path, parent, orig) {\n  var resolved = require.resolve(path);\n\n  // lookup failed\n  if (null == resolved) {\n    orig = orig || path;\n    parent = parent || 'root';\n    var err = new Error('Failed to require \"' + orig + '\" from \"' + parent + '\"');\n    err.path = orig;\n    err.parent = parent;\n    err.require = true;\n    throw err;\n  }\n\n  var module = require.modules[resolved];\n\n  // perform real require()\n  // by invoking the module's\n  // registered function\n  if (!module.exports) {\n    module.exports = {};\n    module.client = module.component = true;\n    module.call(this, module.exports, require.relative(resolved), module);\n  }\n\n  return module.exports;\n}\n\n/**\n * Registered modules.\n */\n\nrequire.modules = {};\n\n/**\n * Registered aliases.\n */\n\nrequire.aliases = {};\n\n/**\n * Resolve `path`.\n *\n * Lookup:\n *\n *   - PATH/index.js\n *   - PATH.js\n *   - PATH\n *\n * @param {String} path\n * @return {String} path or null\n * @api private\n */\n\nrequire.resolve = function(path) {\n  if (path.charAt(0) === '/') path = path.slice(1);\n  var index = path + '/index.js';\n\n  var paths = [\n    path,\n    path + '.js',\n    path + '.json',\n    path + '/index.js',\n    path + '/index.json'\n  ];\n\n  for (var i = 0; i < paths.length; i++) {\n    var path = paths[i];\n    if (has.call(require.modules, path)) return path;\n  }\n\n  if (has.call(require.aliases, index)) {\n    return require.aliases[index];\n  }\n};\n\n/**\n * Normalize `path` relative to the current path.\n *\n * @param {String} curr\n * @param {String} path\n * @return {String}\n * @api private\n */\n\nrequire.normalize = function(curr, path) {\n  var segs = [];\n\n  if ('.' != path.charAt(0)) return path;\n\n  curr = curr.split('/');\n  path = path.split('/');\n\n  for (var i = 0; i < path.length; ++i) {\n    if ('..' == path[i]) {\n      curr.pop();\n    } else if ('.' != path[i] && '' != path[i]) {\n      segs.push(path[i]);\n    }\n  }\n\n  return curr.concat(segs).join('/');\n};\n\n/**\n * Register module at `path` with callback `definition`.\n *\n * @param {String} path\n * @param {Function} definition\n * @api private\n */\n\nrequire.register = function(path, definition) {\n  require.modules[path] = definition;\n};\n\n/**\n * Alias a module definition.\n *\n * @param {String} from\n * @param {String} to\n * @api private\n */\n\nrequire.alias = function(from, to) {\n  if (!has.call(require.modules, from)) {\n    throw new Error('Failed to alias \"' + from + '\", it does not exist');\n  }\n  require.aliases[to] = from;\n};\n\n/**\n * Return a require function relative to the `parent` path.\n *\n * @param {String} parent\n * @return {Function}\n * @api private\n */\n\nrequire.relative = function(parent) {\n  var p = require.normalize(parent, '..');\n\n  /**\n   * lastIndexOf helper.\n   */\n\n  function lastIndexOf(arr, obj) {\n    var i = arr.length;\n    while (i--) {\n      if (arr[i] === obj) return i;\n    }\n    return -1;\n  }\n\n  /**\n   * The relative require() itself.\n   */\n\n  function localRequire(path) {\n    var resolved = localRequire.resolve(path);\n    return require(resolved, parent, path);\n  }\n\n  /**\n   * Resolve relative to the parent.\n   */\n\n  localRequire.resolve = function(path) {\n    var c = path.charAt(0);\n    if ('/' == c) return path.slice(1);\n    if ('.' == c) return require.normalize(p, path);\n\n    // resolve deps by returning\n    // the dep in the nearest \"deps\"\n    // directory\n    var segs = parent.split('/');\n    var i = lastIndexOf(segs, 'deps') + 1;\n    if (!i) i = 0;\n    path = segs.slice(0, i + 1).join('/') + '/deps/' + path;\n    return path;\n  };\n\n  /**\n   * Check if module is defined at `path`.\n   */\n\n  localRequire.exists = function(path) {\n    return has.call(require.modules, localRequire.resolve(path));\n  };\n\n  return localRequire;\n};\nrequire.register(\"component-indexof/index.js\", function(exports, require, module){\n\nvar indexOf = [].indexOf;\n\nmodule.exports = function(arr, obj){\n  if (indexOf) return arr.indexOf(obj);\n  for (var i = 0; i < arr.length; ++i) {\n    if (arr[i] === obj) return i;\n  }\n  return -1;\n};\n});\nrequire.register(\"component-emitter/index.js\", function(exports, require, module){\n\n/**\n * Module dependencies.\n */\n\nvar index = require('indexof');\n\n/**\n * Expose `Emitter`.\n */\n\nmodule.exports = Emitter;\n\n/**\n * Initialize a new `Emitter`.\n *\n * @api public\n */\n\nfunction Emitter(obj) {\n  if (obj) return mixin(obj);\n};\n\n/**\n * Mixin the emitter properties.\n *\n * @param {Object} obj\n * @return {Object}\n * @api private\n */\n\nfunction mixin(obj) {\n  for (var key in Emitter.prototype) {\n    obj[key] = Emitter.prototype[key];\n  }\n  return obj;\n}\n\n/**\n * Listen on the given `event` with `fn`.\n *\n * @param {String} event\n * @param {Function} fn\n * @return {Emitter}\n * @api public\n */\n\nEmitter.prototype.on = function(event, fn){\n  this._callbacks = this._callbacks || {};\n  (this._callbacks[event] = this._callbacks[event] || [])\n    .push(fn);\n  return this;\n};\n\n/**\n * Adds an `event` listener that will be invoked a single\n * time then automatically removed.\n *\n * @param {String} event\n * @param {Function} fn\n * @return {Emitter}\n * @api public\n */\n\nEmitter.prototype.once = function(event, fn){\n  var self = this;\n  this._callbacks = this._callbacks || {};\n\n  function on() {\n    self.off(event, on);\n    fn.apply(this, arguments);\n  }\n\n  fn._off = on;\n  this.on(event, on);\n  return this;\n};\n\n/**\n * Remove the given callback for `event` or all\n * registered callbacks.\n *\n * @param {String} event\n * @param {Function} fn\n * @return {Emitter}\n * @api public\n */\n\nEmitter.prototype.off =\nEmitter.prototype.removeListener =\nEmitter.prototype.removeAllListeners = function(event, fn){\n  this._callbacks = this._callbacks || {};\n\n  // all\n  if (0 == arguments.length) {\n    this._callbacks = {};\n    return this;\n  }\n\n  // specific event\n  var callbacks = this._callbacks[event];\n  if (!callbacks) return this;\n\n  // remove all handlers\n  if (1 == arguments.length) {\n    delete this._callbacks[event];\n    return this;\n  }\n\n  // remove specific handler\n  var i = index(callbacks, fn._off || fn);\n  if (~i) callbacks.splice(i, 1);\n  return this;\n};\n\n/**\n * Emit `event` with the given args.\n *\n * @param {String} event\n * @param {Mixed} ...\n * @return {Emitter}\n */\n\nEmitter.prototype.emit = function(event){\n  this._callbacks = this._callbacks || {};\n  var args = [].slice.call(arguments, 1)\n    , callbacks = this._callbacks[event];\n\n  if (callbacks) {\n    callbacks = callbacks.slice(0);\n    for (var i = 0, len = callbacks.length; i < len; ++i) {\n      callbacks[i].apply(this, args);\n    }\n  }\n\n  return this;\n};\n\n/**\n * Return array of callbacks for `event`.\n *\n * @param {String} event\n * @return {Array}\n * @api public\n */\n\nEmitter.prototype.listeners = function(event){\n  this._callbacks = this._callbacks || {};\n  return this._callbacks[event] || [];\n};\n\n/**\n * Check if this emitter has `event` handlers.\n *\n * @param {String} event\n * @return {Boolean}\n * @api public\n */\n\nEmitter.prototype.hasListeners = function(event){\n  return !! this.listeners(event).length;\n};\n\n});\nrequire.register(\"NetEase-pomelo-protocol/lib/protocol.js\", function(exports, require, module){\n(function (exports, ByteArray, global) {\n  var Protocol = exports;\n\n  var PKG_HEAD_BYTES = 4;\n  var MSG_FLAG_BYTES = 1;\n  var MSG_ROUTE_CODE_BYTES = 2;\n  var MSG_ID_MAX_BYTES = 5;\n  var MSG_ROUTE_LEN_BYTES = 1;\n\n  var MSG_ROUTE_CODE_MAX = 0xffff;\n\n  var MSG_COMPRESS_ROUTE_MASK = 0x1;\n  var MSG_TYPE_MASK = 0x7;\n\n  var Package = Protocol.Package = {};\n  var Message = Protocol.Message = {};\n\n  Package.TYPE_HANDSHAKE = 1;\n  Package.TYPE_HANDSHAKE_ACK = 2;\n  Package.TYPE_HEARTBEAT = 3;\n  Package.TYPE_DATA = 4;\n  Package.TYPE_KICK = 5;\n\n  Message.TYPE_REQUEST = 0;\n  Message.TYPE_NOTIFY = 1;\n  Message.TYPE_RESPONSE = 2;\n  Message.TYPE_PUSH = 3;\n\n  /**\n   * pomele client encode\n   * id message id;\n   * route message route\n   * msg message body\n   * socketio current support string\n   */\n  Protocol.strencode = function(str) {\n    var byteArray = new ByteArray(str.length * 3);\n    var offset = 0;\n    for(var i = 0; i < str.length; i++){\n      var charCode = str.charCodeAt(i);\n      var codes = null;\n      if(charCode <= 0x7f){\n        codes = [charCode];\n      }else if(charCode <= 0x7ff){\n        codes = [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];\n      }else{\n        codes = [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];\n      }\n      for(var j = 0; j < codes.length; j++){\n        byteArray[offset] = codes[j];\n        ++offset;\n      }\n    }\n    var _buffer = new ByteArray(offset);\n    copyArray(_buffer, 0, byteArray, 0, offset);\n    return _buffer;\n  };\n\n  /**\n   * client decode\n   * msg String data\n   * return Message Object\n   */\n  Protocol.strdecode = function(buffer) {\n    var bytes = new ByteArray(buffer);\n    var array = [];\n    var offset = 0;\n    var charCode = 0;\n    var end = bytes.length;\n    while(offset < end){\n      if(bytes[offset] < 128){\n        charCode = bytes[offset];\n        offset += 1;\n      }else if(bytes[offset] < 224){\n        charCode = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);\n        offset += 2;\n      }else{\n        charCode = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);\n        offset += 3;\n      }\n      array.push(charCode);\n    }\n    var res = '';\n    var chunk = 8 * 1024;\n    var i;\n    for (i = 0; i < array.length / chunk; i++) {\n        res += String.fromCharCode.apply(null, array.slice(i * chunk, (i + 1) * chunk));\n    }\n    res += String.fromCharCode.apply(null, array.slice(i * chunk));\n    return res;\n  };\n\n  /**\n   * Package protocol encode.\n   *\n   * Pomelo package format:\n   * +------+-------------+------------------+\n   * | type | body length |       body       |\n   * +------+-------------+------------------+\n   *\n   * Head: 4bytes\n   *   0: package type,\n   *      1 - handshake,\n   *      2 - handshake ack,\n   *      3 - heartbeat,\n   *      4 - data\n   *      5 - kick\n   *   1 - 3: big-endian body length\n   * Body: body length bytes\n   *\n   * @param  {Number}    type   package type\n   * @param  {ByteArray} body   body content in bytes\n   * @return {ByteArray}        new byte array that contains encode result\n   */\n  Package.encode = function(type, body){\n    var length = body ? body.length : 0;\n    var buffer = new ByteArray(PKG_HEAD_BYTES + length);\n    var index = 0;\n    buffer[index++] = type & 0xff;\n    buffer[index++] = (length >> 16) & 0xff;\n    buffer[index++] = (length >> 8) & 0xff;\n    buffer[index++] = length & 0xff;\n    if(body) {\n      copyArray(buffer, index, body, 0, length);\n    }\n    return buffer;\n  };\n\n  /**\n   * Package protocol decode.\n   * See encode for package format.\n   *\n   * @param  {ByteArray} buffer byte array containing package content\n   * @return {Object}           {type: package type, buffer: body byte array}\n   */\n  Package.decode = function(buffer){\n    var bytes =  new ByteArray(buffer);\n    var type = bytes[0];\n    var index = 1;\n    var length = ((bytes[index++]) << 16 | (bytes[index++]) << 8 | bytes[index++]) >>> 0;\n    var body = length ? new ByteArray(length) : null;\n    copyArray(body, 0, bytes, PKG_HEAD_BYTES, length);\n    return {'type': type, 'body': body};\n  };\n\n  /**\n   * Message protocol encode.\n   *\n   * @param  {Number} id            message id\n   * @param  {Number} type          message type\n   * @param  {Number} compressRoute whether compress route\n   * @param  {Number|String} route  route code or route string\n   * @param  {Buffer} msg           message body bytes\n   * @return {Buffer}               encode result\n   */\n  Message.encode = function(id, type, compressRoute, route, msg){\n    // caculate message max length\n    var idBytes = msgHasId(type) ? caculateMsgIdBytes(id) : 0;\n    var msgLen = MSG_FLAG_BYTES + idBytes;\n\n    if(msgHasRoute(type)) {\n      if(compressRoute) {\n        if(typeof route !== 'number'){\n          throw new Error('error flag for number route!');\n        }\n        msgLen += MSG_ROUTE_CODE_BYTES;\n      } else {\n        msgLen += MSG_ROUTE_LEN_BYTES;\n        if(route) {\n          route = Protocol.strencode(route);\n          if(route.length>255) {\n            throw new Error('route maxlength is overflow');\n          }\n          msgLen += route.length;\n        }\n      }\n    }\n\n    if(msg) {\n      msgLen += msg.length;\n    }\n\n    var buffer = new ByteArray(msgLen);\n    var offset = 0;\n\n    // add flag\n    offset = encodeMsgFlag(type, compressRoute, buffer, offset);\n\n    // add message id\n    if(msgHasId(type)) {\n      offset = encodeMsgId(id, idBytes, buffer, offset);\n    }\n\n    // add route\n    if(msgHasRoute(type)) {\n      offset = encodeMsgRoute(compressRoute, route, buffer, offset);\n    }\n\n    // add body\n    if(msg) {\n      offset = encodeMsgBody(msg, buffer, offset);\n    }\n\n    return buffer;\n  };\n\n  /**\n   * Message protocol decode.\n   *\n   * @param  {Buffer|Uint8Array} buffer message bytes\n   * @return {Object}            message object\n   */\n  Message.decode = function(buffer) {\n    var bytes =  new ByteArray(buffer);\n    var bytesLen = bytes.length || bytes.byteLength;\n    var offset = 0;\n    var id = 0;\n    var route = null;\n\n    // parse flag\n    var flag = bytes[offset++];\n    var compressRoute = flag & MSG_COMPRESS_ROUTE_MASK;\n    var type = (flag >> 1) & MSG_TYPE_MASK;\n\n    // parse id\n    if(msgHasId(type)) {\n      var byte = bytes[offset++];\n      id = byte & 0x7f;\n      while(byte & 0x80) {\n        id <<= 7;\n        byte = bytes[offset++];\n        id |= byte & 0x7f;\n      }\n    }\n\n    // parse route\n    if(msgHasRoute(type)) {\n      if(compressRoute) {\n        route = (bytes[offset++]) << 8 | bytes[offset++];\n      } else {\n        var routeLen = bytes[offset++];\n        if(routeLen) {\n          route = new ByteArray(routeLen);\n          copyArray(route, 0, bytes, offset, routeLen);\n          route = Protocol.strdecode(route);\n        } else {\n          route = '';\n        }\n        offset += routeLen;\n      }\n    }\n\n    // parse body\n    var bodyLen = bytesLen - offset;\n    var body = new ByteArray(bodyLen);\n\n    copyArray(body, 0, bytes, offset, bodyLen);\n\n    return {'id': id, 'type': type, 'compressRoute': compressRoute,\n            'route': route, 'body': body};\n  };\n\n  var copyArray = function(dest, doffset, src, soffset, length) {\n    if('function' === typeof src.copy) {\n      // Buffer\n      src.copy(dest, doffset, soffset, soffset + length);\n    } else {\n      // Uint8Array\n      for(var index=0; index<length; index++){\n        dest[doffset++] = src[soffset++];\n      }\n    }\n  };\n\n  var msgHasId = function(type) {\n    return type === Message.TYPE_REQUEST || type === Message.TYPE_RESPONSE;\n  };\n\n  var msgHasRoute = function(type) {\n    return type === Message.TYPE_REQUEST || type === Message.TYPE_NOTIFY ||\n           type === Message.TYPE_PUSH;\n  };\n\n  var caculateMsgIdBytes = function(id) {\n    var len = 0;\n    do {\n      len += 1;\n      id >>= 7;\n    } while(id > 0);\n    return len;\n  };\n\n  var encodeMsgFlag = function(type, compressRoute, buffer, offset) {\n    if(type !== Message.TYPE_REQUEST && type !== Message.TYPE_NOTIFY &&\n       type !== Message.TYPE_RESPONSE && type !== Message.TYPE_PUSH) {\n      throw new Error('unkonw message type: ' + type);\n    }\n\n    buffer[offset] = (type << 1) | (compressRoute ? 1 : 0);\n\n    return offset + MSG_FLAG_BYTES;\n  };\n\n  var encodeMsgId = function(id, idBytes, buffer, offset) {\n    var index = offset + idBytes - 1;\n    buffer[index--] = id & 0x7f;\n    while(index >= offset) {\n      id >>= 7;\n      buffer[index--] = id & 0x7f | 0x80;\n    }\n    return offset + idBytes;\n  };\n\n  var encodeMsgRoute = function(compressRoute, route, buffer, offset) {\n    if (compressRoute) {\n      if(route > MSG_ROUTE_CODE_MAX){\n        throw new Error('route number is overflow');\n      }\n\n      buffer[offset++] = (route >> 8) & 0xff;\n      buffer[offset++] = route & 0xff;\n    } else {\n      if(route) {\n        buffer[offset++] = route.length & 0xff;\n        copyArray(buffer, offset, route, 0, route.length);\n        offset += route.length;\n      } else {\n        buffer[offset++] = 0;\n      }\n    }\n\n    return offset;\n  };\n\n  var encodeMsgBody = function(msg, buffer, offset) {\n    copyArray(buffer, offset, msg, 0, msg.length);\n    return offset + msg.length;\n  };\n\n  module.exports = Protocol;\n})('object' === typeof module ? module.exports : (this.Protocol = {}),'object' === typeof module ? Buffer : Uint8Array, this);\n\n});\nrequire.register(\"pomelonode-pomelo-protobuf/lib/client/protobuf.js\", function(exports, require, module){\n/* ProtocolBuffer client 0.1.0*/\n\n/**\n * pomelo-protobuf\n * @author <zhang0935@gmail.com>\n */\n\n/**\n * Protocol buffer root\n * In browser, it will be window.protbuf\n */\n(function (exports, global){\n  var Protobuf = exports;\n\n  Protobuf.init = function(opts){\n    //On the serverside, use serverProtos to encode messages send to client\n    Protobuf.encoder.init(opts.encoderProtos);\n\n    //On the serverside, user clientProtos to decode messages receive from clients\n    Protobuf.decoder.init(opts.decoderProtos);\n  };\n\n  Protobuf.encode = function(key, msg){\n    return Protobuf.encoder.encode(key, msg);\n  };\n\n  Protobuf.decode = function(key, msg){\n    return Protobuf.decoder.decode(key, msg);\n  };\n\n  // exports to support for components\n  module.exports = Protobuf;\n})('object' === typeof module ? module.exports : (this.protobuf = {}), this);\n\n/**\n * constants\n */\n(function (exports, global){\n  var constants = exports.constants = {};\n\n  constants.TYPES = {\n    uInt32 : 0,\n    sInt32 : 0,\n    int32 : 0,\n    double : 1,\n    string : 2,\n    message : 2,\n    float : 5\n  };\n\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n/**\n * util module\n */\n(function (exports, global){\n\n  var Util = exports.util = {};\n\n  Util.isSimpleType = function(type){\n    return ( type === 'uInt32' ||\n             type === 'sInt32' ||\n             type === 'int32'  ||\n             type === 'uInt64' ||\n             type === 'sInt64' ||\n             type === 'float'  ||\n             type === 'double' );\n  };\n\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n/**\n * codec module\n */\n(function (exports, global){\n\n  var Codec = exports.codec = {};\n\n  var buffer = new ArrayBuffer(8);\n  var float32Array = new Float32Array(buffer);\n  var float64Array = new Float64Array(buffer);\n  var uInt8Array = new Uint8Array(buffer);\n\n  Codec.encodeUInt32 = function(n){\n    var n = parseInt(n);\n    if(isNaN(n) || n < 0){\n      return null;\n    }\n\n    var result = [];\n    do{\n      var tmp = n % 128;\n      var next = Math.floor(n/128);\n\n      if(next !== 0){\n        tmp = tmp + 128;\n      }\n      result.push(tmp);\n      n = next;\n    }while(n !== 0);\n\n    return result;\n  };\n\n  Codec.encodeSInt32 = function(n){\n    var n = parseInt(n);\n    if(isNaN(n)){\n      return null;\n    }\n    n = n<0?(Math.abs(n)*2-1):n*2;\n\n    return Codec.encodeUInt32(n);\n  };\n\n  Codec.decodeUInt32 = function(bytes){\n    var n = 0;\n\n    for(var i = 0; i < bytes.length; i++){\n      var m = parseInt(bytes[i]);\n      n = n + ((m & 0x7f) * Math.pow(2,(7*i)));\n      if(m < 128){\n        return n;\n      }\n    }\n\n    return n;\n  };\n\n\n  Codec.decodeSInt32 = function(bytes){\n    var n = this.decodeUInt32(bytes);\n    var flag = ((n%2) === 1)?-1:1;\n\n    n = ((n%2 + n)/2)*flag;\n\n    return n;\n  };\n\n  Codec.encodeFloat = function(float){\n    float32Array[0] = float;\n    return uInt8Array;\n  };\n\n  Codec.decodeFloat = function(bytes, offset){\n    if(!bytes || bytes.length < (offset +4)){\n      return null;\n    }\n\n    for(var i = 0; i < 4; i++){\n      uInt8Array[i] = bytes[offset + i];\n    }\n\n    return float32Array[0];\n  };\n\n  Codec.encodeDouble = function(double){\n    float64Array[0] = double;\n    return uInt8Array.subarray(0, 8);\n  };\n\n  Codec.decodeDouble = function(bytes, offset){\n    if(!bytes || bytes.length < (8 + offset)){\n      return null;\n    }\n\n    for(var i = 0; i < 8; i++){\n      uInt8Array[i] = bytes[offset + i];\n    }\n\n    return float64Array[0];\n  };\n\n  Codec.encodeStr = function(bytes, offset, str){\n    for(var i = 0; i < str.length; i++){\n      var code = str.charCodeAt(i);\n      var codes = encode2UTF8(code);\n\n      for(var j = 0; j < codes.length; j++){\n        bytes[offset] = codes[j];\n        offset++;\n      }\n    }\n\n    return offset;\n  };\n\n  /**\n   * Decode string from utf8 bytes\n   */\n  Codec.decodeStr = function(bytes, offset, length){\n    var array = [];\n    var end = offset + length;\n\n    while(offset < end){\n      var code = 0;\n\n      if(bytes[offset] < 128){\n        code = bytes[offset];\n\n        offset += 1;\n      }else if(bytes[offset] < 224){\n        code = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);\n        offset += 2;\n      }else{\n        code = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);\n        offset += 3;\n      }\n\n      array.push(code);\n\n    }\n\n    var str = '';\n    for(var i = 0; i < array.length;){\n      str += String.fromCharCode.apply(null, array.slice(i, i + 10000));\n      i += 10000;\n    }\n\n    return str;\n  };\n\n  /**\n   * Return the byte length of the str use utf8\n   */\n  Codec.byteLength = function(str){\n    if(typeof(str) !== 'string'){\n      return -1;\n    }\n\n    var length = 0;\n\n    for(var i = 0; i < str.length; i++){\n      var code = str.charCodeAt(i);\n      length += codeLength(code);\n    }\n\n    return length;\n  };\n\n  /**\n   * Encode a unicode16 char code to utf8 bytes\n   */\n  function encode2UTF8(charCode){\n    if(charCode <= 0x7f){\n      return [charCode];\n    }else if(charCode <= 0x7ff){\n      return [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];\n    }else{\n      return [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];\n    }\n  }\n\n  function codeLength(code){\n    if(code <= 0x7f){\n      return 1;\n    }else if(code <= 0x7ff){\n      return 2;\n    }else{\n      return 3;\n    }\n  }\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n/**\n * encoder module\n */\n(function (exports, global){\n\n  var protobuf = exports;\n  var MsgEncoder = exports.encoder = {};\n\n  var codec = protobuf.codec;\n  var constant = protobuf.constants;\n  var util = protobuf.util;\n\n  MsgEncoder.init = function(protos){\n    this.protos = protos || {};\n  };\n\n  MsgEncoder.encode = function(route, msg){\n    //Get protos from protos map use the route as key\n    var protos = this.protos[route];\n\n    //Check msg\n    if(!checkMsg(msg, protos)){\n      return null;\n    }\n\n    //Set the length of the buffer 2 times bigger to prevent overflow\n    var length = codec.byteLength(JSON.stringify(msg));\n\n    //Init buffer and offset\n    var buffer = new ArrayBuffer(length);\n    var uInt8Array = new Uint8Array(buffer);\n    var offset = 0;\n\n    if(!!protos){\n      offset = encodeMsg(uInt8Array, offset, protos, msg);\n      if(offset > 0){\n        return uInt8Array.subarray(0, offset);\n      }\n    }\n\n    return null;\n  };\n\n  /**\n   * Check if the msg follow the defination in the protos\n   */\n  function checkMsg(msg, protos){\n    if(!protos){\n      return false;\n    }\n\n    for(var name in protos){\n      var proto = protos[name];\n\n      //All required element must exist\n      switch(proto.option){\n        case 'required' :\n          if(typeof(msg[name]) === 'undefined'){\n            return false;\n          }\n        case 'optional' :\n          if(typeof(msg[name]) !== 'undefined'){\n            if(!!protos.__messages[proto.type]){\n              checkMsg(msg[name], protos.__messages[proto.type]);\n            }\n          }\n        break;\n        case 'repeated' :\n          //Check nest message in repeated elements\n          if(!!msg[name] && !!protos.__messages[proto.type]){\n            for(var i = 0; i < msg[name].length; i++){\n              if(!checkMsg(msg[name][i], protos.__messages[proto.type])){\n                return false;\n              }\n            }\n          }\n        break;\n      }\n    }\n\n    return true;\n  }\n\n  function encodeMsg(buffer, offset, protos, msg){\n    for(var name in msg){\n      if(!!protos[name]){\n        var proto = protos[name];\n\n        switch(proto.option){\n          case 'required' :\n          case 'optional' :\n            offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));\n            offset = encodeProp(msg[name], proto.type, offset, buffer, protos);\n          break;\n          case 'repeated' :\n            if(msg[name].length > 0){\n              offset = encodeArray(msg[name], proto, offset, buffer, protos);\n            }\n          break;\n        }\n      }\n    }\n\n    return offset;\n  }\n\n  function encodeProp(value, type, offset, buffer, protos){\n    switch(type){\n      case 'uInt32':\n        offset = writeBytes(buffer, offset, codec.encodeUInt32(value));\n      break;\n      case 'int32' :\n      case 'sInt32':\n        offset = writeBytes(buffer, offset, codec.encodeSInt32(value));\n      break;\n      case 'float':\n        writeBytes(buffer, offset, codec.encodeFloat(value));\n        offset += 4;\n      break;\n      case 'double':\n        writeBytes(buffer, offset, codec.encodeDouble(value));\n        offset += 8;\n      break;\n      case 'string':\n        var length = codec.byteLength(value);\n\n        //Encode length\n        offset = writeBytes(buffer, offset, codec.encodeUInt32(length));\n        //write string\n        codec.encodeStr(buffer, offset, value);\n        offset += length;\n      break;\n      default :\n        if(!!protos.__messages[type]){\n          //Use a tmp buffer to build an internal msg\n          var tmpBuffer = new ArrayBuffer(codec.byteLength(JSON.stringify(value)));\n          var length = 0;\n\n          length = encodeMsg(tmpBuffer, length, protos.__messages[type], value);\n          //Encode length\n          offset = writeBytes(buffer, offset, codec.encodeUInt32(length));\n          //contact the object\n          for(var i = 0; i < length; i++){\n            buffer[offset] = tmpBuffer[i];\n            offset++;\n          }\n        }\n      break;\n    }\n\n    return offset;\n  }\n\n  /**\n   * Encode reapeated properties, simple msg and object are decode differented\n   */\n  function encodeArray(array, proto, offset, buffer, protos){\n    var i = 0;\n\n    if(util.isSimpleType(proto.type)){\n      offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));\n      offset = writeBytes(buffer, offset, codec.encodeUInt32(array.length));\n      for(i = 0; i < array.length; i++){\n        offset = encodeProp(array[i], proto.type, offset, buffer);\n      }\n    }else{\n      for(i = 0; i < array.length; i++){\n        offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));\n        offset = encodeProp(array[i], proto.type, offset, buffer, protos);\n      }\n    }\n\n    return offset;\n  }\n\n  function writeBytes(buffer, offset, bytes){\n    for(var i = 0; i < bytes.length; i++, offset++){\n      buffer[offset] = bytes[i];\n    }\n\n    return offset;\n  }\n\n  function encodeTag(type, tag){\n    var value = constant.TYPES[type]||2;\n    return codec.encodeUInt32((tag<<3)|value);\n  }\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n/**\n * decoder module\n */\n(function (exports, global){\n  var protobuf = exports;\n  var MsgDecoder = exports.decoder = {};\n\n  var codec = protobuf.codec;\n  var util = protobuf.util;\n\n  var buffer;\n  var offset = 0;\n\n  MsgDecoder.init = function(protos){\n    this.protos = protos || {};\n  };\n\n  MsgDecoder.setProtos = function(protos){\n    if(!!protos){\n      this.protos = protos;\n    }\n  };\n\n  MsgDecoder.decode = function(route, buf){\n    var protos = this.protos[route];\n\n    buffer = buf;\n    offset = 0;\n\n    if(!!protos){\n      return decodeMsg({}, protos, buffer.length);\n    }\n\n    return null;\n  };\n\n  function decodeMsg(msg, protos, length){\n    while(offset<length){\n      var head = getHead();\n      var type = head.type;\n      var tag = head.tag;\n      var name = protos.__tags[tag];\n\n      switch(protos[name].option){\n        case 'optional' :\n        case 'required' :\n          msg[name] = decodeProp(protos[name].type, protos);\n        break;\n        case 'repeated' :\n          if(!msg[name]){\n            msg[name] = [];\n          }\n          decodeArray(msg[name], protos[name].type, protos);\n        break;\n      }\n    }\n\n    return msg;\n  }\n\n  /**\n   * Test if the given msg is finished\n   */\n  function isFinish(msg, protos){\n    return (!protos.__tags[peekHead().tag]);\n  }\n  /**\n   * Get property head from protobuf\n   */\n  function getHead(){\n    var tag = codec.decodeUInt32(getBytes());\n\n    return {\n      type : tag&0x7,\n      tag : tag>>3\n    };\n  }\n\n  /**\n   * Get tag head without move the offset\n   */\n  function peekHead(){\n    var tag = codec.decodeUInt32(peekBytes());\n\n    return {\n      type : tag&0x7,\n      tag : tag>>3\n    };\n  }\n\n  function decodeProp(type, protos){\n    switch(type){\n      case 'uInt32':\n        return codec.decodeUInt32(getBytes());\n      case 'int32' :\n      case 'sInt32' :\n        return codec.decodeSInt32(getBytes());\n      case 'float' :\n        var float = codec.decodeFloat(buffer, offset);\n        offset += 4;\n        return float;\n      case 'double' :\n        var double = codec.decodeDouble(buffer, offset);\n        offset += 8;\n        return double;\n      case 'string' :\n        var length = codec.decodeUInt32(getBytes());\n\n        var str =  codec.decodeStr(buffer, offset, length);\n        offset += length;\n\n        return str;\n      default :\n        if(!!protos && !!protos.__messages[type]){\n          var length = codec.decodeUInt32(getBytes());\n          var msg = {};\n          decodeMsg(msg, protos.__messages[type], offset+length);\n          return msg;\n        }\n      break;\n    }\n  }\n\n  function decodeArray(array, type, protos){\n    if(util.isSimpleType(type)){\n      var length = codec.decodeUInt32(getBytes());\n\n      for(var i = 0; i < length; i++){\n        array.push(decodeProp(type));\n      }\n    }else{\n      array.push(decodeProp(type, protos));\n    }\n  }\n\n  function getBytes(flag){\n    var bytes = [];\n    var pos = offset;\n    flag = flag || false;\n\n    var b;\n\n    do{\n      b = buffer[pos];\n      bytes.push(b);\n      pos++;\n    }while(b >= 128);\n\n    if(!flag){\n      offset = pos;\n    }\n    return bytes;\n  }\n\n  function peekBytes(){\n    return getBytes(true);\n  }\n\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n\n});\nrequire.register(\"pomelonode-pomelo-jsclient-websocket/lib/pomelo-client.js\", function(exports, require, module){\n(function() {\n  var JS_WS_CLIENT_TYPE = 'js-websocket';\n  var JS_WS_CLIENT_VERSION = '0.0.1';\n\n  var Protocol = window.Protocol;\n  var Package = Protocol.Package;\n  var Message = Protocol.Message;\n  var EventEmitter = window.EventEmitter;\n\n  var RES_OK = 200;\n  var RES_FAIL = 500;\n  var RES_OLD_CLIENT = 501;\n\n  if (typeof Object.create !== 'function') {\n    Object.create = function (o) {\n      function F() {}\n      F.prototype = o;\n      return new F();\n    };\n  }\n\n  var root = window;\n  var pomelo = Object.create(EventEmitter.prototype); // object extend from object\n  root.pomelo = pomelo;\n  var socket = null;\n  var reqId = 0;\n  var callbacks = {};\n  var handlers = {};\n  //Map from request id to route\n  var routeMap = {};\n\n  var heartbeatInterval = 0;\n  var heartbeatTimeout = 0;\n  var nextHeartbeatTimeout = 0;\n  var gapThreshold = 100;   // heartbeat gap threashold\n  var heartbeatId = null;\n  var heartbeatTimeoutId = null;\n\n  var handshakeCallback = null;\n\n  var handshakeBuffer = {\n    'sys': {\n      type: JS_WS_CLIENT_TYPE,\n      version: JS_WS_CLIENT_VERSION\n    },\n    'user': {\n    }\n  };\n\n  var initCallback = null;\n\n  pomelo.init = function(params, cb){\n    initCallback = cb;\n    var host = params.host;\n    var port = params.port;\n\n    var url = 'ws://' + host;\n    if(port) {\n      url +=  ':' + port;\n    }\n\n    handshakeBuffer.user = params.user;\n    handshakeCallback = params.handshakeCallback;\n    initWebSocket(url, cb);\n  };\n\n  var initWebSocket = function(url,cb) {\n    console.log('connect to ' + url);\n    var onopen = function(event){\n      var obj = Package.encode(Package.TYPE_HANDSHAKE, Protocol.strencode(JSON.stringify(handshakeBuffer)));\n      send(obj);\n    };\n    var onmessage = function(event) {\n      processPackage(Package.decode(event.data), cb);\n      // new package arrived, update the heartbeat timeout\n      if(heartbeatTimeout) {\n        nextHeartbeatTimeout = Date.now() + heartbeatTimeout;\n      }\n    };\n    var onerror = function(event) {\n      pomelo.emit('io-error', event);\n      console.error('socket error: ', event);\n    };\n    var onclose = function(event){\n      pomelo.emit('close',event);\n      console.error('socket close: ', event);\n    };\n    socket = new WebSocket(url);\n    socket.binaryType = 'arraybuffer';\n    socket.onopen = onopen;\n    socket.onmessage = onmessage;\n    socket.onerror = onerror;\n    socket.onclose = onclose;\n  };\n\n  pomelo.disconnect = function() {\n    if(socket) {\n      if(socket.disconnect) socket.disconnect();\n      if(socket.close) socket.close();\n      console.log('disconnect');\n      socket = null;\n    }\n\n    if(heartbeatId) {\n      clearTimeout(heartbeatId);\n      heartbeatId = null;\n    }\n    if(heartbeatTimeoutId) {\n      clearTimeout(heartbeatTimeoutId);\n      heartbeatTimeoutId = null;\n    }\n  };\n\n  pomelo.request = function(route, msg, cb) {\n    if(arguments.length === 2 && typeof msg === 'function') {\n      cb = msg;\n      msg = {};\n    } else {\n      msg = msg || {};\n    }\n    route = route || msg.route;\n    if(!route) {\n      return;\n    }\n\n    reqId++;\n    sendMessage(reqId, route, msg);\n\n    callbacks[reqId] = cb;\n    routeMap[reqId] = route;\n  };\n\n  pomelo.notify = function(route, msg) {\n    msg = msg || {};\n    sendMessage(0, route, msg);\n  };\n\n  var sendMessage = function(reqId, route, msg) {\n    var type = reqId ? Message.TYPE_REQUEST : Message.TYPE_NOTIFY;\n\n    //compress message by protobuf\n    var protos = !!pomelo.data.protos?pomelo.data.protos.client:{};\n    if(!!protos[route]){\n      msg = protobuf.encode(route, msg);\n    }else{\n      msg = Protocol.strencode(JSON.stringify(msg));\n    }\n\n\n    var compressRoute = 0;\n    if(pomelo.dict && pomelo.dict[route]){\n      route = pomelo.dict[route];\n      compressRoute = 1;\n    }\n\n    msg = Message.encode(reqId, type, compressRoute, route, msg);\n    var packet = Package.encode(Package.TYPE_DATA, msg);\n    send(packet);\n  };\n\n  var send = function(packet){\n    socket.send(packet.buffer);\n  };\n\n\n  var handler = {};\n\n  var heartbeat = function(data) {\n    if(!heartbeatInterval) {\n      // no heartbeat\n      return;\n    }\n\n    var obj = Package.encode(Package.TYPE_HEARTBEAT);\n    if(heartbeatTimeoutId) {\n      clearTimeout(heartbeatTimeoutId);\n      heartbeatTimeoutId = null;\n    }\n\n    if(heartbeatId) {\n      // already in a heartbeat interval\n      return;\n    }\n\n    heartbeatId = setTimeout(function() {\n      heartbeatId = null;\n      send(obj);\n\n      nextHeartbeatTimeout = Date.now() + heartbeatTimeout;\n      heartbeatTimeoutId = setTimeout(heartbeatTimeoutCb, heartbeatTimeout);\n    }, heartbeatInterval);\n  };\n\n  var heartbeatTimeoutCb = function() {\n    var gap = nextHeartbeatTimeout - Date.now();\n    if(gap > gapThreshold) {\n      heartbeatTimeoutId = setTimeout(heartbeatTimeoutCb, gap);\n    } else {\n      console.error('server heartbeat timeout');\n      pomelo.emit('heartbeat timeout');\n      pomelo.disconnect();\n    }\n  };\n\n  var handshake = function(data){\n    data = JSON.parse(Protocol.strdecode(data));\n    if(data.code === RES_OLD_CLIENT) {\n      pomelo.emit('error', 'client version not fullfill');\n      return;\n    }\n\n    if(data.code !== RES_OK) {\n      pomelo.emit('error', 'handshake fail');\n      return;\n    }\n\n    handshakeInit(data);\n\n    var obj = Package.encode(Package.TYPE_HANDSHAKE_ACK);\n    send(obj);\n    if(initCallback) {\n      initCallback(socket);\n      initCallback = null;\n    }\n  };\n\n  var onData = function(data){\n    //probuff decode\n    var msg = Message.decode(data);\n\n    if(msg.id > 0){\n      msg.route = routeMap[msg.id];\n      delete routeMap[msg.id];\n      if(!msg.route){\n        return;\n      }\n    }\n\n    msg.body = deCompose(msg);\n\n    processMessage(pomelo, msg);\n  };\n\n  var onKick = function(data) {\n    pomelo.emit('onKick');\n  };\n\n  handlers[Package.TYPE_HANDSHAKE] = handshake;\n  handlers[Package.TYPE_HEARTBEAT] = heartbeat;\n  handlers[Package.TYPE_DATA] = onData;\n  handlers[Package.TYPE_KICK] = onKick;\n\n  var processPackage = function(msg) {\n    handlers[msg.type](msg.body);\n  };\n\n  var processMessage = function(pomelo, msg) {\n    if(!msg.id) {\n      // server push message\n      pomelo.emit(msg.route, msg.body);\n      return;\n    }\n\n    //if have a id then find the callback function with the request\n    var cb = callbacks[msg.id];\n\n    delete callbacks[msg.id];\n    if(typeof cb !== 'function') {\n      return;\n    }\n\n    cb(msg.body);\n    return;\n  };\n\n  var processMessageBatch = function(pomelo, msgs) {\n    for(var i=0, l=msgs.length; i<l; i++) {\n      processMessage(pomelo, msgs[i]);\n    }\n  };\n\n  var deCompose = function(msg){\n    var protos = !!pomelo.data.protos?pomelo.data.protos.server:{};\n    var abbrs = pomelo.data.abbrs;\n    var route = msg.route;\n\n    //Decompose route from dict\n    if(msg.compressRoute) {\n      if(!abbrs[route]){\n        return {};\n      }\n\n      route = msg.route = abbrs[route];\n    }\n    if(!!protos[route]){\n      return protobuf.decode(route, msg.body);\n    }else{\n      return JSON.parse(Protocol.strdecode(msg.body));\n    }\n\n    return msg;\n  };\n\n  var handshakeInit = function(data){\n    if(data.sys && data.sys.heartbeat) {\n      heartbeatInterval = data.sys.heartbeat * 1000;   // heartbeat interval\n      heartbeatTimeout = heartbeatInterval * 2;        // max heartbeat timeout\n    } else {\n      heartbeatInterval = 0;\n      heartbeatTimeout = 0;\n    }\n\n    initData(data);\n\n    if(typeof handshakeCallback === 'function') {\n      handshakeCallback(data.user);\n    }\n  };\n\n  //Initilize data used in pomelo client\n  var initData = function(data){\n    if(!data || !data.sys) {\n      return;\n    }\n    pomelo.data = pomelo.data || {};\n    var dict = data.sys.dict;\n    var protos = data.sys.protos;\n\n    //Init compress dict\n    if(dict){\n      pomelo.data.dict = dict;\n      pomelo.data.abbrs = {};\n\n      for(var route in dict){\n        pomelo.data.abbrs[dict[route]] = route;\n      }\n    }\n\n    //Init protobuf protos\n    if(protos){\n      pomelo.data.protos = {\n        server : protos.server || {},\n        client : protos.client || {}\n      };\n      if(!!protobuf){\n        protobuf.init({encoderProtos: protos.client, decoderProtos: protos.server});\n      }\n    }\n  };\n\n  module.exports = pomelo;\n})();\n\n});\nrequire.register(\"boot/index.js\", function(exports, require, module){\n  var Emitter = require('emitter');\n  window.EventEmitter = Emitter;\n\n  var protocol = require('pomelo-protocol');\n  window.Protocol = protocol;\n\n  var protobuf = require('pomelo-protobuf');\n  window.protobuf = protobuf;\n\n  var pomelo = require('pomelo-jsclient-websocket');\n  window.pomelo = pomelo;\n\n});\nrequire.alias(\"boot/index.js\", \"pomelo-client/deps/boot/index.js\");\nrequire.alias(\"component-emitter/index.js\", \"boot/deps/emitter/index.js\");\nrequire.alias(\"component-indexof/index.js\", \"component-emitter/deps/indexof/index.js\");\n\nrequire.alias(\"NetEase-pomelo-protocol/lib/protocol.js\", \"boot/deps/pomelo-protocol/lib/protocol.js\");\nrequire.alias(\"NetEase-pomelo-protocol/lib/protocol.js\", \"boot/deps/pomelo-protocol/index.js\");\nrequire.alias(\"NetEase-pomelo-protocol/lib/protocol.js\", \"NetEase-pomelo-protocol/index.js\");\n\nrequire.alias(\"pomelonode-pomelo-protobuf/lib/client/protobuf.js\", \"boot/deps/pomelo-protobuf/lib/client/protobuf.js\");\nrequire.alias(\"pomelonode-pomelo-protobuf/lib/client/protobuf.js\", \"boot/deps/pomelo-protobuf/index.js\");\nrequire.alias(\"pomelonode-pomelo-protobuf/lib/client/protobuf.js\", \"pomelonode-pomelo-protobuf/index.js\");\n\nrequire.alias(\"pomelonode-pomelo-jsclient-websocket/lib/pomelo-client.js\", \"boot/deps/pomelo-jsclient-websocket/lib/pomelo-client.js\");\nrequire.alias(\"pomelonode-pomelo-jsclient-websocket/lib/pomelo-client.js\", \"boot/deps/pomelo-jsclient-websocket/index.js\");\nrequire.alias(\"pomelonode-pomelo-jsclient-websocket/lib/pomelo-client.js\", \"pomelonode-pomelo-jsclient-websocket/index.js\");\n\n"
  },
  {
    "path": "template/web-server/public/js/lib/build/build.js.wss",
    "content": "\n\n/**\n * hasOwnProperty.\n */\n\nvar has = Object.prototype.hasOwnProperty;\n\n/**\n * Require the given path.\n *\n * @param {String} path\n * @return {Object} exports\n * @api public\n */\n\nfunction require(path, parent, orig) {\n  var resolved = require.resolve(path);\n\n  // lookup failed\n  if (null == resolved) {\n    orig = orig || path;\n    parent = parent || 'root';\n    var err = new Error('Failed to require \"' + orig + '\" from \"' + parent + '\"');\n    err.path = orig;\n    err.parent = parent;\n    err.require = true;\n    throw err;\n  }\n\n  var module = require.modules[resolved];\n\n  // perform real require()\n  // by invoking the module's\n  // registered function\n  if (!module.exports) {\n    module.exports = {};\n    module.client = module.component = true;\n    module.call(this, module.exports, require.relative(resolved), module);\n  }\n\n  return module.exports;\n}\n\n/**\n * Registered modules.\n */\n\nrequire.modules = {};\n\n/**\n * Registered aliases.\n */\n\nrequire.aliases = {};\n\n/**\n * Resolve `path`.\n *\n * Lookup:\n *\n *   - PATH/index.js\n *   - PATH.js\n *   - PATH\n *\n * @param {String} path\n * @return {String} path or null\n * @api private\n */\n\nrequire.resolve = function(path) {\n  if (path.charAt(0) === '/') path = path.slice(1);\n  var index = path + '/index.js';\n\n  var paths = [\n    path,\n    path + '.js',\n    path + '.json',\n    path + '/index.js',\n    path + '/index.json'\n  ];\n\n  for (var i = 0; i < paths.length; i++) {\n    var path = paths[i];\n    if (has.call(require.modules, path)) return path;\n  }\n\n  if (has.call(require.aliases, index)) {\n    return require.aliases[index];\n  }\n};\n\n/**\n * Normalize `path` relative to the current path.\n *\n * @param {String} curr\n * @param {String} path\n * @return {String}\n * @api private\n */\n\nrequire.normalize = function(curr, path) {\n  var segs = [];\n\n  if ('.' != path.charAt(0)) return path;\n\n  curr = curr.split('/');\n  path = path.split('/');\n\n  for (var i = 0; i < path.length; ++i) {\n    if ('..' == path[i]) {\n      curr.pop();\n    } else if ('.' != path[i] && '' != path[i]) {\n      segs.push(path[i]);\n    }\n  }\n\n  return curr.concat(segs).join('/');\n};\n\n/**\n * Register module at `path` with callback `definition`.\n *\n * @param {String} path\n * @param {Function} definition\n * @api private\n */\n\nrequire.register = function(path, definition) {\n  require.modules[path] = definition;\n};\n\n/**\n * Alias a module definition.\n *\n * @param {String} from\n * @param {String} to\n * @api private\n */\n\nrequire.alias = function(from, to) {\n  if (!has.call(require.modules, from)) {\n    throw new Error('Failed to alias \"' + from + '\", it does not exist');\n  }\n  require.aliases[to] = from;\n};\n\n/**\n * Return a require function relative to the `parent` path.\n *\n * @param {String} parent\n * @return {Function}\n * @api private\n */\n\nrequire.relative = function(parent) {\n  var p = require.normalize(parent, '..');\n\n  /**\n   * lastIndexOf helper.\n   */\n\n  function lastIndexOf(arr, obj) {\n    var i = arr.length;\n    while (i--) {\n      if (arr[i] === obj) return i;\n    }\n    return -1;\n  }\n\n  /**\n   * The relative require() itself.\n   */\n\n  function localRequire(path) {\n    var resolved = localRequire.resolve(path);\n    return require(resolved, parent, path);\n  }\n\n  /**\n   * Resolve relative to the parent.\n   */\n\n  localRequire.resolve = function(path) {\n    var c = path.charAt(0);\n    if ('/' == c) return path.slice(1);\n    if ('.' == c) return require.normalize(p, path);\n\n    // resolve deps by returning\n    // the dep in the nearest \"deps\"\n    // directory\n    var segs = parent.split('/');\n    var i = lastIndexOf(segs, 'deps') + 1;\n    if (!i) i = 0;\n    path = segs.slice(0, i + 1).join('/') + '/deps/' + path;\n    return path;\n  };\n\n  /**\n   * Check if module is defined at `path`.\n   */\n\n  localRequire.exists = function(path) {\n    return has.call(require.modules, localRequire.resolve(path));\n  };\n\n  return localRequire;\n};\nrequire.register(\"component-indexof/index.js\", function(exports, require, module){\n\nvar indexOf = [].indexOf;\n\nmodule.exports = function(arr, obj){\n  if (indexOf) return arr.indexOf(obj);\n  for (var i = 0; i < arr.length; ++i) {\n    if (arr[i] === obj) return i;\n  }\n  return -1;\n};\n});\nrequire.register(\"component-emitter/index.js\", function(exports, require, module){\n\n/**\n * Module dependencies.\n */\n\nvar index = require('indexof');\n\n/**\n * Expose `Emitter`.\n */\n\nmodule.exports = Emitter;\n\n/**\n * Initialize a new `Emitter`.\n *\n * @api public\n */\n\nfunction Emitter(obj) {\n  if (obj) return mixin(obj);\n};\n\n/**\n * Mixin the emitter properties.\n *\n * @param {Object} obj\n * @return {Object}\n * @api private\n */\n\nfunction mixin(obj) {\n  for (var key in Emitter.prototype) {\n    obj[key] = Emitter.prototype[key];\n  }\n  return obj;\n}\n\n/**\n * Listen on the given `event` with `fn`.\n *\n * @param {String} event\n * @param {Function} fn\n * @return {Emitter}\n * @api public\n */\n\nEmitter.prototype.on = function(event, fn){\n  this._callbacks = this._callbacks || {};\n  (this._callbacks[event] = this._callbacks[event] || [])\n    .push(fn);\n  return this;\n};\n\n/**\n * Adds an `event` listener that will be invoked a single\n * time then automatically removed.\n *\n * @param {String} event\n * @param {Function} fn\n * @return {Emitter}\n * @api public\n */\n\nEmitter.prototype.once = function(event, fn){\n  var self = this;\n  this._callbacks = this._callbacks || {};\n\n  function on() {\n    self.off(event, on);\n    fn.apply(this, arguments);\n  }\n\n  fn._off = on;\n  this.on(event, on);\n  return this;\n};\n\n/**\n * Remove the given callback for `event` or all\n * registered callbacks.\n *\n * @param {String} event\n * @param {Function} fn\n * @return {Emitter}\n * @api public\n */\n\nEmitter.prototype.off =\nEmitter.prototype.removeListener =\nEmitter.prototype.removeAllListeners = function(event, fn){\n  this._callbacks = this._callbacks || {};\n\n  // all\n  if (0 == arguments.length) {\n    this._callbacks = {};\n    return this;\n  }\n\n  // specific event\n  var callbacks = this._callbacks[event];\n  if (!callbacks) return this;\n\n  // remove all handlers\n  if (1 == arguments.length) {\n    delete this._callbacks[event];\n    return this;\n  }\n\n  // remove specific handler\n  var i = index(callbacks, fn._off || fn);\n  if (~i) callbacks.splice(i, 1);\n  return this;\n};\n\n/**\n * Emit `event` with the given args.\n *\n * @param {String} event\n * @param {Mixed} ...\n * @return {Emitter}\n */\n\nEmitter.prototype.emit = function(event){\n  this._callbacks = this._callbacks || {};\n  var args = [].slice.call(arguments, 1)\n    , callbacks = this._callbacks[event];\n\n  if (callbacks) {\n    callbacks = callbacks.slice(0);\n    for (var i = 0, len = callbacks.length; i < len; ++i) {\n      callbacks[i].apply(this, args);\n    }\n  }\n\n  return this;\n};\n\n/**\n * Return array of callbacks for `event`.\n *\n * @param {String} event\n * @return {Array}\n * @api public\n */\n\nEmitter.prototype.listeners = function(event){\n  this._callbacks = this._callbacks || {};\n  return this._callbacks[event] || [];\n};\n\n/**\n * Check if this emitter has `event` handlers.\n *\n * @param {String} event\n * @return {Boolean}\n * @api public\n */\n\nEmitter.prototype.hasListeners = function(event){\n  return !! this.listeners(event).length;\n};\n\n});\nrequire.register(\"NetEase-pomelo-protocol/lib/protocol.js\", function(exports, require, module){\n(function (exports, ByteArray, global) {\n  var Protocol = exports;\n\n  var PKG_HEAD_BYTES = 4;\n  var MSG_FLAG_BYTES = 1;\n  var MSG_ROUTE_CODE_BYTES = 2;\n  var MSG_ID_MAX_BYTES = 5;\n  var MSG_ROUTE_LEN_BYTES = 1;\n\n  var MSG_ROUTE_CODE_MAX = 0xffff;\n\n  var MSG_COMPRESS_ROUTE_MASK = 0x1;\n  var MSG_TYPE_MASK = 0x7;\n\n  var Package = Protocol.Package = {};\n  var Message = Protocol.Message = {};\n\n  Package.TYPE_HANDSHAKE = 1;\n  Package.TYPE_HANDSHAKE_ACK = 2;\n  Package.TYPE_HEARTBEAT = 3;\n  Package.TYPE_DATA = 4;\n  Package.TYPE_KICK = 5;\n\n  Message.TYPE_REQUEST = 0;\n  Message.TYPE_NOTIFY = 1;\n  Message.TYPE_RESPONSE = 2;\n  Message.TYPE_PUSH = 3;\n\n  /**\n   * pomele client encode\n   * id message id;\n   * route message route\n   * msg message body\n   * socketio current support string\n   */\n  Protocol.strencode = function(str) {\n    var byteArray = new ByteArray(str.length * 3);\n    var offset = 0;\n    for(var i = 0; i < str.length; i++){\n      var charCode = str.charCodeAt(i);\n      var codes = null;\n      if(charCode <= 0x7f){\n        codes = [charCode];\n      }else if(charCode <= 0x7ff){\n        codes = [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];\n      }else{\n        codes = [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];\n      }\n      for(var j = 0; j < codes.length; j++){\n        byteArray[offset] = codes[j];\n        ++offset;\n      }\n    }\n    var _buffer = new ByteArray(offset);\n    copyArray(_buffer, 0, byteArray, 0, offset);\n    return _buffer;\n  };\n\n  /**\n   * client decode\n   * msg String data\n   * return Message Object\n   */\n  Protocol.strdecode = function(buffer) {\n    var bytes = new ByteArray(buffer);\n    var array = [];\n    var offset = 0;\n    var charCode = 0;\n    var end = bytes.length;\n    while(offset < end){\n      if(bytes[offset] < 128){\n        charCode = bytes[offset];\n        offset += 1;\n      }else if(bytes[offset] < 224){\n        charCode = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);\n        offset += 2;\n      }else{\n        charCode = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);\n        offset += 3;\n      }\n      array.push(charCode);\n    }\n    return String.fromCharCode.apply(null, array);\n  };\n\n  /**\n   * Package protocol encode.\n   *\n   * Pomelo package format:\n   * +------+-------------+------------------+\n   * | type | body length |       body       |\n   * +------+-------------+------------------+\n   *\n   * Head: 4bytes\n   *   0: package type,\n   *      1 - handshake,\n   *      2 - handshake ack,\n   *      3 - heartbeat,\n   *      4 - data\n   *      5 - kick\n   *   1 - 3: big-endian body length\n   * Body: body length bytes\n   *\n   * @param  {Number}    type   package type\n   * @param  {ByteArray} body   body content in bytes\n   * @return {ByteArray}        new byte array that contains encode result\n   */\n  Package.encode = function(type, body){\n    var length = body ? body.length : 0;\n    var buffer = new ByteArray(PKG_HEAD_BYTES + length);\n    var index = 0;\n    buffer[index++] = type & 0xff;\n    buffer[index++] = (length >> 16) & 0xff;\n    buffer[index++] = (length >> 8) & 0xff;\n    buffer[index++] = length & 0xff;\n    if(body) {\n      copyArray(buffer, index, body, 0, length);\n    }\n    return buffer;\n  };\n\n  /**\n   * Package protocol decode.\n   * See encode for package format.\n   *\n   * @param  {ByteArray} buffer byte array containing package content\n   * @return {Object}           {type: package type, buffer: body byte array}\n   */\n  Package.decode = function(buffer){\n    var bytes =  new ByteArray(buffer);\n    var type = bytes[0];\n    var index = 1;\n    var length = ((bytes[index++]) << 16 | (bytes[index++]) << 8 | bytes[index++]) >>> 0;\n    var body = length ? new ByteArray(length) : null;\n    copyArray(body, 0, bytes, PKG_HEAD_BYTES, length);\n    return {'type': type, 'body': body};\n  };\n\n  /**\n   * Message protocol encode.\n   *\n   * @param  {Number} id            message id\n   * @param  {Number} type          message type\n   * @param  {Number} compressRoute whether compress route\n   * @param  {Number|String} route  route code or route string\n   * @param  {Buffer} msg           message body bytes\n   * @return {Buffer}               encode result\n   */\n  Message.encode = function(id, type, compressRoute, route, msg){\n    // caculate message max length\n    var idBytes = msgHasId(type) ? caculateMsgIdBytes(id) : 0;\n    var msgLen = MSG_FLAG_BYTES + idBytes;\n\n    if(msgHasRoute(type)) {\n      if(compressRoute) {\n        if(typeof route !== 'number'){\n          throw new Error('error flag for number route!');\n        }\n        msgLen += MSG_ROUTE_CODE_BYTES;\n      } else {\n        msgLen += MSG_ROUTE_LEN_BYTES;\n        if(route) {\n          route = Protocol.strencode(route);\n          if(route.length>255) {\n            throw new Error('route maxlength is overflow');\n          }\n          msgLen += route.length;\n        }\n      }\n    }\n\n    if(msg) {\n      msgLen += msg.length;\n    }\n\n    var buffer = new ByteArray(msgLen);\n    var offset = 0;\n\n    // add flag\n    offset = encodeMsgFlag(type, compressRoute, buffer, offset);\n\n    // add message id\n    if(msgHasId(type)) {\n      offset = encodeMsgId(id, idBytes, buffer, offset);\n    }\n\n    // add route\n    if(msgHasRoute(type)) {\n      offset = encodeMsgRoute(compressRoute, route, buffer, offset);\n    }\n\n    // add body\n    if(msg) {\n      offset = encodeMsgBody(msg, buffer, offset);\n    }\n\n    return buffer;\n  };\n\n  /**\n   * Message protocol decode.\n   *\n   * @param  {Buffer|Uint8Array} buffer message bytes\n   * @return {Object}            message object\n   */\n  Message.decode = function(buffer) {\n    var bytes =  new ByteArray(buffer);\n    var bytesLen = bytes.length || bytes.byteLength;\n    var offset = 0;\n    var id = 0;\n    var route = null;\n\n    // parse flag\n    var flag = bytes[offset++];\n    var compressRoute = flag & MSG_COMPRESS_ROUTE_MASK;\n    var type = (flag >> 1) & MSG_TYPE_MASK;\n\n    // parse id\n    if(msgHasId(type)) {\n      var byte = bytes[offset++];\n      id = byte & 0x7f;\n      while(byte & 0x80) {\n        id <<= 7;\n        byte = bytes[offset++];\n        id |= byte & 0x7f;\n      }\n    }\n\n    // parse route\n    if(msgHasRoute(type)) {\n      if(compressRoute) {\n        route = (bytes[offset++]) << 8 | bytes[offset++];\n      } else {\n        var routeLen = bytes[offset++];\n        if(routeLen) {\n          route = new ByteArray(routeLen);\n          copyArray(route, 0, bytes, offset, routeLen);\n          route = Protocol.strdecode(route);\n        } else {\n          route = '';\n        }\n        offset += routeLen;\n      }\n    }\n\n    // parse body\n    var bodyLen = bytesLen - offset;\n    var body = new ByteArray(bodyLen);\n\n    copyArray(body, 0, bytes, offset, bodyLen);\n\n    return {'id': id, 'type': type, 'compressRoute': compressRoute,\n            'route': route, 'body': body};\n  };\n\n  var copyArray = function(dest, doffset, src, soffset, length) {\n    if('function' === typeof src.copy) {\n      // Buffer\n      src.copy(dest, doffset, soffset, soffset + length);\n    } else {\n      // Uint8Array\n      for(var index=0; index<length; index++){\n        dest[doffset++] = src[soffset++];\n      }\n    }\n  };\n\n  var msgHasId = function(type) {\n    return type === Message.TYPE_REQUEST || type === Message.TYPE_RESPONSE;\n  };\n\n  var msgHasRoute = function(type) {\n    return type === Message.TYPE_REQUEST || type === Message.TYPE_NOTIFY ||\n           type === Message.TYPE_PUSH;\n  };\n\n  var caculateMsgIdBytes = function(id) {\n    var len = 0;\n    do {\n      len += 1;\n      id >>= 7;\n    } while(id > 0);\n    return len;\n  };\n\n  var encodeMsgFlag = function(type, compressRoute, buffer, offset) {\n    if(type !== Message.TYPE_REQUEST && type !== Message.TYPE_NOTIFY &&\n       type !== Message.TYPE_RESPONSE && type !== Message.TYPE_PUSH) {\n      throw new Error('unkonw message type: ' + type);\n    }\n\n    buffer[offset] = (type << 1) | (compressRoute ? 1 : 0);\n\n    return offset + MSG_FLAG_BYTES;\n  };\n\n  var encodeMsgId = function(id, idBytes, buffer, offset) {\n    var index = offset + idBytes - 1;\n    buffer[index--] = id & 0x7f;\n    while(index >= offset) {\n      id >>= 7;\n      buffer[index--] = id & 0x7f | 0x80;\n    }\n    return offset + idBytes;\n  };\n\n  var encodeMsgRoute = function(compressRoute, route, buffer, offset) {\n    if (compressRoute) {\n      if(route > MSG_ROUTE_CODE_MAX){\n        throw new Error('route number is overflow');\n      }\n\n      buffer[offset++] = (route >> 8) & 0xff;\n      buffer[offset++] = route & 0xff;\n    } else {\n      if(route) {\n        buffer[offset++] = route.length & 0xff;\n        copyArray(buffer, offset, route, 0, route.length);\n        offset += route.length;\n      } else {\n        buffer[offset++] = 0;\n      }\n    }\n\n    return offset;\n  };\n\n  var encodeMsgBody = function(msg, buffer, offset) {\n    copyArray(buffer, offset, msg, 0, msg.length);\n    return offset + msg.length;\n  };\n\n  module.exports = Protocol;\n})('object' === typeof module ? module.exports : (this.Protocol = {}),'object' === typeof module ? Buffer : Uint8Array, this);\n\n});\nrequire.register(\"pomelonode-pomelo-protobuf/lib/client/protobuf.js\", function(exports, require, module){\n/* ProtocolBuffer client 0.1.0*/\n\n/**\n * pomelo-protobuf\n * @author <zhang0935@gmail.com>\n */\n\n/**\n * Protocol buffer root\n * In browser, it will be window.protbuf\n */\n(function (exports, global){\n  var Protobuf = exports;\n\n  Protobuf.init = function(opts){\n    //On the serverside, use serverProtos to encode messages send to client\n    Protobuf.encoder.init(opts.encoderProtos);\n\n    //On the serverside, user clientProtos to decode messages receive from clients\n    Protobuf.decoder.init(opts.decoderProtos);\n  };\n\n  Protobuf.encode = function(key, msg){\n    return Protobuf.encoder.encode(key, msg);\n  };\n\n  Protobuf.decode = function(key, msg){\n    return Protobuf.decoder.decode(key, msg);\n  };\n\n  // exports to support for components\n  module.exports = Protobuf;\n})('object' === typeof module ? module.exports : (this.protobuf = {}), this);\n\n/**\n * constants\n */\n(function (exports, global){\n  var constants = exports.constants = {};\n\n  constants.TYPES = {\n    uInt32 : 0,\n    sInt32 : 0,\n    int32 : 0,\n    double : 1,\n    string : 2,\n    message : 2,\n    float : 5\n  };\n\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n/**\n * util module\n */\n(function (exports, global){\n\n  var Util = exports.util = {};\n\n  Util.isSimpleType = function(type){\n    return ( type === 'uInt32' ||\n             type === 'sInt32' ||\n             type === 'int32'  ||\n             type === 'uInt64' ||\n             type === 'sInt64' ||\n             type === 'float'  ||\n             type === 'double' );\n  };\n\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n/**\n * codec module\n */\n(function (exports, global){\n\n  var Codec = exports.codec = {};\n\n  var buffer = new ArrayBuffer(8);\n  var float32Array = new Float32Array(buffer);\n  var float64Array = new Float64Array(buffer);\n  var uInt8Array = new Uint8Array(buffer);\n\n  Codec.encodeUInt32 = function(n){\n    var n = parseInt(n);\n    if(isNaN(n) || n < 0){\n      return null;\n    }\n\n    var result = [];\n    do{\n      var tmp = n % 128;\n      var next = Math.floor(n/128);\n\n      if(next !== 0){\n        tmp = tmp + 128;\n      }\n      result.push(tmp);\n      n = next;\n    }while(n !== 0);\n\n    return result;\n  };\n\n  Codec.encodeSInt32 = function(n){\n    var n = parseInt(n);\n    if(isNaN(n)){\n      return null;\n    }\n    n = n<0?(Math.abs(n)*2-1):n*2;\n\n    return Codec.encodeUInt32(n);\n  };\n\n  Codec.decodeUInt32 = function(bytes){\n    var n = 0;\n\n    for(var i = 0; i < bytes.length; i++){\n      var m = parseInt(bytes[i]);\n      n = n + ((m & 0x7f) * Math.pow(2,(7*i)));\n      if(m < 128){\n        return n;\n      }\n    }\n\n    return n;\n  };\n\n\n  Codec.decodeSInt32 = function(bytes){\n    var n = this.decodeUInt32(bytes);\n    var flag = ((n%2) === 1)?-1:1;\n\n    n = ((n%2 + n)/2)*flag;\n\n    return n;\n  };\n\n  Codec.encodeFloat = function(float){\n    float32Array[0] = float;\n    return uInt8Array;\n  };\n\n  Codec.decodeFloat = function(bytes, offset){\n    if(!bytes || bytes.length < (offset +4)){\n      return null;\n    }\n\n    for(var i = 0; i < 4; i++){\n      uInt8Array[i] = bytes[offset + i];\n    }\n\n    return float32Array[0];\n  };\n\n  Codec.encodeDouble = function(double){\n    float64Array[0] = double;\n    return uInt8Array.subarray(0, 8);\n  };\n\n  Codec.decodeDouble = function(bytes, offset){\n    if(!bytes || bytes.length < (8 + offset)){\n      return null;\n    }\n\n    for(var i = 0; i < 8; i++){\n      uInt8Array[i] = bytes[offset + i];\n    }\n\n    return float64Array[0];\n  };\n\n  Codec.encodeStr = function(bytes, offset, str){\n    for(var i = 0; i < str.length; i++){\n      var code = str.charCodeAt(i);\n      var codes = encode2UTF8(code);\n\n      for(var j = 0; j < codes.length; j++){\n        bytes[offset] = codes[j];\n        offset++;\n      }\n    }\n\n    return offset;\n  };\n\n  /**\n   * Decode string from utf8 bytes\n   */\n  Codec.decodeStr = function(bytes, offset, length){\n    var array = [];\n    var end = offset + length;\n\n    while(offset < end){\n      var code = 0;\n\n      if(bytes[offset] < 128){\n        code = bytes[offset];\n\n        offset += 1;\n      }else if(bytes[offset] < 224){\n        code = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);\n        offset += 2;\n      }else{\n        code = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);\n        offset += 3;\n      }\n\n      array.push(code);\n\n    }\n\n    var str = '';\n    for(var i = 0; i < array.length;){\n      str += String.fromCharCode.apply(null, array.slice(i, i + 10000));\n      i += 10000;\n    }\n\n    return str;\n  };\n\n  /**\n   * Return the byte length of the str use utf8\n   */\n  Codec.byteLength = function(str){\n    if(typeof(str) !== 'string'){\n      return -1;\n    }\n\n    var length = 0;\n\n    for(var i = 0; i < str.length; i++){\n      var code = str.charCodeAt(i);\n      length += codeLength(code);\n    }\n\n    return length;\n  };\n\n  /**\n   * Encode a unicode16 char code to utf8 bytes\n   */\n  function encode2UTF8(charCode){\n    if(charCode <= 0x7f){\n      return [charCode];\n    }else if(charCode <= 0x7ff){\n      return [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];\n    }else{\n      return [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];\n    }\n  }\n\n  function codeLength(code){\n    if(code <= 0x7f){\n      return 1;\n    }else if(code <= 0x7ff){\n      return 2;\n    }else{\n      return 3;\n    }\n  }\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n/**\n * encoder module\n */\n(function (exports, global){\n\n  var protobuf = exports;\n  var MsgEncoder = exports.encoder = {};\n\n  var codec = protobuf.codec;\n  var constant = protobuf.constants;\n  var util = protobuf.util;\n\n  MsgEncoder.init = function(protos){\n    this.protos = protos || {};\n  };\n\n  MsgEncoder.encode = function(route, msg){\n    //Get protos from protos map use the route as key\n    var protos = this.protos[route];\n\n    //Check msg\n    if(!checkMsg(msg, protos)){\n      return null;\n    }\n\n    //Set the length of the buffer 2 times bigger to prevent overflow\n    var length = codec.byteLength(JSON.stringify(msg));\n\n    //Init buffer and offset\n    var buffer = new ArrayBuffer(length);\n    var uInt8Array = new Uint8Array(buffer);\n    var offset = 0;\n\n    if(!!protos){\n      offset = encodeMsg(uInt8Array, offset, protos, msg);\n      if(offset > 0){\n        return uInt8Array.subarray(0, offset);\n      }\n    }\n\n    return null;\n  };\n\n  /**\n   * Check if the msg follow the defination in the protos\n   */\n  function checkMsg(msg, protos){\n    if(!protos){\n      return false;\n    }\n\n    for(var name in protos){\n      var proto = protos[name];\n\n      //All required element must exist\n      switch(proto.option){\n        case 'required' :\n          if(typeof(msg[name]) === 'undefined'){\n            return false;\n          }\n        case 'optional' :\n          if(typeof(msg[name]) !== 'undefined'){\n            if(!!protos.__messages[proto.type]){\n              checkMsg(msg[name], protos.__messages[proto.type]);\n            }\n          }\n        break;\n        case 'repeated' :\n          //Check nest message in repeated elements\n          if(!!msg[name] && !!protos.__messages[proto.type]){\n            for(var i = 0; i < msg[name].length; i++){\n              if(!checkMsg(msg[name][i], protos.__messages[proto.type])){\n                return false;\n              }\n            }\n          }\n        break;\n      }\n    }\n\n    return true;\n  }\n\n  function encodeMsg(buffer, offset, protos, msg){\n    for(var name in msg){\n      if(!!protos[name]){\n        var proto = protos[name];\n\n        switch(proto.option){\n          case 'required' :\n          case 'optional' :\n            offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));\n            offset = encodeProp(msg[name], proto.type, offset, buffer, protos);\n          break;\n          case 'repeated' :\n            if(msg[name].length > 0){\n              offset = encodeArray(msg[name], proto, offset, buffer, protos);\n            }\n          break;\n        }\n      }\n    }\n\n    return offset;\n  }\n\n  function encodeProp(value, type, offset, buffer, protos){\n    switch(type){\n      case 'uInt32':\n        offset = writeBytes(buffer, offset, codec.encodeUInt32(value));\n      break;\n      case 'int32' :\n      case 'sInt32':\n        offset = writeBytes(buffer, offset, codec.encodeSInt32(value));\n      break;\n      case 'float':\n        writeBytes(buffer, offset, codec.encodeFloat(value));\n        offset += 4;\n      break;\n      case 'double':\n        writeBytes(buffer, offset, codec.encodeDouble(value));\n        offset += 8;\n      break;\n      case 'string':\n        var length = codec.byteLength(value);\n\n        //Encode length\n        offset = writeBytes(buffer, offset, codec.encodeUInt32(length));\n        //write string\n        codec.encodeStr(buffer, offset, value);\n        offset += length;\n      break;\n      default :\n        if(!!protos.__messages[type]){\n          //Use a tmp buffer to build an internal msg\n          var tmpBuffer = new ArrayBuffer(codec.byteLength(JSON.stringify(value)));\n          var length = 0;\n\n          length = encodeMsg(tmpBuffer, length, protos.__messages[type], value);\n          //Encode length\n          offset = writeBytes(buffer, offset, codec.encodeUInt32(length));\n          //contact the object\n          for(var i = 0; i < length; i++){\n            buffer[offset] = tmpBuffer[i];\n            offset++;\n          }\n        }\n      break;\n    }\n\n    return offset;\n  }\n\n  /**\n   * Encode reapeated properties, simple msg and object are decode differented\n   */\n  function encodeArray(array, proto, offset, buffer, protos){\n    var i = 0;\n\n    if(util.isSimpleType(proto.type)){\n      offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));\n      offset = writeBytes(buffer, offset, codec.encodeUInt32(array.length));\n      for(i = 0; i < array.length; i++){\n        offset = encodeProp(array[i], proto.type, offset, buffer);\n      }\n    }else{\n      for(i = 0; i < array.length; i++){\n        offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));\n        offset = encodeProp(array[i], proto.type, offset, buffer, protos);\n      }\n    }\n\n    return offset;\n  }\n\n  function writeBytes(buffer, offset, bytes){\n    for(var i = 0; i < bytes.length; i++, offset++){\n      buffer[offset] = bytes[i];\n    }\n\n    return offset;\n  }\n\n  function encodeTag(type, tag){\n    var value = constant.TYPES[type]||2;\n    return codec.encodeUInt32((tag<<3)|value);\n  }\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n/**\n * decoder module\n */\n(function (exports, global){\n  var protobuf = exports;\n  var MsgDecoder = exports.decoder = {};\n\n  var codec = protobuf.codec;\n  var util = protobuf.util;\n\n  var buffer;\n  var offset = 0;\n\n  MsgDecoder.init = function(protos){\n    this.protos = protos || {};\n  };\n\n  MsgDecoder.setProtos = function(protos){\n    if(!!protos){\n      this.protos = protos;\n    }\n  };\n\n  MsgDecoder.decode = function(route, buf){\n    var protos = this.protos[route];\n\n    buffer = buf;\n    offset = 0;\n\n    if(!!protos){\n      return decodeMsg({}, protos, buffer.length);\n    }\n\n    return null;\n  };\n\n  function decodeMsg(msg, protos, length){\n    while(offset<length){\n      var head = getHead();\n      var type = head.type;\n      var tag = head.tag;\n      var name = protos.__tags[tag];\n\n      switch(protos[name].option){\n        case 'optional' :\n        case 'required' :\n          msg[name] = decodeProp(protos[name].type, protos);\n        break;\n        case 'repeated' :\n          if(!msg[name]){\n            msg[name] = [];\n          }\n          decodeArray(msg[name], protos[name].type, protos);\n        break;\n      }\n    }\n\n    return msg;\n  }\n\n  /**\n   * Test if the given msg is finished\n   */\n  function isFinish(msg, protos){\n    return (!protos.__tags[peekHead().tag]);\n  }\n  /**\n   * Get property head from protobuf\n   */\n  function getHead(){\n    var tag = codec.decodeUInt32(getBytes());\n\n    return {\n      type : tag&0x7,\n      tag : tag>>3\n    };\n  }\n\n  /**\n   * Get tag head without move the offset\n   */\n  function peekHead(){\n    var tag = codec.decodeUInt32(peekBytes());\n\n    return {\n      type : tag&0x7,\n      tag : tag>>3\n    };\n  }\n\n  function decodeProp(type, protos){\n    switch(type){\n      case 'uInt32':\n        return codec.decodeUInt32(getBytes());\n      case 'int32' :\n      case 'sInt32' :\n        return codec.decodeSInt32(getBytes());\n      case 'float' :\n        var float = codec.decodeFloat(buffer, offset);\n        offset += 4;\n        return float;\n      case 'double' :\n        var double = codec.decodeDouble(buffer, offset);\n        offset += 8;\n        return double;\n      case 'string' :\n        var length = codec.decodeUInt32(getBytes());\n\n        var str =  codec.decodeStr(buffer, offset, length);\n        offset += length;\n\n        return str;\n      default :\n        if(!!protos && !!protos.__messages[type]){\n          var length = codec.decodeUInt32(getBytes());\n          var msg = {};\n          decodeMsg(msg, protos.__messages[type], offset+length);\n          return msg;\n        }\n      break;\n    }\n  }\n\n  function decodeArray(array, type, protos){\n    if(util.isSimpleType(type)){\n      var length = codec.decodeUInt32(getBytes());\n\n      for(var i = 0; i < length; i++){\n        array.push(decodeProp(type));\n      }\n    }else{\n      array.push(decodeProp(type, protos));\n    }\n  }\n\n  function getBytes(flag){\n    var bytes = [];\n    var pos = offset;\n    flag = flag || false;\n\n    var b;\n\n    do{\n      b = buffer[pos];\n      bytes.push(b);\n      pos++;\n    }while(b >= 128);\n\n    if(!flag){\n      offset = pos;\n    }\n    return bytes;\n  }\n\n  function peekBytes(){\n    return getBytes(true);\n  }\n\n})('undefined' !== typeof protobuf ? protobuf : module.exports, this);\n\n\n});\nrequire.register(\"pomelonode-pomelo-jsclient-websocket/lib/pomelo-client.js\", function(exports, require, module){\n(function() {\n  var JS_WS_CLIENT_TYPE = 'js-websocket';\n  var JS_WS_CLIENT_VERSION = '0.0.1';\n\n  var Protocol = window.Protocol;\n  var Package = Protocol.Package;\n  var Message = Protocol.Message;\n  var EventEmitter = window.EventEmitter;\n\n  var RES_OK = 200;\n  var RES_FAIL = 500;\n  var RES_OLD_CLIENT = 501;\n\n  if (typeof Object.create !== 'function') {\n    Object.create = function (o) {\n      function F() {}\n      F.prototype = o;\n      return new F();\n    };\n  }\n\n  var root = window;\n  var pomelo = Object.create(EventEmitter.prototype); // object extend from object\n  root.pomelo = pomelo;\n  var socket = null;\n  var reqId = 0;\n  var callbacks = {};\n  var handlers = {};\n  //Map from request id to route\n  var routeMap = {};\n\n  var heartbeatInterval = 0;\n  var heartbeatTimeout = 0;\n  var nextHeartbeatTimeout = 0;\n  var gapThreshold = 100;   // heartbeat gap threashold\n  var heartbeatId = null;\n  var heartbeatTimeoutId = null;\n\n  var handshakeCallback = null;\n\n  var handshakeBuffer = {\n    'sys': {\n      type: JS_WS_CLIENT_TYPE,\n      version: JS_WS_CLIENT_VERSION\n    },\n    'user': {\n    }\n  };\n\n  var initCallback = null;\n\n  pomelo.init = function(params, cb){\n    initCallback = cb;\n    var host = params.host;\n    var port = params.port;\n\n    var url = 'wss://' + host;\n    if(port) {\n      url +=  ':' + port;\n    }\n\n    handshakeBuffer.user = params.user;\n    handshakeCallback = params.handshakeCallback;\n    initWebSocket(url, cb);\n  };\n\n  var initWebSocket = function(url,cb) {\n    console.log('connect to ' + url);\n    var onopen = function(event){\n      var obj = Package.encode(Package.TYPE_HANDSHAKE, Protocol.strencode(JSON.stringify(handshakeBuffer)));\n      send(obj);\n    };\n    var onmessage = function(event) {\n      processPackage(Package.decode(event.data), cb);\n      // new package arrived, update the heartbeat timeout\n      if(heartbeatTimeout) {\n        nextHeartbeatTimeout = Date.now() + heartbeatTimeout;\n      }\n    };\n    var onerror = function(event) {\n      pomelo.emit('io-error', event);\n      console.error('socket error: ', event);\n    };\n    var onclose = function(event){\n      pomelo.emit('close',event);\n      console.error('socket close: ', event);\n    };\n    socket = new WebSocket(url);\n    socket.binaryType = 'arraybuffer';\n    socket.onopen = onopen;\n    socket.onmessage = onmessage;\n    socket.onerror = onerror;\n    socket.onclose = onclose;\n  };\n\n  pomelo.disconnect = function() {\n    if(socket) {\n      if(socket.disconnect) socket.disconnect();\n      if(socket.close) socket.close();\n      console.log('disconnect');\n      socket = null;\n    }\n\n    if(heartbeatId) {\n      clearTimeout(heartbeatId);\n      heartbeatId = null;\n    }\n    if(heartbeatTimeoutId) {\n      clearTimeout(heartbeatTimeoutId);\n      heartbeatTimeoutId = null;\n    }\n  };\n\n  pomelo.request = function(route, msg, cb) {\n    if(arguments.length === 2 && typeof msg === 'function') {\n      cb = msg;\n      msg = {};\n    } else {\n      msg = msg || {};\n    }\n    route = route || msg.route;\n    if(!route) {\n      return;\n    }\n\n    reqId++;\n    sendMessage(reqId, route, msg);\n\n    callbacks[reqId] = cb;\n    routeMap[reqId] = route;\n  };\n\n  pomelo.notify = function(route, msg) {\n    msg = msg || {};\n    sendMessage(0, route, msg);\n  };\n\n  var sendMessage = function(reqId, route, msg) {\n    var type = reqId ? Message.TYPE_REQUEST : Message.TYPE_NOTIFY;\n\n    //compress message by protobuf\n    var protos = !!pomelo.data.protos?pomelo.data.protos.client:{};\n    if(!!protos[route]){\n      msg = protobuf.encode(route, msg);\n    }else{\n      msg = Protocol.strencode(JSON.stringify(msg));\n    }\n\n\n    var compressRoute = 0;\n    if(pomelo.dict && pomelo.dict[route]){\n      route = pomelo.dict[route];\n      compressRoute = 1;\n    }\n\n    msg = Message.encode(reqId, type, compressRoute, route, msg);\n    var packet = Package.encode(Package.TYPE_DATA, msg);\n    send(packet);\n  };\n\n  var send = function(packet){\n    socket.send(packet.buffer);\n  };\n\n\n  var handler = {};\n\n  var heartbeat = function(data) {\n    if(!heartbeatInterval) {\n      // no heartbeat\n      return;\n    }\n\n    var obj = Package.encode(Package.TYPE_HEARTBEAT);\n    if(heartbeatTimeoutId) {\n      clearTimeout(heartbeatTimeoutId);\n      heartbeatTimeoutId = null;\n    }\n\n    if(heartbeatId) {\n      // already in a heartbeat interval\n      return;\n    }\n\n    heartbeatId = setTimeout(function() {\n      heartbeatId = null;\n      send(obj);\n\n      nextHeartbeatTimeout = Date.now() + heartbeatTimeout;\n      heartbeatTimeoutId = setTimeout(heartbeatTimeoutCb, heartbeatTimeout);\n    }, heartbeatInterval);\n  };\n\n  var heartbeatTimeoutCb = function() {\n    var gap = nextHeartbeatTimeout - Date.now();\n    if(gap > gapThreshold) {\n      heartbeatTimeoutId = setTimeout(heartbeatTimeoutCb, gap);\n    } else {\n      console.error('server heartbeat timeout');\n      pomelo.emit('heartbeat timeout');\n      pomelo.disconnect();\n    }\n  };\n\n  var handshake = function(data){\n    data = JSON.parse(Protocol.strdecode(data));\n    if(data.code === RES_OLD_CLIENT) {\n      pomelo.emit('error', 'client version not fullfill');\n      return;\n    }\n\n    if(data.code !== RES_OK) {\n      pomelo.emit('error', 'handshake fail');\n      return;\n    }\n\n    handshakeInit(data);\n\n    var obj = Package.encode(Package.TYPE_HANDSHAKE_ACK);\n    send(obj);\n    if(initCallback) {\n      initCallback(socket);\n      initCallback = null;\n    }\n  };\n\n  var onData = function(data){\n    //probuff decode\n    var msg = Message.decode(data);\n\n    if(msg.id > 0){\n      msg.route = routeMap[msg.id];\n      delete routeMap[msg.id];\n      if(!msg.route){\n        return;\n      }\n    }\n\n    msg.body = deCompose(msg);\n\n    processMessage(pomelo, msg);\n  };\n\n  var onKick = function(data) {\n    pomelo.emit('onKick');\n  };\n\n  handlers[Package.TYPE_HANDSHAKE] = handshake;\n  handlers[Package.TYPE_HEARTBEAT] = heartbeat;\n  handlers[Package.TYPE_DATA] = onData;\n  handlers[Package.TYPE_KICK] = onKick;\n\n  var processPackage = function(msg) {\n    handlers[msg.type](msg.body);\n  };\n\n  var processMessage = function(pomelo, msg) {\n    if(!msg.id) {\n      // server push message\n      pomelo.emit(msg.route, msg.body);\n      return;\n    }\n\n    //if have a id then find the callback function with the request\n    var cb = callbacks[msg.id];\n\n    delete callbacks[msg.id];\n    if(typeof cb !== 'function') {\n      return;\n    }\n\n    cb(msg.body);\n    return;\n  };\n\n  var processMessageBatch = function(pomelo, msgs) {\n    for(var i=0, l=msgs.length; i<l; i++) {\n      processMessage(pomelo, msgs[i]);\n    }\n  };\n\n  var deCompose = function(msg){\n    var protos = !!pomelo.data.protos?pomelo.data.protos.server:{};\n    var abbrs = pomelo.data.abbrs;\n    var route = msg.route;\n\n    //Decompose route from dict\n    if(msg.compressRoute) {\n      if(!abbrs[route]){\n        return {};\n      }\n\n      route = msg.route = abbrs[route];\n    }\n    if(!!protos[route]){\n      return protobuf.decode(route, msg.body);\n    }else{\n      return JSON.parse(Protocol.strdecode(msg.body));\n    }\n\n    return msg;\n  };\n\n  var handshakeInit = function(data){\n    if(data.sys && data.sys.heartbeat) {\n      heartbeatInterval = data.sys.heartbeat * 1000;   // heartbeat interval\n      heartbeatTimeout = heartbeatInterval * 2;        // max heartbeat timeout\n    } else {\n      heartbeatInterval = 0;\n      heartbeatTimeout = 0;\n    }\n\n    initData(data);\n\n    if(typeof handshakeCallback === 'function') {\n      handshakeCallback(data.user);\n    }\n  };\n\n  //Initilize data used in pomelo client\n  var initData = function(data){\n    if(!data || !data.sys) {\n      return;\n    }\n    pomelo.data = pomelo.data || {};\n    var dict = data.sys.dict;\n    var protos = data.sys.protos;\n\n    //Init compress dict\n    if(dict){\n      pomelo.data.dict = dict;\n      pomelo.data.abbrs = {};\n\n      for(var route in dict){\n        pomelo.data.abbrs[dict[route]] = route;\n      }\n    }\n\n    //Init protobuf protos\n    if(protos){\n      pomelo.data.protos = {\n        server : protos.server || {},\n        client : protos.client || {}\n      };\n      if(!!protobuf){\n        protobuf.init({encoderProtos: protos.client, decoderProtos: protos.server});\n      }\n    }\n  };\n\n  module.exports = pomelo;\n})();\n\n});\nrequire.register(\"boot/index.js\", function(exports, require, module){\n  var Emitter = require('emitter');\n  window.EventEmitter = Emitter;\n\n  var protocol = require('pomelo-protocol');\n  window.Protocol = protocol;\n\n  var protobuf = require('pomelo-protobuf');\n  window.protobuf = protobuf;\n\n  var pomelo = require('pomelo-jsclient-websocket');\n  window.pomelo = pomelo;\n\n});\nrequire.alias(\"boot/index.js\", \"pomelo-client/deps/boot/index.js\");\nrequire.alias(\"component-emitter/index.js\", \"boot/deps/emitter/index.js\");\nrequire.alias(\"component-indexof/index.js\", \"component-emitter/deps/indexof/index.js\");\n\nrequire.alias(\"NetEase-pomelo-protocol/lib/protocol.js\", \"boot/deps/pomelo-protocol/lib/protocol.js\");\nrequire.alias(\"NetEase-pomelo-protocol/lib/protocol.js\", \"boot/deps/pomelo-protocol/index.js\");\nrequire.alias(\"NetEase-pomelo-protocol/lib/protocol.js\", \"NetEase-pomelo-protocol/index.js\");\n\nrequire.alias(\"pomelonode-pomelo-protobuf/lib/client/protobuf.js\", \"boot/deps/pomelo-protobuf/lib/client/protobuf.js\");\nrequire.alias(\"pomelonode-pomelo-protobuf/lib/client/protobuf.js\", \"boot/deps/pomelo-protobuf/index.js\");\nrequire.alias(\"pomelonode-pomelo-protobuf/lib/client/protobuf.js\", \"pomelonode-pomelo-protobuf/index.js\");\n\nrequire.alias(\"pomelonode-pomelo-jsclient-websocket/lib/pomelo-client.js\", \"boot/deps/pomelo-jsclient-websocket/lib/pomelo-client.js\");\nrequire.alias(\"pomelonode-pomelo-jsclient-websocket/lib/pomelo-client.js\", \"boot/deps/pomelo-jsclient-websocket/index.js\");\nrequire.alias(\"pomelonode-pomelo-jsclient-websocket/lib/pomelo-client.js\", \"pomelonode-pomelo-jsclient-websocket/index.js\");"
  },
  {
    "path": "template/web-server/public/js/lib/component.json",
    "content": "{\n  \"name\": \"pomelo-client\",\n  \"description\": \"pomelo-client\",\n  \"local\": [ \"boot\" ],\n  \"paths\": [ \"local\"]\n}"
  },
  {
    "path": "template/web-server/public/js/lib/local/boot/component.json",
    "content": "{\n  \"name\": \"boot\",\n  \"description\": \"Main app boot component\",\n  \"dependencies\": {\n    \"component/emitter\":\"master\",\n    \"NetEase/pomelo-protocol\": \"master\",\n    \"pomelonode/pomelo-protobuf\": \"*\",\n    \"pomelonode/pomelo-jsclient-websocket\": \"master\"\n  },\n  \"scripts\": [\"index.js\"]\n}"
  },
  {
    "path": "template/web-server/public/js/lib/local/boot/index.js",
    "content": "  var Emitter = require('emitter');\n  window.EventEmitter = Emitter;\n\n  var protocol = require('pomelo-protocol');\n  window.Protocol = protocol;\n  \n  var protobuf = require('pomelo-protobuf');\n  window.protobuf = protobuf;\n  \n  var pomelo = require('pomelo-jsclient-websocket');\n  window.pomelo = pomelo;\n"
  },
  {
    "path": "template/web-server/public/js/lib/pomeloclient.js",
    "content": "(function() {\n  var isArray = Array.isArray;\n\n  var root = this;\n\n  function EventEmitter() {\n  }\n\n\n  if (typeof module !== 'undefined' && module.exports) {\n    module.exports.EventEmitter = EventEmitter;\n  }\n  else {\n    root = window;\n    root.EventEmitter = EventEmitter;\n  }\n\n  // By default EventEmitters will print a warning if more than\n  // 10 listeners are added to it. This is a useful default which\n  // helps finding memory leaks.\n  //\n  // Obviously not all Emitters should be limited to 10. This function allows\n  // that to be increased. Set to zero for unlimited.\n  var defaultMaxListeners = 10;\n  EventEmitter.prototype.setMaxListeners = function(n) {\n    if (!this._events) this._events = {};\n    this._maxListeners = n;\n  };\n\n\n  EventEmitter.prototype.emit = function() {\n    var type = arguments[0];\n    // If there is no 'error' event listener then throw.\n    if (type === 'error') {\n      if (!this._events || !this._events.error ||\n          (isArray(this._events.error) && !this._events.error.length))\n        {\n          if (this.domain) {\n            var er = arguments[1];\n            er.domain_emitter = this;\n            er.domain = this.domain;\n            er.domain_thrown = false;\n            this.domain.emit('error', er);\n            return false;\n          }\n\n          if (arguments[1] instanceof Error) {\n            throw arguments[1]; // Unhandled 'error' event\n          } else {\n            throw new Error(\"Uncaught, unspecified 'error' event.\");\n          }\n          return false;\n        }\n    }\n\n    if (!this._events) return false;\n    var handler = this._events[type];\n    if (!handler) return false;\n\n    if (typeof handler == 'function') {\n      if (this.domain) {\n        this.domain.enter();\n      }\n      switch (arguments.length) {\n        // fast cases\n        case 1:\n          handler.call(this);\n        break;\n        case 2:\n          handler.call(this, arguments[1]);\n        break;\n        case 3:\n          handler.call(this, arguments[1], arguments[2]);\n        break;\n        // slower\n        default:\n          var l = arguments.length;\n        var args = new Array(l - 1);\n        for (var i = 1; i < l; i++) args[i - 1] = arguments[i];\n        handler.apply(this, args);\n      }\n      if (this.domain) {\n        this.domain.exit();\n      }\n      return true;\n\n    } else if (isArray(handler)) {\n      if (this.domain) {\n        this.domain.enter();\n      }\n      var l = arguments.length;\n      var args = new Array(l - 1);\n      for (var i = 1; i < l; i++) args[i - 1] = arguments[i];\n\n      var listeners = handler.slice();\n      for (var i = 0, l = listeners.length; i < l; i++) {\n        listeners[i].apply(this, args);\n      }\n      if (this.domain) {\n        this.domain.exit();\n      }\n      return true;\n\n    } else {\n      return false;\n    }\n  };\n\n  EventEmitter.prototype.addListener = function(type, listener) {\n    if ('function' !== typeof listener) {\n      throw new Error('addListener only takes instances of Function');\n    }\n\n    if (!this._events) this._events = {};\n\n    // To avoid recursion in the case that type == \"newListeners\"! Before\n    // adding it to the listeners, first emit \"newListeners\".\n    this.emit('newListener', type, typeof listener.listener === 'function' ?\n              listener.listener : listener);\n\n    if (!this._events[type]) {\n      // Optimize the case of one listener. Don't need the extra array object.\n      this._events[type] = listener;\n    } else if (isArray(this._events[type])) {\n\n      // If we've already got an array, just append.\n      this._events[type].push(listener);\n\n    } else {\n      // Adding the second element, need to change to array.\n      this._events[type] = [this._events[type], listener];\n\n    }\n\n    // Check for listener leak\n    if (isArray(this._events[type]) && !this._events[type].warned) {\n      var m;\n      if (this._maxListeners !== undefined) {\n        m = this._maxListeners;\n      } else {\n        m = defaultMaxListeners;\n      }\n\n      if (m && m > 0 && this._events[type].length > m) {\n        this._events[type].warned = true;\n        console.error('(node) warning: possible EventEmitter memory ' +\n                      'leak detected. %d listeners added. ' +\n                      'Use emitter.setMaxListeners() to increase limit.',\n        this._events[type].length);\n        console.trace();\n      }\n    }\n\n    return this;\n  };\n\n  EventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\n  EventEmitter.prototype.once = function(type, listener) {\n    if ('function' !== typeof listener) {\n      throw new Error('.once only takes instances of Function');\n    }\n\n    var self = this;\n    function g() {\n      self.removeListener(type, g);\n      listener.apply(this, arguments);\n    };\n\n    g.listener = listener;\n    self.on(type, g);\n\n    return this;\n  };\n\n  EventEmitter.prototype.removeListener = function(type, listener) {\n    if ('function' !== typeof listener) {\n      throw new Error('removeListener only takes instances of Function');\n    }\n\n    // does not use listeners(), so no side effect of creating _events[type]\n    if (!this._events || !this._events[type]) return this;\n\n    var list = this._events[type];\n\n    if (isArray(list)) {\n      var position = -1;\n      for (var i = 0, length = list.length; i < length; i++) {\n        if (list[i] === listener ||\n            (list[i].listener && list[i].listener === listener))\n          {\n            position = i;\n            break;\n          }\n      }\n\n      if (position < 0) return this;\n      list.splice(position, 1);\n    } else if (list === listener ||\n               (list.listener && list.listener === listener))\n      {\n        delete this._events[type];\n      }\n\n      return this;\n  };\n\n  EventEmitter.prototype.removeAllListeners = function(type) {\n    if (arguments.length === 0) {\n      this._events = {};\n      return this;\n    }\n\n    var events = this._events && this._events[type];\n    if (!events) return this;\n\n    if (isArray(events)) {\n      events.splice(0);\n    } else {\n      this._events[type] = null;\n    }\n\n    return this;\n  };\n\n  EventEmitter.prototype.listeners = function(type) {\n    if (!this._events) this._events = {};\n    if (!this._events[type]) this._events[type] = [];\n    if (!isArray(this._events[type])) {\n      this._events[type] = [this._events[type]];\n    }\n    return this._events[type];\n  }\n})();\n\n(function (exports, global) {\n\n  var Protocol = exports;\n \n  var HEADER = 5;\n\n  var Message = function(id,route,body){\n      this.id = id;\n      this.route = route;\n      this.body = body;\n  };\n\n/**\n *\n *pomele client encode\n * id message id;\n * route message route\n * msg message body\n * socketio current support string\n *\n */\nProtocol.encode = function(id,route,msg){\n    var msgStr = JSON.stringify(msg);\n    if (route.length>255) { throw new Error('route maxlength is overflow'); }\n    var byteArray = new Uint16Array(HEADER + route.length + msgStr.length);\n    var index = 0;\n    byteArray[index++] = (id>>24) & 0xFF;\n    byteArray[index++] = (id>>16) & 0xFF;\n    byteArray[index++] = (id>>8) & 0xFF;\n    byteArray[index++] = id & 0xFF;\n    byteArray[index++] = route.length & 0xFF;\n    for(var i = 0;i<route.length;i++){\n        byteArray[index++] = route.charCodeAt(i);\n    }\n    for (var i = 0; i < msgStr.length; i++) {\n        byteArray[index++] = msgStr.charCodeAt(i);\n    }\n    return bt2Str(byteArray,0,byteArray.length);\n};\n\n\n\n\n/**\n *\n *client decode\n *msg String data\n *return Message Object\n */\nProtocol.decode = function(msg){\n    var idx, len = msg.length, arr = new Array( len );\n    for ( idx = 0 ; idx < len ; ++idx ) {\n        arr[idx] = msg.charCodeAt(idx);\n    }\n    var index = 0;\n    var buf = new Uint16Array(arr);\n    var id = ((buf[index++] <<24) | (buf[index++])  << 16  |  (buf[index++]) << 8 | buf[index++]) >>>0; \n    var routeLen = buf[HEADER-1];\n    var route = bt2Str(buf,HEADER, routeLen+HEADER);\n    var body = bt2Str(buf,routeLen+HEADER,buf.length);  \n    return new Message(id,route,body);\n};\n\nvar bt2Str = function(byteArray,start,end) {\n    var result = \"\";\n    for(var i = start; i < byteArray.length && i<end; i++) {\n        result = result + String.fromCharCode(byteArray[i]);\n    };\n    return result;\n}\n\n})('object' === typeof module ? module.exports : (this.Protocol = {}), this);\n\n(function() {\n  if (typeof Object.create !== 'function') {\n    Object.create = function (o) {\n      function F() {}\n      F.prototype = o;\n      return new F();\n    };\n  }\n\n  var root = window;\n  var pomelo = Object.create(EventEmitter.prototype); // object extend from object\n  root.pomelo = pomelo;\n  var socket = null;\n  var id = 1;\n  var callbacks = {};\n\n  pomelo.init = function(params, cb){\n    pomelo.params = params;\n    params.debug = true;\n    var host = params.host;\n    var port = params.port;\n\n    var url = 'ws://' + host;\n    if(port) {\n      url +=  ':' + port;\n    }\n\n    socket = io(url, {'force new connection': true, reconnect: false});\n\n    socket.on('connect', function(){\n      console.log('[pomeloclient.init] websocket connected!');\n      if (cb) {\n        cb(socket);\n      }\n    });\n\n    socket.on('reconnect', function() {\n      console.log('reconnect');\n    });\n\n    socket.on('message', function(data){\n      if(typeof data === 'string') {\n        data = JSON.parse(data);\n      }\n      if(data instanceof Array) {\n        processMessageBatch(pomelo, data);\n      } else {\n        processMessage(pomelo, data);\n      }\n    });\n\n    socket.on('error', function(err) {\n      console.log(err);\n    });\n\n    socket.on('disconnect', function(reason) {\n      pomelo.emit('disconnect', reason);\n    });\n  };\n\n  pomelo.disconnect = function() {\n    if(socket) {\n      socket.disconnect();\n      socket = null;\n    }\n  };\n\n  pomelo.request = function(route) {\n    if(!route) {\n      return;\n    }\n    var msg = {};\n    var cb;\n    arguments = Array.prototype.slice.apply(arguments);\n    if(arguments.length === 2){\n      if(typeof arguments[1] === 'function'){\n        cb = arguments[1];\n      }else if(typeof arguments[1] === 'object'){\n        msg = arguments[1];\n      }\n    }else if(arguments.length === 3){\n      msg = arguments[1];\n      cb = arguments[2];\n    }\n    msg = filter(msg,route);\n  id++; \n  callbacks[id] = cb;\n  var sg = Protocol.encode(id,route,msg);\n    socket.send(sg);\n  };\n\n  pomelo.notify = function(route,msg) {\n    this.request(route, msg);\n  };\n\n  var processMessage = function(pomelo, msg) {\n    var route;\n    if(msg.id) {\n      //if have a id then find the callback function with the request\n      var cb = callbacks[msg.id];\n      \n      delete callbacks[msg.id];\n      if(typeof cb !== 'function') {\n        console.log('[pomeloclient.processMessage] cb is not a function for request ' + msg.id);\n        return;\n      }\n\n      cb(msg.body);\n      return;\n    }\n\n    // server push message or old format message\n    processCall(msg);\n\n    //if no id then it should be a server push message\n    function processCall(msg) {\n      var route = msg.route;\n      if(!!route) {\n        if (!!msg.body) {\n          var body = msg.body.body;\n          if (!body) {body = msg.body;}\n          pomelo.emit(route, body);\n        } else {\n          pomelo.emit(route,msg);\n        }\n      } else {\n          pomelo.emit(msg.body.route,msg.body);\n      }\n    }\n  };\n\n  var processMessageBatch = function(pomelo, msgs) {\n    for(var i=0, l=msgs.length; i<l; i++) {\n      processMessage(pomelo, msgs[i]);\n    }\n  };\n\n  function filter(msg,route){\n    if(route.indexOf('area.') === 0){\n      msg.areaId = pomelo.areaId;\n    }\n\n    msg.timestamp = Date.now();\n    return msg;\n  }\n\n  \n})();"
  },
  {
    "path": "template/web-server/public/js/lib/pomeloclient.js.wss",
    "content": "(function() {\n  var isArray = Array.isArray;\n\n  var root = this;\n\n  function EventEmitter() {\n  }\n\n\n  if (typeof module !== 'undefined' && module.exports) {\n    module.exports.EventEmitter = EventEmitter;\n  }\n  else {\n    root = window;\n    root.EventEmitter = EventEmitter;\n  }\n\n  // By default EventEmitters will print a warning if more than\n  // 10 listeners are added to it. This is a useful default which\n  // helps finding memory leaks.\n  //\n  // Obviously not all Emitters should be limited to 10. This function allows\n  // that to be increased. Set to zero for unlimited.\n  var defaultMaxListeners = 10;\n  EventEmitter.prototype.setMaxListeners = function(n) {\n    if (!this._events) this._events = {};\n    this._maxListeners = n;\n  };\n\n\n  EventEmitter.prototype.emit = function() {\n    var type = arguments[0];\n    // If there is no 'error' event listener then throw.\n    if (type === 'error') {\n      if (!this._events || !this._events.error ||\n          (isArray(this._events.error) && !this._events.error.length))\n        {\n          if (this.domain) {\n            var er = arguments[1];\n            er.domain_emitter = this;\n            er.domain = this.domain;\n            er.domain_thrown = false;\n            this.domain.emit('error', er);\n            return false;\n          }\n\n          if (arguments[1] instanceof Error) {\n            throw arguments[1]; // Unhandled 'error' event\n          } else {\n            throw new Error(\"Uncaught, unspecified 'error' event.\");\n          }\n          return false;\n        }\n    }\n\n    if (!this._events) return false;\n    var handler = this._events[type];\n    if (!handler) return false;\n\n    if (typeof handler == 'function') {\n      if (this.domain) {\n        this.domain.enter();\n      }\n      switch (arguments.length) {\n        // fast cases\n        case 1:\n          handler.call(this);\n        break;\n        case 2:\n          handler.call(this, arguments[1]);\n        break;\n        case 3:\n          handler.call(this, arguments[1], arguments[2]);\n        break;\n        // slower\n        default:\n          var l = arguments.length;\n        var args = new Array(l - 1);\n        for (var i = 1; i < l; i++) args[i - 1] = arguments[i];\n        handler.apply(this, args);\n      }\n      if (this.domain) {\n        this.domain.exit();\n      }\n      return true;\n\n    } else if (isArray(handler)) {\n      if (this.domain) {\n        this.domain.enter();\n      }\n      var l = arguments.length;\n      var args = new Array(l - 1);\n      for (var i = 1; i < l; i++) args[i - 1] = arguments[i];\n\n      var listeners = handler.slice();\n      for (var i = 0, l = listeners.length; i < l; i++) {\n        listeners[i].apply(this, args);\n      }\n      if (this.domain) {\n        this.domain.exit();\n      }\n      return true;\n\n    } else {\n      return false;\n    }\n  };\n\n  EventEmitter.prototype.addListener = function(type, listener) {\n    if ('function' !== typeof listener) {\n      throw new Error('addListener only takes instances of Function');\n    }\n\n    if (!this._events) this._events = {};\n\n    // To avoid recursion in the case that type == \"newListeners\"! Before\n    // adding it to the listeners, first emit \"newListeners\".\n    this.emit('newListener', type, typeof listener.listener === 'function' ?\n              listener.listener : listener);\n\n    if (!this._events[type]) {\n      // Optimize the case of one listener. Don't need the extra array object.\n      this._events[type] = listener;\n    } else if (isArray(this._events[type])) {\n\n      // If we've already got an array, just append.\n      this._events[type].push(listener);\n\n    } else {\n      // Adding the second element, need to change to array.\n      this._events[type] = [this._events[type], listener];\n\n    }\n\n    // Check for listener leak\n    if (isArray(this._events[type]) && !this._events[type].warned) {\n      var m;\n      if (this._maxListeners !== undefined) {\n        m = this._maxListeners;\n      } else {\n        m = defaultMaxListeners;\n      }\n\n      if (m && m > 0 && this._events[type].length > m) {\n        this._events[type].warned = true;\n        console.error('(node) warning: possible EventEmitter memory ' +\n                      'leak detected. %d listeners added. ' +\n                      'Use emitter.setMaxListeners() to increase limit.',\n        this._events[type].length);\n        console.trace();\n      }\n    }\n\n    return this;\n  };\n\n  EventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\n  EventEmitter.prototype.once = function(type, listener) {\n    if ('function' !== typeof listener) {\n      throw new Error('.once only takes instances of Function');\n    }\n\n    var self = this;\n    function g() {\n      self.removeListener(type, g);\n      listener.apply(this, arguments);\n    };\n\n    g.listener = listener;\n    self.on(type, g);\n\n    return this;\n  };\n\n  EventEmitter.prototype.removeListener = function(type, listener) {\n    if ('function' !== typeof listener) {\n      throw new Error('removeListener only takes instances of Function');\n    }\n\n    // does not use listeners(), so no side effect of creating _events[type]\n    if (!this._events || !this._events[type]) return this;\n\n    var list = this._events[type];\n\n    if (isArray(list)) {\n      var position = -1;\n      for (var i = 0, length = list.length; i < length; i++) {\n        if (list[i] === listener ||\n            (list[i].listener && list[i].listener === listener))\n          {\n            position = i;\n            break;\n          }\n      }\n\n      if (position < 0) return this;\n      list.splice(position, 1);\n    } else if (list === listener ||\n               (list.listener && list.listener === listener))\n      {\n        delete this._events[type];\n      }\n\n      return this;\n  };\n\n  EventEmitter.prototype.removeAllListeners = function(type) {\n    if (arguments.length === 0) {\n      this._events = {};\n      return this;\n    }\n\n    var events = this._events && this._events[type];\n    if (!events) return this;\n\n    if (isArray(events)) {\n      events.splice(0);\n    } else {\n      this._events[type] = null;\n    }\n\n    return this;\n  };\n\n  EventEmitter.prototype.listeners = function(type) {\n    if (!this._events) this._events = {};\n    if (!this._events[type]) this._events[type] = [];\n    if (!isArray(this._events[type])) {\n      this._events[type] = [this._events[type]];\n    }\n    return this._events[type];\n  }\n})();\n\n(function (exports, global) {\n\n  var Protocol = exports;\n \n  var HEADER = 5;\n\n  var Message = function(id,route,body){\n      this.id = id;\n      this.route = route;\n      this.body = body;\n  };\n\n/**\n *\n *pomele client encode\n * id message id;\n * route message route\n * msg message body\n * socketio current support string\n *\n */\nProtocol.encode = function(id,route,msg){\n    var msgStr = JSON.stringify(msg);\n    if (route.length>255) { throw new Error('route maxlength is overflow'); }\n    var byteArray = new Uint16Array(HEADER + route.length + msgStr.length);\n    var index = 0;\n    byteArray[index++] = (id>>24) & 0xFF;\n    byteArray[index++] = (id>>16) & 0xFF;\n    byteArray[index++] = (id>>8) & 0xFF;\n    byteArray[index++] = id & 0xFF;\n    byteArray[index++] = route.length & 0xFF;\n    for(var i = 0;i<route.length;i++){\n        byteArray[index++] = route.charCodeAt(i);\n    }\n    for (var i = 0; i < msgStr.length; i++) {\n        byteArray[index++] = msgStr.charCodeAt(i);\n    }\n    return bt2Str(byteArray,0,byteArray.length);\n};\n\n\n\n\n/**\n *\n *client decode\n *msg String data\n *return Message Object\n */\nProtocol.decode = function(msg){\n    var idx, len = msg.length, arr = new Array( len );\n    for ( idx = 0 ; idx < len ; ++idx ) {\n        arr[idx] = msg.charCodeAt(idx);\n    }\n    var index = 0;\n    var buf = new Uint16Array(arr);\n    var id = ((buf[index++] <<24) | (buf[index++])  << 16  |  (buf[index++]) << 8 | buf[index++]) >>>0; \n    var routeLen = buf[HEADER-1];\n    var route = bt2Str(buf,HEADER, routeLen+HEADER);\n    var body = bt2Str(buf,routeLen+HEADER,buf.length);  \n    return new Message(id,route,body);\n};\n\nvar bt2Str = function(byteArray,start,end) {\n    var result = \"\";\n    for(var i = start; i < byteArray.length && i<end; i++) {\n        result = result + String.fromCharCode(byteArray[i]);\n    };\n    return result;\n}\n\n})('object' === typeof module ? module.exports : (this.Protocol = {}), this);\n\n(function() {\n  if (typeof Object.create !== 'function') {\n    Object.create = function (o) {\n      function F() {}\n      F.prototype = o;\n      return new F();\n    };\n  }\n\n  var root = window;\n  var pomelo = Object.create(EventEmitter.prototype); // object extend from object\n  root.pomelo = pomelo;\n  var socket = null;\n  var id = 1;\n  var callbacks = {};\n\n  pomelo.init = function(params, cb){\n    pomelo.params = params;\n    params.debug = true;\n    var host = params.host;\n    var port = params.port;\n\n    var url = 'https://' + host;\n    if(port) {\n      url +=  ':' + port;\n    }\n\n    socket = io.connect(url, {'force new connection': true, reconnect: false});\n\n    socket.on('connect', function(){\n      console.log('[pomeloclient.init] websocket connected!');\n      if (cb) {\n        cb(socket);\n      }\n    });\n\n    socket.on('reconnect', function() {\n      console.log('reconnect');\n    });\n\n    socket.on('message', function(data){\n      if(typeof data === 'string') {\n        data = JSON.parse(data);\n      }\n      if(data instanceof Array) {\n        processMessageBatch(pomelo, data);\n      } else {\n        processMessage(pomelo, data);\n      }\n    });\n\n    socket.on('error', function(err) {\n      console.log(err);\n    });\n\n    socket.on('disconnect', function(reason) {\n      pomelo.emit('disconnect', reason);\n    });\n  };\n\n  pomelo.disconnect = function() {\n    if(socket) {\n      socket.disconnect();\n      socket = null;\n    }\n  };\n\n  pomelo.request = function(route) {\n    if(!route) {\n      return;\n    }\n    var msg = {};\n    var cb;\n    arguments = Array.prototype.slice.apply(arguments);\n    if(arguments.length === 2){\n      if(typeof arguments[1] === 'function'){\n        cb = arguments[1];\n      }else if(typeof arguments[1] === 'object'){\n        msg = arguments[1];\n      }\n    }else if(arguments.length === 3){\n      msg = arguments[1];\n      cb = arguments[2];\n    }\n    msg = filter(msg,route);\n  id++; \n  callbacks[id] = cb;\n  var sg = Protocol.encode(id,route,msg);\n    socket.send(sg);\n  };\n\n  pomelo.notify = function(route,msg) {\n    this.request(route, msg);\n  };\n\n  var processMessage = function(pomelo, msg) {\n    var route;\n    if(msg.id) {\n      //if have a id then find the callback function with the request\n      var cb = callbacks[msg.id];\n      \n      delete callbacks[msg.id];\n      if(typeof cb !== 'function') {\n        console.log('[pomeloclient.processMessage] cb is not a function for request ' + msg.id);\n        return;\n      }\n\n      cb(msg.body);\n      return;\n    }\n\n    // server push message or old format message\n    processCall(msg);\n\n    //if no id then it should be a server push message\n    function processCall(msg) {\n      var route = msg.route;\n      if(!!route) {\n        if (!!msg.body) {\n          var body = msg.body.body;\n          if (!body) {body = msg.body;}\n          pomelo.emit(route, body);\n        } else {\n          pomelo.emit(route,msg);\n        }\n      } else {\n          pomelo.emit(msg.body.route,msg.body);\n      }\n    }\n  };\n\n  var processMessageBatch = function(pomelo, msgs) {\n    for(var i=0, l=msgs.length; i<l; i++) {\n      processMessage(pomelo, msgs[i]);\n    }\n  };\n\n  function filter(msg,route){\n    if(route.indexOf('area.') === 0){\n      msg.areaId = pomelo.areaId;\n    }\n\n    msg.timestamp = Date.now();\n    return msg;\n  }\n\n  \n})();"
  },
  {
    "path": "template/web-server/public/js/lib/socket.io.js",
    "content": "!function(t,e){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=e():\"function\"==typeof define&&define.amd?define([],e):\"object\"==typeof exports?exports.io=e():t.io=e()}(this,function(){return function(t){function e(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return t[n].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var r={};return e.m=t,e.c=r,e.p=\"\",e(0)}([function(t,e,r){\"use strict\";function n(t,e){\"object\"===(\"undefined\"==typeof t?\"undefined\":i(t))&&(e=t,t=void 0),e=e||{};var r,n=s(t),a=n.source,p=n.id,f=n.path,l=h[p]&&f in h[p].nsps,d=e.forceNew||e[\"force new connection\"]||!1===e.multiplex||l;return d?(u(\"ignoring socket cache for %s\",a),r=c(a,e)):(h[p]||(u(\"new io instance for %s\",a),h[p]=c(a,e)),r=h[p]),n.query&&!e.query?e.query=n.query:e&&\"object\"===i(e.query)&&(e.query=o(e.query)),r.socket(n.path,e)}function o(t){var e=[];for(var r in t)t.hasOwnProperty(r)&&e.push(encodeURIComponent(r)+\"=\"+encodeURIComponent(t[r]));return e.join(\"&\")}var i=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},s=r(1),a=r(7),c=r(17),u=r(3)(\"socket.io-client\");t.exports=e=n;var h=e.managers={};e.protocol=a.protocol,e.connect=n,e.Manager=r(17),e.Socket=r(44)},function(t,e,r){(function(e){\"use strict\";function n(t,r){var n=t;r=r||e.location,null==t&&(t=r.protocol+\"//\"+r.host),\"string\"==typeof t&&(\"/\"===t.charAt(0)&&(t=\"/\"===t.charAt(1)?r.protocol+t:r.host+t),/^(https?|wss?):\\/\\//.test(t)||(i(\"protocol-less url %s\",t),t=\"undefined\"!=typeof r?r.protocol+\"//\"+t:\"https://\"+t),i(\"parse %s\",t),n=o(t)),n.port||(/^(http|ws)$/.test(n.protocol)?n.port=\"80\":/^(http|ws)s$/.test(n.protocol)&&(n.port=\"443\")),n.path=n.path||\"/\";var s=n.host.indexOf(\":\")!==-1,a=s?\"[\"+n.host+\"]\":n.host;return n.id=n.protocol+\"://\"+a+\":\"+n.port,n.href=n.protocol+\"://\"+a+(r&&r.port===n.port?\"\":\":\"+n.port),n}var o=r(2),i=r(3)(\"socket.io-client:url\");t.exports=n}).call(e,function(){return this}())},function(t,e){var r=/^(?:(?![^:@]+:[^:@\\/]*@)(http|https|ws|wss):\\/\\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\\/?#]*)(?::(\\d*))?)(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))(?:\\?([^#]*))?(?:#(.*))?)/,n=[\"source\",\"protocol\",\"authority\",\"userInfo\",\"user\",\"password\",\"host\",\"port\",\"relative\",\"path\",\"directory\",\"file\",\"query\",\"anchor\"];t.exports=function(t){var e=t,o=t.indexOf(\"[\"),i=t.indexOf(\"]\");o!=-1&&i!=-1&&(t=t.substring(0,o)+t.substring(o,i).replace(/:/g,\";\")+t.substring(i,t.length));for(var s=r.exec(t||\"\"),a={},c=14;c--;)a[n[c]]=s[c]||\"\";return o!=-1&&i!=-1&&(a.source=e,a.host=a.host.substring(1,a.host.length-1).replace(/;/g,\":\"),a.authority=a.authority.replace(\"[\",\"\").replace(\"]\",\"\").replace(/;/g,\":\"),a.ipv6uri=!0),a}},function(t,e,r){(function(n){function o(){return\"undefined\"!=typeof document&&\"WebkitAppearance\"in document.documentElement.style||window.console&&(console.firebug||console.exception&&console.table)||navigator.userAgent.toLowerCase().match(/firefox\\/(\\d+)/)&&parseInt(RegExp.$1,10)>=31}function i(){var t=arguments,r=this.useColors;if(t[0]=(r?\"%c\":\"\")+this.namespace+(r?\" %c\":\" \")+t[0]+(r?\"%c \":\" \")+\"+\"+e.humanize(this.diff),!r)return t;var n=\"color: \"+this.color;t=[t[0],n,\"color: inherit\"].concat(Array.prototype.slice.call(t,1));var o=0,i=0;return t[0].replace(/%[a-z%]/g,function(t){\"%%\"!==t&&(o++,\"%c\"===t&&(i=o))}),t.splice(i,0,n),t}function s(){return\"object\"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function a(t){try{null==t?e.storage.removeItem(\"debug\"):e.storage.debug=t}catch(t){}}function c(){try{return e.storage.debug}catch(t){}if(\"undefined\"!=typeof n&&\"env\"in n)return n.env.DEBUG}function u(){try{return window.localStorage}catch(t){}}e=t.exports=r(5),e.log=s,e.formatArgs=i,e.save=a,e.load=c,e.useColors=o,e.storage=\"undefined\"!=typeof chrome&&\"undefined\"!=typeof chrome.storage?chrome.storage.local:u(),e.colors=[\"lightseagreen\",\"forestgreen\",\"goldenrod\",\"dodgerblue\",\"darkorchid\",\"crimson\"],e.formatters.j=function(t){try{return JSON.stringify(t)}catch(t){return\"[UnexpectedJSONParseError]: \"+t.message}},e.enable(c())}).call(e,r(4))},function(t,e){function r(){throw new Error(\"setTimeout has not been defined\")}function n(){throw new Error(\"clearTimeout has not been defined\")}function o(t){if(h===setTimeout)return setTimeout(t,0);if((h===r||!h)&&setTimeout)return h=setTimeout,setTimeout(t,0);try{return h(t,0)}catch(e){try{return h.call(null,t,0)}catch(e){return h.call(this,t,0)}}}function i(t){if(p===clearTimeout)return clearTimeout(t);if((p===n||!p)&&clearTimeout)return p=clearTimeout,clearTimeout(t);try{return p(t)}catch(e){try{return p.call(null,t)}catch(e){return p.call(this,t)}}}function s(){y&&l&&(y=!1,l.length?d=l.concat(d):g=-1,d.length&&a())}function a(){if(!y){var t=o(s);y=!0;for(var e=d.length;e;){for(l=d,d=[];++g<e;)l&&l[g].run();g=-1,e=d.length}l=null,y=!1,i(t)}}function c(t,e){this.fun=t,this.array=e}function u(){}var h,p,f=t.exports={};!function(){try{h=\"function\"==typeof setTimeout?setTimeout:r}catch(t){h=r}try{p=\"function\"==typeof clearTimeout?clearTimeout:n}catch(t){p=n}}();var l,d=[],y=!1,g=-1;f.nextTick=function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r<arguments.length;r++)e[r-1]=arguments[r];d.push(new c(t,e)),1!==d.length||y||o(a)},c.prototype.run=function(){this.fun.apply(null,this.array)},f.title=\"browser\",f.browser=!0,f.env={},f.argv=[],f.version=\"\",f.versions={},f.on=u,f.addListener=u,f.once=u,f.off=u,f.removeListener=u,f.removeAllListeners=u,f.emit=u,f.binding=function(t){throw new Error(\"process.binding is not supported\")},f.cwd=function(){return\"/\"},f.chdir=function(t){throw new Error(\"process.chdir is not supported\")},f.umask=function(){return 0}},function(t,e,r){function n(){return e.colors[h++%e.colors.length]}function o(t){function r(){}function o(){var t=o,r=+new Date,i=r-(u||r);t.diff=i,t.prev=u,t.curr=r,u=r,null==t.useColors&&(t.useColors=e.useColors()),null==t.color&&t.useColors&&(t.color=n());for(var s=new Array(arguments.length),a=0;a<s.length;a++)s[a]=arguments[a];s[0]=e.coerce(s[0]),\"string\"!=typeof s[0]&&(s=[\"%o\"].concat(s));var c=0;s[0]=s[0].replace(/%([a-z%])/g,function(r,n){if(\"%%\"===r)return r;c++;var o=e.formatters[n];if(\"function\"==typeof o){var i=s[c];r=o.call(t,i),s.splice(c,1),c--}return r}),s=e.formatArgs.apply(t,s);var h=o.log||e.log||console.log.bind(console);h.apply(t,s)}r.enabled=!1,o.enabled=!0;var i=e.enabled(t)?o:r;return i.namespace=t,i}function i(t){e.save(t);for(var r=(t||\"\").split(/[\\s,]+/),n=r.length,o=0;o<n;o++)r[o]&&(t=r[o].replace(/[\\\\^$+?.()|[\\]{}]/g,\"\\\\$&\").replace(/\\*/g,\".*?\"),\"-\"===t[0]?e.skips.push(new RegExp(\"^\"+t.substr(1)+\"$\")):e.names.push(new RegExp(\"^\"+t+\"$\")))}function s(){e.enable(\"\")}function a(t){var r,n;for(r=0,n=e.skips.length;r<n;r++)if(e.skips[r].test(t))return!1;for(r=0,n=e.names.length;r<n;r++)if(e.names[r].test(t))return!0;return!1}function c(t){return t instanceof Error?t.stack||t.message:t}e=t.exports=o.debug=o,e.coerce=c,e.disable=s,e.enable=i,e.enabled=a,e.humanize=r(6),e.names=[],e.skips=[],e.formatters={};var u,h=0},function(t,e){function r(t){if(t=String(t),!(t.length>1e4)){var e=/^((?:\\d+)?\\.?\\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(t);if(e){var r=parseFloat(e[1]),n=(e[2]||\"ms\").toLowerCase();switch(n){case\"years\":case\"year\":case\"yrs\":case\"yr\":case\"y\":return r*h;case\"days\":case\"day\":case\"d\":return r*u;case\"hours\":case\"hour\":case\"hrs\":case\"hr\":case\"h\":return r*c;case\"minutes\":case\"minute\":case\"mins\":case\"min\":case\"m\":return r*a;case\"seconds\":case\"second\":case\"secs\":case\"sec\":case\"s\":return r*s;case\"milliseconds\":case\"millisecond\":case\"msecs\":case\"msec\":case\"ms\":return r;default:return}}}}function n(t){return t>=u?Math.round(t/u)+\"d\":t>=c?Math.round(t/c)+\"h\":t>=a?Math.round(t/a)+\"m\":t>=s?Math.round(t/s)+\"s\":t+\"ms\"}function o(t){return i(t,u,\"day\")||i(t,c,\"hour\")||i(t,a,\"minute\")||i(t,s,\"second\")||t+\" ms\"}function i(t,e,r){if(!(t<e))return t<1.5*e?Math.floor(t/e)+\" \"+r:Math.ceil(t/e)+\" \"+r+\"s\"}var s=1e3,a=60*s,c=60*a,u=24*c,h=365.25*u;t.exports=function(t,e){e=e||{};var i=typeof t;if(\"string\"===i&&t.length>0)return r(t);if(\"number\"===i&&isNaN(t)===!1)return e.long?o(t):n(t);throw new Error(\"val is not a non-empty string or a valid number. val=\"+JSON.stringify(t))}},function(t,e,r){function n(){}function o(t){var r=\"\",n=!1;return r+=t.type,e.BINARY_EVENT!=t.type&&e.BINARY_ACK!=t.type||(r+=t.attachments,r+=\"-\"),t.nsp&&\"/\"!=t.nsp&&(n=!0,r+=t.nsp),null!=t.id&&(n&&(r+=\",\",n=!1),r+=t.id),null!=t.data&&(n&&(r+=\",\"),r+=f.stringify(t.data)),p(\"encoded %j as %s\",t,r),r}function i(t,e){function r(t){var r=d.deconstructPacket(t),n=o(r.packet),i=r.buffers;i.unshift(n),e(i)}d.removeBlobs(t,r)}function s(){this.reconstructor=null}function a(t){var r={},n=0;if(r.type=Number(t.charAt(0)),null==e.types[r.type])return h();if(e.BINARY_EVENT==r.type||e.BINARY_ACK==r.type){for(var o=\"\";\"-\"!=t.charAt(++n)&&(o+=t.charAt(n),n!=t.length););if(o!=Number(o)||\"-\"!=t.charAt(n))throw new Error(\"Illegal attachments\");r.attachments=Number(o)}if(\"/\"==t.charAt(n+1))for(r.nsp=\"\";++n;){var i=t.charAt(n);if(\",\"==i)break;if(r.nsp+=i,n==t.length)break}else r.nsp=\"/\";var s=t.charAt(n+1);if(\"\"!==s&&Number(s)==s){for(r.id=\"\";++n;){var i=t.charAt(n);if(null==i||Number(i)!=i){--n;break}if(r.id+=t.charAt(n),n==t.length)break}r.id=Number(r.id)}return t.charAt(++n)&&(r=c(r,t.substr(n))),p(\"decoded %s as %j\",t,r),r}function c(t,e){try{t.data=f.parse(e)}catch(t){return h()}return t}function u(t){this.reconPack=t,this.buffers=[]}function h(t){return{type:e.ERROR,data:\"parser error\"}}var p=r(8)(\"socket.io-parser\"),f=r(11),l=r(13),d=r(14),y=r(16);e.protocol=4,e.types=[\"CONNECT\",\"DISCONNECT\",\"EVENT\",\"ACK\",\"ERROR\",\"BINARY_EVENT\",\"BINARY_ACK\"],e.CONNECT=0,e.DISCONNECT=1,e.EVENT=2,e.ACK=3,e.ERROR=4,e.BINARY_EVENT=5,e.BINARY_ACK=6,e.Encoder=n,e.Decoder=s,n.prototype.encode=function(t,r){if(p(\"encoding packet %j\",t),e.BINARY_EVENT==t.type||e.BINARY_ACK==t.type)i(t,r);else{var n=o(t);r([n])}},l(s.prototype),s.prototype.add=function(t){var r;if(\"string\"==typeof t)r=a(t),e.BINARY_EVENT==r.type||e.BINARY_ACK==r.type?(this.reconstructor=new u(r),0===this.reconstructor.reconPack.attachments&&this.emit(\"decoded\",r)):this.emit(\"decoded\",r);else{if(!y(t)&&!t.base64)throw new Error(\"Unknown type: \"+t);if(!this.reconstructor)throw new Error(\"got binary data when not reconstructing a packet\");r=this.reconstructor.takeBinaryData(t),r&&(this.reconstructor=null,this.emit(\"decoded\",r))}},s.prototype.destroy=function(){this.reconstructor&&this.reconstructor.finishedReconstruction()},u.prototype.takeBinaryData=function(t){if(this.buffers.push(t),this.buffers.length==this.reconPack.attachments){var e=d.reconstructPacket(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null},u.prototype.finishedReconstruction=function(){this.reconPack=null,this.buffers=[]}},function(t,e,r){function n(){return\"WebkitAppearance\"in document.documentElement.style||window.console&&(console.firebug||console.exception&&console.table)||navigator.userAgent.toLowerCase().match(/firefox\\/(\\d+)/)&&parseInt(RegExp.$1,10)>=31}function o(){var t=arguments,r=this.useColors;if(t[0]=(r?\"%c\":\"\")+this.namespace+(r?\" %c\":\" \")+t[0]+(r?\"%c \":\" \")+\"+\"+e.humanize(this.diff),!r)return t;var n=\"color: \"+this.color;t=[t[0],n,\"color: inherit\"].concat(Array.prototype.slice.call(t,1));var o=0,i=0;return t[0].replace(/%[a-z%]/g,function(t){\"%%\"!==t&&(o++,\"%c\"===t&&(i=o))}),t.splice(i,0,n),t}function i(){return\"object\"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function s(t){try{null==t?e.storage.removeItem(\"debug\"):e.storage.debug=t}catch(t){}}function a(){var t;try{t=e.storage.debug}catch(t){}return t}function c(){try{return window.localStorage}catch(t){}}e=t.exports=r(9),e.log=i,e.formatArgs=o,e.save=s,e.load=a,e.useColors=n,e.storage=\"undefined\"!=typeof chrome&&\"undefined\"!=typeof chrome.storage?chrome.storage.local:c(),e.colors=[\"lightseagreen\",\"forestgreen\",\"goldenrod\",\"dodgerblue\",\"darkorchid\",\"crimson\"],e.formatters.j=function(t){return JSON.stringify(t)},e.enable(a())},function(t,e,r){function n(){return e.colors[h++%e.colors.length]}function o(t){function r(){}function o(){var t=o,r=+new Date,i=r-(u||r);t.diff=i,t.prev=u,t.curr=r,u=r,null==t.useColors&&(t.useColors=e.useColors()),null==t.color&&t.useColors&&(t.color=n());var s=Array.prototype.slice.call(arguments);s[0]=e.coerce(s[0]),\"string\"!=typeof s[0]&&(s=[\"%o\"].concat(s));var a=0;s[0]=s[0].replace(/%([a-z%])/g,function(r,n){if(\"%%\"===r)return r;a++;var o=e.formatters[n];if(\"function\"==typeof o){var i=s[a];r=o.call(t,i),s.splice(a,1),a--}return r}),\"function\"==typeof e.formatArgs&&(s=e.formatArgs.apply(t,s));var c=o.log||e.log||console.log.bind(console);c.apply(t,s)}r.enabled=!1,o.enabled=!0;var i=e.enabled(t)?o:r;return i.namespace=t,i}function i(t){e.save(t);for(var r=(t||\"\").split(/[\\s,]+/),n=r.length,o=0;o<n;o++)r[o]&&(t=r[o].replace(/\\*/g,\".*?\"),\"-\"===t[0]?e.skips.push(new RegExp(\"^\"+t.substr(1)+\"$\")):e.names.push(new RegExp(\"^\"+t+\"$\")))}function s(){e.enable(\"\")}function a(t){var r,n;for(r=0,n=e.skips.length;r<n;r++)if(e.skips[r].test(t))return!1;for(r=0,n=e.names.length;r<n;r++)if(e.names[r].test(t))return!0;return!1}function c(t){return t instanceof Error?t.stack||t.message:t}e=t.exports=o,e.coerce=c,e.disable=s,e.enable=i,e.enabled=a,e.humanize=r(10),e.names=[],e.skips=[],e.formatters={};var u,h=0},function(t,e){function r(t){if(t=\"\"+t,!(t.length>1e4)){var e=/^((?:\\d+)?\\.?\\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(t);if(e){var r=parseFloat(e[1]),n=(e[2]||\"ms\").toLowerCase();switch(n){case\"years\":case\"year\":case\"yrs\":case\"yr\":case\"y\":return r*h;case\"days\":case\"day\":case\"d\":return r*u;case\"hours\":case\"hour\":case\"hrs\":case\"hr\":case\"h\":return r*c;case\"minutes\":case\"minute\":case\"mins\":case\"min\":case\"m\":return r*a;case\"seconds\":case\"second\":case\"secs\":case\"sec\":case\"s\":return r*s;case\"milliseconds\":case\"millisecond\":case\"msecs\":case\"msec\":case\"ms\":return r}}}}function n(t){return t>=u?Math.round(t/u)+\"d\":t>=c?Math.round(t/c)+\"h\":t>=a?Math.round(t/a)+\"m\":t>=s?Math.round(t/s)+\"s\":t+\"ms\"}function o(t){return i(t,u,\"day\")||i(t,c,\"hour\")||i(t,a,\"minute\")||i(t,s,\"second\")||t+\" ms\"}function i(t,e,r){if(!(t<e))return t<1.5*e?Math.floor(t/e)+\" \"+r:Math.ceil(t/e)+\" \"+r+\"s\"}var s=1e3,a=60*s,c=60*a,u=24*c,h=365.25*u;t.exports=function(t,e){return e=e||{},\"string\"==typeof t?r(t):e.long?o(t):n(t)}},function(t,e,r){(function(t,r){var n=!1;(function(){function o(t,e){function r(t){if(r[t]!==g)return r[t];var o;if(\"bug-string-char-index\"==t)o=\"a\"!=\"a\"[0];else if(\"json\"==t)o=r(\"json-stringify\")&&r(\"json-parse\");else{var s,a='{\"a\":[1,true,false,null,\"\\\\u0000\\\\b\\\\n\\\\f\\\\r\\\\t\"]}';if(\"json-stringify\"==t){var c=e.stringify,h=\"function\"==typeof c&&b;if(h){(s=function(){return 1}).toJSON=s;try{h=\"0\"===c(0)&&\"0\"===c(new n)&&'\"\"'==c(new i)&&c(v)===g&&c(g)===g&&c()===g&&\"1\"===c(s)&&\"[1]\"==c([s])&&\"[null]\"==c([g])&&\"null\"==c(null)&&\"[null,null,null]\"==c([g,v,null])&&c({a:[s,!0,!1,null,\"\\0\\b\\n\\f\\r\\t\"]})==a&&\"1\"===c(null,s)&&\"[\\n 1,\\n 2\\n]\"==c([1,2],null,1)&&'\"-271821-04-20T00:00:00.000Z\"'==c(new u(-864e13))&&'\"+275760-09-13T00:00:00.000Z\"'==c(new u(864e13))&&'\"-000001-01-01T00:00:00.000Z\"'==c(new u(-621987552e5))&&'\"1969-12-31T23:59:59.999Z\"'==c(new u(-1))}catch(t){h=!1}}o=h}if(\"json-parse\"==t){var p=e.parse;if(\"function\"==typeof p)try{if(0===p(\"0\")&&!p(!1)){s=p(a);var f=5==s.a.length&&1===s.a[0];if(f){try{f=!p('\"\\t\"')}catch(t){}if(f)try{f=1!==p(\"01\")}catch(t){}if(f)try{f=1!==p(\"1.\")}catch(t){}}}}catch(t){f=!1}o=f}}return r[t]=!!o}t||(t=c.Object()),e||(e=c.Object());var n=t.Number||c.Number,i=t.String||c.String,a=t.Object||c.Object,u=t.Date||c.Date,h=t.SyntaxError||c.SyntaxError,p=t.TypeError||c.TypeError,f=t.Math||c.Math,l=t.JSON||c.JSON;\"object\"==typeof l&&l&&(e.stringify=l.stringify,e.parse=l.parse);var d,y,g,m=a.prototype,v=m.toString,b=new u(-0xc782b5b800cec);try{b=b.getUTCFullYear()==-109252&&0===b.getUTCMonth()&&1===b.getUTCDate()&&10==b.getUTCHours()&&37==b.getUTCMinutes()&&6==b.getUTCSeconds()&&708==b.getUTCMilliseconds()}catch(t){}if(!r(\"json\")){var w=\"[object Function]\",k=\"[object Date]\",x=\"[object Number]\",A=\"[object String]\",C=\"[object Array]\",B=\"[object Boolean]\",S=r(\"bug-string-char-index\");if(!b)var T=f.floor,E=[0,31,59,90,120,151,181,212,243,273,304,334],_=function(t,e){return E[e]+365*(t-1970)+T((t-1969+(e=+(e>1)))/4)-T((t-1901+e)/100)+T((t-1601+e)/400)};if((d=m.hasOwnProperty)||(d=function(t){var e,r={};return(r.__proto__=null,r.__proto__={toString:1},r).toString!=v?d=function(t){var e=this.__proto__,r=t in(this.__proto__=null,this);return this.__proto__=e,r}:(e=r.constructor,d=function(t){var r=(this.constructor||e).prototype;return t in this&&!(t in r&&this[t]===r[t])}),r=null,d.call(this,t)}),y=function(t,e){var r,n,o,i=0;(r=function(){this.valueOf=0}).prototype.valueOf=0,n=new r;for(o in n)d.call(n,o)&&i++;return r=n=null,i?y=2==i?function(t,e){var r,n={},o=v.call(t)==w;for(r in t)o&&\"prototype\"==r||d.call(n,r)||!(n[r]=1)||!d.call(t,r)||e(r)}:function(t,e){var r,n,o=v.call(t)==w;for(r in t)o&&\"prototype\"==r||!d.call(t,r)||(n=\"constructor\"===r)||e(r);(n||d.call(t,r=\"constructor\"))&&e(r)}:(n=[\"valueOf\",\"toString\",\"toLocaleString\",\"propertyIsEnumerable\",\"isPrototypeOf\",\"hasOwnProperty\",\"constructor\"],y=function(t,e){var r,o,i=v.call(t)==w,a=!i&&\"function\"!=typeof t.constructor&&s[typeof t.hasOwnProperty]&&t.hasOwnProperty||d;for(r in t)i&&\"prototype\"==r||!a.call(t,r)||e(r);for(o=n.length;r=n[--o];a.call(t,r)&&e(r));}),y(t,e)},!r(\"json-stringify\")){var N={92:\"\\\\\\\\\",34:'\\\\\"',8:\"\\\\b\",12:\"\\\\f\",10:\"\\\\n\",13:\"\\\\r\",9:\"\\\\t\"},j=\"000000\",O=function(t,e){return(j+(e||0)).slice(-t)},P=\"\\\\u00\",R=function(t){for(var e='\"',r=0,n=t.length,o=!S||n>10,i=o&&(S?t.split(\"\"):t);r<n;r++){var s=t.charCodeAt(r);switch(s){case 8:case 9:case 10:case 12:case 13:case 34:case 92:e+=N[s];break;default:if(s<32){e+=P+O(2,s.toString(16));break}e+=o?i[r]:t.charAt(r)}}return e+'\"'},D=function(t,e,r,n,o,i,s){var a,c,u,h,f,l,m,b,w,S,E,N,j,P,q,U;try{a=e[t]}catch(t){}if(\"object\"==typeof a&&a)if(c=v.call(a),c!=k||d.call(a,\"toJSON\"))\"function\"==typeof a.toJSON&&(c!=x&&c!=A&&c!=C||d.call(a,\"toJSON\"))&&(a=a.toJSON(t));else if(a>-1/0&&a<1/0){if(_){for(f=T(a/864e5),u=T(f/365.2425)+1970-1;_(u+1,0)<=f;u++);for(h=T((f-_(u,0))/30.42);_(u,h+1)<=f;h++);f=1+f-_(u,h),l=(a%864e5+864e5)%864e5,m=T(l/36e5)%24,b=T(l/6e4)%60,w=T(l/1e3)%60,S=l%1e3}else u=a.getUTCFullYear(),h=a.getUTCMonth(),f=a.getUTCDate(),m=a.getUTCHours(),b=a.getUTCMinutes(),w=a.getUTCSeconds(),S=a.getUTCMilliseconds();a=(u<=0||u>=1e4?(u<0?\"-\":\"+\")+O(6,u<0?-u:u):O(4,u))+\"-\"+O(2,h+1)+\"-\"+O(2,f)+\"T\"+O(2,m)+\":\"+O(2,b)+\":\"+O(2,w)+\".\"+O(3,S)+\"Z\"}else a=null;if(r&&(a=r.call(e,t,a)),null===a)return\"null\";if(c=v.call(a),c==B)return\"\"+a;if(c==x)return a>-1/0&&a<1/0?\"\"+a:\"null\";if(c==A)return R(\"\"+a);if(\"object\"==typeof a){for(P=s.length;P--;)if(s[P]===a)throw p();if(s.push(a),E=[],q=i,i+=o,c==C){for(j=0,P=a.length;j<P;j++)N=D(j,a,r,n,o,i,s),E.push(N===g?\"null\":N);U=E.length?o?\"[\\n\"+i+E.join(\",\\n\"+i)+\"\\n\"+q+\"]\":\"[\"+E.join(\",\")+\"]\":\"[]\"}else y(n||a,function(t){var e=D(t,a,r,n,o,i,s);e!==g&&E.push(R(t)+\":\"+(o?\" \":\"\")+e)}),U=E.length?o?\"{\\n\"+i+E.join(\",\\n\"+i)+\"\\n\"+q+\"}\":\"{\"+E.join(\",\")+\"}\":\"{}\";return s.pop(),U}};e.stringify=function(t,e,r){var n,o,i,a;if(s[typeof e]&&e)if((a=v.call(e))==w)o=e;else if(a==C){i={};for(var c,u=0,h=e.length;u<h;c=e[u++],a=v.call(c),(a==A||a==x)&&(i[c]=1));}if(r)if((a=v.call(r))==x){if((r-=r%1)>0)for(n=\"\",r>10&&(r=10);n.length<r;n+=\" \");}else a==A&&(n=r.length<=10?r:r.slice(0,10));return D(\"\",(c={},c[\"\"]=t,c),o,i,n,\"\",[])}}if(!r(\"json-parse\")){var q,U,M=i.fromCharCode,L={92:\"\\\\\",34:'\"',47:\"/\",98:\"\\b\",116:\"\\t\",110:\"\\n\",102:\"\\f\",114:\"\\r\"},I=function(){throw q=U=null,h()},H=function(){for(var t,e,r,n,o,i=U,s=i.length;q<s;)switch(o=i.charCodeAt(q)){case 9:case 10:case 13:case 32:q++;break;case 123:case 125:case 91:case 93:case 58:case 44:return t=S?i.charAt(q):i[q],q++,t;case 34:for(t=\"@\",q++;q<s;)if(o=i.charCodeAt(q),o<32)I();else if(92==o)switch(o=i.charCodeAt(++q)){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:t+=L[o],q++;break;case 117:for(e=++q,r=q+4;q<r;q++)o=i.charCodeAt(q),o>=48&&o<=57||o>=97&&o<=102||o>=65&&o<=70||I();t+=M(\"0x\"+i.slice(e,q));break;default:I()}else{if(34==o)break;for(o=i.charCodeAt(q),e=q;o>=32&&92!=o&&34!=o;)o=i.charCodeAt(++q);t+=i.slice(e,q)}if(34==i.charCodeAt(q))return q++,t;I();default:if(e=q,45==o&&(n=!0,o=i.charCodeAt(++q)),o>=48&&o<=57){for(48==o&&(o=i.charCodeAt(q+1),o>=48&&o<=57)&&I(),n=!1;q<s&&(o=i.charCodeAt(q),o>=48&&o<=57);q++);if(46==i.charCodeAt(q)){for(r=++q;r<s&&(o=i.charCodeAt(r),o>=48&&o<=57);r++);r==q&&I(),q=r}if(o=i.charCodeAt(q),101==o||69==o){for(o=i.charCodeAt(++q),43!=o&&45!=o||q++,r=q;r<s&&(o=i.charCodeAt(r),o>=48&&o<=57);r++);r==q&&I(),q=r}return+i.slice(e,q)}if(n&&I(),\"true\"==i.slice(q,q+4))return q+=4,!0;if(\"false\"==i.slice(q,q+5))return q+=5,!1;if(\"null\"==i.slice(q,q+4))return q+=4,null;I()}return\"$\"},z=function(t){var e,r;if(\"$\"==t&&I(),\"string\"==typeof t){if(\"@\"==(S?t.charAt(0):t[0]))return t.slice(1);if(\"[\"==t){for(e=[];t=H(),\"]\"!=t;r||(r=!0))r&&(\",\"==t?(t=H(),\"]\"==t&&I()):I()),\",\"==t&&I(),e.push(z(t));return e}if(\"{\"==t){for(e={};t=H(),\"}\"!=t;r||(r=!0))r&&(\",\"==t?(t=H(),\"}\"==t&&I()):I()),\",\"!=t&&\"string\"==typeof t&&\"@\"==(S?t.charAt(0):t[0])&&\":\"==H()||I(),e[t.slice(1)]=z(H());return e}I()}return t},J=function(t,e,r){var n=X(t,e,r);n===g?delete t[e]:t[e]=n},X=function(t,e,r){var n,o=t[e];if(\"object\"==typeof o&&o)if(v.call(o)==C)for(n=o.length;n--;)J(o,n,r);else y(o,function(t){J(o,t,r)});return r.call(t,e,o)};e.parse=function(t,e){var r,n;return q=0,U=\"\"+t,r=z(H()),\"$\"!=H()&&I(),q=U=null,e&&v.call(e)==w?X((n={},n[\"\"]=r,n),\"\",e):r}}}return e.runInContext=o,e}var i=\"function\"==typeof n&&n.amd,s={function:!0,object:!0},a=s[typeof e]&&e&&!e.nodeType&&e,c=s[typeof window]&&window||this,u=a&&s[typeof t]&&t&&!t.nodeType&&\"object\"==typeof r&&r;if(!u||u.global!==u&&u.window!==u&&u.self!==u||(c=u),a&&!i)o(c,a);else{var h=c.JSON,p=c.JSON3,f=!1,l=o(c,c.JSON3={noConflict:function(){return f||(f=!0,c.JSON=h,c.JSON3=p,h=p=null),l}});c.JSON={parse:l.parse,stringify:l.stringify}}i&&n(function(){return l})}).call(this)}).call(e,r(12)(t),function(){return this}())},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(t,e){function r(t){if(t)return n(t)}function n(t){for(var e in r.prototype)t[e]=r.prototype[e];return t}t.exports=r,r.prototype.on=r.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks[t]=this._callbacks[t]||[]).push(e),this},r.prototype.once=function(t,e){function r(){n.off(t,r),e.apply(this,arguments)}var n=this;return this._callbacks=this._callbacks||{},r.fn=e,this.on(t,r),this},r.prototype.off=r.prototype.removeListener=r.prototype.removeAllListeners=r.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var r=this._callbacks[t];if(!r)return this;if(1==arguments.length)return delete this._callbacks[t],this;for(var n,o=0;o<r.length;o++)if(n=r[o],n===e||n.fn===e){r.splice(o,1);break}return this},r.prototype.emit=function(t){this._callbacks=this._callbacks||{};var e=[].slice.call(arguments,1),r=this._callbacks[t];if(r){r=r.slice(0);for(var n=0,o=r.length;n<o;++n)r[n].apply(this,e)}return this},r.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks[t]||[]},r.prototype.hasListeners=function(t){return!!this.listeners(t).length}},function(t,e,r){(function(t){var n=r(15),o=r(16);e.deconstructPacket=function(t){function e(t){if(!t)return t;if(o(t)){var i={_placeholder:!0,num:r.length};return r.push(t),i}if(n(t)){for(var s=new Array(t.length),a=0;a<t.length;a++)s[a]=e(t[a]);return s}if(\"object\"==typeof t&&!(t instanceof Date)){var s={};for(var c in t)s[c]=e(t[c]);return s}return t}var r=[],i=t.data,s=t;return s.data=e(i),s.attachments=r.length,{packet:s,buffers:r}},e.reconstructPacket=function(t,e){function r(t){if(t&&t._placeholder){var o=e[t.num];return o}if(n(t)){for(var i=0;i<t.length;i++)t[i]=r(t[i]);return t}if(t&&\"object\"==typeof t){for(var s in t)t[s]=r(t[s]);return t}return t}return t.data=r(t.data),t.attachments=void 0,t},e.removeBlobs=function(e,r){function i(e,c,u){if(!e)return e;if(t.Blob&&e instanceof Blob||t.File&&e instanceof File){s++;var h=new FileReader;h.onload=function(){u?u[c]=this.result:a=this.result,--s||r(a)},h.readAsArrayBuffer(e)}else if(n(e))for(var p=0;p<e.length;p++)i(e[p],p,e);else if(e&&\"object\"==typeof e&&!o(e))for(var f in e)i(e[f],f,e)}var s=0,a=e;i(a),s||r(a)}}).call(e,function(){return this}())},function(t,e){t.exports=Array.isArray||function(t){return\"[object Array]\"==Object.prototype.toString.call(t)}},function(t,e){(function(e){function r(t){return e.Buffer&&e.Buffer.isBuffer(t)||e.ArrayBuffer&&t instanceof ArrayBuffer}t.exports=r}).call(e,function(){return this}())},function(t,e,r){\"use strict\";function n(t,e){return this instanceof n?(t&&\"object\"===(\"undefined\"==typeof t?\"undefined\":o(t))&&(e=t,t=void 0),e=e||{},e.path=e.path||\"/socket.io\",this.nsps={},this.subs=[],this.opts=e,this.reconnection(e.reconnection!==!1),this.reconnectionAttempts(e.reconnectionAttempts||1/0),this.reconnectionDelay(e.reconnectionDelay||1e3),this.reconnectionDelayMax(e.reconnectionDelayMax||5e3),this.randomizationFactor(e.randomizationFactor||.5),this.backoff=new l({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()}),this.timeout(null==e.timeout?2e4:e.timeout),this.readyState=\"closed\",this.uri=t,this.connecting=[],this.lastPing=null,this.encoding=!1,this.packetBuffer=[],this.encoder=new c.Encoder,this.decoder=new c.Decoder,this.autoConnect=e.autoConnect!==!1,void(this.autoConnect&&this.open())):new n(t,e)}var o=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\"function\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\"symbol\":typeof t},i=r(18),s=r(44),a=r(35),c=r(7),u=r(46),h=r(47),p=r(3)(\"socket.io-client:manager\"),f=r(42),l=r(48),d=Object.prototype.hasOwnProperty;t.exports=n,n.prototype.emitAll=function(){this.emit.apply(this,arguments);for(var t in this.nsps)d.call(this.nsps,t)&&this.nsps[t].emit.apply(this.nsps[t],arguments)},n.prototype.updateSocketIds=function(){for(var t in this.nsps)d.call(this.nsps,t)&&(this.nsps[t].id=this.engine.id)},a(n.prototype),n.prototype.reconnection=function(t){return arguments.length?(this._reconnection=!!t,this):this._reconnection},n.prototype.reconnectionAttempts=function(t){return arguments.length?(this._reconnectionAttempts=t,this):this._reconnectionAttempts},n.prototype.reconnectionDelay=function(t){return arguments.length?(this._reconnectionDelay=t,this.backoff&&this.backoff.setMin(t),this):this._reconnectionDelay},n.prototype.randomizationFactor=function(t){return arguments.length?(this._randomizationFactor=t,this.backoff&&this.backoff.setJitter(t),this):this._randomizationFactor},n.prototype.reconnectionDelayMax=function(t){return arguments.length?(this._reconnectionDelayMax=t,this.backoff&&this.backoff.setMax(t),this):this._reconnectionDelayMax},n.prototype.timeout=function(t){return arguments.length?(this._timeout=t,this):this._timeout},n.prototype.maybeReconnectOnOpen=function(){!this.reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()},n.prototype.open=n.prototype.connect=function(t,e){if(p(\"readyState %s\",this.readyState),~this.readyState.indexOf(\"open\"))return this;p(\"opening %s\",this.uri),this.engine=i(this.uri,this.opts);var r=this.engine,n=this;this.readyState=\"opening\",this.skipReconnect=!1;var o=u(r,\"open\",function(){n.onopen(),t&&t()}),s=u(r,\"error\",function(e){if(p(\"connect_error\"),n.cleanup(),n.readyState=\"closed\",n.emitAll(\"connect_error\",e),t){var r=new Error(\"Connection error\");r.data=e,t(r)}else n.maybeReconnectOnOpen()});if(!1!==this._timeout){var a=this._timeout;p(\"connect attempt will timeout after %d\",a);var c=setTimeout(function(){p(\"connect attempt timed out after %d\",a),o.destroy(),r.close(),r.emit(\"error\",\"timeout\"),n.emitAll(\"connect_timeout\",a)},a);this.subs.push({destroy:function(){clearTimeout(c)}})}return this.subs.push(o),this.subs.push(s),this},n.prototype.onopen=function(){p(\"open\"),this.cleanup(),this.readyState=\"open\",this.emit(\"open\");var t=this.engine;this.subs.push(u(t,\"data\",h(this,\"ondata\"))),this.subs.push(u(t,\"ping\",h(this,\"onping\"))),this.subs.push(u(t,\"pong\",h(this,\"onpong\"))),this.subs.push(u(t,\"error\",h(this,\"onerror\"))),this.subs.push(u(t,\"close\",h(this,\"onclose\"))),this.subs.push(u(this.decoder,\"decoded\",h(this,\"ondecoded\")))},n.prototype.onping=function(){this.lastPing=new Date,this.emitAll(\"ping\")},n.prototype.onpong=function(){this.emitAll(\"pong\",new Date-this.lastPing)},n.prototype.ondata=function(t){this.decoder.add(t)},n.prototype.ondecoded=function(t){this.emit(\"packet\",t)},n.prototype.onerror=function(t){p(\"error\",t),this.emitAll(\"error\",t)},n.prototype.socket=function(t,e){function r(){~f(o.connecting,n)||o.connecting.push(n)}var n=this.nsps[t];if(!n){n=new s(this,t,e),this.nsps[t]=n;var o=this;n.on(\"connecting\",r),n.on(\"connect\",function(){n.id=o.engine.id}),this.autoConnect&&r()}return n},n.prototype.destroy=function(t){var e=f(this.connecting,t);~e&&this.connecting.splice(e,1),this.connecting.length||this.close()},n.prototype.packet=function(t){p(\"writing packet %j\",t);var e=this;t.query&&0===t.type&&(t.nsp+=\"?\"+t.query),e.encoding?e.packetBuffer.push(t):(e.encoding=!0,this.encoder.encode(t,function(r){for(var n=0;n<r.length;n++)e.engine.write(r[n],t.options);e.encoding=!1,e.processPacketQueue()}))},n.prototype.processPacketQueue=function(){if(this.packetBuffer.length>0&&!this.encoding){var t=this.packetBuffer.shift();this.packet(t)}},n.prototype.cleanup=function(){p(\"cleanup\");for(var t=this.subs.length,e=0;e<t;e++){var r=this.subs.shift();r.destroy()}this.packetBuffer=[],this.encoding=!1,this.lastPing=null,this.decoder.destroy()},n.prototype.close=n.prototype.disconnect=function(){p(\"disconnect\"),this.skipReconnect=!0,this.reconnecting=!1,\"opening\"===this.readyState&&this.cleanup(),this.backoff.reset(),this.readyState=\"closed\",this.engine&&this.engine.close()},n.prototype.onclose=function(t){p(\"onclose\"),this.cleanup(),this.backoff.reset(),this.readyState=\"closed\",this.emit(\"close\",t),this._reconnection&&!this.skipReconnect&&this.reconnect()},n.prototype.reconnect=function(){if(this.reconnecting||this.skipReconnect)return this;var t=this;if(this.backoff.attempts>=this._reconnectionAttempts)p(\"reconnect failed\"),this.backoff.reset(),this.emitAll(\"reconnect_failed\"),this.reconnecting=!1;else{var e=this.backoff.duration();p(\"will wait %dms before reconnect attempt\",e),this.reconnecting=!0;var r=setTimeout(function(){t.skipReconnect||(p(\"attempting reconnect\"),t.emitAll(\"reconnect_attempt\",t.backoff.attempts),t.emitAll(\"reconnecting\",t.backoff.attempts),t.skipReconnect||t.open(function(e){e?(p(\"reconnect attempt error\"),t.reconnecting=!1,t.reconnect(),t.emitAll(\"reconnect_error\",e.data)):(p(\"reconnect success\"),t.onreconnect())}))},e);this.subs.push({destroy:function(){clearTimeout(r)}})}},n.prototype.onreconnect=function(){var t=this.backoff.attempts;this.reconnecting=!1,this.backoff.reset(),this.updateSocketIds(),this.emitAll(\"reconnect\",t)}},function(t,e,r){t.exports=r(19)},function(t,e,r){t.exports=r(20),t.exports.parser=r(27)},function(t,e,r){(function(e){function n(t,r){if(!(this instanceof n))return new n(t,r);r=r||{},t&&\"object\"==typeof t&&(r=t,t=null),t?(t=h(t),r.hostname=t.host,r.secure=\"https\"===t.protocol||\"wss\"===t.protocol,r.port=t.port,t.query&&(r.query=t.query)):r.host&&(r.hostname=h(r.host).host),\nthis.secure=null!=r.secure?r.secure:e.location&&\"https:\"===location.protocol,r.hostname&&!r.port&&(r.port=this.secure?\"443\":\"80\"),this.agent=r.agent||!1,this.hostname=r.hostname||(e.location?location.hostname:\"localhost\"),this.port=r.port||(e.location&&location.port?location.port:this.secure?443:80),this.query=r.query||{},\"string\"==typeof this.query&&(this.query=f.decode(this.query)),this.upgrade=!1!==r.upgrade,this.path=(r.path||\"/engine.io\").replace(/\\/$/,\"\")+\"/\",this.forceJSONP=!!r.forceJSONP,this.jsonp=!1!==r.jsonp,this.forceBase64=!!r.forceBase64,this.enablesXDR=!!r.enablesXDR,this.timestampParam=r.timestampParam||\"t\",this.timestampRequests=r.timestampRequests,this.transports=r.transports||[\"polling\",\"websocket\"],this.readyState=\"\",this.writeBuffer=[],this.prevBufferLen=0,this.policyPort=r.policyPort||843,this.rememberUpgrade=r.rememberUpgrade||!1,this.binaryType=null,this.onlyBinaryUpgrades=r.onlyBinaryUpgrades,this.perMessageDeflate=!1!==r.perMessageDeflate&&(r.perMessageDeflate||{}),!0===this.perMessageDeflate&&(this.perMessageDeflate={}),this.perMessageDeflate&&null==this.perMessageDeflate.threshold&&(this.perMessageDeflate.threshold=1024),this.pfx=r.pfx||null,this.key=r.key||null,this.passphrase=r.passphrase||null,this.cert=r.cert||null,this.ca=r.ca||null,this.ciphers=r.ciphers||null,this.rejectUnauthorized=void 0===r.rejectUnauthorized?null:r.rejectUnauthorized,this.forceNode=!!r.forceNode;var o=\"object\"==typeof e&&e;o.global===o&&(r.extraHeaders&&Object.keys(r.extraHeaders).length>0&&(this.extraHeaders=r.extraHeaders),r.localAddress&&(this.localAddress=r.localAddress)),this.id=null,this.upgrades=null,this.pingInterval=null,this.pingTimeout=null,this.pingIntervalTimer=null,this.pingTimeoutTimer=null,this.open()}function o(t){var e={};for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e}var i=r(21),s=r(35),a=r(3)(\"engine.io-client:socket\"),c=r(42),u=r(27),h=r(2),p=r(43),f=r(36);t.exports=n,n.priorWebsocketSuccess=!1,s(n.prototype),n.protocol=u.protocol,n.Socket=n,n.Transport=r(26),n.transports=r(21),n.parser=r(27),n.prototype.createTransport=function(t){a('creating transport \"%s\"',t);var e=o(this.query);e.EIO=u.protocol,e.transport=t,this.id&&(e.sid=this.id);var r=new i[t]({agent:this.agent,hostname:this.hostname,port:this.port,secure:this.secure,path:this.path,query:e,forceJSONP:this.forceJSONP,jsonp:this.jsonp,forceBase64:this.forceBase64,enablesXDR:this.enablesXDR,timestampRequests:this.timestampRequests,timestampParam:this.timestampParam,policyPort:this.policyPort,socket:this,pfx:this.pfx,key:this.key,passphrase:this.passphrase,cert:this.cert,ca:this.ca,ciphers:this.ciphers,rejectUnauthorized:this.rejectUnauthorized,perMessageDeflate:this.perMessageDeflate,extraHeaders:this.extraHeaders,forceNode:this.forceNode,localAddress:this.localAddress});return r},n.prototype.open=function(){var t;if(this.rememberUpgrade&&n.priorWebsocketSuccess&&this.transports.indexOf(\"websocket\")!==-1)t=\"websocket\";else{if(0===this.transports.length){var e=this;return void setTimeout(function(){e.emit(\"error\",\"No transports available\")},0)}t=this.transports[0]}this.readyState=\"opening\";try{t=this.createTransport(t)}catch(t){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t)},n.prototype.setTransport=function(t){a(\"setting transport %s\",t.name);var e=this;this.transport&&(a(\"clearing existing transport %s\",this.transport.name),this.transport.removeAllListeners()),this.transport=t,t.on(\"drain\",function(){e.onDrain()}).on(\"packet\",function(t){e.onPacket(t)}).on(\"error\",function(t){e.onError(t)}).on(\"close\",function(){e.onClose(\"transport close\")})},n.prototype.probe=function(t){function e(){if(f.onlyBinaryUpgrades){var e=!this.supportsBinary&&f.transport.supportsBinary;p=p||e}p||(a('probe transport \"%s\" opened',t),h.send([{type:\"ping\",data:\"probe\"}]),h.once(\"packet\",function(e){if(!p)if(\"pong\"===e.type&&\"probe\"===e.data){if(a('probe transport \"%s\" pong',t),f.upgrading=!0,f.emit(\"upgrading\",h),!h)return;n.priorWebsocketSuccess=\"websocket\"===h.name,a('pausing current transport \"%s\"',f.transport.name),f.transport.pause(function(){p||\"closed\"!==f.readyState&&(a(\"changing transport and sending upgrade packet\"),u(),f.setTransport(h),h.send([{type:\"upgrade\"}]),f.emit(\"upgrade\",h),h=null,f.upgrading=!1,f.flush())})}else{a('probe transport \"%s\" failed',t);var r=new Error(\"probe error\");r.transport=h.name,f.emit(\"upgradeError\",r)}}))}function r(){p||(p=!0,u(),h.close(),h=null)}function o(e){var n=new Error(\"probe error: \"+e);n.transport=h.name,r(),a('probe transport \"%s\" failed because of error: %s',t,e),f.emit(\"upgradeError\",n)}function i(){o(\"transport closed\")}function s(){o(\"socket closed\")}function c(t){h&&t.name!==h.name&&(a('\"%s\" works - aborting \"%s\"',t.name,h.name),r())}function u(){h.removeListener(\"open\",e),h.removeListener(\"error\",o),h.removeListener(\"close\",i),f.removeListener(\"close\",s),f.removeListener(\"upgrading\",c)}a('probing transport \"%s\"',t);var h=this.createTransport(t,{probe:1}),p=!1,f=this;n.priorWebsocketSuccess=!1,h.once(\"open\",e),h.once(\"error\",o),h.once(\"close\",i),this.once(\"close\",s),this.once(\"upgrading\",c),h.open()},n.prototype.onOpen=function(){if(a(\"socket open\"),this.readyState=\"open\",n.priorWebsocketSuccess=\"websocket\"===this.transport.name,this.emit(\"open\"),this.flush(),\"open\"===this.readyState&&this.upgrade&&this.transport.pause){a(\"starting upgrade probes\");for(var t=0,e=this.upgrades.length;t<e;t++)this.probe(this.upgrades[t])}},n.prototype.onPacket=function(t){if(\"opening\"===this.readyState||\"open\"===this.readyState||\"closing\"===this.readyState)switch(a('socket receive: type \"%s\", data \"%s\"',t.type,t.data),this.emit(\"packet\",t),this.emit(\"heartbeat\"),t.type){case\"open\":this.onHandshake(p(t.data));break;case\"pong\":this.setPing(),this.emit(\"pong\");break;case\"error\":var e=new Error(\"server error\");e.code=t.data,this.onError(e);break;case\"message\":this.emit(\"data\",t.data),this.emit(\"message\",t.data)}else a('packet received with socket readyState \"%s\"',this.readyState)},n.prototype.onHandshake=function(t){this.emit(\"handshake\",t),this.id=t.sid,this.transport.query.sid=t.sid,this.upgrades=this.filterUpgrades(t.upgrades),this.pingInterval=t.pingInterval,this.pingTimeout=t.pingTimeout,this.onOpen(),\"closed\"!==this.readyState&&(this.setPing(),this.removeListener(\"heartbeat\",this.onHeartbeat),this.on(\"heartbeat\",this.onHeartbeat))},n.prototype.onHeartbeat=function(t){clearTimeout(this.pingTimeoutTimer);var e=this;e.pingTimeoutTimer=setTimeout(function(){\"closed\"!==e.readyState&&e.onClose(\"ping timeout\")},t||e.pingInterval+e.pingTimeout)},n.prototype.setPing=function(){var t=this;clearTimeout(t.pingIntervalTimer),t.pingIntervalTimer=setTimeout(function(){a(\"writing ping packet - expecting pong within %sms\",t.pingTimeout),t.ping(),t.onHeartbeat(t.pingTimeout)},t.pingInterval)},n.prototype.ping=function(){var t=this;this.sendPacket(\"ping\",function(){t.emit(\"ping\")})},n.prototype.onDrain=function(){this.writeBuffer.splice(0,this.prevBufferLen),this.prevBufferLen=0,0===this.writeBuffer.length?this.emit(\"drain\"):this.flush()},n.prototype.flush=function(){\"closed\"!==this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length&&(a(\"flushing %d packets in socket\",this.writeBuffer.length),this.transport.send(this.writeBuffer),this.prevBufferLen=this.writeBuffer.length,this.emit(\"flush\"))},n.prototype.write=n.prototype.send=function(t,e,r){return this.sendPacket(\"message\",t,e,r),this},n.prototype.sendPacket=function(t,e,r,n){if(\"function\"==typeof e&&(n=e,e=void 0),\"function\"==typeof r&&(n=r,r=null),\"closing\"!==this.readyState&&\"closed\"!==this.readyState){r=r||{},r.compress=!1!==r.compress;var o={type:t,data:e,options:r};this.emit(\"packetCreate\",o),this.writeBuffer.push(o),n&&this.once(\"flush\",n),this.flush()}},n.prototype.close=function(){function t(){n.onClose(\"forced close\"),a(\"socket closing - telling transport to close\"),n.transport.close()}function e(){n.removeListener(\"upgrade\",e),n.removeListener(\"upgradeError\",e),t()}function r(){n.once(\"upgrade\",e),n.once(\"upgradeError\",e)}if(\"opening\"===this.readyState||\"open\"===this.readyState){this.readyState=\"closing\";var n=this;this.writeBuffer.length?this.once(\"drain\",function(){this.upgrading?r():t()}):this.upgrading?r():t()}return this},n.prototype.onError=function(t){a(\"socket error %j\",t),n.priorWebsocketSuccess=!1,this.emit(\"error\",t),this.onClose(\"transport error\",t)},n.prototype.onClose=function(t,e){if(\"opening\"===this.readyState||\"open\"===this.readyState||\"closing\"===this.readyState){a('socket close with reason: \"%s\"',t);var r=this;clearTimeout(this.pingIntervalTimer),clearTimeout(this.pingTimeoutTimer),this.transport.removeAllListeners(\"close\"),this.transport.close(),this.transport.removeAllListeners(),this.readyState=\"closed\",this.id=null,this.emit(\"close\",t,e),r.writeBuffer=[],r.prevBufferLen=0}},n.prototype.filterUpgrades=function(t){for(var e=[],r=0,n=t.length;r<n;r++)~c(this.transports,t[r])&&e.push(t[r]);return e}}).call(e,function(){return this}())},function(t,e,r){(function(t){function n(e){var r,n=!1,a=!1,c=!1!==e.jsonp;if(t.location){var u=\"https:\"===location.protocol,h=location.port;h||(h=u?443:80),n=e.hostname!==location.hostname||h!==e.port,a=e.secure!==u}if(e.xdomain=n,e.xscheme=a,r=new o(e),\"open\"in r&&!e.forceJSONP)return new i(e);if(!c)throw new Error(\"JSONP disabled\");return new s(e)}var o=r(22),i=r(24),s=r(39),a=r(40);e.polling=n,e.websocket=a}).call(e,function(){return this}())},function(t,e,r){(function(e){var n=r(23);t.exports=function(t){var r=t.xdomain,o=t.xscheme,i=t.enablesXDR;try{if(\"undefined\"!=typeof XMLHttpRequest&&(!r||n))return new XMLHttpRequest}catch(t){}try{if(\"undefined\"!=typeof XDomainRequest&&!o&&i)return new XDomainRequest}catch(t){}if(!r)try{return new(e[[\"Active\"].concat(\"Object\").join(\"X\")])(\"Microsoft.XMLHTTP\")}catch(t){}}}).call(e,function(){return this}())},function(t,e){try{t.exports=\"undefined\"!=typeof XMLHttpRequest&&\"withCredentials\"in new XMLHttpRequest}catch(e){t.exports=!1}},function(t,e,r){(function(e){function n(){}function o(t){if(c.call(this,t),this.requestTimeout=t.requestTimeout,e.location){var r=\"https:\"===location.protocol,n=location.port;n||(n=r?443:80),this.xd=t.hostname!==e.location.hostname||n!==t.port,this.xs=t.secure!==r}else this.extraHeaders=t.extraHeaders}function i(t){this.method=t.method||\"GET\",this.uri=t.uri,this.xd=!!t.xd,this.xs=!!t.xs,this.async=!1!==t.async,this.data=void 0!==t.data?t.data:null,this.agent=t.agent,this.isBinary=t.isBinary,this.supportsBinary=t.supportsBinary,this.enablesXDR=t.enablesXDR,this.requestTimeout=t.requestTimeout,this.pfx=t.pfx,this.key=t.key,this.passphrase=t.passphrase,this.cert=t.cert,this.ca=t.ca,this.ciphers=t.ciphers,this.rejectUnauthorized=t.rejectUnauthorized,this.extraHeaders=t.extraHeaders,this.create()}function s(){for(var t in i.requests)i.requests.hasOwnProperty(t)&&i.requests[t].abort()}var a=r(22),c=r(25),u=r(35),h=r(37),p=r(3)(\"engine.io-client:polling-xhr\");t.exports=o,t.exports.Request=i,h(o,c),o.prototype.supportsBinary=!0,o.prototype.request=function(t){return t=t||{},t.uri=this.uri(),t.xd=this.xd,t.xs=this.xs,t.agent=this.agent||!1,t.supportsBinary=this.supportsBinary,t.enablesXDR=this.enablesXDR,t.pfx=this.pfx,t.key=this.key,t.passphrase=this.passphrase,t.cert=this.cert,t.ca=this.ca,t.ciphers=this.ciphers,t.rejectUnauthorized=this.rejectUnauthorized,t.requestTimeout=this.requestTimeout,t.extraHeaders=this.extraHeaders,new i(t)},o.prototype.doWrite=function(t,e){var r=\"string\"!=typeof t&&void 0!==t,n=this.request({method:\"POST\",data:t,isBinary:r}),o=this;n.on(\"success\",e),n.on(\"error\",function(t){o.onError(\"xhr post error\",t)}),this.sendXhr=n},o.prototype.doPoll=function(){p(\"xhr poll\");var t=this.request(),e=this;t.on(\"data\",function(t){e.onData(t)}),t.on(\"error\",function(t){e.onError(\"xhr poll error\",t)}),this.pollXhr=t},u(i.prototype),i.prototype.create=function(){var t={agent:this.agent,xdomain:this.xd,xscheme:this.xs,enablesXDR:this.enablesXDR};t.pfx=this.pfx,t.key=this.key,t.passphrase=this.passphrase,t.cert=this.cert,t.ca=this.ca,t.ciphers=this.ciphers,t.rejectUnauthorized=this.rejectUnauthorized;var r=this.xhr=new a(t),n=this;try{p(\"xhr open %s: %s\",this.method,this.uri),r.open(this.method,this.uri,this.async);try{if(this.extraHeaders){r.setDisableHeaderCheck(!0);for(var o in this.extraHeaders)this.extraHeaders.hasOwnProperty(o)&&r.setRequestHeader(o,this.extraHeaders[o])}}catch(t){}if(this.supportsBinary&&(r.responseType=\"arraybuffer\"),\"POST\"===this.method)try{this.isBinary?r.setRequestHeader(\"Content-type\",\"application/octet-stream\"):r.setRequestHeader(\"Content-type\",\"text/plain;charset=UTF-8\")}catch(t){}try{r.setRequestHeader(\"Accept\",\"*/*\")}catch(t){}\"withCredentials\"in r&&(r.withCredentials=!0),this.requestTimeout&&(r.timeout=this.requestTimeout),this.hasXDR()?(r.onload=function(){n.onLoad()},r.onerror=function(){n.onError(r.responseText)}):r.onreadystatechange=function(){4===r.readyState&&(200===r.status||1223===r.status?n.onLoad():setTimeout(function(){n.onError(r.status)},0))},p(\"xhr data %s\",this.data),r.send(this.data)}catch(t){return void setTimeout(function(){n.onError(t)},0)}e.document&&(this.index=i.requestsCount++,i.requests[this.index]=this)},i.prototype.onSuccess=function(){this.emit(\"success\"),this.cleanup()},i.prototype.onData=function(t){this.emit(\"data\",t),this.onSuccess()},i.prototype.onError=function(t){this.emit(\"error\",t),this.cleanup(!0)},i.prototype.cleanup=function(t){if(\"undefined\"!=typeof this.xhr&&null!==this.xhr){if(this.hasXDR()?this.xhr.onload=this.xhr.onerror=n:this.xhr.onreadystatechange=n,t)try{this.xhr.abort()}catch(t){}e.document&&delete i.requests[this.index],this.xhr=null}},i.prototype.onLoad=function(){var t;try{var e;try{e=this.xhr.getResponseHeader(\"Content-Type\").split(\";\")[0]}catch(t){}if(\"application/octet-stream\"===e)t=this.xhr.response||this.xhr.responseText;else if(this.supportsBinary)try{t=String.fromCharCode.apply(null,new Uint8Array(this.xhr.response))}catch(e){for(var r=new Uint8Array(this.xhr.response),n=[],o=0,i=r.length;o<i;o++)n.push(r[o]);t=String.fromCharCode.apply(null,n)}else t=this.xhr.responseText}catch(t){this.onError(t)}null!=t&&this.onData(t)},i.prototype.hasXDR=function(){return\"undefined\"!=typeof e.XDomainRequest&&!this.xs&&this.enablesXDR},i.prototype.abort=function(){this.cleanup()},i.requestsCount=0,i.requests={},e.document&&(e.attachEvent?e.attachEvent(\"onunload\",s):e.addEventListener&&e.addEventListener(\"beforeunload\",s,!1))}).call(e,function(){return this}())},function(t,e,r){function n(t){var e=t&&t.forceBase64;h&&!e||(this.supportsBinary=!1),o.call(this,t)}var o=r(26),i=r(36),s=r(27),a=r(37),c=r(38),u=r(3)(\"engine.io-client:polling\");t.exports=n;var h=function(){var t=r(22),e=new t({xdomain:!1});return null!=e.responseType}();a(n,o),n.prototype.name=\"polling\",n.prototype.doOpen=function(){this.poll()},n.prototype.pause=function(t){function e(){u(\"paused\"),r.readyState=\"paused\",t()}var r=this;if(this.readyState=\"pausing\",this.polling||!this.writable){var n=0;this.polling&&(u(\"we are currently polling - waiting to pause\"),n++,this.once(\"pollComplete\",function(){u(\"pre-pause polling complete\"),--n||e()})),this.writable||(u(\"we are currently writing - waiting to pause\"),n++,this.once(\"drain\",function(){u(\"pre-pause writing complete\"),--n||e()}))}else e()},n.prototype.poll=function(){u(\"polling\"),this.polling=!0,this.doPoll(),this.emit(\"poll\")},n.prototype.onData=function(t){var e=this;u(\"polling got data %s\",t);var r=function(t,r,n){return\"opening\"===e.readyState&&e.onOpen(),\"close\"===t.type?(e.onClose(),!1):void e.onPacket(t)};s.decodePayload(t,this.socket.binaryType,r),\"closed\"!==this.readyState&&(this.polling=!1,this.emit(\"pollComplete\"),\"open\"===this.readyState?this.poll():u('ignoring poll - transport state \"%s\"',this.readyState))},n.prototype.doClose=function(){function t(){u(\"writing close packet\"),e.write([{type:\"close\"}])}var e=this;\"open\"===this.readyState?(u(\"transport open - closing\"),t()):(u(\"transport not open - deferring close\"),this.once(\"open\",t))},n.prototype.write=function(t){var e=this;this.writable=!1;var r=function(){e.writable=!0,e.emit(\"drain\")};s.encodePayload(t,this.supportsBinary,function(t){e.doWrite(t,r)})},n.prototype.uri=function(){var t=this.query||{},e=this.secure?\"https\":\"http\",r=\"\";!1!==this.timestampRequests&&(t[this.timestampParam]=c()),this.supportsBinary||t.sid||(t.b64=1),t=i.encode(t),this.port&&(\"https\"===e&&443!==Number(this.port)||\"http\"===e&&80!==Number(this.port))&&(r=\":\"+this.port),t.length&&(t=\"?\"+t);var n=this.hostname.indexOf(\":\")!==-1;return e+\"://\"+(n?\"[\"+this.hostname+\"]\":this.hostname)+r+this.path+t}},function(t,e,r){function n(t){this.path=t.path,this.hostname=t.hostname,this.port=t.port,this.secure=t.secure,this.query=t.query,this.timestampParam=t.timestampParam,this.timestampRequests=t.timestampRequests,this.readyState=\"\",this.agent=t.agent||!1,this.socket=t.socket,this.enablesXDR=t.enablesXDR,this.pfx=t.pfx,this.key=t.key,this.passphrase=t.passphrase,this.cert=t.cert,this.ca=t.ca,this.ciphers=t.ciphers,this.rejectUnauthorized=t.rejectUnauthorized,this.forceNode=t.forceNode,this.extraHeaders=t.extraHeaders,this.localAddress=t.localAddress}var o=r(27),i=r(35);t.exports=n,i(n.prototype),n.prototype.onError=function(t,e){var r=new Error(t);return r.type=\"TransportError\",r.description=e,this.emit(\"error\",r),this},n.prototype.open=function(){return\"closed\"!==this.readyState&&\"\"!==this.readyState||(this.readyState=\"opening\",this.doOpen()),this},n.prototype.close=function(){return\"opening\"!==this.readyState&&\"open\"!==this.readyState||(this.doClose(),this.onClose()),this},n.prototype.send=function(t){if(\"open\"!==this.readyState)throw new Error(\"Transport not open\");this.write(t)},n.prototype.onOpen=function(){this.readyState=\"open\",this.writable=!0,this.emit(\"open\")},n.prototype.onData=function(t){var e=o.decodePacket(t,this.socket.binaryType);this.onPacket(e)},n.prototype.onPacket=function(t){this.emit(\"packet\",t)},n.prototype.onClose=function(){this.readyState=\"closed\",this.emit(\"close\")}},function(t,e,r){(function(t){function n(t,r){var n=\"b\"+e.packets[t.type]+t.data.data;return r(n)}function o(t,r,n){if(!r)return e.encodeBase64Packet(t,n);var o=t.data,i=new Uint8Array(o),s=new Uint8Array(1+o.byteLength);s[0]=v[t.type];for(var a=0;a<i.length;a++)s[a+1]=i[a];return n(s.buffer)}function i(t,r,n){if(!r)return e.encodeBase64Packet(t,n);var o=new FileReader;return o.onload=function(){t.data=o.result,e.encodePacket(t,r,!0,n)},o.readAsArrayBuffer(t.data)}function s(t,r,n){if(!r)return e.encodeBase64Packet(t,n);if(m)return i(t,r,n);var o=new Uint8Array(1);o[0]=v[t.type];var s=new k([o.buffer,t.data]);return n(s)}function a(t){try{t=d.decode(t)}catch(t){return!1}return t}function c(t,e,r){for(var n=new Array(t.length),o=l(t.length,r),i=function(t,r,o){e(r,function(e,r){n[t]=r,o(e,n)})},s=0;s<t.length;s++)i(s,t[s],o)}var u,h=r(28),p=r(29),f=r(30),l=r(31),d=r(32);t&&t.ArrayBuffer&&(u=r(33));var y=\"undefined\"!=typeof navigator&&/Android/i.test(navigator.userAgent),g=\"undefined\"!=typeof navigator&&/PhantomJS/i.test(navigator.userAgent),m=y||g;e.protocol=3;var v=e.packets={open:0,close:1,ping:2,pong:3,message:4,upgrade:5,noop:6},b=h(v),w={type:\"error\",data:\"parser error\"},k=r(34);e.encodePacket=function(e,r,i,a){\"function\"==typeof r&&(a=r,r=!1),\"function\"==typeof i&&(a=i,i=null);var c=void 0===e.data?void 0:e.data.buffer||e.data;if(t.ArrayBuffer&&c instanceof ArrayBuffer)return o(e,r,a);if(k&&c instanceof t.Blob)return s(e,r,a);if(c&&c.base64)return n(e,a);var u=v[e.type];return void 0!==e.data&&(u+=i?d.encode(String(e.data)):String(e.data)),a(\"\"+u)},e.encodeBase64Packet=function(r,n){var o=\"b\"+e.packets[r.type];if(k&&r.data instanceof t.Blob){var i=new FileReader;return i.onload=function(){var t=i.result.split(\",\")[1];n(o+t)},i.readAsDataURL(r.data)}var s;try{s=String.fromCharCode.apply(null,new Uint8Array(r.data))}catch(t){for(var a=new Uint8Array(r.data),c=new Array(a.length),u=0;u<a.length;u++)c[u]=a[u];s=String.fromCharCode.apply(null,c)}return o+=t.btoa(s),n(o)},e.decodePacket=function(t,r,n){if(void 0===t)return w;if(\"string\"==typeof t){if(\"b\"==t.charAt(0))return e.decodeBase64Packet(t.substr(1),r);if(n&&(t=a(t),t===!1))return w;var o=t.charAt(0);return Number(o)==o&&b[o]?t.length>1?{type:b[o],data:t.substring(1)}:{type:b[o]}:w}var i=new Uint8Array(t),o=i[0],s=f(t,1);return k&&\"blob\"===r&&(s=new k([s])),{type:b[o],data:s}},e.decodeBase64Packet=function(t,e){var r=b[t.charAt(0)];if(!u)return{type:r,data:{base64:!0,data:t.substr(1)}};var n=u.decode(t.substr(1));return\"blob\"===e&&k&&(n=new k([n])),{type:r,data:n}},e.encodePayload=function(t,r,n){function o(t){return t.length+\":\"+t}function i(t,n){e.encodePacket(t,!!s&&r,!0,function(t){n(null,o(t))})}\"function\"==typeof r&&(n=r,r=null);var s=p(t);return r&&s?k&&!m?e.encodePayloadAsBlob(t,n):e.encodePayloadAsArrayBuffer(t,n):t.length?void c(t,i,function(t,e){return n(e.join(\"\"))}):n(\"0:\")},e.decodePayload=function(t,r,n){if(\"string\"!=typeof t)return e.decodePayloadAsBinary(t,r,n);\"function\"==typeof r&&(n=r,r=null);var o;if(\"\"==t)return n(w,0,1);for(var i,s,a=\"\",c=0,u=t.length;c<u;c++){var h=t.charAt(c);if(\":\"!=h)a+=h;else{if(\"\"==a||a!=(i=Number(a)))return n(w,0,1);if(s=t.substr(c+1,i),a!=s.length)return n(w,0,1);if(s.length){if(o=e.decodePacket(s,r,!0),w.type==o.type&&w.data==o.data)return n(w,0,1);var p=n(o,c+i,u);if(!1===p)return}c+=i,a=\"\"}}return\"\"!=a?n(w,0,1):void 0},e.encodePayloadAsArrayBuffer=function(t,r){function n(t,r){e.encodePacket(t,!0,!0,function(t){return r(null,t)})}return t.length?void c(t,n,function(t,e){var n=e.reduce(function(t,e){var r;return r=\"string\"==typeof e?e.length:e.byteLength,t+r.toString().length+r+2},0),o=new Uint8Array(n),i=0;return e.forEach(function(t){var e=\"string\"==typeof t,r=t;if(e){for(var n=new Uint8Array(t.length),s=0;s<t.length;s++)n[s]=t.charCodeAt(s);r=n.buffer}e?o[i++]=0:o[i++]=1;for(var a=r.byteLength.toString(),s=0;s<a.length;s++)o[i++]=parseInt(a[s]);o[i++]=255;for(var n=new Uint8Array(r),s=0;s<n.length;s++)o[i++]=n[s]}),r(o.buffer)}):r(new ArrayBuffer(0))},e.encodePayloadAsBlob=function(t,r){function n(t,r){e.encodePacket(t,!0,!0,function(t){var e=new Uint8Array(1);if(e[0]=1,\"string\"==typeof t){for(var n=new Uint8Array(t.length),o=0;o<t.length;o++)n[o]=t.charCodeAt(o);t=n.buffer,e[0]=0}for(var i=t instanceof ArrayBuffer?t.byteLength:t.size,s=i.toString(),a=new Uint8Array(s.length+1),o=0;o<s.length;o++)a[o]=parseInt(s[o]);if(a[s.length]=255,k){var c=new k([e.buffer,a.buffer,t]);r(null,c)}})}c(t,n,function(t,e){return r(new k(e))})},e.decodePayloadAsBinary=function(t,r,n){\"function\"==typeof r&&(n=r,r=null);for(var o=t,i=[],s=!1;o.byteLength>0;){for(var a=new Uint8Array(o),c=0===a[0],u=\"\",h=1;255!=a[h];h++){if(u.length>310){s=!0;break}u+=a[h]}if(s)return n(w,0,1);o=f(o,2+u.length),u=parseInt(u);var p=f(o,0,u);if(c)try{p=String.fromCharCode.apply(null,new Uint8Array(p))}catch(t){var l=new Uint8Array(p);p=\"\";for(var h=0;h<l.length;h++)p+=String.fromCharCode(l[h])}i.push(p),o=f(o,u)}var d=i.length;i.forEach(function(t,o){n(e.decodePacket(t,r,!0),o,d)})}}).call(e,function(){return this}())},function(t,e){t.exports=Object.keys||function(t){var e=[],r=Object.prototype.hasOwnProperty;for(var n in t)r.call(t,n)&&e.push(n);return e}},function(t,e,r){(function(e){function n(t){function r(t){if(!t)return!1;if(e.Buffer&&e.Buffer.isBuffer&&e.Buffer.isBuffer(t)||e.ArrayBuffer&&t instanceof ArrayBuffer||e.Blob&&t instanceof Blob||e.File&&t instanceof File)return!0;if(o(t)){for(var n=0;n<t.length;n++)if(r(t[n]))return!0}else if(t&&\"object\"==typeof t){t.toJSON&&\"function\"==typeof t.toJSON&&(t=t.toJSON());for(var i in t)if(Object.prototype.hasOwnProperty.call(t,i)&&r(t[i]))return!0}return!1}return r(t)}var o=r(15);t.exports=n}).call(e,function(){return this}())},function(t,e){t.exports=function(t,e,r){var n=t.byteLength;if(e=e||0,r=r||n,t.slice)return t.slice(e,r);if(e<0&&(e+=n),r<0&&(r+=n),r>n&&(r=n),e>=n||e>=r||0===n)return new ArrayBuffer(0);for(var o=new Uint8Array(t),i=new Uint8Array(r-e),s=e,a=0;s<r;s++,a++)i[a]=o[s];return i.buffer}},function(t,e){function r(t,e,r){function o(t,n){if(o.count<=0)throw new Error(\"after called too many times\");--o.count,t?(i=!0,e(t),e=r):0!==o.count||i||e(null,n)}var i=!1;return r=r||n,o.count=t,0===t?e():o}function n(){}t.exports=r},function(t,e,r){var n;(function(t,o){!function(i){function s(t){for(var e,r,n=[],o=0,i=t.length;o<i;)e=t.charCodeAt(o++),e>=55296&&e<=56319&&o<i?(r=t.charCodeAt(o++),56320==(64512&r)?n.push(((1023&e)<<10)+(1023&r)+65536):(n.push(e),o--)):n.push(e);return n}function a(t){for(var e,r=t.length,n=-1,o=\"\";++n<r;)e=t[n],e>65535&&(e-=65536,o+=b(e>>>10&1023|55296),e=56320|1023&e),o+=b(e);return o}function c(t,e){return b(t>>e&63|128)}function u(t){if(0==(4294967168&t))return b(t);var e=\"\";return 0==(4294965248&t)?e=b(t>>6&31|192):0==(4294901760&t)?(e=b(t>>12&15|224),e+=c(t,6)):0==(4292870144&t)&&(e=b(t>>18&7|240),e+=c(t,12),e+=c(t,6)),e+=b(63&t|128)}function h(t){for(var e,r=s(t),n=r.length,o=-1,i=\"\";++o<n;)e=r[o],i+=u(e);return i}function p(){if(v>=m)throw Error(\"Invalid byte index\");var t=255&g[v];if(v++,128==(192&t))return 63&t;throw Error(\"Invalid continuation byte\")}function f(){var t,e,r,n,o;if(v>m)throw Error(\"Invalid byte index\");if(v==m)return!1;if(t=255&g[v],v++,0==(128&t))return t;if(192==(224&t)){var e=p();if(o=(31&t)<<6|e,o>=128)return o;throw Error(\"Invalid continuation byte\")}if(224==(240&t)){if(e=p(),r=p(),o=(15&t)<<12|e<<6|r,o>=2048)return o;throw Error(\"Invalid continuation byte\")}if(240==(248&t)&&(e=p(),r=p(),n=p(),o=(15&t)<<18|e<<12|r<<6|n,o>=65536&&o<=1114111))return o;throw Error(\"Invalid WTF-8 detected\")}function l(t){g=s(t),m=g.length,v=0;for(var e,r=[];(e=f())!==!1;)r.push(e);return a(r)}var d=\"object\"==typeof e&&e,y=(\"object\"==typeof t&&t&&t.exports==d&&t,\"object\"==typeof o&&o);y.global!==y&&y.window!==y||(i=y);var g,m,v,b=String.fromCharCode,w={version:\"1.0.0\",encode:h,decode:l};n=function(){return w}.call(e,r,e,t),!(void 0!==n&&(t.exports=n))}(this)}).call(e,r(12)(t),function(){return this}())},function(t,e){!function(){\"use strict\";for(var t=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",r=new Uint8Array(256),n=0;n<t.length;n++)r[t.charCodeAt(n)]=n;e.encode=function(e){var r,n=new Uint8Array(e),o=n.length,i=\"\";for(r=0;r<o;r+=3)i+=t[n[r]>>2],i+=t[(3&n[r])<<4|n[r+1]>>4],i+=t[(15&n[r+1])<<2|n[r+2]>>6],i+=t[63&n[r+2]];return o%3===2?i=i.substring(0,i.length-1)+\"=\":o%3===1&&(i=i.substring(0,i.length-2)+\"==\"),i},e.decode=function(t){var e,n,o,i,s,a=.75*t.length,c=t.length,u=0;\"=\"===t[t.length-1]&&(a--,\"=\"===t[t.length-2]&&a--);var h=new ArrayBuffer(a),p=new Uint8Array(h);for(e=0;e<c;e+=4)n=r[t.charCodeAt(e)],o=r[t.charCodeAt(e+1)],i=r[t.charCodeAt(e+2)],s=r[t.charCodeAt(e+3)],p[u++]=n<<2|o>>4,p[u++]=(15&o)<<4|i>>2,p[u++]=(3&i)<<6|63&s;return h}}()},function(t,e){(function(e){function r(t){for(var e=0;e<t.length;e++){var r=t[e];if(r.buffer instanceof ArrayBuffer){var n=r.buffer;if(r.byteLength!==n.byteLength){var o=new Uint8Array(r.byteLength);o.set(new Uint8Array(n,r.byteOffset,r.byteLength)),n=o.buffer}t[e]=n}}}function n(t,e){e=e||{};var n=new i;r(t);for(var o=0;o<t.length;o++)n.append(t[o]);return e.type?n.getBlob(e.type):n.getBlob()}function o(t,e){return r(t),new Blob(t,e||{})}var i=e.BlobBuilder||e.WebKitBlobBuilder||e.MSBlobBuilder||e.MozBlobBuilder,s=function(){try{var t=new Blob([\"hi\"]);return 2===t.size}catch(t){return!1}}(),a=s&&function(){try{var t=new Blob([new Uint8Array([1,2])]);return 2===t.size}catch(t){return!1}}(),c=i&&i.prototype.append&&i.prototype.getBlob;t.exports=function(){return s?a?e.Blob:o:c?n:void 0}()}).call(e,function(){return this}())},function(t,e,r){function n(t){if(t)return o(t)}function o(t){for(var e in n.prototype)t[e]=n.prototype[e];return t}t.exports=n,n.prototype.on=n.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks[\"$\"+t]=this._callbacks[\"$\"+t]||[]).push(e),this},n.prototype.once=function(t,e){function r(){this.off(t,r),e.apply(this,arguments)}return r.fn=e,this.on(t,r),this},n.prototype.off=n.prototype.removeListener=n.prototype.removeAllListeners=n.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var r=this._callbacks[\"$\"+t];if(!r)return this;if(1==arguments.length)return delete this._callbacks[\"$\"+t],this;for(var n,o=0;o<r.length;o++)if(n=r[o],n===e||n.fn===e){r.splice(o,1);break}return this},n.prototype.emit=function(t){this._callbacks=this._callbacks||{};var e=[].slice.call(arguments,1),r=this._callbacks[\"$\"+t];if(r){r=r.slice(0);for(var n=0,o=r.length;n<o;++n)r[n].apply(this,e)}return this},n.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks[\"$\"+t]||[]},n.prototype.hasListeners=function(t){return!!this.listeners(t).length}},function(t,e){e.encode=function(t){var e=\"\";for(var r in t)t.hasOwnProperty(r)&&(e.length&&(e+=\"&\"),e+=encodeURIComponent(r)+\"=\"+encodeURIComponent(t[r]));return e},e.decode=function(t){for(var e={},r=t.split(\"&\"),n=0,o=r.length;n<o;n++){var i=r[n].split(\"=\");e[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}return e}},function(t,e){t.exports=function(t,e){var r=function(){};r.prototype=e.prototype,t.prototype=new r,t.prototype.constructor=t}},function(t,e){\"use strict\";function r(t){var e=\"\";do e=s[t%a]+e,t=Math.floor(t/a);while(t>0);return e}function n(t){var e=0;for(h=0;h<t.length;h++)e=e*a+c[t.charAt(h)];return e}function o(){var t=r(+new Date);return t!==i?(u=0,i=t):t+\".\"+r(u++)}for(var i,s=\"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_\".split(\"\"),a=64,c={},u=0,h=0;h<a;h++)c[s[h]]=h;o.encode=r,o.decode=n,t.exports=o},function(t,e,r){(function(e){function n(){}function o(t){i.call(this,t),this.query=this.query||{},a||(e.___eio||(e.___eio=[]),a=e.___eio),this.index=a.length;var r=this;a.push(function(t){r.onData(t)}),this.query.j=this.index,e.document&&e.addEventListener&&e.addEventListener(\"beforeunload\",function(){r.script&&(r.script.onerror=n)},!1)}var i=r(25),s=r(37);t.exports=o;var a,c=/\\n/g,u=/\\\\n/g;s(o,i),o.prototype.supportsBinary=!1,o.prototype.doClose=function(){this.script&&(this.script.parentNode.removeChild(this.script),this.script=null),this.form&&(this.form.parentNode.removeChild(this.form),this.form=null,this.iframe=null),i.prototype.doClose.call(this)},o.prototype.doPoll=function(){var t=this,e=document.createElement(\"script\");this.script&&(this.script.parentNode.removeChild(this.script),this.script=null),e.async=!0,e.src=this.uri(),e.onerror=function(e){t.onError(\"jsonp poll error\",e)};var r=document.getElementsByTagName(\"script\")[0];r?r.parentNode.insertBefore(e,r):(document.head||document.body).appendChild(e),this.script=e;var n=\"undefined\"!=typeof navigator&&/gecko/i.test(navigator.userAgent);n&&setTimeout(function(){var t=document.createElement(\"iframe\");document.body.appendChild(t),document.body.removeChild(t)},100)},o.prototype.doWrite=function(t,e){function r(){n(),e()}function n(){if(o.iframe)try{o.form.removeChild(o.iframe)}catch(t){o.onError(\"jsonp polling iframe removal error\",t)}try{var t='<iframe src=\"javascript:0\" name=\"'+o.iframeId+'\">';i=document.createElement(t)}catch(t){i=document.createElement(\"iframe\"),i.name=o.iframeId,i.src=\"javascript:0\"}i.id=o.iframeId,o.form.appendChild(i),o.iframe=i}var o=this;if(!this.form){var i,s=document.createElement(\"form\"),a=document.createElement(\"textarea\"),h=this.iframeId=\"eio_iframe_\"+this.index;s.className=\"socketio\",s.style.position=\"absolute\",s.style.top=\"-1000px\",s.style.left=\"-1000px\",s.target=h,s.method=\"POST\",s.setAttribute(\"accept-charset\",\"utf-8\"),a.name=\"d\",s.appendChild(a),document.body.appendChild(s),this.form=s,this.area=a}this.form.action=this.uri(),n(),t=t.replace(u,\"\\\\\\n\"),this.area.value=t.replace(c,\"\\\\n\");try{this.form.submit()}catch(t){}this.iframe.attachEvent?this.iframe.onreadystatechange=function(){\"complete\"===o.iframe.readyState&&r();\n}:this.iframe.onload=r}}).call(e,function(){return this}())},function(t,e,r){(function(e){function n(t){var e=t&&t.forceBase64;e&&(this.supportsBinary=!1),this.perMessageDeflate=t.perMessageDeflate,this.usingBrowserWebSocket=p&&!t.forceNode,this.usingBrowserWebSocket||(f=o),i.call(this,t)}var o,i=r(26),s=r(27),a=r(36),c=r(37),u=r(38),h=r(3)(\"engine.io-client:websocket\"),p=e.WebSocket||e.MozWebSocket;if(\"undefined\"==typeof window)try{o=r(41)}catch(t){}var f=p;f||\"undefined\"!=typeof window||(f=o),t.exports=n,c(n,i),n.prototype.name=\"websocket\",n.prototype.supportsBinary=!0,n.prototype.doOpen=function(){if(this.check()){var t=this.uri(),e=void 0,r={agent:this.agent,perMessageDeflate:this.perMessageDeflate};r.pfx=this.pfx,r.key=this.key,r.passphrase=this.passphrase,r.cert=this.cert,r.ca=this.ca,r.ciphers=this.ciphers,r.rejectUnauthorized=this.rejectUnauthorized,this.extraHeaders&&(r.headers=this.extraHeaders),this.localAddress&&(r.localAddress=this.localAddress);try{this.ws=this.usingBrowserWebSocket?new f(t):new f(t,e,r)}catch(t){return this.emit(\"error\",t)}void 0===this.ws.binaryType&&(this.supportsBinary=!1),this.ws.supports&&this.ws.supports.binary?(this.supportsBinary=!0,this.ws.binaryType=\"nodebuffer\"):this.ws.binaryType=\"arraybuffer\",this.addEventListeners()}},n.prototype.addEventListeners=function(){var t=this;this.ws.onopen=function(){t.onOpen()},this.ws.onclose=function(){t.onClose()},this.ws.onmessage=function(e){t.onData(e.data)},this.ws.onerror=function(e){t.onError(\"websocket error\",e)}},n.prototype.write=function(t){function r(){n.emit(\"flush\"),setTimeout(function(){n.writable=!0,n.emit(\"drain\")},0)}var n=this;this.writable=!1;for(var o=t.length,i=0,a=o;i<a;i++)!function(t){s.encodePacket(t,n.supportsBinary,function(i){if(!n.usingBrowserWebSocket){var s={};if(t.options&&(s.compress=t.options.compress),n.perMessageDeflate){var a=\"string\"==typeof i?e.Buffer.byteLength(i):i.length;a<n.perMessageDeflate.threshold&&(s.compress=!1)}}try{n.usingBrowserWebSocket?n.ws.send(i):n.ws.send(i,s)}catch(t){h(\"websocket closed before onclose event\")}--o||r()})}(t[i])},n.prototype.onClose=function(){i.prototype.onClose.call(this)},n.prototype.doClose=function(){\"undefined\"!=typeof this.ws&&this.ws.close()},n.prototype.uri=function(){var t=this.query||{},e=this.secure?\"wss\":\"ws\",r=\"\";this.port&&(\"wss\"===e&&443!==Number(this.port)||\"ws\"===e&&80!==Number(this.port))&&(r=\":\"+this.port),this.timestampRequests&&(t[this.timestampParam]=u()),this.supportsBinary||(t.b64=1),t=a.encode(t),t.length&&(t=\"?\"+t);var n=this.hostname.indexOf(\":\")!==-1;return e+\"://\"+(n?\"[\"+this.hostname+\"]\":this.hostname)+r+this.path+t},n.prototype.check=function(){return!(!f||\"__initialize\"in f&&this.name===n.prototype.name)}}).call(e,function(){return this}())},function(t,e){},function(t,e){var r=[].indexOf;t.exports=function(t,e){if(r)return t.indexOf(e);for(var n=0;n<t.length;++n)if(t[n]===e)return n;return-1}},function(t,e){(function(e){var r=/^[\\],:{}\\s]*$/,n=/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g,o=/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g,i=/(?:^|:|,)(?:\\s*\\[)+/g,s=/^\\s+/,a=/\\s+$/;t.exports=function(t){return\"string\"==typeof t&&t?(t=t.replace(s,\"\").replace(a,\"\"),e.JSON&&JSON.parse?JSON.parse(t):r.test(t.replace(n,\"@\").replace(o,\"]\").replace(i,\"\"))?new Function(\"return \"+t)():void 0):null}}).call(e,function(){return this}())},function(t,e,r){\"use strict\";function n(t,e,r){this.io=t,this.nsp=e,this.json=this,this.ids=0,this.acks={},this.receiveBuffer=[],this.sendBuffer=[],this.connected=!1,this.disconnected=!0,r&&r.query&&(this.query=r.query),this.io.autoConnect&&this.open()}var o=r(7),i=r(35),s=r(45),a=r(46),c=r(47),u=r(3)(\"socket.io-client:socket\"),h=r(29);t.exports=e=n;var p={connect:1,connect_error:1,connect_timeout:1,connecting:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1,ping:1,pong:1},f=i.prototype.emit;i(n.prototype),n.prototype.subEvents=function(){if(!this.subs){var t=this.io;this.subs=[a(t,\"open\",c(this,\"onopen\")),a(t,\"packet\",c(this,\"onpacket\")),a(t,\"close\",c(this,\"onclose\"))]}},n.prototype.open=n.prototype.connect=function(){return this.connected?this:(this.subEvents(),this.io.open(),\"open\"===this.io.readyState&&this.onopen(),this.emit(\"connecting\"),this)},n.prototype.send=function(){var t=s(arguments);return t.unshift(\"message\"),this.emit.apply(this,t),this},n.prototype.emit=function(t){if(p.hasOwnProperty(t))return f.apply(this,arguments),this;var e=s(arguments),r=o.EVENT;h(e)&&(r=o.BINARY_EVENT);var n={type:r,data:e};return n.options={},n.options.compress=!this.flags||!1!==this.flags.compress,\"function\"==typeof e[e.length-1]&&(u(\"emitting packet with ack id %d\",this.ids),this.acks[this.ids]=e.pop(),n.id=this.ids++),this.connected?this.packet(n):this.sendBuffer.push(n),delete this.flags,this},n.prototype.packet=function(t){t.nsp=this.nsp,this.io.packet(t)},n.prototype.onopen=function(){u(\"transport is open - connecting\"),\"/\"!==this.nsp&&(this.query?this.packet({type:o.CONNECT,query:this.query}):this.packet({type:o.CONNECT}))},n.prototype.onclose=function(t){u(\"close (%s)\",t),this.connected=!1,this.disconnected=!0,delete this.id,this.emit(\"disconnect\",t)},n.prototype.onpacket=function(t){if(t.nsp===this.nsp)switch(t.type){case o.CONNECT:this.onconnect();break;case o.EVENT:this.onevent(t);break;case o.BINARY_EVENT:this.onevent(t);break;case o.ACK:this.onack(t);break;case o.BINARY_ACK:this.onack(t);break;case o.DISCONNECT:this.ondisconnect();break;case o.ERROR:this.emit(\"error\",t.data)}},n.prototype.onevent=function(t){var e=t.data||[];u(\"emitting event %j\",e),null!=t.id&&(u(\"attaching ack callback to event\"),e.push(this.ack(t.id))),this.connected?f.apply(this,e):this.receiveBuffer.push(e)},n.prototype.ack=function(t){var e=this,r=!1;return function(){if(!r){r=!0;var n=s(arguments);u(\"sending ack %j\",n);var i=h(n)?o.BINARY_ACK:o.ACK;e.packet({type:i,id:t,data:n})}}},n.prototype.onack=function(t){var e=this.acks[t.id];\"function\"==typeof e?(u(\"calling ack %s with %j\",t.id,t.data),e.apply(this,t.data),delete this.acks[t.id]):u(\"bad ack %s\",t.id)},n.prototype.onconnect=function(){this.connected=!0,this.disconnected=!1,this.emit(\"connect\"),this.emitBuffered()},n.prototype.emitBuffered=function(){var t;for(t=0;t<this.receiveBuffer.length;t++)f.apply(this,this.receiveBuffer[t]);for(this.receiveBuffer=[],t=0;t<this.sendBuffer.length;t++)this.packet(this.sendBuffer[t]);this.sendBuffer=[]},n.prototype.ondisconnect=function(){u(\"server disconnect (%s)\",this.nsp),this.destroy(),this.onclose(\"io server disconnect\")},n.prototype.destroy=function(){if(this.subs){for(var t=0;t<this.subs.length;t++)this.subs[t].destroy();this.subs=null}this.io.destroy(this)},n.prototype.close=n.prototype.disconnect=function(){return this.connected&&(u(\"performing disconnect (%s)\",this.nsp),this.packet({type:o.DISCONNECT})),this.destroy(),this.connected&&this.onclose(\"io client disconnect\"),this},n.prototype.compress=function(t){return this.flags=this.flags||{},this.flags.compress=t,this}},function(t,e){function r(t,e){var r=[];e=e||0;for(var n=e||0;n<t.length;n++)r[n-e]=t[n];return r}t.exports=r},function(t,e){\"use strict\";function r(t,e,r){return t.on(e,r),{destroy:function(){t.removeListener(e,r)}}}t.exports=r},function(t,e){var r=[].slice;t.exports=function(t,e){if(\"string\"==typeof e&&(e=t[e]),\"function\"!=typeof e)throw new Error(\"bind() requires a function\");var n=r.call(arguments,2);return function(){return e.apply(t,n.concat(r.call(arguments)))}}},function(t,e){function r(t){t=t||{},this.ms=t.min||100,this.max=t.max||1e4,this.factor=t.factor||2,this.jitter=t.jitter>0&&t.jitter<=1?t.jitter:0,this.attempts=0}t.exports=r,r.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),r=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-r:t+r}return 0|Math.min(t,this.max)},r.prototype.reset=function(){this.attempts=0},r.prototype.setMin=function(t){this.ms=t},r.prototype.setMax=function(t){this.max=t},r.prototype.setJitter=function(t){this.jitter=t}}])});"
  },
  {
    "path": "test/application.js",
    "content": "var app = require('../lib/application');\nvar pomelo = require('../');\nvar should = require('should');\n\nvar WAIT_TIME = 1000;\nvar mockBase = process.cwd() + '/test';\n\ndescribe('application test', function(){\n  afterEach(function() {\n    app.state = 0;\n    app.settings = {};\n  });\n\n  describe('#init', function() {\n    it('should init the app instance', function() {\n      app.init({base: mockBase});\n      app.state.should.equal(1);  // magic number from application.js\n    });\n  });\n\n  describe('#set and get', function() {\n    it('should play the role of normal set and get', function() {\n      should.not.exist(app.get('some undefined key'));\n\n      var key = 'some defined key', value = 'some value';\n      app.set(key, value);\n      value.should.equal(app.get(key));\n    });\n\n    it('should return the value if pass just one parameter to the set method', function() {\n      var key = 'some defined key', value = 'some value';\n      should.not.exist(app.set(key));\n      app.set(key, value);\n      value.should.equal(app.set(key));\n    });\n  });\n\n  describe(\"#enable and disable\", function() {\n    it('should play the role of enable and disable', function() {\n      var key = 'some enable key';\n      app.enabled(key).should.be.false;\n      app.disabled(key).should.be.true;\n\n      app.enable(key);\n      app.enabled(key).should.be.true;\n      app.disabled(key).should.be.false;\n\n      app.disable(key);\n      app.enabled(key).should.be.false;\n      app.disabled(key).should.be.true;\n    });\n  });\n\n  describe(\"#compoent\", function() {\n    it('should load the component and fire their lifecircle callback by app.start, app.afterStart, app.stop', function(done) {\n      var startCount = 0, afterStartCount = 0, stopCount = 0;\n\n      var mockComponent = {\n        start: function(cb) {\n          console.log('start invoked');\n          startCount++;\n          cb();\n        },\n\n        afterStart: function(cb) {\n          console.log('afterStart invoked');\n          afterStartCount++;\n          cb();\n        },\n\n        stop: function(force, cb) {\n          console.log('stop invoked');\n          stopCount++;\n          cb();\n        }\n      };\n\n      app.init({base: mockBase});\n      app.load(mockComponent);\n      app.start(function(err) {\n        should.not.exist(err);\n      });\n\n      setTimeout(function() {\n        // wait for after start\n        app.stop(false);\n\n        setTimeout(function() {\n          // wait for stop\n          startCount.should.equal(1);\n          afterStartCount.should.equal(1);\n          stopCount.should.equal(1);\n          done();\n        }, WAIT_TIME);\n      }, WAIT_TIME);\n    });\n\n    it('should access the component with a name by app.components.name after loaded', function() {\n      var key1 = 'key1', comp1 = {content: 'some thing in comp1'};\n      var comp2 = {name: 'key2', content: 'some thing in comp2'};\n      var key3 = 'key3';\n      var comp3 = function() {\n        return {content: 'some thing in comp3', name: key3};\n      };\n\n      app.init({base: mockBase});\n      app.load(key1, comp1);\n      app.load(comp2);\n      app.load(comp3);\n\n      app.components.key1.should.eql(comp1);\n      app.components.key2.should.eql(comp2);\n      app.components.key3.should.eql(comp3());\n    });\n\n    it('should ignore duplicated components', function() {\n      var key = 'key';\n      var comp1 = {content: 'some thing in comp1'};\n      var comp2 = {content: 'some thing in comp2'};\n\n      app.init({base: mockBase});\n      app.load(key, comp1);\n      app.load(key, comp2);\n\n      app.components[key].should.eql(comp1);\n      app.components[key].should.not.eql(comp2);\n    });\n  });\n\n  describe('#filter', function() {\n    it('should add before filter and could fetch it later', function() {\n      var filters = [\n        function() {console.error('filter1');},\n        function() {}\n      ];\n\n      app.init({base: mockBase});\n\n      var i, l;\n      for(i=0, l=filters.length; i<l; i++) {\n        app.before(filters[i]);\n      }\n\n      var filters2 = app.get('__befores__');\n      should.exist(filters2);\n      filters2.length.should.equal(filters.length);\n      for(i=0, l=filters2.length; i<l; i++) {\n        filters2[i].should.equal(filters[i]);\n      }\n    });\n\n    it('should add after filter and could fetch it later', function() {\n      var filters = [\n        function() {console.error('filter1');},\n        function() {}\n      ];\n\n      app.init({base: mockBase});\n\n      var i, l;\n      for(i=0, l=filters.length; i<l; i++) {\n        app.after(filters[i]);\n      }\n\n      var filters2 = app.get('__afters__');\n      should.exist(filters2);\n      filters2.length.should.equal(filters.length);\n      for(i=0, l=filters2.length; i<l; i++) {\n        filters2[i].should.equal(filters[i]);\n      }\n    });\n\n    it('should add filter and could fetch it from before and after filter later', function() {\n      var filters = [\n        function() {console.error('filter1');},\n        function() {}\n      ];\n\n      app.init({base: mockBase});\n\n      var i, l;\n      for(i=0, l=filters.length; i<l; i++) {\n        app.filter(filters[i]);\n      }\n\n      var filters2 = app.get('__befores__');\n      should.exist(filters2);\n      filters2.length.should.equal(filters.length);\n      for(i=0, l=filters2.length; i<l; i++) {\n        filters2[i].should.equal(filters[i]);\n      }\n\n      var filters3 = app.get('__afters__');\n      should.exist(filters3);\n      filters3.length.should.equal(filters.length);\n      for(i=0, l=filters3.length; i<l; i++) {\n        filters2[i].should.equal(filters[i]);\n      }\n    });\n  });\n\n   describe('#globalFilter', function() {\n    it('should add before global filter and could fetch it later', function() {\n      var filters = [\n        function() {console.error('global filter1');},\n        function() {}\n      ];\n\n      app.init({base: mockBase});\n\n      var i, l;\n      for(i=0, l=filters.length; i<l; i++) {\n        app.globalBefore(filters[i]);\n      }\n\n      var filters2 = app.get('__globalBefores__');\n      should.exist(filters2);\n      filters2.length.should.equal(filters.length);\n      for(i=0, l=filters2.length; i<l; i++) {\n        filters2[i].should.equal(filters[i]);\n      }\n    });\n\n    it('should add after global filter and could fetch it later', function() {\n      var filters = [\n        function() {console.error('filter1');},\n        function() {}\n      ];\n\n      app.init({base: mockBase});\n\n      var i, l;\n      for(i=0, l=filters.length; i<l; i++) {\n        app.globalAfter(filters[i]);\n      }\n\n      var filters2 = app.get('__globalAfters__');\n      should.exist(filters2);\n      filters2.length.should.equal(filters.length);\n      for(i=0, l=filters2.length; i<l; i++) {\n        filters2[i].should.equal(filters[i]);\n      }\n    });\n\n    it('should add filter and could fetch it from before and after filter later', function() {\n      var filters = [\n        function() {console.error('filter1');},\n        function() {}\n      ];\n\n      app.init({base: mockBase});\n\n      var i, l;\n      for(i=0, l=filters.length; i<l; i++) {\n        app.globalFilter(filters[i]);\n      }\n\n      var filters2 = app.get('__globalBefores__');\n      should.exist(filters2);\n      filters2.length.should.equal(filters.length);\n      for(i=0, l=filters2.length; i<l; i++) {\n        filters2[i].should.equal(filters[i]);\n      }\n\n      var filters3 = app.get('__globalAfters__');\n      should.exist(filters3);\n      filters3.length.should.equal(filters.length);\n      for(i=0, l=filters3.length; i<l; i++) {\n        filters2[i].should.equal(filters[i]);\n      }\n    });\n  });\n\n  describe('#configure', function() {\n    it('should execute the code block wtih the right environment', function() {\n      var proCount = 0, devCount = 0;\n      var proEnv = 'production', devEnv = 'development', serverType = 'server';\n\n      app.init({base: mockBase});\n      app.set('serverType', serverType);\n      app.set('env', proEnv);\n\n      app.configure(proEnv, serverType, function() {\n        proCount++;\n      });\n\n      app.configure(devEnv, serverType, function() {\n        devCount++;\n      });\n\n      app.set('env', devEnv);\n\n      app.configure(proEnv, serverType, function() {\n        proCount++;\n      });\n\n      app.configure(devEnv, serverType, function() {\n        devCount++;\n      });\n\n      proCount.should.equal(1);\n      devCount.should.equal(1);\n    });\n\n    it('should execute the code block wtih the right server', function() {\n      var server1Count = 0, server2Count = 0;\n      var proEnv = 'production', serverType1 = 'server1', serverType2 = 'server2';\n\n      app.init({base: mockBase});\n      app.set('serverType', serverType1);\n      app.set('env', proEnv);\n\n      app.configure(proEnv, serverType1, function() {\n        server1Count++;\n      });\n\n      app.configure(proEnv, serverType2, function() {\n        server2Count++;\n      });\n\n      app.set('serverType', serverType2);\n\n      app.configure(proEnv, serverType1, function() {\n        server1Count++;\n      });\n\n      app.configure(proEnv, serverType2, function() {\n        server2Count++;\n      });\n\n      server1Count.should.equal(1);\n      server2Count.should.equal(1);\n    });\n  });\n\n  describe('#route', function() {\n    it('should add route record and could fetch it later', function() {\n      var type1 = 'area', type2 = 'connector';\n      var func1 = function() {console.log('func1');};\n      var func2 = function() {console.log('func2');};\n\n      app.init({base: mockBase});\n\n      app.route(type1, func1);\n      app.route(type2, func2);\n\n      var routes = app.get('__routes__');\n      should.exist(routes);\n      func1.should.equal(routes[type1]);\n      func2.should.equal(routes[type2]);\n    });\n  });\n\n  describe('#transaction', function() {\n    it('should execute all conditions and handlers', function() {\n      var conditions = {\n        test1: function(cb) {\n          console.log('condition1');\n          cb();\n        },\n        test2: function(cb) {\n          console.log('condition2');\n          cb();\n        }\n      };\n      var flag = 1;\n      var handlers = {\n        do1: function(cb) {\n          console.log('handler1');\n          cb();\n        },\n        do2: function(cb) {\n          console.log('handler2');\n          if(flag < 3){\n            flag ++;\n            cb(new Error('error'));\n          } else {\n            cb();\n          }\n        }\n      };\n      app.transaction('test', conditions, handlers, 5);\n    });\n\n    it('shoud execute conditions with error and do not execute handlers', function() {\n      var conditions = {\n        test1: function(cb) {\n          console.log('condition1');\n          cb();\n        },\n        test2: function(cb) {\n          console.log('condition2');\n          cb(new Error('error'));\n        },\n        test3: function(cb) {\n          console.log('condition3');\n          cb();\n        }\n      };\n      var handlers = {\n        do1: function(cb) {\n          console.log('handler1');\n          cb();\n        },\n        do2: function(cb) {\n          console.log('handler2');\n          cb();\n        }\n      };\n      app.transaction('test', conditions, handlers);\n    });\n  });\n\n  describe('#add and remove servers', function() {\n    it('should add servers and emit event and fetch the new server info by get methods', function(done) {\n      var newServers = [\n        {id: 'connector-server-1', serverType: 'connecctor', host: '127.0.0.1', port: 1234, clientPort: 3000, frontend: true},\n        {id: 'area-server-1', serverType: 'area', host: '127.0.0.1', port: 2234}\n      ];\n      app.init({base: mockBase});\n      app.event.on(pomelo.events.ADD_SERVERS, function(servers) {\n        // check event args\n        newServers.should.eql(servers);\n\n        // check servers\n        var curServers = app.getServers();\n        should.exist(curServers);\n        var item, i, l;\n        for(i=0, l=newServers.length; i<l; i++) {\n          item = newServers[i];\n          item.should.eql(curServers[item.id]);\n        }\n\n        // check get server by id\n        for(i=0, l=newServers.length; i<l; i++) {\n          item = newServers[i];\n          item.should.eql(app.getServerById(item.id));\n        }\n\n        // check server types\n        var types = [];\n        for(i=0, l=newServers.length; i<l; i++) {\n          item = newServers[i];\n          if(types.indexOf(item.serverType) < 0) {\n            types.push(item.serverType);\n          }\n        }\n        var types2 = app.getServerTypes();\n        types.length.should.equal(types2.length);\n        for(i=0, l=types.length; i<l; i++) {\n          types2.should.include(types[i]);\n        }\n\n        // check server type list\n        var slist;\n        for(i=0, l=newServers.length; i<l; i++) {\n          item = newServers[i];\n          slist = app.getServersByType(item.serverType);\n          should.exist(slist);\n          contains(slist, item).should.be.true;\n        }\n\n        done();\n      });\n\n      app.addServers(newServers);\n    });\n\n    it('should remove server info and emit event', function(done) {\n      var newServers = [\n        {id: 'connector-server-1', serverType: 'connecctor', host: '127.0.0.1', port: 1234, clientPort: 3000, frontend: true},\n        {id: 'area-server-1', serverType: 'area', host: '127.0.0.1', port: 2234},\n        {id: 'path-server-1', serverType: 'path', host: '127.0.0.1', port: 2235}\n      ];\n      var destServers = [\n        {id: 'connector-server-1', serverType: 'connecctor', host: '127.0.0.1', port: 1234, clientPort: 3000, frontend: true},\n        {id: 'path-server-1', serverType: 'path', host: '127.0.0.1', port: 2235}\n      ];\n      var delIds = ['area-server-1'];\n      var addCount = 0;\n      var delCount = 0;\n\n      app.init({base: mockBase});\n      app.event.on(pomelo.events.ADD_SERVERS, function(servers) {\n        // check event args\n        newServers.should.eql(servers);\n        addCount++;\n      });\n\n      app.event.on(pomelo.events.REMOVE_SERVERS, function(ids) {\n        delIds.should.eql(ids);\n\n        // check servers\n        var curServers = app.getServers();\n        should.exist(curServers);\n        var item, i, l;\n        for(i=0, l=destServers.length; i<l; i++) {\n          item = destServers[i];\n          item.should.eql(curServers[item.id]);\n        }\n\n        // check get server by id\n        for(i=0, l=destServers.length; i<l; i++) {\n          item = destServers[i];\n          item.should.eql(app.getServerById(item.id));\n        }\n\n        // check server types\n        // NOTICE: server types would not clear when remove server from app\n        var types = [];\n        for(i=0, l=newServers.length; i<l; i++) {\n          item = newServers[i];\n          if(types.indexOf(item.serverType) < 0) {\n            types.push(item.serverType);\n          }\n        }\n        var types2 = app.getServerTypes();\n        types.length.should.equal(types2.length);\n        for(i=0, l=types.length; i<l; i++) {\n          types2.should.include(types[i]);\n        }\n\n        // check server type list\n        var slist;\n        for(i=0, l=destServers.length; i<l; i++) {\n          item = destServers[i];\n          slist = app.getServersByType(item.serverType);\n          should.exist(slist);\n          contains(slist, item).should.be.true;\n        }\n\n        done();\n      });\n\n      app.addServers(newServers);\n      app.removeServers(delIds);\n    });\n  });\n\n  describe('#beforeStopHook', function() {\n    it('should be called before application stopped.', function(done) {\n      var count = 0;\n      app.init({base: mockBase});\n      app.beforeStopHook(function() {\n        count++;\n      });\n      app.start(function(err) {\n        should.not.exist(err);\n      });\n\n      setTimeout(function() {\n        // wait for after start\n        app.stop(false);\n\n        setTimeout(function() {\n          // wait for stop\n          count.should.equal(1);\n          done();\n        }, WAIT_TIME);\n      }, WAIT_TIME);\n    });\n  });\n  describe('#use', function() {\n    it('should exist plugin component and event', function(done) {\n      var plugin = {\n        components: mockBase + '/mock-plugin/components/',\n        events: mockBase + '/mock-plugin/events/'\n      };\n      var opts = {};\n      app.use(plugin, opts);\n      should.exist(app.event.listeners('bind_session'));\n      should.exist(app.components.mockPlugin);\n      done();\n    });\n  });\n});\n\nvar contains = function(slist, sinfo) {\n  for(var i=0, l=slist.length; i<l; i++) {\n    if(slist[i].id === sinfo.id) {\n      return true;\n    }\n  }\n  return false;\n};\n"
  },
  {
    "path": "test/config/log4js.json",
    "content": "{\n  \"appenders\": [\n    {\n      \"type\": \"file\",\n      \"filename\": \"./test/logs/node-log-${opts:serverId}.log\",\n      \"fileSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5\n    },\n    {\n      \"type\": \"console\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"./test/logs/con-log-${opts:serverId}.log\",\n      \"pattern\": \"connector\",\n      \"maxLogSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\":\"con-log\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"./test/logs/rpc-log-${opts:serverId}.log\",\n      \"fileSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\":\"rpc-log\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"./test/logs/forward-log-${opts:serverId}.log\",\n      \"fileSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\":\"forward-log\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"./test/logs/crash.log\",\n      \"fileSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\":\"crash-log\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"./test/logs/transaction.log\",\n      \"fileSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\":\"transaction-log\"\n    },\n    {\n      \"type\": \"file\",\n      \"filename\": \"./test/logs/transaction-error.log\",\n      \"fileSize\": 1048576,\n      \"layout\": {\n        \"type\": \"basic\"\n      },\n      \"backups\": 5,\n      \"category\":\"transaction-error-log\"\n    }\n  ],\n\n  \"levels\": {\n    \"rpc-log\" : \"ERROR\",\n    \"forward-log\": \"ERROR\"\n  },\n\n  \"replaceConsole\": true\n}\n"
  },
  {
    "path": "test/config/master.json",
    "content": "{\n  \"development\": {\n    \"id\": \"master-server-1\", \"host\": \"127.0.0.1\", \"port\": 3005\n  },\n\n  \"production\": {\n    \"id\": \"master-server-1\", \"host\": \"172.17.2.237\", \"port\": 3005\n  }\n}\n"
  },
  {
    "path": "test/config/servers.json",
    "content": "{\n  \"development\": {\n  },\n  \"production\": {\n  }\n}\n"
  },
  {
    "path": "test/filters/handler/serial.js",
    "content": "var should = require('should');\nvar serialFilter = require('../../../lib/filters/handler/serial');\nvar FilterService = require('../../../lib/common/service/filterService');\nvar util = require('util');\n\nvar mockSession = {\n  key : \"123\"\n};\n\nvar WAIT_TIME = 100;\ndescribe(\"#serialFilter\",function(){\n  it(\"should do before filter ok\",function(done){\n    var service = new FilterService();\n    var filter = serialFilter();\n    service.before(filter);\n\n    service.beforeFilter(null,mockSession,function(){\n      should.exist(mockSession);\n\n      should.exist(mockSession.__serialTask__);\n      done();\n    });\n  });\n\n  it(\"should do after filter by doing before filter ok\",function(done){\n    var service = new FilterService();\n    var filter = serialFilter();\n    var _session ;\n    service.before(filter);\n\n    service.beforeFilter(null,mockSession,function(){\n      should.exist(mockSession);\n      should.exist(mockSession.__serialTask__);\n      _session = mockSession;\n    });\n\n    service.after(filter);\n\n    service.afterFilter(null,null,mockSession,null,function(){\n      should.exist(mockSession);\n      should.strictEqual(mockSession,_session);\n    });\n\n    setTimeout(done,WAIT_TIME);\n  });\n});"
  },
  {
    "path": "test/filters/handler/time.js",
    "content": "var should = require('should');\nvar serialFilter = require('../../../lib/filters/handler/time');\nvar FilterService = require('../../../lib/common/service/filterService');\nvar util = require('util');\nvar mockSession = {\n  key : \"123\"\n};\n\nvar WAIT_TIME = 100;\ndescribe(\"#serialFilter\",function(){\n  it(\"should do before filter ok\",function(done){\n    var service = new FilterService();\n    var filter = serialFilter();\n    service.before(filter);\n\n\n    service.beforeFilter(null,mockSession,function(){\n      should.exist(mockSession);\n\n      should.exist(mockSession.__startTime__);\n      done();\n    });\n  });\n\n  it(\"should do after filter by doing before filter ok\",function(done){\n    var service = new FilterService();\n    var filter = serialFilter();\n    var _session ;\n    service.before(filter);\n\n    service.beforeFilter(null,mockSession,function(){\n      should.exist(mockSession);\n      should.exist(mockSession.__startTime__);\n      _session = mockSession;\n    });\n\n    service.after(filter);\n\n    service.afterFilter(null,{route:\"hello\"},mockSession,null,function(){\n      should.exist(mockSession);\n      should.strictEqual(mockSession,_session);\n    });\n\n    setTimeout(done,WAIT_TIME);\n    done();\n  });\n});"
  },
  {
    "path": "test/filters/handler/timeout.js",
    "content": "var should = require('should');\nvar timeoutFilter = require('../../../lib/filters/handler/timeout');\nvar FilterService = require('../../../lib/common/service/filterService');\nvar util = require('util');\nvar mockSession = {\n  key : \"123\"\n};\n\nvar WAIT_TIME = 100;\ndescribe(\"#serialFilter\",function(){\n  it(\"should do before filter ok\",function(done){\n    var service = new FilterService();\n    var filter = timeoutFilter();\n    service.before(filter);\n\n    service.beforeFilter(null,mockSession,function(){\n      should.exist(mockSession);\n\n      should.exist(mockSession.__timeout__);\n      done();\n    });\n  });\n\n  it(\"should do after filter by doing before filter ok\",function(done){\n    var service = new FilterService();\n    var filter = timeoutFilter();\n    var _session ;\n    service.before(filter);\n\n    service.beforeFilter(null,mockSession,function(){\n      should.exist(mockSession);\n      should.exist(mockSession.__timeout__);\n      _session = mockSession;\n    });\n\n    service.after(filter);\n\n    service.afterFilter(null,null,mockSession,null,function(){\n      should.exist(mockSession);\n      should.strictEqual(mockSession,_session);\n    });\n\n    setTimeout(done,WAIT_TIME);\n    done();\n  });\n});"
  },
  {
    "path": "test/filters/handler/toobusy.js",
    "content": "var should = require('should');\nvar toobusyFilter = require('../../../lib/filters/handler/toobusy');\nvar FilterService = require('../../../lib/common/service/filterService');\nvar util = require('util');\nvar mockSession = {\n  key : \"123\"\n};\n\ndescribe(\"#toobusyFilter\",function(){\n  it(\"should do before filter ok\",function(done){\n    var service = new FilterService();\n    var filter = toobusyFilter();\n    service.before(filter);\n\n    service.beforeFilter(null,mockSession,function(err){\n      should.not.exist(err);\n      should.exist(mockSession);\n      done();\n    });\n  });\n\n  it(\"should do before filter error because of too busy\",function(done){\n    var service = new FilterService();\n    var filter = toobusyFilter();\n    service.before(filter);\n\n    var exit = false;\n    function load() {\n      service.beforeFilter(null,mockSession,function(err, resp){\n        should.exist(mockSession);\n        console.log('err: ' + err);\n        if (!!err) {\n          exit = true;\n        }\n      });\n\n      console.log('exit: ' + exit);\n      if (exit) {\n        return done();\n      }\n      var start = new Date();\n      while ((new Date() - start) < 250) {\n        for (var i = 0; i < 1e5;) i++;\n      }\n      setTimeout(load, 0);\n    }\n    load();\n\n  });\n});\n"
  },
  {
    "path": "test/filters/rpc/rpcLog.js",
    "content": "var should = require('should');\nvar rpcLogFilter = require('../../../lib/filters/rpc/rpcLog');\n\nvar mockData = {\n  serverId : \"connector-server-1\",\n  msg : \"hello\",\n  opts : {}\n};\n\ndescribe('#rpcLogFilter',function(){\n  it(\"should do after filter by before filter\",function(done){\n    rpcLogFilter.before(mockData.serverId,mockData.msg,mockData.opts,function(serverId,msg,opts){\n      rpcLogFilter.after(serverId,msg,opts,function(serverId,msg,opts){\n        should.exist(opts.__start_time__);\n        done();\n      });\n    });\n  });\n});"
  },
  {
    "path": "test/filters/rpc/toobusy.js",
    "content": "var should = require('should');\nvar toobusyFilter = require('../../../lib/filters/rpc/toobusy');\n\nvar mockData = {\n  serverId : \"connector-server-1\",\n  msg : \"hello\",\n  opts : {}\n};\n\n\ndescribe('#toobusyFilter',function(){\n  it(\"should no callback for toobusy\",function(done){\n    function load() {\n      var callbackInvoked = false;\n      toobusyFilter.before(mockData.serverId,mockData.msg,mockData.opts,function(serverId,msg,opts){\n        callbackInvoked = true;\n      });\n\n      if (!callbackInvoked) {\n        console.log(' logic of toobusy enterd, done!');\n        return done();\n      }\n      var start = new Date();\n      while ((new Date() - start) < 250) {\n        for (var i = 0; i < 1e5;) i++;\n      }\n      setTimeout(load, 0);\n    }\n    load();\n  });\n});\n"
  },
  {
    "path": "test/logs/tmp",
    "content": ""
  },
  {
    "path": "test/manager/mockChannelManager.js",
    "content": "var DEFAULT_PREFIX = 'POMELO:CHANNEL';\nvar utils = require('../../lib/util/utils');\n\nvar MockManager = function(app, opts) {\n  this.app = app;\n  this.opts = opts || {};\n  this.prefix = opts.prefix || DEFAULT_PREFIX;\n};\n\nmodule.exports = MockManager;\n\nMockManager.prototype.start = function(cb) {\n  this.usersMap = {};\n  utils.invokeCallback(cb);\n};\n\nMockManager.prototype.stop = function(force, cb) {\n  this.usersMap = null;\n  utils.invokeCallback(cb);\n};\n\nMockManager.prototype.add = function(name, uid, sid, cb) {\n  var key = genKey(this, name, sid);\n  if(!this.usersMap[key]) {\n    this.usersMap[key] = [];\n  }\n  this.usersMap[key].push(uid);\n  utils.invokeCallback(cb);\n};\n\nMockManager.prototype.leave = function(name, uid, sid, cb) {\n  var key = genKey(this, name, sid);\n  var res = deleteFrom(uid, this.usersMap[key]);\n  if(this.usersMap[key] && this.usersMap[key].length === 0) {\n    delete this.usersMap[sid];\n  }\n  utils.invokeCallback(cb);\n};\n\nMockManager.prototype.getMembersBySid = function(name, sid, cb) {\n    var key = genKey(this, name, sid);\n    if(!this.usersMap[key])\n      this.usersMap[key] = [];\n    utils.invokeCallback(cb, null, this.usersMap[key]);\n};\n\nMockManager.prototype.destroyChannel = function(name, cb) {\n  var servers = this.app.getServers();\n  var server, removes = [];\n  for(var sid in servers) {\n    server = servers[sid];\n    if(this.app.isFrontend(server)) {\n      removes.push(genKey(this, name, sid));\n    }\n  }\n\n  if(removes.length === 0) {\n    utils.invokeCallback(cb);\n    return;\n  }\n\n  for(var i = 0; i<removes.length; i++) {\n    delete this.usersMap[removes[i]];\n  }\n  utils.invokeCallback(cb);\n};\n\nvar genKey = function(self, name, sid) {\n  return self.prefix + ':' + name + ':' + sid;\n};\n\nvar deleteFrom = function(uid, group) {\n  if(!group) {\n    return true;\n  }\n\n  for(var i=0, l=group.length; i<l; i++) {\n    if(group[i] === uid) {\n      group.splice(i, 1);\n      return true;\n    }\n  }\n  return false;\n};"
  },
  {
    "path": "test/manager/taskManager.js",
    "content": "var should = require('should');\nvar taskManager = require('../../lib/common/manager/taskManager');\n\n// set timeout for test\ntaskManager.timeout = 100;\n\nvar WAIT_TIME = 200;\n\ndescribe(\"#taskManager\",function(){\n  it(\"should add task and execute it\",function(done){\n    var key = 'key-1';\n    var fn = function(task) {\n      taskCount++;\n      task.done();\n    };\n    var onTimeout = function() {\n      should.fail('should not timeout.');\n    };\n    var taskCount = 0;\n\n    taskManager.addTask(key, fn, onTimeout);\n\n    setTimeout(function() {\n      taskCount.should.equal(1);\n      done();\n    }, WAIT_TIME);\n  });\n\n  it(\"should fire timeout callback if task timeout\",function(done){\n    var key = 'key-1';\n    var fn = function(task) {\n      taskCount++;\n    };\n    var onTimeout = function() {\n      timeoutCount++;\n    };\n    var taskCount = 0;\n    var timeoutCount = 0;\n\n    taskManager.addTask(key, fn, onTimeout);\n\n    setTimeout(function() {\n      taskCount.should.equal(1);\n      timeoutCount.should.equal(1);\n      done();\n    }, WAIT_TIME);\n  });\n\n  it(\"should not fire timeout after close the task\",function(done){\n    var key = 'key-1';\n    var fn = function(task) {\n      taskCount++;\n    };\n    var onTimeout = function() {\n      timeoutCount++;\n    };\n    var taskCount = 0;\n    var timeoutCount = 0;\n\n    taskManager.addTask(key, fn, onTimeout);\n\n    process.nextTick(function() {\n      taskManager.closeQueue(key, true);\n\n      setTimeout(function() {\n        taskCount.should.equal(1);\n        timeoutCount.should.equal(0);\n        done();\n      }, WAIT_TIME);\n    });\n  });\n\n  it(\"should be ok to remove a queue not exist\",function(){\n    var key = 'key-n';\n    taskManager.closeQueue(key, true);\n  });\n});\n"
  },
  {
    "path": "test/mock-base/.gitignore",
    "content": ""
  },
  {
    "path": "test/mock-base/app/.gitignore",
    "content": ""
  },
  {
    "path": "test/mock-base/app/servers/.file-start-with-dot",
    "content": ""
  },
  {
    "path": "test/mock-base/app/servers/.folder-start-with-dot/.gitignore",
    "content": ""
  },
  {
    "path": "test/mock-base/app/servers/.gitignore",
    "content": ""
  },
  {
    "path": "test/mock-base/app/servers/area/.gitignore",
    "content": ""
  },
  {
    "path": "test/mock-base/app/servers/connector/.gitignore",
    "content": ""
  },
  {
    "path": "test/mock-base/app/servers/connector/handler/.gitignore",
    "content": ""
  },
  {
    "path": "test/mock-base/app/servers/connector/remote/.gitignore",
    "content": ""
  },
  {
    "path": "test/mock-base/app/servers/other-file",
    "content": ""
  },
  {
    "path": "test/mock-plugin/components/mockPlugin.js",
    "content": "module.exports = function(app, opts) {\n  var service = {name: 'mockPlugin'};\n  return service;\n};"
  },
  {
    "path": "test/mock-plugin/events/mockEvent.js",
    "content": "var Event = function(app) {\n\tthis.app = app;\n};\n\nmodule.exports = Event;\n\nEvent.prototype.bind_session = function(session) {\n};"
  },
  {
    "path": "test/modules/console.js",
    "content": "var should = require('should');\nvar pomelo = require('../../');\nvar consoleModule = require('../../lib/modules/console');\n\ndescribe('console module test', function() {\n\tdescribe('#monitorHandler', function() {\n\t\tit('should execute the corresponding command with different signals', function() {\n\t\t\tvar flag;\n\t\t\tvar rs;\n\t\t\tvar opts = {\n\t\t\t\tapp: {\n\t\t\t\t\tcomponents: {\n\t\t\t\t\t\t__connector__: {\n\t\t\t\t\t\t\tblacklist: []\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tstop: function(value) {\n\t\t\t\t\t\tflag = value;\n\t\t\t\t\t},\n\t\t\t\t\taddCrons: function(array) {\n\t\t\t\t\t\trs = array;\n\t\t\t\t\t},\n\t\t\t\t\tremoveCrons: function(array) {\n\t\t\t\t\t\trs = array;\n\t\t\t\t\t},\n\t\t\t\t\tisFrontend: function() {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\tvar module = new consoleModule(opts);\n\t\t\tvar agent1 = {\n\t\t\t\ttype: 'area'\n\t\t\t};\n\t\t\tvar msg1 = {signal: 'stop'};\n\t\t\tmodule.monitorHandler(agent1, msg1);\n\t\t\tflag.should.eql(true);\n\n\t\t\tvar msg2 = {signal: 'list'};\n\t\t\tvar agent2 = {\n\t\t\t\ttype: 'chat',\n\t\t\t\tid: 'chat-server-1'\n\t\t\t};\n\t\t\tmodule.monitorHandler(agent2, msg2, function(obj) {\n\t\t\t\tobj.serverId.should.eql('chat-server-1');\n\t\t\t\tobj.body.serverType.should.eql('chat');\n\t\t\t});\n\n\t\t\tvar msg3 = {signal: 'addCron'};\n\t\t\tmodule.monitorHandler(agent2, msg3, null);\n\t\t \trs.length.should.eql(1);\n\n\t\t \tvar msg4 = {signal: 'removeCron'};\n\t\t \tmodule.monitorHandler(agent2, msg4, null);\n\t\t \trs.length.should.eql(1);\n\n\t\t \tvar msg5 = {signal: 'blacklist', blacklist: ['127.0.0.1']};\n\t\t \tmodule.monitorHandler(agent1, msg5, null);\n\t\t \topts.app.components.__connector__.blacklist.length.should.eql(1);\n\n\n\t\t});\n\t});\n\n\tdescribe('#clientHandler', function() {\n    var _exit;\n    var _setTimeout;\n    var exitCount = 0;\n\n    before(function(done) {\n      _exit = process.exit;\n      _setTimeout = setTimeout;\n      done();\n    });\n\n    after(function(done) {\n      process.exit = _exit;\n      setTimeout = _setTimeout;\n      done();\n    });\n\n\t\tvar opts = {\n\t\t\t\tapp: {\n\t\t\t\t\tclusterSeq: {},\n\t\t\t\t\tstop: function(value) {\n\t\t\t\t\t\treturn value;\n\t\t\t\t\t},\n\t\t\t\t\tgetServerById: function() {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\thost: '127.0.0.1'\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t\tgetServers: function() {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t'chat-server-1': {\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tget: function(value) {\n\t\t\t\t\t\tswitch(value) {\n\t\t\t\t\t\t\tcase 'main':\n\t\t\t\t\t\t\t  return __dirname + '/../../index.js';\n\t\t\t\t\t\t\tcase 'env':\n\t\t\t\t\t\t\t  return 'dev';\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tset: function(value) {\n\t\t\t\t\t\treturn value;\n\t\t\t\t\t},\n\t\t\t\t\tgetServersByType: function() {\n\t\t\t\t\t\treturn [{id: 'chat-server-1'}];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\tvar module = new consoleModule(opts);\n\t\tit('should execute kill command', function(done) {\n\t\t\tvar msg = {signal: 'kill'};\n      process.exit = function() {exitCount++;};\n      setTimeout = function(cb, timeout) {\n        if (timeout > 3000) {\n          timeout = 3000;\n        }\n        _setTimeout(cb, timeout);\n      };\n\n      var agent1 = {\n\t\t\t\trequest: function(recordId, moduleId, msg, cb) {\n\t\t\t\t\tcb('chat-server-1');\n        },\n\t\t\t\tidMap: {\n\t\t\t\t\t'chat-server-1': {\n\t\t\t\t\t\ttype: 'chat',\n\t\t\t\t\t\tid: 'chat-server-1'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\tmodule.clientHandler(agent1, msg, function(err, result) {\n        should.not.exist(err);\n        should.exist(result.code);\n\t\t\t});\n\n      var agent2 = {\n\t\t\t\trequest: function(recordId, moduleId, msg, cb) {\n\t\t\t\t\tcb(null);\n        },\n\t\t\t\tidMap: {\n\t\t\t\t\t'chat-server-1': {\n\t\t\t\t\t\ttype: 'chat',\n\t\t\t\t\t\tid: 'chat-server-1'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\tmodule.clientHandler(agent2, msg, function(err, result) {\n        should.not.exist(err);\n        should.exist(result.code);\n        result.code.should.eql('remained');\n        done();\n\t\t\t});\n\t\t});\n\n\t\tit('should execute stop command', function(done) {\n\t\t\tvar msg1 = {signal: 'stop', ids: ['chat-server-1']};\n\t\t\tvar msg2 = {signal: 'stop', ids:[]};\n\t\t\tvar agent = {\n\t\t\t\tnotifyById: function(serverId, moduleId, msg) {\n\n\t\t\t\t},\n\t\t\t\tnotifyAll: function(moduleId, msg) {\n\n\t\t\t\t}\n\t\t\t};\n\t\t\tmodule.clientHandler(agent, msg1, function(err, result) {\n\t\t\t\tresult.status.should.eql('part');\n\t\t\t});\n\n\t\t\tmodule.clientHandler(agent, msg2, function(err, result) {\n\t\t\t\tresult.status.should.eql('all');\n\t\t\t\tdone();\n\t\t\t});\n\t\t});\n\n\t\tit('should execute list command', function() {\n\t\t\tvar msg = {signal: 'list'};\n\t\t\tvar agent = {\n\t\t\t\trequest: function(recordId, moduleId, msg, cb) {\n\t\t\t\t\tcb({serverId: 'chat-server-1', body: {'server':{}}});\n\t\t\t\t},\n\t\t\t\tidMap: {\n\t\t\t\t\t'chat-server-1': {\n\t\t\t\t\t\ttype: 'chat',\n\t\t\t\t\t\tid: 'chat-server-1'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\tmodule.clientHandler(agent, msg, function(err, result) {\n\t\t\t\tshould.exist(result.msg);\n\t\t\t});\n\t\t});\n\n\t\tit('should execute add command', function() {\n\t\t\tvar msg1 = {signal: 'add', args: ['host=127.0.0.1', 'port=88888', 'clusterCount=2']};\n\t\t\tvar msg2 = {signal: 'add', args: ['host=127.0.0.1', 'port=88888', 'id=chat-server-1', 'serverType=chat']};\n\t\t\tvar agent = {};\n\t\t\tmodule.clientHandler(agent, msg1, function(err, result) {\n\t\t\t\tshould.not.exist(err);\n\t\t\t\tresult.length.should.eql(0);\n\t\t\t});\n\t\t\tmodule.clientHandler(agent, msg2, function(err, result) {\n\t\t\t\tresult.status.should.eql('ok');\n\t\t\t});\n\t\t});\n\n\t\tit('should execute blacklist command', function() {\n\t\t\tvar msg1 = {signal: 'blacklist', args: ['127.0.0.1']};\n\t\t\tvar msg2 = {signal: 'blacklist', args: ['abc']};\n\t\t\tvar agent = {\n\t\t\t\tnotifyAll: function(moduleId, msg) {\n\n\t\t\t\t}\n\t\t\t};\n\t\t\tmodule.clientHandler(agent, msg1, function(err, result) {\n\t\t\t\tresult.status.should.eql('ok');\n\t\t\t});\n\t\t\tmodule.clientHandler(agent, msg2, function(err, result) {\n\t\t\t\tshould.exist(err);\n\t\t\t});\n\t\t});\n\n\t\tit('should execute restart command', function() {\n\t\t\tvar msg1 = {signal: 'restart', ids:['chat-server-1']};\n\t\t\tvar msg2 = {signal: 'restart', type:'chat', ids:[]};\n\t\t\tvar agent = {\n\t\t\t\trequest: function(recordId, moduleId, msg, cb) {\n\t\t\t\t\tcb(null);\n\t\t\t\t}\n\t\t\t};\n\t\t\tmodule.clientHandler(agent, msg1, function(err, result) {\n\t\t\t\tshould.exist(err);\n\t\t\t});\n\t\t\tmodule.clientHandler(agent, msg2, function(err, result) {\n\t\t\t\tshould.exist(err);\n\t\t\t});\n\n\t\t});\n\n\t});\n});\n"
  },
  {
    "path": "test/pomelo.js",
    "content": "var pomelo = require('../');\nvar should = require('should');\nvar mockBase = process.cwd() + '/test';\n\ndescribe('pomelo', function() {\n  describe('#createApp', function() {\n    it('should create and get app, be the same instance', function(done) {\n      var app = pomelo.createApp({base: mockBase});\n      should.exist(app);\n\n      var app2 = pomelo.app;\n      should.exist(app2);\n      should.strictEqual(app, app2);\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/remote/channelRemote.js",
    "content": "var should = require('should');\nvar pomelo = require('../../');\nvar remote = require('../../lib/common/remote/frontend/channelRemote');\nvar SessionService = require('../../lib/common/service/sessionService');\nvar ChannelService = require('../../lib/common/service/channelService');\nvar countDownLatch = require('../../lib/util/countDownLatch');\nvar MockChannelManager = require('../manager/mockChannelManager');\n\n\nvar mockBase = process.cwd() + '/test';\n\nvar WAIT_TIME = 200;\n\ndescribe('channel remote test', function() {\n  describe('#pushMessage', function() {\n    it('should push message the the specified clients', function(done) {\n      var sids = [1, 2, 3, 4, 5, 6];\n      var uids = [11, 12, 13];\n      var frontendId = 'frontend-server-id';\n      var mockRoute = 'mock-route-string';\n      var mockMsg = {msg: 'some test msg'};\n      var invokeCount = 0;\n      var invokeUids = [];\n\n      var sessionService = new SessionService();\n      sessionService.sendMessageByUid = function(uid, msg) {\n        mockMsg.should.eql(msg);\n        invokeCount++;\n        invokeUids.push(uid);\n      };\n\n      var session;\n      for(var i=0, l=sids.length, j=0; i<l; i++) {\n        session = sessionService.create(sids[i], frontendId);\n        if(i % 2) {\n          sessionService.bind(session.id, uids[j]);\n          j++;\n        }\n      }\n\n      var app = pomelo.createApp({base: mockBase});\n      app.components.__connector__ = {\n        send: function(reqId, route, msg, recvs, opts, cb) {\n          app.components.__pushScheduler__.schedule(reqId, route, msg, recvs, opts, cb);\n        }\n      };\n      app.components.__connector__.connector = {};\n      app.components.__pushScheduler__ = {\n        schedule: function(reqId, route, msg, recvs, opts, cb) {\n          mockMsg.should.eql(msg);\n          invokeCount += recvs.length;\n          var sess;\n          for(var i=0; i<recvs.length; i++) {\n            sess = sessionService.get(recvs[i]);\n            if(sess) {\n              invokeUids.push(sess.uid);\n            }\n          }\n          cb();\n        }\n      };\n      app.set('sessionService', sessionService);\n      var channelRemote = remote(app);\n      channelRemote.pushMessage(mockRoute, mockMsg, uids, {isPush: true}, function() {\n        invokeCount.should.equal(uids.length);\n        invokeUids.length.should.equal(uids.length);\n        for(var i=0, l=uids.length; i<l; i++) {\n          invokeUids.should.include(uids[i]);\n        }\n        done();\n      });\n    });\n  });\n\n  describe('#broadcast', function() {\n    it('should broadcast to all the client connected', function(done) {\n      var sids = [1, 2, 3, 4, 5];\n      var uids = [11, 12, 13, 14, 15];\n      var frontendId = 'frontend-server-id';\n      var mockRoute = 'mock-route-string';\n      var mockMsg = {msg: 'some test msg'};\n      var invokeCount = 0;\n\n      var sessionService = new SessionService();\n      var channelService = new ChannelService();\n\n      var session;\n      for(var i=0, l=sids.length; i<l; i++) {\n        session = sessionService.create(sids[i], frontendId);\n        if(i % 2) {\n          session.bind(uids[i]);\n        }\n      }\n\n      var app = pomelo.createApp({base: mockBase});\n      app.components.__connector__ = {\n        send: function(reqId, route, msg, recvs, opts, cb) {\n          app.components.__pushScheduler__.schedule(reqId, route, msg, recvs, opts, cb);\n        }\n      };\n      app.components.__connector__.connector = {};\n      app.components.__pushScheduler__ = {\n        schedule: function(reqId, route, msg, recvs, opts, cb) {\n          invokeCount++;\n          mockMsg.should.eql(msg);\n          should.exist(opts);\n          should.equal(opts.type, 'broadcast');\n          cb();\n        }\n      };\n      app.set('sessionService', sessionService);\n      app.set('channelService', channelService);\n      var channelRemote = remote(app);\n      channelRemote.broadcast(mockRoute, mockMsg, {type: 'broadcast'}, function() {\n        invokeCount.should.equal(1);\n        done();\n      });\n    });\n\n    it('should broadcast to all the binded client connected', function(done) {\n      var sids = [1, 2, 3, 4, 5, 6];\n      var uids = [11, 12, 13];\n      var frontendId = 'frontend-server-id';\n      var mockRoute = 'mock-route-string';\n      var mockMsg = {msg: 'some test msg'};\n      var invokeCount = 0;\n      var invokeUids = [];\n\n      var sessionService = new SessionService();\n      var channelService = new ChannelService();\n\n      var session;\n      for(var i=0, l=sids.length, j=0; i<l; i++) {\n        session = sessionService.create(sids[i], frontendId);\n        if(i % 2) {\n          session.bind(uids[j]);\n          j++;\n        }\n      }\n\n      var app = pomelo.createApp({base: mockBase});\n      app.components.__connector__ = {\n        send: function(reqId, route, msg, recvs, opts, cb) {\n          app.components.__pushScheduler__.schedule(reqId, route, msg, recvs, opts, cb);\n        }\n      };\n      app.components.__connector__.connector = {};\n      app.components.__pushScheduler__ = {\n        schedule: function(reqId, route, msg, recvs, opts, cb) {\n          invokeCount++;\n          mockMsg.should.eql(msg);\n          should.exist(opts);\n          should.equal(opts.type, 'broadcast');\n          true.should.equal(opts.userOptions.binded);\n          cb();\n        }\n      };\n      app.set('sessionService', sessionService);\n      app.set('channelService', channelService);\n      var channelRemote = remote(app);\n      channelRemote.broadcast(mockRoute, mockMsg, {type: 'broadcast', userOptions: {binded: true}}, function() {\n        invokeCount.should.equal(1);\n        done();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/service/channel.js",
    "content": "var should = require('should');\nvar pomelo = require('../../');\nvar ChannelService = require('../../lib/common/service/channelService');\n\nvar mockBase = process.cwd() + '/test';\nvar channelName = 'test_channel';\nvar mockApp = {serverId: 'test-server-1'};\n\ndescribe('channel test', function() {\n  describe('#add', function() {\n    it('should add a member into channel and could fetch it later', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n      should.exist(channel);\n\n      var uid = 'uid1', sid = 'sid1';\n      channel.add(uid, sid).should.be.true;\n\n      var member = channel.getMember(uid);\n      should.exist(member);\n      uid.should.equal(member.uid);\n      sid.should.equal(member.sid);\n    });\n\n    it('should fail if the sid not specified', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n      should.exist(channel);\n\n      var uid = 'uid1';\n      channel.add(uid, null).should.be.false;\n    });\n\n    it('should fail after the channel has been destroied', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n      should.exist(channel);\n\n      channel.destroy();\n\n      var uid = 'uid1', sid = 'sid1';\n      channel.add(uid, sid).should.be.false;\n    });\n  });\n\n  describe('#leave', function() {\n    it('should remove the member from channel when leave', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n      should.exist(channel);\n\n      var uid = 'uid1', sid = 'sid1';\n      channel.add(uid, sid).should.be.true;\n\n      var member = channel.getMember(uid);\n      should.exist(member);\n\n      channel.leave(uid, sid);\n      member = channel.getMember(uid);\n      should.not.exist(member);\n    });\n\n    it('should fail if uid or sid not specified', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n      should.exist(channel);\n\n      var uid = 'uid1', sid = 'sid1';\n      channel.add(uid, sid).should.be.true;\n\n      channel.leave(uid, null).should.be.false;\n      channel.leave(null, sid).should.be.false;\n    });\n  });\n\n  describe('#getMembers', function() {\n    it('should return all the members of channel', function() {\n      var uinfos = [\n        {uid: 'uid1', sid: 'sid1'},\n        {uid: 'uid2', sid: 'sid2'},\n        {uid: 'uid3', sid: 'sid3'}\n      ];\n\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n\n      var i, l, item;\n      for(i=0, l=uinfos.length; i<l; i++) {\n        item = uinfos[i];\n        channel.add(item.uid, item.sid);\n      }\n\n      var members = channel.getMembers();\n      should.exist(members);\n      members.length.should.equal(uinfos.length);\n      for(i=0, l=uinfos.length; i<l; i++) {\n        item = uinfos[i];\n        members.should.include(item.uid);\n      }\n    });\n  });\n\n  describe('#pushMessage', function() {\n    it('should push message to the right frontend server by sid', function(done) {\n      var sid1 = 'sid1', sid2 = 'sid2';\n      var uid1 = 'uid1', uid2 = 'uid2', uid3 = 'uid3';\n      var mockUids = [{sid: sid1, uid: uid1}, {sid: sid2, uid: uid2}, {sid: sid2, uid: uid3}];\n      var mockMsg = {key: 'some remote message'};\n      var uidMap = {};\n      for(var i in mockUids) {\n        uidMap[mockUids[i].uid] = mockUids[i];\n      }\n\n      var invokeCount = 0;\n\n      var mockRpcInvoke = function(sid, rmsg, cb) {\n        invokeCount++;\n        var args = rmsg.args;\n        var route = args[0];\n        var msg = args[1];\n        var uids = args[2];\n        mockMsg.should.eql(msg);\n\n        for(var j=0, l=uids.length; j<l; j++) {\n          var uid = uids[j];\n          var r2 = uidMap[uid];\n          r2.sid.should.equal(sid);\n        }\n\n        cb();\n      };\n\n      var app = pomelo.createApp({base: mockBase});\n      app.rpcInvoke = mockRpcInvoke;\n      var channelService = new ChannelService(app);\n\n      var channel = channelService.createChannel(channelName);\n      for(var i=0, l=mockUids.length; i<l; i++) {\n        channel.add(mockUids[i].uid, mockUids[i].sid);\n      }\n\n      channel.pushMessage(mockMsg, function() {\n        invokeCount.should.equal(2);\n        done();\n      });\n    });\n    it('should fail if channel has destroied', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n      should.exist(channel);\n\n      channel.destroy();\n\n      channel.pushMessage({}, function(err) {\n        should.exist(err);\n        err.message.should.equal('channel is not running now');\n      });\n    });\n  });\n});"
  },
  {
    "path": "test/service/channelService.js",
    "content": "var should = require('should');\nvar pomelo = require('../../');\nvar ChannelService = require('../../lib/common/service/channelService');\n\nvar channelName = 'test_channel';\nvar mockBase = process.cwd() + '/test';\nvar mockApp = {serverId: 'test-server-1'};\n\ndescribe('channel manager test', function() {\n  describe('#createChannel', function() {\n    it('should create and return a channel with the specified name', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n      should.exist(channel);\n      channelName.should.equal(channel.name);\n    });\n\n    it('should return the same channel if the name has already existed', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n      should.exist(channel);\n      channelName.should.equal(channel.name);\n      var channel2 = channelService.createChannel(channelName);\n      channel.should.equal(channel2);\n    });\n  });\n\n  describe('#destroyChannel', function() {\n    it('should delete the channel instance', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.createChannel(channelName);\n      should.exist(channel);\n      channelName.should.equal(channel.name);\n      channelService.destroyChannel(channelName);\n      var channel2 = channelService.createChannel(channelName);\n      channel.should.not.equal(channel2);\n    });\n  });\n\n  describe('#getChannel', function() {\n    it('should return the channel with the specified name if it exists', function() {\n      var channelService = new ChannelService(mockApp);\n      channelService.createChannel(channelName);\n      var channel = channelService.getChannel(channelName);\n      should.exist(channel);\n      channelName.should.equal(channel.name);\n    });\n\n    it('should return undefined if the channel dose not exist', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.getChannel(channelName);\n      should.not.exist(channel);\n    });\n\n    it('should create and return a new channel if create parameter is set', function() {\n      var channelService = new ChannelService(mockApp);\n      var channel = channelService.getChannel(channelName, true);\n      should.exist(channel);\n      channelName.should.equal(channel.name);\n    });\n  });\n\n  describe('#pushMessageByUids', function() {\n    it('should push message to the right frontend server', function(done) {\n      var sid1 = 'sid1', sid2 = 'sid2';\n      var uid1 = 'uid1', uid2 = 'uid2', uid3 = 'uid3';\n      var orgRoute = 'test.route.string';\n      var mockUids = [\n        {sid: sid1, uid: uid1},\n        {sid: sid2, uid: uid2},\n        {sid: sid2, uid: uid3}\n      ];\n      var mockMsg = {key: 'some remote message'};\n      var uidMap = {};\n      for(var i in mockUids) {\n        uidMap[mockUids[i].uid] = mockUids[i];\n      }\n\n      var invokeCount = 0;\n\n      var mockRpcInvoke = function(sid, rmsg, cb) {\n        invokeCount++;\n        var args = rmsg.args;\n        var route = args[0];\n        var msg = args[1];\n        var uids = args[2];\n        mockMsg.should.eql(msg);\n\n        for(var j=0, l=uids.length; j<l; j++) {\n          var uid = uids[j];\n          var r2 = uidMap[uid];\n          r2.sid.should.equal(sid);\n        }\n\n        cb();\n      };\n\n      var app = pomelo.createApp({base: mockBase});\n      app.rpcInvoke = mockRpcInvoke;\n      var channelService = new ChannelService(app);\n\n      channelService.pushMessageByUids(orgRoute, mockMsg, mockUids, function() {\n        invokeCount.should.equal(2);\n        done();\n      });\n    });\n\n    it('should return an err if uids is empty', function(done) {\n      var mockMsg = {key: 'some remote message'};\n      var app = pomelo.createApp({base: mockBase});\n      var channelService = new ChannelService(app);\n\n      channelService.pushMessageByUids(mockMsg, null, function(err) {\n        should.exist(err);\n        err.message.should.equal('uids should not be empty');\n        done();\n      });\n    });\n\n    it('should return err if all message fail to push', function(done) {\n      var sid1 = 'sid1', sid2 = 'sid2';\n      var uid1 = 'uid1', uid2 = 'uid2', uid3 = 'uid3';\n      var mockUids = [\n        {sid: sid1, uid: uid1},\n        {sid: sid2, uid: uid2},\n        {sid: sid2, uid: uid3}\n      ];\n      var mockMsg = {key: 'some remote message'};\n      var uidMap = {};\n      for(var i in mockUids) {\n        uidMap[mockUids[i].uid] = mockUids[i];\n      }\n\n      var invokeCount = 0;\n\n      var mockRpcInvoke = function(sid, rmsg, cb) {\n        invokeCount++;\n        cb(new Error('[TestMockError] mock rpc error'));\n      };\n\n      var app = pomelo.createApp({base: mockBase});\n      app.rpcInvoke = mockRpcInvoke;\n      var channelService = new ChannelService(app);\n\n      channelService.pushMessageByUids(mockMsg, mockUids, function(err) {\n        invokeCount.should.equal(2);\n        should.exist(err);\n        err.message.should.equal('all uids push message fail');\n        done();\n      });\n    });\n\n    it('should return fail uid list if fail to push messge to some of the uids', function(done) {\n      var sid1 = 'sid1', sid2 = 'sid2';\n      var uid1 = 'uid1', uid2 = 'uid2', uid3 = 'uid3';\n      var mockUids = [{sid: sid1, uid: uid1}, {sid: sid2, uid: uid2}, {sid: sid2, uid: uid3}];\n      var mockMsg = {key: 'some remote message'};\n      var uidMap = {};\n      for(var i in mockUids) {\n        uidMap[mockUids[i].uid] = mockUids[i];\n      }\n\n      var invokeCount = 0;\n\n      var mockRpcInvoke = function(sid, rmsg, cb) {\n        invokeCount++;\n        if(rmsg.args[2].indexOf(uid1) >= 0) {\n          cb(null, [uid1]);\n        } else if(rmsg.args[2].indexOf(uid3) >= 0) {\n          cb(null, [uid3]);\n        } else {\n          cb();\n        }\n      };\n\n      var app = pomelo.createApp({base: mockBase});\n      app.rpcInvoke = mockRpcInvoke;\n      var channelService = new ChannelService(app);\n\n      channelService.pushMessageByUids(mockMsg, mockUids, function(err, fails) {\n        invokeCount.should.equal(2);\n        should.not.exist(err);\n        should.exist(fails);\n        fails.length.should.equal(2);\n        fails.should.include(uid1);\n        fails.should.include(uid3);\n        done();\n      });\n    });\n  });\n\n  describe('#broadcast', function() {\n    it('should push message to all specified frontend servers', function(done) {\n      var mockServers = [\n        {id: 'connector-1', serverType: 'connector', other: 'xxx1'},\n        {id: 'connector-2', serverType: 'connector', other: 'xxx2'},\n        {id: 'area-1', serverType: 'area', other: 'yyy1'},\n        {id: 'gate-1', serverType: 'gate', other: 'zzz1'},\n        {id: 'gate-2', serverType: 'gate', other: 'xxx1'},\n        {id: 'gate-3', serverType: 'gate', other: 'yyy1'}\n      ];\n      var connectorIds = ['connector-1', 'connector-2'];\n      var mockSType = 'connector';\n      var mockRoute = 'test.route.string';\n      var mockBinded = true;\n      var opts = {binded: mockBinded};\n      var mockMsg = {key: 'some remote message'};\n\n      var invokeCount = 0;\n      var sids = [];\n\n      var mockRpcInvoke = function(sid, rmsg, cb) {\n        invokeCount++;\n        var args = rmsg.args;\n        var route = args[0];\n        var msg = args[1];\n        var opts = args[2];\n        mockMsg.should.eql(msg);\n        mockRoute.should.equal(route);\n        should.exist(opts);\n        mockBinded.should.equal(opts.userOptions.binded);\n        sids.push(sid);\n        cb();\n      };\n\n      var app = pomelo.createApp({base: mockBase});\n      app.rpcInvoke = mockRpcInvoke;\n      app.addServers(mockServers);\n      var channelService = new ChannelService(app);\n\n      channelService.broadcast(mockSType, mockRoute, mockMsg,\n                               opts, function() {\n        invokeCount.should.equal(2);\n        sids.length.should.equal(connectorIds.length);\n        for(var i=0, l=connectorIds.length; i<l; i++) {\n          sids.should.include(connectorIds[i]);\n        }\n        done();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/service/connectionService.js",
    "content": "var should = require('should');\nvar ConnectionService = require('../../lib/common/service/connectionService');\n\nvar mockApp = {\n  settings: {\n    serverId: 'connector-server-1'\n  },\n\n  get: function(key) {\n    return this.settings[key];\n  },\n\n  getServerId: function() {\n    return this.get('serverId');\n  }\n};\n\ndescribe('connection service test', function() {\n  describe('#addLoginedUser', function() {\n    it('should add logined user and could fetch it later', function() {\n      var service = new ConnectionService(mockApp);\n      should.exist(service);\n      service.loginedCount.should.equal(0);\n\n      var uid = 'uid1';\n      var info = {msg: 'some other message'};\n      service.addLoginedUser(uid, info);\n\n      service.loginedCount.should.equal(1);\n      var record = service.logined[uid];\n      should.exist(record);\n      record.should.eql(info);\n    });\n  });\n\n  describe('#increaseConnectionCount', function() {\n    it('should increate connection count and could fetch it later', function() {\n      var service = new ConnectionService(mockApp);\n      should.exist(service);\n      service.connCount.should.equal(0);\n\n      service.increaseConnectionCount();\n      service.connCount.should.equal(1);\n    });\n  });\n\n  describe('#removeLoginedUser', function() {\n    it('should remove logined user info with the uid', function() {\n      var service = new ConnectionService(mockApp);\n      should.exist(service);\n      service.loginedCount.should.equal(0);\n\n      var uid = 'uid1';\n      var info = {msg: 'some other message'};\n      service.addLoginedUser(uid, info);\n\n      service.loginedCount.should.equal(1);\n      var record = service.logined[uid];\n      should.exist(record);\n\n      var uid2 = 'uid2';\n      service.removeLoginedUser(uid2);\n      service.loginedCount.should.equal(1);\n      record = service.logined[uid];\n      should.exist(record);\n\n      service.removeLoginedUser(uid);\n      service.loginedCount.should.equal(0);\n      record = service.logined[uid];\n      should.not.exist(record);\n    });\n  });\n\n  describe('#decreaseConnectionCount', function() {\n    it('should decrease connection count only if uid is empty', function() {\n      var service = new ConnectionService(mockApp);\n      should.exist(service);\n\n      service.increaseConnectionCount();\n      service.connCount.should.equal(1);\n      service.decreaseConnectionCount();\n      service.connCount.should.equal(0);\n    });\n\n    it('should keep zero if connection count become zero', function() {\n      var service = new ConnectionService(mockApp);\n      should.exist(service);\n\n      service.connCount.should.equal(0);\n      service.decreaseConnectionCount();\n      service.connCount.should.equal(0);\n    });\n\n    it('should remove the logined info if uid is specified', function() {\n      var service = new ConnectionService(mockApp);\n      should.exist(service);\n\n      service.increaseConnectionCount();\n\n      var uid = 'uid1';\n      var info = {msg: 'some other message'};\n      service.addLoginedUser(uid, info);\n\n      service.connCount.should.equal(1);\n      service.logined[uid].should.eql(info);\n\n      service.decreaseConnectionCount(uid);\n\n      service.connCount.should.equal(0);\n      should.not.exist(service.logined[uid]);\n    });\n  });\n\n  it('should getStatisticsInfo',  function(done){\n    var service = new ConnectionService(mockApp);\n    var uid1 = 'uid1', uid2 = 'uid2';\n    var info1 = 'msg1', info2 = 'msg2';\n\n    service.increaseConnectionCount();\n    service.increaseConnectionCount();\n    service.increaseConnectionCount();\n\n    service.addLoginedUser(uid1, info1);\n    service.addLoginedUser(uid2, info2);\n\n\n    var sinfo = service.getStatisticsInfo();\n\n    sinfo.should.have.property('serverId', 'connector-server-1');\n    sinfo.should.have.property('totalConnCount', 3);\n    sinfo.should.have.property('loginedCount', 2);\n\n    var infos = sinfo.loginedList;\n    should.exist(infos);\n    infos.length.should.equal(2);\n    infos.should.include(info1);\n    infos.should.include(info2);\n\n    done();\n  });\n});\n"
  },
  {
    "path": "test/service/filterService.js",
    "content": "var should = require('should');\nvar FilterService = require('../../lib/common/service/filterService');\n\nvar WAIT_TIME = 50;\n\nvar mockFilter1 = {\n  before: function(msg, session, cb) {\n    session.beforeCount1++;\n    cb();\n  },\n\n  after: function(err, msg, session, resp, cb) {\n    session.afterCount1++;\n    cb();\n  }\n};\n\nvar mockFilter2 = {\n  before: function(msg, session, cb) {\n    session.beforeCount2++;\n    cb();\n  },\n\n  after: function(err, msg, session, resp, cb) {\n    session.afterCount2++;\n    cb();\n  }\n};\n\nvar blackholdFilter = {\n  before: function() {},\n  after: function() {}\n};\n\nvar MockSession = function(){\n  this.beforeCount1 = 0;\n  this.afterCount1 = 0;\n  this.beforeCount2 = 0;\n  this.afterCount2 = 0;\n};\n\ndescribe('filter service test', function() {\n  describe('#filter', function() {\n    it('should register before filter by calling before method and fire filter chain by calling beforeFilter', function(done) {\n      var session = new MockSession();\n      var service = new FilterService();\n      service.before(mockFilter1);\n      service.before(mockFilter2);\n      service.beforeFilter(null, session, function() {\n        should.exist(session);\n        session.beforeCount1.should.equal(1);\n        session.beforeCount2.should.equal(1);\n        session.afterCount1.should.equal(0);\n        session.afterCount2.should.equal(0);\n        done();\n      });\n    });\n\n    it('should register after filter by calling after method and fire filter chain by calling afterFilter', function(done) {\n      var session = new MockSession();\n      var service = new FilterService();\n      service.after(mockFilter1);\n      service.after(mockFilter2);\n      service.afterFilter(null, null, session, null, function() {\n        should.exist(session);\n        session.beforeCount1.should.equal(0);\n        session.beforeCount2.should.equal(0);\n        session.afterCount1.should.equal(1);\n        session.afterCount2.should.equal(1);\n        done();\n      });\n    });\n\n    it('should be ok if filter is a function', function(done) {\n      var session = {beforeCount: 0, afterCount: 0};\n      var service = new FilterService();\n      var beforeCount = 0, afterCount = 0;\n\n      service.before(function(msg, session, cb) {\n        session.beforeCount++;\n        cb();\n      });\n      service.after(function(err, msg, session, resp, cb) {\n        session.afterCount++;\n        cb();\n      });\n      service.beforeFilter(null, session, function() {\n        beforeCount++;\n      });\n      service.afterFilter(null, null, session, null, function() {\n        afterCount++;\n      });\n\n      setTimeout(function() {\n        session.beforeCount.should.equal(1);\n        session.afterCount.should.equal(1);\n        beforeCount.should.equal(1);\n        afterCount.should.equal(1);\n\n        done();\n      }, WAIT_TIME);\n    });\n\n    it('should not invoke the callback if filter not invoke callback', function(done) {\n      var session = new MockSession();\n      var service = new FilterService();\n      var beforeCount = 0, afterCount = 0;\n\n      service.before(blackholdFilter);\n      service.after(blackholdFilter);\n      service.beforeFilter(null, session, function() {\n        beforeCount++;\n      });\n      service.afterFilter(null, null, session, null, function() {\n        afterCount++;\n      });\n\n      setTimeout(function() {\n        session.beforeCount1.should.equal(0);\n        session.beforeCount2.should.equal(0);\n        session.afterCount1.should.equal(0);\n        session.afterCount2.should.equal(0);\n        beforeCount.should.equal(0);\n        afterCount.should.equal(0);\n\n        done();\n      }, WAIT_TIME);\n    });\n\n    it('should pass the err and resp parameters to callback and ignore the filters behind if them specified in before filter', function(done) {\n      var session = new MockSession();\n      var service = new FilterService();\n      var error = 'some error message';\n      var response = {key: 'some value'};\n      var respFilter = {\n        before: function(msg, session, cb) {\n          cb(error, response);\n        }\n      };\n\n      service.before(mockFilter1);\n      service.before(respFilter);\n      service.before(mockFilter2);\n      service.beforeFilter(null, session, function(err, resp) {\n        should.exist(err);\n        err.should.equal(error);\n        should.exist(resp);\n        resp.should.equal(response);\n\n        session.beforeCount1.should.equal(1);\n        session.beforeCount2.should.equal(0);\n        session.afterCount1.should.equal(0);\n        session.afterCount2.should.equal(0);\n\n        done();\n      });\n    });\n  });\n});"
  },
  {
    "path": "test/service/handlerService.js",
    "content": "var should = require('should');\nvar HandlerService = require('../../lib/common/service/handlerService');\n\nvar mockApp = {\n  serverType: 'connector',\n\n  get: function(key) {\n    return this[key];\n  }\n};\n\nvar mockSession = {\n  exportSession: function() {\n    return this;\n  }\n};\n\nvar mockMsg = {key: 'some request message'};\nvar mockRouteRecord = {serverType: 'connector', handler: 'testHandler', method: 'testMethod'};\n\ndescribe('handler service test', function() {\n  describe('handle', function() {\n    it('should dispatch the request to the handler if the route match current server type', function(done) {\n      var invoke1Count = 0, invoke2Count = 0;\n      // mock datas\n      var mockHandlers = {\n        testHandler: {\n          testMethod: function(msg, session, next) {\n            invoke1Count++;\n            msg.should.eql(mockMsg);\n            next();\n          }\n        },\n        test2Handler: {\n          testMethod: function(msg, session, next) {\n            invoke2Count++;\n            next();\n          }\n        }\n      };\n\n      var mockOpts = {};\n\n      var service = new HandlerService(mockApp, mockOpts);\n      service.handlerMap = {connector: mockHandlers};\n\n      service.handle(mockRouteRecord, mockMsg, mockSession, function() {\n        invoke1Count.should.equal(1);\n        invoke2Count.should.equal(0);\n        done();\n      });\n    });\n\n    it('should return an error if can not find the appropriate handler locally', function(done) {\n      var mockHandlers = {};\n      var mockOpts = {};\n      var service = new HandlerService(mockApp, mockOpts);\n      service.handlerMap = {connector: mockHandlers};\n\n      service.handle(mockRouteRecord, mockMsg, mockSession, function(err) {\n        should.exist(err);\n        done();\n      });\n    });\n  });\n});"
  },
  {
    "path": "test/service/sessionService.js",
    "content": "var should = require('should');\nvar pomelo = require('../../');\nvar SessionService = require('../../lib/common/service/sessionService');\n\ndescribe('session service test', function() {\n  describe('#bind', function() {\n    it('should get session by uid after binded', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var uid = 'changchang';\n      var eventCount = 0;\n\n      var session = service.create(sid, fid, socket);\n\n      should.exist(session);\n\n      session.should.eql(service.get(sid));\n\n      session.on('bind', function(euid) {\n        eventCount++;\n        uid.should.equal(euid);\n      });\n\n      service.bind(sid, uid, function(err) {\n        should.not.exist(err);\n        var sessions = service.getByUid(uid);\n        should.exist(sessions);\n        sessions.length.should.equal(1);\n        session.should.eql(sessions[0]);\n        eventCount.should.equal(1);\n        service.bind(sid, uid, function(err) {\n          should.not.exist(err);\n          done();\n        });\n      });\n    });\n    it('should fail if already binded uid', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var uid = 'py', test_uid = 'test';\n\n      var session = service.create(sid, fid, socket);\n\n      service.bind(sid, uid, null);\n\n      service.bind(sid, test_uid, function(err) {\n        should.exist(err);\n        done();\n      });\n    });\n    it('should fail if try to bind a session not exist', function(done) {\n      var service = new SessionService();\n      var sid = 1, uid = 'changchang';\n\n      service.bind(sid, uid, function(err) {\n        should.exist(err);\n        done();\n      });\n    });\n  });\n\n  describe('#unbind', function() {\n    it('should fail unbind session if session not exist', function(done) {\n      var service = new SessionService();\n      var sid = 1;\n      var uid = 'py';\n  \n      service.unbind(sid, uid, function(err) {\n        should.exist(err);\n        done();\n      });      \n    });\n    it('should fail unbind session if session not binded', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var uid = 'py';\n\n      var session = service.create(sid, fid, socket);\n\n      service.unbind(sid, uid, function(err) {\n        should.exist(err);\n        done();\n      });\n    });\n    it('should fail to get session after session unbinded', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var uid = 'py';\n\n      var session = service.create(sid, fid, socket);\n      service.bind(sid, uid, null);\n\n      service.unbind(sid, uid, function(err) {\n        should.not.exist(err);\n        var sessions = service.getByUid(uid);\n        should.not.exist(sessions);\n        done();\n      });\n    });\n  });\n\n  describe('#remove', function() {\n    it('should not get the session after remove', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var uid = 'changchang';\n\n      var session = service.create(sid, fid, socket);\n\n      service.bind(sid, uid, function(err) {\n        service.remove(sid);\n        should.not.exist(service.get(sid));\n        should.not.exist(service.getByUid(uid));\n        done();\n      });\n    });\n  });\n\n  describe('#import', function() {\n    it('should update the session with the key/value pair', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var key = 'key-1', value = 'value-1';\n\n      var session = service.create(sid, fid, socket);\n\n      service.import(sid, key, value, function(err) {\n        should.not.exist(err);\n        value.should.eql(session.get(key));\n        done();\n      });\n    });\n\n    it('should fail if try to update a session not exist', function(done) {\n      var service = new SessionService();\n      var sid = 1;\n      var key = 'key-1', value = 'value-1';\n\n      service.import(sid, key, value, function(err) {\n        should.exist(err);\n        done();\n      });\n    });\n\n    it('should update the session with the key/value pairs', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var key = 'key-1', value = 'value-1', key2 = 'key-2', value2 = {};\n\n      var settings = {};\n      settings[key] = value;\n      settings[key2] = value2;\n\n      var session = service.create(sid, fid, socket);\n\n      service.importAll(sid, settings, function(err) {\n        should.not.exist(err);\n        value.should.eql(session.get(key));\n        value2.should.eql(session.get(key2));\n        done();\n      });\n    });\n\n    it('should fail if try to update a session not exist', function(done) {\n      var service = new SessionService();\n      var sid = 1;\n      var key = 'key-1', value = 'value-1';\n\n      service.import(sid, key, value, function(err) {\n        should.exist(err);\n        done();\n      });\n    });\n\n    it('should fail if try to update a session not exist', function(done) {\n      var service = new SessionService();\n      var sid = 1;\n      var key = 'key-1', value = 'value-1', key2 = 'key-2', value2 = {};\n\n      var settings = {};\n      settings[key] = value;\n      settings[key2] = value2;\n\n      service.importAll(sid, settings, function(err) {\n        should.exist(err);\n        done();\n      });\n    });\n  });\n\n  describe('#kick', function() {\n    it('should kick the sessions', function(done) {\n      var service = new SessionService();\n      var sid1 = 1, fid1 = 'frontend-server-1';\n      var sid2 = 2, fid2 = 'frontend-server-1';\n\n      var socket = {\n        emit: function(){},\n        disconnect: function(){}\n      };\n      var uid = 'changchang';\n      var eventCount = 0;\n\n      var session1 = service.create(sid1, fid1, socket);\n      var session2 = service.create(sid2, fid2, socket);\n      session1.on('closed', function() {\n        eventCount++;\n      });\n\n      session2.on('closed', function() {\n        eventCount++;\n      });\n\n      service.bind(sid1, uid, function(err) {\n        service.bind(sid2, uid, function(err) {\n          service.kick(uid, function(err) {\n            should.not.exist(err);\n            should.not.exist(service.get(sid1));\n            should.not.exist(service.get(sid2));\n            should.not.exist(service.getByUid(uid));\n            eventCount.should.equal(2);\n            done();\n          });\n        });\n      });\n    });\n\n    it('should kick the session by sessionId', function(done) {\n      var service = new SessionService();\n      var sid1 = 1, fid1 = 'frontend-server-1';\n      var sid2 = 2, fid2 = 'frontend-server-1';\n\n      var socket = {\n        emit: function(){},\n        disconnect: function(){}\n      };\n      var uid = 'changchang';\n      var eventCount = 0;\n\n      var session1 = service.create(sid1, fid1, socket);\n      var session2 = service.create(sid2, fid2, socket);\n      session1.on('closed', function() {\n        eventCount++;\n      });\n\n      session2.on('closed', function() {\n        eventCount++;\n      });\n\n      service.bind(sid1, uid, function(err) {\n        service.bind(sid2, uid, function(err) {\n          service.kickBySessionId(sid1, function(err) {\n            should.not.exist(err);\n            should.not.exist(service.get(sid1));\n            should.exist(service.get(sid2));\n            should.exist(service.getByUid(uid));\n            eventCount.should.equal(1);\n            done();\n          });\n        });\n      });\n    });\n\n    it('should ok if kick a session not exist', function(done) {\n      var service = new SessionService();\n      var uid = 'changchang';\n\n      service.kick(uid, function(err) {\n        should.not.exist(err);\n        done();\n      });\n    });\n\n    it('should kick session by sid', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1';\n      var socket = {\n        emit: function(){},\n        disconnect: function(){}\n      };\n      var eventCount = 0;\n\n      var session = service.create(sid, fid, socket);\n      session.on('closed', function() {\n        eventCount++;\n      });\n\n      service.kickBySessionId(sid, function(err) {\n        should.not.exist(err);\n        should.not.exist(service.get(sid));\n        eventCount.should.equal(1);\n        done();\n      });\n    });\n\n    it('should ok if kick a session not exist', function(done) {\n      var service = new SessionService();\n      var sid = 1;\n\n      service.kickBySessionId(sid, function(err) {\n        should.not.exist(err);\n        done();\n      });\n    });\n  });\n\n  describe('#forEachSession', function() {\n    it('should iterate all created sessions', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var eventCount = 0;\n\n      var outter_session = service.create(sid, fid, socket);\n\n      service.forEachSession(function(session) {\n        should.exist(session);\n        outter_session.id.should.eql(session.id);\n        done();\n      });\n    });\n  });\n\n  describe('#forEachBindedSession', function() {\n    it('should iterate all binded sessions', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var uid = 'py';\n\n      var outter_session = service.create(sid, fid, socket);\n      service.bind(sid, uid, null);\n\n      service.forEachBindedSession(function(session) {\n        should.exist(session);\n        outter_session.id.should.eql(session.id);\n        outter_session.uid.should.eql(session.uid);\n        done();\n      });\n    });\n  });\n});\n\ndescribe('frontend session test', function() {\n  describe('#bind', function() {\n    it('should get session by uid after binded', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var uid = 'changchang';\n      var eventCount = 0;\n\n      var session = service.create(sid, fid, socket);\n      var fsession = session.toFrontendSession();\n\n      should.exist(fsession);\n\n      fsession.on('bind', function(euid) {\n        eventCount++;\n        uid.should.equal(euid);\n      });\n\n      fsession.bind(uid, function(err) {\n        should.not.exist(err);\n        var sessions = service.getByUid(uid);\n        should.exist(sessions);\n        sessions.length.should.equal(1);\n        session.should.eql(sessions[0]);\n        eventCount.should.equal(1);\n        done();\n      });\n    });\n  });\n\n  describe('#unbind', function() {\n    it('should fail to get session after session unbinded', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var uid = 'py';\n\n      var session = service.create(sid, fid, socket);\n      var fsession = session.toFrontendSession();\n\n      fsession.bind(uid, null);\n      fsession.unbind(uid, function(err) {\n        should.not.exist(err);\n        var sessions = service.getByUid(uid);\n        should.not.exist(sessions);\n        done();\n      });\n    });\n  });\n\n  describe('#set/get', function() {\n    it('should update the key/value pair in frontend session but not session',\n        function() {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var key = 'key-1', value = 'value-1';\n\n      var session = service.create(sid, fid, socket);\n      var fsession = session.toFrontendSession();\n\n      fsession.set(key, value);\n\n      should.not.exist(session.get(key));\n      value.should.eql(fsession.get(key));\n    });\n  });\n\n  describe('#push', function() {\n    it('should push the specified key/value pair to session', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var key = 'key-1', value = 'value-1', key2 = 'key-2', value2 = {};\n\n      var session = service.create(sid, fid, socket);\n      var fsession = session.toFrontendSession();\n\n      fsession.set(key, value);\n      fsession.set(key2, value2);\n\n      fsession.push(key, function(err) {\n        should.not.exist(err);\n        value.should.eql(session.get(key));\n        should.not.exist(session.get(key2));\n        done();\n      });\n    });\n\n    it('should push all the key/value pairs to session', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var key = 'key-1', value = 'value-1', key2 = 'key-2', value2 = {};\n\n      var session = service.create(sid, fid, socket);\n      var fsession = session.toFrontendSession();\n\n      fsession.set(key, value);\n      fsession.set(key2, value2);\n\n      fsession.pushAll(function(err) {\n        should.not.exist(err);\n        value.should.eql(session.get(key));\n        value2.should.eql(session.get(key2));\n        done();\n      });\n    });\n  });\n  \n  describe('#export', function() {\n    it('should equal frontend session after export', function(done) {\n      var service = new SessionService();\n      var sid = 1, fid = 'frontend-server-1', socket = {};\n      var uid = 'py';\n\n      var session = service.create(sid, fid, socket);\n      var fsession = session.toFrontendSession();\n      var esession = fsession.export();\n      esession.id.should.eql(fsession.id);\n      esession.frontendId.should.eql(fsession.frontendId);\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/util/countDownLatch.js",
    "content": "var CountDownLatch = require('../../lib/util/countDownLatch');\nvar should = require('should');\n\nvar cbCreator = (function() {\n  var count =0;\n\n  return {\n    callback: function() {\n      count++;\n    },\n    getCount: function() {\n      return count;\n    },\n    count: count\n  };\n})();\n\ndescribe('countdown latch test', function() {\n  var countDownLatch1;\n  var countDownLatch2;\n\n  describe('#count down', function() {\n    it('should invoke the callback after the done method was invoked the specified times', function(done) {\n      var n = 3, doneCount = 0;\n      var cdl = CountDownLatch.createCountDownLatch(n, function() {\n        doneCount.should.equal(n);\n        done();\n      });\n\n      for(var i=0; i<n; i++) {\n        doneCount++;\n        cdl.done();\n      }\n    });\n\n    it('should throw exception if pass a negative or zero to the create method', function() {\n      (function() {\n        CountDownLatch.createCountDownLatch(-1, function() {});\n      }).should.throw();\n\n      (function() {\n        CountDownLatch.createCountDownLatch(0, function() {});\n      }).should.throw();\n    });\n\n    it('should throw exception if pass illegal cb to the create method', function() {\n      (function() {\n        CountDownLatch.createCountDownLatch(1, null);\n      }).should.throw();\n    });\n\n    it('should throw exception if try to invoke done metho of a latch that has fired cb', function() {\n      var n = 3;\n      var cdl = CountDownLatch.createCountDownLatch(n, function() {});\n\n      for(var i=0; i<n; i++) {\n        cdl.done();\n      }\n\n      (function() {\n        cdl.done();\n      }).should.throw();\n    });\n\n    it('should invoke the callback if timeout', function() {\n      var n = 3;\n      var cdl = CountDownLatch.createCountDownLatch(n, {timeout: 3000}, function(isTimeout) {\n        isTimeout.should.equal(true);\n      });\n\n      for(var i=0; i<n-1; i++) {\n        cdl.done();\n      }\n    });\n\n  });\n});\n"
  },
  {
    "path": "test/util/pathUtil.js",
    "content": "var pathUtil = require('../../lib/util/pathUtil');\nvar utils = require('../../lib/util/utils');\nvar should = require('should');\nvar fs = require('fs');\n\nvar mockBase = process.cwd() + '/test/mock-base';\n\ndescribe('path util test', function() {\n  describe('#getSysRemotePath', function() {\n    it('should return the system remote service path for frontend server', function() {\n      var role = 'frontend';\n      var expectSuffix = '/common/remote/frontend';\n      var p = pathUtil.getSysRemotePath(role);\n      should.exist(p);\n      fs.existsSync(p).should.be.true;\n      utils.endsWith(p, expectSuffix).should.be.true;\n    });\n\n    it('should return the system remote service path for backend server', function() {\n      var role = 'backend';\n      var expectSuffix = '/common/remote/backend';\n      var p = pathUtil.getSysRemotePath(role);\n      should.exist(p);\n      fs.existsSync(p).should.be.true;\n      utils.endsWith(p, expectSuffix).should.be.true;\n    });\n\n  });\n\n  describe('#getUserRemotePath', function() {\n    it('should return user remote service path for the associated server type', function() {\n      var serverType = 'connector';\n      var expectSuffix = '/app/servers/connector/remote';\n      var p = pathUtil.getUserRemotePath(mockBase, serverType);\n      should.exist(p);\n      fs.existsSync(p).should.be.true;\n      utils.endsWith(p, expectSuffix).should.be.true;\n    });\n\n    it('should return null if the directory not exist', function() {\n      var serverType = 'area';\n      var p = pathUtil.getUserRemotePath(mockBase, serverType);\n      should.not.exist(p);\n\n      serverType = 'some-dir-not-exist';\n      p = pathUtil.getUserRemotePath(mockBase, serverType);\n      should.not.exist(p);\n    });\n  });\n\n  describe('#listUserRemoteDir', function() {\n    it('should return sub-direcotry name list of servers/ directory', function() {\n      var expectNames = ['connector', 'area'];\n      var p = pathUtil.listUserRemoteDir(mockBase);\n      should.exist(p);\n      expectNames.length.should.equal(p.length);\n      for(var i=0, l=expectNames.length; i<l; i++) {\n        p.should.include(expectNames[i]);\n      }\n    });\n\n    it('should throw err if the servers/ illegal', function() {\n      (function() {\n        pathUtil.listUserRemoteDir('some illegal base');\n      }).should.throw();\n    });\n  });\n\n  describe('#remotePathRecord', function() {\n    var namespace = 'user';\n    var serverType = 'connector';\n    var path = '/some/path/to/remote';\n    var r = pathUtil.remotePathRecord(namespace, serverType, path);\n    should.exist(r);\n    namespace.should.equal(r.namespace);\n    serverType.should.equal(r.serverType);\n    path.should.equal(r.path);\n  });\n\n  describe('#getHandlerPath', function() {\n    it('should return user handler path for the associated server type', function() {\n      var serverType = 'connector';\n      var expectSuffix = '/app/servers/connector/handler';\n      var p = pathUtil.getHandlerPath(mockBase, serverType);\n      should.exist(p);\n      fs.existsSync(p).should.be.true;\n      utils.endsWith(p, expectSuffix).should.be.true;\n    });\n\n    it('should return null if the directory not exist', function() {\n      var serverType = 'area';\n      var p = pathUtil.getHandlerPath(mockBase, serverType);\n      should.not.exist(p);\n\n      serverType = 'some-dir-not-exist';\n      p = pathUtil.getHandlerPath(mockBase, serverType);\n      should.not.exist(p);\n    });\n  });\n\n  describe('#getScriptPath', function() {\n    var p = pathUtil.getScriptPath(mockBase);\n    var expectSuffix = '/scripts';\n    should.exist(p);\n    utils.endsWith(p, expectSuffix).should.be.true;\n  });\n\n  describe('#getLogPath', function() {\n    var p = pathUtil.getLogPath(mockBase);\n    var expectSuffix = '/logs';\n    should.exist(p);\n    utils.endsWith(p, expectSuffix).should.be.true;\n  });\n\n});"
  },
  {
    "path": "test/util/utils.js",
    "content": "var utils = require('../../lib/util/utils');\nvar should = require('should');\n\ndescribe('utils test', function() {\n  describe('#invokeCallback', function() {\n    it('should invoke the function with the parameters', function() {\n      var p1 = 1, p2 = 'str';\n\n      var func = function(arg1, arg2) {\n        p1.should.equal(arg1);\n        p2.should.equal(arg2);\n      };\n\n      utils.invokeCallback(func, p1, p2);\n    });\n\n    it('should ok if cb is null', function() {\n      var p1 = 1, p2 = 'str';\n      (function() {\n        utils.invokeCallback(null, p1, p2);\n      }).should.not.throw();\n    });\n  });\n\n  describe('#size', function() {\n    it('should return the own property count of the object', function() {\n      var obj = {\n        p1: 'str',\n        p2: 1,\n        m1: function() {}\n      };\n\n      utils.size(obj).should.equal(2);\n    });\n  });\n\n  describe('#startsWith', function() {\n    it('should return true if the string do start with the prefix', function() {\n      var src = 'prefix with a string';\n      var prefix = 'prefix';\n\n      utils.startsWith(src, prefix).should.be.true;\n    });\n\n    it('should return false if the string not start with the prefix', function() {\n      var src = 'prefix with a string';\n      var prefix = 'prefix222';\n\n      utils.startsWith(src, prefix).should.be.false;\n\n      prefix = 'with';\n      utils.startsWith(src, prefix).should.be.false;\n    });\n\n    it('should return false if the src not a string', function() {\n      utils.startsWith(1, 'str').should.be.false;\n    });\n  });\n\n  describe('#endsWith', function() {\n    it('should return true if the string do end with the prefix', function() {\n      var src = 'string with a suffix';\n      var suffix = 'suffix';\n\n      utils.endsWith(src, suffix).should.be.true;\n    });\n\n    it('should return false if the string not end with the prefix', function() {\n      var src = 'string with a suffix';\n      var suffix = 'suffix222';\n\n      utils.endsWith(src, suffix).should.be.false;\n\n      suffix = 'with';\n      utils.endsWith(src, suffix).should.be.false;\n    });\n\n    it('should return false if the src not a string', function() {\n      utils.endsWith(1, 'str').should.be.false;\n    });\n  });\n\n  describe('#hasChineseChar', function() {\n    it('should return false if the string does not have any Chinese characters', function() {\n      var src = 'string without Chinese characters';\n      utils.hasChineseChar(src).should.be.false;\n    });\n\n    it('should return true if the string has Chinese characters', function() {\n      var src = 'string with Chinese characters 你好';\n      utils.hasChineseChar(src).should.be.true;\n    });\n  });\n\n  describe('#unicodeToUtf8', function() {\n    it('should return the origin string if the string does not have any Chinese characters', function() {\n      var src = 'string without Chinese characters';\n      utils.unicodeToUtf8(src).should.equal(src);\n    });\n\n    it('should not return the origin string if the string has Chinese characters', function() {\n      var src = 'string with Chinese characters 你好';\n      utils.unicodeToUtf8(src).should.not.equal(src);\n    });\n  });\n\n  describe('#isLocal', function() {\n    it('should return true if the ip is local', function() {\n      var ip = '127.0.0.1';\n      var host = 'localhost';\n      var other = '192.168.1.1';\n      utils.isLocal(ip).should.be.true;\n      utils.isLocal(host).should.be.true;\n      utils.isLocal(other).should.be.false;\n    });\n  });\n\n  describe('#loadCluster', function() {\n    it('should produce cluster servers', function() {\n      var clusterServer = {host: '127.0.0.1', port: '3010++', serverType: 'chat', cluster: true, clusterCount: 2};\n      var serverMap = {};\n      var app = {clusterSeq:{}};\n      utils.loadCluster(app, clusterServer, serverMap);\n      utils.size(serverMap).should.equal(2);\n    });\n  });\n\n  describe('#arrayDiff', function() {\n    it('should return the difference of two arrays', function() {\n      var array1 = [1, 2, 3, 4, 5];\n      var array2 = [1, 2, 3];\n      var array = utils.arrayDiff(array1, array2);\n      array.should.eql([4, 5]);\n    });\n  });\n\n  describe('#extends', function() {\n    it('should extends opts', function() {\n      var opts = {\n        test: 123\n      };\n      var add = {\n        aaa: 555\n      };\n      var result = utils.extends(opts, add);\n      result.should.eql({\n        test: 123,\n        aaa: 555\n      });\n    });\n  });\n\n  describe('#ping', function() {\n    it('should ping server', function() {\n      utils.ping('127.0.0.1', function(flag) {\n        flag.should.be.true;\n      });\n      utils.ping('111.111.111.111', function(flag) {\n        flag.should.be.false;\n      });\n    });\n  });\n\n});"
  }
]