diff --git a/bin/pomelo b/bin/pomelo index 73b626964..50ae73d9a 100755 --- a/bin/pomelo +++ b/bin/pomelo @@ -1,5 +1,7 @@ #!/usr/bin/env node +"use strict"; + /** * Module dependencies. */ @@ -9,13 +11,10 @@ var fs = require('fs'), util = require('util'), cliff = require('cliff'), mkdirp = require('mkdirp'), - co = require('../lib/modules/console'), utils = require('../lib/util/utils'), - starter = require('../lib/master/starter'), exec = require('child_process').exec, spawn = require('child_process').spawn, version = require('../package.json').version, - adminClient = require('pomelo-admin').adminClient, constants = require('../lib/util/constants'), program = require('commander'); @@ -23,26 +22,14 @@ var fs = require('fs'), * Constant Variables */ var TIME_INIT = 1 * 1000; -var TIME_KILL_WAIT = 5 * 1000; -var KILL_CMD_LUX = 'kill -9 `ps -ef|grep node|awk \'{print $2}\'`'; -var KILL_CMD_WIN = 'taskkill /im node.exe /f'; var CUR_DIR = process.cwd(); var DEFAULT_GAME_SERVER_DIR = CUR_DIR; -var DEFAULT_USERNAME = 'admin'; -var DEFAULT_PWD = 'admin'; -var DEFAULT_ENV = 'development'; -var DEFAULT_MASTER_HOST = '127.0.0.1'; -var DEFAULT_MASTER_PORT = 3005; -var CONNECT_ERROR = 'Fail to connect to admin console server.'; var FILEREAD_ERROR = 'Fail to read the file, please check if the application is started legally.'; var CLOSEAPP_INFO = 'Closing the application......\nPlease wait......'; -var ADD_SERVER_INFO = 'Successfully add server.'; -var RESTART_SERVER_INFO = 'Successfully restart server.'; var 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 '; var 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; -var 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; var COMMAND_ERROR = 'Illegal command format. Use `pomelo --help` to get more info.\n'.red; var DAEMON_INFO = 'The application is running in the background now.\n'; @@ -54,86 +41,6 @@ program.command('init [path]') init(path || CUR_DIR); }); -program.command('start') - .description('start the application') - .option('-e, --env ', 'the used environment', DEFAULT_ENV) - .option('-D, --daemon', 'enable the daemon start') - .option('-d, --directory, ', 'the code directory', DEFAULT_GAME_SERVER_DIR) - .option('-t, --type ,', 'start server type') - .option('-i, --id ', 'start server id') - .action(function(opts) { - start(opts); - }); - -program.command('list') - .description('list the servers') - .option('-u, --username ', 'administration user name', DEFAULT_USERNAME) - .option('-p, --password ', 'administration password', DEFAULT_PWD) - .option('-h, --host ', 'master server host', DEFAULT_MASTER_HOST) - .option('-P, --port ', 'master server port', DEFAULT_MASTER_PORT) - .action(function(opts) { - list(opts); - }); - -program.command('add') - .description('add a new server') - .option('-u, --username ', 'administration user name', DEFAULT_USERNAME) - .option('-p, --password ', 'administration password', DEFAULT_PWD) - .option('-h, --host ', 'master server host', DEFAULT_MASTER_HOST) - .option('-P, --port ', 'master server port', DEFAULT_MASTER_PORT) - .action(function() { - var args = [].slice.call(arguments, 0); - var opts = args[args.length - 1]; - opts.args = args.slice(0, -1); - add(opts); - }); - -program.command('stop') - .description('stop the servers, for multiple servers, use `pomelo stop server-id-1 server-id-2`') - .option('-u, --username ', 'administration user name', DEFAULT_USERNAME) - .option('-p, --password ', 'administration password', DEFAULT_PWD) - .option('-h, --host ', 'master server host', DEFAULT_MASTER_HOST) - .option('-P, --port ', 'master server port', DEFAULT_MASTER_PORT) - .action(function() { - var args = [].slice.call(arguments, 0); - var opts = args[args.length - 1]; - opts.serverIds = args.slice(0, -1); - terminal('stop', opts); - }); - -program.command('kill') - .description('kill the application') - .option('-u, --username ', 'administration user name', DEFAULT_USERNAME) - .option('-p, --password ', 'administration password', DEFAULT_PWD) - .option('-h, --host ', 'master server host', DEFAULT_MASTER_HOST) - .option('-P, --port ', 'master server port', DEFAULT_MASTER_PORT) - .option('-f, --force', 'using this option would kill all the node processes') - .action(function() { - var args = [].slice.call(arguments, 0); - var opts = args[args.length - 1]; - opts.serverIds = args.slice(0, -1); - terminal('kill', opts); - }); - -program.command('restart') - .description('restart the servers, for multiple servers, use `pomelo restart server-id-1 server-id-2`') - .option('-u, --username ', 'administration user name', DEFAULT_USERNAME) - .option('-p, --password ', 'administration password', DEFAULT_PWD) - .option('-h, --host ', 'master server host', DEFAULT_MASTER_HOST) - .option('-P, --port ', 'master server port', DEFAULT_MASTER_PORT) - .option('-t, --type ,', 'start server type') - .option('-i, --id ', 'start server id') - .action(function(opts) { - restart(opts); - }); - -program.command('masterha') - .description('start all the slaves of the master') - .option('-d, --directory ', 'the code directory', DEFAULT_GAME_SERVER_DIR) - .action(function(opts) { - startMasterha(opts); - }); - program.command('*') .action(function() { console.log(COMMAND_ERROR); @@ -369,161 +276,6 @@ function start(opts) { } } -/** - * List pomelo processes. - * - * @param {Object} opts options for `list` operation - */ -function list(opts) { - var id = 'pomelo_list_' + Date.now(); - connectToMaster(id, opts, function(client) { - client.request(co.moduleId, {signal: 'list'}, function(err, data) { - if(err) { - console.error(err); - } - var servers = []; - for(var key in data.msg) { - servers.push(data.msg[key]); - } - var comparer = function(a, b) { - if (a.serverType < b.serverType) { - return -1; - } else if (a.serverType > b.serverType) { - return 1; - } else if (a.serverId < b.serverId) { - return -1; - } else if (a.serverId > b.serverId) { - return 1; - } else { - return 0; - } - }; - servers.sort(comparer); - var rows = []; - rows.push(['serverId', 'serverType', 'pid', 'rss(M)', 'heapTotal(M)', 'heapUsed(M)', 'uptime(m)']); - servers.forEach(function(server) { - rows.push([server.serverId, server.serverType, server.pid, server.rss, server.heapTotal, server.heapUsed, server.uptime]); - }); - console.log(cliff.stringifyRows(rows, ['red', 'blue', 'green', 'cyan', 'magenta', 'white', 'yellow'])); - process.exit(0); - }); - }); -} - -/** - * Add server to application. - * - * @param {Object} opts options for `add` operation - */ -function add(opts) { - var id = 'pomelo_add_' + Date.now(); - connectToMaster(id, opts, function(client) { - client.request(co.moduleId, { signal: 'add', args: opts.args }, function(err) { - if(err) { - console.error(err); - } - else { - console.info(ADD_SERVER_INFO); - } - process.exit(0); - }); - }); -} - -/** - * Terminal application. - * - * @param {String} signal stop/kill - * @param {Object} opts options for `stop/kill` operation - */ -function terminal(signal, opts) { - console.info(CLOSEAPP_INFO); - // option force just for `kill` - if(opts.force) { - if (os.platform() === constants.PLATFORM.WIN) { - exec(KILL_CMD_WIN); - } else { - exec(KILL_CMD_LUX); - } - process.exit(1); - return; - } - var id = 'pomelo_terminal_' + Date.now(); - connectToMaster(id, opts, function(client) { - client.request(co.moduleId, { - signal: signal, ids: opts.serverIds - }, function(err, msg) { - if(err) { - console.error(err); - } - if(signal === 'kill') { - if(msg.code === 'ok') { - console.log('All the servers have been terminated!'); - } else { - console.log('There may be some servers remained:', msg.serverIds); - } - } - process.exit(0); - }); - }); -} - -function restart(opts) { - var id = 'pomelo_restart_' + Date.now(); - var serverIds = []; - var type = null; - if(!!opts.id) { - serverIds.push(opts.id); - } - if(!!opts.type) { - type = opts.type; - } - connectToMaster(id, opts, function(client) { - client.request(co.moduleId, { signal: 'restart', ids: serverIds, type: type}, function(err, fails) { - if(!!err) { - console.error(err); - } else if(!!fails.length) { - console.info('restart fails server ids: %j', fails); - } else { - console.info(RESTART_SERVER_INFO); - } - process.exit(0); - }); - }); -} - -function connectToMaster(id, opts, cb) { - var client = new adminClient({username: opts.username, password: opts.password, md5: true}); - client.connect(id, opts.host, opts.port, function(err) { - if(err) { - abort(CONNECT_ERROR + err.red); - } - if(typeof cb === 'function') { - cb(client); - } - }); -} - -/** - * Start master slaves. - * - * @param {String} option for `startMasterha` operation - */ -function startMasterha(opts) { - var configFile = path.join(opts.directory, constants.FILEPATH.MASTER_HA); - if(!fs.existsSync(configFile)) { - abort(MASTER_HA_NOT_FOUND); - } - var masterha = require(configFile).masterha; - for(var i=0; i @@ -7,22 +8,22 @@ /** * Module dependencies. */ -var utils = require('./util/utils'); -var logger = require('pomelo-logger').getLogger('pomelo', __filename); -var EventEmitter = require('events').EventEmitter; -var events = require('./util/events'); -var appUtil = require('./util/appUtil'); -var Constants = require('./util/constants'); -var appManager = require('./common/manager/appManager'); -var fs = require('fs'); -var path = require('path'); + var utils = require('./util/utils'); + var logger = require('pomelo-logger').getLogger('pomelo', __filename); + var EventEmitter = require('events').EventEmitter; + var events = require('./util/events'); + var appUtil = require('./util/appUtil'); + var Constants = require('./util/constants'); + var appManager = require('./common/manager/appManager'); + var fs = require('fs'); + var path = require('path'); /** * Application prototype. * * @module */ -var Application = module.exports = {}; + var Application = module.exports = {}; /** * Application states @@ -37,7 +38,7 @@ var STATE_STOPED = 4; // app has stoped * * - setup default configuration */ -Application.init = function(opts) { + Application.init = function(opts) { opts = opts || {}; this.loaded = []; // loaded component list this.components = {}; // name -> component map @@ -53,7 +54,6 @@ Application.init = function(opts) { this.startTime = null; // current server start time // global server infos - this.master = null; // master server info this.servers = {}; // current global server info maps, id -> info this.serverTypeMaps = {}; // current global type maps, type -> [info] this.serverTypes = []; // current global server type list @@ -77,7 +77,7 @@ Application.init = function(opts) { * * @memberOf Application */ -Application.getBase = function() { + Application.getBase = function() { return this.get(Constants.RESERVED.BASE); }; @@ -88,7 +88,7 @@ Application.getBase = function() { * * @memberOf Application */ -Application.require = function(ph) { + Application.require = function(ph) { return require(path.join(Application.getBase(), ph)); }; @@ -99,7 +99,7 @@ Application.require = function(ph) { * * @memberOf Application */ -Application.configureLogger = function(logger) { + Application.configureLogger = function(logger) { if (process.env.POMELO_LOGGER !== 'off') { var base = this.getBase(); var env = this.get(Constants.RESERVED.ENV); @@ -122,7 +122,7 @@ Application.configureLogger = function(logger) { * A filter should have two methods: before and after. * @memberOf Application */ -Application.filter = function (filter) { + Application.filter = function (filter) { this.before(filter); this.after(filter); }; @@ -133,7 +133,7 @@ Application.filter = function (filter) { * @param {Object|Function} bf before fileter, bf(msg, session, next) * @memberOf Application */ -Application.before = function (bf) { + Application.before = function (bf) { addFilter(this, Constants.KEYWORDS.BEFORE_FILTER, bf); }; @@ -143,7 +143,7 @@ Application.before = function (bf) { * @param {Object|Function} af after filter, `af(err, msg, session, resp, next)` * @memberOf Application */ -Application.after = function (af) { + Application.after = function (af) { addFilter(this, Constants.KEYWORDS.AFTER_FILTER, af); }; @@ -154,7 +154,7 @@ Application.after = function (af) { * A filter should have two methods: before and after. * @memberOf Application */ -Application.globalFilter = function (filter) { + Application.globalFilter = function (filter) { this.globalBefore(filter); this.globalAfter(filter); }; @@ -165,7 +165,7 @@ Application.globalFilter = function (filter) { * @param {Object|Function} bf before fileter, bf(msg, session, next) * @memberOf Application */ -Application.globalBefore = function (bf) { + Application.globalBefore = function (bf) { addFilter(this, Constants.KEYWORDS.GLOBAL_BEFORE_FILTER, bf); }; @@ -175,7 +175,7 @@ Application.globalBefore = function (bf) { * @param {Object|Function} af after filter, `af(err, msg, session, resp, next)` * @memberOf Application */ -Application.globalAfter = function (af) { + Application.globalAfter = function (af) { addFilter(this, Constants.KEYWORDS.GLOBAL_AFTER_FILTER, af); }; @@ -185,7 +185,7 @@ Application.globalAfter = function (af) { * @param {Object|Function} bf before fileter, bf(serverId, msg, opts, next) * @memberOf Application */ -Application.rpcBefore = function(bf) { + Application.rpcBefore = function(bf) { addFilter(this, Constants.KEYWORDS.RPC_BEFORE_FILTER, bf); }; @@ -195,7 +195,7 @@ Application.rpcBefore = function(bf) { * @param {Object|Function} af after filter, `af(serverId, msg, opts, next)` * @memberOf Application */ -Application.rpcAfter = function(af) { + Application.rpcAfter = function(af) { addFilter(this, Constants.KEYWORDS.RPC_AFTER_FILTER, af); }; @@ -206,7 +206,7 @@ Application.rpcAfter = function(af) { * A filter should have two methods: before and after. * @memberOf Application */ -Application.rpcFilter = function(filter) { + Application.rpcFilter = function(filter) { this.rpcBefore(filter); this.rpcAfter(filter); }; @@ -220,7 +220,7 @@ Application.rpcFilter = function(filter) { * @return {Object} app instance for chain invoke * @memberOf Application */ -Application.load = function(name, component, opts) { + Application.load = function(name, component, opts) { if(typeof name !== 'string') { opts = component; component = name; @@ -262,35 +262,35 @@ Application.load = function(name, component, opts) { * @return {Server|Mixed} for chaining, or the setting value * @memberOf Application */ -Application.loadConfigBaseApp = function (key, val, reload) { + Application.loadConfigBaseApp = function (key, val, reload) { var self = this; var env = this.get(Constants.RESERVED.ENV); var originPath = path.join(Application.getBase(), val); var presentPath = path.join(Application.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(val)); var realPath; if(fs.existsSync(originPath)) { - realPath = originPath; - var file = require(originPath); - if (file[env]) { - file = file[env]; - } - this.set(key, file); - } else if(fs.existsSync(presentPath)) { - realPath = presentPath; - var pfile = require(presentPath); - this.set(key, pfile); - } else { - logger.error('invalid configuration with file path: %s', key); - } - - if(!!realPath && !!reload) { - fs.watch(realPath, function (event, filename) { - if(event === 'change') { - delete require.cache[require.resolve(realPath)] - self.loadConfigBaseApp(key, val); - } - }); - } + realPath = originPath; + var file = require(originPath); + if (file[env]) { + file = file[env]; + } + this.set(key, file); + } else if(fs.existsSync(presentPath)) { + realPath = presentPath; + var pfile = require(presentPath); + this.set(key, pfile); +} else { + logger.error('invalid configuration with file path: %s', key); +} + +if(!!realPath && !!reload) { + fs.watch(realPath, function (event, filename) { + if(event === 'change') { + delete require.cache[require.resolve(realPath)]; + self.loadConfigBaseApp(key, val); + } + }); +} }; /** @@ -301,7 +301,7 @@ Application.loadConfigBaseApp = function (key, val, reload) { * @return {Server|Mixed} for chaining, or the setting value * @memberOf Application */ -Application.loadConfig = function(key, val) { + Application.loadConfig = function(key, val) { var env = this.get(Constants.RESERVED.ENV); val = require(val); if (val[env]) { @@ -328,7 +328,7 @@ Application.loadConfig = function(key, val) { * @return {Object} current application instance for chain invoking * @memberOf Application */ -Application.route = function(serverType, routeFunc) { + Application.route = function(serverType, routeFunc) { var routes = this.get(Constants.KEYWORDS.ROUTE); if(!routes) { routes = {}; @@ -345,7 +345,7 @@ Application.route = function(serverType, routeFunc) { * @return {Void} * @memberOf Application */ -Application.beforeStopHook = function(fun) { + Application.beforeStopHook = function(fun) { logger.warn('this method was deprecated in pomelo 0.8'); if(!!fun && typeof fun === 'function') { this.set(Constants.KEYWORDS.BEFORE_STOP_HOOK, fun); @@ -366,26 +366,24 @@ Application.beforeStopHook = function(fun) { } var self = this; - appUtil.startByType(self, function() { - appUtil.loadDefaultComponents(self); - var startUp = function() { - appUtil.optComponents(self.loaded, Constants.RESERVED.START, function(err) { - self.state = STATE_START; - if(err) { - utils.invokeCallback(cb, err); - } else { - logger.info('%j enter after start...', self.getServerId()); - self.afterStart(cb); - } - }); - }; - var beforeFun = self.lifecycleCbs[Constants.LIFECYCLE.BEFORE_STARTUP]; - if(!!beforeFun) { - beforeFun.call(null, self, startUp); - } else { - startUp(); - } - }); + appUtil.loadDefaultComponents(self); + var startUp = function() { + appUtil.optComponents(self.loaded, Constants.RESERVED.START, function(err) { + self.state = STATE_START; + if(err) { + utils.invokeCallback(cb, err); + } else { + logger.info('%j enter after start...', self.getServerId()); + self.afterStart(cb); + } + }); + }; + var beforeFun = self.lifecycleCbs[Constants.LIFECYCLE.BEFORE_STARTUP]; + if(!!beforeFun) { + beforeFun.call(null, self, startUp); + } else { + startUp(); + } }; /** @@ -394,7 +392,7 @@ Application.beforeStopHook = function(fun) { * @param {Function} cb callback function * @return {Void} */ -Application.afterStart = function(cb) { + Application.afterStart = function(cb) { if(this.state !== STATE_START) { utils.invokeCallback(cb, new Error('application is not running now.')); return; @@ -426,7 +424,7 @@ Application.afterStart = function(cb) { * * @param {Boolean} force whether stop the app immediately */ -Application.stop = function(force) { + Application.stop = function(force) { if(this.state > STATE_STARTED) { logger.warn('[pomelo application] application is not running now.'); return; @@ -439,9 +437,9 @@ Application.stop = function(force) { }, Constants.TIME.TIME_WAIT_STOP); var cancelShutDownTimer =function(){ - if(!!self.stopTimer) { - clearTimeout(self.stopTimer); - } + if(!!self.stopTimer) { + clearTimeout(self.stopTimer); + } }; var shutDown = function() { appUtil.stopComps(self.loaded, 0, force, function() { @@ -481,7 +479,7 @@ Application.stop = function(force) { * @return {Server|Mixed} for chaining, or the setting value * @memberOf Application */ -Application.set = function (setting, val, attach) { + Application.set = function (setting, val, attach) { if (arguments.length === 1) { return this.settings[setting]; } @@ -499,7 +497,7 @@ Application.set = function (setting, val, attach) { * @return {String} val * @memberOf Application */ -Application.get = function (setting) { + Application.get = function (setting) { return this.settings[setting]; }; @@ -510,7 +508,7 @@ Application.get = function (setting) { * @return {Boolean} * @memberOf Application */ -Application.enabled = function (setting) { + Application.enabled = function (setting) { return !!this.get(setting); }; @@ -521,7 +519,7 @@ Application.enabled = function (setting) { * @return {Boolean} * @memberOf Application */ -Application.disabled = function (setting) { + Application.disabled = function (setting) { return !this.get(setting); }; @@ -532,7 +530,7 @@ Application.disabled = function (setting) { * @return {app} for chaining * @memberOf Application */ -Application.enable = function (setting) { + Application.enable = function (setting) { return this.set(setting, true); }; @@ -543,7 +541,7 @@ Application.enable = function (setting) { * @return {app} for chaining * @memberOf Application */ -Application.disable = function (setting) { + Application.disable = function (setting) { return this.set(setting, false); }; @@ -573,7 +571,7 @@ Application.disable = function (setting) { * @return {Application} for chaining * @memberOf Application */ -Application.configure = function (env, type, fn) { + Application.configure = function (env, type, fn) { var args = [].slice.call(arguments); fn = args.pop(); env = type = Constants.RESERVED.ALL; @@ -593,40 +591,6 @@ Application.configure = function (env, type, fn) { return this; }; -/** - * Register admin modules. Admin modules is the extends point of the monitor system. - * - * @param {String} module (optional) module id or provoided by module.moduleId - * @param {Object} module module object or factory function for module - * @param {Object} opts construct parameter for module - * @memberOf Application - */ -Application.registerAdmin = function(moduleId, module, opts) { - var modules = this.get(Constants.KEYWORDS.MODULE); - if(!modules) { - modules = {}; - this.set(Constants.KEYWORDS.MODULE, modules); - } - - if(typeof moduleId !== 'string') { - opts = module; - module = moduleId; - if(module) { - moduleId = module.moduleId; - } - } - - if(!moduleId){ - return; - } - - modules[moduleId] = { - moduleId: moduleId, - module: module, - opts: opts - }; -}; - /** * Use plugin. * @@ -634,7 +598,7 @@ Application.registerAdmin = function(moduleId, module, opts) { * @param {[type]} opts (optional) construct parameters for the factory function * @memberOf Application */ -Application.use = function(plugin, opts) { + Application.use = function(plugin, opts) { if(!plugin.components) { logger.error('invalid components, no components exist'); return; @@ -696,27 +660,17 @@ Application.use = function(plugin, opts) { * @param {Number} retry retry times to execute handlers if conditions are successfully executed * @memberOf Application */ -Application.transaction = function(name, conditions, handlers, retry) { + Application.transaction = function(name, conditions, handlers, retry) { appManager.transaction(name, conditions, handlers, retry); }; -/** - * Get master server info. - * - * @return {Object} master server info, {id, host, port} - * @memberOf Application - */ -Application.getMaster = function() { - return this.master; -}; - /** * Get current server info. * * @return {Object} current server info, {id, serverType, host, port} * @memberOf Application */ -Application.getCurServer = function() { + Application.getCurServer = function() { return this.curServer; }; @@ -726,7 +680,7 @@ Application.getCurServer = function() { * @return {String|Number} current server id from servers.json * @memberOf Application */ -Application.getServerId = function() { + Application.getServerId = function() { return this.serverId; }; @@ -736,7 +690,7 @@ Application.getServerId = function() { * @return {String|Number} current server type from servers.json * @memberOf Application */ -Application.getServerType = function() { + Application.getServerType = function() { return this.serverType; }; @@ -746,7 +700,7 @@ Application.getServerType = function() { * @return {Object} server info map, key: server id, value: server info * @memberOf Application */ -Application.getServers = function() { + Application.getServers = function() { return this.servers; }; @@ -756,7 +710,7 @@ Application.getServers = function() { * @return {Object} server info map, key: server id, value: server info * @memberOf Application */ -Application.getServersFromConfig = function() { + Application.getServersFromConfig = function() { return this.get(Constants.KEYWORDS.SERVER_MAP); }; @@ -766,7 +720,7 @@ Application.getServersFromConfig = function() { * @return {Array} server type list * @memberOf Application */ -Application.getServerTypes = function() { + Application.getServerTypes = function() { return this.serverTypes; }; @@ -777,7 +731,7 @@ Application.getServerTypes = function() { * @return {Object} server info or undefined * @memberOf Application */ -Application.getServerById = function(serverId) { + Application.getServerById = function(serverId) { return this.servers[serverId]; }; @@ -789,7 +743,7 @@ Application.getServerById = function(serverId) { * @memberOf Application */ -Application.getServerFromConfig = function(serverId) { + Application.getServerFromConfig = function(serverId) { return this.get(Constants.KEYWORDS.SERVER_MAP)[serverId]; }; @@ -800,7 +754,7 @@ Application.getServerFromConfig = function(serverId) { * @return {Array} server info list * @memberOf Application */ -Application.getServersByType = function(serverType) { + Application.getServersByType = function(serverType) { return this.serverTypeMaps[serverType]; }; @@ -813,7 +767,7 @@ Application.getServersByType = function(serverType) { * * @memberOf Application */ -Application.isFrontend = function(server) { + Application.isFrontend = function(server) { server = server || this.getCurServer(); return !!server && server.frontend === 'true'; }; @@ -826,90 +780,19 @@ Application.isFrontend = function(server) { * @return {Boolean} * @memberOf Application */ -Application.isBackend = function(server) { + Application.isBackend = function(server) { server = server || this.getCurServer(); return !!server && !server.frontend; }; -/** - * Check whether current server is a master server - * - * @return {Boolean} - * @memberOf Application - */ -Application.isMaster = function() { - return this.serverType === Constants.RESERVED.MASTER; -}; - -/** - * Add new server info to current application in runtime. - * - * @param {Array} servers new server info list - * @memberOf Application - */ -Application.addServers = function(servers) { - if(!servers || !servers.length) { - return; - } - - var item, slist; - for(var i=0, l=servers.length; i mnemonic */ diff --git a/lib/connectors/mqttconnector.js b/lib/connectors/mqttconnector.js index b5c26e4fc..4d8350e19 100644 --- a/lib/connectors/mqttconnector.js +++ b/lib/connectors/mqttconnector.js @@ -1,3 +1,4 @@ +'use strict'; var util = require('util'); var EventEmitter = require('events').EventEmitter; var mqtt = require('mqtt'); diff --git a/lib/connectors/mqttsocket.js b/lib/connectors/mqttsocket.js index 41698fd22..f15ce7190 100644 --- a/lib/connectors/mqttsocket.js +++ b/lib/connectors/mqttsocket.js @@ -1,3 +1,4 @@ +'use strict'; var util = require('util'); var EventEmitter = require('events').EventEmitter; diff --git a/lib/connectors/sioconnector.js b/lib/connectors/sioconnector.js index 4a760d36f..6746c8895 100644 --- a/lib/connectors/sioconnector.js +++ b/lib/connectors/sioconnector.js @@ -1,3 +1,4 @@ +'use strict'; var util = require('util'); var EventEmitter = require('events').EventEmitter; var sio = require('socket.io'); @@ -22,7 +23,6 @@ var Connector = function(port, host, opts) { this.port = port; this.host = host; this.opts = opts; - this.heartbeats = opts.heartbeats || true; this.closeTimeout = opts.closeTimeout || 60; this.heartbeatTimeout = opts.heartbeatTimeout || 60; this.heartbeatInterval = opts.heartbeatInterval || 25; @@ -44,15 +44,12 @@ Connector.prototype.start = function(cb) { else { this.wsocket = sio.listen(this.port, { transports: [ - 'websocket', 'htmlfile', 'xhr-polling', 'jsonp-polling', 'flashsocket' + 'websocket', 'polling' ] }); } - this.wsocket.set('close timeout', this.closeTimeout); - this.wsocket.set('heartbeat timeout', this.heartbeatTimeout); - this.wsocket.set('heartbeat interval', this.heartbeatInterval); - this.wsocket.set('heartbeats', this.heartbeats); - this.wsocket.set('log level', 1); + this.wsocket.set('heartbeat timeout', this.heartbeatTimeout * 1000); + this.wsocket.set('heartbeat interval', this.heartbeatInterval * 1000); this.wsocket.sockets.on('connection', function (socket) { var siosocket = new SioSocket(curId++, socket); self.emit('connection', siosocket); diff --git a/lib/connectors/siosocket.js b/lib/connectors/siosocket.js index d95d3ea1d..70d41baa8 100644 --- a/lib/connectors/siosocket.js +++ b/lib/connectors/siosocket.js @@ -1,3 +1,4 @@ +'use strict'; var util = require('util'); var EventEmitter = require('events').EventEmitter; diff --git a/lib/connectors/udpconnector.js b/lib/connectors/udpconnector.js index 6ab99076a..5a5814190 100644 --- a/lib/connectors/udpconnector.js +++ b/lib/connectors/udpconnector.js @@ -1,3 +1,4 @@ +'use strict'; var net = require('net'); var util = require('util'); var dgram = require("dgram"); @@ -74,7 +75,7 @@ Connector.prototype.start = function(cb) { }); this.socket.on('error', function(err) { - logger.error('udp socket encounters with error: %j', err.stack); + //logger.error('udp socket encounters with error: %j', err.stack); return; }); @@ -94,4 +95,4 @@ Connector.prototype.stop = function(force, cb) { var genKey = function(peer) { return peer.address + ":" + peer.port; -}; \ No newline at end of file +}; diff --git a/lib/connectors/udpsocket.js b/lib/connectors/udpsocket.js index 769f5ae8c..8e64d1386 100644 --- a/lib/connectors/udpsocket.js +++ b/lib/connectors/udpsocket.js @@ -1,3 +1,4 @@ +'use strict'; var util = require('util'); var handler = require('./common/handler'); var protocol = require('pomelo-protocol'); diff --git a/lib/filters/handler/serial.js b/lib/filters/handler/serial.js index 8044721a3..f443796e7 100644 --- a/lib/filters/handler/serial.js +++ b/lib/filters/handler/serial.js @@ -1,3 +1,4 @@ +'use strict'; /** * Filter to keep request sequence. */ @@ -9,7 +10,7 @@ module.exports = function(timeout) { }; var Filter = function(timeout) { - this.timeout = timeout + this.timeout = timeout; }; /** diff --git a/lib/filters/handler/time.js b/lib/filters/handler/time.js index e4b8e4037..c8132ccb1 100644 --- a/lib/filters/handler/time.js +++ b/lib/filters/handler/time.js @@ -1,3 +1,4 @@ +'use strict'; /** * Filter for statistics. * Record used time for each request. diff --git a/lib/filters/handler/timeout.js b/lib/filters/handler/timeout.js index a8c7a6d50..e9f9e3d92 100644 --- a/lib/filters/handler/timeout.js +++ b/lib/filters/handler/timeout.js @@ -1,3 +1,4 @@ +'use strict'; /** * Filter for timeout. * Print a warn information when request timeout. diff --git a/lib/filters/handler/toobusy.js b/lib/filters/handler/toobusy.js index 8b243c72b..8c1fd3c5c 100644 --- a/lib/filters/handler/toobusy.js +++ b/lib/filters/handler/toobusy.js @@ -1,3 +1,4 @@ +'use strict'; /** * Filter for toobusy. * if the process is toobusy, just skip the new request diff --git a/lib/filters/rpc/rpcLog.js b/lib/filters/rpc/rpcLog.js index 15b85e3d0..a370ac657 100644 --- a/lib/filters/rpc/rpcLog.js +++ b/lib/filters/rpc/rpcLog.js @@ -1,3 +1,4 @@ +'use strict'; /** * Filter for rpc log. * Record used time for remote process call. diff --git a/lib/filters/rpc/toobusy.js b/lib/filters/rpc/toobusy.js index 9608b44d9..245cf25a9 100644 --- a/lib/filters/rpc/toobusy.js +++ b/lib/filters/rpc/toobusy.js @@ -1,3 +1,4 @@ +'use strict'; /** * Filter for rpc log. * Reject rpc request when toobusy diff --git a/lib/index.js b/lib/index.js index d077a1038..dfac0a1cd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1 +1,2 @@ +'use strict'; module.exports = require('./pomelo'); \ No newline at end of file diff --git a/lib/master/master.js b/lib/master/master.js deleted file mode 100644 index d5005d518..000000000 --- a/lib/master/master.js +++ /dev/null @@ -1,126 +0,0 @@ -var starter = require('./starter'); -var logger = require('pomelo-logger').getLogger('pomelo', __filename); -var crashLogger = require('pomelo-logger').getLogger('crash-log', __filename); -var adminLogger = require('pomelo-logger').getLogger('admin-log', __filename); -var admin = require('pomelo-admin'); -var util = require('util'); -var utils = require('../util/utils'); -var moduleUtil = require('../util/moduleUtil'); -var Constants = require('../util/constants'); - -var Server = function(app, opts) { - this.app = app; - this.masterInfo = app.getMaster(); - this.registered = {}; - this.modules = []; - opts = opts || {}; - - opts.port = this.masterInfo.port; - opts.env = this.app.get(Constants.RESERVED.ENV); - this.closeWatcher = opts.closeWatcher; - this.masterConsole = admin.createMasterConsole(opts); -}; - -module.exports = Server; - -Server.prototype.start = function(cb) { - moduleUtil.registerDefaultModules(true, this.app, this.closeWatcher); - moduleUtil.loadModules(this, this.masterConsole); - - var self = this; - // start master console - this.masterConsole.start(function(err) { - if(err) { - process.exit(0); - } - moduleUtil.startModules(self.modules, function(err) { - if(err) { - utils.invokeCallback(cb, err); - return; - } - - if(self.app.get(Constants.RESERVED.MODE) !== Constants.RESERVED.STAND_ALONE) { - starter.runServers(self.app); - } - utils.invokeCallback(cb); - }); - }); - - this.masterConsole.on('error', function(err) { - if(!!err) { - logger.error('masterConsole encounters with error: ' + err.stack); - return; - } - }); - - this.masterConsole.on('reconnect', function(info){ - self.app.addServers([info]); - }); - - // monitor servers disconnect event - this.masterConsole.on('disconnect', function(id, type, info, reason) { - crashLogger.info(util.format('[%s],[%s],[%s],[%s]', type, id, Date.now(), reason || 'disconnect')); - var count = 0; - var time = 0; - var pingTimer = null; - var server = self.app.getServerById(id); - var stopFlags = self.app.get(Constants.RESERVED.STOP_SERVERS) || []; - if(!!server && (server[Constants.RESERVED.AUTO_RESTART] === 'true' || server[Constants.RESERVED.RESTART_FORCE] === 'true') && stopFlags.indexOf(id) < 0) { - var setTimer = function(time) { - pingTimer = setTimeout(function() { - utils.ping(server.host, function(flag) { - if(flag) { - handle(); - } else { - count++; - if(count > 3) { - time = Constants.TIME.TIME_WAIT_MAX_PING; - } else { - time = Constants.TIME.TIME_WAIT_PING * count; - } - setTimer(time); - } - }); - }, time); - }; - setTimer(time); - var handle = function() { - clearTimeout(pingTimer); - utils.checkPort(server, function(status) { - if(status === 'error') { - utils.invokeCallback(cb, new Error('Check port command executed with error.')); - return; - } else if(status === 'busy') { - if(!!server[Constants.RESERVED.RESTART_FORCE]) { - starter.kill([info.pid], [server]); - } else { - utils.invokeCallback(cb, new Error('Port occupied already, check your server to add.')); - return; - } - } - setTimeout(function() { - starter.run(self.app, server, null); - }, Constants.TIME.TIME_WAIT_STOP); - }); - }; - } - }); - - // monitor servers register event - this.masterConsole.on('register', function(record) { - starter.bindCpu(record.id, record.pid, record.host); - }); - - this.masterConsole.on('admin-log', function(log, error) { - if(error) { - adminLogger.error(JSON.stringify(log)); - } else { - adminLogger.info(JSON.stringify(log)); - } - }); -}; - -Server.prototype.stop = function(cb) { - this.masterConsole.stop(); - process.nextTick(cb); -}; diff --git a/lib/master/starter.js b/lib/master/starter.js deleted file mode 100644 index f23bc3647..000000000 --- a/lib/master/starter.js +++ /dev/null @@ -1,220 +0,0 @@ -var cp = require('child_process'); -var logger = require('pomelo-logger').getLogger('pomelo', __filename); -var starter = module.exports; -var util = require('util'); -var utils = require('../util/utils'); -var Constants = require('../util/constants'); -var env = Constants.RESERVED.ENV_DEV; -var os=require('os'); -var cpus = {}; -var pomelo = require('../pomelo'); - -/** - * Run all servers - * - * @param {Object} app current application context - * @return {Void} - */ - starter.runServers = function(app) { - var server, servers; - var condition = app.startId || app.type; - switch(condition) { - case Constants.RESERVED.MASTER: - break; - case Constants.RESERVED.ALL: - servers = app.getServersFromConfig(); - for (var serverId in servers) { - this.run(app, servers[serverId]); - } - break; - default: - server = app.getServerFromConfig(condition); - if(!!server) { - this.run(app, server); - } else { - servers = app.get(Constants.RESERVED.SERVERS)[condition]; - for(var i=0; i - * MIT Licensed - */ -var logger = require('pomelo-logger').getLogger('pomelo', __filename); -var countDownLatch = require('../util/countDownLatch'); -var utils = require('../util/utils'); -var Constants = require('../util/constants'); -var starter = require('../master/starter'); -var exec = require('child_process').exec; - -module.exports = function(opts) { - return new Module(opts); -}; - -module.exports.moduleId = '__console__'; - -var Module = function(opts) { - opts = opts || {}; - this.app = opts.app; - this.starter = opts.starter; -}; - -Module.prototype.monitorHandler = function(agent, msg, cb) { - var serverId = agent.id; - switch(msg.signal) { - case 'stop': - if(agent.type === Constants.RESERVED.MASTER) { - return; - } - this.app.stop(true); - break; - case 'list': - var serverType = agent.type; - var pid = process.pid; - var heapUsed = (process.memoryUsage().heapUsed/(1024 * 1024)).toFixed(2); - var rss = (process.memoryUsage().rss/(1024 * 1024)).toFixed(2); - var heapTotal = (process.memoryUsage().heapTotal/(1024 * 1024)).toFixed(2); - var uptime = (process.uptime()/60).toFixed(2); - utils.invokeCallback(cb, { - serverId: serverId, - body: {serverId:serverId, serverType: serverType, pid:pid, rss: rss, heapTotal: heapTotal, heapUsed:heapUsed, uptime:uptime} - }); - break; - case 'kill': - utils.invokeCallback(cb, serverId); - if (agent.type !== 'master') { - setTimeout(function() { - process.exit(-1); - }, Constants.TIME.TIME_WAIT_MONITOR_KILL); - } - break; - case 'addCron': - this.app.addCrons([msg.cron]); - break; - case 'removeCron': - this.app.removeCrons([msg.cron]); - break; - case 'blacklist': - if(this.app.isFrontend()) { - var connector = this.app.components.__connector__; - connector.blacklist = connector.blacklist.concat(msg.blacklist); - } - break; - case 'restart': - if(agent.type === Constants.RESERVED.MASTER) { - return; - } - var self = this; - var server = this.app.get(Constants.RESERVED.CURRENT_SERVER); - utils.invokeCallback(cb, server); - process.nextTick(function() { - self.app.stop(true); - }); - break; - default: - logger.error('receive error signal: %j', msg); - break; - } -}; - -Module.prototype.clientHandler = function(agent, msg, cb) { - var app = this.app; - switch(msg.signal) { - case 'kill': - kill(app, agent, msg, cb); - break; - case 'stop': - stop(app, agent, msg, cb); - break; - case 'list': - list(agent, msg, cb); - break; - case 'add': - add(app, msg, cb); - break; - case 'addCron': - addCron(app, agent, msg, cb); - break; - case 'removeCron': - removeCron(app, agent, msg, cb); - break; - case 'blacklist': - blacklist(agent, msg, cb); - break; - case 'restart': - restart(app, agent, msg, cb); - break; - default: - utils.invokeCallback(cb, new Error('The command cannot be recognized, please check.'), null); - break; - } -}; - -var kill = function(app, agent, msg, cb) { - var sid, record; - var serverIds = []; - var count = utils.size(agent.idMap); - var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_MASTER_KILL}, function(isTimeout) { - if (!isTimeout) { - utils.invokeCallback(cb, null, {code: 'ok'}); - } else { - utils.invokeCallback(cb, null, {code: 'remained', serverIds: serverIds}); - } - setTimeout(function() { - process.exit(-1); - }, Constants.TIME.TIME_WAIT_MONITOR_KILL); - }); - - var agentRequestCallback = function(msg) { - for (var i = 0; i < serverIds.length; ++i) { - if (serverIds[i] === msg) { - serverIds.splice(i,1); - latch.done(); - break; - } - } - }; - - for(sid in agent.idMap) { - record = agent.idMap[sid]; - serverIds.push(record.id); - agent.request(record.id, module.exports.moduleId, { signal: msg.signal }, agentRequestCallback); - } -}; - -var stop = function(app, agent, msg, cb) { - var serverIds = msg.ids; - if(!!serverIds.length) { - var servers = app.getServers(); - app.set(Constants.RESERVED.STOP_SERVERS, serverIds); - for(var i=0; i= 0; i--) { + var server = JSON.parse(res[i]); + results[server.id] = server; + } + + logger.info('cluster servers info: %j',results); + app.replaceServers(results); + }); + }); +}; + +var getCommand = function(self, app, redis, serverInfo) { + var key = constants.RESERVED.REDIS_REG_PREFIX + app.env + ':' + serverInfo.id; + redis.get(key, function(err, res) { + if(err) { + logger.error('get pomelo-regist cmd err %j', err); + return; + } + + if(res) { + logger.debug('get cmd: ', res); + redis.del(key, function(err) { + if(err) { + logger.error('del command err %j', err); + }else { + commander.init(self, res); + } + }); + } + }); +}; + +var getMaster = function(self, redisNodes) { + logger.info("[redisMonitor] get master"); + var redis = self.client; + var clients = redisNodes.redis; + for(var i=0; i= 0; i--) { + // fetch any missing server + var serverId = res[i]; + var lastServerInfo = self.lastResults[serverId] + if (lastServerInfo) { + results[serverId] = lastServerInfo; + } + else { + missingServersKeys.push(constants.RESERVED.REDIS_REG_SERVER_PREFIX + app.env + ":" +serverId); + } + } + if (missingServersKeys.length > 0) { + // fetch missing servers info first + if (self.maxServerInfoBatch) { + missingServersKeys = missingServersKeys.slice(0, self.maxServerInfoBatch); + } + redis.mget(missingServersKeys, function(err, res) { + if(err) { + logger.error('mget %j err: %j', query_args, err); + return; + } + for (var i = res.length - 1; i >= 0; i--) { + if (res[i]) { + var server = JSON.parse(res[i]); + results[server.id] = server; + } + } + logger.debug('cluster servers info: %j',results); + self.lastResults = results; + app.replaceServers(results); + }); + } + else { + logger.debug('cluster servers info: %j',results); + self.lastResults = results; + app.replaceServers(results); + } + }); + }); +}; + +var updateServerInfo = function(self, app, redis, serverInfo) { + var key = constants.RESERVED.REDIS_REG_SERVER_PREFIX + app.env + ":" +serverInfo.id; + serverInfo.pid = process.pid; + var args = [key, self.updateInfoExpire, JSON.stringify(serverInfo)]; + redis.setex(args, function(err, res) { + if(err) { + logger.error('setex %j err: %j', args, err); + return; + } + logger.debug('updated server info'); + }); +} + +var getCommand = function(self, app, redis, serverInfo) { + var key = constants.RESERVED.REDIS_REG_PREFIX + app.env + ':' + serverInfo.id; + redis.get(key, function(err, res) { + if(err) { + logger.error('get pomelo-regist cmd err %j', err); + return; + } + + if(res) { + logger.debug('get cmd: ', res); + redis.del(key, function(err) { + if(err) { + logger.error('del command err %j', err); + }else { + commander.init(self, res); + } + }); + } + }); +}; + +var getMaster = function(self, redisNodes) { + logger.info("[redisMonitor] get master"); + var redis = self.client; + var clients = redisNodes.redis; + for(var i=0; i @@ -62,7 +63,13 @@ Pomelo.pushSchedulers = {}; Pomelo.pushSchedulers.__defineGetter__('direct', load.bind(null, './pushSchedulers/direct')); Pomelo.pushSchedulers.__defineGetter__('buffer', load.bind(null, './pushSchedulers/buffer')); -var self = this; +/** + * monitors + */ +Pomelo.monitors = {}; +Pomelo.monitors.__defineGetter__('zookeepermonitor', load.bind(null, './monitors/zookeepermonitor')); +Pomelo.monitors.__defineGetter__('redismonitor', load.bind(null, './monitors/redismonitor')); +Pomelo.monitors.__defineGetter__('redismonitorlight', load.bind(null, './monitors/redismonitorlight')); /** * Create an pomelo application. @@ -74,19 +81,19 @@ var self = this; Pomelo.createApp = function (opts) { var app = application; app.init(opts); - self.app = app; + Pomelo.app = app; return app; }; /** * Get application */ -Object.defineProperty(Pomelo, 'app', { +/*Object.defineProperty(Pomelo, 'app', { get:function () { - return self.app; + return abc.app; } }); - +*/ /** * Auto-load bundled components with getters. */ @@ -96,7 +103,7 @@ fs.readdirSync(__dirname + '/components').forEach(function (filename) { } var name = path.basename(filename, '.js'); var _load = load.bind(null, './components/', name); - + Pomelo.components.__defineGetter__(name, _load); Pomelo.__defineGetter__(name, _load); }); @@ -107,7 +114,7 @@ fs.readdirSync(__dirname + '/filters/handler').forEach(function (filename) { } var name = path.basename(filename, '.js'); var _load = load.bind(null, './filters/handler/', name); - + Pomelo.filters.__defineGetter__(name, _load); Pomelo.__defineGetter__(name, _load); }); @@ -118,7 +125,7 @@ fs.readdirSync(__dirname + '/filters/rpc').forEach(function (filename) { } var name = path.basename(filename, '.js'); var _load = load.bind(null, './filters/rpc/', name); - + Pomelo.rpcFilters.__defineGetter__(name, _load); }); diff --git a/lib/pushSchedulers/buffer.js b/lib/pushSchedulers/buffer.js index 8f2187335..86fbdaf55 100644 --- a/lib/pushSchedulers/buffer.js +++ b/lib/pushSchedulers/buffer.js @@ -1,3 +1,4 @@ +'use strict'; var utils = require('../util/utils'); var DEFAULT_FLUSH_INTERVAL = 20; diff --git a/lib/pushSchedulers/direct.js b/lib/pushSchedulers/direct.js index 6f2dd72f0..701cc9629 100644 --- a/lib/pushSchedulers/direct.js +++ b/lib/pushSchedulers/direct.js @@ -1,3 +1,4 @@ +'use strict'; var utils = require('../util/utils'); var Service = function(app, opts) { diff --git a/lib/server/server.js b/lib/server/server.js index f58454d48..b43e2f3d2 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -1,3 +1,4 @@ +'use strict'; /** * Implementation of server component. * Init and start server instance. diff --git a/lib/util/appUtil.js b/lib/util/appUtil.js index 497e5b5a9..2e39d31a7 100644 --- a/lib/util/appUtil.js +++ b/lib/util/appUtil.js @@ -1,10 +1,11 @@ +'use strict'; var async = require('async'); var log = require('./log'); var utils = require('./utils'); var path = require('path'); var fs = require('fs'); +var uuid = require('node-uuid'); var Constants = require('./constants'); -var starter = require('../master/starter'); var logger = require('pomelo-logger').getLogger('pomelo', __filename); /** @@ -13,49 +14,25 @@ var logger = require('pomelo-logger').getLogger('pomelo', __filename); module.exports.defaultConfiguration = function(app) { var args = parseArgs(process.argv); setupEnv(app, args); - loadMaster(app); loadServers(app); processArgs(app, args); configLogger(app); loadLifecycle(app); }; -/** - * Start servers by type. - */ -module.exports.startByType = function(app, cb) { - if(!!app.startId) { - if(app.startId === Constants.RESERVED.MASTER) { - utils.invokeCallback(cb); - } else { - starter.runServers(app); - } - } else { - if(!!app.type && app.type !== Constants.RESERVED.ALL && app.type !== Constants.RESERVED.MASTER) { - starter.runServers(app); - } else { - utils.invokeCallback(cb); - } - } -}; - /** * Load default components for application. */ -module.exports.loadDefaultComponents = function(app) { + module.exports.loadDefaultComponents = function(app) { var pomelo = require('../pomelo'); // load system default components - if (app.serverType === Constants.RESERVED.MASTER) { - app.load(pomelo.master, app.get('masterConfig')); - } else { - app.load(pomelo.proxy, app.get('proxyConfig')); - if (app.getCurServer().port) { - app.load(pomelo.remote, app.get('remoteConfig')); - } - if (app.isFrontend()) { - app.load(pomelo.connection, app.get('connectionConfig')); - app.load(pomelo.connector, app.get('connectorConfig')); - app.load(pomelo.session, app.get('sessionConfig')); + if (app.getCurServer().port) { + app.load(pomelo.remote, app.get('remoteConfig')); + } + if (app.isFrontend()) { + app.load(pomelo.connection, app.get('connectionConfig')); + app.load(pomelo.connector, app.get('connectorConfig')); + app.load(pomelo.session, app.get('sessionConfig')); // compatible for schedulerConfig if(app.get('schedulerConfig')) { app.load(pomelo.pushScheduler, app.get('schedulerConfig')); @@ -63,12 +40,12 @@ module.exports.loadDefaultComponents = function(app) { app.load(pomelo.pushScheduler, app.get('pushSchedulerConfig')); } } + app.load(pomelo.proxy, app.get('proxyConfig')); app.load(pomelo.backendSession, app.get('backendSessionConfig')); app.load(pomelo.channel, app.get('channelConfig')); app.load(pomelo.server, app.get('serverConfig')); - } - app.load(pomelo.monitor, app.get('monitorConfig')); -}; + app.load(pomelo.monitor, app.get('monitorConfig')); + }; /** * Stop components. @@ -149,42 +126,21 @@ var loadServers = function(app) { app.set(Constants.KEYWORDS.SERVER_MAP, serverMap); }; -/** - * Load master info from config/master.json. - */ -var loadMaster = function(app) { - app.loadConfigBaseApp(Constants.RESERVED.MASTER, Constants.FILEPATH.MASTER); - app.master = app.get(Constants.RESERVED.MASTER); -}; - /** * Process server start command */ var processArgs = function(app, args) { - var serverType = args.serverType || Constants.RESERVED.MASTER; - var serverId = args.id || app.getMaster().id; - var mode = args.mode || Constants.RESERVED.CLUSTER; - var masterha = args.masterha || 'false'; - var type = args.type || Constants.RESERVED.ALL; - var startId = args.startId; + var server = args || {}; + var serverId = args.id || uuid.v1(); + var serverType = args.serverType || Constants.RESERVED.DEFAULT_SERVERTYPE; + server.serverId = serverId; + server.id = serverId; + server.serverType = serverType; app.set(Constants.RESERVED.MAIN, args.main, true); app.set(Constants.RESERVED.SERVER_TYPE, serverType, true); app.set(Constants.RESERVED.SERVER_ID, serverId, true); - app.set(Constants.RESERVED.MODE, mode, true); - app.set(Constants.RESERVED.TYPE, type, true); - if(!!startId) { - app.set(Constants.RESERVED.STARTID, startId, true); - } - - if (masterha === 'true') { - app.master = args; - app.set(Constants.RESERVED.CURRENT_SERVER, args, true); - } else if (serverType !== Constants.RESERVED.MASTER) { - app.set(Constants.RESERVED.CURRENT_SERVER, args, true); - } else { - app.set(Constants.RESERVED.CURRENT_SERVER, app.getMaster(), true); - } + app.set(Constants.RESERVED.CURRENT_SERVER, server, true); }; /** @@ -259,4 +215,4 @@ var loadLifecycle = function(app) { logger.warn('lifecycle.js in %s is error format.', filePath); } } -}; +}; \ No newline at end of file diff --git a/lib/util/constants.js b/lib/util/constants.js index 99987e45b..9a7646193 100644 --- a/lib/util/constants.js +++ b/lib/util/constants.js @@ -1,3 +1,4 @@ +'use strict'; module.exports = { KEYWORDS: { BEFORE_FILTER: '__befores__', @@ -15,7 +16,7 @@ module.exports = { }, FILEPATH: { - MASTER: '/config/master.json', + MONITOR: '/config/monitorConfig.json', SERVER: '/config/servers.json', CRON: '/config/crons.json', LOG: '/config/log4js.json', @@ -40,7 +41,7 @@ module.exports = { RESERVED: { BASE: 'base', MAIN: 'main', - MASTER: 'master', + MONITOR: 'monitorConfig', SERVERS: 'servers', ENV: 'env', CPU: 'cpu', @@ -69,7 +70,17 @@ module.exports = { CLIENT: 'client', STARTID: 'startId', STOP_SERVERS: 'stop_servers', - SSH_CONFIG_PARAMS: 'ssh_config_params' + SSH_CONFIG_PARAMS: 'ssh_config_params', + DEFAULT_ROOT: '/pomelo-cluster', + ZK_NODE_SEP: '::', + ZK_NODE_COMMAND: 'cmd::', + STOP_FLAG: 'stop_by_cmd', + ZK: 'zookeeper', + REDIS: 'redis', + REDIS_REG_PREFIX: 'pomelo-regist:', + REDIS_REG_RES_PREFIX: 'pomelo-regist-result:', + REDIS_REG_SERVER_PREFIX: 'pomelo-server:', + DEFAULT_SERVERTYPE: 'pomelo-server' }, COMMAND: { @@ -107,6 +118,20 @@ module.exports = { TIME_WAIT_MAX_PING: 5 * 60 * 1000, DEFAULT_UDP_HEARTBEAT_TIME: 20 * 1000, DEFAULT_UDP_HEARTBEAT_TIMEOUT: 100 * 1000, - DEFAULT_MQTT_HEARTBEAT_TIMEOUT: 90 * 1000 + DEFAULT_MQTT_HEARTBEAT_TIMEOUT: 90 * 1000, + DEFAULT_ZK_CONNECT_TIMEOUT: 15 * 1000, + DEFAULT_ZK_TIMEOUT: 5 * 1000, + DEFAULT_SPIN_DELAY: 1000, + DEFAULT_REDIS_CONNECT_TIMEOUT: 15 * 1000, + DEFAULT_REDIS_REG: 5 * 1000, + DEFAULT_REDIS_REG_UPDATE_INFO: 600 * 1000, + DEFAULT_REDIS_EXPIRE: 15 * 1000, + DEFAULT_REDIS_PING_TIMEOUT: 15 * 1000, + DEFAULT_REDIS_PING: 60 * 1000 + }, + + RETRY: { + CONNECT_RETRY: 3, + RECONNECT_RETRY: 10000 } -}; \ No newline at end of file +}; diff --git a/lib/util/countDownLatch.js b/lib/util/countDownLatch.js index 101048db1..3ad2c45c7 100644 --- a/lib/util/countDownLatch.js +++ b/lib/util/countDownLatch.js @@ -1,3 +1,4 @@ +'use strict'; var exp = module.exports; /** diff --git a/lib/util/events.js b/lib/util/events.js index 75ccc685a..1184fc350 100644 --- a/lib/util/events.js +++ b/lib/util/events.js @@ -1,3 +1,4 @@ +'use strict'; module.exports = { ADD_SERVERS: 'add_servers', REMOVE_SERVERS: 'remove_servers', diff --git a/lib/util/log.js b/lib/util/log.js index 98706ec30..028b3f079 100644 --- a/lib/util/log.js +++ b/lib/util/log.js @@ -1,3 +1,4 @@ +'use strict'; var logger = require('pomelo-logger'); /** diff --git a/lib/util/moduleUtil.js b/lib/util/moduleUtil.js deleted file mode 100644 index 65c6510dd..000000000 --- a/lib/util/moduleUtil.js +++ /dev/null @@ -1,96 +0,0 @@ -var os = require('os'); -var admin = require('pomelo-admin'); -var utils = require('./utils'); -var Constants = require('./constants'); -var pathUtil = require('./pathUtil'); -var starter = require('../master/starter'); -var logger = require('pomelo-logger').getLogger('pomelo', __filename); -var pro = module.exports; - -/** - * Load admin modules - */ -pro.loadModules = function(self, consoleService) { - // load app register modules - var _modules = self.app.get(Constants.KEYWORDS.MODULE); - - if(!_modules) { - return; - } - - var modules = []; - for(var m in _modules){ - modules.push(_modules[m]); - } - - var record, moduleId, module; - for(var i=0, l=modules.length; i= modules.length) { - utils.invokeCallback(cb, err); - return; - } - - var module = modules[index]; - if(module && typeof module.start === 'function') { - module.start(function(err) { - startModule(err, modules, index + 1, cb); - }); - } else { - startModule(err, modules, index + 1, cb); - } -}; diff --git a/lib/util/pathUtil.js b/lib/util/pathUtil.js index 16a29e404..633b78d19 100644 --- a/lib/util/pathUtil.js +++ b/lib/util/pathUtil.js @@ -1,3 +1,4 @@ +'use strict'; var fs = require('fs'); var path = require('path'); var Constants = require('./constants'); diff --git a/lib/util/utils.js b/lib/util/utils.js index 943d94138..cf224cd5e 100644 --- a/lib/util/utils.js +++ b/lib/util/utils.js @@ -1,3 +1,4 @@ +'use strict'; var os = require('os'); var util = require('util'); var exec = require('child_process').exec; diff --git a/package.json b/package.json index af27bb817..dfbd15c3f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pomelo", - "version": "1.1.9", + "version": "2.0.0", "private": false, "homepage": "https://github.com/NetEase/pomelo", "repository": { @@ -28,23 +28,26 @@ "server" ], "dependencies": { - "socket.io": "0.9.16", + "socket.io": "1.3.7", "async": "0.2.5", "seq-queue": "0.0.5", "crc": "0.2.0", "cliff": "0.1.8", "mkdirp": "0.3.3", - "pomelo-loader": "0.0.6", - "pomelo-rpc": "0.4.10", + "pomelo-loader": "0.0.7", + "pomelo-rpc": "1.0.3", "pomelo-protocol": "0.1.6", - "pomelo-admin": "0.4.4", "pomelo-logger": "0.1.7", "pomelo-scheduler": "0.3.9", - "ws": "0.4.25", + "ws": "0.8.0", "pomelo-protobuf": "0.4.0", "node-bignumber": "1.2.1", "commander": "2.0.0", - "mqtt": "0.3.9" + "mqtt": "0.3.9", + "node-zookeeper-client": "0.2.2", + "node-uuid": "1.4.3", + "ioredis": "1.10.0", + "redis": "0.12.1" }, "bin": { "pomelo": "./bin/pomelo" diff --git a/template/game-server/app.js b/template/game-server/app.js index 1a4e0e7d0..4e382d5cb 100644 --- a/template/game-server/app.js +++ b/template/game-server/app.js @@ -6,6 +6,15 @@ var pomelo = require('pomelo'); var app = pomelo.createApp(); app.set('name', '$'); +// configure monitor +app.configure('production|development', function(){ + app.set('monitorConfig', + { + monitor : pomelo.monitors.zookeepermonitor, + servers: "127.0.0.1:2181" + }); +}); + // app configuration app.configure('production|development', 'connector', function(){ app.set('connectorConfig', diff --git a/template/game-server/app.js.mqtt b/template/game-server/app.js.mqtt index 2d1a6ccd8..4070ba2cc 100644 --- a/template/game-server/app.js.mqtt +++ b/template/game-server/app.js.mqtt @@ -6,6 +6,15 @@ var pomelo = require('pomelo'); var app = pomelo.createApp(); app.set('name', '$'); +// configure monitor +app.configure('production|development', function(){ + app.set('monitorConfig', + { + monitor : pomelo.monitors.zookeepermonitor, + servers: "127.0.0.1:2181" + }); +}); + // app configuration app.configure('production|development', 'connector', function(){ app.set('connectorConfig', diff --git a/template/game-server/app.js.sio b/template/game-server/app.js.sio index fc3529771..b95c7009d 100644 --- a/template/game-server/app.js.sio +++ b/template/game-server/app.js.sio @@ -6,15 +6,22 @@ var pomelo = require('pomelo'); var app = pomelo.createApp(); app.set('name', '$'); +// configure monitor +app.configure('production|development', function(){ + app.set('monitorConfig', + { + monitor : pomelo.monitors.zookeepermonitor, + servers: "127.0.0.1:2181" + }); +}); + // app configuration app.configure('production|development', 'connector', function(){ app.set('connectorConfig', { connector : pomelo.connectors.sioconnector, - //websocket, htmlfile, xhr-polling, jsonp-polling, flashsocket + //websocket, polling transports : ['websocket'], - heartbeats : true, - closeTimeout : 60, heartbeatTimeout : 60, heartbeatInterval : 25 }); diff --git a/template/game-server/app.js.sio.wss b/template/game-server/app.js.sio.wss index fb14c07d4..288c1ffca 100644 --- a/template/game-server/app.js.sio.wss +++ b/template/game-server/app.js.sio.wss @@ -7,11 +7,22 @@ var pomelo = require('pomelo'); var app = pomelo.createApp(); app.set('name', '$'); +// configure monitor +app.configure('production|development', function(){ + app.set('monitorConfig', + { + monitor : pomelo.monitors.zookeepermonitor, + servers: "127.0.0.1:2181" + }); +}); + // app configuration app.configure('production|development', function(){ app.set('connectorConfig', { connector : pomelo.connectors.sioconnector, + //websocket, polling + transports : ['websocket'], key: fs.readFileSync('../shared/server.key'), cert: fs.readFileSync('../shared/server.crt') }); diff --git a/template/game-server/app.js.udp b/template/game-server/app.js.udp index 99b2b3be6..59accb3ec 100644 --- a/template/game-server/app.js.udp +++ b/template/game-server/app.js.udp @@ -6,6 +6,15 @@ var pomelo = require('pomelo'); var app = pomelo.createApp(); app.set('name', '$'); +// configure monitor +app.configure('production|development', function(){ + app.set('monitorConfig', + { + monitor : pomelo.monitors.zookeepermonitor, + servers: "127.0.0.1:2181" + }); +}); + // app configuration app.configure('production|development', function(){ app.set('connectorConfig', diff --git a/template/game-server/app.js.wss b/template/game-server/app.js.wss index 3fe713824..3b56fb7e0 100644 --- a/template/game-server/app.js.wss +++ b/template/game-server/app.js.wss @@ -7,6 +7,15 @@ var pomelo = require('pomelo'); var app = pomelo.createApp(); app.set('name', '$'); +// configure monitor +app.configure('production|development', function(){ + app.set('monitorConfig', + { + monitor : pomelo.monitors.zookeepermonitor, + servers: "127.0.0.1:2181" + }); +}); + // app configuration app.configure('production|development', 'connector', function(){ app.set('connectorConfig', diff --git a/template/game-server/app/servers/connector/handler/entryHandler.js b/template/game-server/app/servers/connector/handler/entryHandler.js index adf36ea11..d529c6efd 100644 --- a/template/game-server/app/servers/connector/handler/entryHandler.js +++ b/template/game-server/app/servers/connector/handler/entryHandler.js @@ -15,7 +15,7 @@ var Handler = function(app) { * @return {Void} */ Handler.prototype.entry = function(msg, session, next) { - next(null, {code: 200, msg: 'game server is ok.'}); + next(null, {code: 200, msg: 'Welcome to pomelo 2.0.'}); }; /** diff --git a/template/game-server/config/adminServer.json b/template/game-server/config/adminServer.json deleted file mode 100644 index 4b0e27e16..000000000 --- a/template/game-server/config/adminServer.json +++ /dev/null @@ -1,5 +0,0 @@ -[{ - "type": "connector", - "token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn" -} -] \ No newline at end of file diff --git a/template/game-server/config/adminUser.json b/template/game-server/config/adminUser.json deleted file mode 100644 index ff97f8523..000000000 --- a/template/game-server/config/adminUser.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "id": "user-1", - "username": "admin", - "password": "admin", - "level": 1 - }, - - { - "id": "user-2", - "username": "monitor", - "password": "monitor", - "level": 2 - }, - - { - "id": "user-3", - "username": "test", - "password": "test", - "level": 2 - } -] diff --git a/template/game-server/config/master.json b/template/game-server/config/master.json deleted file mode 100644 index 592be40dc..000000000 --- a/template/game-server/config/master.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "development": { - "id": "master-server-1", "host": "127.0.0.1", "port": 3005 - }, - "production": { - "id": "master-server-1", "host": "127.0.0.1", "port": 3005 - } -} diff --git a/template/game-server/start.sh b/template/game-server/start.sh new file mode 100644 index 000000000..1dde1fe72 --- /dev/null +++ b/template/game-server/start.sh @@ -0,0 +1 @@ +node app.js host=127.0.0.1 port=3150 clientPort=3010 frontend=true serverType=connector >/dev/null 2>&1 & \ No newline at end of file diff --git a/template/web-server/app.js b/template/web-server/app.js index c33b80861..2312da911 100644 --- a/template/web-server/app.js +++ b/template/web-server/app.js @@ -1,27 +1,14 @@ -var express = require('express'); -var app = express.createServer(); +var app = require('express')(); +var serveStatic = require('serve-static'); -app.configure(function(){ - app.use(express.methodOverride()); - app.use(express.bodyParser()); - app.use(app.router); - app.set('view engine', 'jade'); - app.set('views', __dirname + '/public'); - app.set('view options', {layout: false}); - app.set('basepath',__dirname + '/public'); -}); - -app.configure('development', function(){ - app.use(express.static(__dirname + '/public')); - app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); -}); - -app.configure('production', function(){ +var env = process.env.NODE_ENV || 'development'; +if ('development' === env) { + app.use(serveStatic(__dirname + '/public')); +} else { var oneYear = 31557600000; - app.use(express.static(__dirname + '/public', { maxAge: oneYear })); - app.use(express.errorHandler()); -}); - -console.log("Web server has started.\nPlease log on http://127.0.0.1:3001/index.html"); + app.use(serveStatic(__dirname + '/public', { maxAge: oneYear })); +} -app.listen(3001); +app.listen(3001, function() { + console.log("Web server has started.\nPlease log on http://127.0.0.1:3001"); +}); \ No newline at end of file diff --git a/template/web-server/app.js.https b/template/web-server/app.js.https index 8c975eb9a..b13ef1352 100644 --- a/template/web-server/app.js.https +++ b/template/web-server/app.js.https @@ -1,5 +1,6 @@ -var https = require('https'); var express = require('express'); +var https = require('https'); +var app = express(); var fs = require('fs'); @@ -8,31 +9,16 @@ var options = { cert: fs.readFileSync('../shared/server.crt') }; -var app = express(); +var serveStatic = require('serve-static'); -app.configure(function(){ - app.use(express.methodOverride()); - app.use(express.bodyParser()); - app.use(app.router); - app.set('view engine', 'jade'); - app.set('views', __dirname + '/public'); - app.set('view options', {layout: false}); - app.set('basepath',__dirname + '/public'); -}); - -app.configure('development', function(){ - app.use(express.static(__dirname + '/public')); - app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); -}); - -app.configure('production', function(){ +var env = process.env.NODE_ENV || 'development'; +if ('development' === env) { + app.use(serveStatic(__dirname + '/public')); +} else { var oneYear = 31557600000; - app.use(express.static(__dirname + '/public', { maxAge: oneYear })); - app.use(express.errorHandler()); -}); - -console.log("Web server has started.\nPlease log on http://127.0.0.1:3001/index.html"); + app.use(serveStatic(__dirname + '/public', { maxAge: oneYear })); +} -var httpsServer = https.createServer(options, app); +https.createServer(options, app).listen(3001); -httpsServer.listen(3001); \ No newline at end of file +console.log("Web server has started.\nPlease log on https://127.0.0.1:3001"); \ No newline at end of file diff --git a/template/web-server/package.json b/template/web-server/package.json index 7afd4cb6a..e3e776cde 100644 --- a/template/web-server/package.json +++ b/template/web-server/package.json @@ -3,6 +3,7 @@ "version": "0.0.1", "private": false, "dependencies": { - "express": "3.4.8" + "express": "4.10.2", + "serve-static": "1.10.0" } } \ No newline at end of file diff --git a/template/web-server/public/css/base.css b/template/web-server/public/css/base.css index 17af8af8d..44aa308a9 100644 --- a/template/web-server/public/css/base.css +++ b/template/web-server/public/css/base.css @@ -50,6 +50,14 @@ font-size: 15pt; } +.g-result { + height: 100px; + margin-top: 30px; + text-align: center; + font-size: 18pt; + color: #FF0000; +} + .g-button { margin-top: 10px; text-align: center; diff --git a/template/web-server/public/index.html b/template/web-server/public/index.html index e815cc563..c91cbedb4 100644 --- a/template/web-server/public/index.html +++ b/template/web-server/public/index.html @@ -1,57 +1,72 @@ - - - - Pomelo - - - - - - - - - - - + + - - -
-
- + }); + } else { + para.removeChild(node); + pomelo.request("connector.entryHandler.entry", "hello pomelo", function(data) { + console.log(data.msg); + para.appendChild(node); + }); + } + } + + + +
+
+ -
-
- Welcome to Pomelo -
-
- -
- +
+
+
+ Welcome to Pomelo
- - + +
+

