<commit_before>/**
 * Base
 *
 * This contains settings shared across all environments.
 */
module.exports = {
  env: {
    mocha: true
  },
  extends: [
    'eslint-config-mongodb-js/rules/best-practices',
    /**
     * @todo (imlucas): Someday....
     * 'eslint-config-mongodb-js/rules/ecmascript-6',
     */
    'eslint-config-mongodb-js/rules/nodejs-and-commonjs',
    'eslint-config-mongodb-js/rules/possible-errors',
    'eslint-config-mongodb-js/rules/strict-mode',
    'eslint-config-mongodb-js/rules/stylistic-issues',
    'eslint-config-mongodb-js/rules/variables'
  ],
  rules: {
    'no-unused-expressions': 0,
    'chai-friendly/no-unused-expressions': 2
  },
  plugins: [
    'chai-friendly'
  ]
};
<commit_msg>Add parserOptions for #46<commit_after>/**
 * Base
 *
 * This contains settings shared across all environments.
 */
module.exports = {
  env: {
    mocha: true,
    expect: true,
    sinon: true,
    es6: true
  },
  parserOptions: {
    ecmaVersion: 6
  },
  extends: [
    'eslint-config-mongodb-js/rules/best-practices',
    /**
     * @todo (imlucas): Someday....
     * 'eslint-config-mongodb-js/rules/ecmascript-6',
     */
    'eslint-config-mongodb-js/rules/nodejs-and-commonjs',
    'eslint-config-mongodb-js/rules/possible-errors',
    'eslint-config-mongodb-js/rules/strict-mode',
    'eslint-config-mongodb-js/rules/stylistic-issues',
    'eslint-config-mongodb-js/rules/variables'
  ],
  rules: {
    'no-unused-expressions': 0,
    'chai-friendly/no-unused-expressions': 2
  },
  plugins: [
    'chai-friendly'
  ]
};
<|endoftext|>


<commit_before>var util = require('util')
var fs = require('fs')

