<commit_before>/**
 * Some basic commands for bot
 */

'use strict';

module.exports.init = function () {
	var dispatcher = this.dispatcher;
	var irc = this.require('irc');
	var controls = this.require('controls');
	var module = this;

	//get moduleManager and bot and save it for the commands
	this.dispatcher.on('init', function (bot) {
		module.mm = bot.modules;
		module.bot = bot;
	});

	this.addAction('quit', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}

		source.respond('okey, ' + source.nick + '! Goodbye everypony!');
		irc.quit(args[1]);
	}, /^quit[ ]?(.*)$/i, ['owner']);

	this.addAction('part', function (source, args) {
		//dispatcher.on('irc/PART', ok);
		//is not needed as nothing can stop us from parting

		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}

		var chans = args[1].match(/#[\w\-\_]+/gi);
		if (chans !== null) {
			source.respond('okey, ' + source.nick + '! I didnt like the \'' + chans.join('\', \'') + '\' either.');
			irc.part.apply(irc, chans);
		} else if (source.channel) {
			source.respond('okey, ' + source.nick + '! I\'m going to look for ponies elsewhere.');
			irc.part(source.channel);
		} else {
			source.mention('please specify a channel.');
		}
	}, /^part([ ]+(.*)|)$/i, ['owner', 'operators']);

	this.addAction('join', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}
		var chans = args[1].match(/#[\w\-\_]+/gi);
		if (chans !== null) {
			source.respond('okey, ' + source.nick + '! Lemme see what goes in \'' + chans.join('\', \'') + '\'.');

			var fail = function (s, args) {
				source.mention('i cannot go to \'' + args[1] + '\'! Because ' + args[2]);
				clean();
			};
			var ok = function (s, args) {
				if (s.nick === irc.currentNick) {
					source.respond('*ding*, i am now in \'' + args[0] + '\' too!');
					clean();
				}
			};

			var timer;
			var clean = function () {
				dispatcher.off('irc/405', fail);
				dispatcher.off('irc/471', fail);
				dispatcher.off('irc/473', fail);
				dispatcher.off('irc/474', fail);
				dispatcher.off('irc/475', fail);
				dispatcher.off('irc/JOIN', ok);
				clearTimeout(timer);
			};
			timer = setTimeout(clean, 5000);

			irc.join.apply(irc, chans);

			dispatcher.on('irc/405', fail);
			dispatcher.on('irc/471', fail);
			dispatcher.on('irc/473', fail);
			dispatcher.on('irc/474', fail);
			dispatcher.on('irc/475', fail);
			dispatcher.on('irc/JOIN', ok);
		} else {
			source.mention('please specify a channel.');
		}
	}, /^join([ ]+(.*)|)$/i, ['owner', 'operators']);

	this.addAction('nick', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}
		var nick = args[2];
		if (nick) {
			source.respond('okey, ' + source.nick + '! Lemmy try this new name.');

			var fail = function () {
				source.mention('i failed in renaming myself!');
				clean();
			};
			var inuse = function () {
				source.mention('i failed, somepony uses that name already!');
				clean();
			};
			var toofast = function () {
				source.mention('i failed, they told me i need to wait!');
				clean();
			};
			var ok = function (s) {
				if (s.nick === irc.lastNick) {
					source.mention('success! Do u like my new name?');
					clean();
				}
			};

			var timer;
			var clean = function () {
				dispatcher.off('irc/430', fail);
				dispatcher.off('irc/431', fail);
				dispatcher.off('irc/432', fail);
				dispatcher.off('irc/433', inuse);
				dispatcher.off('irc/438', toofast);
				dispatcher.off('irc/NICK', ok);
				clearTimeout(timer);
			};
			timer = setTimeout(clean, 5000);

			irc.nick(nick);

			dispatcher.on('irc/430', fail);
			dispatcher.on('irc/431', fail);
			dispatcher.on('irc/432', fail);
			dispatcher.on('irc/433', inuse);
			dispatcher.on('irc/438', toofast);
			dispatcher.on('irc/NICK', ok);
		} else {
			source.respond('Hello, my name is ' + irc.currentNick);
		}
	}, /^nick([ ]+(.*)|)$/i, ['owner', 'operators']);

	function actionList(source) {
		var actions = [];
		Object.keys(controls.actions).forEach(function (n) {
			if (this.controls.checkAccess(source, this.actions[n])) {
				actions.push(n);
			}
		}, controls);

		source.mention('check private message from me for available actions.');
		source.message('actions that can be triggered by you are: ' + actions.join(', '));
	}

	function commandList(source, args) {
		if (args[0] === 'actions' ||  args[1] === ' actions') {
			actionList(source);
			return;
		}

		var commands = [];
		Object.keys(controls.commands).forEach(function (n) {
			if (this.controls.checkAccess(source, this.commands[n])) {
				commands.push(n);
			}
		}, controls);

		source.mention('the available commands are: ' + controls.commandDelimiter + commands.join(', ' + controls.commandDelimiter));
	}

	this.addCommand('help', commandList).addAction('help', commandList, /^help( actions)?$/);

	this.addAction('config save', function (source) {
		if (module.bot._configFile) {
			module.bot.saveConfig(module.bot._configFile);
			if (source.toString() !== 'ItzAInternallPonyShell') {
				source.respond('Config saved!');
			}
		} else {
			source.respond('Config cannot be saved!');
		}
	}, /^config save$/i);

	this.addAction('config load', function (source) {
		if (module.bot._configFile) {
			module.bot.loadConfig(module.bot._configFile);
			if (source.toString() !== 'ItzAInternallPonyShell') {
				source.respond('Config (re)loaded!');
			}
		} else {
			source.respond('Config cannot be loaded!');
		}
	}, /^config load$/i);

	this.addAction('lsmod', function (source) {
		source.mention('i have these modules active: ' + module.mm.getModules().join(', '));
	}, /^lsmod|modules$/i, ['owner']);

	this.addAction('reload', function (source, args) {
		var modules = args[1].match(/\w+/gi);
		source.respond('okey, ' + source.nick + '! Gimmie a sec to tune my element to it.');
		modules.forEach(function (name) {
			try {
				module.mm.reload(name);
				source.respond('module \'' + name + '\' reloaded!');
			} catch (e) {
				source.respond('module \'' + name + '\' failed to reload! ' + e);
			}
		});
	}, /^reload[ ]+(.*)$/i, ['owner']);

	this.addAction('load', function (source, args) {
		var modules = args[1].match(/\w+/gi);
		source.respond('okey, ' + source.nick + '! I\'m really eager to learn new things!');
		modules.forEach(function (name) {
			try {
				module.mm.load(name);
				source.respond('module \'' + name + '\' loaded!');
			} catch (e) {
				source.respond('module \'' + name + '\' failed to load! ' + e);
			}
		});
	}, /^load[ ]+(.*)$/i, ['owner']);

	this.addAction('unload', function (source, args) {
		var modules = args[1].match(/\w+/gi);
		source.respond('okey, ' + source.nick + '! Let\'s throw it away!');
		modules.forEach(function (name) {
			try {
				module.mm.unload(name);
				source.respond('module \'' + name + '\' unloaded!');
			} catch (e) {
				source.respond('module \'' + name + '\' failed to unload! ' + e);
			}
		});
	}, /^unload[ ]+(.*)$/i, ['owner']);

	this.addAction('say', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}
		var target = args[2];
		var msg = args[3];
		if (msg.trim() === '') {
			source.respond('you probadly should tell me what i should tell to \'' + target + '\'');
		} else {
			var resendreply = function (s, text) {
				if (target === s.nick) {
					source.respond(target + ' REPLY: ' + text);
					clean();
				}
			};
			var wrongnick = function (s, argv) {
				source.respond('Nick or channel with name \'' + argv[1] + '\' was not found.');
				clean();
			};
			var noExternal = function (s, argv) {
				source.respond('Channel \'' + argv[1] + '\' has blocked external messages.');
				clean();
			};

			var timer;
			var clean = function () {
				dispatcher.off('irc/PRIVMSG', resendreply);
				dispatcher.off('irc/NOTICE', resendreply);
				dispatcher.off('irc/401', wrongnick);
				dispatcher.off('irc/404', noExternal);
				clearTimeout(timer);
			};
			timer = setTimeout(clean, 5000);

			//say it
			var ret;
			if (args[1] === 'note') {
				ret = irc.notice(target, msg);
			} else if (args[1] === 'me') {
				ret = irc.action(target, msg);
			} else if (args[1] === 'tell') {
				ret = irc.privMsg(target, 'hey ' + target + ', ' + msg);
			} else {
				ret = irc.privMsg(target, msg);
			}

			if (ret) {
				dispatcher.on('irc/PRIVMSG', resendreply);
				dispatcher.on('irc/NOTICE', resendreply);
				dispatcher.on('irc/401', wrongnick);
				dispatcher.on('irc/404', noExternal);
			} else {
				source.mention('wait a little while...');
			}
		}
	}, /^(say|tell|note|me)[ ]+(#?[\w\_\-\\\[\]\{\}\^\`\|]+)[ ]*(.*)$/i, ['owner', 'operators']);

	//pass the command
	this.addAction('command', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}

		if (args[1]) {
			var wrongcommand = function (s, args) {
				if (args[0] === irc.currentNick) {
					source.mention('my pony powers tells me that command \'' + args[1] + '\' is unknown!');
					clean();
				}
			};

			var timer;
			var clean = function () {
				dispatcher.off('irc/421', wrongcommand);
				clearTimeout(timer);
			};
			timer = setTimeout(clean, 5000);

			irc.irc.send(args[1]);

			dispatcher.on('irc/421', wrongcommand);

			source.mention('okey');
		} else {
			source.mention('tell me what');
		}
	}, /^command[ ]+(.*)/i, ['owner']);

	this.addAction('update', function (source) {
		source.action('is fetching updates...');

		require('child_process').exec('cd ' + BOT_DIR + ' && git pull', function (error, stdout) {
			if (error) {
				source.respond('Update failed!');
				return;
			}

			var stdouts = stdout.replace(/\n$/, '').split('\n');
			var message = stdouts.shift();
			var updatedModules = [];
			var updatedCore = false;
			var npm = false;
			var config = false;
			var uptodate = false;

			if (message) {
				if (message.match(/up\-to\-date/)) {
					uptodate = true;
				}
				source.respond(message);
			} else {
				source.respond('Update failed!');
				return;
			}

			if (stdouts.length > 0) {
				stdouts.forEach(function (value) {
					var tmp = value.match(/modules\/(.+)\.js/);
					if (tmp) {
						updatedModules.push(tmp[1]);
					} else {
						tmp = value.match(/app\.js|libs\//);
						if (tmp) {
							updatedCore = true;
						} else {
							tmp = value.match(/package\.json/);
							if (tmp) {
								npm = true;
							} else {
								tmp = value.match(/example-config\.json/);
								if (tmp) {
									config = true;
								}
							}
						}
					}
				});
			}

			if (updatedCore) {
				source.respond('CORE was updated, please restart the bot! ... Eh, i mean\'t: release the pony!');
			}

			if (npm) {
				source.respond('package.json was updated, please use \'npm install\'');
			}

			if (config) {
				source.respond('example-config.json was updated, please use check your configuration');
			}

			updatedModules = require(LIBS_DIR + '/helpers').unique(updatedModules);
			if (updatedModules.length > 0) {
				source.respond('Don\'t forget to reload updated modules: ' + updatedModules.join(', '));
			}

			if (!updatedCore && updatedModules.length <= 0 && !uptodate) {
				source.respond('Nothing important has been updated.');
			}
		});
	}, /^update$/i, ['owner']);

	this.addAction('memory', function (source) {
		var helpers = require(LIBS_DIR + '/helpers');
		source.mention('i currently pony with ' + helpers.formatSizeUnits(process.memoryUsage().rss) + ' for ' + helpers.formatTime(process.uptime()));
	}, /^mem(ory)?|uptime$/i, ['owner']);
};<commit_msg>add stats<commit_after>/**
 * Some basic commands for bot
 */