+
+
+ +
+
+ + \ No newline at end of file diff --git a/template/web-server/public/index.html.sio b/template/web-server/public/index.html.sio index f33ae290e..f35dc6059 100644 --- a/template/web-server/public/index.html.sio +++ b/template/web-server/public/index.html.sio @@ -1,58 +1,72 @@ - - - - Pomelo - - - - - - - - - - - + + - - - -
-
- + }); + } else { + para.removeChild(node); + pomelo.request("connector.entryHandler.entry", "hello pomelo", function(data) { + console.log(data.msg); + para.appendChild(node); + }); + } + } + + + +
+
+ -
-
- Welcome to Pomelo -
-
- -
- +
+
+
+ Welcome to Pomelo
- - + +
+

+
+
+ +
+
+ + \ No newline at end of file diff --git a/template/web-server/public/js/lib/pomeloclient.js b/template/web-server/public/js/lib/pomeloclient.js index 792ae8522..f27d113e6 100644 --- a/template/web-server/public/js/lib/pomeloclient.js +++ b/template/web-server/public/js/lib/pomeloclient.js @@ -33,51 +33,51 @@ // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events || !this._events.error || - (isArray(this._events.error) && !this._events.error.length)) - { - if (this.domain) { - var er = arguments[1]; - er.domain_emitter = this; - er.domain = this.domain; - er.domain_thrown = false; - this.domain.emit('error', er); - return false; - } + (isArray(this._events.error) && !this._events.error.length)) + { + if (this.domain) { + var er = arguments[1]; + er.domain_emitter = this; + er.domain = this.domain; + er.domain_thrown = false; + this.domain.emit('error', er); + return false; + } - if (arguments[1] instanceof Error) { + if (arguments[1] instanceof Error) { throw arguments[1]; // Unhandled 'error' event } else { throw new Error("Uncaught, unspecified 'error' event."); } return false; } - } + } - if (!this._events) return false; - var handler = this._events[type]; - if (!handler) return false; + if (!this._events) return false; + var handler = this._events[type]; + if (!handler) return false; - if (typeof handler == 'function') { - if (this.domain) { - this.domain.enter(); - } - switch (arguments.length) { + if (typeof handler == 'function') { + if (this.domain) { + this.domain.enter(); + } + switch (arguments.length) { // fast cases case 1: - handler.call(this); + handler.call(this); break; case 2: - handler.call(this, arguments[1]); + handler.call(this, arguments[1]); break; case 3: - handler.call(this, arguments[1], arguments[2]); + handler.call(this, arguments[1], arguments[2]); break; // slower default: - var l = arguments.length; + var l = arguments.length; var args = new Array(l - 1); for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; - handler.apply(this, args); + handler.apply(this, args); } if (this.domain) { this.domain.exit(); @@ -92,7 +92,7 @@ var args = new Array(l - 1); for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; - var listeners = handler.slice(); + var listeners = handler.slice(); for (var i = 0, l = listeners.length; i < l; i++) { listeners[i].apply(this, args); } @@ -116,7 +116,7 @@ // To avoid recursion in the case that type == "newListeners"! Before // adding it to the listeners, first emit "newListeners". this.emit('newListener', type, typeof listener.listener === 'function' ? - listener.listener : listener); + listener.listener : listener); if (!this._events[type]) { // Optimize the case of one listener. Don't need the extra array object. @@ -144,9 +144,9 @@ if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + - 'leak detected. %d listeners added. ' + - 'Use emitter.setMaxListeners() to increase limit.', - this._events[type].length); + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); console.trace(); } } @@ -187,22 +187,22 @@ var position = -1; for (var i = 0, length = list.length; i < length; i++) { if (list[i] === listener || - (list[i].listener && list[i].listener === listener)) - { - position = i; - break; - } + (list[i].listener && list[i].listener === listener)) + { + position = i; + break; + } } if (position < 0) return this; list.splice(position, 1); } else if (list === listener || - (list.listener && list.listener === listener)) - { - delete this._events[type]; - } + (list.listener && list.listener === listener)) + { + delete this._events[type]; + } - return this; + return this; }; EventEmitter.prototype.removeAllListeners = function(type) { @@ -236,13 +236,13 @@ (function (exports, global) { var Protocol = exports; - + var HEADER = 5; var Message = function(id,route,body){ - this.id = id; - this.route = route; - this.body = body; + this.id = id; + this.route = route; + this.body = body; }; /** @@ -254,23 +254,23 @@ * socketio current support string * */ -Protocol.encode = function(id,route,msg){ - var msgStr = JSON.stringify(msg); - if (route.length>255) { throw new Error('route maxlength is overflow'); } - var byteArray = new Uint16Array(HEADER + route.length + msgStr.length); - var index = 0; - byteArray[index++] = (id>>24) & 0xFF; - byteArray[index++] = (id>>16) & 0xFF; - byteArray[index++] = (id>>8) & 0xFF; - byteArray[index++] = id & 0xFF; - byteArray[index++] = route.length & 0xFF; - for(var i = 0;i255) { throw new Error('route maxlength is overflow'); } + var byteArray = new Uint16Array(HEADER + route.length + msgStr.length); + var index = 0; + byteArray[index++] = (id>>24) & 0xFF; + byteArray[index++] = (id>>16) & 0xFF; + byteArray[index++] = (id>>8) & 0xFF; + byteArray[index++] = id & 0xFF; + byteArray[index++] = route.length & 0xFF; + for(var i = 0;i>>0; - var routeLen = buf[HEADER-1]; - var route = bt2Str(buf,HEADER, routeLen+HEADER); - var body = bt2Str(buf,routeLen+HEADER,buf.length); - return new Message(id,route,body); + Protocol.decode = function(msg){ + var idx, len = msg.length, arr = new Array( len ); + for ( idx = 0 ; idx < len ; ++idx ) { + arr[idx] = msg.charCodeAt(idx); + } + var index = 0; + var buf = new Uint16Array(arr); + var id = ((buf[index++] <<24) | (buf[index++]) << 16 | (buf[index++]) << 8 | buf[index++]) >>>0; + var routeLen = buf[HEADER-1]; + var route = bt2Str(buf,HEADER, routeLen+HEADER); + var body = bt2Str(buf,routeLen+HEADER,buf.length); + return new Message(id,route,body); }; var bt2Str = function(byteArray,start,end) { - var result = ""; - for(var i = start; i < byteArray.length && i MIT Licensed */ - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, global) { - - /** - * IO namespace. - * - * @namespace - */ - - var io = exports; - - /** - * Socket.IO version - * - * @api public - */ - - io.version = '0.9.6'; - - /** - * Protocol implemented. - * - * @api public - */ - - io.protocol = 1; - - /** - * Available transports, these will be populated with the available transports - * - * @api public - */ - - io.transports = []; - - /** - * Keep track of jsonp callbacks. - * - * @api private - */ - - io.j = []; - - /** - * Keep track of our io.Sockets - * - * @api private - */ - io.sockets = {}; - - - /** - * Manages connections to hosts. - * - * @param {String} uri - * @Param {Boolean} force creation of new socket (defaults to false) - * @api public - */ - - io.connect = function (host, details) { - var uri = io.util.parseUri(host) - , uuri - , socket; - - if (global && global.location) { - uri.protocol = uri.protocol || global.location.protocol.slice(0, -1); - uri.host = uri.host || (global.document - ? global.document.domain : global.location.hostname); - uri.port = uri.port || global.location.port; - } - - uuri = io.util.uniqueUri(uri); - - var options = { - host: uri.host - , secure: 'https' == uri.protocol - , port: uri.port || ('https' == uri.protocol ? 443 : 80) - , query: uri.query || '' - }; - - io.util.merge(options, details); - - if (options['force new connection'] || !io.sockets[uuri]) { - socket = new io.Socket(options); - } - - if (!options['force new connection'] && socket) { - io.sockets[uuri] = socket; - } - - socket = socket || io.sockets[uuri]; - - // if path is different from '' or / - return socket.of(uri.path.length > 1 ? uri.path : ''); - }; - -})('object' === typeof module ? module.exports : (this.io = {}), this); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, global) { - - /** - * Utilities namespace. - * - * @namespace - */ - - var util = exports.util = {}; - - /** - * Parses an URI - * - * @author Steven Levithan (MIT license) - * @api public - */ - - var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; - - var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', - 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', - 'anchor']; - - util.parseUri = function (str) { - var m = re.exec(str || '') - , uri = {} - , i = 14; - - while (i--) { - uri[parts[i]] = m[i] || ''; - } - - return uri; - }; - - /** - * Produces a unique url that identifies a Socket.IO connection. - * - * @param {Object} uri - * @api public - */ - - util.uniqueUri = function (uri) { - var protocol = uri.protocol - , host = uri.host - , port = uri.port; - - if ('document' in global) { - host = host || document.domain; - port = port || (protocol == 'https' - && document.location.protocol !== 'https:' ? 443 : document.location.port); - } else { - host = host || 'localhost'; - - if (!port && protocol == 'https') { - port = 443; - } - } - - return (protocol || 'http') + '://' + host + ':' + (port || 80); - }; - - /** - * Mergest 2 query strings in to once unique query string - * - * @param {String} base - * @param {String} addition - * @api public - */ - - util.query = function (base, addition) { - var query = util.chunkQuery(base || '') - , components = []; - - util.merge(query, util.chunkQuery(addition || '')); - for (var part in query) { - if (query.hasOwnProperty(part)) { - components.push(part + '=' + query[part]); - } - } - - return components.length ? '?' + components.join('&') : ''; - }; - - /** - * Transforms a querystring in to an object - * - * @param {String} qs - * @api public - */ - - util.chunkQuery = function (qs) { - var query = {} - , params = qs.split('&') - , i = 0 - , l = params.length - , kv; - - for (; i < l; ++i) { - kv = params[i].split('='); - if (kv[0]) { - query[kv[0]] = kv[1]; - } - } - - return query; - }; - - /** - * Executes the given function when the page is loaded. - * - * io.util.load(function () { console.log('page loaded'); }); - * - * @param {Function} fn - * @api public - */ - - var pageLoaded = false; - - util.load = function (fn) { - if ('document' in global && document.readyState === 'complete' || pageLoaded) { - return fn(); - } - - util.on(global, 'load', fn, false); - }; - - /** - * Adds an event. - * - * @api private - */ - - util.on = function (element, event, fn, capture) { - if (element.attachEvent) { - element.attachEvent('on' + event, fn); - } else if (element.addEventListener) { - element.addEventListener(event, fn, capture); - } - }; - - /** - * Generates the correct `XMLHttpRequest` for regular and cross domain requests. - * - * @param {Boolean} [xdomain] Create a request that can be used cross domain. - * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest. - * @api private - */ - - util.request = function (xdomain) { - - if (xdomain && 'undefined' != typeof XDomainRequest) { - return new XDomainRequest(); - } - - if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) { - return new XMLHttpRequest(); - } - - if (!xdomain) { - try { - return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP'); - } catch(e) { } - } - - return null; - }; - - /** - * XHR based transport constructor. - * - * @constructor - * @api public - */ - - /** - * Change the internal pageLoaded value. - */ - - if ('undefined' != typeof window) { - util.load(function () { - pageLoaded = true; - }); - } - - /** - * Defers a function to ensure a spinner is not displayed by the browser - * - * @param {Function} fn - * @api public - */ - - util.defer = function (fn) { - if (!util.ua.webkit || 'undefined' != typeof importScripts) { - return fn(); - } - - util.load(function () { - setTimeout(fn, 100); - }); - }; - - /** - * Merges two objects. - * - * @api public - */ - - util.merge = function merge (target, additional, deep, lastseen) { - var seen = lastseen || [] - , depth = typeof deep == 'undefined' ? 2 : deep - , prop; - - for (prop in additional) { - if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) { - if (typeof target[prop] !== 'object' || !depth) { - target[prop] = additional[prop]; - seen.push(additional[prop]); - } else { - util.merge(target[prop], additional[prop], depth - 1, seen); - } - } - } - - return target; - }; - - /** - * Merges prototypes from objects - * - * @api public - */ - - util.mixin = function (ctor, ctor2) { - util.merge(ctor.prototype, ctor2.prototype); - }; - - /** - * Shortcut for prototypical and static inheritance. - * - * @api private - */ - - util.inherit = function (ctor, ctor2) { - function f() {}; - f.prototype = ctor2.prototype; - ctor.prototype = new f; - }; - - /** - * Checks if the given object is an Array. - * - * io.util.isArray([]); // true - * io.util.isArray({}); // false - * - * @param Object obj - * @api public - */ - - util.isArray = Array.isArray || function (obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; - }; - - /** - * Intersects values of two arrays into a third - * - * @api public - */ - - util.intersect = function (arr, arr2) { - var ret = [] - , longest = arr.length > arr2.length ? arr : arr2 - , shortest = arr.length > arr2.length ? arr2 : arr; - - for (var i = 0, l = shortest.length; i < l; i++) { - if (~util.indexOf(longest, shortest[i])) - ret.push(shortest[i]); - } - - return ret; - } - - /** - * Array indexOf compatibility. - * - * @see bit.ly/a5Dxa2 - * @api public - */ - - util.indexOf = function (arr, o, i) { - - for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0; - i < j && arr[i] !== o; i++) {} - - return j <= i ? -1 : i; - }; - - /** - * Converts enumerables to array. - * - * @api public - */ - - util.toArray = function (enu) { - var arr = []; - - for (var i = 0, l = enu.length; i < l; i++) - arr.push(enu[i]); - - return arr; - }; - - /** - * UA / engines detection namespace. - * - * @namespace - */ - - util.ua = {}; - - /** - * Whether the UA supports CORS for XHR. - * - * @api public - */ - - util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () { - try { - var a = new XMLHttpRequest(); - } catch (e) { - return false; - } - - return a.withCredentials != undefined; - })(); - - /** - * Detect webkit. - * - * @api public - */ - - util.ua.webkit = 'undefined' != typeof navigator - && /webkit/i.test(navigator.userAgent); - -})('undefined' != typeof io ? io : module.exports, this); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.EventEmitter = EventEmitter; - - /** - * Event emitter constructor. - * - * @api public. - */ - - function EventEmitter () {}; - - /** - * Adds a listener - * - * @api public - */ - - EventEmitter.prototype.on = function (name, fn) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = fn; - } else if (io.util.isArray(this.$events[name])) { - this.$events[name].push(fn); - } else { - this.$events[name] = [this.$events[name], fn]; - } - - return this; - }; - - EventEmitter.prototype.addListener = EventEmitter.prototype.on; - - /** - * Adds a volatile listener. - * - * @api public - */ - - EventEmitter.prototype.once = function (name, fn) { - var self = this; - - function on () { - self.removeListener(name, on); - fn.apply(this, arguments); - }; - - on.listener = fn; - this.on(name, on); - - return this; - }; - - /** - * Removes a listener. - * - * @api public - */ - - EventEmitter.prototype.removeListener = function (name, fn) { - if (this.$events && this.$events[name]) { - var list = this.$events[name]; - - if (io.util.isArray(list)) { - var pos = -1; - - for (var i = 0, l = list.length; i < l; i++) { - if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { - pos = i; - break; - } - } - - if (pos < 0) { - return this; - } - - list.splice(pos, 1); - - if (!list.length) { - delete this.$events[name]; - } - } else if (list === fn || (list.listener && list.listener === fn)) { - delete this.$events[name]; - } - } - - return this; - }; - - /** - * Removes all listeners for an event. - * - * @api public - */ - - EventEmitter.prototype.removeAllListeners = function (name) { - // TODO: enable this when node 0.5 is stable - //if (name === undefined) { - //this.$events = {}; - //return this; - //} - - if (this.$events && this.$events[name]) { - this.$events[name] = null; - } - - return this; - }; - - /** - * Gets all listeners for a certain event. - * - * @api publci - */ - - EventEmitter.prototype.listeners = function (name) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = []; - } - - if (!io.util.isArray(this.$events[name])) { - this.$events[name] = [this.$events[name]]; - } - - return this.$events[name]; - }; - - /** - * Emits an event. - * - * @api public - */ - - EventEmitter.prototype.emit = function (name) { - if (!this.$events) { - return false; - } - - var handler = this.$events[name]; - - if (!handler) { - return false; - } - - var args = Array.prototype.slice.call(arguments, 1); - - if ('function' == typeof handler) { - handler.apply(this, args); - } else if (io.util.isArray(handler)) { - var listeners = handler.slice(); - - for (var i = 0, l = listeners.length; i < l; i++) { - listeners[i].apply(this, args); - } - } else { - return false; - } - - return true; - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -/** - * Based on JSON2 (http://www.JSON.org/js.html). - */ - -(function (exports, nativeJSON) { - "use strict"; - - // use native JSON if it's available - if (nativeJSON && nativeJSON.parse){ - return exports.JSON = { - parse: nativeJSON.parse - , stringify: nativeJSON.stringify - } - } - - var JSON = exports.JSON = {}; - - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - function date(d, key) { - return isFinite(d.valueOf()) ? - d.getUTCFullYear() + '-' + - f(d.getUTCMonth() + 1) + '-' + - f(d.getUTCDate()) + 'T' + - f(d.getUTCHours()) + ':' + - f(d.getUTCMinutes()) + ':' + - f(d.getUTCSeconds()) + 'Z' : null; - }; - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' ? c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - - - function str(key, holder) { - -// Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - -// If the value has a toJSON method, call it to obtain a replacement value. - - if (value instanceof Date) { - value = date(key); - } - -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - -// What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - -// JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. - - return String(value); - -// If the type is 'object', we might be dealing with an object or an array or -// null. - - case 'object': - -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. - - if (!value) { - return 'null'; - } - -// Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - -// Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - -// Join all of the elements together, separated with commas, and wrap them in -// brackets. - - v = partial.length === 0 ? '[]' : gap ? - '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : - '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - -// If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - -// Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - -// Join all of the member texts together, separated with commas, -// and wrap them in braces. - - v = partial.length === 0 ? '{}' : gap ? - '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : - '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - -// If the JSON object does not yet have a stringify method, give it one. - - JSON.stringify = function (value, replacer, space) { - -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - -// If the space parameter is a number, make an indent string containing that -// many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - -// If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === 'string') { - indent = space; - } - -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. - - return str('', {'': value}); - }; - -// If the JSON object does not yet have a parse method, give it one. - - JSON.parse = function (text, reviver) { - // The parse method takes a text and an optional reviver function, and returns - // a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - - // The walk method is used to recursively walk the resulting structure so - // that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - - // Parsing happens in four stages. In the first stage, we replace certain - // Unicode characters with escape sequences. JavaScript handles many characters - // incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - - // In the second stage, we run the text against regular expressions that look - // for non-JSON patterns. We are especially concerned with '()' and 'new' - // because they can cause invocation, and '=' because it can cause mutation. - // But just to be safe, we want to reject all unexpected forms. - - // We split the second stage into 4 regexp operations in order to work around - // crippling inefficiencies in IE's and Safari's regexp engines. First we - // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we - // replace all simple value tokens with ']' characters. Third, we delete all - // open brackets that follow a colon or comma or that begin the text. Finally, - // we look to see that the remaining characters are only whitespace or ']' or - // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - - // In the third stage we use the eval function to compile the text into a - // JavaScript structure. The '{' operator is subject to a syntactic ambiguity - // in JavaScript: it can begin a block or an object literal. We wrap the text - // in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - - // In the optional fourth stage, we recursively walk the new structure, passing - // each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' ? - walk({'': j}, '') : j; - } - - // If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - -})( - 'undefined' != typeof io ? io : module.exports - , typeof JSON !== 'undefined' ? JSON : undefined -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Parser namespace. - * - * @namespace - */ - - var parser = exports.parser = {}; - - /** - * Packet types. - */ - - var packets = parser.packets = [ - 'disconnect' - , 'connect' - , 'heartbeat' - , 'message' - , 'json' - , 'event' - , 'ack' - , 'error' - , 'noop' - ]; - - /** - * Errors reasons. - */ - - var reasons = parser.reasons = [ - 'transport not supported' - , 'client not handshaken' - , 'unauthorized' - ]; - - /** - * Errors advice. - */ - - var advice = parser.advice = [ - 'reconnect' - ]; - - /** - * Shortcuts. - */ - - var JSON = io.JSON - , indexOf = io.util.indexOf; - - /** - * Encodes a packet. - * - * @api private - */ - - parser.encodePacket = function (packet) { - var type = indexOf(packets, packet.type) - , id = packet.id || '' - , endpoint = packet.endpoint || '' - , ack = packet.ack - , data = null; - - switch (packet.type) { - case 'error': - var reason = packet.reason ? indexOf(reasons, packet.reason) : '' - , adv = packet.advice ? indexOf(advice, packet.advice) : ''; - - if (reason !== '' || adv !== '') - data = reason + (adv !== '' ? ('+' + adv) : ''); - - break; - - case 'message': - if (packet.data !== '') - data = packet.data; - break; - - case 'event': - var ev = { name: packet.name }; - - if (packet.args && packet.args.length) { - ev.args = packet.args; - } - - data = JSON.stringify(ev); - break; - - case 'json': - data = JSON.stringify(packet.data); - break; - - case 'connect': - if (packet.qs) - data = packet.qs; - break; - - case 'ack': - data = packet.ackId - + (packet.args && packet.args.length - ? '+' + JSON.stringify(packet.args) : ''); - break; - } - - // construct packet with required fragments - var encoded = [ - type - , id + (ack == 'data' ? '+' : '') - , endpoint - ]; - - // data fragment is optional - if (data !== null && data !== undefined) - encoded.push(data); - - return encoded.join(':'); - }; - - /** - * Encodes multiple messages (payload). - * - * @param {Array} messages - * @api private - */ - - parser.encodePayload = function (packets) { - var decoded = ''; - - if (packets.length == 1) - return packets[0]; - - for (var i = 0, l = packets.length; i < l; i++) { - var packet = packets[i]; - decoded += '\ufffd' + packet.length + '\ufffd' + packets[i]; - } - - return decoded; - }; - - /** - * Decodes a packet - * - * @api private - */ - - var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/; - - parser.decodePacket = function (data) { - var pieces = data.match(regexp); - - if (!pieces) return {}; - - var id = pieces[2] || '' - , data = pieces[5] || '' - , packet = { - type: packets[pieces[1]] - , endpoint: pieces[4] || '' - }; - - // whether we need to acknowledge the packet - if (id) { - packet.id = id; - if (pieces[3]) - packet.ack = 'data'; - else - packet.ack = true; - } - - // handle different packet types - switch (packet.type) { - case 'error': - var pieces = data.split('+'); - packet.reason = reasons[pieces[0]] || ''; - packet.advice = advice[pieces[1]] || ''; - break; - - case 'message': - packet.data = data || ''; - break; - - case 'event': - try { - var opts = JSON.parse(data); - packet.name = opts.name; - packet.args = opts.args; - } catch (e) { } - - packet.args = packet.args || []; - break; - - case 'json': - try { - packet.data = JSON.parse(data); - } catch (e) { } - break; - - case 'connect': - packet.qs = data || ''; - break; - - case 'ack': - var pieces = data.match(/^([0-9]+)(\+)?(.*)/); - if (pieces) { - packet.ackId = pieces[1]; - packet.args = []; - - if (pieces[3]) { - try { - packet.args = pieces[3] ? JSON.parse(pieces[3]) : []; - } catch (e) { } - } - } - break; - - case 'disconnect': - case 'heartbeat': - break; - }; - - return packet; - }; - - /** - * Decodes data payload. Detects multiple messages - * - * @return {Array} messages - * @api public - */ - - parser.decodePayload = function (data) { - // IE doesn't like data[i] for unicode chars, charAt works fine - if (data.charAt(0) == '\ufffd') { - var ret = []; - - for (var i = 1, length = ''; i < data.length; i++) { - if (data.charAt(i) == '\ufffd') { - ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length))); - i += Number(length) + 1; - length = ''; - } else { - length += data.charAt(i); - } - } - - return ret; - } else { - return [parser.decodePacket(data)]; - } - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.Transport = Transport; - - /** - * This is the transport template for all supported transport methods. - * - * @constructor - * @api public - */ - - function Transport (socket, sessid) { - this.socket = socket; - this.sessid = sessid; - }; - - /** - * Apply EventEmitter mixin. - */ - - io.util.mixin(Transport, io.EventEmitter); - - /** - * Handles the response from the server. When a new response is received - * it will automatically update the timeout, decode the message and - * forwards the response to the onMessage function for further processing. - * - * @param {String} data Response from the server. - * @api private - */ - - Transport.prototype.onData = function (data) { - this.clearCloseTimeout(); - - // If the connection in currently open (or in a reopening state) reset the close - // timeout since we have just received data. This check is necessary so - // that we don't reset the timeout on an explicitly disconnected connection. - if (this.socket.connected || this.socket.connecting || this.socket.reconnecting) { - this.setCloseTimeout(); - } - - if (data !== '') { - // todo: we should only do decodePayload for xhr transports - var msgs = io.parser.decodePayload(data); - - if (msgs && msgs.length) { - for (var i = 0, l = msgs.length; i < l; i++) { - this.onPacket(msgs[i]); - } - } - } - - return this; - }; - - /** - * Handles packets. - * - * @api private - */ - - Transport.prototype.onPacket = function (packet) { - this.socket.setHeartbeatTimeout(); - - if (packet.type == 'heartbeat') { - return this.onHeartbeat(); - } - - if (packet.type == 'connect' && packet.endpoint == '') { - this.onConnect(); - } - - if (packet.type == 'error' && packet.advice == 'reconnect') { - this.open = false; - } - - this.socket.onPacket(packet); - - return this; - }; - - /** - * Sets close timeout - * - * @api private - */ - - Transport.prototype.setCloseTimeout = function () { - if (!this.closeTimeout) { - var self = this; - - this.closeTimeout = setTimeout(function () { - self.onDisconnect(); - }, this.socket.closeTimeout); - } - }; - - /** - * Called when transport disconnects. - * - * @api private - */ - - Transport.prototype.onDisconnect = function () { - if (this.close && this.open) this.close(); - this.clearTimeouts(); - this.socket.onDisconnect(); - return this; - }; - - /** - * Called when transport connects - * - * @api private - */ - - Transport.prototype.onConnect = function () { - this.socket.onConnect(); - return this; - } - - /** - * Clears close timeout - * - * @api private - */ - - Transport.prototype.clearCloseTimeout = function () { - if (this.closeTimeout) { - clearTimeout(this.closeTimeout); - this.closeTimeout = null; - } - }; - - /** - * Clear timeouts - * - * @api private - */ - - Transport.prototype.clearTimeouts = function () { - this.clearCloseTimeout(); - - if (this.reopenTimeout) { - clearTimeout(this.reopenTimeout); - } - }; - - /** - * Sends a packet - * - * @param {Object} packet object. - * @api private - */ - - Transport.prototype.packet = function (packet) { - this.send(io.parser.encodePacket(packet)); - }; - - /** - * Send the received heartbeat message back to server. So the server - * knows we are still connected. - * - * @param {String} heartbeat Heartbeat response from the server. - * @api private - */ - - Transport.prototype.onHeartbeat = function (heartbeat) { - this.packet({ type: 'heartbeat' }); - }; - - /** - * Called when the transport opens. - * - * @api private - */ - - Transport.prototype.onOpen = function () { - this.open = true; - this.clearCloseTimeout(); - this.socket.onOpen(); - }; - - /** - * Notifies the base when the connection with the Socket.IO server - * has been disconnected. - * - * @api private - */ - - Transport.prototype.onClose = function () { - var self = this; - - /* FIXME: reopen delay causing a infinit loop - this.reopenTimeout = setTimeout(function () { - self.open(); - }, this.socket.options['reopen delay']);*/ - - this.open = false; - this.socket.onClose(); - this.onDisconnect(); - }; - - /** - * Generates a connection url based on the Socket.IO URL Protocol. - * See for more details. - * - * @returns {String} Connection url - * @api private - */ - - Transport.prototype.prepareUrl = function () { - var options = this.socket.options; - - return this.scheme() + '://' - + options.host + ':' + options.port + '/' - + options.resource + '/' + io.protocol - + '/' + this.name + '/' + this.sessid; - }; - - /** - * Checks if the transport is ready to start a connection. - * - * @param {Socket} socket The socket instance that needs a transport - * @param {Function} fn The callback - * @api private - */ - - Transport.prototype.ready = function (socket, fn) { - fn.call(this); - }; -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - */ - - exports.Socket = Socket; - - /** - * Create a new `Socket.IO client` which can establish a persistent - * connection with a Socket.IO enabled server. - * - * @api public - */ - - function Socket (options) { - this.options = { - port: 80 - , secure: false - , document: 'document' in global ? document : false - , resource: 'socket.io' - , transports: io.transports - , 'connect timeout': 10000 - , 'try multiple transports': true - , 'reconnect': true - , 'reconnection delay': 500 - , 'reconnection limit': Infinity - , 'reopen delay': 3000 - , 'max reconnection attempts': 10 - , 'sync disconnect on unload': true - , 'auto connect': true - , 'flash policy port': 10843 - }; - - io.util.merge(this.options, options); - - this.connected = false; - this.open = false; - this.connecting = false; - this.reconnecting = false; - this.namespaces = {}; - this.buffer = []; - this.doBuffer = false; - - if (this.options['sync disconnect on unload'] && - (!this.isXDomain() || io.util.ua.hasCORS)) { - var self = this; - - io.util.on(global, 'unload', function () { - self.disconnectSync(); - }, false); - } - - if (this.options['auto connect']) { - this.connect(); - } -}; - - /** - * Apply EventEmitter mixin. - */ - - io.util.mixin(Socket, io.EventEmitter); - - /** - * Returns a namespace listener/emitter for this socket - * - * @api public - */ - - Socket.prototype.of = function (name) { - if (!this.namespaces[name]) { - this.namespaces[name] = new io.SocketNamespace(this, name); - - if (name !== '') { - this.namespaces[name].packet({ type: 'connect' }); - } - } - - return this.namespaces[name]; - }; - - /** - * Emits the given event to the Socket and all namespaces - * - * @api private - */ - - Socket.prototype.publish = function () { - this.emit.apply(this, arguments); - - var nsp; - - for (var i in this.namespaces) { - if (this.namespaces.hasOwnProperty(i)) { - nsp = this.of(i); - nsp.$emit.apply(nsp, arguments); - } - } - }; - - /** - * Performs the handshake - * - * @api private - */ - - function empty () { }; - - Socket.prototype.handshake = function (fn) { - var self = this - , options = this.options; - - function complete (data) { - if (data instanceof Error) { - self.onError(data.message); - } else { - fn.apply(null, data.split(':')); - } - }; - - var url = [ - 'http' + (options.secure ? 's' : '') + ':/' - , options.host + ':' + options.port - , options.resource - , io.protocol - , io.util.query(this.options.query, 't=' + +new Date) - ].join('/'); - - if (this.isXDomain() && !io.util.ua.hasCORS) { - var insertAt = document.getElementsByTagName('script')[0] - , script = document.createElement('script'); - - script.src = url + '&jsonp=' + io.j.length; - insertAt.parentNode.insertBefore(script, insertAt); - - io.j.push(function (data) { - complete(data); - script.parentNode.removeChild(script); - }); - } else { - var xhr = io.util.request(); - - xhr.open('GET', url, true); - xhr.withCredentials = true; - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - xhr.onreadystatechange = empty; - - if (xhr.status == 200) { - complete(xhr.responseText); - } else { - !self.reconnecting && self.onError(xhr.responseText); - } - } - }; - xhr.send(null); - } - }; - - /** - * Find an available transport based on the options supplied in the constructor. - * - * @api private - */ - - Socket.prototype.getTransport = function (override) { - var transports = override || this.transports, match; - - for (var i = 0, transport; transport = transports[i]; i++) { - if (io.Transport[transport] - && io.Transport[transport].check(this) - && (!this.isXDomain() || io.Transport[transport].xdomainCheck())) { - return new io.Transport[transport](this, this.sessionid); - } - } - - return null; - }; - - /** - * Connects to the server. - * - * @param {Function} [fn] Callback. - * @returns {io.Socket} - * @api public - */ - - Socket.prototype.connect = function (fn) { - if (this.connecting) { - return this; - } - - var self = this; - - this.handshake(function (sid, heartbeat, close, transports) { - self.sessionid = sid; - self.closeTimeout = close * 1000; - self.heartbeatTimeout = heartbeat * 1000; - self.transports = transports ? io.util.intersect( - transports.split(',') - , self.options.transports - ) : self.options.transports; - - self.setHeartbeatTimeout(); - - function connect (transports){ - if (self.transport) self.transport.clearTimeouts(); - - self.transport = self.getTransport(transports); - if (!self.transport) return self.publish('connect_failed'); - - // once the transport is ready - self.transport.ready(self, function () { - self.connecting = true; - self.publish('connecting', self.transport.name); - self.transport.open(); - - if (self.options['connect timeout']) { - self.connectTimeoutTimer = setTimeout(function () { - if (!self.connected) { - self.connecting = false; - - if (self.options['try multiple transports']) { - if (!self.remainingTransports) { - self.remainingTransports = self.transports.slice(0); - } - - var remaining = self.remainingTransports; - - while (remaining.length > 0 && remaining.splice(0,1)[0] != - self.transport.name) {} - - if (remaining.length){ - connect(remaining); - } else { - self.publish('connect_failed'); - } - } - } - }, self.options['connect timeout']); - } - }); - } - - connect(self.transports); - - self.once('connect', function (){ - clearTimeout(self.connectTimeoutTimer); - - fn && typeof fn == 'function' && fn(); - }); - }); - - return this; - }; - - /** - * Clears and sets a new heartbeat timeout using the value given by the - * server during the handshake. - * - * @api private - */ - - Socket.prototype.setHeartbeatTimeout = function () { - clearTimeout(this.heartbeatTimeoutTimer); - - var self = this; - this.heartbeatTimeoutTimer = setTimeout(function () { - self.transport.onClose(); - }, this.heartbeatTimeout); - }; - - /** - * Sends a message. - * - * @param {Object} data packet. - * @returns {io.Socket} - * @api public - */ - - Socket.prototype.packet = function (data) { - if (this.connected && !this.doBuffer) { - this.transport.packet(data); - } else { - this.buffer.push(data); - } - - return this; - }; - - /** - * Sets buffer state - * - * @api private - */ - - Socket.prototype.setBuffer = function (v) { - this.doBuffer = v; - - if (!v && this.connected && this.buffer.length) { - this.transport.payload(this.buffer); - this.buffer = []; - } - }; - - /** - * Disconnect the established connect. - * - * @returns {io.Socket} - * @api public - */ - - Socket.prototype.disconnect = function () { - if (this.connected || this.connecting) { - if (this.open) { - this.of('').packet({ type: 'disconnect' }); - } - - // handle disconnection immediately - this.onDisconnect('booted'); - } - - return this; - }; - - /** - * Disconnects the socket with a sync XHR. - * - * @api private - */ - - Socket.prototype.disconnectSync = function () { - // ensure disconnection - var xhr = io.util.request() - , uri = this.resource + '/' + io.protocol + '/' + this.sessionid; - - xhr.open('GET', uri, true); - - // handle disconnection immediately - this.onDisconnect('booted'); - }; - - /** - * Check if we need to use cross domain enabled transports. Cross domain would - * be a different port or different domain name. - * - * @returns {Boolean} - * @api private - */ - - Socket.prototype.isXDomain = function () { - - var port = global.location.port || - ('https:' == global.location.protocol ? 443 : 80); - - return this.options.host !== global.location.hostname - || this.options.port != port; - }; - - /** - * Called upon handshake. - * - * @api private - */ - - Socket.prototype.onConnect = function () { - if (!this.connected) { - this.connected = true; - this.connecting = false; - if (!this.doBuffer) { - // make sure to flush the buffer - this.setBuffer(false); - } - this.emit('connect'); - } - }; - - /** - * Called when the transport opens - * - * @api private - */ - - Socket.prototype.onOpen = function () { - this.open = true; - }; - - /** - * Called when the transport closes. - * - * @api private - */ - - Socket.prototype.onClose = function () { - this.open = false; - clearTimeout(this.heartbeatTimeoutTimer); - }; - - /** - * Called when the transport first opens a connection - * - * @param text - */ - - Socket.prototype.onPacket = function (packet) { - this.of(packet.endpoint).onPacket(packet); - }; - - /** - * Handles an error. - * - * @api private - */ - - Socket.prototype.onError = function (err) { - if (err && err.advice) { - if (err.advice === 'reconnect' && (this.connected || this.connecting)) { - this.disconnect(); - if (this.options.reconnect) { - this.reconnect(); - } - } - } - - this.publish('error', err && err.reason ? err.reason : err); - }; - - /** - * Called when the transport disconnects. - * - * @api private - */ - - Socket.prototype.onDisconnect = function (reason) { - var wasConnected = this.connected - , wasConnecting = this.connecting; - - this.connected = false; - this.connecting = false; - this.open = false; - - if (wasConnected || wasConnecting) { - this.transport.close(); - this.transport.clearTimeouts(); - if (wasConnected) { - this.publish('disconnect', reason); - - if ('booted' != reason && this.options.reconnect && !this.reconnecting) { - this.reconnect(); - } - } - } - }; - - /** - * Called upon reconnection. - * - * @api private - */ - - Socket.prototype.reconnect = function () { - this.reconnecting = true; - this.reconnectionAttempts = 0; - this.reconnectionDelay = this.options['reconnection delay']; - - var self = this - , maxAttempts = this.options['max reconnection attempts'] - , tryMultiple = this.options['try multiple transports'] - , limit = this.options['reconnection limit']; - - function reset () { - if (self.connected) { - for (var i in self.namespaces) { - if (self.namespaces.hasOwnProperty(i) && '' !== i) { - self.namespaces[i].packet({ type: 'connect' }); - } - } - self.publish('reconnect', self.transport.name, self.reconnectionAttempts); - } - - clearTimeout(self.reconnectionTimer); - - self.removeListener('connect_failed', maybeReconnect); - self.removeListener('connect', maybeReconnect); - - self.reconnecting = false; - - delete self.reconnectionAttempts; - delete self.reconnectionDelay; - delete self.reconnectionTimer; - delete self.redoTransports; - - self.options['try multiple transports'] = tryMultiple; - }; - - function maybeReconnect () { - if (!self.reconnecting) { - return; - } - - if (self.connected) { - return reset(); - }; - - if (self.connecting && self.reconnecting) { - return self.reconnectionTimer = setTimeout(maybeReconnect, 1000); - } - - if (self.reconnectionAttempts++ >= maxAttempts) { - if (!self.redoTransports) { - self.on('connect_failed', maybeReconnect); - self.options['try multiple transports'] = true; - self.transport = self.getTransport(); - self.redoTransports = true; - self.connect(); - } else { - self.publish('reconnect_failed'); - reset(); - } - } else { - if (self.reconnectionDelay < limit) { - self.reconnectionDelay *= 2; // exponential back off - } - - self.connect(); - self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts); - self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay); - } - }; - - this.options['try multiple transports'] = false; - this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay); - - this.on('connect', maybeReconnect); - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.SocketNamespace = SocketNamespace; - - /** - * Socket namespace constructor. - * - * @constructor - * @api public - */ - - function SocketNamespace (socket, name) { - this.socket = socket; - this.name = name || ''; - this.flags = {}; - this.json = new Flag(this, 'json'); - this.ackPackets = 0; - this.acks = {}; - }; - - /** - * Apply EventEmitter mixin. - */ - - io.util.mixin(SocketNamespace, io.EventEmitter); - - /** - * Copies emit since we override it - * - * @api private - */ - - SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit; - - /** - * Creates a new namespace, by proxying the request to the socket. This - * allows us to use the synax as we do on the server. - * - * @api public - */ - - SocketNamespace.prototype.of = function () { - return this.socket.of.apply(this.socket, arguments); - }; - - /** - * Sends a packet. - * - * @api private - */ - - SocketNamespace.prototype.packet = function (packet) { - packet.endpoint = this.name; - this.socket.packet(packet); - this.flags = {}; - return this; - }; - - /** - * Sends a message - * - * @api public - */ - - SocketNamespace.prototype.send = function (data, fn) { - var packet = { - type: this.flags.json ? 'json' : 'message' - , data: data - }; - - if ('function' == typeof fn) { - packet.id = ++this.ackPackets; - packet.ack = true; - this.acks[packet.id] = fn; - } - - return this.packet(packet); - }; - - /** - * Emits an event - * - * @api public - */ - - SocketNamespace.prototype.emit = function (name) { - var args = Array.prototype.slice.call(arguments, 1) - , lastArg = args[args.length - 1] - , packet = { - type: 'event' - , name: name - }; - - if ('function' == typeof lastArg) { - packet.id = ++this.ackPackets; - packet.ack = 'data'; - this.acks[packet.id] = lastArg; - args = args.slice(0, args.length - 1); - } - - packet.args = args; - - return this.packet(packet); - }; - - /** - * Disconnects the namespace - * - * @api private - */ - - SocketNamespace.prototype.disconnect = function () { - if (this.name === '') { - this.socket.disconnect(); - } else { - this.packet({ type: 'disconnect' }); - this.$emit('disconnect'); - } - - return this; - }; - - /** - * Handles a packet - * - * @api private - */ - - SocketNamespace.prototype.onPacket = function (packet) { - var self = this; - - function ack () { - self.packet({ - type: 'ack' - , args: io.util.toArray(arguments) - , ackId: packet.id - }); - }; - - switch (packet.type) { - case 'connect': - this.$emit('connect'); - break; - - case 'disconnect': - if (this.name === '') { - this.socket.onDisconnect(packet.reason || 'booted'); - } else { - this.$emit('disconnect', packet.reason); - } - break; - - case 'message': - case 'json': - var params = ['message', packet.data]; - - if (packet.ack == 'data') { - params.push(ack); - } else if (packet.ack) { - this.packet({ type: 'ack', ackId: packet.id }); - } - - this.$emit.apply(this, params); - break; - - case 'event': - var params = [packet.name].concat(packet.args); - - if (packet.ack == 'data') - params.push(ack); - - this.$emit.apply(this, params); - break; - - case 'ack': - if (this.acks[packet.ackId]) { - this.acks[packet.ackId].apply(this, packet.args); - delete this.acks[packet.ackId]; - } - break; - - case 'error': - if (packet.advice){ - this.socket.onError(packet); - } else { - if (packet.reason == 'unauthorized') { - this.$emit('connect_failed', packet.reason); - } else { - this.$emit('error', packet.reason); - } - } - break; - } - }; - - /** - * Flag interface. - * - * @api private - */ - - function Flag (nsp, name) { - this.namespace = nsp; - this.name = name; - }; - - /** - * Send a message - * - * @api public - */ - - Flag.prototype.send = function () { - this.namespace.flags[this.name] = true; - this.namespace.send.apply(this.namespace, arguments); - }; - - /** - * Emit an event - * - * @api public - */ - - Flag.prototype.emit = function () { - this.namespace.flags[this.name] = true; - this.namespace.emit.apply(this.namespace, arguments); - }; - -})( - 'undefined' != typeof io ? io : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - */ - - exports.websocket = WS; - - /** - * The WebSocket transport uses the HTML5 WebSocket API to establish an - * persistent connection with the Socket.IO server. This transport will also - * be inherited by the FlashSocket fallback as it provides a API compatible - * polyfill for the WebSockets. - * - * @constructor - * @extends {io.Transport} - * @api public - */ - - function WS (socket) { - io.Transport.apply(this, arguments); - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(WS, io.Transport); - - /** - * Transport name - * - * @api public - */ - - WS.prototype.name = 'websocket'; - - /** - * Initializes a new `WebSocket` connection with the Socket.IO server. We attach - * all the appropriate listeners to handle the responses from the server. - * - * @returns {Transport} - * @api public - */ - - WS.prototype.open = function () { - var query = io.util.query(this.socket.options.query) - , self = this - , Socket - - - if (!Socket) { - Socket = global.MozWebSocket || global.WebSocket; - } - - this.websocket = new Socket(this.prepareUrl() + query); - - this.websocket.onopen = function () { - self.onOpen(); - self.socket.setBuffer(false); - }; - this.websocket.onmessage = function (ev) { - self.onData(ev.data); - }; - this.websocket.onclose = function () { - self.onClose(); - self.socket.setBuffer(true); - }; - this.websocket.onerror = function (e) { - self.onError(e); - }; - - return this; - }; - - /** - * Send a message to the Socket.IO server. The message will automatically be - * encoded in the correct message format. - * - * @returns {Transport} - * @api public - */ - - WS.prototype.send = function (data) { - this.websocket.send(data); - return this; - }; - - /** - * Payload - * - * @api private - */ - - WS.prototype.payload = function (arr) { - for (var i = 0, l = arr.length; i < l; i++) { - this.packet(arr[i]); - } - return this; - }; - - /** - * Disconnect the established `WebSocket` connection. - * - * @returns {Transport} - * @api public - */ - - WS.prototype.close = function () { - this.websocket.close(); - return this; - }; - - /** - * Handle the errors that `WebSocket` might be giving when we - * are attempting to connect or send messages. - * - * @param {Error} e The error. - * @api private - */ - - WS.prototype.onError = function (e) { - this.socket.onError(e); - }; - - /** - * Returns the appropriate scheme for the URI generation. - * - * @api private - */ - WS.prototype.scheme = function () { - return this.socket.options.secure ? 'wss' : 'ws'; - }; - - /** - * Checks if the browser has support for native `WebSockets` and that - * it's not the polyfill created for the FlashSocket transport. - * - * @return {Boolean} - * @api public - */ - - WS.check = function () { - return ('WebSocket' in global && !('__addTask' in WebSocket)) - || 'MozWebSocket' in global; - }; - - /** - * Check if the `WebSocket` transport support cross domain communications. - * - * @returns {Boolean} - * @api public - */ - - WS.xdomainCheck = function () { - return true; - }; - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('websocket'); - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.flashsocket = Flashsocket; - - /** - * The FlashSocket transport. This is a API wrapper for the HTML5 WebSocket - * specification. It uses a .swf file to communicate with the server. If you want - * to serve the .swf file from a other server than where the Socket.IO script is - * coming from you need to use the insecure version of the .swf. More information - * about this can be found on the github page. - * - * @constructor - * @extends {io.Transport.websocket} - * @api public - */ - - function Flashsocket () { - io.Transport.websocket.apply(this, arguments); - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(Flashsocket, io.Transport.websocket); - - /** - * Transport name - * - * @api public - */ - - Flashsocket.prototype.name = 'flashsocket'; - - /** - * Disconnect the established `FlashSocket` connection. This is done by adding a - * new task to the FlashSocket. The rest will be handled off by the `WebSocket` - * transport. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.open = function () { - var self = this - , args = arguments; - - WebSocket.__addTask(function () { - io.Transport.websocket.prototype.open.apply(self, args); - }); - return this; - }; - - /** - * Sends a message to the Socket.IO server. This is done by adding a new - * task to the FlashSocket. The rest will be handled off by the `WebSocket` - * transport. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.send = function () { - var self = this, args = arguments; - WebSocket.__addTask(function () { - io.Transport.websocket.prototype.send.apply(self, args); - }); - return this; - }; - - /** - * Disconnects the established `FlashSocket` connection. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.close = function () { - WebSocket.__tasks.length = 0; - io.Transport.websocket.prototype.close.call(this); - return this; - }; - - /** - * The WebSocket fall back needs to append the flash container to the body - * element, so we need to make sure we have access to it. Or defer the call - * until we are sure there is a body element. - * - * @param {Socket} socket The socket instance that needs a transport - * @param {Function} fn The callback - * @api private - */ - - Flashsocket.prototype.ready = function (socket, fn) { - function init () { - var options = socket.options - , port = options['flash policy port'] - , path = [ - 'http' + (options.secure ? 's' : '') + ':/' - , options.host + ':' + options.port - , options.resource - , 'static/flashsocket' - , 'WebSocketMain' + (socket.isXDomain() ? 'Insecure' : '') + '.swf' - ]; - - // Only start downloading the swf file when the checked that this browser - // actually supports it - if (!Flashsocket.loaded) { - if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') { - // Set the correct file based on the XDomain settings - WEB_SOCKET_SWF_LOCATION = path.join('/'); - } - - if (port !== 843) { - WebSocket.loadFlashPolicyFile('xmlsocket://' + options.host + ':' + port); - } - - WebSocket.__initialize(); - Flashsocket.loaded = true; - } - - fn.call(self); - } - - var self = this; - if (document.body) return init(); - - io.util.load(init); - }; - - /** - * Check if the FlashSocket transport is supported as it requires that the Adobe - * Flash Player plug-in version `10.0.0` or greater is installed. And also check if - * the polyfill is correctly loaded. - * - * @returns {Boolean} - * @api public - */ - - Flashsocket.check = function () { - if ( - typeof WebSocket == 'undefined' - || !('__initialize' in WebSocket) || !swfobject - ) return false; - - return swfobject.getFlashPlayerVersion().major >= 10; - }; - - /** - * Check if the FlashSocket transport can be used as cross domain / cross origin - * transport. Because we can't see which type (secure or insecure) of .swf is used - * we will just return true. - * - * @returns {Boolean} - * @api public - */ - - Flashsocket.xdomainCheck = function () { - return true; - }; - - /** - * Disable AUTO_INITIALIZATION - */ - - if (typeof window != 'undefined') { - WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true; - } - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('flashsocket'); -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/* SWFObject v2.2 - is released under the MIT License -*/ -if ('undefined' != typeof window) { -var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O[(['Active'].concat('Object').join('X'))]!=D){try{var ad=new window[(['Active'].concat('Object').join('X'))](W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab -// License: New BSD License -// Reference: http://dev.w3.org/html5/websockets/ -// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol - -(function() { - - if ('undefined' == typeof window || window.WebSocket) return; - - var console = window.console; - if (!console || !console.log || !console.error) { - console = {log: function(){ }, error: function(){ }}; - } - - if (!swfobject.hasFlashPlayerVersion("10.0.0")) { - console.error("Flash Player >= 10.0.0 is required."); - return; - } - if (location.protocol == "file:") { - console.error( - "WARNING: web-socket-js doesn't work in file:///... URL " + - "unless you set Flash Security Settings properly. " + - "Open the page via Web server i.e. http://..."); - } - - /** - * This class represents a faux web socket. - * @param {string} url - * @param {array or string} protocols - * @param {string} proxyHost - * @param {int} proxyPort - * @param {string} headers - */ - WebSocket = function(url, protocols, proxyHost, proxyPort, headers) { - var self = this; - self.__id = WebSocket.__nextId++; - WebSocket.__instances[self.__id] = self; - self.readyState = WebSocket.CONNECTING; - self.bufferedAmount = 0; - self.__events = {}; - if (!protocols) { - protocols = []; - } else if (typeof protocols == "string") { - protocols = [protocols]; - } - // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. - // Otherwise, when onopen fires immediately, onopen is called before it is set. - setTimeout(function() { - WebSocket.__addTask(function() { - WebSocket.__flash.create( - self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null); - }); - }, 0); - }; - - /** - * Send data to the web socket. - * @param {string} data The data to send to the socket. - * @return {boolean} True for success, false for failure. - */ - WebSocket.prototype.send = function(data) { - if (this.readyState == WebSocket.CONNECTING) { - throw "INVALID_STATE_ERR: Web Socket connection has not been established"; - } - // We use encodeURIComponent() here, because FABridge doesn't work if - // the argument includes some characters. We don't use escape() here - // because of this: - // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions - // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't - // preserve all Unicode characters either e.g. "\uffff" in Firefox. - // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require - // additional testing. - var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data)); - if (result < 0) { // success - return true; - } else { - this.bufferedAmount += result; - return false; - } - }; - - /** - * Close this web socket gracefully. - */ - WebSocket.prototype.close = function() { - if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { - return; - } - this.readyState = WebSocket.CLOSING; - WebSocket.__flash.close(this.__id); - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture - * @return void - */ - WebSocket.prototype.addEventListener = function(type, listener, useCapture) { - if (!(type in this.__events)) { - this.__events[type] = []; - } - this.__events[type].push(listener); - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture - * @return void - */ - WebSocket.prototype.removeEventListener = function(type, listener, useCapture) { - if (!(type in this.__events)) return; - var events = this.__events[type]; - for (var i = events.length - 1; i >= 0; --i) { - if (events[i] === listener) { - events.splice(i, 1); - break; - } - } - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {Event} event - * @return void - */ - WebSocket.prototype.dispatchEvent = function(event) { - var events = this.__events[event.type] || []; - for (var i = 0; i < events.length; ++i) { - events[i](event); - } - var handler = this["on" + event.type]; - if (handler) handler(event); - }; - - /** - * Handles an event from Flash. - * @param {Object} flashEvent - */ - WebSocket.prototype.__handleEvent = function(flashEvent) { - if ("readyState" in flashEvent) { - this.readyState = flashEvent.readyState; - } - if ("protocol" in flashEvent) { - this.protocol = flashEvent.protocol; - } - - var jsEvent; - if (flashEvent.type == "open" || flashEvent.type == "error") { - jsEvent = this.__createSimpleEvent(flashEvent.type); - } else if (flashEvent.type == "close") { - // TODO implement jsEvent.wasClean - jsEvent = this.__createSimpleEvent("close"); - } else if (flashEvent.type == "message") { - var data = decodeURIComponent(flashEvent.message); - jsEvent = this.__createMessageEvent("message", data); - } else { - throw "unknown event type: " + flashEvent.type; - } - - this.dispatchEvent(jsEvent); - }; - - WebSocket.prototype.__createSimpleEvent = function(type) { - if (document.createEvent && window.Event) { - var event = document.createEvent("Event"); - event.initEvent(type, false, false); - return event; - } else { - return {type: type, bubbles: false, cancelable: false}; - } - }; - - WebSocket.prototype.__createMessageEvent = function(type, data) { - if (document.createEvent && window.MessageEvent && !window.opera) { - var event = document.createEvent("MessageEvent"); - event.initMessageEvent("message", false, false, data, null, null, window, null); - return event; - } else { - // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes. - return {type: type, data: data, bubbles: false, cancelable: false}; - } - }; - - /** - * Define the WebSocket readyState enumeration. - */ - WebSocket.CONNECTING = 0; - WebSocket.OPEN = 1; - WebSocket.CLOSING = 2; - WebSocket.CLOSED = 3; - - WebSocket.__flash = null; - WebSocket.__instances = {}; - WebSocket.__tasks = []; - WebSocket.__nextId = 0; - - /** - * Load a new flash security policy file. - * @param {string} url - */ - WebSocket.loadFlashPolicyFile = function(url){ - WebSocket.__addTask(function() { - WebSocket.__flash.loadManualPolicyFile(url); - }); - }; - - /** - * Loads WebSocketMain.swf and creates WebSocketMain object in Flash. - */ - WebSocket.__initialize = function() { - if (WebSocket.__flash) return; - - if (WebSocket.__swfLocation) { - // For backword compatibility. - window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; - } - if (!window.WEB_SOCKET_SWF_LOCATION) { - console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); - return; - } - var container = document.createElement("div"); - container.id = "webSocketContainer"; - // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents - // Flash from loading at least in IE. So we move it out of the screen at (-100, -100). - // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash - // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is - // the best we can do as far as we know now. - container.style.position = "absolute"; - if (WebSocket.__isFlashLite()) { - container.style.left = "0px"; - container.style.top = "0px"; - } else { - container.style.left = "-100px"; - container.style.top = "-100px"; - } - var holder = document.createElement("div"); - holder.id = "webSocketFlash"; - container.appendChild(holder); - document.body.appendChild(container); - // See this article for hasPriority: - // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html - swfobject.embedSWF( - WEB_SOCKET_SWF_LOCATION, - "webSocketFlash", - "1" /* width */, - "1" /* height */, - "10.0.0" /* SWF version */, - null, - null, - {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"}, - null, - function(e) { - if (!e.success) { - console.error("[WebSocket] swfobject.embedSWF failed"); - } - }); - }; - - /** - * Called by Flash to notify JS that it's fully loaded and ready - * for communication. - */ - WebSocket.__onFlashInitialized = function() { - // We need to set a timeout here to avoid round-trip calls - // to flash during the initialization process. - setTimeout(function() { - WebSocket.__flash = document.getElementById("webSocketFlash"); - WebSocket.__flash.setCallerUrl(location.href); - WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); - for (var i = 0; i < WebSocket.__tasks.length; ++i) { - WebSocket.__tasks[i](); - } - WebSocket.__tasks = []; - }, 0); - }; - - /** - * Called by Flash to notify WebSockets events are fired. - */ - WebSocket.__onFlashEvent = function() { - setTimeout(function() { - try { - // Gets events using receiveEvents() instead of getting it from event object - // of Flash event. This is to make sure to keep message order. - // It seems sometimes Flash events don't arrive in the same order as they are sent. - var events = WebSocket.__flash.receiveEvents(); - for (var i = 0; i < events.length; ++i) { - WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); - } - } catch (e) { - console.error(e); - } - }, 0); - return true; - }; - - // Called by Flash. - WebSocket.__log = function(message) { - console.log(decodeURIComponent(message)); - }; - - // Called by Flash. - WebSocket.__error = function(message) { - console.error(decodeURIComponent(message)); - }; - - WebSocket.__addTask = function(task) { - if (WebSocket.__flash) { - task(); - } else { - WebSocket.__tasks.push(task); - } - }; - - /** - * Test if the browser is running flash lite. - * @return {boolean} True if flash lite is running, false otherwise. - */ - WebSocket.__isFlashLite = function() { - if (!window.navigator || !window.navigator.mimeTypes) { - return false; - } - var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"]; - if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) { - return false; - } - return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; - }; - - if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { - if (window.addEventListener) { - window.addEventListener("load", function(){ - WebSocket.__initialize(); - }, false); - } else { - window.attachEvent("onload", function(){ - WebSocket.__initialize(); - }); - } - } - -})(); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - * - * @api public - */ - - exports.XHR = XHR; - - /** - * XHR constructor - * - * @costructor - * @api public - */ - - function XHR (socket) { - if (!socket) return; - - io.Transport.apply(this, arguments); - this.sendBuffer = []; - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(XHR, io.Transport); - - /** - * Establish a connection - * - * @returns {Transport} - * @api public - */ - - XHR.prototype.open = function () { - this.socket.setBuffer(false); - this.onOpen(); - this.get(); - - // we need to make sure the request succeeds since we have no indication - // whether the request opened or not until it succeeded. - this.setCloseTimeout(); - - return this; - }; - - /** - * Check if we need to send data to the Socket.IO server, if we have data in our - * buffer we encode it and forward it to the `post` method. - * - * @api private - */ - - XHR.prototype.payload = function (payload) { - var msgs = []; - - for (var i = 0, l = payload.length; i < l; i++) { - msgs.push(io.parser.encodePacket(payload[i])); - } - - this.send(io.parser.encodePayload(msgs)); - }; - - /** - * Send data to the Socket.IO server. - * - * @param data The message - * @returns {Transport} - * @api public - */ - - XHR.prototype.send = function (data) { - this.post(data); - return this; - }; - - /** - * Posts a encoded message to the Socket.IO server. - * - * @param {String} data A encoded message. - * @api private - */ - - function empty () { }; - - XHR.prototype.post = function (data) { - var self = this; - this.socket.setBuffer(true); - - function stateChange () { - if (this.readyState == 4) { - this.onreadystatechange = empty; - self.posting = false; - - if (this.status == 200){ - self.socket.setBuffer(false); - } else { - self.onClose(); - } - } - } - - function onload () { - this.onload = empty; - self.socket.setBuffer(false); - }; - - this.sendXHR = this.request('POST'); - - if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) { - this.sendXHR.onload = this.sendXHR.onerror = onload; - } else { - this.sendXHR.onreadystatechange = stateChange; - } - - this.sendXHR.send(data); - }; - - /** - * Disconnects the established `XHR` connection. - * - * @returns {Transport} - * @api public - */ - - XHR.prototype.close = function () { - this.onClose(); - return this; - }; - - /** - * Generates a configured XHR request - * - * @param {String} url The url that needs to be requested. - * @param {String} method The method the request should use. - * @returns {XMLHttpRequest} - * @api private - */ - - XHR.prototype.request = function (method) { - var req = io.util.request(this.socket.isXDomain()) - , query = io.util.query(this.socket.options.query, 't=' + +new Date); - - req.open(method || 'GET', this.prepareUrl() + query, true); - - if (method == 'POST') { - try { - if (req.setRequestHeader) { - req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); - } else { - // XDomainRequest - req.contentType = 'text/plain'; - } - } catch (e) {} - } - - return req; - }; - - /** - * Returns the scheme to use for the transport URLs. - * - * @api private - */ - - XHR.prototype.scheme = function () { - return this.socket.options.secure ? 'https' : 'http'; - }; - - /** - * Check if the XHR transports are supported - * - * @param {Boolean} xdomain Check if we support cross domain requests. - * @returns {Boolean} - * @api public - */ - - XHR.check = function (socket, xdomain) { - try { - var request = io.util.request(xdomain), - usesXDomReq = (global.XDomainRequest && request instanceof XDomainRequest), - socketProtocol = (socket && socket.options && socket.options.secure ? 'https:' : 'http:'), - isXProtocol = (socketProtocol != global.location.protocol); - if (request && !(usesXDomReq && isXProtocol)) { - return true; - } - } catch(e) {} - - return false; - }; - - /** - * Check if the XHR transport supports cross domain requests. - * - * @returns {Boolean} - * @api public - */ - - XHR.xdomainCheck = function () { - return XHR.check(null, true); - }; - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - */ - - exports.htmlfile = HTMLFile; - - /** - * The HTMLFile transport creates a `forever iframe` based transport - * for Internet Explorer. Regular forever iframe implementations will - * continuously trigger the browsers buzy indicators. If the forever iframe - * is created inside a `htmlfile` these indicators will not be trigged. - * - * @constructor - * @extends {io.Transport.XHR} - * @api public - */ - - function HTMLFile (socket) { - io.Transport.XHR.apply(this, arguments); - }; - - /** - * Inherits from XHR transport. - */ - - io.util.inherit(HTMLFile, io.Transport.XHR); - - /** - * Transport name - * - * @api public - */ - - HTMLFile.prototype.name = 'htmlfile'; - - /** - * Creates a new Ac...eX `htmlfile` with a forever loading iframe - * that can be used to listen to messages. Inside the generated - * `htmlfile` a reference will be made to the HTMLFile transport. - * - * @api private - */ - - HTMLFile.prototype.get = function () { - this.doc = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); - this.doc.open(); - this.doc.write(''); - this.doc.close(); - this.doc.parentWindow.s = this; - - var iframeC = this.doc.createElement('div'); - iframeC.className = 'socketio'; - - this.doc.body.appendChild(iframeC); - this.iframe = this.doc.createElement('iframe'); - - iframeC.appendChild(this.iframe); - - var self = this - , query = io.util.query(this.socket.options.query, 't='+ +new Date); - - this.iframe.src = this.prepareUrl() + query; - - io.util.on(window, 'unload', function () { - self.destroy(); - }); - }; - - /** - * The Socket.IO server will write script tags inside the forever - * iframe, this function will be used as callback for the incoming - * information. - * - * @param {String} data The message - * @param {document} doc Reference to the context - * @api private - */ - - HTMLFile.prototype._ = function (data, doc) { - this.onData(data); - try { - var script = doc.getElementsByTagName('script')[0]; - script.parentNode.removeChild(script); - } catch (e) { } - }; - - /** - * Destroy the established connection, iframe and `htmlfile`. - * And calls the `CollectGarbage` function of Internet Explorer - * to release the memory. - * - * @api private - */ - - HTMLFile.prototype.destroy = function () { - if (this.iframe){ - try { - this.iframe.src = 'about:blank'; - } catch(e){} - - this.doc = null; - this.iframe.parentNode.removeChild(this.iframe); - this.iframe = null; - - CollectGarbage(); - } - }; - - /** - * Disconnects the established connection. - * - * @returns {Transport} Chaining. - * @api public - */ - - HTMLFile.prototype.close = function () { - this.destroy(); - return io.Transport.XHR.prototype.close.call(this); - }; - - /** - * Checks if the browser supports this transport. The browser - * must have an `Ac...eXObject` implementation. - * - * @return {Boolean} - * @api public - */ - - HTMLFile.check = function () { - if (typeof window != "undefined" && (['Active'].concat('Object').join('X')) in window){ - try { - var a = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); - return a && io.Transport.XHR.check(); - } catch(e){} - } - return false; - }; - - /** - * Check if cross domain requests are supported. - * - * @returns {Boolean} - * @api public - */ - - HTMLFile.xdomainCheck = function () { - // we can probably do handling for sub-domains, we should - // test that it's cross domain but a subdomain here - return false; - }; - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('htmlfile'); - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - - /** - * Expose constructor. - */ - - exports['xhr-polling'] = XHRPolling; - - /** - * The XHR-polling transport uses long polling XHR requests to create a - * "persistent" connection with the server. - * - * @constructor - * @api public - */ - - function XHRPolling () { - io.Transport.XHR.apply(this, arguments); - }; - - /** - * Inherits from XHR transport. - */ - - io.util.inherit(XHRPolling, io.Transport.XHR); - - /** - * Merge the properties from XHR transport - */ - - io.util.merge(XHRPolling, io.Transport.XHR); - - /** - * Transport name - * - * @api public - */ - - XHRPolling.prototype.name = 'xhr-polling'; - - /** - * Establish a connection, for iPhone and Android this will be done once the page - * is loaded. - * - * @returns {Transport} Chaining. - * @api public - */ - - XHRPolling.prototype.open = function () { - var self = this; - - io.Transport.XHR.prototype.open.call(self); - return false; - }; - - /** - * Starts a XHR request to wait for incoming messages. - * - * @api private - */ - - function empty () {}; - - XHRPolling.prototype.get = function () { - if (!this.open) return; - - var self = this; - - function stateChange () { - if (this.readyState == 4) { - this.onreadystatechange = empty; - - if (this.status == 200) { - self.onData(this.responseText); - self.get(); - } else { - self.onClose(); - } - } - }; - - function onload () { - this.onload = empty; - this.onerror = empty; - self.onData(this.responseText); - self.get(); - }; - - function onerror () { - self.onClose(); - }; - - this.xhr = this.request(); - - if (global.XDomainRequest && this.xhr instanceof XDomainRequest) { - this.xhr.onload = onload; - this.xhr.onerror = onerror; - } else { - this.xhr.onreadystatechange = stateChange; - } - - this.xhr.send(null); - }; - - /** - * Handle the unclean close behavior. - * - * @api private - */ - - XHRPolling.prototype.onClose = function () { - io.Transport.XHR.prototype.onClose.call(this); - - if (this.xhr) { - this.xhr.onreadystatechange = this.xhr.onload = this.xhr.onerror = empty; - try { - this.xhr.abort(); - } catch(e){} - this.xhr = null; - } - }; - - /** - * Webkit based browsers show a infinit spinner when you start a XHR request - * before the browsers onload event is called so we need to defer opening of - * the transport until the onload event is called. Wrapping the cb in our - * defer method solve this. - * - * @param {Socket} socket The socket instance that needs a transport - * @param {Function} fn The callback - * @api private - */ - - XHRPolling.prototype.ready = function (socket, fn) { - var self = this; - - io.util.defer(function () { - fn.call(self); - }); - }; - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('xhr-polling'); - -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports - , this -); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io, global) { - /** - * There is a way to hide the loading indicator in Firefox. If you create and - * remove a iframe it will stop showing the current loading indicator. - * Unfortunately we can't feature detect that and UA sniffing is evil. - * - * @api private - */ - - var indicator = global.document && "MozAppearance" in - global.document.documentElement.style; - - /** - * Expose constructor. - */ - - exports['jsonp-polling'] = JSONPPolling; - - /** - * The JSONP transport creates an persistent connection by dynamically - * inserting a script tag in the page. This script tag will receive the - * information of the Socket.IO server. When new information is received - * it creates a new script tag for the new data stream. - * - * @constructor - * @extends {io.Transport.xhr-polling} - * @api public - */ - - function JSONPPolling (socket) { - io.Transport['xhr-polling'].apply(this, arguments); - - this.index = io.j.length; - - var self = this; - - io.j.push(function (msg) { - self._(msg); - }); - }; - - /** - * Inherits from XHR polling transport. - */ - - io.util.inherit(JSONPPolling, io.Transport['xhr-polling']); - - /** - * Transport name - * - * @api public - */ - - JSONPPolling.prototype.name = 'jsonp-polling'; - - /** - * Posts a encoded message to the Socket.IO server using an iframe. - * The iframe is used because script tags can create POST based requests. - * The iframe is positioned outside of the view so the user does not - * notice it's existence. - * - * @param {String} data A encoded message. - * @api private - */ - - JSONPPolling.prototype.post = function (data) { - var self = this - , query = io.util.query( - this.socket.options.query - , 't='+ (+new Date) + '&i=' + this.index - ); - - if (!this.form) { - var form = document.createElement('form') - , area = document.createElement('textarea') - , id = this.iframeId = 'socketio_iframe_' + this.index - , iframe; - - form.className = 'socketio'; - form.style.position = 'absolute'; - form.style.top = '0px'; - form.style.left = '0px'; - form.style.display = 'none'; - form.target = id; - form.method = 'POST'; - form.setAttribute('accept-charset', 'utf-8'); - area.name = 'd'; - form.appendChild(area); - document.body.appendChild(form); - - this.form = form; - this.area = area; - } - - this.form.action = this.prepareUrl() + query; - - function complete () { - initIframe(); - self.socket.setBuffer(false); - }; - - function initIframe () { - if (self.iframe) { - self.form.removeChild(self.iframe); - } - - try { - // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) - iframe = document.createElement('