var escapeMessage = function (message) {
  if (message === null || message === undefined) {
    return ''
  }

  return message.toString()
    .replace(/\|/g, '||')
    .replace(/\'/g, "|'")
    .replace(/\n/g, '|n')
    .replace(/\r/g, '|r')
    .replace(/\u0085/g, '|x')
    .replace(/\u2028/g, '|l')
    .replace(/\u2029/g, '|p')
    .replace(/\[/g, '|[')
    .replace(/\]/g, '|]')
}

var formatMessage = function () {
  var args = Array.prototype.slice.call(arguments)

  for (var i = args.length - 1; i > 0; i--) {
    args[i] = escapeMessage(args[i])
  }
  return util.format.apply(null, args) + '\n'
}

var fullTestName = function (result) {
  return result.suite.join(' ') + ' ' + result.description
}

var TeamcityReporter = function (baseReporterDecorator) {
  baseReporterDecorator(this)
  var self = this

  this.adapters = [fs.writeSync.bind(fs.writeSync, 1)]

  this.TEST_IGNORED = "##teamcity[testIgnored name='%s']"
  this.SUITE_START = "##teamcity[testSuiteStarted name='%s']"
  this.SUITE_END = "##teamcity[testSuiteFinished name='%s']"
  this.TEST_START = "##teamcity[testStarted name='%s']"
  this.TEST_FAILED = "##teamcity[testFailed name='%s' message='FAILED' details='%s']"
  this.TEST_END = "##teamcity[testFinished name='%s' duration='%s']"
  this.BLOCK_OPENED = "##teamcity[blockOpened name='%s']"
  this.BLOCK_CLOSED = "##teamcity[blockClosed name='%s']"

  var reporter = this
  var initializeBrowser = function (browser) {
    reporter.browserResults[browser.id] = {
      name: browser.name,
      log: [],
      lastSuite: null
    }
  }

  this.onRunStart = function (browsers) {
    this.write(formatMessage(this.BLOCK_OPENED, 'JavaScript Unit Tests'))

    this.browserResults = {}
    // Support Karma 0.10 (TODO: remove)
    browsers.forEach(initializeBrowser)
  }

  this.onBrowserStart = function (browser) {
    initializeBrowser(browser)
  }

  this.specSuccess = function (browser, result) {
    var log = this.getLog(browser, result)
    var testName = fullTestName(result)

    log.push(formatMessage(this.TEST_START, testName))
    log.push(formatMessage(this.TEST_END, testName, result.time))
  }

  this.specFailure = function (browser, result) {
    var log = this.getLog(browser, result)
    var testName = fullTestName(result)

    log.push(formatMessage(this.TEST_START, testName))
    log.push(formatMessage(this.TEST_FAILED, testName, result.log.join('\n\n')))
    log.push(formatMessage(this.TEST_END, testName, result.time))
  }

  this.specSkipped = function (browser, result) {
    var log = this.getLog(browser, result)
    var testName = fullTestName(result)

    log.push(formatMessage(this.TEST_IGNORED, testName))
  }

  this.onRunComplete = function () {
    Object.keys(this.browserResults).forEach(function (browserId) {
      var browserResult = self.browserResults[browserId]
      self.flushLogs(browserResult)
    })
    self.write(formatMessage(self.BLOCK_CLOSED, 'JavaScript Unit Tests'))
  }

  this.getLog = function (browser, result) {
    var browserResult = this.browserResults[browser.id]
    return browserResult.log
  }

  this.flushLogs = function (browserResult) {
    self.write(browserResult.log.join(''))
    while (browserResult.log.length > 0) {
      browserResult.log.shift()
    }
  }
}

TeamcityReporter.$inject = ['baseReporterDecorator']

module.exports = {
  'reporter:teamcity': ['type', TeamcityReporter]
}
<commit_msg>log as specs happen, not from memory<commit_after>var util = require('util')
var fs = require('fs')

var escapeMessage = function (message) {
  if (message === null || message === undefined) {
    return ''
  }

  return message.toString()
    .replace(/\|/g, '||')
    .replace(/\'/g, "|'")
    .replace(/\n/g, '|n')
    .replace(/\r/g, '|r')
    .replace(/\u0085/g, '|x')
    .replace(/\u2028/g, '|l')
    .replace(/\u2029/g, '|p')
    .replace(/\[/g, '|[')
    .replace(/\]/g, '|]')
}

var formatMessage = function () {
  var args = Array.prototype.slice.call(arguments)

  for (var i = args.length - 1; i > 0; i--) {
    args[i] = escapeMessage(args[i])
  }
  return util.format.apply(null, args) + '\n'
}

var fullTestName = function (result) {
  return result.suite.join(' ') + ' ' + result.description
}

var TeamcityReporter = function (baseReporterDecorator) {
  baseReporterDecorator(this)
  var self = this

  this.adapters = [fs.writeSync.bind(fs.writeSync, 1)]

  this.TEST_IGNORED = "##teamcity[testIgnored name='%s']"
  this.SUITE_START = "##teamcity[testSuiteStarted name='%s']"
  this.SUITE_END = "##teamcity[testSuiteFinished name='%s']"
  this.TEST_START = "##teamcity[testStarted name='%s']"
  this.TEST_FAILED = "##teamcity[testFailed name='%s' message='FAILED' details='%s']"
  this.TEST_END = "##teamcity[testFinished name='%s' duration='%s']"
  this.BLOCK_OPENED = "##teamcity[blockOpened name='%s']"
  this.BLOCK_CLOSED = "##teamcity[blockClosed name='%s']"

  this.onRunStart = function (browsers) {
    this.write(formatMessage(this.BLOCK_OPENED, 'JavaScript Unit Tests'))

    this._browsers = []
  }

  this.specSuccess = function (browser, result) {
    var testName = fullTestName(result)

    this.write(formatMessage(this.TEST_START, testName))
    this.write(formatMessage(this.TEST_END, testName, result.time))
  }

  this.specFailure = function (browser, result) {
    var testName = fullTestName(result)

    this.write(formatMessage(this.TEST_START, testName))
    this.write(formatMessage(this.TEST_FAILED, testName, result.log.join('\n\n')))
    this.write(formatMessage(this.TEST_END, testName, result.time))
  }

  this.specSkipped = function (browser, result) {
    var testName = fullTestName(result)

    this.write(formatMessage(this.TEST_IGNORED, testName))
  }

  this.onRunComplete = function () {
    self.write(formatMessage(self.BLOCK_CLOSED, 'JavaScript Unit Tests'))
  }
}

TeamcityReporter.$inject = ['baseReporterDecorator']

module.exports = {
  'reporter:teamcity': ['type', TeamcityReporter]
}
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */
var request = require('superagent')
  , _ = require('underscore');

// API endpoint
var endpoint = 'http://productsearch.linksynergy.com/productsearch';

var Linkshare = module.exports = function (opts) {
  if (!(this instanceof Linkshare)) return new Linkshare (opts);

  this._keywords = opts.keywords;
};

Linkshare.prototype.id = function (id) {
  return this._id = id, this;
};

Linkshare.prototype.advertiser = function (id) {
  return this._advertiser = id, this;
};

Linkshare.prototype.done = function (cb) {
  return request
    .get(endpoint)
    .query({token: this._id})
    .query({keyword: this._keywords})
    .query({mid: this._advertiser})
    .end(function (err, res) {
      if (err) return cb(err);
      return cb(null, res.text);
    });
};
<commit_msg>Add formatter<commit_after>/**
 * Module dependencies
 */
var request = require('superagent')
  , _ = require('underscore');

// API endpoint
var endpoint = 'http://productsearch.linksynergy.com/productsearch';

var Linkshare = module.exports = function (opts) {
  if (!(this instanceof Linkshare)) return new Linkshare (opts);

  this._keywords = opts.keywords;
};

Linkshare.prototype.id = function (id) {
  return this._id = id, this;
};

Linkshare.prototype.advertiser = function (id) {
  return this._advertiser = id, this;
};

Linkshare.prototype.done = function (cb) {
  return request
    .get(endpoint)
    .query({token: this._id})
    .query({keyword: this._keywords})
    .query({mid: this._advertiser})
    .end(function (err, res) {
      if (err) return cb(err);
      return cb(null, res.text);
    });
};

var format = function (items) {
  return items.map(function (i) {
    return {
      name: i.productname[0],
      listPrice: i.price[0]['_'],
      currency: i.price[0]['$']['currency'],
      url: i.linkurl[0],
      id: i.linkid[0]
    }
  });
};
<|endoftext|>


<commit_before>module.exports = {
    'plugins': [
        'jsdoc'
    ],
    'rules': {
        'curly': [2, 'all'],
        'eqeqeq': 2,
        'wrap-iife': 2,
        'no-use-before-define': [2, {
            'functions': false
        }],
        'no-caller': 2,
        'no-irregular-whitespace': 2,
        'no-undef': 2,
        'no-unused-vars': 2,
        'no-console': 1,
        'no-alert': 1,
        'linebreak-style': [2, 'unix'],
        'comma-style': [2, 'last'],
        'dot-notation': 2,
        'no-empty': 2,
        'no-spaced-func': 2,
        'array-bracket-spacing': [2, 'never'],
        'space-in-parens': [2, 'never'],
        'key-spacing': [2, {
            'beforeColon': false,
            'afterColon': true
        }],
        'object-curly-spacing': [2, 'always'],
        'space-unary-ops': [2, {
            'words': false,
            'nonwords': false
        }],
        'one-var': [2, {
            'uninitialized': 'always',
            'initialized': 'never'
        }],
        'no-mixed-spaces-and-tabs': 2,
        'no-trailing-spaces': 2,
        'no-with': 2,
        'brace-style': 2,
        'no-multiple-empty-lines': 2,
        'no-multi-str': 2,
        'space-before-blocks': [2, 'always'],
        'space-infix-ops': 2,
        'camelcase': [2, {
            'properties': 'never'
        }],
        'eol-last': 2,
        'keyword-spacing': [2, {
            'overrides': {
                'if': {
                    'after': true
                },
                'else': {
                    'after': true
                },
                'for': {
                    'after': true
                },
                'while': {
                    'after': true
                },
                'do': {
                    'after': true
                },
                'switch': {
                    'after': true
                },
                'case': {
                    'after': true
                },
                'return': {
                    'after': true
                },
                'try': {
                    'after': true
                },
                'catch': {
                    'after': true
                },
                'typeof': {
                    'after': true
                }
            }
        }],
        'lines-around-comment': [2, {
            'beforeBlockComment': true,
            'afterBlockComment': false,
            'beforeLineComment': false,
            'afterLineComment': false,
            'allowBlockStart': true,
            'allowObjectStart': true,
            'allowArrayStart': true
        }],
        'semi': [2, 'always'],
        'consistent-this': [2, 'self'],
        'quotes': [2, 'single', {
            'avoidEscape': true,
            'allowTemplateLiterals': true
        }],
        'indent': [2, 4, {
            'SwitchCase': 1
        }],
        'valid-jsdoc': [1, {
            'prefer': {
                'return': 'returns'
            },
            'requireReturn': false
        }],
        'jsdoc/require-hyphen-before-param-description': 1
    }
};
<commit_msg>Make no-alert throw error<commit_after>module.exports = {
    'plugins': [
        'jsdoc'
    ],
    'rules': {
        'curly': [2, 'all'],
        'eqeqeq': 2,
        'wrap-iife': 2,
        'no-use-before-define': [2, {
            'functions': false
        }],
        'no-caller': 2,
        'no-irregular-whitespace': 2,
        'no-undef': 2,
        'no-unused-vars': 2,
        'no-console': 1,
        'no-alert': 2,
        'linebreak-style': [2, 'unix'],
        'comma-style': [2, 'last'],
        'dot-notation': 2,
        'no-empty': 2,
        'no-spaced-func': 2,
        'array-bracket-spacing': [2, 'never'],
        'space-in-parens': [2, 'never'],
        'key-spacing': [2, {
            'beforeColon': false,
            'afterColon': true
        }],
        'object-curly-spacing': [2, 'always'],
        'space-unary-ops': [2, {
            'words': false,
            'nonwords': false
        }],
        'one-var': [2, {
            'uninitialized': 'always',
            'initialized': 'never'
        }],
        'no-mixed-spaces-and-tabs': 2,
        'no-trailing-spaces': 2,
        'no-with': 2,
        'brace-style': 2,
        'no-multiple-empty-lines': 2,
        'no-multi-str': 2,
        'space-before-blocks': [2, 'always'],
        'space-infix-ops': 2,
        'camelcase': [2, {
            'properties': 'never'
        }],
        'eol-last': 2,
        'keyword-spacing': [2, {
            'overrides': {
                'if': {
                    'after': true
                },
                'else': {
                    'after': true
                },
                'for': {
                    'after': true
                },
                'while': {
                    'after': true
                },
                'do': {
                    'after': true
                },
                'switch': {
                    'after': true
                },
                'case': {
                    'after': true
                },
                'return': {
                    'after': true
                },
                'try': {
                    'after': true
                },
                'catch': {
                    'after': true
                },
                'typeof': {
                    'after': true
                }
            }
        }],
        'lines-around-comment': [2, {
            'beforeBlockComment': true,
            'afterBlockComment': false,
            'beforeLineComment': false,
            'afterLineComment': false,
            'allowBlockStart': true,
            'allowObjectStart': true,
            'allowArrayStart': true
        }],
        'semi': [2, 'always'],
        'consistent-this': [2, 'self'],
        'quotes': [2, 'single', {
            'avoidEscape': true,
            'allowTemplateLiterals': true
        }],
        'indent': [2, 4, {
            'SwitchCase': 1
        }],
        'valid-jsdoc': [1, {
            'prefer': {
                'return': 'returns'
            },
            'requireReturn': false
        }],
        'jsdoc/require-hyphen-before-param-description': 1
    }
};
<|endoftext|>


<commit_before>import Vue from 'vue';

export var version = '2.2.0';

var compatible = (/^2\./).test(Vue.version);
if (!compatible) {
  Vue.util.warn('VueClickaway ' + version + ' only supports Vue 2.x, and does not support Vue ' + Vue.version);
}

// @SECTION: implementation

var HANDLER = '_vue_clickaway_handler';

function bind(el, binding) {
  unbind(el);

  var callback = binding.value;
  if (typeof callback !== 'function') {
    if (process.env.NODE_ENV !== 'production') {
      Vue.util.warn(
        'v-' + binding.name + '="' +
        binding.expression + '" expects a function value, ' +
        'got ' + callback
      );
    }
    return;
  }

  // @NOTE: Vue binds directives in microtasks, while UI events are dispatched
  //        in macrotasks. This causes the listener to be set up before
  //        the "origin" click event (the event that lead to the binding of
  //        the directive) arrives at the document root. To work around that,
  //        we ignore events until the end of the "initial" macrotask.
  // @REFERENCE: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
  // @REFERENCE: https://github.com/simplesmiler/vue-clickaway/issues/8
  var initialMacrotaskEnded = false;
  setTimeout(function() {
    initialMacrotaskEnded = true;
  }, 0);

  el[HANDLER] = function(ev) {
    // @NOTE: this test used to be `el.containts`, but `ev.path` is better,
    //        because it tests whether the element was there at the time of
    //        the click, not whether it is there now, that the event has arrived
    //        to the top.
    if (initialMacrotaskEnded && ev.path.indexOf(el) < 0) {
      return callback(ev);
    }
  };

  document.documentElement.addEventListener('click', el[HANDLER], false);
}

function unbind(el) {
  document.documentElement.removeEventListener('click', el[HANDLER], false);
  delete el[HANDLER];
}

export var directive = {
  bind: bind,
  update: function(el, binding) {
    if (binding.value === binding.oldValue) return;
    bind(el, binding);
  },
  unbind: unbind,
};

export var mixin = {
  directives: { onClickaway: directive },
};
<commit_msg>Try to use path, then composed path, then fallback to contains<commit_after>import Vue from 'vue';

export var version = '2.2.0';

var compatible = (/^2\./).test(Vue.version);
if (!compatible) {
  Vue.util.warn('VueClickaway ' + version + ' only supports Vue 2.x, and does not support Vue ' + Vue.version);
}

// @SECTION: implementation

var HANDLER = '_vue_clickaway_handler';

function bind(el, binding) {
  unbind(el);

  var callback = binding.value;
  if (typeof callback !== 'function') {
    if (process.env.NODE_ENV !== 'production') {
      Vue.util.warn(
        'v-' + binding.name + '="' +
        binding.expression + '" expects a function value, ' +
        'got ' + callback
      );
    }
    return;
  }

  // @NOTE: Vue binds directives in microtasks, while UI events are dispatched
  //        in macrotasks. This causes the listener to be set up before
  //        the "origin" click event (the event that lead to the binding of
  //        the directive) arrives at the document root. To work around that,
  //        we ignore events until the end of the "initial" macrotask.
  // @REFERENCE: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
  // @REFERENCE: https://github.com/simplesmiler/vue-clickaway/issues/8
  var initialMacrotaskEnded = false;
  setTimeout(function() {
    initialMacrotaskEnded = true;
  }, 0);

  el[HANDLER] = function(ev) {
    // @NOTE: this test used to be just `el.containts`, but working with path is better,
    //        because it tests whether the element was there at the time of
    //        the click, not whether it is there now, that the event has arrived
    //        to the top.
    // @NOTE: `.path` is non-standard, the standard way is `.composedPath()`
    var path = ev.path || (ev.composedPath ? ev.composedPath() : undefined);
    if (initialMacrotaskEnded && (path ? path.indexOf(el) < 0 : !el.contains(ev.target))) {
      return callback(ev);
    }
  };

  document.documentElement.addEventListener('click', el[HANDLER], false);
}

function unbind(el) {
  document.documentElement.removeEventListener('click', el[HANDLER], false);
  delete el[HANDLER];
}

export var directive = {
  bind: bind,
  update: function(el, binding) {
    if (binding.value === binding.oldValue) return;
    bind(el, binding);
  },
  unbind: unbind,
};

export var mixin = {
  directives: { onClickaway: directive },
};
<|endoftext|>


<commit_before>'use strict';

var executable = require('executable');
var spawn = require('child_process').spawn;

/**
 * Check if a executable is working correctly by checking it's exit code
 *
 * @param {String} bin
 * @param {Array} cmd
 * @param {Function} cb
 * @api public
 */

module.exports = function (bin, cmd, cb) {
	if (typeof cmd === 'function') {
		cb = cmd;
		cmd = ['--help'];
	}

	executable(bin, function (err, works) {
		if (err) {
			cb(err);
			return;
		}

		if (works) {
			var cp = spawn(bin, cmd);

			cp.on('error', function (err) {
				cb(err);
				return;
			});

			cp.on('exit', function (code) {
				cb(null, code === 0 ? true : false);
				return;
			});
		} else {
			cb(new Error('Couldn\'t execute the `' + bin + '` binary. Make sure it has the right permissions.'));
			return;
		}
	});
};
<commit_msg>Minor tweak<commit_after>'use strict';

var executable = require('executable');
var spawn = require('child_process').spawn;

/**
 * Check if a executable is working correctly by checking it's exit code
 *
 * @param {String} bin
 * @param {Array} cmd
 * @param {Function} cb
 * @api public
 */

module.exports = function (bin, cmd, cb) {
	if (typeof cmd === 'function') {
		cb = cmd;
		cmd = ['--help'];
	}

	executable(bin, function (err, works) {
		if (err) {
			cb(err);
			return;
		}

		if (!works) {
			cb(new Error('Couldn\'t execute the `' + bin + '` binary. Make sure it has the right permissions.'));
			return;
		}

		var cp = spawn(bin, cmd);

		cp.on('error', function (err) {
			cb(err);
			return;
		});

		cp.on('exit', function (code) {
			cb(null, code === 0 ? true : false);
			return;
		});
	});
};
<|endoftext|>


<commit_before>var _ = require('lodash');
var path = require('path');
var async = require('async');
var moment = require('moment');
var Nightwatch = require('nightwatch/lib/index.js');
var child_process = require('child_process');
var selenium = require('selenium-server-standalone-jar');
var selenium_proc = null;
var sauceConnectLauncher = require('sauce-connect-launcher');

/*
On some machines, selenium fails with a timeout error when nightwatch tries to connect due to a
deadlock situation. The following is a temporary workaround that starts selenium without a pipe
from stdin until this issue is fixed in nightwatch:
https://github.com/nightwatchjs/nightwatch/issues/470
*/
function runSeleniumInBackground (done) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' kne: starting selenium server in background...');
	selenium_proc = child_process.spawn('java',
		[
			'-jar', selenium.path,
		],
		{
			stdio: ['ignore', 'pipe', 'pipe'],
		});
	var running = false;

	selenium_proc.stderr.on('data', function (buffer) {
		var line = buffer.toString();
		if (line.search(/Selenium Server is up and running/g) !== -1) {
			running = true;
			done();
		}
	});

	selenium_proc.on('close', function (code) {
		if (!running) {
			done(new Error('Selenium exited with error code ' + code));
		}
	});
}