'use strict';

module.exports.init = function () {
	var dispatcher = this.dispatcher;
	var irc = this.require('irc');
	var controls = this.require('controls');
	var module = this;

	//get moduleManager and bot and save it for the commands
	this.dispatcher.on('init', function (bot) {
		module.mm = bot.modules;
		module.bot = bot;
	});

	this.addAction('quit', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}

		source.respond('okey, ' + source.nick + '! Goodbye everypony!');
		irc.quit(args[1]);
	}, /^quit[ ]?(.*)$/i, ['owner']);

	this.addAction('part', function (source, args) {
		//dispatcher.on('irc/PART', ok);
		//is not needed as nothing can stop us from parting

		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}

		var chans = args[1].match(/#[\w\-\_]+/gi);
		if (chans !== null) {
			source.respond('okey, ' + source.nick + '! I didnt like the \'' + chans.join('\', \'') + '\' either.');
			irc.part.apply(irc, chans);
		} else if (source.channel) {
			source.respond('okey, ' + source.nick + '! I\'m going to look for ponies elsewhere.');
			irc.part(source.channel);
		} else {
			source.mention('please specify a channel.');
		}
	}, /^part([ ]+(.*)|)$/i, ['owner', 'operators']);

	this.addAction('join', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}
		var chans = args[1].match(/#[\w\-\_]+/gi);
		if (chans !== null) {
			source.respond('okey, ' + source.nick + '! Lemme see what goes in \'' + chans.join('\', \'') + '\'.');

			var fail = function (s, args) {
				source.mention('i cannot go to \'' + args[1] + '\'! Because ' + args[2]);
				clean();
			};
			var ok = function (s, args) {
				if (s.nick === irc.currentNick) {
					source.respond('*ding*, i am now in \'' + args[0] + '\' too!');
					clean();
				}
			};

			var timer;
			var clean = function () {
				dispatcher.off('irc/405', fail);
				dispatcher.off('irc/471', fail);
				dispatcher.off('irc/473', fail);
				dispatcher.off('irc/474', fail);
				dispatcher.off('irc/475', fail);
				dispatcher.off('irc/JOIN', ok);
				clearTimeout(timer);
			};
			timer = setTimeout(clean, 5000);

			irc.join.apply(irc, chans);

			dispatcher.on('irc/405', fail);
			dispatcher.on('irc/471', fail);
			dispatcher.on('irc/473', fail);
			dispatcher.on('irc/474', fail);
			dispatcher.on('irc/475', fail);
			dispatcher.on('irc/JOIN', ok);
		} else {
			source.mention('please specify a channel.');
		}
	}, /^join([ ]+(.*)|)$/i, ['owner', 'operators']);

	this.addAction('nick', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}
		var nick = args[2];
		if (nick) {
			source.respond('okey, ' + source.nick + '! Lemmy try this new name.');

			var fail = function () {
				source.mention('i failed in renaming myself!');
				clean();
			};
			var inuse = function () {
				source.mention('i failed, somepony uses that name already!');
				clean();
			};
			var toofast = function () {
				source.mention('i failed, they told me i need to wait!');
				clean();
			};
			var ok = function (s) {
				if (s.nick === irc.lastNick) {
					source.mention('success! Do u like my new name?');
					clean();
				}
			};

			var timer;
			var clean = function () {
				dispatcher.off('irc/430', fail);
				dispatcher.off('irc/431', fail);
				dispatcher.off('irc/432', fail);
				dispatcher.off('irc/433', inuse);
				dispatcher.off('irc/438', toofast);
				dispatcher.off('irc/NICK', ok);
				clearTimeout(timer);
			};
			timer = setTimeout(clean, 5000);

			irc.nick(nick);

			dispatcher.on('irc/430', fail);
			dispatcher.on('irc/431', fail);
			dispatcher.on('irc/432', fail);
			dispatcher.on('irc/433', inuse);
			dispatcher.on('irc/438', toofast);
			dispatcher.on('irc/NICK', ok);
		} else {
			source.respond('Hello, my name is ' + irc.currentNick);
		}
	}, /^nick([ ]+(.*)|)$/i, ['owner', 'operators']);

	function actionList(source) {
		var actions = [];
		Object.keys(controls.actions).forEach(function (n) {
			if (this.controls.checkAccess(source, this.actions[n])) {
				actions.push(n);
			}
		}, controls);

		source.mention('check private message from me for available actions.');
		source.message('actions that can be triggered by you are: ' + actions.join(', '));
	}

	function commandList(source, args) {
		if (args[0] === 'actions' ||  args[1] === ' actions') {
			actionList(source);
			return;
		}

		var commands = [];
		Object.keys(controls.commands).forEach(function (n) {
			if (this.controls.checkAccess(source, this.commands[n])) {
				commands.push(n);
			}
		}, controls);

		source.mention('the available commands are: ' + controls.commandDelimiter + commands.join(', ' + controls.commandDelimiter));
	}

	this.addCommand('help', commandList).addAction('help', commandList, /^help( actions)?$/);

	this.addAction('config save', function (source) {
		if (module.bot._configFile) {
			module.bot.saveConfig(module.bot._configFile);
			if (source.toString() !== 'ItzAInternallPonyShell') {
				source.respond('Config saved!');
			}
		} else {
			source.respond('Config cannot be saved!');
		}
	}, /^config save$/i);

	this.addAction('config load', function (source) {
		if (module.bot._configFile) {
			module.bot.loadConfig(module.bot._configFile);
			if (source.toString() !== 'ItzAInternallPonyShell') {
				source.respond('Config (re)loaded!');
			}
		} else {
			source.respond('Config cannot be loaded!');
		}
	}, /^config load$/i);

	this.addAction('lsmod', function (source) {
		source.mention('i have these modules active: ' + module.mm.getModules().join(', '));
	}, /^lsmod|modules$/i, ['owner']);

	this.addAction('reload', function (source, args) {
		var modules = args[1].match(/\w+/gi);
		source.respond('okey, ' + source.nick + '! Gimmie a sec to tune my element to it.');
		modules.forEach(function (name) {
			try {
				module.mm.reload(name);
				source.respond('module \'' + name + '\' reloaded!');
			} catch (e) {
				source.respond('module \'' + name + '\' failed to reload! ' + e);
			}
		});
	}, /^reload[ ]+(.*)$/i, ['owner']);

	this.addAction('load', function (source, args) {
		var modules = args[1].match(/\w+/gi);
		source.respond('okey, ' + source.nick + '! I\'m really eager to learn new things!');
		modules.forEach(function (name) {
			try {
				module.mm.load(name);
				source.respond('module \'' + name + '\' loaded!');
			} catch (e) {
				source.respond('module \'' + name + '\' failed to load! ' + e);
			}
		});
	}, /^load[ ]+(.*)$/i, ['owner']);

	this.addAction('unload', function (source, args) {
		var modules = args[1].match(/\w+/gi);
		source.respond('okey, ' + source.nick + '! Let\'s throw it away!');
		modules.forEach(function (name) {
			try {
				module.mm.unload(name);
				source.respond('module \'' + name + '\' unloaded!');
			} catch (e) {
				source.respond('module \'' + name + '\' failed to unload! ' + e);
			}
		});
	}, /^unload[ ]+(.*)$/i, ['owner']);

	this.addAction('say', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}
		var target = args[2];
		var msg = args[3];
		if (msg.trim() === '') {
			source.respond('you probadly should tell me what i should tell to \'' + target + '\'');
		} else {
			var resendreply = function (s, text) {
				if (target === s.nick) {
					source.respond(target + ' REPLY: ' + text);
					clean();
				}
			};
			var wrongnick = function (s, argv) {
				source.respond('Nick or channel with name \'' + argv[1] + '\' was not found.');
				clean();
			};
			var noExternal = function (s, argv) {
				source.respond('Channel \'' + argv[1] + '\' has blocked external messages.');
				clean();
			};

			var timer;
			var clean = function () {
				dispatcher.off('irc/PRIVMSG', resendreply);
				dispatcher.off('irc/NOTICE', resendreply);
				dispatcher.off('irc/401', wrongnick);
				dispatcher.off('irc/404', noExternal);
				clearTimeout(timer);
			};
			timer = setTimeout(clean, 5000);

			//say it
			var ret;
			if (args[1] === 'note') {
				ret = irc.notice(target, msg);
			} else if (args[1] === 'me') {
				ret = irc.action(target, msg);
			} else if (args[1] === 'tell') {
				ret = irc.privMsg(target, 'hey ' + target + ', ' + msg);
			} else {
				ret = irc.privMsg(target, msg);
			}

			if (ret) {
				dispatcher.on('irc/PRIVMSG', resendreply);
				dispatcher.on('irc/NOTICE', resendreply);
				dispatcher.on('irc/401', wrongnick);
				dispatcher.on('irc/404', noExternal);
			} else {
				source.mention('wait a little while...');
			}
		}
	}, /^(say|tell|note|me)[ ]+(#?[\w\_\-\\\[\]\{\}\^\`\|]+)[ ]*(.*)$/i, ['owner', 'operators']);

	//pass the command
	this.addAction('command', function (source, args) {
		if (!irc.connected) {
			source.respond('I\'m not connected to server!');
			return;
		}

		if (args[1]) {
			var wrongcommand = function (s, args) {
				if (args[0] === irc.currentNick) {
					source.mention('my pony powers tells me that command \'' + args[1] + '\' is unknown!');
					clean();
				}
			};

			var timer;
			var clean = function () {
				dispatcher.off('irc/421', wrongcommand);
				clearTimeout(timer);
			};
			timer = setTimeout(clean, 5000);

			irc.irc.send(args[1]);

			dispatcher.on('irc/421', wrongcommand);

			source.mention('okey');
		} else {
			source.mention('tell me what');
		}
	}, /^command[ ]+(.*)/i, ['owner']);

	this.addAction('update', function (source) {
		source.action('is fetching updates...');

		require('child_process').exec('cd ' + BOT_DIR + ' && git pull', function (error, stdout) {
			if (error) {
				source.respond('Update failed!');
				return;
			}

			var stdouts = stdout.replace(/\n$/, '').split('\n');
			var message = stdouts.shift();
			var updatedModules = [];
			var updatedCore = false;
			var npm = false;
			var config = false;
			var uptodate = false;

			if (message) {
				if (message.match(/up\-to\-date/)) {
					uptodate = true;
				}
				source.respond(message);
			} else {
				source.respond('Update failed!');
				return;
			}

			if (stdouts.length > 0) {
				stdouts.forEach(function (value) {
					var tmp = value.match(/modules\/(.+)\.js/);
					if (tmp) {
						updatedModules.push(tmp[1]);
					} else {
						tmp = value.match(/app\.js|libs\//);
						if (tmp) {
							updatedCore = true;
						} else {
							tmp = value.match(/package\.json/);
							if (tmp) {
								npm = true;
							} else {
								tmp = value.match(/example-config\.json/);
								if (tmp) {
									config = true;
								}
							}
						}
					}
				});
			}

			if (updatedCore) {
				source.respond('CORE was updated, please restart the bot! ... Eh, i mean\'t: release the pony!');
			}

			if (npm) {
				source.respond('package.json was updated, please use \'npm install\'');
			}

			if (config) {
				source.respond('example-config.json was updated, please use check your configuration');
			}

			updatedModules = require(LIBS_DIR + '/helpers').unique(updatedModules);
			if (updatedModules.length > 0) {
				source.respond('Don\'t forget to reload updated modules: ' + updatedModules.join(', '));
			}

			if (!updatedCore && updatedModules.length <= 0 && !uptodate) {
				source.respond('Nothing important has been updated.');
			}
		});
	}, /^update$/i, ['owner']);

	this.addAction('stats', function (source, args) {
		var helpers = require(LIBS_DIR + '/helpers');

		if (args[0] === 'uptime') {
			source.mention('i\'m running already ' + helpers.formatTime(process.uptime()));
		} else if (args[0] === 'mem' || args[0] === 'memory') {
			source.mention('i currently pony with ' + helpers.formatSizeUnits(process.memoryUsage().rss));
		} else if (args[0] === 'connected') {
			if (irc.connected) {
				source.mention('i connected to server \'' + irc.server + '\' at ' + helpers.formatDate(new Date(irc.connectedOn * 1000), module.config.dateFormat));
			} else {
				source.mention('i\'m not currently connected to server');
			}
		} else {
			source.mention('i currently pony with ' + helpers.formatSizeUnits(process.memoryUsage().rss) + ' for ' + helpers.formatTime(process.uptime()) + (irc.connected ? ' and i connected to server \'' + irc.server + '\' at ' + helpers.formatDate(new Date(irc.connectedOn * 1000), module.config.dateFormat) : ' and i am not connected to server'));
		}
	}, /^stats|mem(ory)?|uptime|connected$/i, ['owner']);
};<|endoftext|>


<commit_before>goog.provide('ol.renderer.TileLayerRenderer');

goog.require('ol.renderer.LayerRenderer');
goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.Composite');
goog.require('ol.TileSet');
goog.require('ol.Bounds');

goog.require('goog.style');

/**
 * A single layer renderer that will be created by the composite map renderer.
 *
 * @constructor
 * @param {!Element} container
 * @param {!ol.layer.Layer} layer
 * @extends {ol.renderer.LayerRenderer}
 */
ol.renderer.TileLayerRenderer = function(container, layer) {

    goog.base(this, container, layer);

    /**
     * @type {ol.Bounds}
     * @private
     */
    this.rendererdBounds_ = null;

    /**
     * @type {Array.<number>}
     */
    this.layerResolutions_ = layer.getResolutions();

    /**
     * @type {Array.<number>}
     */
    this.tileOrigin_ = layer.getTileOrigin();

    /**
     * @type {Array.<number>}
     */
    this.tileSize_ = layer.getTileSize();

    /**
     * @type {number}
     */
    this.xRight_ = layer.getXRight() ? 1 : -1;

    /**
     * @type {number}
     */
    this.yDown_ = layer.getYDown() ? 1 : -1;

    /**
     * @type {number|undefined}
     * @private
     */
    this.renderedResolution_ = undefined;

    /**
     * @type {goog.math.Size}
     * @private
     */
    this.containerSize_ = null;

};

goog.inherits(ol.renderer.TileLayerRenderer, ol.renderer.LayerRenderer);

/**
 * @param {number} resolution
 * @return {Array.<number>}
 */
ol.renderer.TileLayerRenderer.prototype.getPreferredResAndZ_ = function(resolution) {
    var minDiff = Number.POSITIVE_INFINITY;
    var candidate, diff, z, r;
    for (var i=0, ii=this.layerResolutions_.length; i<ii; ++i) {
        // assumes sorted resolutions
        candidate = this.layerResolutions_[i];
        diff = Math.abs(resolution - candidate);
        if (diff < minDiff) {
            z = i;
            r = candidate;
            minDiff = diff;
        } else {
            break;
        }
    }
    return [r, z];
};

/**
 * @return {goog.math.Size}
 */
ol.renderer.TileLayerRenderer.prototype.getContainerSize_ = function() {
    // TODO: listen for resize and set this.constainerSize_ null
    // https://github.com/openlayers/ol3/issues/2
    if (goog.isNull(this.containerSize_)) {
        this.containerSize_ = goog.style.getSize(this.container_);
    }
    return this.containerSize_;
};

/**
 * Tiles rendered at the current resolution;
 * @type {Object}
 */
ol.renderer.TileLayerRenderer.prototype.renderedTiles_ = {};

/**
 * Render the layer.
 *
 * @param {!ol.Loc} center
 * @param {number} resolution
 */
ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) {
    if (resolution !== this.renderedResolution_) {
        this.changeResolution_(center, resolution);
    }
    this.renderedResolution_ = resolution;
    var pair = this.getPreferredResAndZ_(resolution);
    var tileResolution = pair[0];
    var tileZ = pair[1];
    var scale = resolution / tileResolution;

    var pxMapSize = this.getContainerSize_();
    var xRight = this.xRight_;
    var yDown = this.yDown_;

    var halfMapWidth = (resolution * pxMapSize.width) / 2;
    var halfMapHeight = (resolution * pxMapSize.height) / 2;
    var centerX = center.getX();
    var centerY = center.getY();
    var mapMinX = centerX - halfMapWidth;
    var mapMaxY = centerY + halfMapHeight;
    var pxOffsetX = (mapMinX - this.tileOrigin_[0]) / resolution;
    var pxOffsetY = (this.tileOrigin_[1] - mapMaxY) / resolution;

    // this gives us the desired size in fractional pixels
    var pxTileWidth = this.tileSize_[0] / scale;
    var pxTileHeight = this.tileSize_[1] / scale;

    // this is the index of the top left tile
    var leftTileX = Math.floor(xRight * pxOffsetX / pxTileWidth);
    var topTileY = Math.floor(yDown * pxOffsetY / pxTileHeight);

    var pxMinX;
    if (xRight > 0) {
        pxMinX = Math.round(leftTileX * pxTileWidth) - pxOffsetX;
    } else {
        pxMinX = Math.round((-leftTileX-1) * pxTileWidth) - pxOffsetX;
    }
    var pxMinY;
    if (yDown > 0) {
        pxMinY = Math.round(topTileY * pxTileHeight) - pxOffsetY;
    } else {
        pxMinY = Math.round((-topTileY-1) * pxTileHeight) - pxOffsetY;
    }

    var pxTileLeft = pxMinX;
    var pxTileTop = pxMinY;

    var numTilesWide = Math.ceil(pxMapSize.width / pxTileWidth);
    var numTilesHigh = Math.ceil(pxMapSize.height / pxTileHeight);

    // assume a buffer of zero for now
    if (pxMinX < 0) {
        numTilesWide += 1;
    }
    if (pxMinY < 0) {
        numTilesHigh += 1;
    }

    var tileX, tileY, tile, img, pxTileRight, pxTileBottom, xyz, append;
    var fragment = document.createDocumentFragment();
    for (var i=0; i<numTilesWide; ++i) {
        pxTileTop = pxMinY;
        tileX = leftTileX + (i * xRight);
        if (scale !== 1) {
            pxTileRight = Math.round(pxMinX + ((i+1) * pxTileWidth));
        } else {
            pxTileRight = pxTileLeft + pxTileWidth;
        }
        for (var j=0; j<numTilesHigh; ++j) {
            append = false;
            tileY = topTileY + (j * yDown);
            xyz = tileX + "," + tileY + "," + tileZ;
            if (scale !== 1) {
                pxTileBottom = Math.round(pxMinY + ((j+1) * pxTileHeight));
            } else {
                pxTileBottom = pxTileTop + pxTileHeight;
            }
            tile = this.renderedTiles_[xyz];
            if (!tile) {
                tile = this.layer_.getTileForXYZ(tileX, tileY, tileZ);
                tile.load();
                this.renderedTiles_[xyz] = tile;
                img = tile.getImg();
                fragment.appendChild(img);
            } else {
                img = tile.getImg();
            }
            img.style.top = pxTileTop + "px";
            img.style.left = pxTileLeft + "px";
            if (scale !== 1) {
                img.style.height = (pxTileRight - pxTileLeft) + "px";
                img.style.width = (pxTileBottom - pxTileTop) + "px";
            }
            pxTileTop = pxTileBottom;
        }
        pxTileLeft = pxTileRight;
    }
    this.container_.appendChild(fragment);
    this.removeInvisibleTiles_(leftTileX, topTileY, tileX, tileY, tileZ);
};

