<commit_before>/**
 * Utility module to manage the WebDriver
 *
 * @author Julien Roche
 * @version 0.4.0
 * @since 0.0.1
 */

'use strict';

// Imports
const gutil = require('gulp-util');
const url = require('url');
const http = require('http');
const childProcess = require('child_process');
const protractorUtils = require('./protractor-utils');

// Constants & variables
const PLUGIN_NAME = require('./constants.json').PLUGIN_NAME;
const IS_WINDOWS = /^win/.test(process.platform);
const WIN_COMMAND_EXTENSION = IS_WINDOWS ? '.cmd' : '';
const COMMAND_RELATIVE_PATH = IS_WINDOWS ? '' : './';
const PROTRACTOR_COMMAND = 'protractor' + WIN_COMMAND_EXTENSION;

const WEB_DRIVER_LOG_STARTED = 'Started org.openqa.jetty.jetty.Server';
const WEB_DRIVER_LOG_STARTED_NEW = 'Selenium Server is up and running';
const WEB_DRIVER_LOG_STOPPED = 'Command request: shutDownSeleniumServer';
const WEB_DRIVER_SHUTDOWN_PATH = '/selenium-server/driver/?cmd=shutDownSeleniumServer';
const WEB_DRIVER_COMMAND = 'webdriver-manager' + WIN_COMMAND_EXTENSION;
const WEB_DRIVER_START_COMMAND = 'start';

module.exports = function (protractorModulePath) {
    let protractorDirToUse = protractorModulePath ? protractorModulePath : protractorUtils.getProtractorDir();

    return {
        /**
         * Default WebDriver URL
         *
         * @constant
         * @static
         */
        'DEFAULT_WEB_DRIVER_URL': 'http://localhost:4444/wd/hub',

        /**
         * Return the WebDriver shutdown url based on the webDriverUrl
         *
         * @method
         * @static
         * @param {string} webDriverUrl
         * @returns {string}
         */
        'getWebDriverShutdownUrl': function (webDriverUrl) {
            if (webDriverUrl) {
                let urlMetaData = url.parse(webDriverUrl);

                if (urlMetaData && urlMetaData.host) {
                    return `${urlMetaData.protocol}//${urlMetaData.host}${WEB_DRIVER_SHUTDOWN_PATH}`;
                }
            }

            return null;
        },

        /**
         * Execute the protractor engine
         *
         * @method
         * @static
         * @param {string[]} args
         * @returns {Object}
         */
        'runProtractor': function (args) {
            return childProcess.spawn(COMMAND_RELATIVE_PATH + PROTRACTOR_COMMAND, args, {
                'stdio': 'inherit',
                'env': process.env,
                'cwd': protractorDirToUse
            });
        },

        /**
         * Execute the protractor engine
         *
         * @method
         * @static
         * @param {string[]} args
         * @param {Function} callback
         */
        'runProtractorAndWait': function (args, callback) {
            let child = this
                .runProtractor(args)
                .on('exit', function (code) {
                    if (child) {
                        child.kill();
                    }

                    if (callback) {
                        callback(code);
                    }
                });
        },

        /**
         * Start the WebDriver server
         *
         * @method
         * @static
         * @param {Function} callback
         * @param {boolean} [verbose=true]
         * @param {Object} [startOptions]
         */
        'webDriverStandaloneStart': function (callback, verbose, startOptions) {
            gutil.log(PLUGIN_NAME + ' - Webdriver standalone server will be started');

            let callbackWasCalled = false;
            let logOutput = true;
            let command;

            function _interceptLogData(data) {
                let dataString = data.toString();

                if (logOutput && verbose) {
                    gutil.log(dataString);
                }

                if (dataString.indexOf(WEB_DRIVER_LOG_STARTED_NEW) >= 0 || dataString.indexOf(WEB_DRIVER_LOG_STARTED) >= 0) {
                    gutil.log(PLUGIN_NAME + ' - Webdriver standalone server is started');
                    callbackWasCalled = true;
                    logOutput = false;
                    callback();

                } else if (dataString.indexOf(WEB_DRIVER_LOG_STOPPED) >= 0) {
                    logOutput = true;

                    if (verbose) {
                        gutil.log(dataString);
                    }
                }
            }

            command = childProcess.spawn(
                COMMAND_RELATIVE_PATH + WEB_DRIVER_COMMAND,
                [WEB_DRIVER_START_COMMAND].concat(startOptions && startOptions.args ? startOptions.args : []),
                {
                    'cwd': protractorDirToUse
                }
            );

            command.once('close', function (errorCode) {
                gutil.log(PLUGIN_NAME + ' - Webdriver standalone server will be closed');

                if (!callbackWasCalled) {
                    callback(errorCode);
                }
            });

            command.stderr.on('data', _interceptLogData);
            command.stdout.on('data', _interceptLogData);
        },

        /**
         * Stop the WebDriver server
         *
         * @method
         * @static
         * @param {string} webDriverUrl
         * @param {Function} callback
         */
        'webDriverStandaloneStop': function (webDriverUrl, callback) {
            let shutDownUrl = this.getWebDriverShutdownUrl(webDriverUrl);
            let shutDownUrlMetaData = url.parse(shutDownUrl);

            http
                .get({
                    'host': shutDownUrlMetaData.hostname,
                    'port': shutDownUrlMetaData.port,
                    'path': shutDownUrlMetaData.path
                }, function () {
                    gutil.log(PLUGIN_NAME + ' - Webdriver standalone server is stopped');
                    callback();
                })
                .on('error', function (err) {
                    gutil.log(PLUGIN_NAME + ' - An error occured to stop the Webdriver standalone server');
                    callback(err);
                });
        },

        /**
         * Update the webDriver connector
         *
         * @method
         * @static
         * @params {{ 'browsers' } | Function} optsOrCallback
         * @param {Function} cb
         */
        'webDriverUpdate': function (optsOrCallback, cb) {
            let callback = cb ? cb : optsOrCallback;
            let options = cb ? optsOrCallback : null;
            let args = ['update', '--standalone'];
            let browsers = ['chrome'];

            if (options) {
                if (options.browsers && options.browsers.length > 0) {
                    browsers = options.browsers;
                }

                browsers.forEach(function (element) {
                    args.push('--' + element);
                });

                if (options.args) {
                    args = args.concat(options.args);
                }
            }

            childProcess
                .spawn(
                    COMMAND_RELATIVE_PATH + WEB_DRIVER_COMMAND,
                    args,
                    {
                        'cwd': protractorDirToUse,
                        'stdio': 'inherit'
                    }
                )
                .once('close', callback);
        },

        /**
         * Update and start the webDriver connector
         *
         * @method
         * @static
         * @param {Function} callback
         * @param {boolean} [verbose=true]
         * @param {Object} [updateOptions]
         * @param {Object} [startOptions]
         */
        'webDriverUpdateAndStart': function (callback, verbose, updateOptions, startOptions) {
            gutil.log(PLUGIN_NAME + ' - Webdriver standalone will be updated');

            this.webDriverUpdate(updateOptions, () => {
                gutil.log(PLUGIN_NAME + ' - Webdriver standalone is updated');
                this.webDriverStandaloneStart(callback, verbose, startOptions);
            });
        }
    };
};
<commit_msg>webDriverStandaloneStart returns a function to stop the server<commit_after>/**
 * Utility module to manage the WebDriver
 *
 * @author Julien Roche
 * @version 0.4.0
 * @since 0.0.1
 */

'use strict';

// Imports
const gutil = require('gulp-util');
const url = require('url');
const http = require('http');
const childProcess = require('child_process');
const protractorUtils = require('./protractor-utils');

// Constants & variables
const PLUGIN_NAME = require('./constants.json').PLUGIN_NAME;
const IS_WINDOWS = /^win/.test(process.platform);
const WIN_COMMAND_EXTENSION = IS_WINDOWS ? '.cmd' : '';
const COMMAND_RELATIVE_PATH = IS_WINDOWS ? '' : './';
const PROTRACTOR_COMMAND = 'protractor' + WIN_COMMAND_EXTENSION;

const WEB_DRIVER_LOG_STARTED = 'Started org.openqa.jetty.jetty.Server';
const WEB_DRIVER_LOG_STARTED_NEW = 'Selenium Server is up and running';
const WEB_DRIVER_LOG_STOPPED = 'Command request: shutDownSeleniumServer';
const WEB_DRIVER_SHUTDOWN_PATH = '/selenium-server/driver/?cmd=shutDownSeleniumServer';
const WEB_DRIVER_COMMAND = 'webdriver-manager' + WIN_COMMAND_EXTENSION;
const WEB_DRIVER_START_COMMAND = 'start';