// Function that starts the nightwatch service
function runNightwatch (done) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' kne: running nightwatch...');

	try {
		Nightwatch.cli(function (argv) {
			// Set app-specific env for nightwatch session
			process.env.KNE_TEST_ENV = argv.env;

			if (argv['browser-name']) {
				process.env.KNE_BROWSER_NAME = argv['browser-name'];
			}

			if (argv['browser-version']) {
				process.env.KNE_BROWSER_VERSION = argv['browser-version'];
			}

			// If possible, argv inputs and environment variables will be merged together
			// If not, argv inputs will override environment variables.
			process.env.KNE_TEST_PATHS = process.env.KNE_TEST_PATHS || '';
			if (argv.test_paths) {
				// If argv.test_paths is set, add these paths to the environment variable
				process.env.KNE_TEST_PATHS += ',' + argv.test_paths;
			} else if (process.env.KNE_TEST_PATHS === '') {
				// If neither argv.test_paths nor the environment variable is set, throw an error.
				done(new Error('No test paths provided. Either set the --test_paths config option or the KNE_TEST_PATHS environment variable'));
			}
			process.env.KNE_SELENIUM_SERVER = process.env.KNE_SELENIUM_SERVER || selenium.path;
			if (process.env.KNE_PAGE_OBJECT_PATHS) {
				process.env.KNE_PAGE_OBJECT_PATHS += ',' + path.resolve(__dirname, 'lib/src/pageObjects/');
			} else {
				process.env.KNE_PAGE_OBJECT_PATHS = path.resolve(__dirname, 'lib/src/pageObjects/');
			}
			if (argv.po_paths) {
				process.env.KNE_PAGE_OBJECT_PATHS += ',' + argv.po_paths;
			}

			if (!process.env.KNE_EXCLUDE_TEST_PATHS) {
				process.env.KNE_EXCLUDE_TEST_PATHS = ''; // Needs to be initialised for .split in conf.js file.
			} else if (argv.exclude_paths) {
				process.env.KNE_EXCLUDE_TEST_PATHS += ',' + argv.exclude_paths;
			}

			if (argv.env === 'saucelabs-local' && argv['sauce-username'] && argv['sauce-access-key']) {
				process.env.SAUCE_USERNAME = argv['sauce-username'];
				process.env.SAUCE_ACCESS_KEY = argv['sauce-access-key'];
				// TODO:  tried to set a TRAVIS_JOB_NUMBER here to something like --sauce-tunnel-id but it just
				//		doesn't work, at least, not in my dev environment.  Something to look into later.  For right
				//		now the default tunnel name will do.
			}

			argv.config = path.resolve(__dirname, 'nightwatch.conf.js');

			async.series([

				/**
				 * If the user wants us to start selenium manually then do so
				 */
				function (cb) {
					if (argv.env === 'default' && argv['selenium-in-background']) {
						process.env.KNE_SELENIUM_START_PROCESS = false;
						runSeleniumInBackground(cb);
					} else if (argv.env === 'default') {
						process.env.KNE_SELENIUM_START_PROCESS = true;
						cb();
					} else {
						process.env.KNE_SELENIUM_START_PROCESS = false;
						cb();
					}
				},

				/**
				 * The only environment that currently requires starting sauce connect is travis.
				 */
				function (cb) {
					if (argv.env === 'saucelabs-travis' || (argv.env === 'saucelabs-local' && argv['sauce-username'] && argv['sauce-access-key'])) {
						startSauceConnect(cb);
					} else {
						if (argv.env === 'saucelabs-local') {
							console.error([moment().format('HH:mm:ss:SSS')] + ' kne: You must specify --sauce-username and --sauce-access-key when using: --' + argv.env);
							cb(new Error('kne: You must specify --sauce-username and --sauce-access-key when using: --' + argv.env));
						} else {
							cb();
						}
					}
				},

				/**
				 * Run nightwatch to start executing the tests
				 */
				function (cb) {
					Nightwatch.runner(argv, function (status) {
						var err = null;
						if (status) {
							console.log([moment().format('HH:mm:ss:SSS')] + ' kne: tests passed');
						} else {
							console.log([moment().format('HH:mm:ss:SSS')] + ' kne: tests failed');
							err = new Error('kne: nightwatch runner return error status code: 0');
						}
						cb(err);
					});
				},
			], function (err) {
				if (err) {
					console.error(err);
				}
				done(err);
			});
		});
	} catch (ex) {
		console.error('\nThere was an error while starting the nightwatch test runner:\n\n');
		process.stderr.write(ex.stack + '\n');
		done('failed to run nightwatch!');
	}
}

var sauceConnection = null;
var sauceConnectionRunning = false;

function sauceConnectLog (message) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' Sauce Connect: ' + message);
}

// Function that starts the sauce connect servers if SAUCE_ACCESS_KEY is set.
function startSauceConnect (done) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' kne: Starting Sauce Connect');

	var default_options = {
		username: process.env.SAUCE_USERNAME,
		accessKey: process.env.SAUCE_ACCESS_KEY,
		connectRetries: 5,
		connectRetryTimeout: 60000,
		logger: sauceConnectLog,
		readyFileId: process.env.TRAVIS_JOB_NUMBER,
	};
	var custom_options = process.env.TRAVIS_JOB_NUMBER
		? {
			tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
		} : {
		};
	var options = _.extend({}, default_options, custom_options);

	sauceConnectLauncher(options, function (err, sauceConnectProcess) {
		if (err) {
			console.error([moment().format('HH:mm:ss:SSS')] + ' kne: There was an error starting Sauce Connect');
			done(err);
		} else {
			console.log([moment().format('HH:mm:ss:SSS')] + ' kne: Sauce Connect Ready');
			sauceConnection = sauceConnectProcess;
			sauceConnectionRunning = true;
			setTimeout(done, 5000);
		}
	});
}

function stopSauceConnect (done) {
	if (process.env.SAUCE_ACCESS_KEY !== undefined && sauceConnection !== null) {
		console.log([moment().format('HH:mm:ss:SSS')] + ' kne: Stopping Sauce Connect');
		sauceConnection.close(function () {
			console.log([moment().format('HH:mm:ss:SSS')] + ' kne: Sauce Connect Stopped');
			sauceConnectionRunning = false;
			setTimeout(done, 60000);
		});
	} else {
		done();
	}
}

/*
	Function that starts the nightwatch-based e2e framework service

	options:
	{
		keystone: <keystone instance>		// REQUIRED
	}
*/
function start (options, callback) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' kne: starting...');

	// add the keystone instance to the module exports so that the keystone-nightwatch-e2e library may use it
	exports.keystone = options.keystone;

	runNightwatch(function (err) {
		console.log([moment().format('HH:mm:ss:SSS')] + ' kne: finishing...');
		if (err) {
			console.error([moment().format('HH:mm:ss:SSS')] + ' kne: finished with error\n' + err);
		}
		if (selenium_proc) {
			console.error([moment().format('HH:mm:ss:SSS')] + ' kne: terminating selenium process');
			selenium_proc.kill('SIGTERM');
			selenium_proc.kill('SIGKILL');
		}
		if (sauceConnectionRunning) {
			stopSauceConnect(function () { callback && callback(err); });
		} else {
			callback && callback(err);
		}
	});
}

// exported e2e framework service
exports = module.exports = {
	startE2E: start,
	pageObjectsPath: path.resolve(__dirname, 'lib/src/pageObjects/'),
	fieldTestObjectsPath: path.resolve(__dirname, 'lib/src/fieldTestObjects'),
};
<commit_msg>Update error wording<commit_after>var _ = require('lodash');
var path = require('path');
var async = require('async');
var moment = require('moment');
var Nightwatch = require('nightwatch/lib/index.js');
var child_process = require('child_process');
var selenium = require('selenium-server-standalone-jar');
var selenium_proc = null;
var sauceConnectLauncher = require('sauce-connect-launcher');

/*
On some machines, selenium fails with a timeout error when nightwatch tries to connect due to a
deadlock situation. The following is a temporary workaround that starts selenium without a pipe
from stdin until this issue is fixed in nightwatch:
https://github.com/nightwatchjs/nightwatch/issues/470
*/
function runSeleniumInBackground (done) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' kne: starting selenium server in background...');
	selenium_proc = child_process.spawn('java',
		[
			'-jar', selenium.path,
		],
		{
			stdio: ['ignore', 'pipe', 'pipe'],
		});
	var running = false;

	selenium_proc.stderr.on('data', function (buffer) {
		var line = buffer.toString();
		if (line.search(/Selenium Server is up and running/g) !== -1) {
			running = true;
			done();
		}
	});

	selenium_proc.on('close', function (code) {
		if (!running) {
			done(new Error('Selenium exited with error code ' + code));
		}
	});
}

// Function that starts the nightwatch service
function runNightwatch (done) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' kne: running nightwatch...');

	try {
		Nightwatch.cli(function (argv) {
			// Set app-specific env for nightwatch session
			process.env.KNE_TEST_ENV = argv.env;

			if (argv['browser-name']) {
				process.env.KNE_BROWSER_NAME = argv['browser-name'];
			}

			if (argv['browser-version']) {
				process.env.KNE_BROWSER_VERSION = argv['browser-version'];
			}

			// If possible, argv inputs and environment variables will be merged together
			// If not, argv inputs will override environment variables.
			process.env.KNE_TEST_PATHS = process.env.KNE_TEST_PATHS || '';
			if (argv.test_paths) {
				// If argv.test_paths is set, add these paths to the environment variable
				process.env.KNE_TEST_PATHS += ',' + argv.test_paths;
			} else if (process.env.KNE_TEST_PATHS === '') {
				// If neither argv.test_paths nor the environment variable is set, throw an error.
				done(new Error('No test paths provided. Either set the --test_paths config option or the KNE_TEST_PATHS environment variable'));
			}
			process.env.KNE_SELENIUM_SERVER = process.env.KNE_SELENIUM_SERVER || selenium.path;
			if (process.env.KNE_PAGE_OBJECT_PATHS) {
				process.env.KNE_PAGE_OBJECT_PATHS += ',' + path.resolve(__dirname, 'lib/src/pageObjects/');
			} else {
				process.env.KNE_PAGE_OBJECT_PATHS = path.resolve(__dirname, 'lib/src/pageObjects/');
			}
			if (argv.po_paths) {
				process.env.KNE_PAGE_OBJECT_PATHS += ',' + argv.po_paths;
			}

			if (!process.env.KNE_EXCLUDE_TEST_PATHS) {
				process.env.KNE_EXCLUDE_TEST_PATHS = ''; // Needs to be initialised for .split in conf.js file.
			} else if (argv.exclude_paths) {
				process.env.KNE_EXCLUDE_TEST_PATHS += ',' + argv.exclude_paths;
			}

			if (argv.env === 'saucelabs-local' && argv['sauce-username'] && argv['sauce-access-key']) {
				process.env.SAUCE_USERNAME = argv['sauce-username'];
				process.env.SAUCE_ACCESS_KEY = argv['sauce-access-key'];
				// TODO:  tried to set a TRAVIS_JOB_NUMBER here to something like --sauce-tunnel-id but it just
				//		doesn't work, at least, not in my dev environment.  Something to look into later.  For right
				//		now the default tunnel name will do.
			}

			argv.config = path.resolve(__dirname, 'nightwatch.conf.js');

			async.series([

				/**
				 * If the user wants us to start selenium manually then do so
				 */
				function (cb) {
					if (argv.env === 'default' && argv['selenium-in-background']) {
						process.env.KNE_SELENIUM_START_PROCESS = false;
						runSeleniumInBackground(cb);
					} else if (argv.env === 'default') {
						process.env.KNE_SELENIUM_START_PROCESS = true;
						cb();
					} else {
						process.env.KNE_SELENIUM_START_PROCESS = false;
						cb();
					}
				},

				/**
				 * The only environment that currently requires starting sauce connect is travis.
				 */
				function (cb) {
					if (argv.env === 'saucelabs-travis' || (argv.env === 'saucelabs-local' && argv['sauce-username'] && argv['sauce-access-key'])) {
						startSauceConnect(cb);
					} else {
						if (argv.env === 'saucelabs-local') {
							console.error([moment().format('HH:mm:ss:SSS')] + ' kne: You must specify --sauce-username and --sauce-access-key when using: --' + argv.env);
							cb(new Error('kne: You must specify --sauce-username and --sauce-access-key when using: --' + argv.env));
						} else {
							cb();
						}
					}
				},

				/**
				 * Run nightwatch to start executing the tests
				 */
				function (cb) {
					Nightwatch.runner(argv, function (status) {
						var err = null;
						if (status) {
							console.log([moment().format('HH:mm:ss:SSS')] + ' kne: tests passed');
						} else {
							console.log([moment().format('HH:mm:ss:SSS')] + ' kne: tests failed');
							err = new Error('kne: nightwatch runner returned an error status code');
						}
						cb(err);
					});
				},
			], function (err) {
				if (err) {
					console.error(err);
				}
				done(err);
			});
		});
	} catch (ex) {
		console.error('\nThere was an error while starting the nightwatch test runner:\n\n');
		process.stderr.write(ex.stack + '\n');
		done('failed to run nightwatch!');
	}
}