/**
 * Get rid of tiles outside the newly rendered extent.
 *
 * @param {number|undefined} left The far left tile x coord.
 * @param {number|undefined} top The far top tile y coord.
 * @param {number|undefined} right The far right tile x coord.
 * @param {number|undefined} bottom The far bottom tile y coord.
 * @param {number|undefined} renderedZ The z coord for the newly rendered tiles.
 * @private
 */
ol.renderer.TileLayerRenderer.prototype.removeInvisibleTiles_ = function(left, top, right, bottom, renderedZ) {
    var index, prune, x, y, z, tile;
    var xRight = (this.xRight_ > 0);
    var yDown = (this.yDown_ > 0);
    for (var xyz in this.renderedTiles_) {
        index = xyz.split(",");
        x = +index[0];
        y = +index[1];
        z = +index[2];
        prune = renderedZ !== z ||
            // beyond on the left side
            (xRight ? x < left : x > left) ||
            // beyond on the right side
            (xRight ? x > right : x < right) ||
            // above
            (yDown ? y < top : y > top) ||
            // below
            (yDown ? y > bottom : y < bottom);
        if (prune) {
            tile = this.renderedTiles_[xyz];
            this.container_.removeChild(tile.getImg());
            delete this.renderedTiles_[xyz];
        }
    }
};