module.exports = function (protractorModulePath) {
    let protractorDirToUse = protractorModulePath ? protractorModulePath : protractorUtils.getProtractorDir();

    return {
        /**
         * Default WebDriver URL
         *
         * @constant
         * @static
         */
        'DEFAULT_WEB_DRIVER_URL': 'http://localhost:4444/wd/hub',

        /**
         * Return the WebDriver shutdown url based on the webDriverUrl
         *
         * @method
         * @static
         * @param {string} webDriverUrl
         * @returns {string}
         */
        'getWebDriverShutdownUrl': function (webDriverUrl) {
            if (webDriverUrl) {
                let urlMetaData = url.parse(webDriverUrl);

                if (urlMetaData && urlMetaData.host) {
                    return `${urlMetaData.protocol}//${urlMetaData.host}${WEB_DRIVER_SHUTDOWN_PATH}`;
                }
            }

            return null;
        },

        /**
         * Execute the protractor engine
         *
         * @method
         * @static
         * @param {string[]} args
         * @returns {Object}
         */
        'runProtractor': function (args) {
            return childProcess.spawn(COMMAND_RELATIVE_PATH + PROTRACTOR_COMMAND, args, {
                'stdio': 'inherit',
                'env': process.env,
                'cwd': protractorDirToUse
            });
        },

        /**
         * Execute the protractor engine
         *
         * @method
         * @static
         * @param {string[]} args
         * @param {Function} callback
         */
        'runProtractorAndWait': function (args, callback) {
            let child = this
                .runProtractor(args)
                .on('exit', function (code) {
                    if (child) {
                        child.kill();
                    }

                    if (callback) {
                        callback(code);
                    }
                });
        },

        /**
         * @callback WebDriverService~stopServer
         */

        /**
         * Start the WebDriver server
         *
         * @method
         * @static
         * @param {Function} callback
         * @param {boolean} [verbose=true]
         * @param {Object} [startOptions]
         * @returns {WebDriverService~stopServer} Function to stop the server
         */
        'webDriverStandaloneStart': function (callback, verbose, startOptions) {
            gutil.log(PLUGIN_NAME + ' - Webdriver standalone server will be started');

            let callbackWasCalled = false;
            let logOutput = true;
            let command;

            function _interceptLogData(data) {
                let dataString = data.toString();

                if (logOutput && verbose) {
                    gutil.log(dataString);
                }

                if (dataString.indexOf(WEB_DRIVER_LOG_STARTED_NEW) >= 0 || dataString.indexOf(WEB_DRIVER_LOG_STARTED) >= 0) {
                    gutil.log(PLUGIN_NAME + ' - Webdriver standalone server is started');
                    callbackWasCalled = true;
                    logOutput = false;
                    callback();

                } else if (dataString.indexOf(WEB_DRIVER_LOG_STOPPED) >= 0) {
                    logOutput = true;

                    if (verbose) {
                        gutil.log(dataString);
                    }
                }
            }

            command = childProcess.spawn(
                COMMAND_RELATIVE_PATH + WEB_DRIVER_COMMAND,
                [WEB_DRIVER_START_COMMAND].concat(startOptions && startOptions.args ? startOptions.args : []),
                {
                    'cwd': protractorDirToUse
                }
            );

            command.once('close', function (errorCode) {
                gutil.log(PLUGIN_NAME + ' - Webdriver standalone server will be closed');

                if (!callbackWasCalled) {
                    callback(errorCode);
                }
            });

            command.stderr.on('data', _interceptLogData);
            command.stdout.on('data', _interceptLogData);

            return function () {
                if (!callbackWasCalled) {
                    command.stdin.pause();
                    command.kill();
                }
            };
        },

        /**
         * Stop the WebDriver server
         *
         * @method
         * @static
         * @param {string} webDriverUrl
         * @param {Function} callback
         */
        'webDriverStandaloneStop': function (webDriverUrl, callback) {
            let shutDownUrl = this.getWebDriverShutdownUrl(webDriverUrl);
            let shutDownUrlMetaData = url.parse(shutDownUrl);

            http
                .get({
                    'host': shutDownUrlMetaData.hostname,
                    'port': shutDownUrlMetaData.port,
                    'path': shutDownUrlMetaData.path
                }, function () {
                    gutil.log(PLUGIN_NAME + ' - Webdriver standalone server is stopped');
                    callback();
                })
                .on('error', function (err) {
                    gutil.log(PLUGIN_NAME + ' - An error occured to stop the Webdriver standalone server');
                    callback(err);
                });
        },

        /**
         * Update the webDriver connector
         *
         * @method
         * @static
         * @params {{ 'browsers' } | Function} optsOrCallback
         * @param {Function} cb
         */
        'webDriverUpdate': function (optsOrCallback, cb) {
            let callback = cb ? cb : optsOrCallback;
            let options = cb ? optsOrCallback : null;
            let args = ['update', '--standalone'];
            let browsers = ['chrome'];

            if (options) {
                if (options.browsers && options.browsers.length > 0) {
                    browsers = options.browsers;
                }

                browsers.forEach(function (element) {
                    args.push('--' + element);
                });

                if (options.args) {
                    args = args.concat(options.args);
                }
            }

            childProcess
                .spawn(
                    COMMAND_RELATIVE_PATH + WEB_DRIVER_COMMAND,
                    args,
                    {
                        'cwd': protractorDirToUse,
                        'stdio': 'inherit'
                    }
                )
                .once('close', callback);
        },

        /**
         * Update and start the webDriver connector
         *
         * @method
         * @static
         * @param {Function} callback
         * @param {boolean} [verbose=true]
         * @param {Object} [updateOptions]
         * @param {Object} [startOptions]
         */
        'webDriverUpdateAndStart': function (callback, verbose, updateOptions, startOptions) {
            gutil.log(PLUGIN_NAME + ' - Webdriver standalone will be updated');

            this.webDriverUpdate(updateOptions, () => {
                gutil.log(PLUGIN_NAME + ' - Webdriver standalone is updated');
                this.webDriverStandaloneStart(callback, verbose, startOptions);
            });
        }
    };
};
<|endoftext|>


<commit_before>if (typeof JSDOC == "undefined") JSDOC = {};