var sauceConnection = null;
var sauceConnectionRunning = false;

function sauceConnectLog (message) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' Sauce Connect: ' + message);
}

// Function that starts the sauce connect servers if SAUCE_ACCESS_KEY is set.
function startSauceConnect (done) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' kne: Starting Sauce Connect');

	var default_options = {
		username: process.env.SAUCE_USERNAME,
		accessKey: process.env.SAUCE_ACCESS_KEY,
		connectRetries: 5,
		connectRetryTimeout: 60000,
		logger: sauceConnectLog,
		readyFileId: process.env.TRAVIS_JOB_NUMBER,
	};
	var custom_options = process.env.TRAVIS_JOB_NUMBER
		? {
			tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
		} : {
		};
	var options = _.extend({}, default_options, custom_options);

	sauceConnectLauncher(options, function (err, sauceConnectProcess) {
		if (err) {
			console.error([moment().format('HH:mm:ss:SSS')] + ' kne: There was an error starting Sauce Connect');
			done(err);
		} else {
			console.log([moment().format('HH:mm:ss:SSS')] + ' kne: Sauce Connect Ready');
			sauceConnection = sauceConnectProcess;
			sauceConnectionRunning = true;
			setTimeout(done, 5000);
		}
	});
}

function stopSauceConnect (done) {
	if (process.env.SAUCE_ACCESS_KEY !== undefined && sauceConnection !== null) {
		console.log([moment().format('HH:mm:ss:SSS')] + ' kne: Stopping Sauce Connect');
		sauceConnection.close(function () {
			console.log([moment().format('HH:mm:ss:SSS')] + ' kne: Sauce Connect Stopped');
			sauceConnectionRunning = false;
			setTimeout(done, 60000);
		});
	} else {
		done();
	}
}

/*
	Function that starts the nightwatch-based e2e framework service

	options:
	{
		keystone: <keystone instance>		// REQUIRED
	}
*/
function start (options, callback) {
	console.log([moment().format('HH:mm:ss:SSS')] + ' kne: starting...');

	// add the keystone instance to the module exports so that the keystone-nightwatch-e2e library may use it
	exports.keystone = options.keystone;

	runNightwatch(function (err) {
		console.log([moment().format('HH:mm:ss:SSS')] + ' kne: finishing...');
		if (err) {
			console.error([moment().format('HH:mm:ss:SSS')] + ' kne: finished with error\n' + err);
		}
		if (selenium_proc) {
			console.error([moment().format('HH:mm:ss:SSS')] + ' kne: terminating selenium process');
			selenium_proc.kill('SIGTERM');
			selenium_proc.kill('SIGKILL');
		}
		if (sauceConnectionRunning) {
			stopSauceConnect(function () { callback && callback(err); });
		} else {
			callback && callback(err);
		}
	});
}

// exported e2e framework service
exports = module.exports = {
	startE2E: start,
	pageObjectsPath: path.resolve(__dirname, 'lib/src/pageObjects/'),
	fieldTestObjectsPath: path.resolve(__dirname, 'lib/src/fieldTestObjects'),
};
<|endoftext|>


<commit_before>var through = require('through');

module.exports = police;

function police(options) {
  options = options || {};
  var exclude = false,
      verify = false;
  if (options.exclude) {
    exclude = true;
    var excludeLength = options.exclude.length;
  }
  if (options.verify) {
    verify = true;
    var verifyLength = options.verify.length;
  }
  var tr = through(write);

  function write(buf) {
    var str = buf.toString();
    if (exclude) {
      var i = 0;
      for (; i < excludeLength; ++i) {
        if (str.match(options.exclude[i])) return;
      }
    }
    if (verify) {
      var i = 0;
      for (; i < verifyLength; ++i) {
        if (str.match(options.verify[i])) {
          this.queue(buf);
          return;
        }
      }
      return;
    }
    this.queue(buf);
  }
  return tr;
}

<commit_msg>performance<commit_after>var through = require('through');

module.exports = police;

function police(options) {
  options = options || {};
  var exclude = false,
      verify = false;
  if (options.exclude) {
    exclude = true;
    var excludeLength = options.exclude.length;
  }
  if (options.verify) {
    verify = true;
    var verifyLength = options.verify.length;
  }
  var tr = through(write);

  function write(buf) {
    var str = buf.toString();
    if (exclude) {
      var i = 0;
      for (; i < excludeLength; ++i) {
        if (options.exclude[i].test(str)) return;
      }
    }
    if (verify) {
      var i = 0;
      for (; i < verifyLength; ++i) {
        if (options.verify[i].test(str)) {
          this.queue(buf);
          return;
        }
      }
      return;
    }
    this.queue(buf);
  }
  return tr;
}

<|endoftext|>


<commit_before>/**
 * The MIT License
 *
 * Copyright (c) 2014 Martin Micunda
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

'use strict';

/**
 * Module dependencies.
 */
var extend         = require('util')._extend,
    nodemailer     = require('nodemailer'),
    emailTemplates = require('email-templates');

/**
 * A node module for sending e-mails.
 *
 * @module mm-node-mailer
 * @example
 *  ```js
 *      var mailer = new mmMailer({
 *          config: {
 *              service: 'Gmail',
 *              auth: {
 *                  user: 'test@gmail.com',
 *                  pass: 'password'
 *              }
 *          },
 *          mail: {
 *              from: 'test@gmail.com',
 *              subject: 'Hello',
 *              text: 'Hello world!',
 *              html: '<b>Hello world!</b>'
 *          }
 *      });
 *      var mailOptions = {
 *          to: 'your.email@address.com',
 *      }
 *      mailer.send(mailOptions, function (err, respond) {
 *          if (err) return console.error(err);
 *          console.log('An e-mail has been sent successfully. Server responded with "' + respond + '"');
 *      });
 *  ```
 */
module.exports = Mailer;

/**
 * Define default mailer options.
 *
 * @type {Object} defaultOptions - the default mailer options
 * @type {Object} defaultOptions.config - the nodemailer configuration. See {@link https://github.com/andris9/nodemailer-smtp-transport `nodemailer-smtp-transport`} for full description of config fields.
 * @type {Object} defaultOptions.mail - the to, from, cc, etc, fields for all emails. See {@link https://github.com/andris9/Nodemailer#e-mail-message-fields `e-mail-message-fields`} for full description of email fields.
 * @type {string} defaultOptions.mail.templateName - (optional) the template folder name that is store in templates directory
 * @type {Object} defaultOptions.mail.templateContent - (optional) the template JSON content
 * @type {string} defaultOptions.templatesDir - (optional) the templates directory path
 * @type {Object} defaultOptions.templateEngineOptions - (optional) the template engine options e.g. helpers, partials etc. See {@link https://github.com/niftylettuce/node-email-templates#templating-language-options-eg-ejs-custom-tags `node-email-templates`} for options.
 * @private
 */
var defaultOptions = {
    config: {},
    mail: {
        templateName: '',
        templateContent: {}
    },
    templatesDir: '',
    templateEngineOptions: {}
};

/**
 * A `Mailer` constructor.
 *
 * @class Mailer
 * @constructor
 *
 * @param {Object} options - custom mailer configuration options
 */
function Mailer(options) {
    if (!(this instanceof Mailer)) {return new Mailer(options);}
    // extend default options
    this.options = extend(defaultOptions, options);
    // create transporter that is able to send mail
    this.transporter = nodemailer.createTransport(options.config);
}

/**
 * Sends an email using the preselected transport object.
 *
 * @method send
 * @param {Object}   mailOptions - the email data options, see {@link https://github.com/andris9/Nodemailer#e-mail-message-fields `e-mail-message-fields`} for full description of email fields.
 * @param {Function} cb          - the callback function
 *
 * @returns {Function} the callback function
 */
Mailer.prototype.send = function(mailOptions, cb) {
    var self = this;

    // extend the default mail options with options passed by user
    self.options.mail = extend(self.options.mail, mailOptions);

    if(!self.options.mail.from) {
        return cb(new Error('Sender email address required'));
    }
    if(!self.options.mail.to) {
        return cb(new Error('Receiver email address required'));
    }

    // check if it should send text or template e-mail
    if(self.options.templatesDir && self.options.mail.templateName) {
        emailTemplates(self.options.templatesDir, self.options.templateEngineOptions, function (err, template) {
            if (err) {return cb(err);}

            template(self.options.mail.templateName, self.options.mail.templateContent, function (err, html, text) {
                if (err) {return cb(err);}

                self.options.mail.html = html;
                self.options.mail.text = text;

                // if we are testing don't send out an email instead return success, info response, html and txt strings for inspection
                if (process.env.NODE_ENV === 'test') {
                    return cb(null, '250 2.0.0 OK 1407018531 gc8sm23308604wic.3 - gsmtp', html, text);
                }

                self.transporter.sendMail(self.options.mail, function (err, info) {
                    if (err) {return cb(err);}

                    return cb(null, info.response, html, text);
                });
            });
        });
    } else {
        // if we are testing don't send out an email instead return success and info response for inspection
        if (process.env.NODE_ENV === 'test') {
            return cb(null, '250 2.0.0 OK 1407018531 gc8sm23308604wic.3 - gsmtp');
        }
        self.transporter.sendMail(self.options.mail, function (err, info) {
            if (err) {return cb(err);}

            return cb(null, info.response);
        });
    }
};

/**
 * Shut down the connection pool so no more messages are send (use if you don't
 * want to use transport object anymore).
 *
 * @method close
 */
Mailer.prototype.close = function() {
    this.transporter.close();
};

/**
 * Retrieves the Mailer configuration options.
 *
 * @method getOptions
 * @returns {Object} the mailer configuration options
 */
Mailer.prototype.getOptions = function() {
    return this.options;
};<commit_msg>chore(comments): add more comments<commit_after>/**
 * The MIT License
 *
 * Copyright (c) 2014 Martin Micunda
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

'use strict';

/**
 * Module dependencies.
 */
var extend         = require('util')._extend,
    nodemailer     = require('nodemailer'),
    emailTemplates = require('email-templates');

/**
 * A node module for sending e-mails.
 *
 * @module mm-node-mailer
 * @example
 *  ```js
 *      var mailer = new mmMailer({
 *          config: {
 *              service: 'Gmail',
 *              auth: {
 *                  user: 'test@gmail.com',
 *                  pass: 'password'
 *              }
 *          },
 *          mail: {
 *              from: 'test@gmail.com',
 *              subject: 'Hello',
 *              text: 'Hello world!',
 *              html: '<b>Hello world!</b>'
 *          }
 *      });
 *      var mailOptions = {
 *          to: 'your.email@address.com',
 *      }
 *      mailer.send(mailOptions, function (err, respond) {
 *          if (err) return console.error(err);
 *          console.log('An e-mail has been sent successfully. Server responded with "' + respond + '"');
 *      });
 *  ```
 */