/**
 * Deal with changes in resolution.
 * TODO: implement the animation
 *
 * @param {ol.Loc} center New center.
 * @param {number} resolution New resolution.
 */
ol.renderer.TileLayerRenderer.prototype.changeResolution_ = function(center, resolution) {
    var tile;
    for (var xyz in this.renderedTiles_) {
        tile = this.renderedTiles_[xyz];
        this.container_.removeChild(tile.getImg());
        delete this.renderedTiles_[xyz];
    }
};

/**
 * Get an identifying string for this renderer.
 *
 * @returns {string}
 */
ol.renderer.TileLayerRenderer.getType = function() {
    // TODO: revisit
    return "tile";
};

/**
 * Determine if this renderer type is supported in this environment.
 *
 * @return {boolean} This renderer is supported.
 */
ol.renderer.TileLayerRenderer.isSupported = function() {
    return true;
};

/**
 * Determine if this renderer can render the given layer.
 *
 * @param {ol.layer.Layer} layer The candidate layer.
 * @return {boolean} This renderer is capable of rendering the layer.
 */
ol.renderer.TileLayerRenderer.canRender = function(layer) {
    return layer instanceof ol.layer.TileLayer;
};

ol.renderer.Composite.register(ol.renderer.TileLayerRenderer);
<commit_msg>Drag and zoom without throwing.<commit_after>goog.provide('ol.renderer.TileLayerRenderer');

goog.require('ol.renderer.LayerRenderer');
goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.Composite');
goog.require('ol.TileSet');
goog.require('ol.Bounds');

goog.require('goog.style');

/**
 * A single layer renderer that will be created by the composite map renderer.
 *
 * @constructor
 * @param {!Element} container
 * @param {!ol.layer.Layer} layer
 * @extends {ol.renderer.LayerRenderer}
 */
ol.renderer.TileLayerRenderer = function(container, layer) {

    goog.base(this, container, layer);

    /**
     * @type {ol.Bounds}
     * @private
     */
    this.rendererdBounds_ = null;

    /**
     * @type {Array.<number>}
     */
    this.layerResolutions_ = layer.getResolutions();

    /**
     * @type {Array.<number>}
     */
    this.tileOrigin_ = layer.getTileOrigin();

    /**
     * @type {Array.<number>}
     */
    this.tileSize_ = layer.getTileSize();

    /**
     * @type {number}
     */
    this.xRight_ = layer.getXRight() ? 1 : -1;

    /**
     * @type {number}
     */
    this.yDown_ = layer.getYDown() ? 1 : -1;

    /**
     * @type {number|undefined}
     * @private
     */
    this.renderedResolution_ = undefined;

    /**
     * @type {goog.math.Size}
     * @private
     */
    this.containerSize_ = null;

};

goog.inherits(ol.renderer.TileLayerRenderer, ol.renderer.LayerRenderer);

/**
 * @param {number} resolution
 * @return {Array.<number>}
 */
ol.renderer.TileLayerRenderer.prototype.getPreferredResAndZ_ = function(resolution) {
    var minDiff = Number.POSITIVE_INFINITY;
    var candidate, diff, z, r;
    for (var i=0, ii=this.layerResolutions_.length; i<ii; ++i) {
        // assumes sorted resolutions
        candidate = this.layerResolutions_[i];
        diff = Math.abs(resolution - candidate);
        if (diff < minDiff) {
            z = i;
            r = candidate;
            minDiff = diff;
        } else {
            break;
        }
    }
    return [r, z];
};

/**
 * @return {goog.math.Size}
 */
ol.renderer.TileLayerRenderer.prototype.getContainerSize_ = function() {
    // TODO: listen for resize and set this.constainerSize_ null
    // https://github.com/openlayers/ol3/issues/2
    if (goog.isNull(this.containerSize_)) {
        this.containerSize_ = goog.style.getSize(this.container_);
    }
    return this.containerSize_;
};

/**
 * Tiles rendered at the current resolution;
 * @type {Object}
 */
ol.renderer.TileLayerRenderer.prototype.renderedTiles_ = {};

