├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── lib ├── agent │ ├── actor.js │ └── agent.js ├── common │ ├── logging.js │ └── util.js ├── console │ ├── css │ │ ├── colors.css │ │ └── web_client.css │ ├── http.js │ ├── images │ │ └── icons │ │ │ ├── bin.png │ │ │ ├── control_pause.png │ │ │ ├── control_play.png │ │ │ └── cross.png │ ├── index.html │ └── js │ │ ├── lib │ │ ├── jquery.min.js │ │ ├── scrollTo.jquery.js │ │ ├── socket.io.js │ │ └── underscore.min.js │ │ ├── nodeclient.js │ │ ├── ui │ │ ├── graph.js │ │ ├── layout.js │ │ ├── renderers.jquery.js │ │ └── webclient.jquery.js │ │ └── webclient.js ├── master │ ├── nodeclient.js │ ├── server.js │ ├── starter.js │ └── webclient.js ├── monitor │ ├── monitor.js │ └── stat.js └── robot.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | */node-log.log 3 | logs/*.log 4 | !.gitignore 5 | node_modules/* 6 | .project 7 | .settings/ 8 | **/*.svn 9 | *.svn 10 | *.sublime-project 11 | *.sublime-workspace 12 | *.swp 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2012 Netease, Inc. and other pomelo contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #pomelo-robot 2 | pomelo-robot is a simple tool to benchmark the socket.io server's performance. 3 | 4 | pomelo-robot can run in multiple mode such as single machine or distributed machines with many processes. 5 | 6 | pomelo-robot executes developer's custom javascript in a sand box and statistical analysis monitors including avg(min/max) responsing time and QPS, etc. Then reports data to the http server with graph display. 7 | 8 | pomelo-robot can be also used in http benchmark with developer script. 9 | 10 | 11 | ##Installation 12 | ``` 13 | npm install pomelo-robot 14 | ``` 15 | 16 | ##Usage 17 | ``` javascript 18 | var envConfig = require('./app/config/env.json'); 19 | var config = require('./app/config/' + envConfig.env + '/config'); 20 | var Robot = require('pomelo-robot').Robot; 21 | 22 | var robot = new Robot(config); 23 | var mode = 'master'; 24 | 25 | if (process.argv.length > 2){ 26 | mode = process.argv[2]; 27 | } 28 | 29 | if (mode !== 'master' && mode !== 'client') { 30 | throw new Error(' mode must be master or client'); 31 | } 32 | 33 | if (mode === 'master') { 34 | robot.runMaster(__filename); 35 | } else { 36 | var script = (process.cwd() + envConfig.script); 37 | robot.runAgent(script); 38 | } 39 | ``` 40 | 41 | ##API 42 | ###robot.runMaster() 43 | run master server and http server, then init server status including clients with startup file. 44 | ####Arguments 45 | + startupFile - The master server auto startup agent file name, default is current running file; 46 | 47 | ###robot.runAgent() 48 | robot run in client agent mode. 49 | ####Arguments 50 | + script - The developer's custom script that the agent will execute. 51 | 52 | ###Notice 53 | When pomelo-robot run in distribute mode, every client should be in same directory path and master could be ssh login automatic. Otherwise developer can start up agent manually. For the custom script, refer to [the demo](https://github.com/NetEase/pomelo-robot-demo). 54 | 55 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports.Server = require('./lib/master/server').Server; 2 | module.exports.Agent = require('./lib/agent/agent').Agent; 3 | module.exports.HTTP_SERVER = require('./lib/console/http').HTTP_SERVER; 4 | module.exports.Robot = require('./lib/robot').Robot; 5 | -------------------------------------------------------------------------------- /lib/agent/actor.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var vm = require('vm'); 3 | var EventEmitter = require('events').EventEmitter; 4 | var monitor = require('../monitor/monitor'); 5 | var envConfig = require(process.cwd() + '/app/config/env.json'); 6 | var fs = require('fs'); 7 | var script = fs.readFileSync(process.cwd()+envConfig.script, 'utf8'); 8 | 9 | var Actor = function(conf,aid) { 10 | EventEmitter.call(this); 11 | this.id = aid; 12 | this.script = conf.script || script; 13 | var self = this; 14 | self.on('start',function(action,reqId){ 15 | monitor.beginTime(action,self.id,reqId); 16 | }); 17 | self.on('end',function(action,reqId){ 18 | monitor.endTime(action,self.id,reqId); 19 | }); 20 | self.on('incr',function(action){ 21 | monitor.incr(action); 22 | }); 23 | self.on('decr',function(action){ 24 | monitor.decr(action); 25 | }); 26 | }; 27 | 28 | util.inherits(Actor, EventEmitter); 29 | 30 | var pro = Actor.prototype; 31 | 32 | pro.run = function() { 33 | try { 34 | var initSandbox = { 35 | console:console, 36 | require:require, 37 | actor:this, 38 | setTimeout:setTimeout, 39 | clearTimeout:clearTimeout, 40 | setInterval:setInterval, 41 | clearInterval:clearInterval, 42 | global:global, 43 | process:process 44 | }; 45 | 46 | var context = vm.createContext(initSandbox); 47 | vm.runInContext(script,context); 48 | } catch(ex){ 49 | this.emit('error',ex.stack); 50 | } 51 | }; 52 | 53 | /** 54 | * clear data 55 | * 56 | */ 57 | pro.reset = function() { 58 | monitor.clear(); 59 | }; 60 | 61 | /** 62 | * wrap setTimeout 63 | * 64 | *@param {Function} fn 65 | *@param {Number} time 66 | */ 67 | pro.later = function(fn,time){ 68 | if (time>0 && typeof(fn)=='function') { 69 | return setTimeout(fn,time); 70 | } 71 | }; 72 | 73 | /** 74 | * wrap setInterval 75 | * when time is Array, the interval time is thd random number 76 | * between then 77 | * 78 | *@param {Function} fn 79 | *@param {Number} time 80 | */ 81 | pro.interval = function(fn,time){ 82 | var fn = arguments[0]; 83 | var self = this; 84 | switch (typeof(time)) { 85 | case 'number': 86 | if (arguments[1]>0) return setInterval(fn,arguments[1]); 87 | break; 88 | case 'object': 89 | var start = time[0], end = time[1]; 90 | var time = Math.round(Math.random()*(end-start) +start); 91 | return setTimeout(function(){fn(),self.interval(fn,time);},time); 92 | break; 93 | default: 94 | self.log.error('wrong argument'); 95 | return; 96 | } 97 | }; 98 | 99 | /** 100 | *wrap clearTimeout 101 | * 102 | * @param {Number} timerId 103 | * 104 | */ 105 | pro.clean = function(timerId){ 106 | clearTimeOut(timerId); 107 | } 108 | 109 | /** 110 | *encode message 111 | * 112 | * @param {Number} id 113 | * @param {Object} msg 114 | * 115 | */ 116 | 117 | exports.Actor = Actor; 118 | -------------------------------------------------------------------------------- /lib/agent/agent.js: -------------------------------------------------------------------------------- 1 | var __ = require('underscore'); 2 | var io = require('socket.io-client'); 3 | var logging = require('../common/logging').Logger; 4 | var Actor = require('./actor').Actor; 5 | var monitor = require('../monitor/monitor'); 6 | var fs = require('fs'); 7 | var util = require('../common/util'); 8 | 9 | var STATUS_INTERVAL = 10 * 1000; // 10 seconds 10 | var RECONNECT_INTERVAL = 10 * 1000; // 15 seconds 11 | var HEARTBEAT_PERIOD = 30 * 1000; // 30 seconds 12 | var HEARTBEAT_FAILS = 3; // Reconnect after 3 missed heartbeats 13 | 14 | /** 15 | * 16 | * @param {Object} conf 17 | * init the master and app server for the agent 18 | * include app data, exec script,etc. 19 | * 20 | */ 21 | var Agent = function(conf) { 22 | this.log = logging; 23 | this.conf = conf || {}; 24 | this.last_heartbeat = null; 25 | this.connected = false; 26 | this.reconnecting = false; 27 | this.actors = {}; 28 | this.count = 0; 29 | }; 30 | 31 | Agent.prototype = { 32 | // Create socket, bind callbacks, connect to server 33 | connect: function() { 34 | var agent = this; 35 | var uri = agent.conf.master.host + ":" + agent.conf.master.port; 36 | agent.socket = io.connect(uri,{'force new connection':true,'try multiple transports':false}); 37 | agent.socket.on('error', function(reason) { 38 | agent.reconnect(); 39 | }); 40 | // Register announcement callback 41 | agent.socket.on('connect', function() { 42 | agent.log.info("Connected to server, sending announcement..."); 43 | //console.log(agent.socket.socket.sessionid); 44 | //console.log(require('util').inspect(agent.socket.address,true,10,10)); 45 | agent.announce(agent.socket); 46 | agent.connected = true; 47 | agent.reconnecting = false; 48 | agent.last_heartbeat = new Date().getTime(); 49 | }); 50 | 51 | agent.socket.on('disconnect', function() { 52 | agent.socket.disconnect(); 53 | agent.log.error("Disconnect..."); 54 | }); 55 | // Server heartbeat 56 | agent.socket.on('heartbeat', function() { 57 | //agent.log.info("Received server heartbeat"); 58 | agent.last_heartbeat = new Date().getTime(); 59 | return; 60 | }); 61 | 62 | // Node with same label already exists on server, kill process 63 | agent.socket.on('node_already_exists', function() { 64 | agent.log.error("ERROR: A node of the same name is already registered"); 65 | agent.log.error("with the log server. Change this agent's instance_name."); 66 | agent.log.error("Exiting."); 67 | process.exit(1); 68 | }); 69 | //begin to run 70 | agent.socket.on('run', function(message) { 71 | agent.run(message); 72 | }); 73 | // Exit for BTN_ReReady 74 | agent.socket.on('exit4reready', function() { 75 | agent.log.info("Exit for BTN_ReReady."); 76 | process.exit(0); 77 | }); 78 | }, 79 | 80 | run:function(msg){ 81 | var agent = this; 82 | util.deleteLog(); 83 | this.count = msg.maxuser; 84 | var script = msg.script; 85 | var index = msg.index; 86 | if (!!script && script.length>1){ 87 | agent.conf.script = script; 88 | } 89 | agent.log.info(this.nodeId + ' run ' + this.count + ' actors '); 90 | monitor.clear(); 91 | this.actors = {}; 92 | var offset = index*this.count; 93 | for (var i = 0;i < this.count;i++) { 94 | var aid = i+offset; //calc database key offset; 95 | var actor = new Actor(agent.conf,aid); 96 | this.actors[aid]= actor; 97 | (function(actor){ 98 | actor.on('error',function(error){ 99 | agent.socket.emit('error',error); 100 | }); 101 | if (agent.conf.master.interval<=0) { 102 | actor.run(); 103 | } else { 104 | var time = Math.round(Math.random()*1000+ i*agent.conf.master.interval); 105 | setTimeout(function(){ 106 | actor.run(); 107 | },time); 108 | } 109 | })(actor); 110 | }; 111 | setInterval(function(){ 112 | var mdata = monitor.getData(); 113 | agent.socket.emit('report',mdata); 114 | },STATUS_INTERVAL); 115 | }, 116 | 117 | // Run agent 118 | start: function() { 119 | var agent = this; 120 | agent.connect(); 121 | // Check for heartbeat every HEARTBEAT_PERIOD, reconnect if necessary 122 | setInterval(function() { 123 | var delta = ((new Date().getTime()) - agent.last_heartbeat); 124 | if (delta > (HEARTBEAT_PERIOD * HEARTBEAT_FAILS)) { 125 | agent.log.warn("Failed heartbeat check, reconnecting..."); 126 | agent.connected = false; 127 | agent.reconnect(); 128 | } 129 | }, HEARTBEAT_PERIOD); 130 | }, 131 | // Sends announcement 132 | announce: function(socket) { 133 | var agent = this; 134 | var sessionid = agent.socket.socket.sessionid; 135 | agent.nodeId = sessionid; 136 | this._send('announce_node', { 137 | client_type:'node', 138 | nodeId:sessionid 139 | }); 140 | }, 141 | 142 | // Reconnect helper, retry until connection established 143 | reconnect: function(force) { 144 | var agent = this; 145 | if (!force && agent.reconnecting) { return; } 146 | this.reconnecting = true; 147 | if (agent.socket!=null){ 148 | agent.socket.disconnect(); 149 | agent.connected = false; 150 | } 151 | agent.log.info("Reconnecting to server..."); 152 | setTimeout(function() { 153 | if (agent.connected) { return; } 154 | agent.connect(); 155 | }, RECONNECT_INTERVAL); 156 | }, 157 | _send: function(event, message) { 158 | try { 159 | this.socket.emit(event, message); 160 | // If server is down, a non-writeable stream error is thrown. 161 | } catch(err) { 162 | this.log.error("ERROR: Unable to send message over socket."); 163 | this.connected = false; 164 | this.reconnect(); 165 | } 166 | } 167 | }; 168 | 169 | exports.Agent = Agent; 170 | -------------------------------------------------------------------------------- /lib/common/logging.js: -------------------------------------------------------------------------------- 1 | /* Generic logging module. 2 | * 3 | * Log Levels: 4 | * - 3 (Debug) 5 | * - 2 (Info) 6 | * - 1 (Warn) 7 | * - 0 (Error) 8 | */ 9 | 10 | var Logger = function(log_level) { 11 | this._log_level = log_level ? log_level : 2; 12 | }; 13 | 14 | Logger.prototype = { 15 | _timestamp: function(msg) { 16 | return (new Date()).toLocaleString().slice(0,24); 17 | }, 18 | set:function(level){ 19 | this._log_level = level; 20 | }, 21 | debug: function(msg) { 22 | if (this._log_level < 3) { return; } 23 | console.info("[" + this._timestamp() + "] DEBUG: " + msg); 24 | }, 25 | 26 | isDebug: function(msg) { 27 | if (this._log_level < 3) { return false; } else {return true;} 28 | }, 29 | 30 | info: function(msg) { 31 | if (this._log_level < 2) { return; } 32 | console.info("[" + this._timestamp() + "] INFO: " + msg); 33 | }, 34 | 35 | warn: function(msg) { 36 | if (this._log_level < 1) { return; } 37 | console.warn("[" + this._timestamp() + "] WARN: " + msg); 38 | }, 39 | 40 | error: function(msg) { 41 | if (this._log_level < 0) { return; } 42 | console.error("[" + this._timestamp() + "] ERROR: " + msg); 43 | } 44 | }; 45 | 46 | var instance = new Logger(); 47 | 48 | getLogger = function() { 49 | return instance(); 50 | }; 51 | 52 | exports.Logger = instance; 53 | -------------------------------------------------------------------------------- /lib/common/util.js: -------------------------------------------------------------------------------- 1 | // ------------------------------------ 2 | // Statistics Manager 3 | // ------------------------------------ 4 | // 5 | // This file defines qputs, qprint, and extends the util namespace. 6 | // 7 | // Extends node.js util.js with other common functions. 8 | // 9 | var BUILD_AS_SINGLE_FILE; 10 | if (!BUILD_AS_SINGLE_FILE) { 11 | var util = require('util'); 12 | var EventEmitter = require('events').EventEmitter; 13 | } 14 | 15 | var fs = require('fs'); 16 | 17 | util.getPath = function() { 18 | var path = "./"; 19 | if (__filename.indexOf('node_modules')==-1) { 20 | path = __filename.substring(0,__filename.lastIndexOf('/')) + '/../../log'; 21 | } else { 22 | path = __filename.substring(0,__filename.lastIndexOf('node_modules')) + 'log'; 23 | } 24 | return path; 25 | }; 26 | 27 | util.createPath = function() { 28 | var path = util.getPath(); 29 | if (!require('fs').existsSync(path)) { 30 | fs.mkdirSync(path); 31 | } 32 | }; 33 | 34 | util.deleteLog = function() { 35 | var path = util.getPath(); 36 | try { 37 | fs.unlinkSync(path+'/detail'); 38 | fs.unlinkSync(path+'/.log'); 39 | } catch(ex){ 40 | 41 | } 42 | } 43 | 44 | // A few common global functions so we can access them with as few keystrokes as possible 45 | // 46 | var qputs = util.qputs = function(s) { 47 | util.puts(s); 48 | }; 49 | 50 | var qprint = util.qprint = function(s) { 51 | util.print(s); 52 | }; 53 | 54 | 55 | // Static utility methods 56 | // 57 | util.uid = function() { 58 | exports.lastUid_ = exports.lastUid_ || 0; 59 | return exports.lastUid_++; 60 | }; 61 | util.defaults = function(obj, defaults) { 62 | for (var i in defaults) { 63 | if (obj[i] === undefined) { 64 | obj[i] = defaults[i]; 65 | } 66 | } 67 | return obj; 68 | }; 69 | util.extend = function(obj, extension) { 70 | for (var i in extension) { 71 | if (extension.hasOwnProperty(i)) { 72 | obj[i] = extension[i]; 73 | } 74 | } 75 | return obj; 76 | }; 77 | 78 | util.forEach = function(obj, f) { 79 | for (var i in obj) { 80 | if (obj.hasOwnProperty(i)) { 81 | f(i, obj[i]); 82 | } 83 | } 84 | }; 85 | 86 | util.every = function(obj, f) { 87 | for (var i in obj) { 88 | if (obj.hasOwnProperty(i)) { 89 | if (!f(i, obj[i])) { 90 | return false; 91 | } 92 | } 93 | } 94 | return true; 95 | }; 96 | util.argarray = function(args) { 97 | return Array.prototype.slice.call(args); 98 | }; 99 | util.readStream = function(stream, callback) { 100 | var data = []; 101 | stream.on('data', function(chunk) { 102 | data.push(chunk.toString()); 103 | }); 104 | stream.on('end', function() { 105 | callback(data.join('')); 106 | }); 107 | }; 108 | 109 | /** Make an object a PeriodicUpdater by adding PeriodicUpdater.call(this) to the constructor. 110 | The object will call this.update() every interval. */ 111 | util.PeriodicUpdater = function(updateIntervalMs) { 112 | var self = this, updateTimeoutId; 113 | this.__defineGetter__('updateInterval', function() { return updateIntervalMs; }); 114 | this.__defineSetter__('updateInterval', function(milliseconds) { 115 | clearInterval(updateTimeoutId); 116 | if (milliseconds > 0 && milliseconds < Infinity) { 117 | updateTimeoutId = setInterval(self.update.bind(self), milliseconds); 118 | } 119 | updateIntervalMs = milliseconds; 120 | }); 121 | this.updateInterval = updateIntervalMs; 122 | }; 123 | 124 | /** Same arguments as http.createClient. Returns an wrapped http.Client object that will reconnect when 125 | connection errors are detected. In the current implementation of http.Client (11/29/10), calls to 126 | request() fail silently after the initial 'error' event. */ 127 | util.createReconnectingClient = function() { 128 | var http = require('http'), 129 | clientArgs = arguments, events = {}, client, wrappedClient = {}, 130 | clientMethod = function(method) { 131 | return function() { return client[method].apply(client, arguments); }; 132 | }, 133 | clientGetter = function(member) { return function() { return client[member]; };}, 134 | clientSetter = function(member) { return function(val) { client[member] = val; };}, 135 | reconnect = function() { 136 | var oldclient = client; 137 | if (oldclient) { oldclient.destroy(); } 138 | client = http.createClient.apply(http, clientArgs); 139 | client._events = util.extend(events, client._events); // EventEmitter._events stores event handlers 140 | client.emit('reconnect', oldclient); 141 | }; 142 | 143 | // Create initial http.Client 144 | reconnect(); 145 | client.on('error', function(err) { reconnect(); }); 146 | 147 | // Wrap client so implementation can be swapped out when there are connection errors 148 | for (var j in client) { 149 | if (typeof client[j] === 'function') { 150 | wrappedClient[j] = clientMethod(j); 151 | } else { 152 | wrappedClient.__defineGetter__(j, clientGetter(j)); 153 | wrappedClient.__defineSetter__(j, clientSetter(j)); 154 | } 155 | } 156 | wrappedClient.impl = client; 157 | return wrappedClient; 158 | }; 159 | 160 | /** Accepts an EventEmitter object that emits text data. LineReader buffers the text and emits a 'data' 161 | event each time a newline is encountered. For example, */ 162 | util.LineReader = function(eventEmitter, event) { 163 | EventEmitter.call(this); 164 | event = event || 'data'; 165 | 166 | var self = this, buffer = ''; 167 | 168 | var emitLine = function(buffer) { 169 | var lineEnd = buffer.indexOf("\n"); 170 | var line = (lineEnd === -1) ? buffer : buffer.substring(0, lineEnd); 171 | if (line) { self.emit('data', line); } 172 | return buffer.substring(line.length + 1, buffer.length); 173 | }; 174 | 175 | var readloop = function(data) { 176 | if (data) { buffer += data.toString(); } 177 | if (buffer.indexOf("\n") > -1) { 178 | buffer = emitLine(buffer); 179 | process.nextTick(readloop.bind(this)); 180 | } 181 | }; 182 | 183 | eventEmitter.on(event, readloop.bind(this)); 184 | } 185 | util.inherits(util.LineReader, EventEmitter); 186 | 187 | util.extend(exports, util); 188 | -------------------------------------------------------------------------------- /lib/console/css/colors.css: -------------------------------------------------------------------------------- 1 | 2 | .labelcolor1 { 3 | color: #1f77b4; 4 | } 5 | .statuscolor1 { 6 | background-color: #1f77b4 !important; 7 | } 8 | .labelcolor2 { 9 | color: #aec7e8; 10 | } 11 | .statuscolor2 { 12 | background-color: #aec7e8 !important; 13 | } 14 | .labelcolor3 { 15 | color: #ff7f0e; 16 | } 17 | .statuscolor3 { 18 | background-color: #ff7f0e !important; 19 | } 20 | .labelcolor4 { 21 | color: #ffbb78; 22 | } 23 | .statuscolor4 { 24 | background-color: #ffbb78 !important; 25 | } 26 | .labelcolor5 { 27 | color: #2ca02c; 28 | } 29 | .statuscolor5 { 30 | background-color: #2ca02c !important; 31 | } 32 | .labelcolor6 { 33 | color: #98df8a; 34 | } 35 | .statuscolor6 { 36 | background-color: #98df8a !important; 37 | } 38 | .labelcolor7 { 39 | color: #d62728; 40 | } 41 | .statuscolor7 { 42 | background-color: #d62728 !important; 43 | } 44 | .labelcolor8 { 45 | color: #ff9896; 46 | } 47 | .statuscolor8 { 48 | background-color: #ff9896 !important; 49 | } 50 | .labelcolor9 { 51 | color: #9467bd; 52 | } 53 | .statuscolor9 { 54 | background-color: #9467bd !important; 55 | } 56 | .labelcolor10 { 57 | color: #c5b0d5; 58 | } 59 | .statuscolor10 { 60 | background-color: #c5b0d5 !important; 61 | } 62 | .labelcolor11 { 63 | color: #8c564b; 64 | } 65 | .statuscolor11 { 66 | background-color: #8c564b !important; 67 | } 68 | .labelcolor12 { 69 | color: #c49c94; 70 | } 71 | .statuscolor12 { 72 | background-color: #c49c94 !important; 73 | } 74 | .labelcolor13 { 75 | color: #e377c2; 76 | } 77 | .statuscolor13 { 78 | background-color: #e377c2 !important; 79 | } 80 | .labelcolor14 { 81 | color: #f7b6d2; 82 | } 83 | .statuscolor14 { 84 | background-color: #f7b6d2 !important; 85 | } 86 | .labelcolor15 { 87 | color: #7f7f7f; 88 | } 89 | .statuscolor15 { 90 | background-color: #7f7f7f !important; 91 | } 92 | .labelcolor16 { 93 | color: #c7c7c7; 94 | } 95 | .statuscolor16 { 96 | background-color: #c7c7c7 !important; 97 | } 98 | .labelcolor17 { 99 | color: #bcbd22; 100 | } 101 | .statuscolor17 { 102 | background-color: #bcbd22 !important; 103 | } 104 | .labelcolor18 { 105 | color: #dbdb8d; 106 | } 107 | .statuscolor18 { 108 | background-color: #dbdb8d !important; 109 | } 110 | .labelcolor19 { 111 | color: #17becf; 112 | } 113 | .statuscolor19 { 114 | background-color: #17becf !important; 115 | } 116 | .labelcolor20 { 117 | color: #9edae5; 118 | } 119 | .statuscolor20 { 120 | background-color: #9edae5 !important; 121 | } 122 | .ping { 123 | background-color: #33ff33 !important; 124 | } -------------------------------------------------------------------------------- /lib/console/css/web_client.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0px; 3 | margin: 0px; 4 | font-family: Verdana, Arial, sans-serif; 5 | } 6 | body { 7 | background-color: #333; 8 | overflow: auto; 9 | overflow-x: hidden; 10 | } 11 | td { 12 | font-family: Verdana, Arial, sans-serif; 13 | font-size: 12px; 14 | } 15 | #container { 16 | background-color: #000; 17 | display: table-row; 18 | overflow: auto; 19 | } 20 | #controls { 21 | background-color: #222; 22 | display: table-cell; 23 | border-right: 1px solid #333; 24 | width: 200px; 25 | } 26 | #controls2 { 27 | overflow: auto; 28 | vertical-align: top; 29 | } 30 | #right { 31 | display: table-cell; 32 | vertical-align: top; 33 | position: relative; 34 | overflow:auto; 35 | } 36 | #page { 37 | margin: 0px auto; 38 | padding: 30px 0px; 39 | background: #fff 40 | } 41 | .dspan { 42 | margin: 0px auto; 43 | padding: 5px 5px; 44 | background: #fff 45 | } 46 | .screen { 47 | margin: 10px; 48 | } 49 | .screen .bar { 50 | font-size:14px; 51 | font-weight: bold; 52 | background: #333; 53 | color: #ccc; 54 | text-shadow: #111 1px 1px; 55 | padding: 3px 40px 3px 10px; 56 | border-top-left-radius: 6px; 57 | border-top-right-radius: 6px; 58 | -webkit-border-top-left-radius: 6px; 59 | -webkit-border-top-right-radius: 6px; 60 | -moz-border-radius-topleft: 6px; 61 | -moz-border-radius-topright: 6px; 62 | 63 | border: 1px solid #333; 64 | border-bottom: none; 65 | 66 | background: -webkit-gradient(linear, left top, left bottom, from(#222), to(#333)); 67 | background: -moz-linear-gradient(top, #222, #333); 68 | position: relative; 69 | overflow: hidden; 70 | height:20px; 71 | } 72 | .screen .bar .nodes .vtd { 73 | font-size: 12px 74 | } 75 | .screen .console { 76 | padding: 10px; 77 | overflow: auto; 78 | border: 1px solid #333; 79 | border-top: none; 80 | background-color: #fff; 81 | font-size: 12px; 82 | } 83 | .heightf { 84 | height:200px; 85 | } 86 | 87 | .screen .console p, 88 | .screen .console p .label, 89 | .screen .console p .highlight { 90 | font-family: Lucida Console, Courier New, Monaco5, monospace !important; 91 | line-height: 15px; 92 | font-size: 13px; 93 | } 94 | .screen .console p { 95 | margin-bottom: 4px; 96 | word-wrap: break-word; 97 | } 98 | .screen .console .highlight { 99 | background-color: #00ff00; 100 | color: #000; 101 | } 102 | .screen .console .active-match .highlight { 103 | background-color: #fff; 104 | } 105 | .bar-top-right { 106 | position: absolute; 107 | top: 3px; 108 | right: 3px; 109 | } 110 | .screen-buttons { 111 | float: right; 112 | } 113 | .screen-buttons img { 114 | border: none; 115 | margin: 1px 2px 0px 2px; 116 | } 117 | .screen-buttons .start { 118 | display: none; 119 | } 120 | .screen-buttons .button { 121 | float: right; 122 | padding: 0px 0px 0px 3px; 123 | font-size: 12px; 124 | text-shadow: none; 125 | color: #ccc; 126 | text-decoration: none; 127 | font-weight: normal; 128 | } 129 | .screen-search { 130 | float: right; 131 | } 132 | .screen-search input { 133 | font-size: 12px; 134 | padding: 1px; 135 | width: 250px; 136 | border: none; 137 | background: none; 138 | margin-top: 0px; 139 | text-align: right; 140 | outline: none; 141 | margin-right: 2px; 142 | } 143 | .screen-search .inactive { 144 | color: #999; 145 | font-style: italic; 146 | } 147 | .screen-search .active { 148 | color: #eee; 149 | font-style: normal; 150 | } 151 | .screen-search .bad-regex { 152 | color: #f00; 153 | } 154 | .screen-search .result-count { 155 | float: right; 156 | font-size: 12px; 157 | line-height: 15px; 158 | font-weight: normal; 159 | width: 30px; 160 | text-shadow: none; 161 | color: #999; 162 | text-decoration: none; 163 | outline: none; 164 | } 165 | .screen .log { 166 | margin-bottom: 4px; 167 | } 168 | .screen .log .source { 169 | font-size: 10px; 170 | margin-bottom: 2px; 171 | } 172 | #stats { 173 | margin-left: 10px; 174 | float: right; 175 | } 176 | #stats .stat { 177 | color: #666; 178 | font-size: 12px; 179 | margin: 0px 15px 0px 10px; 180 | float: left; 181 | } 182 | #stats .stat b { 183 | font-size: 24px; 184 | color: #ccc; 185 | font-weight: normal; 186 | } 187 | 188 | #bottom { 189 | bottom: 10px; 190 | } 191 | #bottom2 { 192 | position: relative; 193 | } 194 | 195 | #add-buttons { 196 | position: absolute; 197 | top: 0px; 198 | left: 0px; 199 | } 200 | 201 | .add-button { 202 | margin: 2px 0px 0px 10px; 203 | padding: 4px 8px; 204 | font-size: 12px; 205 | color: #000; 206 | font-weight: bold; 207 | background: #444; 208 | border: 1px solid #aaa; 209 | border-bottom: 1px solid #888; 210 | border-right: 1px solid #888; 211 | cursor: pointer; 212 | border-radius: 3px; 213 | -webkit-border-radius: 3px; 214 | -moz-border-radius: 3px; 215 | float: left; 216 | background: -webkit-gradient(linear, left top, left bottom, from(#999), to(#777)); 217 | background: -moz-linear-gradient(top, #999, #777); 218 | } 219 | div.add-button:hover { 220 | color: #fff; 221 | background: #666; 222 | background: -webkit-gradient(linear, left top, left bottom, from(#777), to(#999)); 223 | background: -moz-linear-gradient(top, #777, #999); 224 | } 225 | 226 | .group { 227 | margin:0px 75px; 228 | } 229 | #controls .group2 { 230 | float: left; 231 | overflow: hidden; 232 | width: 100%; 233 | } 234 | #controls .node { 235 | color: #eee; 236 | font-weight: bold; 237 | font-size: 12px; 238 | padding: 8px 8px; 239 | background: #111; 240 | border-top: 1px solid #000; 241 | overflow: hidden; 242 | max-width: 270px; 243 | background: -webkit-gradient(linear, left top, left bottom, from(#222), to(#000)); 244 | background: -moz-linear-gradient(top, #222, #000); 245 | } 246 | #controls .log_file { 247 | color: #999; 248 | font-size: 12px; 249 | border-bottom: 1px dotted #333; 250 | float: left; 251 | width: 100%; 252 | } 253 | #controls .status { 254 | float: left; 255 | background-color: #333; 256 | margin: 7px 0px 0px 7px; 257 | height: 10px; 258 | width: 10px; 259 | border-radius: 5px; 260 | -webkit-border-radius: 5px; 261 | -moz-border-radius: 5px; 262 | } 263 | #controls .label { 264 | float: left; 265 | margin: 4px 4px 5px 6px; 266 | font-size: 12px; 267 | } 268 | #controls .screens { 269 | float: right; 270 | margin: 5px 5px 0px 0px; 271 | opacity: 0.5; 272 | } 273 | #controls div.log_file:hover .screens { 274 | opacity: 1.0; 275 | } 276 | #controls .screens input { 277 | margin: 0px 2px 3px 2px; 278 | } 279 | 280 | 281 | #templates { 282 | display: none; 283 | } 284 | -------------------------------------------------------------------------------- /lib/console/http.js: -------------------------------------------------------------------------------- 1 | // ------------------------------------ 2 | // HTTP Server 3 | // ------------------------------------ 4 | // 5 | // This file defines HttpServer and the singleton HTTP_SERVER. 6 | // 7 | // This file defines a generic HTTP server that serves static files and that can be configured 8 | // with new routes. It also starts the nodeload HTTP server unless require('nodeload/config') 9 | // .disableServer() was called. 10 | // 11 | var BUILD_AS_SINGLE_FILE; 12 | if (!BUILD_AS_SINGLE_FILE) { 13 | var http = require('http'); 14 | var fs = require('fs'); 15 | var util = require('../common/util'); 16 | var qputs = util.qputs; 17 | var EventEmitter = require('events').EventEmitter; 18 | } 19 | 20 | var stat = require('../monitor/stat'); 21 | var __ = require('underscore'); 22 | /** By default, HttpServer knows how to return static files from the current directory. Add new route 23 | regexs using HttpServer.on(). */ 24 | var HttpServer = exports.HttpServer = function HttpServer() { 25 | this.routes = []; 26 | this.running = false; 27 | }; 28 | util.inherits(HttpServer, EventEmitter); 29 | /** Start the server listening on the given port */ 30 | HttpServer.prototype.start = function(port, hostname) { 31 | if (this.running) { return; } 32 | this.running = true; 33 | 34 | var self = this; 35 | port = port || 8000; 36 | self.hostname = hostname || 'localhost'; 37 | self.port = port; 38 | self.connections = []; 39 | 40 | self.server = http.createServer(function(req, res) { self.route_(req, res); }); 41 | self.server.on('connection', function(c) { 42 | // We need to track incoming connections, beause Server.close() won't terminate active 43 | // connections by default. 44 | c.on('close', function() { 45 | var idx = self.connections.indexOf(c); 46 | if (idx !== -1) { 47 | self.connections.splice(idx, 1); 48 | } 49 | }); 50 | self.connections.push(c); 51 | }); 52 | self.server.listen(port, hostname); 53 | 54 | self.emit('start', self.hostname, self.port); 55 | return self; 56 | }; 57 | /** Terminate the server */ 58 | HttpServer.prototype.stop = function() { 59 | if (!this.running) { return; } 60 | this.running = false; 61 | this.connections.forEach(function(c) { c.destroy(); }); 62 | this.server.close(); 63 | this.server = null; 64 | this.emit('end'); 65 | }; 66 | /** When an incoming request matches a given regex, route it to the provided handler: 67 | function(url, ServerRequest, ServerResponse) */ 68 | HttpServer.prototype.addRoute = function(regex, handler) { 69 | this.routes.unshift({regex: regex, handler: handler}); 70 | return this; 71 | }; 72 | HttpServer.prototype.removeRoute = function(regex, handler) { 73 | this.routes = this.routes.filter(function(r) { 74 | return !((regex === r.regex) && (!handler || handler === r.handler)); 75 | }); 76 | return this; 77 | }; 78 | HttpServer.prototype.route_ = function(req, res) { 79 | for (var i = 0; i < this.routes.length; i++) { 80 | if (req.url.match(this.routes[i].regex)) { 81 | this.routes[i].handler(req.url, req, res); 82 | return; 83 | } 84 | } 85 | if (req.method === 'GET') { 86 | this.serveFile_('.' + req.url, res); 87 | } else { 88 | res.writeHead(405, {"Content-Length": "0"}); 89 | res.end(); 90 | } 91 | }; 92 | 93 | 94 | function doReport(response) { 95 | var pdata = stat.getData(); 96 | //console.log('pdata %j',pdata); 97 | var mdata = []; 98 | var _show = false; 99 | __.each(pdata,function(val,key){ 100 | var single = {}; 101 | _show = true; 102 | single['name'] = key; 103 | single['uid'] = key; 104 | var keycolumns = []; 105 | var maxId = 0; 106 | __.each(val,function(kval,akey){ 107 | var _length = __.size(kval); 108 | if (_length>maxId) maxId = _length; 109 | if (_length>0) keycolumns.push(akey); 110 | }); 111 | var gcolumns = []; 112 | gcolumns.push('users'); 113 | var glastkeyData = {}; 114 | __.each(keycolumns,function(dkey){gcolumns.push(dkey);}); 115 | var grows = []; 116 | for (var i = 0;i< maxId;i++) { 117 | var rows = []; 118 | rows.push(i+1); 119 | __.each(keycolumns,function(dkey){ 120 | //console.log('dkey' + dkey + ' ' +i + JSON.stringify(val[dkey])) 121 | rows.push(val[dkey][i] || 0); 122 | //_vaild = true; 123 | }); 124 | grows.push(rows); 125 | } 126 | var gsummary = {}; 127 | __.each(keycolumns,function(dkey){ 128 | var summary = {}; 129 | var kdata = val[dkey]; 130 | var min = Number.MAX_VALUE,max = 0; 131 | var sindex = 0,sum = 0; 132 | __.each(kdata,function(time){ 133 | if (time>max) max = time; 134 | if (time0) mdata.push(single); 145 | }); 146 | if (_show) { 147 | var data = JSON.stringify(mdata); 148 | //response.writeHead(200, { 'Content-Length': pdata.length }); 149 | response.write(data, "binary"); 150 | } 151 | response.end(); 152 | } 153 | 154 | 155 | HttpServer.prototype.serveFile_ = function(file, response) { 156 | if (file.lastIndexOf('report')!=-1) { 157 | doReport(response); 158 | return; 159 | } 160 | if (file==='./') 161 | file = 'index.html'; 162 | file = process.cwd() + '/node_modules/pomelo-robot/lib/console/' + file; 163 | fs.stat(file, function(err, stat) { 164 | if (err) { 165 | response.writeHead(404, {"Content-Type": "text/plain"}); 166 | response.write("Cannot find file: " + file); 167 | response.end(); 168 | return; 169 | } 170 | 171 | fs.readFile(file, "binary", function (err, data) { 172 | if (err) { 173 | response.writeHead(500, {"Content-Type": "text/plain"}); 174 | response.write("Error opening file " + file + ": " + err); 175 | } else { 176 | if (file.lastIndexOf('.html')==-1) { 177 | response.writeHead(200, { 'Content-Length': data.length }); 178 | response.write(data, "binary"); 179 | } else { 180 | response.writeHead(200, { 'Content-Length': data.length,"Content-Type":"text/html; charset=utf-8"}); 181 | response.write(data, "binary"); 182 | } 183 | } 184 | response.end(); 185 | }); 186 | }); 187 | }; 188 | 189 | // ================= 190 | // Singletons 191 | // ================= 192 | /** The global HTTP server used by nodeload */ 193 | var HTTP_SERVER = exports.HTTP_SERVER = new HttpServer(); 194 | HTTP_SERVER.on('start', function(hostname, port) { 195 | qputs('Started HTTP server on ' + hostname + ':' + port + '.'); 196 | }); 197 | 198 | HTTP_SERVER.on('end', function() { 199 | qputs('Shutdown HTTP server.'); 200 | }); 201 | 202 | -------------------------------------------------------------------------------- /lib/console/images/icons/bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetEase/pomelo-robot/a2bc544aa14f5ff696304a89703d868931e5d365/lib/console/images/icons/bin.png -------------------------------------------------------------------------------- /lib/console/images/icons/control_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetEase/pomelo-robot/a2bc544aa14f5ff696304a89703d868931e5d365/lib/console/images/icons/control_pause.png -------------------------------------------------------------------------------- /lib/console/images/icons/control_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetEase/pomelo-robot/a2bc544aa14f5ff696304a89703d868931e5d365/lib/console/images/icons/control_play.png -------------------------------------------------------------------------------- /lib/console/images/icons/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetEase/pomelo-robot/a2bc544aa14f5ff696304a89703d868931e5d365/lib/console/images/icons/cross.png -------------------------------------------------------------------------------- /lib/console/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Pomelo Robot Console 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 122 |
123 | 126 | 127 |
128 |
129 | Summary 130 |
131 |
132 |
133 | 136 |
137 |
138 |
139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 |
RequestMaxMinAvgQpsCount
153 |
154 |
155 | 156 | 157 |
158 |
159 | Response Time Graph 160 |
161 |
163 |
164 | 167 |
168 |
169 |
170 |
171 |
172 |
173 | 174 |
175 |
176 | 177 | 178 | -------------------------------------------------------------------------------- /lib/console/js/lib/jquery.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery JavaScript Library v1.4.2 3 | * http://jquery.com/ 4 | * 5 | * Copyright 2010, John Resig 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://jquery.org/license 8 | * 9 | * Includes Sizzle.js 10 | * http://sizzlejs.com/ 11 | * Copyright 2010, The Dojo Foundation 12 | * Released under the MIT, BSD, and GPL Licenses. 13 | * 14 | * Date: Sat Feb 13 22:33:48 2010 -0500 15 | */ 16 | (function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, 21 | Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& 22 | (d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, 23 | a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== 24 | "find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, 25 | function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; 34 | var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, 35 | parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= 36 | false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= 37 | s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, 38 | applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; 39 | else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, 40 | a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== 41 | w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, 42 | cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= 47 | c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); 48 | a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, 49 | function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); 50 | k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), 51 | C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= 53 | e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& 54 | f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; 55 | if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", 63 | e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, 64 | "_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, 65 | d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, 71 | e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); 72 | t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| 73 | g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, 80 | CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, 81 | g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, 82 | text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, 83 | setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= 84 | h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== 86 | "="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, 87 | h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& 90 | q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; 91 | if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); 92 | (function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: 93 | function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= 96 | {},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== 97 | "string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", 98 | d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? 99 | a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== 100 | 1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= 102 | c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, 103 | wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, 104 | prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, 105 | this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); 106 | return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, 107 | ""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); 111 | return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", 112 | ""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= 113 | c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? 114 | c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= 115 | function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= 116 | Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, 117 | "border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= 118 | a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= 119 | a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== 120 | "string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, 121 | serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), 122 | function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, 123 | global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& 124 | e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? 125 | "&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== 126 | false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= 127 | false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", 128 | c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| 129 | d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); 130 | g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== 131 | 1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== 132 | "json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; 133 | if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== 139 | "number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| 140 | c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; 141 | this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= 142 | this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, 143 | e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; 149 | a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); 150 | c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, 151 | d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- 152 | f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": 153 | "pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in 154 | e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); 155 | -------------------------------------------------------------------------------- /lib/console/js/lib/scrollTo.jquery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery.ScrollTo - Easy element scrolling using jQuery. 3 | * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com 4 | * Dual licensed under MIT and GPL. 5 | * Date: 5/25/2009 6 | * @author Ariel Flesler 7 | * @version 1.4.2 8 | * 9 | * http://flesler.blogspot.com/2007/10/jqueryscrollto.html 10 | */ 11 | ;(function(d){var k=d.scrollTo=function(a,i,e){d(window).scrollTo(a,i,e)};k.defaults={axis:'xy',duration:parseFloat(d.fn.jquery)>=1.3?0:1};k.window=function(a){return d(window)._scrollable()};d.fn._scrollable=function(){return this.map(function(){var a=this,i=!a.nodeName||d.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!i)return a;var e=(a.contentWindow||a).document||a.ownerDocument||a;return d.browser.safari||e.compatMode=='BackCompat'?e.body:e.documentElement})};d.fn.scrollTo=function(n,j,b){if(typeof j=='object'){b=j;j=0}if(typeof b=='function')b={onAfter:b};if(n=='max')n=9e9;b=d.extend({},k.defaults,b);j=j||b.speed||b.duration;b.queue=b.queue&&b.axis.length>1;if(b.queue)j/=2;b.offset=p(b.offset);b.over=p(b.over);return this._scrollable().each(function(){var q=this,r=d(q),f=n,s,g={},u=r.is('html,body');switch(typeof f){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(f)){f=p(f);break}f=d(f,this);case'object':if(f.is||f.style)s=(f=d(f)).offset()}d.each(b.axis.split(''),function(a,i){var e=i=='x'?'Left':'Top',h=e.toLowerCase(),c='scroll'+e,l=q[c],m=k.max(q,i);if(s){g[c]=s[h]+(u?0:l-r.offset()[h]);if(b.margin){g[c]-=parseInt(f.css('margin'+e))||0;g[c]-=parseInt(f.css('border'+e+'Width'))||0}g[c]+=b.offset[h]||0;if(b.over[h])g[c]+=f[i=='x'?'width':'height']()*b.over[h]}else{var o=f[h];g[c]=o.slice&&o.slice(-1)=='%'?parseFloat(o)/100*m:o}if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],m);if(!a&&b.queue){if(l!=g[c])t(b.onAfterFirst);delete g[c]}});t(b.onAfter);function t(a){r.animate(g,j,b.easing,a&&function(){a.call(this,n,b)})}}).end()};k.max=function(a,i){var e=i=='x'?'Width':'Height',h='scroll'+e;if(!d(a).is('html,body'))return a[h]-d(a)[e.toLowerCase()]();var c='client'+e,l=a.ownerDocument.documentElement,m=a.ownerDocument.body;return Math.max(l[h],m[h])-Math.min(l[c],m[c])};function p(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery); -------------------------------------------------------------------------------- /lib/console/js/lib/underscore.min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.1.5 2 | // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | (function(){var q=this,D=q._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,E=k.unshift,F=o.toString,m=o.hasOwnProperty,s=k.forEach,t=k.map,u=k.reduce,v=k.reduceRight,w=k.filter,x=k.every,y=k.some,p=k.indexOf,z=k.lastIndexOf;o=Array.isArray;var G=Object.keys,A=Function.prototype.bind,c=function(a){return new l(a)};if(typeof module!=="undefined"&&module.exports){module.exports=c;c._=c}else q._=c;c.VERSION="1.1.5";var j=c.each=c.forEach=function(a,b,d){if(a!=null)if(s&&a.forEach===s)a.forEach(b, 9 | d);else if(c.isNumber(a.length))for(var e=0,f=a.length;e=e.computed&&(e={value:f,computed:g})});return e.value};c.min=function(a,b,d){if(!b&&c.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};j(a,function(f,g,h){g=b?b.call(d,f,g,h):f;gh?1:0}),"value")};c.sortedIndex= 14 | function(a,b,d){d=d||c.identity;for(var e=0,f=a.length;e>1;d(a[g])=0})})};c.zip=function(){for(var a=i.call(arguments),b=c.max(c.pluck(a,"length")),d=Array(b),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};c.keys=G||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)if(m.call(a, 20 | d))b[b.length]=d;return b};c.values=function(a){return c.map(a,c.identity)};c.functions=c.methods=function(a){return c.filter(c.keys(a),function(b){return c.isFunction(a[b])}).sort()};c.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};c.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)if(a[d]==null)a[d]=b[d]});return a};c.clone=function(a){return c.isArray(a)?a.slice():c.extend({},a)};c.tap=function(a,b){b(a);return a};c.isEqual=function(a, 21 | b){if(a===b)return true;var d=typeof a;if(d!=typeof b)return false;if(a==b)return true;if(!a&&b||a&&!b)return false;if(a._chain)a=a._wrapped;if(b._chain)b=b._wrapped;if(a.isEqual)return a.isEqual(b);if(c.isDate(a)&&c.isDate(b))return a.getTime()===b.getTime();if(c.isNaN(a)&&c.isNaN(b))return false;if(c.isRegExp(a)&&c.isRegExp(b))return a.source===b.source&&a.global===b.global&&a.ignoreCase===b.ignoreCase&&a.multiline===b.multiline;if(d!=="object")return false;if(a.length&&a.length!==b.length)return false; 22 | d=c.keys(a);var e=c.keys(b);if(d.length!=e.length)return false;for(var f in a)if(!(f in b)||!c.isEqual(a[f],b[f]))return false;return true};c.isEmpty=function(a){if(c.isArray(a)||c.isString(a))return a.length===0;for(var b in a)if(m.call(a,b))return false;return true};c.isElement=function(a){return!!(a&&a.nodeType==1)};c.isArray=o||function(a){return F.call(a)==="[object Array]"};c.isArguments=function(a){return!!(a&&m.call(a,"callee"))};c.isFunction=function(a){return!!(a&&a.constructor&&a.call&& 23 | a.apply)};c.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};c.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};c.isNaN=function(a){return a!==a};c.isBoolean=function(a){return a===true||a===false};c.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};c.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};c.isNull=function(a){return a===null};c.isUndefined=function(a){return a===void 0};c.noConflict=function(){q._= 24 | D;return this};c.identity=function(a){return a};c.times=function(a,b,d){for(var e=0;e/g,interpolate:/<%=([\s\S]+?)%>/g};c.template=function(a,b){var d=c.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate, 25 | function(e,f){return"',"+f.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(e,f){return"');"+f.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return b?d(b):d};var l=function(a){this._wrapped=a};c.prototype=l.prototype;var r=function(a,b){return b?c(a).chain():a},I=function(a,b){l.prototype[a]=function(){var d=i.call(arguments);E.call(d,this._wrapped);return r(b.apply(c, 26 | d),this._chain)}};c.mixin(c);j(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=k[a];l.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];l.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});l.prototype.chain=function(){this._chain=true;return this};l.prototype.value=function(){return this._wrapped}})(); 27 | -------------------------------------------------------------------------------- /lib/console/js/nodeclient.js: -------------------------------------------------------------------------------- 1 | var Node = function(nodeId,iport,web_client) { 2 | this._dom; 3 | this.nodeId = nodeId; 4 | this.iport = iport; 5 | this.web_client = web_client; 6 | var node = this; 7 | }; 8 | 9 | Node.prototype = { 10 | render: function() { throw "Node.render() not defined"; }, 11 | destroy: function() { throw "Node.destroy() not defined"; } 12 | }; 13 | 14 | //try { 15 | // module.exports = { 16 | // Node: Node 17 | // } 18 | //} catch(err) {} 19 | -------------------------------------------------------------------------------- /lib/console/js/ui/graph.js: -------------------------------------------------------------------------------- 1 | var graphs = {}; 2 | 3 | function updateGraph(chart,targetName) { 4 | var main = document.getElementById(targetName); 5 | if (!main) { 6 | var tname = targetName+'div'; 7 | var _dom = $("#avg_template").clone().attr('id',tname).find('.screen-label').html(targetName+' Response Time Graph').end().find('.cgraphdiv').attr('id',targetName).end(); 8 | $("#graph_div").append(_dom); 9 | main = document.getElementById(targetName); 10 | var p = $('#'+tname); 11 | p.find('.screen-buttons').click(function(){ 12 | //var t = 13 | p.find('.console').toggle(); 14 | //p.find('.console').hide(); 15 | //p.css('height','26px'); 16 | }); 17 | 18 | }; 19 | if (graphs[chart.uid]) { 20 | graphs[chart.uid].updateOptions({ 21 | "file" : chart.rows, 22 | labels : chart.columns 23 | }); 24 | } else { 25 | var newchart = document.createElement("div"); 26 | newchart.setAttribute("class", "post"); 27 | newchart.innerHTML = [] 28 | .concat( '
', 29 | '
', 30 | '
', 31 | '
').join(''); 32 | main.appendChild(newchart); 33 | graphs[chart.uid] = new Dygraph(document.getElementById("chart" 34 | + chart.uid), chart.rows, { 35 | labelsDiv : document.getElementById("chartlegend" + chart.uid), 36 | labelsSeparateLines : true, 37 | labels : chart.columns 38 | }); 39 | } 40 | 41 | } 42 | 43 | var sumshow = 'none'; 44 | 45 | function updateDetailAgent(_totalAgentSum,title){ 46 | for (var agent in _totalAgentSum) { 47 | var target = $("#sum_"+agent); 48 | if (!!target.html()) {target.remove();}; 49 | var _summary = _totalAgentSum[agent]; 50 | var _dom = $("#table_template").clone() 51 | .find(".summary").html(jsonToTable(_summary)).end().attr("id", "sum_" + agent); 52 | _dom.find(".screen-label").html(agent + title); 53 | $("#summary_div").append(_dom); 54 | }; 55 | $(".close").click(function() { 56 | var self = $(this).parent().parent().parent().parent().css('display','none'); 57 | }); 58 | }; 59 | 60 | function showDetailAgent(){ 61 | sumshow = ''; 62 | $("#summary_div").css('display',sumshow); 63 | $("#table_img").css('display',sumshow); 64 | $("#table_img").click(function(){ 65 | sumshow = 'none'; 66 | $(this).css('display',sumshow); 67 | $("#summary_div").css('display',sumshow); 68 | }); 69 | }; 70 | 71 | 72 | function updateAvgAgent(everyAgentavgDatas,title){ 73 | for (var index in everyAgentavgDatas) { 74 | var chart = everyAgentavgDatas[index]; 75 | var uid = chart.uid; 76 | var target = $("#avg_"+uid); 77 | if (!!target.html()) { 78 | } else { 79 | var _dom = $("#avg_template").clone().attr("id", "avg_" + uid); 80 | _dom.find(".screen-label").html(uid.substring(3,uid.length)+title); 81 | _dom.find(".avgrestime").attr("id", "davg_" + uid); 82 | _dom.css('display','block'); 83 | $("#avg_div").append(_dom); 84 | }; 85 | updateGraph(chart,"davg_"+uid); 86 | }; 87 | }; 88 | 89 | var avgshow = 'none'; 90 | 91 | function showDetailAgentAvg(){ 92 | avgshow = ''; 93 | $("#avg_div").css('display',avgshow); 94 | $("#avg_img").css('display',avgshow); 95 | $("#avg_img").click(function(){ 96 | avgshow = 'none'; 97 | $(this).css('display',avgshow); 98 | $("#avg_div").css('display',avgshow); 99 | }); 100 | }; 101 | 102 | var qsshow = 'none'; 103 | 104 | function showDetailAgentQs(){ 105 | qsshow = ''; 106 | $("#qs_div").css('display',qsshow); 107 | $("#qs_img").css('display',qsshow); 108 | $("#qs_img").click(function(){ 109 | qsshow = 'none'; 110 | $(this).css('display',qsshow); 111 | $("#qs_div").css('display',qsshow); 112 | }); 113 | }; 114 | 115 | function updateEveryAgent(everyAgentavgDatas,divname,title){ 116 | for (var index in everyAgentavgDatas) { 117 | var chart = everyAgentavgDatas[index]; 118 | var uid = chart.uid; 119 | var target = $("#qs_"+uid); 120 | if (!!target.html()) { 121 | } else { 122 | var _dom = $("#avg_template").clone().attr("id", "qs_" + uid); 123 | _dom.find(".screen-label").html(uid.substring(2,uid.length)+title); 124 | _dom.find(".avgrestime").attr("id", "dqs_" + uid); 125 | $("#qs_div").append(_dom); 126 | }; 127 | updateGraph(chart,"dqs_"+uid); 128 | }; 129 | $(".close").click(function() { 130 | var self = $(this).parent().parent().parent().parent().css('display','none'); 131 | }); 132 | }; 133 | 134 | 135 | function updateEveryAgent1(everyAgentqsDatas,divname,title){ 136 | for (var index in everyAgentqsDatas) { 137 | var chart = everyAgentqsDatas[index]; 138 | var uid = chart.uid; 139 | var target = $("#"+uid); 140 | if (!!target.html()) { 141 | } else { 142 | var _dom = $("#avg_template").clone(); 143 | _dom.find(".screen-label").html(uid +' ' + title); 144 | _dom.find(".avgrestime").attr("id", uid); 145 | _dom.css('display','block'); 146 | $("#" + divname).append(_dom); 147 | }; 148 | //target = $(); 149 | updateGraph(chart,uid); 150 | }; 151 | $(".close").click(function() { 152 | var self = $(this).parent().parent().parent().parent().css('display','none'); 153 | }); 154 | }; 155 | 156 | //update([{"name":"","uid":0,"summary": 157 | //{"Load Data uniques uniqs":2000},"charts":{"latency":{"name":"","uid":22,"columns":["time","min","max","avg","median","95%","99%"],"rows":[[0.03,0,0,0,0,0,0],[0.03,5,92,27.5,26,45,75],[0.04,6,62,26,25,45,57]]}}}]); 158 | -------------------------------------------------------------------------------- /lib/console/js/ui/layout.js: -------------------------------------------------------------------------------- 1 | DygraphLayout=function(b,a){this.dygraph_=b;this.options={};Dygraph.update(this.options,a?a:{});this.datasets=new Array()};DygraphLayout.prototype.attr_=function(a){return this.dygraph_.attr_(a)};DygraphLayout.prototype.addDataset=function(a,b){this.datasets[a]=b};DygraphLayout.prototype.evaluate=function(){this._evaluateLimits();this._evaluateLineCharts();this._evaluateLineTicks()};DygraphLayout.prototype._evaluateLimits=function(){this.minxval=this.maxxval=null;if(this.options.dateWindow){this.minxval=this.options.dateWindow[0];this.maxxval=this.options.dateWindow[1]}else{for(var c in this.datasets){if(!this.datasets.hasOwnProperty(c)){continue}var d=this.datasets[c];var b=d[0][0];if(!this.minxval||bthis.maxxval){this.maxxval=a}}}this.xrange=this.maxxval-this.minxval;this.xscale=(this.xrange!=0?1/this.xrange:1);this.minyval=this.options.yAxis[0];this.maxyval=this.options.yAxis[1];this.yrange=this.maxyval-this.minyval;this.yscale=(this.yrange!=0?1/this.yrange:1)};DygraphLayout.prototype._evaluateLineCharts=function(){this.points=new Array();for(var e in this.datasets){if(!this.datasets.hasOwnProperty(e)){continue}var d=this.datasets[e];for(var b=0;b=1){a.y=1}this.points.push(a)}}};DygraphLayout.prototype._evaluateLineTicks=function(){this.xticks=new Array();for(var c=0;c=0)&&(d<=1)){this.xticks.push([d,a])}}this.yticks=new Array();for(var c=0;c=0)&&(d<=1)){this.yticks.push([d,a])}}};DygraphLayout.prototype.evaluateWithError=function(){this.evaluate();if(!this.options.errorBars){return}var d=0;for(var g in this.datasets){if(!this.datasets.hasOwnProperty(g)){continue}var c=0;var f=this.datasets[g];for(var c=0;c0){for(var e=0;ethis.height){k.style.bottom="0px"}else{k.style.top=h+"px"}k.style.left="0px";k.style.textAlign="right";k.style.width=this.options.yAxisLabelWidth+"px";this.container.appendChild(k);this.ylabels.push(k)}var m=this.ylabels[0];var n=this.options.axisLabelFontSize;var a=parseInt(m.style.top)+n;if(a>this.height-n){m.style.top=(parseInt(m.style.top)-n/2)+"px"}}b.beginPath();b.moveTo(this.area.x,this.area.y);b.lineTo(this.area.x,this.area.y+this.area.h);b.closePath();b.stroke()}if(this.options.drawXAxis){if(this.layout.xticks){for(var e=0;ethis.width){c=this.width-this.options.xAxisLabelWidth;k.style.textAlign="right"}if(c<0){c=0;k.style.textAlign="left"}k.style.left=c+"px";k.style.width=this.options.xAxisLabelWidth+"px";this.container.appendChild(k);this.xlabels.push(k)}}b.beginPath();b.moveTo(this.area.x,this.area.y+this.area.h);b.lineTo(this.area.x+this.area.w,this.area.y+this.area.h);b.closePath();b.stroke()}b.restore()};DygraphCanvasRenderer.prototype._renderLineChart=function(){var b=this.element.getContext("2d");var d=this.options.colorScheme.length;var n=this.options.colorScheme;var x=this.options.fillAlpha;var C=this.layout.options.errorBars;var q=this.layout.options.fillGraph;var E=[];for(var F in this.layout.datasets){if(this.layout.datasets.hasOwnProperty(F)){E.push(F)}}var y=E.length;this.colors={};for(var A=0;A0){r=E[A-1]}var v=this.colors[g];s.save();s.strokeStyle=v;s.lineWidth=this.options.strokeWidth;var h=NaN;var f=[-1,-1];var k=0;var B=this.layout.yscale;var a=new RGBColor(v);var D="rgba("+a.r+","+a.g+","+a.b+","+x+")";s.fillStyle=D;s.beginPath();for(var w=0;w1){e=1}}var p=[t.y,e];p[0]=this.area.h*p[0]+this.area.y;p[1]=this.area.h*p[1]+this.area.y;if(!isNaN(h)){s.moveTo(h,f[0]);s.lineTo(t.canvasx,p[0]);s.lineTo(t.canvasx,p[1]);s.lineTo(h,f[1]);s.closePath()}f[0]=p[0];f[1]=p[1];h=t.canvasx}}s.fill()}}}for(var A=0;A0){if(arguments.length==4){this.warn("Using deprecated four-argument dygraph constructor");this.__old_init__(c,b,arguments[2],arguments[3])}else{this.__init__(c,b,a)}}};Dygraph.NAME="Dygraph";Dygraph.VERSION="1.2";Dygraph.__repr__=function(){return"["+this.NAME+" "+this.VERSION+"]"};Dygraph.toString=function(){return this.__repr__()};Dygraph.DEFAULT_ROLL_PERIOD=1;Dygraph.DEFAULT_WIDTH=480;Dygraph.DEFAULT_HEIGHT=320;Dygraph.AXIS_LINE_WIDTH=0.3;Dygraph.DEFAULT_ATTRS={highlightCircleSize:3,pixelsPerXLabel:60,pixelsPerYLabel:30,labelsDivWidth:250,labelsDivStyles:{},labelsSeparateLines:false,labelsKMB:false,labelsKMG2:false,showLabelsOnHighlight:true,yValueFormatter:function(a){return Dygraph.round_(a,2)},strokeWidth:1,axisTickSize:3,axisLabelFontSize:14,xAxisLabelWidth:50,yAxisLabelWidth:50,rightGap:5,showRoller:false,xValueFormatter:Dygraph.dateString_,xValueParser:Dygraph.dateParser,xTicker:Dygraph.dateTicker,delimiter:",",logScale:false,sigma:2,errorBars:false,fractions:false,wilsonInterval:true,customBars:false,fillGraph:false,fillAlpha:0.15,connectSeparatedPoints:false,stackedGraph:false,hideOverlayOnMouseOut:true};Dygraph.DEBUG=1;Dygraph.INFO=2;Dygraph.WARNING=3;Dygraph.ERROR=3;Dygraph.prototype.__old_init__=function(f,d,e,b){if(e!=null){var a=["Date"];for(var c=0;c=10){n.doZoom_(Math.min(b,m),Math.max(b,m))}else{n.canvas_.getContext("2d").clearRect(0,0,n.canvas_.width,n.canvas_.height)}b=null;a=null}if(e){e=false;l=null;j=null}});Dygraph.addEvent(this.hidden_,"dblclick",function(o){if(n.dateWindow_==null){return}n.dateWindow_=null;n.drawGraph_(n.rawData_);var p=n.rawData_[0][0];var q=n.rawData_[n.rawData_.length-1][0];if(n.attr_("zoomCallback")){n.attr_("zoomCallback")(p,q)}})};Dygraph.prototype.drawZoomRect_=function(c,d,b){var a=this.canvas_.getContext("2d");if(b){a.clearRect(Math.min(c,b),0,Math.abs(c-b),this.height_)}if(d&&c){a.fillStyle="rgba(128,128,128,0.33)";a.fillRect(Math.min(c,d),0,Math.abs(d-c),this.height_)}};Dygraph.prototype.doZoom_=function(d,a){var b=this.toDataCoords(d,null);var c=b[0];b=this.toDataCoords(a,null);var e=b[0];this.dateWindow_=[c,e];this.drawGraph_(this.rawData_);if(this.attr_("zoomCallback")){this.attr_("zoomCallback")(c,e)}};Dygraph.prototype.mouseMove_=function(b){var a=Dygraph.pageX(b)-Dygraph.findPosX(this.hidden_);var s=this.layout_.points;var m=-1;var j=-1;var q=1e+100;var r=-1;for(var f=0;fq){continue}q=h;r=f}if(r>=0){m=s[r].xval}if(a>s[s.length-1].canvasx){m=s[s.length-1].xval}this.selPoints_=[];var g=0;var d=s.length;var o=this.attr_("stackedGraph");if(!this.attr_("stackedGraph")){for(var f=0;f=0;f--){if(s[f].xval==m){var c={};for(var e in s[f]){c[e]=s[f][e]}c.yval-=g;g+=c.yval;this.selPoints_.push(c)}}}if(this.attr_("highlightCallback")){var n=this.lastHighlightCallbackX;if(n!==null&&m!=n){this.lastHighlightCallbackX=m;this.attr_("highlightCallback")(b,m,this.selPoints_)}}this.lastx_=m;this.updateSelection_()};Dygraph.prototype.updateSelection_=function(){var a=this.attr_("highlightCircleSize");var n=this.canvas_.getContext("2d");if(this.previousVerticalX_>=0){var l=this.previousVerticalX_;n.clearRect(l-a-1,0,2*a+2,this.height_)}var m=function(c){return c&&!isNaN(c)};if(this.selPoints_.length>0){var b=this.selPoints_[0].canvasx;var d=this.attr_("xValueFormatter")(this.lastx_,this)+":";var e=this.attr_("yValueFormatter");var j=this.colors_.length;if(this.attr_("showLabelsOnHighlight")){for(var f=0;f"}var k=this.selPoints_[f];var h=new RGBColor(this.colors_[f%j]);var g=e(k.yval);d+=" "+k.name+":"+g}this.attr_("labelsDiv").innerHTML=d}n.save();for(var f=0;f=0){for(var a in this.layout_.datasets){if(b=Dygraph.DAILY){y.push({v:l,label:new Date(l+3600*1000).strftime(u)})}else{y.push({v:l,label:this.hmsString_(l)})}}}else{var f;var o=1;if(a==Dygraph.MONTHLY){f=[0,1,2,3,4,5,6,7,8,9,10,11,12]}else{if(a==Dygraph.QUARTERLY){f=[0,3,6,9]}else{if(a==Dygraph.BIANNUAL){f=[0,6]}else{if(a==Dygraph.ANNUAL){f=[0]}else{if(a==Dygraph.DECADAL){f=[0];o=10}}}}}var r=new Date(n).getFullYear();var p=new Date(k).getFullYear();var c=Dygraph.zeropad;for(var s=r;s<=p;s++){if(s%o!=0){continue}for(var q=0;qk){continue}y.push({v:l,label:new Date(l).strftime("%b %y")})}}}return y};Dygraph.dateTicker=function(a,f,d){var b=-1;for(var e=0;e=d.attr_("pixelsPerXLabel")){b=e;break}}if(b>=0){return d.GetXAxis(a,f,b)}else{}};Dygraph.numericTicks=function(v,u,l){if(l.attr_("labelsKMG2")){var f=[1,2,4,8]}else{var f=[1,2,5]}var x,p,a,q;var h=l.attr_("pixelsPerYLabel");for(var t=-10;t<50;t++){if(l.attr_("labelsKMG2")){var c=Math.pow(16,t)}else{var c=Math.pow(10,t)}for(var s=0;sh){break}}if(d>h){break}}var w=[];var r;var o=[];if(l.attr_("labelsKMB")){r=1000;o=["K","M","B","T"]}if(l.attr_("labelsKMG2")){if(r){l.warn("Setting both labelsKMB and labelsKMG2. Pick one!")}r=1024;o=["k","M","G","T"]}if(p>a){x*=-1}for(var t=0;t=0;s--,m/=r){if(b>=m){e=Dygraph.round_(g/m,1)+o[s];break}}}w.push({label:e,v:g})}return w};Dygraph.prototype.addYTicks_=function(c,b){var a=Dygraph.numericTicks(c,b,this);this.layout_.updateOptions({yAxis:[c,b],yTicks:a})};Dygraph.prototype.extremeValues_=function(d){var h=null,f=null;var b=this.attr_("errorBars")||this.attr_("customBars");if(b){for(var c=0;cg){a=g}if(ef){f=e}if(h==null||af){f=g}if(h==null||g=E&&e===null){e=t}if(g[t][0]<=f){D=t}}if(e===null){e=0}if(e>0){e--}if(D===null){D=g.length-1}if(Dx){x=o}if(p){var m=[];for(var u=0;ux){x=d[g[u][0]]}}h.push([this.attr_("labels")[w],m])}else{this.layout_.addDataset(this.attr_("labels")[w],g)}}}if(h.length>0){for(var w=(h.length-1);w>=0;w--){this.layout_.addDataset(h[w][0],h[w][1])}}if(this.valueRange_!=null){this.addYTicks_(this.valueRange_[0],this.valueRange_[1]);this.displayedYRange_=this.valueRange_}else{if(this.attr_("includeZero")&&y>0){y=0}var v=x-y;if(v==0){v=x}var c=x+0.1*v;var B=y-0.1*v;if(B<0&&y>=0){B=0}if(c>0&&x<=0){c=0}if(this.attr_("includeZero")){if(x<0){c=0}if(y>0){B=0}}this.addYTicks_(B,c);this.displayedYRange_=[B,c]}this.addXTicks_();this.layout_.updateOptions({dateWindow:this.dateWindow_});this.layout_.evaluateWithError();this.plotter_.clear();this.plotter_.render();this.canvas_.getContext("2d").clearRect(0,0,this.canvas_.width,this.canvas_.height);if(this.attr_("drawCallback")!==null){this.attr_("drawCallback")(this,n)}};Dygraph.prototype.rollingAverage=function(m,d){if(m.length<2){return m}var d=Math.min(d,m.length-1);var b=[];var s=this.attr_("sigma");if(this.fractions_){var k=0;var h=0;var e=100;for(var x=0;x=0){k-=m[x-d][1][0];h-=m[x-d][1][1]}var B=m[x][0];var v=h?k/h:0;if(this.attr_("errorBars")){if(this.wilsonInterval_){if(h){var t=v<0?0:v,u=h;var A=s*Math.sqrt(t*(1-t)/u+s*s/(4*u*u));var a=1+s*s/h;var F=(t+s*s/(2*h)-A)/a;var o=(t+s*s/(2*h)+A)/a;b[x]=[B,[t*e,(t-F)*e,(o-t)*e]]}else{b[x]=[B,[0,0,0]]}}else{var z=h?s*Math.sqrt(v*(1-v)/h):1;b[x]=[B,[e*v,e*z,e*z]]}}else{b[x]=[B,e*v]}}}else{if(this.attr_("customBars")){var F=0;var C=0;var o=0;var g=0;for(var x=0;x=0){var r=m[x-d];if(r[1][1]!=null&&!isNaN(r[1][1])){F-=r[1][0];C-=r[1][1];o-=r[1][2];g-=1}}b[x]=[m[x][0],[1*C/g,1*(C-F)/g,1*(o-C)/g]]}}else{var q=Math.min(d-1,m.length-2);if(!this.attr_("errorBars")){if(d==1){return m}for(var x=0;x=0||b.indexOf("/")>=0||isNaN(parseFloat(b))){a=true}else{if(b.length==8&&b>"19700101"&&b<"20371231"){a=true}}if(a){this.attrs_.xValueFormatter=Dygraph.dateString_;this.attrs_.xValueParser=Dygraph.dateParser;this.attrs_.xTicker=Dygraph.dateTicker}else{this.attrs_.xValueFormatter=function(c){return c};this.attrs_.xValueParser=function(c){return parseFloat(c)};this.attrs_.xTicker=Dygraph.numericTicks}};Dygraph.prototype.parseCSV_=function(h){var m=[];var q=h.split("\n");var b=this.attr_("delimiter");if(q[0].indexOf(b)==-1&&q[0].indexOf("\t")>=0){b="\t"}var a=0;if(this.labelsFromCSV_){a=1;this.attrs_.labels=q[0].split(b)}var c;var o=false;var d=this.attr_("labels").length;var l=false;for(var g=a;g0&&k[0]0&&k[0]=0){this.loadedEvent_(this.file_)}else{var b=new XMLHttpRequest();var a=this;b.onreadystatechange=function(){if(b.readyState==4){if(b.status==200){a.loadedEvent_(b.responseText)}}};b.open("GET",this.file_,true);b.send(null)}}else{this.error("Unknown data format: "+(typeof this.file_))}}}}};Dygraph.prototype.updateOptions=function(a){if(a.rollPeriod){this.rollPeriod_=a.rollPeriod}if(a.dateWindow){this.dateWindow_=a.dateWindow}if(a.valueRange){this.valueRange_=a.valueRange}Dygraph.update(this.user_attrs_,a);this.labelsFromCSV_=(this.attr_("labels")==null);this.layout_.updateOptions({errorBars:this.attr_("errorBars")});if(a.file){this.file_=a.file;this.start_()}else{this.drawGraph_(this.rawData_)}};Dygraph.prototype.resize=function(b,a){if((b===null)!=(a===null)){this.warn("Dygraph.resize() should be called with zero parameters or two non-NULL parameters. Pretending it was zero.");b=a=null}this.maindiv_.innerHTML="";this.attrs_.labelsDiv=null;if(b){this.maindiv_.style.width=b+"px";this.maindiv_.style.height=a+"px";this.width_=b;this.height_=a}else{this.width_=this.maindiv_.offsetWidth;this.height_=this.maindiv_.offsetHeight}this.createInterface_();this.drawGraph_(this.rawData_)};Dygraph.prototype.adjustRoll=function(a){this.rollPeriod_=a;this.drawGraph_(this.rawData_)};Dygraph.prototype.visibility=function(){if(!this.attr_("visibility")){this.attrs_.visibility=[]}while(this.attr_("visibility").length=a.length){this.warn("invalid series number in setVisibility: "+b)}else{a[b]=c;this.drawGraph_(this.rawData_)}};Dygraph.createCanvas=function(){var a=document.createElement("canvas");isIE=(/MSIE/.test(navigator.userAgent)&&!window.opera);if(isIE){a=G_vmlCanvasManager.initElement(a)}return a};Dygraph.GVizChart=function(a){this.container=a};Dygraph.GVizChart.prototype.draw=function(b,a){this.container.innerHTML="";this.date_graph=new Dygraph(this.container,b,a)};Dygraph.GVizChart.prototype.setSelection=function(b){var a=false;if(b.length){a=b[0].row}this.date_graph.setSelection(a)};Dygraph.GVizChart.prototype.getSelection=function(){var b=[];var c=this.date_graph.getSelection();if(c<0){return b}col=1;for(var a in this.date_graph.layout_.datasets){b.push({row:c,column:col});col++}return b};DateGraph=Dygraph;function RGBColor(g){this.ok=false;if(g.charAt(0)=="#"){g=g.substr(1,6)}g=g.replace(/ /g,"");g=g.toLowerCase();var a={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",feldspar:"d19275",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslateblue:"8470ff",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",violetred:"d02090",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32"};for(var c in a){if(g==c){g=a[c]}}var h=[{re:/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,example:["rgb(123, 234, 45)","rgb(255,234,245)"],process:function(i){return[parseInt(i[1]),parseInt(i[2]),parseInt(i[3])]}},{re:/^(\w{2})(\w{2})(\w{2})$/,example:["#00ff00","336699"],process:function(i){return[parseInt(i[1],16),parseInt(i[2],16),parseInt(i[3],16)]}},{re:/^(\w{1})(\w{1})(\w{1})$/,example:["#fb0","f0f"],process:function(i){return[parseInt(i[1]+i[1],16),parseInt(i[2]+i[2],16),parseInt(i[3]+i[3],16)]}}];for(var b=0;b255)?255:this.r);this.g=(this.g<0||isNaN(this.g))?0:((this.g>255)?255:this.g);this.b=(this.b<0||isNaN(this.b))?0:((this.b>255)?255:this.b);this.toRGB=function(){return"rgb("+this.r+", "+this.g+", "+this.b+")"};this.toHex=function(){var k=this.r.toString(16);var j=this.g.toString(16);var i=this.b.toString(16);if(k.length==1){k="0"+k}if(j.length==1){j="0"+j}if(i.length==1){i="0"+i}return"#"+k+j+i}}Date.ext={};Date.ext.util={};Date.ext.util.xPad=function(a,c,b){if(typeof(b)=="undefined"){b=10}for(;parseInt(a,10)1;b/=10){a=c.toString()+a}return a.toString()};Date.prototype.locale="en-GB";if(document.getElementsByTagName("html")&&document.getElementsByTagName("html")[0].lang){Date.prototype.locale=document.getElementsByTagName("html")[0].lang}Date.ext.locales={};Date.ext.locales.en={a:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],x:"%d/%m/%y",X:"%T"};Date.ext.locales["en-US"]=Date.ext.locales.en;Date.ext.locales["en-US"].c="%a %d %b %Y %r %Z";Date.ext.locales["en-US"].x="%D";Date.ext.locales["en-US"].X="%r";Date.ext.locales["en-GB"]=Date.ext.locales.en;Date.ext.locales["en-AU"]=Date.ext.locales["en-GB"];Date.ext.formats={a:function(a){return Date.ext.locales[a.locale].a[a.getDay()]},A:function(a){return Date.ext.locales[a.locale].A[a.getDay()]},b:function(a){return Date.ext.locales[a.locale].b[a.getMonth()]},B:function(a){return Date.ext.locales[a.locale].B[a.getMonth()]},c:"toLocaleString",C:function(a){return Date.ext.util.xPad(parseInt(a.getFullYear()/100,10),0)},d:["getDate","0"],e:["getDate"," "],g:function(a){return Date.ext.util.xPad(parseInt(Date.ext.util.G(a)/100,10),0)},G:function(c){var e=c.getFullYear();var b=parseInt(Date.ext.formats.V(c),10);var a=parseInt(Date.ext.formats.W(c),10);if(a>b){e++}else{if(a===0&&b>=52){e--}}return e},H:["getHours","0"],I:function(b){var a=b.getHours()%12;return Date.ext.util.xPad(a===0?12:a,0)},j:function(c){var a=c-new Date(""+c.getFullYear()+"/1/1 GMT");a+=c.getTimezoneOffset()*60000;var b=parseInt(a/60000/60/24,10)+1;return Date.ext.util.xPad(b,0,100)},m:function(a){return Date.ext.util.xPad(a.getMonth()+1,0)},M:["getMinutes","0"],p:function(a){return Date.ext.locales[a.locale].p[a.getHours()>=12?1:0]},P:function(a){return Date.ext.locales[a.locale].P[a.getHours()>=12?1:0]},S:["getSeconds","0"],u:function(a){var b=a.getDay();return b===0?7:b},U:function(e){var a=parseInt(Date.ext.formats.j(e),10);var c=6-e.getDay();var b=parseInt((a+c)/7,10);return Date.ext.util.xPad(b,0)},V:function(e){var c=parseInt(Date.ext.formats.W(e),10);var a=(new Date(""+e.getFullYear()+"/1/1")).getDay();var b=c+(a>4||a<=1?0:1);if(b==53&&(new Date(""+e.getFullYear()+"/12/31")).getDay()<4){b=1}else{if(b===0){b=Date.ext.formats.V(new Date(""+(e.getFullYear()-1)+"/12/31"))}}return Date.ext.util.xPad(b,0)},w:"getDay",W:function(e){var a=parseInt(Date.ext.formats.j(e),10);var c=7-Date.ext.formats.u(e);var b=parseInt((a+c)/7,10);return Date.ext.util.xPad(b,0,10)},y:function(a){return Date.ext.util.xPad(a.getFullYear()%100,0)},Y:"getFullYear",z:function(c){var b=c.getTimezoneOffset();var a=Date.ext.util.xPad(parseInt(Math.abs(b/60),10),0);var e=Date.ext.util.xPad(b%60,0);return(b>0?"-":"+")+a+e},Z:function(a){return a.toString().replace(/^.*\(([^)]+)\)$/,"$1")},"%":function(a){return"%"}};Date.ext.aggregates={c:"locale",D:"%m/%d/%y",h:"%b",n:"\n",r:"%I:%M:%S %p",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"};Date.ext.aggregates.z=Date.ext.formats.z(new Date());Date.ext.aggregates.Z=Date.ext.formats.Z(new Date());Date.ext.unsupported={};Date.prototype.strftime=function(a){if(!(this.locale in Date.ext.locales)){if(this.locale.replace(/-[a-zA-Z]+$/,"") in Date.ext.locales){this.locale=this.locale.replace(/-[a-zA-Z]+$/,"")}else{this.locale="en-GB"}}var c=this;while(a.match(/%[cDhnrRtTxXzZ]/)){a=a.replace(/%([cDhnrRtTxXzZ])/g,function(e,d){var g=Date.ext.aggregates[d];return(g=="locale"?Date.ext.locales[c.locale][d]:g)})}var b=a.replace(/%([aAbBCdegGHIjmMpPSuUVwWyY%])/g,function(e,d){var g=Date.ext.formats[d];if(typeof(g)=="string"){return c[g]()}else{if(typeof(g)=="function"){return g.call(c,c)}else{if(typeof(g)=="object"&&typeof(g[0])=="string"){return Date.ext.util.xPad(c[g[0]](),g[1])}else{return d}}}});c=null;return b}; 2 | -------------------------------------------------------------------------------- /lib/console/js/ui/renderers.jquery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * node abstract for agent node 3 | * 4 | */ 5 | Node.prototype.render = function() { 6 | var n = this; 7 | this._dom = $("#node_template").clone() 8 | .find(".node").html('[' +n.iport+ ']').end() 9 | .attr("id", "node_" + n.nodeId) 10 | .data('label', n.nodeId); 11 | // alert(this._dom.html()); 12 | // Add to control panel in alphabetical order 13 | $("#conndiv").append(n._dom); 14 | }; 15 | 16 | Node.prototype.destroy = function() { 17 | this._dom.remove(); 18 | }; 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/console/js/ui/webclient.jquery.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Instantiates WebClient(), binds document.ready() 3 | */ 4 | 5 | var web_client = new WebClient(io); 6 | 7 | var REPORT_INTERVAL = 1000; 8 | 9 | // Update statistics widget 10 | setInterval(function() { 11 | var now = new Date(); 12 | var elapsed = (now.getTime() - web_client.stats.start.getTime()) / 1000; 13 | var minutes = parseInt(elapsed / 60); 14 | var seconds = parseInt(elapsed % 60); 15 | var rate = web_client.stats.messages/elapsed; 16 | $("#stats") 17 | .find(".nodes b").html(web_client.stats.nodes).end() 18 | .find(".elapsed b").html(minutes + ":" + (seconds < 10 ? "0" : "") + seconds).end(); 19 | //.find(".summary b").html((rate).toFixed(2)); 20 | },REPORT_INTERVAL); 21 | 22 | 23 | function jsonToTable(json) { 24 | var txt = ""; 25 | for ( var i in json) { 26 | var ele = json[i]; 27 | txt += "" + ele.key + "" + ele.max + "" + ele.min + "" + Math.round(ele.avg) + "" + Math.round(ele.qps) + "" + ele.num + ""; 28 | } 29 | return txt; 30 | }; 31 | 32 | var updateIncrData = function(sincrData){ 33 | var incrData = {}; 34 | for (var agent in sincrData) { 35 | var params = sincrData[agent]; 36 | for (var key in params) { 37 | incrData[key] = incrData[key]==null?params[key]:incrData[key]+params[key]; 38 | } 39 | } 40 | var incrHTML = ""; 41 | for (var key in incrData){ 42 | incrHTML +=key+ ":(" + incrData[key] + ') '; 43 | } 44 | $('#errorinput').html(incrHTML); 45 | } 46 | 47 | var gdata = {}; 48 | 49 | var updateTimesData = function(agent,user,stimeData){ 50 | var conns = agent; 51 | var act = {}; 52 | var summary = {}; 53 | for (var agent in stimeData) { 54 | var params = stimeData[agent]; 55 | for (var key in params) { 56 | act[key] = {}; 57 | summary[key] = null; 58 | } 59 | } 60 | for (var key in act) { 61 | for (var agent in stimeData) { 62 | var params = stimeData[agent]; 63 | var single = params[key]; 64 | if (!single) continue; 65 | var exist = summary[key]; 66 | if (!exist){ 67 | summary[key] = {key:key,min:single.min,max:single.max,avg:single.avg,num:single.num}; 68 | summary[key].qps = 1000/single.avg*conns; 69 | } else { 70 | if (single.minexist.max) { 74 | exist.max = single.max; 75 | } 76 | var num = exist.num+single.num; 77 | exist.avg = (exist.avg*exist.num + single.avg*single.num)/num; 78 | exist.qps = 1000/exist.avg*conns; 79 | exist.num = num; 80 | } 81 | } 82 | }; 83 | var sortSum = _.sortBy(summary,function(ele){ return -1*(ele.num); }); 84 | for (var index in sortSum) { 85 | var ex = sortSum[index]; 86 | var key = ex.key; 87 | var columns = [key,'avg','min','max','qps']; 88 | var grow = gdata[key] || []; 89 | var last = grow[grow.length-1]; 90 | if (!last || last[0]!==ex.num) { 91 | grow.push([ex.num,ex.avg,ex.min,ex.max,ex.qps]); 92 | } 93 | gdata[key] = grow; 94 | var qpschart = {}; 95 | qpschart.columns = columns; 96 | qpschart.rows = grow; 97 | qpschart.uid = key; 98 | updateGraph(qpschart,key); 99 | } 100 | document.getElementById("reportSummary").innerHTML = jsonToTable(sortSum); 101 | return; 102 | } 103 | 104 | var gavgrows = []; 105 | var gqpsrows = []; 106 | 107 | // Event bindings, main method 108 | $(document).ready(function() { 109 | var bottom_height = $(".stat:first").height(); 110 | var bar_height = $(".bar:first").height(); 111 | 112 | // Calculate individual screen size 113 | function calc_screen_size(scount) { 114 | if (!scount) { scount = $("#screens .screen").length; } 115 | var ssize = (($(window).height() - bottom_height - 20) / scount) 116 | - (bar_height + 53); 117 | return ssize; 118 | } 119 | 120 | // Resize screens 121 | web_client.resize = function(scount, resize_bottom) { 122 | if (!resize_bottom) { resize_bottom = true; } 123 | //$("#controls2, #right").height($(window).height()); 124 | //$(".console").height(calc_screen_size(scount)); 125 | var screen_width = $(window).width() - $("#controls2").width(); 126 | $("#right" + (resize_bottom ? ", #bottom" : "")) 127 | .width(screen_width).css('max-width', screen_width); 128 | }; 129 | $(window).resize(function() { 130 | web_client.resize(); 131 | }); 132 | 133 | //$("#run-button").css('display','none'); 134 | $("#runcode-button").css('display','none'); 135 | $("#codeinput").css('display','none'); 136 | 137 | web_client.resize(); 138 | 139 | $("#ready-button").click(function() { 140 | web_client.stats = {messages: 0, nodes: 0, start:new Date()}; 141 | web_client.socket.emit('exit4reready'); 142 | var agent = $("#agentinput").val(); 143 | var user = $("#maxuserinput").val(); 144 | var message = {agent:agent,maxuser:user}; 145 | web_client.socket.emit('ready',message); 146 | $(this).attr('disable',true); 147 | $('#conndiv').html(''); 148 | $('#run-button').css('display','none'); 149 | }); 150 | 151 | $("#run-button").click(function() { 152 | var agent = $("#agentinput").val(); 153 | var maxuser = $("#maxuserinput").val(); 154 | var script = $("#robotinput").val(); 155 | var message = {agent:agent,maxuser:maxuser,script:script}; 156 | web_client.socket.emit('run',message); 157 | gdata = {}; 158 | $("#avg_div").html(''); 159 | $("#hitdiv").html('Running...'); 160 | $('#run-button').css('display','none'); 161 | $("#errorinput").html(''); 162 | }); 163 | 164 | $("#sumbtn").click(function() { 165 | web_client.detailTimer = true; 166 | showDetailAgent(); 167 | }); 168 | 169 | $("#avgbtn").click(function() { 170 | web_client.detailTimer = true; 171 | showDetailAgentAvg(); 172 | }); 173 | 174 | $("#qsbtn").click(function() { 175 | web_client.detailTimer = true; 176 | showDetailAgentQs(); 177 | }); 178 | 179 | }); 180 | -------------------------------------------------------------------------------- /lib/console/js/webclient.js: -------------------------------------------------------------------------------- 1 | var _node = {Node: Node}; 2 | 3 | var WebClient = function(io) { 4 | this.nodes = {}; 5 | this.ids = {}; 6 | this.streams = {}; 7 | this.count = 0; 8 | this.detailTimer = false; 9 | this.histories = {}; 10 | this.stats = { messages:0,nodes:0,start:new Date()}; 11 | this.connected = false; 12 | var wc = this; 13 | this.socket = io.connect('http://'+window.location.hostname+':8888'); 14 | this.socket.on('connect', function() { 15 | wc.connected = true; 16 | wc.socket.emit('announce_web_client'); 17 | var REPORT_INTERVAL = 3 * 1000; 18 | setInterval(function() { 19 | wc.socket.emit('webreport',{}); 20 | },REPORT_INTERVAL); 21 | 22 | var detailId = setInterval(function() { 23 | if (!!wc.detailTimer) { 24 | wc.socket.emit('detailreport',{}); 25 | } 26 | },REPORT_INTERVAL); 27 | 28 | }); 29 | 30 | var isInited = false; 31 | // Add a new Node to pool 32 | this.socket.on('add_node', function(message) { 33 | console.log(JSON.stringify(message)); 34 | var nodeId = message.nodeId; 35 | var iport = message.iport; 36 | if (!wc.ids[nodeId]){ 37 | wc.add_node(nodeId,iport); 38 | wc.ids[nodeId] = nodeId; 39 | showUI('block'); 40 | } else { 41 | console.log('duplicated server add ' + nodeId); 42 | } 43 | }); 44 | 45 | // Remove Node from pool 46 | this.socket.on('remove_node', function(message) { 47 | wc.remove_node(message.node); 48 | }); 49 | //report status 50 | this.socket.on('webreport', function(snum,suser,stimeData,sincrData) { 51 | //doReport(timeData); 52 | $('#agentinput').val(snum); 53 | $('#maxuserinput').val(suser); 54 | updateIncrData(sincrData); 55 | updateTimesData(snum,suser,stimeData); 56 | }); 57 | 58 | this.socket.on('detailreport', function(message) { 59 | doReportDetail(message); 60 | }); 61 | 62 | /* temporary code */ 63 | this.socket.on('error', function(message) { 64 | $("#errorinput").html('['+message.node + ']:'+ message.error).end(); 65 | }); 66 | /* temporary code */ 67 | 68 | this.socket.on('statusreport', function(message) { 69 | var nodeId = message.id; 70 | var status = message.status; 71 | var hit = ''; 72 | if (status===0) { hit = 'IDLE'; } 73 | if (status==1) { hit = 'READY';$('#run-button').css('display','') } 74 | if (status==2) { hit = 'RUNNING';$('#run-button').css('display','none')}; 75 | $("#hitdiv").html(hit); 76 | }); 77 | 78 | // Update total message count stats 79 | this.socket.on('stats', function(message) { 80 | if (!wc.stats.message_offset) { 81 | wc.stats.message_offset = message.message_count; 82 | } 83 | wc.stats.messages = message.message_count - wc.stats.message_offset; 84 | }); 85 | 86 | }; 87 | 88 | function doReportDetail(msg){ 89 | updateDetailAgent(msg.detailAgentSummary,' Summary'); 90 | updateAvgAgent(msg.detailAgentAvg,' Response Time'); 91 | updateEveryAgent(msg.detailAgentQs,'qs_div',' Qps Time'); 92 | } 93 | 94 | 95 | function doReport(message){ 96 | updateMain(message.globaldata); 97 | return ; 98 | } 99 | 100 | function showUI(value){ 101 | //$("#run-button").css('display',value); 102 | //$("#runcode-button").css('display',value); 103 | //$("#codeinput").css('display',value); 104 | } 105 | 106 | WebClient.prototype = { 107 | add_node: function(nodeId,iport) { 108 | var node = new _node.Node(nodeId,iport,this); 109 | node.render(); 110 | var nodeId = nodeId; 111 | this.nodes[nodeId] = node; 112 | this.stats.nodes++; 113 | if (this.stats.nodes>=parseInt($('#agentinput').val())){ 114 | $('#ready-button').val('ReReady'); 115 | $('#run-button').show(); 116 | } else { 117 | $('#ready-button').val('Readying'); 118 | $("#run-button").css('display','none'); 119 | } 120 | }, 121 | 122 | remove_node: function(nodeId) { 123 | var node = this.nodes[nodeId]; 124 | if (!!node) { 125 | node.destroy(); 126 | delete this.nodes[node.nodeId]; 127 | } 128 | this.stats.nodes--; 129 | if (this.stats.nodes<=0){ 130 | showUI('none'); 131 | this.stats.nodes = 0; 132 | } 133 | delete this.ids[nodeId]; 134 | }, 135 | 136 | // Resize screens, defined in web_client.jquery.js 137 | resize: function() { throw Error("WebClient.resize() not defined"); }, 138 | 139 | }; 140 | 141 | // Export for nodeunit tests 142 | try { 143 | exports.WebClient = WebClient; 144 | } catch(err) { 145 | 146 | }; 147 | -------------------------------------------------------------------------------- /lib/master/nodeclient.js: -------------------------------------------------------------------------------- 1 | var __ = require('underscore'); 2 | 3 | // NodeClient is a server/machine/instance running a agent socket 4 | var NodeClient = function(nodeId, socket, server) { 5 | this.nodeId = nodeId; 6 | this.socket = socket; 7 | this.iport = socket.handshake.address.address + ":" + socket.handshake.address.port; 8 | this.id = socket.id; 9 | this.log_server = server; 10 | var node = this; 11 | 12 | // Join 'nodes' room 13 | socket.join('nodes'); 14 | 15 | socket.on('disconnect', function() { 16 | // Notify all WebClients upon disconnect 17 | __(node.log_server.web_clients).each(function(web_client, client_id) { 18 | web_client.remove_node(node); 19 | }); 20 | socket.leave('nodes'); 21 | }); 22 | } 23 | 24 | module.exports = { 25 | NodeClient: NodeClient 26 | } 27 | -------------------------------------------------------------------------------- /lib/master/server.js: -------------------------------------------------------------------------------- 1 | var io = require('socket.io'); 2 | var __ = require('underscore'); 3 | var _nodeclient = require('./nodeclient.js'); 4 | var _wc = require('./webclient.js'); 5 | var logging = require('../common/logging').Logger; 6 | var stat = require('../monitor/stat'); 7 | var starter = require('./starter'); 8 | 9 | var STATUS_INTERVAL = 60 * 1000; // 60 seconds 10 | var HEARTBEAT_INTERVAL = 30 * 1000; // 30 seconds 11 | var STATUS_IDLE = 0; 12 | var STATUS_READY = 1; 13 | var STATUS_RUNNING = 2; 14 | var STATUS_DISCONN = 3; 15 | /** 16 | * 17 | * robot master instance 18 | * 19 | * @param {Object} conf 20 | * 21 | * conf.main client run file 22 | */ 23 | var Server = function(conf) { 24 | this.log = logging; 25 | this.nodes = {}; 26 | this.web_clients = {}; 27 | this.conf = conf || {}; 28 | this.runconfig = null; 29 | this.status = STATUS_IDLE; 30 | var rserver = this; 31 | 32 | setInterval(function() { 33 | rserver.log.info("Nodes: " + __(rserver.nodes).size() + ", " + 34 | "WebClients: " + __(rserver.web_clients).size()); 35 | }, STATUS_INTERVAL); 36 | }; 37 | 38 | Server.prototype = { 39 | 40 | listen: function(port) { 41 | this.io = io.listen(port); 42 | this.register(); 43 | }, 44 | // Registers new Node with Server, announces to WebClients 45 | announce_node: function(socket, message) { 46 | var rserver = this,nodeId = message.nodeId; 47 | if (!!rserver.nodes[nodeId]) { 48 | this.log.warn("Warning: Node '" + nodeId + "' already exists, delete old items "); 49 | socket.emit('node_already_exists'); 50 | delete rserver.nodes[nodeId]; 51 | } 52 | 53 | var node = new _nodeclient.NodeClient(nodeId,socket, this); 54 | rserver.nodes[nodeId] = node; 55 | 56 | __(rserver.web_clients).each(function(web_client) { 57 | web_client.add_node(node); 58 | }); 59 | 60 | socket.on('disconnect', function() { 61 | delete rserver.nodes[nodeId]; 62 | __(rserver.web_clients).each(function(web_client) { 63 | web_client.remove_node(node); 64 | }); 65 | if (__.size(rserver.nodes)<=0){ 66 | rserver.status = STATUS_IDLE; 67 | } 68 | stat.clear(nodeId); 69 | }); 70 | 71 | socket.on('report', function(message) { 72 | stat.merge(nodeId,message); 73 | }); 74 | 75 | /* temporary code */ 76 | socket.on('error', function(message) { 77 | __(rserver.web_clients).each(function(web_client) { 78 | web_client.error_node(node,message); 79 | }); 80 | }); 81 | socket.on('crash', function(message) { 82 | __(rserver.web_clients).each(function(web_client) { 83 | web_client.error_node(node,message); 84 | }); 85 | rserver.status = STATUS_READY; 86 | }); 87 | /* temporary code */ 88 | }, 89 | // Registers new WebClient with Server 90 | announce_web_client: function(socket) { 91 | var rserver = this; 92 | var web_client = new _wc.WebClient(socket, rserver); 93 | rserver.web_clients[web_client.id] = web_client; 94 | __(rserver.nodes).each(function(node, nlabel) { 95 | web_client.add_node(node); 96 | }); 97 | setInterval(function(){ 98 | rserver.io.sockets.in('web_clients').emit('statusreport',{status:rserver.status}); 99 | },STATUS_INTERVAL/10); 100 | socket.on('webreport', function(message) { 101 | if (rserver.status==STATUS_RUNNING) { 102 | socket.emit('webreport',rserver.runconfig.agent,rserver.runconfig.maxuser,stat.getTimeData(rserver),stat.getCountData()); 103 | } 104 | }); 105 | 106 | socket.on('detailreport', function(message) { 107 | if (rserver.status==STATUS_RUNNING) { 108 | socket.emit('detailreport',stat.getDetails()); 109 | } 110 | }); 111 | 112 | socket.on('disconnect', function() { 113 | delete rserver.web_clients[web_client.id]; 114 | }); 115 | 116 | }, 117 | 118 | // Register announcement, disconnect callbacks 119 | register: function() { 120 | var rserver = this; 121 | rserver.io.set('log level', 1); 122 | rserver.io.sockets.on('connection', function(socket) { 123 | socket.on('announce_node', function(message) { 124 | rserver.log.info("Registering new node " + JSON.stringify(message)); 125 | rserver.announce_node(socket, message); 126 | }); 127 | socket.on('announce_web_client', function(message) { 128 | rserver.log.info("Registering new web_client"); 129 | rserver.announce_web_client(socket); 130 | socket.on('run', function(msg) { 131 | stat.clear(); 132 | msg.agent = __.size(rserver.nodes); 133 | console.log('server begin notify client to run machine...'); 134 | rserver.runconfig = msg; 135 | var i = 0; 136 | __.each(rserver.nodes,function(ele){ 137 | //console.log(i++); 138 | msg.index = i++; 139 | ele.socket.emit('run',msg); 140 | }); 141 | //rserver.io.sockets.in('nodes').emit('run',msg); 142 | rserver.status = STATUS_RUNNING; 143 | return ; 144 | }); 145 | socket.on('ready', function(msg) { 146 | console.log('server begin ready client ...'); 147 | rserver.io.sockets.in('nodes').emit('disconnect',{}); 148 | stat.clear(); 149 | rserver.status = STATUS_READY; 150 | rserver.runconfig = msg; 151 | starter.run(rserver.conf.mainFile,msg,rserver.conf.clients); 152 | return; 153 | }); 154 | 155 | socket.on('exit4reready', function() { 156 | __.each(rserver.nodes, function(obj){ 157 | obj.socket.emit('exit4reready'); 158 | }); 159 | rserver.nodes = {}; 160 | return ; 161 | }); 162 | 163 | }); 164 | }); 165 | 166 | // Broadcast heartbeat to all clients 167 | setInterval(function() { 168 | rserver.io.sockets.emit('heartbeat'); 169 | }, HEARTBEAT_INTERVAL); 170 | } 171 | }; 172 | 173 | exports.Server = Server; 174 | -------------------------------------------------------------------------------- /lib/master/starter.js: -------------------------------------------------------------------------------- 1 | var starter; 2 | var cp = require('child_process'); 3 | var fs = require('fs'); 4 | var vm = require('vm'); 5 | var path = require('path'); 6 | var util = require('util'); 7 | 8 | function Starter(){} 9 | 10 | var starter = module.exports = new Starter(); 11 | 12 | var log = function () { 13 | util.puts([].join.call(arguments, ' ')); 14 | }; 15 | 16 | /** 17 | *begin notify to run agent 18 | * 19 | * 20 | */ 21 | Starter.prototype.run = function(main,message,clients){ 22 | if (!clients) { 23 | clients = ['127.0.0.1']; 24 | this.prepare(main,message,clients); 25 | } else { 26 | this.prepare(main,message,clients); 27 | } 28 | }; 29 | 30 | Starter.prototype.prepare = function(main,message,clients){ 31 | var self = this; 32 | var count = parseInt(message.agent,10) || 1; 33 | for(var ipindex in clients) { 34 | for (var i = 0;i log/.log'; 36 | var ip = clients[ipindex]; 37 | if (ip==='127.0.0.1') { 38 | self.localrun(cmd); 39 | } else { 40 | self.sshrun(cmd,ip); 41 | } 42 | } 43 | } 44 | } 45 | 46 | Starter.prototype.sshrun = function (cmd,host,callback) { 47 | var hosts = [host]; 48 | log('Executing ' + $(cmd).yellow + ' on ' + $(hosts.join(', ')).blue); 49 | var wait = 0; 50 | data = []; 51 | if (hosts.length > 1) { 52 | parallelRunning = true; 53 | } 54 | hosts.forEach(function (host) { 55 | wait += 1; 56 | spawnProcess('ssh', [host, cmd], function (err, out) { 57 | if (!err) { 58 | data.push({ 59 | host: host, 60 | out: out 61 | }); 62 | } 63 | done(err); 64 | }); 65 | }); 66 | 67 | var error; 68 | function done(err) { 69 | error = error || err; 70 | if (--wait === 0) { 71 | starter.parallelRunning = false; 72 | if (error) { 73 | starter.abort('FAILED TO RUN, return code: ' + error); 74 | } else if (callback) { 75 | callback(data); 76 | } 77 | } 78 | } 79 | 80 | }; 81 | 82 | Starter.prototype.localrun = function (cmd, callback) { 83 | log('Executing ' + $(cmd).green + ' locally'); 84 | spawnProcess(cmd, ['',''], function (err, data) { 85 | if (err) { 86 | starter.abort('FAILED TO RUN, return code: ' + err); 87 | } else { 88 | if (callback) callback(data); 89 | } 90 | }); 91 | }; 92 | 93 | 94 | function addBeauty(prefix,buf) { 95 | var out = prefix + ' ' + buf 96 | .toString() 97 | .replace(/\s+$/, '') 98 | .replace(/\n/g, '\n' + prefix); 99 | return $(out).green; 100 | } 101 | 102 | function spawnProcess(command, options, callback) { 103 | var child = null; 104 | if (!!options[0]) { 105 | child = cp.spawn(command, options); 106 | 107 | } else { 108 | child = cp.exec(command, options); 109 | } 110 | 111 | var prefix = command === 'ssh' ? '[' + options[0] + '] ' : ''; 112 | prefix = $(prefix).grey; 113 | 114 | //child.stderr.on('data', function (chunk) { 115 | // log(addBeauty(chunk)); 116 | //}); 117 | 118 | var res = []; 119 | child.stdout.on('data', function (chunk) { 120 | res.push(chunk.toString()); 121 | log(addBeauty(chunk)); 122 | }); 123 | 124 | function addBeauty(buf) { 125 | return prefix + buf 126 | .toString() 127 | .replace(/\s+$/, '') 128 | .replace(/\n/g, '\n' + prefix); 129 | } 130 | 131 | child.on('exit', function (code) { 132 | if (callback) { 133 | callback(code === 0 ? null : code, res && res.join('\n')); 134 | } 135 | }); 136 | } 137 | 138 | 139 | Starter.prototype.set = function (key, def) { 140 | if (typeof def === 'function') { 141 | starter.__defineGetter__(key, def); 142 | } else { 143 | starter.__defineGetter__(key, function () { 144 | return def; 145 | }); 146 | } 147 | }; 148 | 149 | Starter.prototype.load = function (file) { 150 | if (!file) throw new Error('File not specified'); 151 | log('Executing compile ' + file); 152 | var code = coffee.compile(fs.readFileSync(file).toString()); 153 | var script = vm.createScript(code, file); 154 | script.runInNewContext(this); 155 | }; 156 | 157 | Starter.prototype.abort = function (msg) { 158 | log($(msg).red); 159 | //process.exit(1); 160 | }; 161 | 162 | 163 | // Stylize a string 164 | function stylize(str, style) { 165 | var styles = { 166 | 'bold' : [1, 22], 167 | 'italic' : [3, 23], 168 | 'underline' : [4, 24], 169 | 'cyan' : [96, 39], 170 | 'blue' : [34, 39], 171 | 'yellow' : [33, 39], 172 | 'green' : [32, 39], 173 | 'red' : [31, 39], 174 | 'grey' : [90, 39], 175 | 'green-hi' : [92, 32], 176 | }; 177 | return '\033[' + styles[style][0] + 'm' + str + 178 | '\033[' + styles[style][1] + 'm'; 179 | }; 180 | 181 | function $(str) { 182 | str = new(String)(str); 183 | ['bold', 'grey', 'yellow', 'red', 'green', 'cyan', 'blue', 'italic', 'underline'].forEach(function (style) { 184 | Object.defineProperty(str, style, { 185 | get: function () { 186 | return $(stylize(this, style)); 187 | } 188 | }); 189 | }); 190 | return str; 191 | }; 192 | 193 | stylize.$ = $; 194 | -------------------------------------------------------------------------------- /lib/master/webclient.js: -------------------------------------------------------------------------------- 1 | var __ = require('underscore'); 2 | 3 | // WebClient is an end-user using a browser 4 | var WebClient = function(socket, server) { 5 | this.log_server = server; 6 | this.socket = socket; 7 | this.id = socket.id; 8 | var wc = this; 9 | 10 | // Join web_clients room 11 | socket.join('web_clients'); 12 | 13 | // Remove WebClient 14 | socket.on('disconnect', function() { 15 | __(wc.watching_logs).each(function(log_file) { 16 | log_file.remove_web_client(wc); 17 | }); 18 | socket.leave('web_clients'); 19 | }); 20 | }; 21 | 22 | WebClient.prototype = { 23 | 24 | // Tell WebClient to add new Node 25 | add_node: function(node) { 26 | this.socket.emit('add_node', { 27 | nodeId: node.nodeId, 28 | iport:node.iport 29 | }); 30 | }, 31 | 32 | // Tell WebClient to remove Node 33 | remove_node: function(node) { 34 | this.socket.emit('remove_node', { 35 | node: node.nodeId 36 | }); 37 | }, 38 | error_node: function(node,error) { 39 | this.socket.emit('error', { 40 | node: node.iport, 41 | error:error 42 | }); 43 | } 44 | 45 | }; 46 | 47 | module.exports = { 48 | WebClient: WebClient 49 | }; 50 | -------------------------------------------------------------------------------- /lib/monitor/monitor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * agent monitor data map 4 | * 5 | * every agent put start and end time in to route map 6 | * then report to master 7 | * 8 | */ 9 | var fs = require('fs'); 10 | var util = require('../common/util'); 11 | 12 | var monitor = module.exports; 13 | var dataMap = {}; 14 | var incrMap = {}; 15 | var profData = {}; 16 | 17 | monitor.getData = function(){ 18 | return { 19 | timeData:profData, 20 | incrData:incrMap 21 | }; 22 | }; 23 | 24 | monitor.clear = function(){ 25 | profData = {}; 26 | incrMap = {}; 27 | }; 28 | 29 | monitor.incr = function(name){ 30 | incrMap[name] = incrMap[name]==null?1:incrMap[name]+1; 31 | console.log(incrMap[name] + ' ' + name); 32 | } 33 | 34 | monitor.decr = function(name){ 35 | incrMap[name] = incrMap[name]==null?0:incrMap[name]-1; 36 | } 37 | 38 | monitor.beginTime = function(route,uid,id){ 39 | var time = Date.now(); 40 | if(!dataMap[route]) { 41 | dataMap[route] = buildMapData(); 42 | } 43 | if(!dataMap[route][uid]){ 44 | dataMap[route][uid] = buildMapData(); 45 | dataMap[route][uid][id] = time; 46 | } 47 | dataMap[route][uid][id] = time; 48 | }; 49 | 50 | monitor.endTime = function(route,uid,id){ 51 | if(!dataMap[route]){ 52 | return; 53 | } 54 | if(!dataMap[route][uid]){ 55 | return; 56 | } 57 | if(!dataMap[route][uid][id]){ 58 | return; 59 | } 60 | var beginTime = dataMap[route][uid][id]; 61 | delete dataMap[route][uid][id]; 62 | var span = Date.now()-beginTime; 63 | //console.log('route span ' + route+ ' ' + uid + ' ' + span); 64 | //saveTimes(uid,route+":"+span+'\r\n'); 65 | var srcData = profData[route]; 66 | if (!srcData) { 67 | srcData = {min:span,max:span,avg:span,num:1}; 68 | profData[route] = srcData; 69 | } else { 70 | if (spansrcData.max){ 74 | srcData.max = span; 75 | } 76 | srcData.avg = (srcData.avg*srcData.num+span)/(srcData.num+1); 77 | srcData.num = (srcData.num+1); 78 | } 79 | }; 80 | 81 | function buildMapData(){ 82 | var data = {}; 83 | return data; 84 | } 85 | 86 | var saveTimes = function(uid,value) { 87 | fs.appendFile(util.getPath()+'/detail', value, function(err) { 88 | if(err) { 89 | console.log(err); 90 | } 91 | }) 92 | } 93 | 94 | -------------------------------------------------------------------------------- /lib/monitor/stat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * stat receive agent client monitor data 3 | * merger vaild data that has response 4 | * when server restart, it will clear 5 | * 6 | * 7 | */ 8 | var _ = require('underscore'); 9 | var stat = module.exports; 10 | var _timeDataMap = {}; 11 | var _countDataMap = {}; 12 | 13 | var incrData = {}; 14 | 15 | stat.getTimeData = function(){ 16 | return _timeDataMap; 17 | }; 18 | 19 | stat.getCountData = function(){ 20 | return _countDataMap; 21 | }; 22 | 23 | /** 24 | * clear data 25 | */ 26 | stat.clear = function(agent){ 27 | if (!!agent) { 28 | delete _timeDataMap[agent]; 29 | delete _countDataMap[agent]; 30 | } else { 31 | _timeDataMap = {}; 32 | _countDataMap = {}; 33 | } 34 | }; 35 | 36 | 37 | stat.merge = function(agent,message){ 38 | _timeDataMap[agent]= message.timeData; 39 | _countDataMap[agent] = message.incrData; 40 | }; 41 | -------------------------------------------------------------------------------- /lib/robot.js: -------------------------------------------------------------------------------- 1 | var Agent = require('./agent/agent').Agent; 2 | var Server = require('./master/server').Server; 3 | var HTTP_SERVER = require('./console/http').HTTP_SERVER; 4 | var util = require('./common/util').createPath(); 5 | /** 6 | * export to developer prototype 7 | * 8 | * @param {Object} config 9 | * include deal with master and agent mode 10 | * 11 | * param include mode 12 | * 13 | */ 14 | var Robot = function(conf){ 15 | this.conf = conf; 16 | this.master = null; 17 | this.agent = null; 18 | }; 19 | 20 | 21 | /* 22 | * run master server 23 | * 24 | * @param {String} start up file 25 | * 26 | */ 27 | Robot.prototype.runMaster = function(mainFile) { 28 | var conf = {},master; 29 | conf.clients = this.conf.clients; 30 | conf.mainFile = mainFile; 31 | this.master = new Server(conf); 32 | this.master.listen(this.conf.master.port); 33 | HTTP_SERVER.start(this.conf.master.webport); 34 | }; 35 | 36 | /** 37 | * run agent client 38 | * 39 | * @param {String} script 40 | * 41 | */ 42 | Robot.prototype.runAgent = function(scriptFile) { 43 | var conf = {}; 44 | conf.master = this.conf.master; 45 | conf.apps = this.conf.apps; 46 | conf.scriptFile = scriptFile; 47 | this.agent = new Agent(conf); 48 | this.agent.start(); 49 | }; 50 | 51 | Robot.prototype.restart = function(){ 52 | if (this.agent!= null){ 53 | this.agent.reconnect(true); 54 | } 55 | } 56 | 57 | exports.Robot = Robot; 58 | 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pomelo-robot", 3 | "version": "0.1.4", 4 | "private": false, 5 | "dependencies": { 6 | "socket.io": "0.9.13", 7 | "socket.io-client": "0.9.11", 8 | "underscore": "1.4.4" 9 | }, 10 | "readmeFilename": "README.md", 11 | "gitHead": "d33db82d670b7a5d21c95e15f91548f8a8ba7821", 12 | "description": "pomelo-robot is a simple tool to benchmark the socket.io server's performance.", 13 | "main": "index.js", 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/NetEase/pomelo-robot.git" 17 | }, 18 | "keywords": [ 19 | "pomelo", 20 | "client", 21 | "robot" 22 | ], 23 | "author": "palmtoy", 24 | "license": "MIT" 25 | } 26 | --------------------------------------------------------------------------------