module.exports = Mailer;

/**
 * The default mailer options.
 *
 * @type {Object} defaultOptions - the default mailer options
 * @type {Object} defaultOptions.config - the nodemailer configuration. See {@link https://github.com/andris9/nodemailer-smtp-transport `nodemailer-smtp-transport`} for full description of config fields.
 * @type {Object} defaultOptions.mail - the to, from, cc, etc, fields for all emails. See {@link https://github.com/andris9/Nodemailer#e-mail-message-fields `e-mail-message-fields`} for full description of email fields.
 * @type {string} defaultOptions.mail.templateName - (optional) the template folder name that is store in templates directory
 * @type {Object} defaultOptions.mail.templateContent - (optional) the template JSON content
 * @type {string} defaultOptions.templatesDir - (optional) the templates directory path
 * @type {Object} defaultOptions.templateEngineOptions - (optional) the template engine options e.g. helpers, partials etc. See {@link https://github.com/niftylettuce/node-email-templates#templating-language-options-eg-ejs-custom-tags `node-email-templates`} for options.
 * @private
 */
var defaultOptions = {
    config: {},
    mail: {
        templateName: '',
        templateContent: {}
    },
    templatesDir: '',
    templateEngineOptions: {}
};

/**
 * A `Mailer` constructor.
 *
 * @class Mailer
 * @constructor
 *
 * @param {Object} options - custom mailer configuration options
 */
function Mailer(options) {
    if (!(this instanceof Mailer)) {return new Mailer(options);}

    // extend `defaultOptions` with custom `options`
    this.options = extend(defaultOptions, options);
    // create transporter that is able to send mail
    this.transporter = nodemailer.createTransport(options.config);
}

/**
 * Sends an email using the preselected transport object.
 *
 * @method send
 * @param {Object}   mailOptions - the email data options, see {@link https://github.com/andris9/Nodemailer#e-mail-message-fields `e-mail-message-fields`} for full description of email fields.
 * @param {Function} cb          - the callback function
 *
 * @returns {Function} the callback function
 */
Mailer.prototype.send = function(mailOptions, cb) {
    var self = this;

    // extend the default mail options with options passed by user
    self.options.mail = extend(self.options.mail, mailOptions);

    if(!self.options.mail.from) {
        return cb(new Error('Sender email address required'));
    }
    if(!self.options.mail.to) {
        return cb(new Error('Receiver email address required'));
    }

    // check if it should send text or template e-mail
    if(self.options.templatesDir && self.options.mail.templateName) {
        emailTemplates(self.options.templatesDir, self.options.templateEngineOptions, function (err, template) {
            if (err) {return cb(err);}

            template(self.options.mail.templateName, self.options.mail.templateContent, function (err, html, text) {
                if (err) {return cb(err);}

                self.options.mail.html = html;
                self.options.mail.text = text;

                // if we are testing don't send out an email instead return success, info response, html and txt strings for inspection
                if (process.env.NODE_ENV === 'test') {
                    return cb(null, '250 2.0.0 OK 1407018531 gc8sm23308604wic.3 - gsmtp', html, text);
                }

                self.transporter.sendMail(self.options.mail, function (err, info) {
                    if (err) {return cb(err);}

                    return cb(null, info.response, html, text);
                });
            });
        });
    } else {
        // if we are testing don't send out an email instead return success and info response for inspection
        if (process.env.NODE_ENV === 'test') {
            return cb(null, '250 2.0.0 OK 1407018531 gc8sm23308604wic.3 - gsmtp');
        }
        self.transporter.sendMail(self.options.mail, function (err, info) {
            if (err) {return cb(err);}

            return cb(null, info.response);
        });
    }
};

/**
 * Shut down the connection pool so no more messages are send (use if you don't
 * want to use transport object anymore).
 *
 * @method close
 */
Mailer.prototype.close = function() {
    this.transporter.close();
};

/**
 * Retrieves the Mailer configuration options.
 *
 * @method getOptions
 * @returns {Object} the mailer configuration options
 */
Mailer.prototype.getOptions = function() {
    return this.options;
};<|endoftext|>


<commit_before>// jshint esversion: 6, globalstrict: true, strict: true
'use strict';

const util = require('util');
const Transform = require('stream').Transform;
const PP = require('polygon-points');

function PamDiff(options) {
    if (!(this instanceof PamDiff)) {
        return new PamDiff(options);
    }
    Transform.call(this, {objectMode: true});
    this.setGrayscale(this._parseOptions('grayscale', options));//global option
    this.setDifference(this._parseOptions('difference', options));//global option, can be overridden per region
    this.setPercent(this._parseOptions('percent', options));//global option, can be overridden per region
    this.setRegions(this._parseOptions('regions', options));//can be no regions or a single region or multiple regions. if no regions, all pixels will be compared.
    this._parseChunk = this._parseFirstChunk;//first parsing will be reading settings and configuring internal pixel reading
    //todo add option for break on first region so that pixel is not measured in multiple overlapping regions
}

util.inherits(PamDiff, Transform);

PamDiff.prototype.setGrayscale = function (value) {
    switch (value) {
        case 'red' :
            this._grayscale = this._redToGray;
            break;
        case 'green' :
            this._grayscale = this._greenToGray;
            break;
        case 'blue' :
            this._grayscale = this._blueToGray;
            break;
        case 'desaturation' :
            this._grayscale = this._desaturationToGray;
            break;
        case 'luminosity' :
            this._grayscale = this._luminosityToGray;
            break;
        default :
            this._grayscale = this._averageToGray;
    }
};

PamDiff.prototype.setDifference = function (value) {
    this._difference = this._validateNumber(parseInt(value), 5, 1, 255);
};

PamDiff.prototype.setPercent = function (value) {
    this._percent = this._validateNumber(parseInt(value), 5, 1, 100);
};

PamDiff.prototype.setRegions = function (regions) {
    if (!regions) {
        if (this._regions) {
            delete this._regions;
            delete this._regionsLength;
        }
        this._diffs = 0;
        return;
    } else if (!Array.isArray(regions) || regions.length < 1) {
        throw new Error(`Regions must be an array of at least 1 region object {name: 'region1', difference: 10, percent: 10, polygon: [[0, 0], [0, 50], [50, 50], [50, 0]]}`);
    }
    this._regions = [];
    for (const region of regions) {
        if (!region.hasOwnProperty('name') || !region.hasOwnProperty('polygon')) {
            throw new Error('Region must include a name and a polygon property');
        }
        const polygonPoints = new PP(region.polygon);
        this._regions.push(
            {
                name: region.name,
                polygon: polygonPoints,
                pointsLength: polygonPoints.pointsLength(),
                difference: this._validateNumber(parseInt(region.difference), this._difference, 1, 255),
                percent: this._validateNumber(parseInt(region.percent), this._percent, 1, 100),
                diffs: 0
            }
        );
    }
    this._regionsLength = this._regions.length;
};

PamDiff.prototype._parseOptions = function (option, options) {
    if (options && options.hasOwnProperty(option)) {
        return options[option];
    }
    return null;
};

PamDiff.prototype._validateNumber = function (number, def, low, high) {
    if (isNaN(number)) {
        return def;
    } else if (number < low) {
        return low;
    } else if (number > high) {
        return high;
    } else {
        return number;
    }
};

//convert rgb to gray
PamDiff.prototype._averageToGray = function (r, g, b) {
    return (r + g + b) / 3;
};

//convert rgb to gray
PamDiff.prototype._desaturationToGray = function (r, g, b) {
    return (Math.max(r, g, b) + Math.min(r, g, b)) / 2;
};

//convert rgb to gray
PamDiff.prototype._luminosityToGray = function (r, g, b) {
    //return 0.299 * r + 0.587 * g + 0.114 * b;
    return 0.3 * r + 0.59 * g + 0.11 * b;
};

//convert rgb to gray
PamDiff.prototype._redToGray = function (r, g, b) {
    return r;
};

//convert rgb to gray
PamDiff.prototype._greenToGray = function (r, g, b) {
    return g;
};

//convert rgb to gray
PamDiff.prototype._blueToGray = function (r, g, b) {
    return b;
};

PamDiff.prototype._blackAndWhitePixelDiff = function (chunk) {
    this._newPix = chunk.pixels;
    for (let y = 0, i = 0; y < this._height; y++) {
        for (let x = 0; x < this._width; x++, i++) {
            if (this._regions) {
                for (let j = 0; j < this._regionsLength; j++) {
                    if (this._regions[j].polygon.containsPoint([x,y]) === true) {
                        if (this._oldPix[i] !== this._newPix[i]) {
                            this._regions[j].diffs++;
                        }
                        break;//todo add option for break on first region
                    }
                }
            } else {
                if (this._oldPix[i] !== this._newPix[i]) {
                    this._diffs++;
                }
            }
        }
    }
    if (this._regions) {
        const regionDiffArray = [];
        for (let i = 0; i < this._regionsLength; i++) {
            const percent = Math.ceil(this._regions[i].diffs / this._regions[i].pointsLength * 100);
            if (percent >= this._regions[i].percent) {
                regionDiffArray.push({name: this._regions[i].name, percent: percent});
            }
            this._regions[i].diffs = 0;
        }
        if (regionDiffArray.length > 0) {
            this.emit('diff', {trigger: regionDiffArray, pam: chunk.pam});
        }
    } else {
        const percent = Math.ceil(this._diffs / this._length * 100);
        if (percent >= this._percent) {
            this.emit('diff', {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam});
        }
        this._diffs = 0;
    }
    this._oldPix = this._newPix;
};

PamDiff.prototype._grayScalePixelDiff = function (chunk) {
    this._newPix = chunk.pixels;
    for (let y = 0, i = 0; y < this._height; y++) {
        for (let x = 0; x < this._width; x++, i++) {
            if (this._regions) {
                for (let j = 0; j < this._regionsLength; j++) {
                    if (this._regions[j].polygon.containsPoint([x,y]) === true) {
                        if (Math.abs(this._oldPix[i] - this._newPix[i]) >= this._regions[j].difference) {
                            this._regions[j].diffs++;
                        }
                        break;//todo add option for break on first region
                    }
                }
            } else {
                if (Math.abs(this._oldPix[i] - this._newPix[i]) >= this._difference) {
                    this._diffs++;
                }
            }
        }
    }
    if (this._regions) {
        const regionDiffArray = [];
        for (let i = 0; i < this._regionsLength; i++) {
            const percent = Math.ceil(this._regions[i].diffs / this._regions[i].pointsLength * 100);
            if (percent >= this._regions[i].percent) {
                regionDiffArray.push({name: this._regions[i].name, percent: percent});
            }
            this._regions[i].diffs = 0;
        }
        if (regionDiffArray.length > 0) {
            this.emit('diff', {trigger: regionDiffArray, pam: chunk.pam});
        }
    } else {
        const percent = Math.ceil(this._diffs / this._length * 100);
        if (percent >= this._percent) {
            this.emit('diff', {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam});
        }
        this._diffs = 0;
    }
    this._oldPix = this._newPix;
};