/**
 * Render the layer.
 *
 * @param {!ol.Loc} center
 * @param {number} resolution
 */
ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) {
    if (resolution !== this.renderedResolution_) {
        this.changeResolution_(center, resolution);
    }
    this.renderedResolution_ = resolution;
    var pair = this.getPreferredResAndZ_(resolution);
    var tileResolution = pair[0];
    var tileZ = pair[1];
    var scale = resolution / tileResolution;

    var pxMapSize = this.getContainerSize_();
    var xRight = this.xRight_;
    var yDown = this.yDown_;

    var halfMapWidth = (resolution * pxMapSize.width) / 2;
    var halfMapHeight = (resolution * pxMapSize.height) / 2;
    var centerX = center.getX();
    var centerY = center.getY();
    var mapMinX = centerX - halfMapWidth;
    var mapMaxY = centerY + halfMapHeight;
    var pxOffsetX = (mapMinX - this.tileOrigin_[0]) / resolution;
    var pxOffsetY = (this.tileOrigin_[1] - mapMaxY) / resolution;

    // this gives us the desired size in fractional pixels
    var pxTileWidth = this.tileSize_[0] / scale;
    var pxTileHeight = this.tileSize_[1] / scale;

    // this is the index of the top left tile
    var leftTileX = Math.floor(xRight * pxOffsetX / pxTileWidth);
    var topTileY = Math.floor(yDown * pxOffsetY / pxTileHeight);

    var pxMinX;
    if (xRight > 0) {
        pxMinX = Math.round(leftTileX * pxTileWidth) - pxOffsetX;
    } else {
        pxMinX = Math.round((-leftTileX-1) * pxTileWidth) - pxOffsetX;
    }
    var pxMinY;
    if (yDown > 0) {
        pxMinY = Math.round(topTileY * pxTileHeight) - pxOffsetY;
    } else {
        pxMinY = Math.round((-topTileY-1) * pxTileHeight) - pxOffsetY;
    }

    var pxTileLeft = pxMinX;
    var pxTileTop = pxMinY;

    var numTilesWide = Math.ceil(pxMapSize.width / pxTileWidth);
    var numTilesHigh = Math.ceil(pxMapSize.height / pxTileHeight);

    // assume a buffer of zero for now
    if (pxMinX < 0) {
        numTilesWide += 1;
    }
    if (pxMinY < 0) {
        numTilesHigh += 1;
    }

    var tileX, tileY, tile, img, pxTileRight, pxTileBottom, xyz, append;
    var fragment = document.createDocumentFragment();
    for (var i=0; i<numTilesWide; ++i) {
        pxTileTop = pxMinY;
        tileX = leftTileX + (i * xRight);
        if (scale !== 1) {
            pxTileRight = Math.round(pxMinX + ((i+1) * pxTileWidth));
        } else {
            pxTileRight = pxTileLeft + pxTileWidth;
        }
        for (var j=0; j<numTilesHigh; ++j) {
            append = false;
            tileY = topTileY + (j * yDown);
            xyz = tileX + "," + tileY + "," + tileZ;
            if (scale !== 1) {
                pxTileBottom = Math.round(pxMinY + ((j+1) * pxTileHeight));
            } else {
                pxTileBottom = pxTileTop + pxTileHeight;
            }
            tile = this.renderedTiles_[xyz];
            if (!tile) {
                tile = this.layer_.getTileForXYZ(tileX, tileY, tileZ);
                if (!tile.isLoaded() && !tile.isLoading()) {
                    tile.load();
                }
                this.renderedTiles_[xyz] = tile;
                img = tile.getImg();
                fragment.appendChild(img);
            } else {
                img = tile.getImg();
            }
            img.style.top = pxTileTop + "px";
            img.style.left = pxTileLeft + "px";
            if (scale !== 1) {
                img.style.height = (pxTileRight - pxTileLeft) + "px";
                img.style.width = (pxTileBottom - pxTileTop) + "px";
            }
            pxTileTop = pxTileBottom;
        }
        pxTileLeft = pxTileRight;
    }
    this.container_.appendChild(fragment);
    this.removeInvisibleTiles_(leftTileX, topTileY, tileX, tileY, tileZ);
};

/**
 * Get rid of tiles outside the newly rendered extent.
 *
 * @param {number|undefined} left The far left tile x coord.
 * @param {number|undefined} top The far top tile y coord.
 * @param {number|undefined} right The far right tile x coord.
 * @param {number|undefined} bottom The far bottom tile y coord.
 * @param {number|undefined} renderedZ The z coord for the newly rendered tiles.
 * @private
 */
ol.renderer.TileLayerRenderer.prototype.removeInvisibleTiles_ = function(left, top, right, bottom, renderedZ) {
    var index, prune, x, y, z, tile;
    var xRight = (this.xRight_ > 0);
    var yDown = (this.yDown_ > 0);
    for (var xyz in this.renderedTiles_) {
        index = xyz.split(",");
        x = +index[0];
        y = +index[1];
        z = +index[2];
        prune = renderedZ !== z ||
            // beyond on the left side
            (xRight ? x < left : x > left) ||
            // beyond on the right side
            (xRight ? x > right : x < right) ||
            // above
            (yDown ? y < top : y > top) ||
            // below
            (yDown ? y > bottom : y < bottom);
        if (prune) {
            tile = this.renderedTiles_[xyz];
            this.container_.removeChild(tile.getImg());
            delete this.renderedTiles_[xyz];
        }
    }
};

/**
 * Deal with changes in resolution.
 * TODO: implement the animation
 *
 * @param {ol.Loc} center New center.
 * @param {number} resolution New resolution.
 */
ol.renderer.TileLayerRenderer.prototype.changeResolution_ = function(center, resolution) {
    var tile;
    for (var xyz in this.renderedTiles_) {
        tile = this.renderedTiles_[xyz];
        this.container_.removeChild(tile.getImg());
        delete this.renderedTiles_[xyz];
    }
};

/**
 * Get an identifying string for this renderer.
 *
 * @returns {string}
 */
ol.renderer.TileLayerRenderer.getType = function() {
    // TODO: revisit
    return "tile";
};

/**
 * Determine if this renderer type is supported in this environment.
 *
 * @return {boolean} This renderer is supported.
 */
ol.renderer.TileLayerRenderer.isSupported = function() {
    return true;
};

/**
 * Determine if this renderer can render the given layer.
 *
 * @param {ol.layer.Layer} layer The candidate layer.
 * @return {boolean} This renderer is capable of rendering the layer.
 */
ol.renderer.TileLayerRenderer.canRender = function(layer) {
    return layer instanceof ol.layer.TileLayer;
};

ol.renderer.Composite.register(ol.renderer.TileLayerRenderer);
<|endoftext|>


<commit_before>import React from 'react';

import SPELLS from 'common/SPELLS';
import { Doughnut as DoughnutChart } from 'react-chartjs-2';

import SpellLink from 'common/SpellLink';
import { formatPercentage, formatThousands } from 'common/format';
import Tooltip from 'common/Tooltip';

import Analyzer from 'parser/core/Analyzer';
import EssenceFontMastery from 'parser/monk/mistweaver/modules/features/EssenceFontMastery';
import EnvelopingMists from 'parser/monk/mistweaver/modules/spells/EnvelopingMists';
import SoothingMist from 'parser/monk/mistweaver/modules/spells/SoothingMist';
import RenewingMist from 'parser/monk/mistweaver/modules/spells/RenewingMist';
import Vivify from 'parser/monk/mistweaver/modules/spells/Vivify';

import StatisticsListBox, { STATISTIC_ORDER } from 'interface/others/StatisticsListBox';

const CHART_SIZE = 75;
const debug = false;

class MasteryStats extends Analyzer {
  static dependencies = {
    essenceFontMastery: EssenceFontMastery,
    envelopingMists: EnvelopingMists,
    soothingMist: SoothingMist,
    renewingMist: RenewingMist,
    vivify: Vivify,
  }

  get totalMasteryHealing() {
    return (this.vivify.gustsHealing || 0)
            + (this.renewingMist.gustsHealing || 0)
            + (this.envelopingMists.gustsHealing || 0)
            + (this.soothingMist.gustsHealing || 0)
            + (this.essenceFontMastery.healing || 0);
  }

  legend(items, total) {
    const numItems = items.length;
    return items.map(({ color, label, tooltip, value, spellId }, index) => {
      label = tooltip ? (
        <Tooltip content={tooltip}>{label}</Tooltip>
      ) : label;
      label = spellId ? (
        <SpellLink id={spellId}>{label}</SpellLink>
      ) : label;
      return (

            {label}

            <Tooltip content={formatThousands(value)}>
              {formatPercentage(value / total, 0)}%
            </Tooltip>

      );
    });
  }

  chart(items) {
    return (
      <DoughnutChart
        data={{
          datasets: [{
            data: items.map(item => item.value),
            backgroundColor: items.map(item => item.color),
            borderColor: '#666',
            borderWidth: 1.5,
          }],
          labels: items.map(item => item.label),
        }}
        options={{
          legend: {
            display: false,
          },
          tooltips: {
            bodyFontSize: 8,
          },
          cutoutPercentage: 45,
          animation: false,
          responsive: false,
        }}
        width={CHART_SIZE}
        height={CHART_SIZE}
      />
    );
  }