/**
	@namespace
	@requires JSDOC.Walker
	@requires JSDOC.Symbol
	@requires JSDOC.DocComment
*/
JSDOC.Parser = {
	conf: {
		ignoreCode:               JSDOC.opt.n,
		ignoreAnonymous:           true, // factory: true
		treatUnderscoredAsPrivate: true, // factory: true
		explain:                   false // factory: false
	},

	addSymbol: function(symbol) {

		if (JSDOC.Parser.rename) {
			for (var n in JSDOC.Parser.rename) {
				if (symbol.alias.indexOf(n) == 0) {
					if (symbol.name == symbol.alias) {
						symbol.name = symbol.name.replace(n, JSDOC.Parser.rename[n]);
					}
					symbol.alias = symbol.alias.replace(n, JSDOC.Parser.rename[n]);
				}
			}
		}

		if (JSDOC.opt.S) {
			if (typeof JSDOC.Parser.secureModules == "undefined") JSDOC.Parser.secureModules = {};
			if (/^exports\./.test(symbol.alias)) {
				symbol.srcFile.match(/(^|[\\\/])([^\\\/]+)\.js/i);
				var fileNS = RegExp.$2;

				// need to create the namespace associated with this file first
				if (!JSDOC.Parser.secureModules[fileNS]) {
					JSDOC.Parser.secureModules[fileNS] = 1;
					var nsSymbol = new JSDOC.Symbol(fileNS, [], "GLOBAL", new JSDOC.DocComment(""));
					nsSymbol.isNamespace = true;
					nsSymbol.srcFile = "";
					nsSymbol.isPrivate = false;
					nsSymbol.srcFile = symbol.srcFile;
					nsSymbol.desc = (JSDOC.Parser.symbols.getSymbol(symbol.srcFile) || {desc: ""}).desc;
					JSDOC.Parser.addSymbol(nsSymbol);
				}

				symbol.alias = symbol.alias.replace(/^exports\./, fileNS + '.');
				symbol.name = symbol.name.replace(/^exports\./, '');
				symbol.memberOf = fileNS;
				symbol.isStatic = true;
			}
		}

		// if a symbol alias is documented more than once the first one with the user docs wins
		if (JSDOC.Parser.symbols.hasSymbol(symbol.alias)) {
 			var oldSymbol = JSDOC.Parser.symbols.getSymbol(symbol.alias);
			if (oldSymbol.comment.isUserComment) {
				if (JSDOC.opt.m) return;
				if (symbol.comment.isUserComment) { // old and new are both documented
					LOG.warn("The symbol '"+symbol.alias+"' is documented more than once.");
					return;
				}
				else { // old is documented but new isn't
					return;
				}
			}
		}

		// we don't document anonymous things
		if (JSDOC.Parser.conf.ignoreAnonymous && symbol.name.match(/\$anonymous\b/)) return;

		// uderscored things may be treated as if they were marked private, this cascades
		if (JSDOC.Parser.conf.treatUnderscoredAsPrivate && symbol.name.match(/[.#-]_[^.#-]+$/)) {
			if (!symbol.comment.getTag("public").length > 0) symbol.isPrivate = true;
		}

		// -p flag is required to document private things
		if (!JSDOC.opt.p && symbol.isPrivate) return; // issue #161 fixed by mcbain.asm

		// ignored things are not documented, this doesn't cascade
		if (symbol.isIgnored) return;
		JSDOC.Parser.symbols.addSymbol(symbol);
	},

	addBuiltin: function(name) {
		var builtin = new JSDOC.Symbol(name, [], "CONSTRUCTOR", new JSDOC.DocComment(""));
		builtin.isNamespace = true;
		builtin.srcFile = "";
		builtin.isPrivate = false;
		JSDOC.Parser.addSymbol(builtin);
		return builtin;
	},

	init: function() {
		JSDOC.Parser.symbols = new JSDOC.SymbolSet();
		JSDOC.Parser.walker = new JSDOC.Walker();
	},

	finish: function() {
		JSDOC.Parser.symbols.relate();

		// make a litle report about what was found
		if (JSDOC.Parser.conf.explain) {
			var symbols = JSDOC.Parser.symbols.toArray();
			var srcFile = "";
			for (var i = 0, l = symbols.length; i < l; i++) {
				var symbol = symbols[i];
				if (srcFile != symbol.srcFile) {
					srcFile = symbol.srcFile;
					print("\n"+srcFile+"\n-------------------");
				}
				print(i+":\n  alias => "+symbol.alias + "\n  name => "+symbol.name+ "\n  isa => "+symbol.isa + "\n  memberOf => " + symbol.memberOf + "\n  isStatic => " + symbol.isStatic + ",  isInner => " + symbol.isInner+ ",  isPrivate => " + symbol.isPrivate);
			}
			print("-------------------\n");
		}
	}
}

JSDOC.Parser.parse = function(/**JSDOC.TokenStream*/ts, /**String*/srcFile) {
	JSDOC.Symbol.srcFile = (srcFile || "");
	JSDOC.DocComment.shared = ""; // shared comments don't cross file boundaries

	if (!JSDOC.Parser.walker) JSDOC.Parser.init();
	JSDOC.Parser.walker.walk(ts); // adds to our symbols

	// filter symbols by option
	for (var p = JSDOC.Parser.symbols._index.first(); p; p = JSDOC.Parser.symbols._index.next()) {
		var symbol = p.value;

		if (!symbol) continue;

		if (symbol.is("FILE") || symbol.is("GLOBAL")) {
			continue;
		}
		else if (!JSDOC.opt.a && !symbol.comment.isUserComment) {
			JSDOC.Parser.symbols.deleteSymbol(symbol.alias);
		}

		if (/#$/.test(symbol.alias)) { // we don't document prototypes
			JSDOC.Parser.symbols.deleteSymbol(symbol.alias);
		}
	}

	return JSDOC.Parser.symbols.toArray();
}
<commit_msg>jsdoc: change parser to allow for multiply symbols of the same name.<commit_after>if (typeof JSDOC == "undefined") JSDOC = {};

/**
	@namespace
	@requires JSDOC.Walker
	@requires JSDOC.Symbol
	@requires JSDOC.DocComment
*/
JSDOC.Parser = {
	conf: {
		ignoreCode:               JSDOC.opt.n,
		ignoreAnonymous:           true, // factory: true
		treatUnderscoredAsPrivate: true, // factory: true
		explain:                   false // factory: false
	},

	addSymbol: function(symbol) {

		if (JSDOC.Parser.rename) {
			for (var n in JSDOC.Parser.rename) {
				if (symbol.alias.indexOf(n) == 0) {
					if (symbol.name == symbol.alias) {
						symbol.name = symbol.name.replace(n, JSDOC.Parser.rename[n]);
					}
					symbol.alias = symbol.alias.replace(n, JSDOC.Parser.rename[n]);
				}
			}
		}

		if (JSDOC.opt.S) {
			if (typeof JSDOC.Parser.secureModules == "undefined") JSDOC.Parser.secureModules = {};
			if (/^exports\./.test(symbol.alias)) {
				symbol.srcFile.match(/(^|[\\\/])([^\\\/]+)\.js/i);
				var fileNS = RegExp.$2;

				// need to create the namespace associated with this file first
				if (!JSDOC.Parser.secureModules[fileNS]) {
					JSDOC.Parser.secureModules[fileNS] = 1;
					var nsSymbol = new JSDOC.Symbol(fileNS, [], "GLOBAL", new JSDOC.DocComment(""));
					nsSymbol.isNamespace = true;
					nsSymbol.srcFile = "";
					nsSymbol.isPrivate = false;
					nsSymbol.srcFile = symbol.srcFile;
					nsSymbol.desc = (JSDOC.Parser.symbols.getSymbol(symbol.srcFile) || {desc: ""}).desc;
					JSDOC.Parser.addSymbol(nsSymbol);
				}

				symbol.alias = symbol.alias.replace(/^exports\./, fileNS + '.');
				symbol.name = symbol.name.replace(/^exports\./, '');
				symbol.memberOf = fileNS;
				symbol.isStatic = true;
			}
		}

		// if a symbol alias is documented more than once, add ^n to its alias:
		if (JSDOC.Parser.symbols.hasSymbol(symbol.alias)) {
			var num = 0;
			while (JSDOC.Parser.symbols.hasSymbol(symbol.alias + '^' + num)) {
				num++;
			}
			symbol.alias = symbol.alias + '^' + num;
		}

		// we don't document anonymous things
		if (JSDOC.Parser.conf.ignoreAnonymous && symbol.name.match(/\$anonymous\b/)) return;

		// uderscored things may be treated as if they were marked private, this cascades
		if (JSDOC.Parser.conf.treatUnderscoredAsPrivate && symbol.name.match(/[.#-]_[^.#-]+$/)) {
			if (!symbol.comment.getTag("public").length > 0) symbol.isPrivate = true;
		}

		// -p flag is required to document private things
		if (!JSDOC.opt.p && symbol.isPrivate) return; // issue #161 fixed by mcbain.asm

		// ignored things are not documented, this doesn't cascade
		if (symbol.isIgnored) return;
		JSDOC.Parser.symbols.addSymbol(symbol);
	},

	addBuiltin: function(name) {
		var builtin = new JSDOC.Symbol(name, [], "CONSTRUCTOR", new JSDOC.DocComment(""));
		builtin.isNamespace = true;
		builtin.srcFile = "";
		builtin.isPrivate = false;
		JSDOC.Parser.addSymbol(builtin);
		return builtin;
	},

	init: function() {
		JSDOC.Parser.symbols = new JSDOC.SymbolSet();
		JSDOC.Parser.walker = new JSDOC.Walker();
	},

	finish: function() {
		JSDOC.Parser.symbols.relate();

		// make a litle report about what was found
		if (JSDOC.Parser.conf.explain) {
			var symbols = JSDOC.Parser.symbols.toArray();
			var srcFile = "";
			for (var i = 0, l = symbols.length; i < l; i++) {
				var symbol = symbols[i];
				if (srcFile != symbol.srcFile) {
					srcFile = symbol.srcFile;
					print("\n"+srcFile+"\n-------------------");
				}
				print(i+":\n  alias => "+symbol.alias + "\n  name => "+symbol.name+ "\n  isa => "+symbol.isa + "\n  memberOf => " + symbol.memberOf + "\n  isStatic => " + symbol.isStatic + ",  isInner => " + symbol.isInner+ ",  isPrivate => " + symbol.isPrivate);
			}
			print("-------------------\n");
		}
	}
}

JSDOC.Parser.parse = function(/**JSDOC.TokenStream*/ts, /**String*/srcFile) {
	JSDOC.Symbol.srcFile = (srcFile || "");
	JSDOC.DocComment.shared = ""; // shared comments don't cross file boundaries

	if (!JSDOC.Parser.walker) JSDOC.Parser.init();
	JSDOC.Parser.walker.walk(ts); // adds to our symbols

	// filter symbols by option
	for (var p = JSDOC.Parser.symbols._index.first(); p; p = JSDOC.Parser.symbols._index.next()) {
		var symbol = p.value;

		if (!symbol) continue;

		if (symbol.is("FILE") || symbol.is("GLOBAL")) {
			continue;
		}
		else if (!JSDOC.opt.a && !symbol.comment.isUserComment) {
			JSDOC.Parser.symbols.deleteSymbol(symbol.alias);
		}

		if (/#$/.test(symbol.alias)) { // we don't document prototypes
			JSDOC.Parser.symbols.deleteSymbol(symbol.alias);
		}
	}

	return JSDOC.Parser.symbols.toArray();
}
<|endoftext|>


<commit_before>var GmailChannel = (function() {
  'use strict'
  /**
  *
  * GmailChannel - Pub/Sub & Middleware framework for easy dealing with Gmails by Channel
  *
  * GmailChannel provide a easy way to filter out the emails in gmail by search options to a named Channel,
  * then you could Sub to this Channel, and use Middleware to process them.
  *
  * Github - https://github.com/zixia/gas-gmail-channel
  *
  * Example:
  ```javascript
  if ((typeof GmailChannel)==='undefined') { // GmailChannel Initialization. (only if not initialized yet.)
    eval(UrlFetchApp.fetch('https://raw.githubusercontent.com/zixia/gas-gmail-channel/master/src/gas-gmail-channel-lib.js').getContentText())
  } // Class GmailChannel is ready for use now!

  var testChannel = new GmailChannel({
    keywords: ['the', '-abcdefghijilmn']
    , labels: ['inbox', '-trash']
    , limit: 1
    , doneLabel: 'OutOfGmailChannel'
  })

  testChannel.use(
    function (req, res, next) {
      Logger.log(req.thread.getFirstMessageSubject())
      req.data = 'set'
      next()
    }
    , function (req, res, next) {
      Logger.log('req.data got: ' + req.data)
      // NO next() here
    }
    , function (req, res,next) {
      throw Error('should not run to here')
    }
  )

  testChannel.done()
  ```
  */

  var VERSION = '0.1.0'

  var DEFAULT = {
    name: 'GmailChannel v' + VERSION
    , labels: ['inbox', '-trash']
    , dayspan: '365'   // only check in last 365 days
    , limit: 500       // default max return 999 threads
    , doneLabel: null
    , res: {}
    , keywords: []
  }

  /************************************************************************************************
  *
  * Class GmailChannel
  * ------------------
  *
  * @param object optoins
  *    options.name string Channel Name
  *    options.labels array gmail channel labels. default: label:inbox label:unread -label:trash
  *    optoins.keywords array gmail search keywords
  *
  *    options.query string gmail search query string
  *    options.dayspan number newer_than:{{dayspan}}d
  *    options.limit number no more then {{limit}} results
  *
  *    options.doneLabel message labeled with {{doneLabel}} will be ignored
  */
  var GmailChannel = function (options) {

    if (!options) throw Error('options must be defined for GmailChannel!')

    var dayspan = options.dayspan || DEFAULT.dayspan

    /**
    *
    * 1. if we don't set doneLabel, then it should be the default label name.
    * 2. but if we set it to null, then we will not use doneLabel anymore.
    *
    * so we use "typeof options.doneLabel === 'undefined'" to check if use defined it.
    *
    */
    if ((typeof options.doneLabel)==='undefined') {
      var doneLabel = options.doneLabel || DEFAULT.doneLabel
    } else {
      doneLabel = options.doneLabel
    }

    var labels = options.labels || DEFAULT.labels
    var keywords = options.keywords || DEFAULT.keywords
    if (!(labels instanceof Array) || !(keywords instanceof Array)) {
      throw Error('options.keywords or options.labels must be array for GmailChannel!')
    }

    var limit = options.limit || DEFAULT.limit
    if (limit%1 !== 0 || limit>500) throw Error('limit must be integer(<500) for GmailChannle! error: limit=' + limit );

    var name = options.name || DEFAULT.name
    var res = options.res || DEFAULT.res

    /////////////////////////////////////////////////////
    //
    // queryString start building. to filter out email

    // 1. query
    var queryString  = options.query || ''
    // 2. timespan
    queryString += ' ' + 'newer_than:' + dayspan + 'd'
    // 3. -doneLabel
    if (doneLabel) queryString += ' ' + '-label:' + doneLabel
    // 4. keywords
    keywords.forEach(function (k) {
        queryString += ' ' + k
    })
    // 5. labels
    labels.forEach(function (l) {
      if (!l) return // tolearnt empty label

      var minusLabel = /^-(.+)$/.exec(l)
      if (minusLabel) {
        queryString += ' ' + '-label:' + minusLabel[1]
      } else {
        queryString += ' ' + 'label:' + l
      }
    })

    // queryString has been built.
    //
    ///////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////
    //
    // UPPER_CASE varibles for quota in instance methods

    if (doneLabel) {
      var DONE_LABEL = GmailApp.getUserLabelByName(doneLabel)
      if (!DONE_LABEL) DONE_LABEL = GmailApp.createLabel(doneLabel)
    } else {
      DONE_LABEL = null
    }

    var NAME = name
    var LIMIT = limit
    var QUERY_STRING = queryString
    var RES = res
    var MIDDLEWARES = [] // for use() use

    // UPPER_CASE variables set
    //
    ///////////////////////////////////////////////////////

    /**
    * Instance of this
    */

    this.use = use
    this.done = done

    this.getName = getName
    this.getQueryString = function () { return QUERY_STRING }
    this.getMiddlewares = function () { return MIDDLEWARES }

    return this

    ////////////////////////////////////////////////////////
    //
    // Instance Methods

    function use(middleware) {

      if (middleware instanceof Array) return middleware.map(function (m) { return use(m) })

      if (!(middleware instanceof Function)) throw Error('must use function(s) for middleware! error[' + middleware + ']')

      MIDDLEWARES.push(middleware)

      /**
      *
      * in case of use(fn1, fn2....)
      *
      */
      if (arguments.length>1) {
        return Array.prototype.slice.call(arguments, 1).map(function (m) { return use(m) })
      }

      return true
    }

    function done() {

      var mailThreads = getNewThreads(LIMIT)

      for (var i=0; i<mailThreads.length; i++) {

        var res = RES
        var req = {
          getChannelName: getName()
          , getThread: (function (t) { return function () { return t } })(mailThreads[i]) // closure for the furture possible run in nodejs, because of async call back
//          thread: mailThreads[i] // Deprecated
        }

        for (var j=0; j<MIDDLEWARES.length; j++) {

          var middleware = MIDDLEWARES[j]

          var isNextCalled = false

          middleware(req, res, function () {
            isNextCalled = true
          })

          if (!isNextCalled) {
            // loop end, because middleware did not call next
            break
          }

        } // END for loop of MIDDLEWARES

        if (DONE_LABEL) mailThreads[i].addLabel(DONE_LABEL)

      } // END for loop of mailThreads

    }

    function getName() { return NAME }

    /**
    *
    * return arrary[]
    *
    */
    function getNewThreads(limit) {

      if (!limit) limit = 500 // 500 is the max number that gmail permit.
      if (!QUERY_STRING) return []

      // search for mails
      var threads = GmailApp.search(QUERY_STRING, 0, limit)

      return threads
    }
  }

  /**
  * Class GmailChannel
  */

  GmailChannel.getVersion = getVersion

  return GmailChannel

  ////////////////////////////////////////////////
  //                                           //
  // Class Static Methods                     //
  //                                         //
  ////////////////////////////////////////////

  function getVersion() {
    return VERSION
  }

}())
<commit_msg>fix res.getName: should return function instead of function result<commit_after>var GmailChannel = (function() {
  'use strict'
  /**
  *
  * GmailChannel - Pub/Sub & Middleware framework for easy dealing with Gmails by Channel
  *
  * GmailChannel provide a easy way to filter out the emails in gmail by search options to a named Channel,
  * then you could Sub to this Channel, and use Middleware to process them.
  *
  * Github - https://github.com/zixia/gas-gmail-channel
  *
  * Example:
  ```javascript
  if ((typeof GmailChannel)==='undefined') { // GmailChannel Initialization. (only if not initialized yet.)
    eval(UrlFetchApp.fetch('https://raw.githubusercontent.com/zixia/gas-gmail-channel/master/src/gas-gmail-channel-lib.js').getContentText())
  } // Class GmailChannel is ready for use now!

  var testChannel = new GmailChannel({
    keywords: ['the', '-abcdefghijilmn']
    , labels: ['inbox', '-trash']
    , limit: 1
    , doneLabel: 'OutOfGmailChannel'
  })

  testChannel.use(
    function (req, res, next) {
      Logger.log(req.thread.getFirstMessageSubject())
      req.data = 'set'
      next()
    }
    , function (req, res, next) {
      Logger.log('req.data got: ' + req.data)
      // NO next() here
    }
    , function (req, res,next) {
      throw Error('should not run to here')
    }
  )

  testChannel.done()
  ```
  */

  var VERSION = '0.1.0'

  var DEFAULT = {
    name: 'GmailChannel v' + VERSION
    , labels: ['inbox', '-trash']
    , dayspan: '365'   // only check in last 365 days
    , limit: 500       // default max return 999 threads
    , doneLabel: null
    , res: {}
    , keywords: []
  }

  /************************************************************************************************
  *
  * Class GmailChannel
  * ------------------
  *
  * @param object optoins
  *    options.name string Channel Name
  *    options.labels array gmail channel labels. default: label:inbox label:unread -label:trash
  *    optoins.keywords array gmail search keywords
  *
  *    options.query string gmail search query string
  *    options.dayspan number newer_than:{{dayspan}}d
  *    options.limit number no more then {{limit}} results
  *
  *    options.doneLabel message labeled with {{doneLabel}} will be ignored
  */
  var GmailChannel = function (options) {

    if (!options) throw Error('options must be defined for GmailChannel!')

    var dayspan = options.dayspan || DEFAULT.dayspan

    /**
    *
    * 1. if we don't set doneLabel, then it should be the default label name.
    * 2. but if we set it to null, then we will not use doneLabel anymore.
    *
    * so we use "typeof options.doneLabel === 'undefined'" to check if use defined it.
    *
    */
    if ((typeof options.doneLabel)==='undefined') {
      var doneLabel = options.doneLabel || DEFAULT.doneLabel
    } else {
      doneLabel = options.doneLabel
    }

    var labels = options.labels || DEFAULT.labels
    var keywords = options.keywords || DEFAULT.keywords
    if (!(labels instanceof Array) || !(keywords instanceof Array)) {
      throw Error('options.keywords or options.labels must be array for GmailChannel!')
    }

    var limit = options.limit || DEFAULT.limit
    if (limit%1 !== 0 || limit>500) throw Error('limit must be integer(<500) for GmailChannle! error: limit=' + limit );

    var name = options.name || DEFAULT.name
    var res = options.res || DEFAULT.res

    /////////////////////////////////////////////////////
    //
    // queryString start building. to filter out email

    // 1. query
    var queryString  = options.query || ''
    // 2. timespan
    queryString += ' ' + 'newer_than:' + dayspan + 'd'
    // 3. -doneLabel
    if (doneLabel) queryString += ' ' + '-label:' + doneLabel
    // 4. keywords
    keywords.forEach(function (k) {
        queryString += ' ' + k
    })
    // 5. labels
    labels.forEach(function (l) {
      if (!l) return // tolearnt empty label

      var minusLabel = /^-(.+)$/.exec(l)
      if (minusLabel) {
        queryString += ' ' + '-label:' + minusLabel[1]
      } else {
        queryString += ' ' + 'label:' + l
      }
    })

    // queryString has been built.
    //
    ///////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////
    //
    // UPPER_CASE varibles for quota in instance methods

    if (doneLabel) {
      var DONE_LABEL = GmailApp.getUserLabelByName(doneLabel)
      if (!DONE_LABEL) DONE_LABEL = GmailApp.createLabel(doneLabel)
    } else {
      DONE_LABEL = null
    }

    var NAME = name
    var LIMIT = limit
    var QUERY_STRING = queryString
    var RES = res
    var MIDDLEWARES = [] // for use() use

    // UPPER_CASE variables set
    //
    ///////////////////////////////////////////////////////

    /**
    * Instance of this
    */

    this.use = use
    this.done = done

    this.getName = getName
    this.getQueryString = function () { return QUERY_STRING }
    this.getMiddlewares = function () { return MIDDLEWARES }

    return this

    ////////////////////////////////////////////////////////
    //
    // Instance Methods

    function use(middleware) {

      if (middleware instanceof Array) return middleware.map(function (m) { return use(m) })

      if (!(middleware instanceof Function)) throw Error('must use function(s) for middleware! error[' + middleware + ']')

      MIDDLEWARES.push(middleware)

      /**
      *
      * in case of use(fn1, fn2....)
      *
      */
      if (arguments.length>1) {
        return Array.prototype.slice.call(arguments, 1).map(function (m) { return use(m) })
      }

      return true
    }

    function done() {

      var mailThreads = getNewThreads(LIMIT)

      for (var i=0; i<mailThreads.length; i++) {

        var res = RES
        var req = {
          getChannelName: getName
          , getThread: (function (t) { return function () { return t } })(mailThreads[i]) // closure for the furture possible run in nodejs, because of async call back
//          thread: mailThreads[i] // Deprecated
        }

        for (var j=0; j<MIDDLEWARES.length; j++) {

          var middleware = MIDDLEWARES[j]

          var isNextCalled = false

          middleware(req, res, function () {
            isNextCalled = true
          })

          if (!isNextCalled) {
            // loop end, because middleware did not call next
            break
          }

        } // END for loop of MIDDLEWARES

        if (DONE_LABEL) mailThreads[i].addLabel(DONE_LABEL)

      } // END for loop of mailThreads

    }

    function getName() { return NAME }

    /**
    *
    * return arrary[]
    *
    */
    function getNewThreads(limit) {

      if (!limit) limit = 500 // 500 is the max number that gmail permit.
      if (!QUERY_STRING) return []

      // search for mails
      var threads = GmailApp.search(QUERY_STRING, 0, limit)

      return threads
    }
  }

  /**
  * Class GmailChannel
  */

  GmailChannel.getVersion = getVersion

  return GmailChannel

  ////////////////////////////////////////////////
  //                                           //
  // Class Static Methods                     //
  //                                         //
  ////////////////////////////////////////////

  function getVersion() {
    return VERSION
  }

}())
<|endoftext|>


<commit_before>'use strict';

var Cesium = require('cesium');
var Promise = require('bluebird');
var requestPromise = require('request-promise');

var defined = Cesium.defined;

var dateLog = require('../lib/dateLog');
var checkStatus = require('../lib/checkStatus');
var Settings = require('../lib/Settings');

module.exports = stalePullRequest;

if (require.main === module) {
    Settings.loadRepositoriesSettings('./config.json')
        .then(stalePullRequest)
        .catch(function (err) {
            dateLog('Received error: ' + err);
        });
}
/** Bump stale pull requests for each repository
 *
 * @param {String[]} repositoryNames Names of repositories
 * @returns {Promise<Array<http.IncomingMessage | undefined> | undefined>} Promise to an array of incoming messages
 */
function stalePullRequest(repositoryNames) {
    return Promise.all(
        repositoryNames.map(function (repositoryName) {
            var repositorySettings = Settings.repositories[repositoryName];
            if (!defined(repositorySettings.bumpStalePullRequests)) {
                dateLog('Repository ' + repositoryName + ' does not have `bumpStalePullRequests` turned on');
                return Promise.resolve();
            }
            return stalePullRequest.implementation(repositorySettings.bumpStalePullRequestsUrl + '?sort=updated&direction=asc',
                repositorySettings.gitHubToken, repositorySettings.maxDaysSinceUpdate);
        })
    );
}

/** Implementation
 *
 * @param {String} pullRequestsUrl Base url to list pull requests https://developer.github.com/v3/pulls/#list-pull-requests
 * @param {String} gitHubToken Token to verify with github
 * @param {Number} maxDaysSinceUpdate Maximum days since a PR has been updated
 * @return {Promise<Array<http.IncomingMessage | undefined>>} Promise to an array of incoming messages
 */
stalePullRequest.implementation = function (pullRequestsUrl, gitHubToken, maxDaysSinceUpdate) {
    var headers = {
        'User-Agent': 'cesium-concierge',
        Authorization: 'token ' + gitHubToken
    };
    maxDaysSinceUpdate = defined(maxDaysSinceUpdate) ? maxDaysSinceUpdate : 30;
    return requestPromise.get({
        uri: pullRequestsUrl,
        headers: headers,
        json: true,
        resolveWithFullResponse: true
    })
    .then(function (pullRequestsJsonResponse) {
        return checkStatus(pullRequestsJsonResponse);
    })
    .then(function (pullRequestsJsonResponse) {
        var thankYou = 'Thank you for the pull request!\n\n';
        var firstMessage = thankYou + 'I noticed that this pull request hasn\'t been updated in ' + maxDaysSinceUpdate + ' days. ' +
            'If it is waiting on a review or changes from a previous review, could someone please take a look?\n' +
            'If I don’t see a commit or comment in the next ' + maxDaysSinceUpdate + ' days, we may want to close this pull request to keep things tidy.\n\n' +
            '__I am a bot who helps facilitate your development!__ Thanks again for contributing.';

        var alreadyBumpedMessage = thankYou + 'Looks like this pull request hasn\'t been updated in ' + maxDaysSinceUpdate + ' days since I last commented.\n' +
            'To keep things tidy should this be closed? Perhaps keep the branch and submit an issue?\n\n' +
            '__I am a bot who helps facilitate your development!__ Have a nice day.\n';

        return Promise.each(pullRequestsJsonResponse.body, function(pullRequest) {
            var lastUpdate = new Date(pullRequest.updated_at);
            var commentsUrl = pullRequest.comments_url;
            if (stalePullRequest.dateIsOlderThan(lastUpdate, maxDaysSinceUpdate)) {
                // Check if last post was cesium-concierge
                return requestPromise.get({
                    uri: commentsUrl,
                    headers: headers,
                    json: true,
                    resolveWithFullResponse: true
                })
                .then(function (commentsJsonResponse) {
                    return checkStatus(commentsJsonResponse);
                })
                .then(function (commentsJsonResponse) {
                    var alreadyBumped = false;
                    commentsJsonResponse.body.forEach(function (comment) {
                        if (comment.user.login === 'cesium-concierge') {
                            alreadyBumped = true;
                        }
                    });
                    var message = alreadyBumped ? alreadyBumpedMessage : firstMessage;
                    return requestPromise.post({
                        uri: commentsUrl,
                        headers: headers,
                        body: {
                            body: message
                        },
                        json: true,
                        resolveWithFullResponse: true
                    });
                });
            }
            dateLog('Pull request at ' + pullRequest.url + ' was not older than ' + maxDaysSinceUpdate + ' days.');
        });
    });
};

/** Time in seconds from date and now
 *
 * @param {Date} date Date to compare
 * @param {Number} days Days from Date.now() to compare
 * @return {boolean} True if date is older than `days` ago
 */
stalePullRequest.dateIsOlderThan = function (date, days) {
    return date.getTime() + days * 86400000 <= Date.now();
};
<commit_msg>Add dateLog line<commit_after>'use strict';

var Cesium = require('cesium');
var Promise = require('bluebird');
var requestPromise = require('request-promise');

var defined = Cesium.defined;

var dateLog = require('../lib/dateLog');
var checkStatus = require('../lib/checkStatus');
var Settings = require('../lib/Settings');

module.exports = stalePullRequest;

if (require.main === module) {
    Settings.loadRepositoriesSettings('./config.json')
        .then(stalePullRequest)
        .catch(function (err) {
            dateLog('Received error: ' + err);
        });
}
/** Bump stale pull requests for each repository
 *
 * @param {String[]} repositoryNames Names of repositories
 * @returns {Promise<Array<http.IncomingMessage | undefined> | undefined>} Promise to an array of incoming messages
 */
function stalePullRequest(repositoryNames) {
    return Promise.all(
        repositoryNames.map(function (repositoryName) {
            var repositorySettings = Settings.repositories[repositoryName];
            if (!defined(repositorySettings.bumpStalePullRequests)) {
                dateLog('Repository ' + repositoryName + ' does not have `bumpStalePullRequests` turned on');
                return Promise.resolve();
            }
            return stalePullRequest.implementation(repositorySettings.bumpStalePullRequestsUrl + '?sort=updated&direction=asc',
                repositorySettings.gitHubToken, repositorySettings.maxDaysSinceUpdate);
        })
    );
}

/** Implementation
 *
 * @param {String} pullRequestsUrl Base url to list pull requests https://developer.github.com/v3/pulls/#list-pull-requests
 * @param {String} gitHubToken Token to verify with github
 * @param {Number} maxDaysSinceUpdate Maximum days since a PR has been updated
 * @return {Promise<Array<http.IncomingMessage | undefined>>} Promise to an array of incoming messages
 */
stalePullRequest.implementation = function (pullRequestsUrl, gitHubToken, maxDaysSinceUpdate) {
    var headers = {
        'User-Agent': 'cesium-concierge',
        Authorization: 'token ' + gitHubToken
    };
    maxDaysSinceUpdate = defined(maxDaysSinceUpdate) ? maxDaysSinceUpdate : 30;
    return requestPromise.get({
        uri: pullRequestsUrl,
        headers: headers,
        json: true,
        resolveWithFullResponse: true
    })
    .then(function (pullRequestsJsonResponse) {
        return checkStatus(pullRequestsJsonResponse);
    })
    .then(function (pullRequestsJsonResponse) {
        var thankYou = 'Thank you for the pull request!\n\n';
        var firstMessage = thankYou + 'I noticed that this pull request hasn\'t been updated in ' + maxDaysSinceUpdate + ' days. ' +
            'If it is waiting on a review or changes from a previous review, could someone please take a look?\n' +
            'If I don’t see a commit or comment in the next ' + maxDaysSinceUpdate + ' days, we may want to close this pull request to keep things tidy.\n\n' +
            '__I am a bot who helps facilitate your development!__ Thanks again for contributing.';

        var alreadyBumpedMessage = thankYou + 'Looks like this pull request hasn\'t been updated in ' + maxDaysSinceUpdate + ' days since I last commented.\n' +
            'To keep things tidy should this be closed? Perhaps keep the branch and submit an issue?\n\n' +
            '__I am a bot who helps facilitate your development!__ Have a nice day.\n';

        return Promise.each(pullRequestsJsonResponse.body, function(pullRequest) {
            var lastUpdate = new Date(pullRequest.updated_at);
            var commentsUrl = pullRequest.comments_url;
            if (stalePullRequest.dateIsOlderThan(lastUpdate, maxDaysSinceUpdate)) {
                // Check if last post was cesium-concierge
                return requestPromise.get({
                    uri: commentsUrl,
                    headers: headers,
                    json: true,
                    resolveWithFullResponse: true
                })
                .then(function (commentsJsonResponse) {
                    return checkStatus(commentsJsonResponse);
                })
                .then(function (commentsJsonResponse) {
                    var alreadyBumped = false;
                    commentsJsonResponse.body.forEach(function (comment) {
                        if (comment.user.login === 'cesium-concierge') {
                            alreadyBumped = true;
                        }
                    });
                    var message = alreadyBumped ? alreadyBumpedMessage : firstMessage;
                    dateLog('Posting comment to ' + commentsUrl);
                    return requestPromise.post({
                        uri: commentsUrl,
                        headers: headers,
                        body: {
                            body: message
                        },
                        json: true,
                        resolveWithFullResponse: true
                    });
                });
            }
            dateLog('Pull request at ' + pullRequest.url + ' was not older than ' + maxDaysSinceUpdate + ' days.');
        });
    });
};

/** Time in seconds from date and now
 *
 * @param {Date} date Date to compare
 * @param {Number} days Days from Date.now() to compare
 * @return {boolean} True if date is older than `days` ago
 */
stalePullRequest.dateIsOlderThan = function (date, days) {
    return date.getTime() + days * 86400000 <= Date.now();
};
<|endoftext|>


<commit_before>/**
 * On load get all the list of commands
 */
const commands = {};
const settings = require('./../settings.json');
const list = require('./commandlist');
const getcommands = () => {
    Object.entries(list).forEach(
        ([key, value]) => {
            commands[key] = require('./commands/' + value);
        }
    );
}
getcommands();
const getError = function (err) {
    if (settings.debug)
        return err.stack
    else
        return err
}
module.exports = {
    execute: function (cmd, params, message) {
        if (commands[cmd]) {
            let bm = message.channel.messages.filter(m => (m.author.id == message.client.user.id) && (m.createdTimestamp > Date.now() - 5000)).size
            console.log(bm);
            if (bm > 2 && !(message.channel.name.toLowerCase().includes("bot") || message.channel.name.toLowerCase().includes("command") || message.channel.name.toLowerCase().includes("test")))
                return message.reply("Dont abuse me");
            commands[cmd].execute(params, message).catch(err => message.channel.send(getError(err), { code: 'error', split: true }))//.then(message.delete(3000));
        }
        else if (cmd === 'help')
            help(message);
    }
}
function help(message) {
    let res = "";
    let carray = [];
    Object.entries(commands).forEach(
        ([key, value]) => {
            carray.push({ key, value });
        }
    )
    let ownerc = Object.values(carray).filter(c => c.value.owner);
    let modc = Object.values(carray).filter(c => c.value.mod);
    let func = Object.values(carray).filter(c => c.value.fun);
    let revivec = Object.values(carray).filter(c => c.value.custom);
    if (settings.owners.includes(message.author.id)) {
        res = res + "**Owner Only Commands:** \n\n";
        ownerc.map(
            (t) => {
                res = res + "**" + t.key + "** : " + t.value.description + "\n";
            }
        )
    }
    res = res + "\n**Moderator/Admin Only Commands:** \n\n";
    modc.map(
        (t) => {
            res = res + "**" + t.key + "** : " + t.value.description + "\n";
        }
    )
    res = res + "\n**Fun/Utility Commands:** \n\n";
    func.map(
        (t) => {
            res = res + "**" + t.key + "** : " + t.value.description + "\n";
        }
    )
    res = res + "\n**ReviveNetwork Commands:** \n\n";
    revivec.map(
        (t) => {
            res = res + "**" + t.key + "** : " + t.value.description + "\n";
        }
    )
    message.author.send(res, { split: true });
}
<commit_msg>Prevent bot abuse<commit_after>/**
 * On load get all the list of commands
 */
const commands = {};
const settings = require('./../settings.json');
const list = require('./commandlist');
const getcommands = () => {
    Object.entries(list).forEach(
        ([key, value]) => {
            commands[key] = require('./commands/' + value);
        }
    );
}
getcommands();
const getError = function (err) {
    if (settings.debug)
        return err.stack
    else
        return err
}
module.exports = {
    execute: function (cmd, params, message) {
        if (commands[cmd]) {
            let bm = message.channel.messages.filter(m => (m.author.id == message.client.user.id) && (m.createdTimestamp > Date.now() - 15000)).size

            if (bm >=2 && commands[cmd].fun && !(message.channel.name.toLowerCase().includes("bot") || message.channel.name.toLowerCase().includes("command") || message.channel.name.toLowerCase().includes("test")))
                return message.reply("Dont abuse me");
            commands[cmd].execute(params, message).catch(err => message.channel.send(getError(err), { code: 'error', split: true }))//.then(message.delete(3000));
        }
        else if (cmd === 'help')
            help(message);
    }
}
function help(message) {
    let res = "";
    let carray = [];
    Object.entries(commands).forEach(
        ([key, value]) => {
            carray.push({ key, value });
        }
    )
    let ownerc = Object.values(carray).filter(c => c.value.owner);
    let modc = Object.values(carray).filter(c => c.value.mod);
    let func = Object.values(carray).filter(c => c.value.fun);
    let revivec = Object.values(carray).filter(c => c.value.custom);
    if (settings.owners.includes(message.author.id)) {
        res = res + "**Owner Only Commands:** \n\n";
        ownerc.map(
            (t) => {
                res = res + "**" + t.key + "** : " + t.value.description + "\n";
            }
        )
    }
    res = res + "\n**Moderator/Admin Only Commands:** \n\n";
    modc.map(
        (t) => {
            res = res + "**" + t.key + "** : " + t.value.description + "\n";
        }
    )
    res = res + "\n**Fun/Utility Commands:** \n\n";
    func.map(
        (t) => {
            res = res + "**" + t.key + "** : " + t.value.description + "\n";
        }
    )
    res = res + "\n**ReviveNetwork Commands:** \n\n";
    revivec.map(
        (t) => {
            res = res + "**" + t.key + "** : " + t.value.description + "\n";
        }
    )
    message.author.send(res, { split: true });
}
<|endoftext|>


<commit_before>// Global config
var GRAPHIC_DEFAULT_WIDTH = 600;
var MOBILE_THRESHOLD = 500;

// Global vars
var pymChild = null;
var isMobile = false;
var graphicData = null;
var graphicConfig = null;

// D3 formatters
var fmtYearAbbrev = d3.time.format('%y');
var fmtYearFull = d3.time.format('%b %Y');

var defaultColors = [ COLORS['blue3'], COLORS['red3'], COLORS['yellow3'], COLORS['orange3'], COLORS['teal3'] ];

/*
 * Initialize graphic
 */
var onWindowLoaded = function() {
    if (Modernizr.svg) {
        graphicConfig = GRAPHIC_CONFIG;
        loadLocalData(GRAPHIC_DATA);

        if (graphicConfig.timeFormatLarge) {
            fmtYearFull = d3.time.format(graphicConfig.timeFormatLarge);
        }

        if (graphicConfig.timeFormatSmall) {
            fmtYearAbbrev = d3.time.format(graphicConfig.timeFormatSmall);
        }
    } else {
        pymChild = new pym.Child({});
    }
}

/*
 * Load graphic data from a local source.
 */
var loadLocalData = function(data) {
    graphicData = data;

    if (graphicData[0].date) {
        formatData();
    }

    pymChild = new pym.Child({
        renderCallback: render
    });
}

/*
 * Load graphic data from a CSV.
 */
var loadCSV = function(url) {
    d3.csv(GRAPHIC_DATA_URL, function(error, data) {
        graphicData = data;

        if (graphicData[0].date) {
            formatData();
        }

        pymChild = new pym.Child({
            renderCallback: render
        });
    });
}

/*
 * Format graphic data for processing by D3.
 */
var formatData = function() {
    graphicData.forEach(function(d) {
        var date = d3.time.format('%d/%m/%y').parse(d['date']);
        if (!date) {
            date = d3.time.format('%d/%m/%Y').parse(d['date']);
        }

        d['date'] = date;

        for (var key in d) {
            if (key != 'date') {
                d[key] = +d[key];
            }
        }
    });
}

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function(containerWidth) {
    if (!containerWidth) {
        containerWidth = GRAPHIC_DEFAULT_WIDTH;
    }

    if (containerWidth <= MOBILE_THRESHOLD) {
        isMobile = true;
    } else {
        isMobile = false;
    }

    // Render the chart!
    renderLineChart({
        container: '#graphic',
        width: containerWidth,
        data: graphicData
    });

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
}

/*
 * Render a line chart.
 */
var renderLineChart = function(config) {
    /*
     * Setup
     */
    var dateColumn = 'date';
    var valueColumn = 'amt';

    var aspectWidth = isMobile ? 4 : 16;
    var aspectHeight = isMobile ? 3 : 9;
    if ('ratio' in graphicConfig) {
        var parts = graphicConfig.ratio.split("x");
        aspectWidth = parseInt(parts[0], 10);
        aspectHeight = parseInt(parts[1], 10);
    }

    var margins = {
        top: 5,
        right: 80,
        bottom: 20,
        left: 30
    };

    if (graphicConfig.marginTop) {
        margins.top = parseInt(graphicConfig.marginTop, 10);
    }

    if (graphicConfig.marginRight) {
        margins.right = parseInt(graphicConfig.marginRight, 10);
    }

    if (graphicConfig.marginBottom) {
        margins.bottom = parseInt(graphicConfig.marginBottom, 10);
    }

    if (graphicConfig.marginLeft) {
        margins.left = parseInt(graphicConfig.marginLeft, 10);
    }

    var ticksX = parseInt(graphicConfig.ticksX || 10, 10);
    var ticksY = parseInt(graphicConfig.ticksY || 10, 10);
    var roundTicksFactor = parseInt(graphicConfig.roundTicksFactor || 5, 10);

    // Mobile
    if (isMobile) {
        ticksX = 5;
        ticksY = 5;
        margins['right'] = 25;
    }

    // Calculate actual chart dimensions
    var chartWidth = config['width'] - margins['left'] - margins['right'];
    var chartHeight = Math.ceil((config['width'] * aspectHeight) / aspectWidth) - margins['top'] - margins['bottom'];

    // Clear existing graphic (for redraw)
    var containerElement = d3.select(config['container']);
    containerElement.html('');

    var formattedData = {};

    /*
     * Restructure tabular data for easier charting.
     */
    for (var column in graphicData[0]) {
        if (column == dateColumn || column == 'x') {
            continue;
        }

        formattedData[column] = graphicData.map(function(d) {
            return {
                'x': d[dateColumn] || d['x'],
                'amt': +d[column]
            };
        });
    }

    /*
     * Create D3 scale objects.
     */

    var minY = 'minValue' in graphicConfig ? parseFloat(graphicConfig.minValue, 10) : d3.min(d3.entries(formattedData), function(c) {
        return d3.min(c['value'], function(v) {
            var n = v[valueColumn];
            return Math.floor(n / roundTicksFactor) * roundTicksFactor;
        });
    });

    var maxY = 'maxValue' in graphicConfig ? parseFloat(graphicConfig.maxValue, 10) : d3.max(d3.entries(formattedData), function(c) {
        return d3.max(c['value'], function(v) {
            var n = v[valueColumn];
            return Math.ceil(n / roundTicksFactor) * roundTicksFactor;
        });
    });

    var xFormat;
    var xScale;

    if (formattedData['date']) {
        xFormat = function(d, i) {
            if (isMobile) {
                return '\u2019' + fmtYearAbbrev(d);
            } else {
                return fmtYearFull(d);
            }
        };

        xScale = d3.time.scale()
        .domain(d3.extent(config['data'], function(d) {
            return d[dateColumn];
        }))
        .range([ 0, chartWidth ])
    } else {
        xFormat = function (d, i) {
            return d;
        };

        xScale = d3.scale.ordinal()
        .rangePoints([0, chartWidth])
        .domain(graphicData.map(function (d) {
            return d['x'];
        }))
        // .range([0, chartWidth]);
    }

    var yScale = d3.scale.linear()
        .domain([ minY, maxY ])
        .range([ chartHeight, 0 ]);

    var colorList = defaultColors;
    if ('colors' in graphicConfig) {
        colorList = graphicConfig.colors.split(/\s*,\s*/);
    }

    var colorScale = d3.scale.ordinal()
        .domain([0, colorList.length])
        .range(colorList);

    /*
     * Render the HTML legend.
     */
    var legend = containerElement.append('ul')
        .attr('class', 'key')
        .selectAll('g')
            .data(d3.entries(formattedData))
        .enter().append('li')
            .attr('class', function(d, i) {
                return 'key-item key-' + i + ' ' + classify(d['key']);
            });

    legend.append('b')
        .style('background-color', function(d) {
            return colorScale(d['key']);
        });

    legend.append('label')
        .text(function(d) {
            return d['key'];
        });

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphic-wrapper');

    var chartElement = chartWrapper.append('svg')
        .attr('width', chartWidth + margins['left'] + margins['right'])
        .attr('height', chartHeight + margins['top'] + margins['bottom'])
        .append('g')
        .attr('transform', 'translate(' + margins['left'] + ',' + margins['top'] + ')');

    /*
     * Create D3 axes.
     */
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom')
        .ticks(ticksX)
        .tickFormat(xFormat);

    var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient('left')
        .ticks(ticksY)
        .tickFormat(function (d) {
            return (graphicConfig.prefixY || '') + d + (graphicConfig.suffixY || '');
        });

    /*
     * Render axes to chart.
     */
    chartElement.append('g')
        .attr('class', 'x axis')
        .attr('transform', makeTranslate(0, chartHeight))
        .call(xAxis);

    chartElement.append('g')
        .attr('class', 'y axis')
        .call(yAxis);

    /*
     * Render grid to chart.
     */
    var xAxisGrid = function() {
        return xAxis;
    }

    var yAxisGrid = function() {
        return yAxis;
    }

    chartElement.append('g')
        .attr('class', 'x grid')
        .attr('transform', makeTranslate(0, chartHeight))
        .call(xAxisGrid()
            .tickSize(-chartHeight, 0, 0)
            .tickFormat('')
        );

    chartElement.append('g')
        .attr('class', 'y grid')
        .call(yAxisGrid()
            .tickSize(-chartWidth, 0, 0)
            .tickFormat('')
        );

    /*
     * Render lines to chart.
     */
    var line = d3.svg.line()
        .interpolate(graphicConfig.interpolate || 'monotone')
        .x(function(d) {
            return xScale(d[dateColumn] || d['x']);
        })
        .y(function(d) {
            return yScale(d[valueColumn]);
        });

    chartElement.append('g')
        .attr('class', 'lines')
        .selectAll('path')
        .data(d3.entries(formattedData))
        .enter()
        .append('path')
            .attr('class', function(d, i) {
                return 'line line-' + i + ' ' + classify(d['key']);
            })
            .attr('stroke', function(d) {
                return colorScale(d['key']);
            })
            .attr('d', function(d) {
                return line(d['value']);
            });

    chartElement.append('g')
        .attr('class', 'value')
        .selectAll('text')
        .data(d3.entries(formattedData))
        .enter().append('text')
            .attr('x', function(d, i) {
                var last = d['value'][d['value'].length - 1];

                return xScale(last[dateColumn] || last['x']) + 5;
            })
            .attr('y', function(d) {
                var last = d['value'][d['value'].length - 1];

                return yScale(last[valueColumn]) + 3;
            })
            .text(function(d) {
                var last = d['value'][d['value'].length - 1];
                var value = last[valueColumn];

                var label = last[valueColumn].toFixed(1);

                if (!isMobile) {
                    label = d['key'] + ': ' + label;
                }

                return label;
            });

    if (graphicConfig.xLabel) {
        var t = chartElement.append("text")
            .text(graphicConfig.xLabel)
            .attr("y", chartHeight + margins['bottom'] - 5)
            .attr("class", "axis-label");

        t.attr("x", (chartWidth - t.node().getComputedTextLength()) / 2)
    }

    if (graphicConfig.yLabel) {
        var t = chartElement.append("text")
            .text(graphicConfig.yLabel)
            .attr("y", -25)
            .attr("class", "axis-label");

        t.attr("x", 0 - (chartHeight / 2));
        t.attr("transform", "rotate(-90)");
    }
}

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;
<commit_msg>* Include margins<commit_after>// Global config
var GRAPHIC_DEFAULT_WIDTH = 600;
var MOBILE_THRESHOLD = 500;

// Global vars
var pymChild = null;
var isMobile = false;
var graphicData = null;
var graphicConfig = null;

// D3 formatters
var fmtYearAbbrev = d3.time.format('%y');
var fmtYearFull = d3.time.format('%b %Y');

var defaultColors = [ COLORS['blue3'], COLORS['red3'], COLORS['yellow3'], COLORS['orange3'], COLORS['teal3'] ];

/*
 * Initialize graphic
 */
var onWindowLoaded = function() {
    if (Modernizr.svg) {
        graphicConfig = GRAPHIC_CONFIG;
        loadLocalData(GRAPHIC_DATA);

        if (graphicConfig.timeFormatLarge) {
            fmtYearFull = d3.time.format(graphicConfig.timeFormatLarge);
        }

        if (graphicConfig.timeFormatSmall) {
            fmtYearAbbrev = d3.time.format(graphicConfig.timeFormatSmall);
        }
    } else {
        pymChild = new pym.Child({});
    }
}

/*
 * Load graphic data from a local source.
 */
var loadLocalData = function(data) {
    graphicData = data;

    if (graphicData[0].date) {
        formatData();
    }

    pymChild = new pym.Child({
        renderCallback: render
    });
}

/*
 * Load graphic data from a CSV.
 */
var loadCSV = function(url) {
    d3.csv(GRAPHIC_DATA_URL, function(error, data) {
        graphicData = data;

        if (graphicData[0].date) {
            formatData();
        }

        pymChild = new pym.Child({
            renderCallback: render
        });
    });
}

/*
 * Format graphic data for processing by D3.
 */
var formatData = function() {
    graphicData.forEach(function(d) {
        var date = d3.time.format('%d/%m/%y').parse(d['date']);
        if (!date) {
            date = d3.time.format('%d/%m/%Y').parse(d['date']);
        }

        d['date'] = date;

        for (var key in d) {
            if (key != 'date') {
                d[key] = +d[key];
            }
        }
    });
}

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function(containerWidth) {
    if (!containerWidth) {
        containerWidth = GRAPHIC_DEFAULT_WIDTH;
    }

    if (containerWidth <= MOBILE_THRESHOLD) {
        isMobile = true;
    } else {
        isMobile = false;
    }

    // Render the chart!
    renderLineChart({
        container: '#graphic',
        width: containerWidth,
        data: graphicData
    });

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
}

/*
 * Render a line chart.
 */
var renderLineChart = function(config) {
    /*
     * Setup
     */
    var dateColumn = 'date';
    var valueColumn = 'amt';

    var aspectWidth = isMobile ? 4 : 16;
    var aspectHeight = isMobile ? 3 : 9;
    if ('ratio' in graphicConfig) {
        var parts = graphicConfig.ratio.split("x");
        aspectWidth = parseInt(parts[0], 10);
        aspectHeight = parseInt(parts[1], 10);
    }

    var margins = {
        top: 5,
        right: 80,
        bottom: 20,
        left: 30
    };

    if (graphicConfig.marginTop) {
        margins.top = parseInt(graphicConfig.marginTop, 10);
    }

    if (graphicConfig.marginRight) {
        margins.right = parseInt(graphicConfig.marginRight, 10);
    }

    if (graphicConfig.marginBottom) {
        margins.bottom = parseInt(graphicConfig.marginBottom, 10);
    }

    if (graphicConfig.marginLeft) {
        margins.left = parseInt(graphicConfig.marginLeft, 10);
    }

    var ticksX = parseInt(graphicConfig.ticksX || 10, 10);
    var ticksY = parseInt(graphicConfig.ticksY || 10, 10);
    var roundTicksFactor = parseInt(graphicConfig.roundTicksFactor || 5, 10);

    // Mobile
    if (isMobile) {
        ticksX = 5;
        ticksY = 5;
        margins['right'] = 25;
    }

    // Calculate actual chart dimensions
    var chartWidth = config['width'] - margins['left'] - margins['right'];
    var chartHeight = Math.ceil((config['width'] * aspectHeight) / aspectWidth) - margins['top'] - margins['bottom'];

    // Clear existing graphic (for redraw)
    var containerElement = d3.select(config['container']);
    containerElement.html('');

    var formattedData = {};

    /*
     * Restructure tabular data for easier charting.
     */
    for (var column in graphicData[0]) {
        if (column == dateColumn || column == 'x') {
            continue;
        }

        formattedData[column] = graphicData.map(function(d) {
            return {
                'x': d[dateColumn] || d['x'],
                'amt': +d[column]
            };
        });
    }

    /*
     * Create D3 scale objects.
     */

    var minY = 'minValue' in graphicConfig ? parseFloat(graphicConfig.minValue, 10) : d3.min(d3.entries(formattedData), function(c) {
        return d3.min(c['value'], function(v) {
            var n = v[valueColumn];
            return Math.floor(n / roundTicksFactor) * roundTicksFactor;
        });
    });

    var maxY = 'maxValue' in graphicConfig ? parseFloat(graphicConfig.maxValue, 10) : d3.max(d3.entries(formattedData), function(c) {
        return d3.max(c['value'], function(v) {
            var n = v[valueColumn];
            return Math.ceil(n / roundTicksFactor) * roundTicksFactor;
        });
    });

    var xFormat;
    var xScale;

    if (formattedData['date']) {
        xFormat = function(d, i) {
            if (isMobile) {
                return '\u2019' + fmtYearAbbrev(d);
            } else {
                return fmtYearFull(d);
            }
        };

        xScale = d3.time.scale()
        .domain(d3.extent(config['data'], function(d) {
            return d[dateColumn];
        }))
        .range([ 0, chartWidth ])
    } else {
        xFormat = function (d, i) {
            return d;
        };

        xScale = d3.scale.ordinal()
        .rangePoints([0, chartWidth])
        .domain(graphicData.map(function (d) {
            return d['x'];
        }))
        // .range([0, chartWidth]);
    }

    var yScale = d3.scale.linear()
        .domain([ minY, maxY ])
        .range([ chartHeight, 0 ]);

    var colorList = defaultColors;
    if ('colors' in graphicConfig) {
        colorList = graphicConfig.colors.split(/\s*,\s*/);
    }

    var colorScale = d3.scale.ordinal()
        .domain([0, colorList.length])
        .range(colorList);

    /*
     * Render the HTML legend.
     */
    var legend = containerElement.append('ul')
        .attr('class', 'key')
        .selectAll('g')
            .data(d3.entries(formattedData))
        .enter().append('li')
            .attr('class', function(d, i) {
                return 'key-item key-' + i + ' ' + classify(d['key']);
            });

    legend.append('b')
        .style('background-color', function(d) {
            return colorScale(d['key']);
        });

    legend.append('label')
        .text(function(d) {
            return d['key'];
        });

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphic-wrapper');

    var chartElement = chartWrapper.append('svg')
        .attr('width', chartWidth + margins['left'] + margins['right'])
        .attr('height', chartHeight + margins['top'] + margins['bottom'])
        .append('g')
        .attr('transform', 'translate(' + margins['left'] + ',' + margins['top'] + ')');

    /*
     * Create D3 axes.
     */
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom')
        .ticks(ticksX)
        .tickFormat(xFormat);

    var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient('left')
        .ticks(ticksY)
        .tickFormat(function (d) {
            return (graphicConfig.prefixY || '') + d + (graphicConfig.suffixY || '');
        });

    /*
     * Render axes to chart.
     */
    chartElement.append('g')
        .attr('class', 'x axis')
        .attr('transform', makeTranslate(0, chartHeight))
        .call(xAxis);

    chartElement.append('g')
        .attr('class', 'y axis')
        .call(yAxis);

    /*
     * Render grid to chart.
     */
    var xAxisGrid = function() {
        return xAxis;
    }

    var yAxisGrid = function() {
        return yAxis;
    }

    chartElement.append('g')
        .attr('class', 'x grid')
        .attr('transform', makeTranslate(0, chartHeight))
        .call(xAxisGrid()
            .tickSize(-chartHeight, 0, 0)
            .tickFormat('')
        );

    chartElement.append('g')
        .attr('class', 'y grid')
        .call(yAxisGrid()
            .tickSize(-chartWidth, 0, 0)
            .tickFormat('')
        );

    /*
     * Render lines to chart.
     */
    var line = d3.svg.line()
        .interpolate(graphicConfig.interpolate || 'monotone')
        .x(function(d) {
            return xScale(d[dateColumn] || d['x']);
        })
        .y(function(d) {
            return yScale(d[valueColumn]);
        });

    chartElement.append('g')
        .attr('class', 'lines')
        .selectAll('path')
        .data(d3.entries(formattedData))
        .enter()
        .append('path')
            .attr('class', function(d, i) {
                return 'line line-' + i + ' ' + classify(d['key']);
            })
            .attr('stroke', function(d) {
                return colorScale(d['key']);
            })
            .attr('d', function(d) {
                return line(d['value']);
            });

    chartElement.append('g')
        .attr('class', 'value')
        .selectAll('text')
        .data(d3.entries(formattedData))
        .enter().append('text')
            .attr('x', function(d, i) {
                var last = d['value'][d['value'].length - 1];

                return xScale(last[dateColumn] || last['x']) + 5;
            })
            .attr('y', function(d) {
                var last = d['value'][d['value'].length - 1];

                return yScale(last[valueColumn]) + 3;
            })
            .text(function(d) {
                var last = d['value'][d['value'].length - 1];
                var value = last[valueColumn];

                var label = last[valueColumn].toFixed(1);

                if (!isMobile) {
                    label = d['key'] + ': ' + label;
                }

                return label;
            });

    if (graphicConfig.xLabel) {
        var t = chartElement.append("text")
            .text(graphicConfig.xLabel)
            .attr("y", chartHeight + margins['bottom'] - 5)
            .attr("class", "axis-label");

        t.attr("x", (chartWidth - t.node().getComputedTextLength()) / 2)
    }

    if (graphicConfig.yLabel) {
        var t = chartElement.append("text")
            .text(graphicConfig.yLabel)
            .attr("y", -25)
            .attr("class", "axis-label");

        t.attr("x", 0 - ((chartHeight + margins['top'] + margins['bottom']) / 2));
        t.attr("transform", "rotate(-90)");
    }
}

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;
<|endoftext|>