PamDiff.prototype._rgbPixelDiff = function (chunk) {
    this._newPix = chunk.pixels;
    for (let y = 0, i = 0; y < this._height; y++) {
        for (let x = 0; x < this._width; x++, i += 3) {
            if (this._regions) {
                for (let j = 0; j < this._regionsLength; j++) {
                    if (this._regions[j].polygon.containsPoint([x,y]) === true) {
                        if (Math.abs(this._grayscale(this._oldPix[i], this._oldPix[i + 1], this._oldPix[i + 2]) - this._grayscale(this._newPix[i], this._newPix[i + 1], this._newPix[i + 2])) >= this._regions[j].difference) {
                            this._regions[j].diffs++;
                        }
                        break;//todo add option for break on first region
                    }
                }
            } else {
                if (Math.abs(this._oldPix[i] - this._newPix[i]) >= this._difference) {
                    this._diffs++;
                }
            }
        }
    }
    if (this._regions) {
        const regionDiffArray = [];
        for (let i = 0; i < this._regionsLength; i++) {
            const percent = Math.ceil(this._regions[i].diffs / this._regions[i].pointsLength * 100);
            if (percent >= this._regions[i].percent) {
                regionDiffArray.push({name: this._regions[i].name, percent: percent});
            }
            this._regions[i].diffs = 0;
        }
        if (regionDiffArray.length > 0) {
            this.emit('diff', {trigger: regionDiffArray, pam: chunk.pam});
        }
    } else {
        const percent = Math.ceil(this._diffs / this._length * 100);
        if (percent >= this._percent) {
            this.emit('diff', {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam});
        }
        this._diffs = 0;
    }
    this._oldPix = this._newPix;
};

PamDiff.prototype._rgbAlphaPixelDiff = function (chunk) {
    this._newPix = chunk.pixels;
    for (let y = 0, i = 0; y < this._height; y++) {
        for (let x = 0; x < this._width; x++, i += 4) {
            if (this._regions) {
                for (let j = 0; j < this._regionsLength; j++) {
                    if (this._regions[j].polygon.containsPoint([x,y]) === true) {
                        if (Math.abs(this._grayscale(this._oldPix[i], this._oldPix[i + 1], this._oldPix[i + 2]) - this._grayscale(this._newPix[i], this._newPix[i + 1], this._newPix[i + 2])) >= this._regions[j].difference) {
                            this._regions[j].diffs++;
                        }
                        break;//todo add option for break on first region
                    }
                }
            } else {
                if (Math.abs(this._oldPix[i] - this._newPix[i]) >= this._difference) {
                    this._diffs++;
                }
            }
        }
    }
    if (this._regions) {
        const regionDiffArray = [];
        for (let i = 0; i < this._regionsLength; i++) {
            const percent = Math.ceil(this._regions[i].diffs / this._regions[i].pointsLength * 100);
            if (percent >= this._regions[i].percent) {
                regionDiffArray.push({name: this._regions[i].name, percent: percent});
            }
            this._regions[i].diffs = 0;
        }
        if (regionDiffArray.length > 0) {
            this.emit('diff', {trigger: regionDiffArray, pam: chunk.pam});
        }
    } else {
        const percent = Math.ceil(this._diffs / this._length * 100);
        if (percent >= this._percent) {
            this.emit('diff', {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam});
        }
        this._diffs = 0;
    }
    this._oldPix = this._newPix;
};

PamDiff.prototype._parseFirstChunk = function (chunk) {
    this._width = parseInt(chunk.width);
    this._height = parseInt(chunk.height);
    this._oldPix = chunk.pixels;
    this._length = this._oldPix.length;
    switch (chunk.tupltype) {
        case 'blackandwhite' :
            this._parseChunk = this._blackAndWhitePixelDiff;
            break;
        case 'grayscale' :
            this._parseChunk = this._grayScalePixelDiff;
            break;
        case 'rgb' :
            this._parseChunk = this._rgbPixelDiff;
            //this._increment = 3;//future use
            break;
        case 'rgb_alpha' :
            this._parseChunk = this._rgbAlphaPixelDiff;
            //this._increment = 4;//future use
            break;
        default :
            throw Error(`Unsupported tupltype: ${chunk.tupltype}. Supported tupltypes include grayscale(gray), blackandwhite(monob), rgb(rgb24), and rgb_alpha(rgba).`);
    }
};

PamDiff.prototype._transform = function (chunk, encoding, callback) {
    this._parseChunk(chunk);
    callback();
};

PamDiff.prototype._flush = function (callback) {
    delete this._oldPix;
    delete this._newPix;
    delete this._width;
    delete this._length;
    this._parseChunk = this._parseFirstChunk;
    callback();
};

module.exports = PamDiff;
//todo get bounding box of all regions combined to exclude some pixels before checking if they exist inside specific regions<commit_msg>corrected rgb grayscaling<commit_after>// jshint esversion: 6, globalstrict: true, strict: true
'use strict';

const util = require('util');
const Transform = require('stream').Transform;
const PP = require('polygon-points');

function PamDiff(options) {
    if (!(this instanceof PamDiff)) {
        return new PamDiff(options);
    }
    Transform.call(this, {objectMode: true});
    this.setGrayscale(this._parseOptions('grayscale', options));//global option
    this.setDifference(this._parseOptions('difference', options));//global option, can be overridden per region
    this.setPercent(this._parseOptions('percent', options));//global option, can be overridden per region
    this.setRegions(this._parseOptions('regions', options));//can be no regions or a single region or multiple regions. if no regions, all pixels will be compared.
    this._parseChunk = this._parseFirstChunk;//first parsing will be reading settings and configuring internal pixel reading
    //todo add option for break on first region so that pixel is not measured in multiple overlapping regions
}

util.inherits(PamDiff, Transform);

PamDiff.prototype.setGrayscale = function (value) {
    switch (value) {
        case 'red' :
            this._grayscale = this._redToGray;
            break;
        case 'green' :
            this._grayscale = this._greenToGray;
            break;
        case 'blue' :
            this._grayscale = this._blueToGray;
            break;
        case 'desaturation' :
            this._grayscale = this._desaturationToGray;
            break;
        case 'luminosity' :
            this._grayscale = this._luminosityToGray;
            break;
        default :
            this._grayscale = this._averageToGray;
    }
};

PamDiff.prototype.setDifference = function (value) {
    this._difference = this._validateNumber(parseInt(value), 5, 1, 255);
};

PamDiff.prototype.setPercent = function (value) {
    this._percent = this._validateNumber(parseInt(value), 5, 1, 100);
};

PamDiff.prototype.setRegions = function (regions) {
    if (!regions) {
        if (this._regions) {
            delete this._regions;
            delete this._regionsLength;
        }
        this._diffs = 0;
        return;
    } else if (!Array.isArray(regions) || regions.length < 1) {
        throw new Error(`Regions must be an array of at least 1 region object {name: 'region1', difference: 10, percent: 10, polygon: [[0, 0], [0, 50], [50, 50], [50, 0]]}`);
    }
    this._regions = [];
    for (const region of regions) {
        if (!region.hasOwnProperty('name') || !region.hasOwnProperty('polygon')) {
            throw new Error('Region must include a name and a polygon property');
        }
        const polygonPoints = new PP(region.polygon);
        this._regions.push(
            {
                name: region.name,
                polygon: polygonPoints,
                pointsLength: polygonPoints.pointsLength(),
                difference: this._validateNumber(parseInt(region.difference), this._difference, 1, 255),
                percent: this._validateNumber(parseInt(region.percent), this._percent, 1, 100),
                diffs: 0
            }
        );
    }
    this._regionsLength = this._regions.length;
};

PamDiff.prototype._parseOptions = function (option, options) {
    if (options && options.hasOwnProperty(option)) {
        return options[option];
    }
    return null;
};

PamDiff.prototype._validateNumber = function (number, def, low, high) {
    if (isNaN(number)) {
        return def;
    } else if (number < low) {
        return low;
    } else if (number > high) {
        return high;
    } else {
        return number;
    }
};

//convert rgb to gray
PamDiff.prototype._averageToGray = function (r, g, b) {
    return (r + g + b) / 3;
};

//convert rgb to gray
PamDiff.prototype._desaturationToGray = function (r, g, b) {
    return (Math.max(r, g, b) + Math.min(r, g, b)) / 2;
};

//convert rgb to gray
PamDiff.prototype._luminosityToGray = function (r, g, b) {
    //return 0.299 * r + 0.587 * g + 0.114 * b;
    return 0.3 * r + 0.59 * g + 0.11 * b;
};

//convert rgb to gray
PamDiff.prototype._redToGray = function (r, g, b) {
    return r;
};

//convert rgb to gray
PamDiff.prototype._greenToGray = function (r, g, b) {
    return g;
};

//convert rgb to gray
PamDiff.prototype._blueToGray = function (r, g, b) {
    return b;
};

PamDiff.prototype._blackAndWhitePixelDiff = function (chunk) {
    this._newPix = chunk.pixels;
    for (let y = 0, i = 0; y < this._height; y++) {
        for (let x = 0; x < this._width; x++, i++) {
            if (this._regions) {
                for (let j = 0; j < this._regionsLength; j++) {
                    if (this._regions[j].polygon.containsPoint([x,y]) === true) {
                        if (this._oldPix[i] !== this._newPix[i]) {
                            this._regions[j].diffs++;
                        }
                        break;//todo add option for break on first region
                    }
                }
            } else {
                if (this._oldPix[i] !== this._newPix[i]) {
                    this._diffs++;
                }
            }
        }
    }
    if (this._regions) {
        const regionDiffArray = [];
        for (let i = 0; i < this._regionsLength; i++) {
            const percent = Math.ceil(this._regions[i].diffs / this._regions[i].pointsLength * 100);
            if (percent >= this._regions[i].percent) {
                regionDiffArray.push({name: this._regions[i].name, percent: percent});
            }
            this._regions[i].diffs = 0;
        }
        if (regionDiffArray.length > 0) {
            this.emit('diff', {trigger: regionDiffArray, pam: chunk.pam});
        }
    } else {
        const percent = Math.ceil(this._diffs / this._length * 100);
        if (percent >= this._percent) {
            this.emit('diff', {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam});
        }
        this._diffs = 0;
    }
    this._oldPix = this._newPix;
};

PamDiff.prototype._grayScalePixelDiff = function (chunk) {
    this._newPix = chunk.pixels;
    for (let y = 0, i = 0; y < this._height; y++) {
        for (let x = 0; x < this._width; x++, i++) {
            if (this._regions) {
                for (let j = 0; j < this._regionsLength; j++) {
                    if (this._regions[j].polygon.containsPoint([x,y]) === true) {
                        if (Math.abs(this._oldPix[i] - this._newPix[i]) >= this._regions[j].difference) {
                            this._regions[j].diffs++;
                        }
                        break;//todo add option for break on first region
                    }
                }
            } else {
                if (Math.abs(this._oldPix[i] - this._newPix[i]) >= this._difference) {
                    this._diffs++;
                }
            }
        }
    }
    if (this._regions) {
        const regionDiffArray = [];
        for (let i = 0; i < this._regionsLength; i++) {
            const percent = Math.ceil(this._regions[i].diffs / this._regions[i].pointsLength * 100);
            if (percent >= this._regions[i].percent) {
                regionDiffArray.push({name: this._regions[i].name, percent: percent});
            }
            this._regions[i].diffs = 0;
        }
        if (regionDiffArray.length > 0) {
            this.emit('diff', {trigger: regionDiffArray, pam: chunk.pam});
        }
    } else {
        const percent = Math.ceil(this._diffs / this._length * 100);
        if (percent >= this._percent) {
            this.emit('diff', {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam});
        }
        this._diffs = 0;
    }
    this._oldPix = this._newPix;
};