  masterySourceChart() {
    const items = [
      {
        color: '#00b159',
        label: 'Vivify',
        spellId: SPELLS.VIVIFY.id,
        value: this.vivify.gustsHealing,
      },
      {
        color: '#db00db',
        label: 'Renewing Mist',
        spellId: SPELLS.RENEWING_MIST.id,
        value: this.renewingMist.gustsHealing,
      },
      {
        color: '#f37735',
        label: 'Enveloping Mists',
        spellId: SPELLS.ENVELOPING_MIST.id,
        value: this.envelopingMists.gustsHealing,
      },
      {
        color: '#ffc425',
        label: 'Soothing Mist',
        spellId: SPELLS.SOOTHING_MIST.id,
        value: this.soothingMist.gustsHealing,
      },
      {
        color: '#00bbcc',
        label: 'Essence font',
        spellId: SPELLS.ESSENCE_FONT.id,
        value: this.essenceFontMastery.healing,
      },
    ];

    return (

          {this.chart(items)}

          {this.legend(items, this.totalMasteryHealing)}

    );
  }

  on_fightend() {
    if (debug) {
      console.log(`Vivify:${this.vivify.gustsHealing}`);
      console.log(`Renewing:${this.renewingMist.gustsHealing}`);
      console.log(`Enveloping:${this.envelopingMists.gustsHealing}`);
      console.log(`Soothing:${this.soothingMist.gustsHealing}`);
      console.log(`Essence font:${this.essenceFontMastery.healing}`);
      console.log(`Total Mastery healing:${this.totalMasteryHealing}`);
    }
  }

  statistic() {
    return (
      <StatisticsListBox
        position={STATISTIC_ORDER.CORE(20)}
        title={<><SpellLink id={SPELLS.GUSTS_OF_MISTS.id}>Gusts of Mists</SpellLink> breakdown</>}
      >
        {this.masterySourceChart()}
      </StatisticsListBox>
    );
  }
}

export default MasteryStats;
<commit_msg>Used DonutChart in Mistweaver Monk Gusts of Mists breakdown<commit_after>import React from 'react';

import SPELLS from 'common/SPELLS';

import SpellLink from 'common/SpellLink';
import { formatThousands } from 'common/format';

import Analyzer from 'parser/core/Analyzer';
import EssenceFontMastery from 'parser/monk/mistweaver/modules/features/EssenceFontMastery';
import EnvelopingMists from 'parser/monk/mistweaver/modules/spells/EnvelopingMists';
import SoothingMist from 'parser/monk/mistweaver/modules/spells/SoothingMist';
import RenewingMist from 'parser/monk/mistweaver/modules/spells/RenewingMist';
import Vivify from 'parser/monk/mistweaver/modules/spells/Vivify';

import { STATISTIC_ORDER } from 'interface/others/StatisticsListBox';
import DonutChart from 'interface/statistics/components/DonutChart';
import Statistic from 'interface/statistics/Statistic';

const debug = false;

class MasteryStats extends Analyzer {
  static dependencies = {
    essenceFontMastery: EssenceFontMastery,
    envelopingMists: EnvelopingMists,
    soothingMist: SoothingMist,
    renewingMist: RenewingMist,
    vivify: Vivify,
  };

  get totalMasteryHealing() {
    return (this.vivify.gustsHealing || 0)
            + (this.renewingMist.gustsHealing || 0)
            + (this.envelopingMists.gustsHealing || 0)
            + (this.soothingMist.gustsHealing || 0)
            + (this.essenceFontMastery.healing || 0);
  }

  get masterySourceChart() {
    const items = [
      {
        color: '#00b159',
        label: 'Vivify',
        spellId: SPELLS.VIVIFY.id,
        value: this.vivify.gustsHealing,
        valueTooltip: formatThousands(this.vivify.gustsHealing),
      },
      {
        color: '#db00db',
        label: 'Renewing Mist',
        spellId: SPELLS.RENEWING_MIST.id,
        value: this.renewingMist.gustsHealing,
        valueTooltip: formatThousands(this.renewingMist.gustsHealing),
      },
      {
        color: '#f37735',
        label: 'Enveloping Mists',
        spellId: SPELLS.ENVELOPING_MIST.id,
        value: this.envelopingMists.gustsHealing,
        valueTooltip: formatThousands(this.envelopingMists.gustsHealing),
      },
      {
        color: '#ffc425',
        label: 'Soothing Mist',
        spellId: SPELLS.SOOTHING_MIST.id,
        value: this.soothingMist.gustsHealing,
        valueTooltip: formatThousands(this.soothingMist.gustsHealing),
      },
      {
        color: '#00bbcc',
        label: 'Essence font',
        spellId: SPELLS.ESSENCE_FONT.id,
        value: this.essenceFontMastery.healing,
        valueTooltip: formatThousands(this.essenceFontMastery.healing),
      },
    ];

    return (
      <DonutChart
        items={items}
      />
    );
  }

  on_fightend() {
    if (debug) {
      console.log(`Vivify:${this.vivify.gustsHealing}`);
      console.log(`Renewing:${this.renewingMist.gustsHealing}`);
      console.log(`Enveloping:${this.envelopingMists.gustsHealing}`);
      console.log(`Soothing:${this.soothingMist.gustsHealing}`);
      console.log(`Essence font:${this.essenceFontMastery.healing}`);
      console.log(`Total Mastery healing:${this.totalMasteryHealing}`);
    }
  }

  statistic() {
    return (
      <Statistic
        position={STATISTIC_ORDER.CORE(20)}
        style={{ height: '220px' }}
      >

          <SpellLink id={SPELLS.GUSTS_OF_MISTS.id}>Gusts of Mists</SpellLink> breakdown
          {this.masterySourceChart}

      </Statistic>
    );
  }
}

export default MasteryStats;
<|endoftext|>