PamDiff.prototype._rgbPixelDiff = function (chunk) {
    this._newPix = chunk.pixels;
    for (let y = 0, i = 0; y < this._height; y++) {
        for (let x = 0; x < this._width; x++, i += 3) {
            if (this._regions) {
                for (let j = 0; j < this._regionsLength; j++) {
                    if (this._regions[j].polygon.containsPoint([x,y]) === true) {
                        if (Math.abs(this._grayscale(this._oldPix[i], this._oldPix[i + 1], this._oldPix[i + 2]) - this._grayscale(this._newPix[i], this._newPix[i + 1], this._newPix[i + 2])) >= this._regions[j].difference) {
                            this._regions[j].diffs++;
                        }
                        break;//todo add option for break on first region
                    }
                }
            } else {
                if (Math.abs(this._grayscale(this._oldPix[i], this._oldPix[i + 1], this._oldPix[i + 2]) - this._grayscale(this._newPix[i], this._newPix[i + 1], this._newPix[i + 2])) >= this._difference) {
                    this._diffs++;
                }
            }
        }
    }
    if (this._regions) {
        const regionDiffArray = [];
        for (let i = 0; i < this._regionsLength; i++) {
            const percent = Math.ceil(this._regions[i].diffs / this._regions[i].pointsLength * 100);
            if (percent >= this._regions[i].percent) {
                regionDiffArray.push({name: this._regions[i].name, percent: percent});
            }
            this._regions[i].diffs = 0;
        }
        if (regionDiffArray.length > 0) {
            this.emit('diff', {trigger: regionDiffArray, pam: chunk.pam});
        }
    } else {
        const percent = Math.ceil(this._diffs / this._length * 100);
        if (percent >= this._percent) {
            this.emit('diff', {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam});
        }
        this._diffs = 0;
    }
    this._oldPix = this._newPix;
};

PamDiff.prototype._rgbAlphaPixelDiff = function (chunk) {
    this._newPix = chunk.pixels;
    for (let y = 0, i = 0; y < this._height; y++) {
        for (let x = 0; x < this._width; x++, i += 4) {
            if (this._regions) {
                for (let j = 0; j < this._regionsLength; j++) {
                    if (this._regions[j].polygon.containsPoint([x,y]) === true) {
                        if (Math.abs(this._grayscale(this._oldPix[i], this._oldPix[i + 1], this._oldPix[i + 2]) - this._grayscale(this._newPix[i], this._newPix[i + 1], this._newPix[i + 2])) >= this._regions[j].difference) {
                            this._regions[j].diffs++;
                        }
                        break;//todo add option for break on first region
                    }
                }
            } else {
                if (Math.abs(this._grayscale(this._oldPix[i], this._oldPix[i + 1], this._oldPix[i + 2]) - this._grayscale(this._newPix[i], this._newPix[i + 1], this._newPix[i + 2])) >= this._difference) {
                    this._diffs++;
                }
            }
        }
    }
    if (this._regions) {
        const regionDiffArray = [];
        for (let i = 0; i < this._regionsLength; i++) {
            const percent = Math.ceil(this._regions[i].diffs / this._regions[i].pointsLength * 100);
            if (percent >= this._regions[i].percent) {
                regionDiffArray.push({name: this._regions[i].name, percent: percent});
            }
            this._regions[i].diffs = 0;
        }
        if (regionDiffArray.length > 0) {
            this.emit('diff', {trigger: regionDiffArray, pam: chunk.pam});
        }
    } else {
        const percent = Math.ceil(this._diffs / this._length * 100);
        if (percent >= this._percent) {
            this.emit('diff', {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam});
        }
        this._diffs = 0;
    }
    this._oldPix = this._newPix;
};

PamDiff.prototype._parseFirstChunk = function (chunk) {
    this._width = parseInt(chunk.width);
    this._height = parseInt(chunk.height);
    this._oldPix = chunk.pixels;
    this._length = this._oldPix.length;
    switch (chunk.tupltype) {
        case 'blackandwhite' :
            this._parseChunk = this._blackAndWhitePixelDiff;
            break;
        case 'grayscale' :
            this._parseChunk = this._grayScalePixelDiff;
            break;
        case 'rgb' :
            this._parseChunk = this._rgbPixelDiff;
            //this._increment = 3;//future use
            break;
        case 'rgb_alpha' :
            this._parseChunk = this._rgbAlphaPixelDiff;
            //this._increment = 4;//future use
            break;
        default :
            throw Error(`Unsupported tupltype: ${chunk.tupltype}. Supported tupltypes include grayscale(gray), blackandwhite(monob), rgb(rgb24), and rgb_alpha(rgba).`);
    }
};

PamDiff.prototype._transform = function (chunk, encoding, callback) {
    this._parseChunk(chunk);
    callback();
};

PamDiff.prototype._flush = function (callback) {
    delete this._oldPix;
    delete this._newPix;
    delete this._width;
    delete this._length;
    this._parseChunk = this._parseFirstChunk;
    callback();
};

module.exports = PamDiff;
//todo get bounding box of all regions combined to exclude some pixels before checking if they exist inside specific regions<|endoftext|>


<commit_before>'use strict';

const repl = require('repl');
const io = require('socket.io');

let randInt = (min, max) => Math.floor(Math.random() * (max - min)) + min;
let randChoose = choices => choices[randInt(0, choices.length)];
class Player {
  constructor(server, id) {
    let config = server.config;
    this.id = id;
    this.clan = null;
    this.server = server;
    this.untilSend = 1;

    this.lastPing = new Date(2017, 9, 7, 5, 57, 0);.

    this.name = 'unknown';
    this.skin = 0;
    this.size = config.playerScale;

    this.aimAngle = 0;
    this.movement = null;
    this.kill();
  }
  updateMovement(delta) {
    let config = this.server.config;
    let tx = 0;
    let ty = 0;
    if (this.movement != null) {
      tx = Math.cos(this.movement);
      ty = Math.sin(this.movement);
    }
    this.vx *= Math.pow(config.playerDecel, delta);
    this.vy *= Math.pow(config.playerDecel, delta);
    this.vx += tx * config.playerSpeed * delta * this.size / 2;
    this.vy += ty * config.playerSpeed * delta * this.size / 2;
    this.x += this.vx;
    this.y += this.vy;
  }
  update(delta) {
    if (this.alive) {
      this.updateMovement(delta);
      this.untilSend--;
      if (!this.untilSend) {
        this.untilSend = this.server.config.clientSendRate;
        this.sendPosition();
      }
    }
  }
  sendPosition() {
    let socket = this.socket;
    socket.emit('a');
    socket.emit('3', [
      this.id,
      this.x,
      this.y, -2.26,-1,0,0,null,0,0,0,0]);
  }
  link(socket) {
    this.socket = socket;
    socket.once('error', err => {
      console.log(err);
      this.destroy();
    });

    var emit = (...arg) => {
      try {
        socket.broadcast.emit(...arg)
        socket.emit(...arg)
      } catch (e) {
        sockets.forEach(a => a.emit(...arg))
      }
    }

    socket.on('2', angle => this.aimAngle = angle);
    socket.on('3', angle => this.movement = angle);

    socket.on("14", data => {
      var dif = Math.abs(this.lastPing.getTime() - new Date().getTime());

      if (dif < 2500) {
        this.lastPing = new Date();
        emit("p",this.x,this.y);
      }
    });

    socket.once('disconnect', () => this.destroy());
    socket.emit('id', {
      teams: this.server.clans
    });
    socket.on('1', data => {
      this.name = data.name.length > 15 || !data.name ? 'unknown' : data.name;
      this.skin = data.skin;
      this.spawn();
    });
  }
  destroy() {
    this.kill();
    this.server.remove(this.id);
    this.socket.disconnect();
  }
  kill() {
    this.alive = false;
    this.x = 0;
    this.y = 0;
    this.slowDown();
  }
  slowDown() {
    this.vx = 0;
    this.vy = 0;
  }
  spawn() {
    let config = this.server.config;
    let socket = this.socket;
    this.alive = true;
    let { x, y } = this.server.allocatePosition(this.size);
    this.x = x;
    this.y = y;
    this.slowDown();
    socket.emit('1', this.id);
    socket.emit('2', [socket.id,this.id,this.name,this.x,this.y,0,100,100,this.size,this.skin],true);
  }
  hitResource(type) {

  }
}
class Resource {
  constructor(server, x, y, size, type) {
    let config = server.config;
    this.x = x;
    this.y = y;
    this.type = config.resourceTypes.indexOf(type);
    this.size = size;
  }
  reward(by) {
    by.hitResource(this.type);
  }
}
class Server {
  constructor(config) {
    this.config = config;
    this.players = Array(config.maxPlayers).fill(null);
    this.lastRun = Date.now();
    this.clans = [];
    this.objects = [];
    setInterval(() => this.update(), config.serverUpdateRate);
    this.generateWorld();
  }
  remove(sid) {
    this.players[sid] = null;
  }
  update() {
    let now = Date.now();
    let delta = now - this.lastRun;
    this.lastRun = now;
    for (let i of this.players) {
      if (i != null) {
        i.update(delta);
      }
    }
  }
  generateWorld() {
    let config = this.config;
    let areaCount = config.areaCount;
    let mapScale = config.mapScale;
    let areaSize = mapScale / areaCount;
    let all = [];
    for (let afx = 0, atx = 1; afx < areaCount; afx++, atx++) {
      for (let afy = 0, aty = 1; afy < areaCount; afy++, aty++) {
        for (let i = 0; i < config.treesPerArea; i++) {
          let x = randInt(areaSize * afx, areaSize * atx);
          let y = randInt(areaSize * afy, areaSize * aty);
          all.push(new Resource(this, x, y, randChoose(config.treeScales), 'wood'));
        }
        for (let i = 0; i < config.bushesPerArea; i++) {
          let x = randInt(areaSize * afx, areaSize * atx);
          let y = randInt(areaSize * afy, areaSize * aty);
          all.push(new Resource(this, x, y, randChoose(config.bushScales), 'food'));
        }
      }
    }
    for (let i = 0; i < config.totalRocks; i++) {
      let x = randInt(0, mapScale);
      let y = randInt(0, mapScale);
      all.push(new Resource(this, x, y, randChoose(config.rockScales), 'stone'));
    }
    for (let i = 0; i < config.goldOres; i++) {
      let x = randInt(0, mapScale);
      let y = randInt(0, mapScale);
      all.push(new Resource(this, x, y, 0, 'points'));
    }
    this.objects = all;
  }
  allocatePosition(size) {
    let scale = this.config.mapScale;
    let x = 0;
    let y = 0;
    while (true) {
      x = randInt(0, scale);
      y = randInt(0, scale);
      if (true) { // check if there's nothing overlapping
        break;
      }
    }
    return { x, y };
  }
  handle(socket) {
    for (let i = 0; i < this.players.length; i++) {
      if (this.players[i] == null) {
        let player = new Player(this, i);
        player.link(socket);
        this.players[i] = player;
        break;
      }
    }
  }
}

let app = new Server({
  mapScale: 14400,
  maxPlayers: 50,
  clientSendRate: 5,
  serverUpdateRate: 9,
  playerDecel: 0.993,
  playerSpeed: 0.0016,
  playerScale: 35,
  areaCount: 7,
  treesPerArea: 9,
  bushesPerArea: 3,
  totalRocks: 32,
  goldOres: 7,
  resourceTypes: ['wood', 'food', 'stone', 'points'],
  treeScales: [140, 145, 150, 155],
  bushScales: [80, 85, 95],
  rockScales: [80, 85, 90],
});