<commit_before>(function(angular){
    "use strict";

    var app = angular.module('Entity', ['RelatedAgents', 'angularSpinner', 'ngSanitize']);

    app.factory('FindService', ['$rootScope', '$http', function($rootScope, $http){
        var baseUrl = MapasCulturais.baseURL + '/api/';
        function extend (query){
            return angular.extend(query, {
                "@select": 'id,name,type,shortDescription,terms,avatar',
                "@files": '(avatar.avatarSmall):url',
                "@order": 'name'
            });
        };

        function request (url, query, success_cb, error_cb){
            query = extend(query);

            var p = $http({
                url: url,
                method: "GET",
                params: query
            });

            if( angular.isFunction( success_cb ) ){
                p.success( success_cb );
            }

            if( angular.isFunction( error_cb ) ){
                p.error( error_cb );
            }
        };

        return {

            find: function(entity, query, success_cb, error_cb){
                var url = baseUrl + entity + '/find';

                request(url, query, success_cb, error_cb);
            },

            findOne: function(entity, query, success_cb, error_cb){
                var url = baseUrl + entity + '/find';

                request(url, query, success_cb, error_cb);
            },

            pagination: function(entity, resultsPerPage, query, success_cb, error_cb){
                var url = baseUrl + entity + '/find',
                    page = 0, executing = false;

                function success () {
                    success_cb();
                    executing = false;
                }

                function error () {
                    error_cb();
                    executing = false;
                }

                return {
                    nextPage: function () {
                        if(!executing){
                            executing = true;

                            page++;

                            query['@page'] = page;
                            query['@limit'] = resultsPerPage;

                            request(url, query, success, error);
                        }
                    },

                    currentPage: function (){
                        if(this._page > 0 && !executing){
                            executing = true;

                            query['@page'] = page;
                            query['@limit'] = resultsPerPage;

                            request(url, query, success, error);
                        }
                    },

                    previousPage: function () {
                        if(this._page > 0 && !executing){
                            executing = true;

                            page--;

                            query['@page'] = page;
                            query['@limit'] = resultsPerPage;

                            request(url, query, success, error);
                        }
                    }
                };
            }
        };
    }]);

    app.controller('EntityController',['$scope', '$timeout', 'RelatedAgents', function($scope, $timeout, RelatedAgents){
        $scope.openEditBox = function(editboxId){

        };
    }]);

    app.directive('findEntity', ['$timeout', 'FindService', function($timeout, FindService){
        var timeouts = {};

        return {
            restrict: 'E',
            templateUrl: MapasCulturais.assetURL + '/js/directives/find-entity.html',
            scope:{
                spinnerCondition: '=',
                entity: '@',
                noResultsText: '@',
                filter: '=',
                select: '='
            },

            link: function($scope, el, attrs){
                $scope.attrs = attrs;

                $scope.result = [];

                $scope.searchText = '';

                $scope.noEntityFound = false;

                $scope.avatarUrl = function(entity){
                    if(entity['@files:avatar.avatarSmall'])
                        return entity['@files:avatar.avatarSmall'].url;
                    else
                        return MapasCulturais.defaultAvatarURL;
                };

                $scope.find = function(){
                    if(timeouts.find)
                        $timeout.cancel(timeouts.find);

                    var s = $scope.searchText.trim().replace(' ', '*');

                    timeouts.find = $timeout(function(){
                        $scope.spinnerCondition = true;
                        FindService.find($scope.entity, { name: 'ILIKE(*' + s + '*)' }, function(data,status){
                            $scope.processResult(data, status);
                            $scope.spinnerCondition = false;
                        });
                    },500);
                };

                $scope.processResult = function(data, status){
                    if(angular.isFunction($scope.filter))
                        data = $scope.filter(data, status);

                    if(data.length === 0){
                        $scope.noEntityFound = true;

                        $timeout(function(){
                            $scope.noEntityFound = false;
                        },3000);
                    }

                    $scope.result = data;
                };
            }
        };
    }]);

    app.factory('EditBox', function(){
        function setPosition($box, target){
            if($box.hasClass('mc-left')){
                $box.position({
                    my: 'right-20 center',
                    at: 'left center',
                    of: target
                });

            }else if($box.hasClass('mc-right')){
                $box.position({
                    my: 'left-20 center',
                    at: 'right center',
                    of: target
                });

            }else if($box.hasClass('mc-top')){
                $box.position({
                    my: 'center top-20',
                    at: 'center bottom',
                    of: target
                });

            }else if($box.hasClass('mc-bottom')){
                $box.position({
                    my: 'center bottom-20',
                    at: 'center top',
                    of: target
                });
            }
        };

        return {
            openEditboxes: {},

            register: function(editboxId){
                if(this.openEditboxes[editboxId])
                    throw new Error('EditBox with id ' + editboxId + ' already exists');

                this.openEditboxes[editboxId] = false;
            },

            open: function(editboxId, $event){
                if(typeof this.openEditboxes[editboxId] === 'undefined')
                    throw new Error('EditBox with id ' + editboxId + ' does not exists');

                this.openEditboxes[editboxId] = true;

                var $box = $('#' + editboxId).find('>div.edit-box');
                $box.show();

                setPosition($box, $event.target);
            },

            close: function(editboxId){
                if(typeof this.openEditboxes[editboxId] === 'undefined')
                    throw new Error('EditBox with id ' + editboxId + ' does not exists');

                this.openEditboxes[editboxId] = false;

                var $box = $('#' + editboxId).find('>div.edit-box');
                $box.hide();
            }
        };
    });

    app.directive('editBox', ['EditBox', function(EditBox) {
        return {
            restrict: 'E',
            templateUrl: MapasCulturais.assetURL + '/js/directives/edit-box.html',
            transclude: true,

            scope: {
                spinnerCondition: '=',
                onSubmit: '=',
                onCancel: '='
            },

            link: function($scope, el, attrs) {
                if(!attrs.id)
                    throw new Error('EditBox id is required');

                $scope.editbox = EditBox;
                $scope.cancelLabel = attrs.cancelLabel;

                EditBox.register(attrs.id);

                $scope.args = attrs;

                $scope.spinnerUrl = MapasCulturais.assetURL + '/img/spinner.gif';

                $scope.classes = {
                    'mc-bottom': attrs.position === 'bottom' || !attrs.position,
                    'mc-top': attrs.position === 'top',
                    'mc-left': attrs.position === 'left',
                    'mc-right': attrs.position === 'right'
                };

                $scope.submit = function(){
                    if(angular.isFunction($scope.onSubmit)){
                        $scope.onSubmit();
                    }
                };

                $scope.cancel = function(){
                    if(attrs.closeOnCancel)
                        EditBox.close(attrs.id);

                    if(angular.isFunction($scope.onCancel)){
                        $scope.onCancel();
                    }
                };
            }
        }
    }]);

})(angular);<commit_msg>EditBox angular directive input focus and keyboard events (enter, esc)<commit_after>(function(angular){
    "use strict";

    var app = angular.module('Entity', ['RelatedAgents', 'angularSpinner', 'ngSanitize']);

    app.factory('FindService', ['$rootScope', '$http', function($rootScope, $http){
        var baseUrl = MapasCulturais.baseURL + '/api/';
        function extend (query){
            return angular.extend(query, {
                "@select": 'id,name,type,shortDescription,terms,avatar',
                "@files": '(avatar.avatarSmall):url',
                "@order": 'name'
            });
        };

        function request (url, query, success_cb, error_cb){
            query = extend(query);

            var p = $http({
                url: url,
                method: "GET",
                params: query
            });

            if( angular.isFunction( success_cb ) ){
                p.success( success_cb );
            }

            if( angular.isFunction( error_cb ) ){
                p.error( error_cb );
            }
        };

        return {

            find: function(entity, query, success_cb, error_cb){
                var url = baseUrl + entity + '/find';

                request(url, query, success_cb, error_cb);
            },

            findOne: function(entity, query, success_cb, error_cb){
                var url = baseUrl + entity + '/find';

                request(url, query, success_cb, error_cb);
            },

            pagination: function(entity, resultsPerPage, query, success_cb, error_cb){
                var url = baseUrl + entity + '/find',
                    page = 0, executing = false;

                function success () {
                    success_cb();
                    executing = false;
                }

                function error () {
                    error_cb();
                    executing = false;
                }

                return {
                    nextPage: function () {
                        if(!executing){
                            executing = true;

                            page++;

                            query['@page'] = page;
                            query['@limit'] = resultsPerPage;

                            request(url, query, success, error);
                        }
                    },

                    currentPage: function (){
                        if(this._page > 0 && !executing){
                            executing = true;

                            query['@page'] = page;
                            query['@limit'] = resultsPerPage;

                            request(url, query, success, error);
                        }
                    },

                    previousPage: function () {
                        if(this._page > 0 && !executing){
                            executing = true;

                            page--;

                            query['@page'] = page;
                            query['@limit'] = resultsPerPage;

                            request(url, query, success, error);
                        }
                    }
                };
            }
        };
    }]);

    app.controller('EntityController',['$scope', '$timeout', 'RelatedAgents', function($scope, $timeout, RelatedAgents){
        $scope.openEditBox = function(editboxId){

        };
    }]);

    app.directive('findEntity', ['$timeout', 'FindService', function($timeout, FindService){
        var timeouts = {};

        return {
            restrict: 'E',
            templateUrl: MapasCulturais.assetURL + '/js/directives/find-entity.html',
            scope:{
                spinnerCondition: '=',
                entity: '@',
                noResultsText: '@',
                filter: '=',
                select: '='
            },

            link: function($scope, el, attrs){
                $scope.attrs = attrs;

                $scope.result = [];

                $scope.searchText = '';

                $scope.noEntityFound = false;

                $scope.avatarUrl = function(entity){
                    if(entity['@files:avatar.avatarSmall'])
                        return entity['@files:avatar.avatarSmall'].url;
                    else
                        return MapasCulturais.defaultAvatarURL;
                };

                $scope.find = function(){
                    if(timeouts.find)
                        $timeout.cancel(timeouts.find);

                    var s = $scope.searchText.trim().replace(' ', '*');

                    timeouts.find = $timeout(function(){
                        $scope.spinnerCondition = true;
                        FindService.find($scope.entity, { name: 'ILIKE(*' + s + '*)' }, function(data,status){
                            $scope.processResult(data, status);
                            $scope.spinnerCondition = false;
                        });
                    },500);
                };

                $scope.processResult = function(data, status){
                    if(angular.isFunction($scope.filter))
                        data = $scope.filter(data, status);

                    if(data.length === 0){
                        $scope.noEntityFound = true;

                        $timeout(function(){
                            $scope.noEntityFound = false;
                        },3000);
                    }

                    $scope.result = data;
                };
            }
        };
    }]);

    app.factory('EditBox', function(){
        function setPosition($box, target){
            if($box.hasClass('mc-left')){
                $box.position({
                    my: 'right-20 center',
                    at: 'left center',
                    of: target
                });

            }else if($box.hasClass('mc-right')){
                $box.position({
                    my: 'left-20 center',
                    at: 'right center',
                    of: target
                });

            }else if($box.hasClass('mc-top')){
                $box.position({
                    my: 'center top-20',
                    at: 'center bottom',
                    of: target
                });

            }else if($box.hasClass('mc-bottom')){
                $box.position({
                    my: 'center bottom-20',
                    at: 'center top',
                    of: target
                });
            }
        };

        var editBox = {
            openEditboxes: {},

            register: function(editboxId){
                if(this.openEditboxes[editboxId])
                    throw new Error('EditBox with id ' + editboxId + ' already exists');

                this.openEditboxes[editboxId] = false;

                var $box = $('#' + editboxId);
                var $submitInput = $box.find('input:text');
                $submitInput.on('keyup', function(event){
                    if(event.keyCode === 13){
                        $box.find('button[type="submit"]').click();
                    }
                });
            },

            open: function(editboxId, $event){
                if(typeof this.openEditboxes[editboxId] === 'undefined')
                    throw new Error('EditBox with id ' + editboxId + ' does not exists');

                this.openEditboxes[editboxId] = true;

                var $box = $('#' + editboxId).find('>div.edit-box');
                $box.show();
                var $firstInput = $box.find('input:first,select:first,textarea:first');
                $firstInput.focus();
                setPosition($box, $event.target);
            },

            close: function(editboxId){
                if(typeof this.openEditboxes[editboxId] === 'undefined')
                    throw new Error('EditBox with id ' + editboxId + ' does not exists');

                this.openEditboxes[editboxId] = false;

                var $box = $('#' + editboxId).find('>div.edit-box');
                $box.hide();
            }
        };

        $('body').on('keyup', 'edit-box', function(event){
            if(event.keyCode === 27){
                editBox.close(this.id);
            }
        });

        return editBox;
    });

    app.directive('editBox', ['EditBox', function(EditBox) {
        return {
            restrict: 'E',
            templateUrl: MapasCulturais.assetURL + '/js/directives/edit-box.html',
            transclude: true,

            scope: {
                spinnerCondition: '=',
                onSubmit: '=',
                onCancel: '='
            },

            link: function($scope, el, attrs) {
                if(!attrs.id)
                    throw new Error('EditBox id is required');

                $scope.editbox = EditBox;
                $scope.cancelLabel = attrs.cancelLabel;

                EditBox.register(attrs.id);

                $scope.args = attrs;

                $scope.spinnerUrl = MapasCulturais.assetURL + '/img/spinner.gif';

                $scope.classes = {
                    'mc-bottom': attrs.position === 'bottom' || !attrs.position,
                    'mc-top': attrs.position === 'top',
                    'mc-left': attrs.position === 'left',
                    'mc-right': attrs.position === 'right'
                };

                $scope.submit = function(){
                    if(angular.isFunction($scope.onSubmit)){
                        $scope.onSubmit();
                    }
                };

                $scope.cancel = function(){
                    if(attrs.closeOnCancel)
                        EditBox.close(attrs.id);

                    if(angular.isFunction($scope.onCancel)){
                        $scope.onCancel();
                    }
                };
            }
        }
    }]);

})(angular);<|endoftext|>


<commit_before>var net = require('net');
var rules = require('../lib/rules');
var util = require('../lib/util');

var HTTP_PROXY_RE = /^(?:proxy|http-proxy|http2https-proxy|https2http-proxy|internal-proxy):\/\//;
var INTERNAL_APP;
var WEBUI_PATH;
var PLUGIN_RE;

function convertToLocalUIHost(req, isInternal) {
  req.url = req.url.replace(isInternal ? INTERNAL_APP : WEBUI_PATH, '/');
  req.headers.host = 'local.whistlejs.com';
}

module.exports = function(req, res, next) {
  var config = this.config;
  var pluginMgr = this.pluginMgr;
  WEBUI_PATH = config.WEBUI_PATH;
  if (!INTERNAL_APP) {
    var webuiPathRe = util.escapeRegExp(WEBUI_PATH);
    INTERNAL_APP = new RegExp('^' + webuiPathRe + '(log|weinre)\\.(\\d{1,5})/');
    PLUGIN_RE = new RegExp('^' + webuiPathRe + 'whistle\\.([a-z\\d_-]+)/');
  }
  var host = (req.headers.host || '').split(':');
  var port = host[1] || 80;
  var bypass;
  host = host[0];
  var transformPort, proxyUrl;
  var isWebUI = req.path.indexOf(WEBUI_PATH) === 0;
  if (isWebUI) {
    isWebUI = !config.pureProxy;
    if (isWebUI) {
      if (INTERNAL_APP.test(req.path)) {
        transformPort = RegExp.$2;
        proxyUrl = transformPort != (RegExp.$1 === 'weinre' ? config.weinreport : config.uiport);
      } else if (PLUGIN_RE.test(req.path)) {
        proxyUrl = !pluginMgr.getPlugin(RegExp.$1 + ':');
      } else {
        isWebUI = false;
      }
      if (proxyUrl) {
        proxyUrl = rules.resolveProxy(util.getFullUrl(req));
        proxyUrl = proxyUrl && proxyUrl.matcher;
        if (proxyUrl && HTTP_PROXY_RE.test(proxyUrl)) {
          proxyUrl = proxyUrl.replace(HTTP_PROXY_RE, '');
        } else {
          proxyUrl = null;
        }
      }
    }
  } else {
    isWebUI = req.headers[config.WEBUI_HEAD] || config.isLocalUIUrl(host);
    if (isWebUI || (net.isIP(host) && util.isLocalAddress(host))) {
      if (req.path.indexOf('/_/') === 0) {
        bypass = '/_/';
      } else if (req.path.indexOf('/-/') === 0) {
        bypass = '/-/';
      }
      if (bypass) {
        req.url = req.url.replace(bypass, '/');
      } else if (!isWebUI) {
        if (port == config.port || port == config.uiport) {
          host = config.localUIHost;
          isWebUI = true;
        } else if (port == config.weinreport) {
          host = config.WEINRE_HOST;
        }
      }
    }
  }
  // 后续有用到
  var fullUrl = req.fullUrl = util.getFullUrl(req);
  if (bypass) {
    return next();
  }
  var pluginHomePage, localRule;
  if (proxyUrl) {
    rules.resolveHost('http://' + proxyUrl, function(err, ip) {
      if (err) {
        return next(err);
      }
      var colon = proxyUrl.indexOf(':');
      var proxyPort = colon === -1 ? 80 : proxyUrl.substring(colon + 1);
      convertToLocalUIHost(req, transformPort);
      util.transformReq(req, res, proxyPort > 0 ? proxyPort : 80, ip);
    });
  } else if (isWebUI) {
    convertToLocalUIHost(req, transformPort);
    util.transformReq(req, res, transformPort || config.uiport);
  } else if (pluginHomePage || (pluginHomePage = pluginMgr.getPluginByHomePage(fullUrl))) {
    pluginMgr.loadPlugin(pluginHomePage, function(err, ports) {
      if (err || !ports.uiPort) {
        res.response(util.wrapResponse({
          statusCode: err ? 500 : 404,
          headers: {
            'content-type': 'text/html; charset=utf-8'
          },
          body: '
' + (err || 'Not Found') + '
'
        }));
        return;
      }
      util.transformReq(req, res, ports.uiPort);
    });
  } else if (localRule = rules.resolveLocalRule(fullUrl)) {
    req.url = localRule.url;
    util.transformReq(req, res, config.uiport);
  } else {
    next();
  }
};

<commit_msg>refactor: the webui url<commit_after>var net = require('net');
var rules = require('../lib/rules');
var util = require('../lib/util');

var HTTP_PROXY_RE = /^(?:proxy|http-proxy|http2https-proxy|https2http-proxy|internal-proxy):\/\//;
var INTERNAL_APP;
var PLUGIN_RE;

module.exports = function(req, res, next) {
  var config = this.config;
  var pluginMgr = this.pluginMgr;
  var WEBUI_PATH = config.WEBUI_PATH;
  if (!INTERNAL_APP) {
    var webuiPathRe = util.escapeRegExp(WEBUI_PATH);
    INTERNAL_APP = new RegExp('^' + webuiPathRe + '(log|weinre)\\.(\\d{1,5})/');
    PLUGIN_RE = new RegExp('^' + webuiPathRe + 'whistle\\.([a-z\\d_-]+)/');
  }
  var host = (req.headers.host || '').split(':');
  var port = host[1] || 80;
  var bypass;
  host = host[0];
  var transformPort, proxyUrl;
  var isWebUI = req.path.indexOf(WEBUI_PATH) === 0;
  if (isWebUI) {
    isWebUI = !config.pureProxy;
    if (isWebUI) {
      if (INTERNAL_APP.test(req.path)) {
        transformPort = RegExp.$2;
        proxyUrl = transformPort != (RegExp.$1 === 'weinre' ? config.weinreport : config.uiport);
      } else if (PLUGIN_RE.test(req.path)) {
        proxyUrl = !pluginMgr.getPlugin(RegExp.$1 + ':');
      } else {
        isWebUI = false;
      }
      if (proxyUrl) {
        proxyUrl = rules.resolveProxy(util.getFullUrl(req));
        proxyUrl = proxyUrl && proxyUrl.matcher;
        if (proxyUrl && HTTP_PROXY_RE.test(proxyUrl)) {
          proxyUrl = proxyUrl.replace(HTTP_PROXY_RE, '');
        } else {
          proxyUrl = null;
        }
      }
    }
  } else {
    isWebUI = req.headers[config.WEBUI_HEAD] || config.isLocalUIUrl(host);
    if (isWebUI || (net.isIP(host) && util.isLocalAddress(host))) {
      if (req.path.indexOf('/_/') === 0) {
        bypass = '/_/';
      } else if (req.path.indexOf('/-/') === 0) {
        bypass = '/-/';
      }
      if (bypass) {
        req.url = req.url.replace(bypass, '/');
      } else if (!isWebUI) {
        if (port == config.port || port == config.uiport) {
          host = config.localUIHost;
          isWebUI = true;
        } else if (port == config.weinreport) {
          host = config.WEINRE_HOST;
        }
      }
    }
  }
  // 后续有用到
  var fullUrl = req.fullUrl = util.getFullUrl(req);
  if (bypass) {
    return next();
  }
  var pluginHomePage, localRule;
  if (proxyUrl) {
    rules.resolveHost('http://' + proxyUrl, function(err, ip) {
      if (err) {
        return next(err);
      }
      var colon = proxyUrl.indexOf(':');
      var proxyPort = colon === -1 ? 80 : proxyUrl.substring(colon + 1);
      util.transformReq(req, res, proxyPort > 0 ? proxyPort : 80, ip);
    });
  } else if (isWebUI) {
    req.url = req.url.replace(transformPort ? INTERNAL_APP : WEBUI_PATH, '/');
    util.transformReq(req, res, transformPort || config.uiport);
  } else if (pluginHomePage || (pluginHomePage = pluginMgr.getPluginByHomePage(fullUrl))) {
    pluginMgr.loadPlugin(pluginHomePage, function(err, ports) {
      if (err || !ports.uiPort) {
        res.response(util.wrapResponse({
          statusCode: err ? 500 : 404,
          headers: {
            'content-type': 'text/html; charset=utf-8'
          },
          body: '
' + (err || 'Not Found') + '
'
        }));
        return;
      }
      util.transformReq(req, res, ports.uiPort);
    });
  } else if (localRule = rules.resolveLocalRule(fullUrl)) {
    req.url = localRule.url;
    util.transformReq(req, res, config.uiport);
  } else {
    next();
  }
};

<|endoftext|>