for (let i = 5000; i <= 5010; i++) {
  io(i).on('connection', socket => app.handle(socket));
}
/*
let teams = [];
let sockets = [];
  io(i).on('connection', function (socket) {
    let emit = (...arg) => {
      try {
        socket.broadcast.emit(...arg);
        socket.emit(...arg);
      } catch (e) {
        sockets.forEach(a => a.emit(...arg));
      }
    };
    sockets.push(socket);
    socket.on('1', function (data) {
      let id = 27;
      let name = data.name.length > 15 ? 'unknown' : data.name;
      socket.emit('id', {
        "teams": teams
      });
      socket.emit('1', id);
      socket.emit('mm', 0);
      socket.emit('3', []);
      let x = randInt(0, config.mapScale);
      let y = randInt(0, config.mapScale);
      let userteam = null;
      let last = 0;
      socket.emit("2", ["a3Pm5dMzeKOjc5gvAJEF", id, name, x, 7200, 0, 100, 100, 35, data.skin], true);
      socket.emit("5", [27, data.name, 9001]); //[5,"RIP",31988,45,"KADEJO503",23404,34,"winter wolf",4821,28,"Godenot",4500,33,"Arena Closer",3000,32,"LightTheif",2940,6,"CarlosKoS-_-16",2800,4,"GD desconhecido",2635,35,"jack black GD",2357,19,"AMIGO BOM",1623])
      socket.emit("6", [
        29, 3115.1, 13592.9, 0, 109.2, 1, null, -1,
        353, 4103, 13436, 0, 80, 2, null, -1,
        339, 2498, 14155, 0, 90, 2, null, -1
      ]);
      socket.emit("8", 2.6, 28);
      socket.on("ch", data => {
        emit("ch", id, data);
      });
      socket.on("8", data => {
        console.log('CLAN CREATE REQUEST:', data);
        teams.push([{
          "sid": data,
          "owner": id
        }]);
        let userteam = data;
        emit("st", data, true);
      });
      socket.on("3", function (data) {
        if (data == null) {
          vx = vy = 0;
          return;
        } else if (x < 0 || x > config.mapScale || y < 0 || y > config.mapScale) {
          x = 7200;
          y = 7200;
        }
        if (y >= 6840 && y <= 7560) {
          x += 0.44;
        }
        let speed = y > 2400 ? 60 * 0.8 : 60;
        vx = Math.cos(data) * speed;
        vy = Math.sin(data) * speed;
        x += vx;
        y += vy;
        socket.emit('a');
        socket.emit("3", [id, x, y, -2.26, -1, 0, 0, null, 0, 0, 0, 0]);
      });
      socket.on("14", function (data) {
        console.log('ping!!!', arguments);
      });
    });
  });
}
*/
repl.start({
  eval: (a, _c, _f, cb) => {
    try {
      cb(null, eval(a)); // jshint ignore: line
    } catch (e) {
      cb(e);
    }
  }
});
<commit_msg>Fixed a random period<commit_after>'use strict';

const repl = require('repl');
const io = require('socket.io');

let randInt = (min, max) => Math.floor(Math.random() * (max - min)) + min;
let randChoose = choices => choices[randInt(0, choices.length)];
class Player {
  constructor(server, id) {
    let config = server.config;
    this.id = id;
    this.clan = null;
    this.server = server;
    this.untilSend = 1;

    this.lastPing = new Date(2017, 9, 7, 5, 57, 0);

    this.name = 'unknown';
    this.skin = 0;
    this.size = config.playerScale;

    this.aimAngle = 0;
    this.movement = null;
    this.kill();
  }
  updateMovement(delta) {
    let config = this.server.config;
    let tx = 0;
    let ty = 0;
    if (this.movement != null) {
      tx = Math.cos(this.movement);
      ty = Math.sin(this.movement);
    }
    this.vx *= Math.pow(config.playerDecel, delta);
    this.vy *= Math.pow(config.playerDecel, delta);
    this.vx += tx * config.playerSpeed * delta * this.size / 2;
    this.vy += ty * config.playerSpeed * delta * this.size / 2;
    this.x += this.vx;
    this.y += this.vy;
  }
  update(delta) {
    if (this.alive) {
      this.updateMovement(delta);
      this.untilSend--;
      if (!this.untilSend) {
        this.untilSend = this.server.config.clientSendRate;
        this.sendPosition();
      }
    }
  }
  sendPosition() {
    let socket = this.socket;
    socket.emit('a');
    socket.emit('3', [
      this.id,
      this.x,
      this.y, -2.26,-1,0,0,null,0,0,0,0]);
  }
  link(socket) {
    this.socket = socket;
    socket.once('error', err => {
      console.log(err);
      this.destroy();
    });

    var emit = (...arg) => {
      try {
        socket.broadcast.emit(...arg)
        socket.emit(...arg)
      } catch (e) {
        sockets.forEach(a => a.emit(...arg))
      }
    }

    socket.on('2', angle => this.aimAngle = angle);
    socket.on('3', angle => this.movement = angle);

    socket.on("14", data => {
      var dif = Math.abs(this.lastPing.getTime() - new Date().getTime());

      if (dif < 2500) {
        this.lastPing = new Date();
        emit("p",this.x,this.y);
      }
    });

    socket.once('disconnect', () => this.destroy());
    socket.emit('id', {
      teams: this.server.clans
    });
    socket.on('1', data => {
      this.name = data.name.length > 15 || !data.name ? 'unknown' : data.name;
      this.skin = data.skin;
      this.spawn();
    });
  }
  destroy() {
    this.kill();
    this.server.remove(this.id);
    this.socket.disconnect();
  }
  kill() {
    this.alive = false;
    this.x = 0;
    this.y = 0;
    this.slowDown();
  }
  slowDown() {
    this.vx = 0;
    this.vy = 0;
  }
  spawn() {
    let config = this.server.config;
    let socket = this.socket;
    this.alive = true;
    let { x, y } = this.server.allocatePosition(this.size);
    this.x = x;
    this.y = y;
    this.slowDown();
    socket.emit('1', this.id);
    socket.emit('2', [socket.id,this.id,this.name,this.x,this.y,0,100,100,this.size,this.skin],true);
  }
  hitResource(type) {

  }
}
class Resource {
  constructor(server, x, y, size, type) {
    let config = server.config;
    this.x = x;
    this.y = y;
    this.type = config.resourceTypes.indexOf(type);
    this.size = size;
  }
  reward(by) {
    by.hitResource(this.type);
  }
}
class Server {
  constructor(config) {
    this.config = config;
    this.players = Array(config.maxPlayers).fill(null);
    this.lastRun = Date.now();
    this.clans = [];
    this.objects = [];
    setInterval(() => this.update(), config.serverUpdateRate);
    this.generateWorld();
  }
  remove(sid) {
    this.players[sid] = null;
  }
  update() {
    let now = Date.now();
    let delta = now - this.lastRun;
    this.lastRun = now;
    for (let i of this.players) {
      if (i != null) {
        i.update(delta);
      }
    }
  }
  generateWorld() {
    let config = this.config;
    let areaCount = config.areaCount;
    let mapScale = config.mapScale;
    let areaSize = mapScale / areaCount;
    let all = [];
    for (let afx = 0, atx = 1; afx < areaCount; afx++, atx++) {
      for (let afy = 0, aty = 1; afy < areaCount; afy++, aty++) {
        for (let i = 0; i < config.treesPerArea; i++) {
          let x = randInt(areaSize * afx, areaSize * atx);
          let y = randInt(areaSize * afy, areaSize * aty);
          all.push(new Resource(this, x, y, randChoose(config.treeScales), 'wood'));
        }
        for (let i = 0; i < config.bushesPerArea; i++) {
          let x = randInt(areaSize * afx, areaSize * atx);
          let y = randInt(areaSize * afy, areaSize * aty);
          all.push(new Resource(this, x, y, randChoose(config.bushScales), 'food'));
        }
      }
    }
    for (let i = 0; i < config.totalRocks; i++) {
      let x = randInt(0, mapScale);
      let y = randInt(0, mapScale);
      all.push(new Resource(this, x, y, randChoose(config.rockScales), 'stone'));
    }
    for (let i = 0; i < config.goldOres; i++) {
      let x = randInt(0, mapScale);
      let y = randInt(0, mapScale);
      all.push(new Resource(this, x, y, 0, 'points'));
    }
    this.objects = all;
  }
  allocatePosition(size) {
    let scale = this.config.mapScale;
    let x = 0;
    let y = 0;
    while (true) {
      x = randInt(0, scale);
      y = randInt(0, scale);
      if (true) { // check if there's nothing overlapping
        break;
      }
    }
    return { x, y };
  }
  handle(socket) {
    for (let i = 0; i < this.players.length; i++) {
      if (this.players[i] == null) {
        let player = new Player(this, i);
        player.link(socket);
        this.players[i] = player;
        break;
      }
    }
  }
}

let app = new Server({
  mapScale: 14400,
  maxPlayers: 50,
  clientSendRate: 5,
  serverUpdateRate: 9,
  playerDecel: 0.993,
  playerSpeed: 0.0016,
  playerScale: 35,
  areaCount: 7,
  treesPerArea: 9,
  bushesPerArea: 3,
  totalRocks: 32,
  goldOres: 7,
  resourceTypes: ['wood', 'food', 'stone', 'points'],
  treeScales: [140, 145, 150, 155],
  bushScales: [80, 85, 95],
  rockScales: [80, 85, 90],
});

for (let i = 5000; i <= 5010; i++) {
  io(i).on('connection', socket => app.handle(socket));
}
/*
let teams = [];
let sockets = [];
  io(i).on('connection', function (socket) {
    let emit = (...arg) => {
      try {
        socket.broadcast.emit(...arg);
        socket.emit(...arg);
      } catch (e) {
        sockets.forEach(a => a.emit(...arg));
      }
    };
    sockets.push(socket);
    socket.on('1', function (data) {
      let id = 27;
      let name = data.name.length > 15 ? 'unknown' : data.name;
      socket.emit('id', {
        "teams": teams
      });
      socket.emit('1', id);
      socket.emit('mm', 0);
      socket.emit('3', []);
      let x = randInt(0, config.mapScale);
      let y = randInt(0, config.mapScale);
      let userteam = null;
      let last = 0;
      socket.emit("2", ["a3Pm5dMzeKOjc5gvAJEF", id, name, x, 7200, 0, 100, 100, 35, data.skin], true);
      socket.emit("5", [27, data.name, 9001]); //[5,"RIP",31988,45,"KADEJO503",23404,34,"winter wolf",4821,28,"Godenot",4500,33,"Arena Closer",3000,32,"LightTheif",2940,6,"CarlosKoS-_-16",2800,4,"GD desconhecido",2635,35,"jack black GD",2357,19,"AMIGO BOM",1623])
      socket.emit("6", [
        29, 3115.1, 13592.9, 0, 109.2, 1, null, -1,
        353, 4103, 13436, 0, 80, 2, null, -1,
        339, 2498, 14155, 0, 90, 2, null, -1
      ]);
      socket.emit("8", 2.6, 28);
      socket.on("ch", data => {
        emit("ch", id, data);
      });
      socket.on("8", data => {
        console.log('CLAN CREATE REQUEST:', data);
        teams.push([{
          "sid": data,
          "owner": id
        }]);
        let userteam = data;
        emit("st", data, true);
      });
      socket.on("3", function (data) {
        if (data == null) {
          vx = vy = 0;
          return;
        } else if (x < 0 || x > config.mapScale || y < 0 || y > config.mapScale) {
          x = 7200;
          y = 7200;
        }
        if (y >= 6840 && y <= 7560) {
          x += 0.44;
        }
        let speed = y > 2400 ? 60 * 0.8 : 60;
        vx = Math.cos(data) * speed;
        vy = Math.sin(data) * speed;
        x += vx;
        y += vy;
        socket.emit('a');
        socket.emit("3", [id, x, y, -2.26, -1, 0, 0, null, 0, 0, 0, 0]);
      });
      socket.on("14", function (data) {
        console.log('ping!!!', arguments);
      });
    });
  });
}
*/
repl.start({
  eval: (a, _c, _f, cb) => {
    try {
      cb(null, eval(a)); // jshint ignore: line
    } catch (e) {
      cb(e);
    }
  }
});
<|endoftext|>