<commit_before>const dotenv = require('dotenv');
const path = require('path');
const defaultsDeep = require('lodash.defaultsdeep');
const lodashClone = require('lodash.clone');
const lodashMerge = require('lodash.merge');
const CI_Info = require('ci-info');
const Defaults = require('./defaults.js');
const Utils = require('../utils');
const {isObject, isUndefined, isDefined, isNumber} = Utils;

class Settings {

  static get DEFAULT_ENV() {
    return 'default';
  }

  static get DEFAULTS() {
    return Defaults;
  }

  /**
   * Looks for pattern ${VAR_NAME} in settings
   * @param {Object} [target]
   */
  static replaceEnvVariables(target) {
    for (const key in target) {
      switch (typeof target[key]) {
        case 'object':
          Settings.replaceEnvVariables(target[key]);
          break;

        case 'string':
          target[key] = target[key].replace(/\$\{(\w+)\}/g, function(match, varName) {
            return process.env[varName] || '${' + varName + '}';
          });
          break;
      }
    }

    return this;
  }

  static getDefaults() {
    return lodashClone(Settings.DEFAULTS, true);
  }

  /**
   * Called from the cli runner with data from config file
   *
   * @param {Object} [settings] additional settings which can be passed when called programmatically
   * @param {Object} [baseSettings] settings data from nightwatch config file
   * @param {Object} [argv] cli arguments object
   * @param {String} [testEnv] current test environment
   * @returns {Object}
   */
  static parse(settings = {}, baseSettings = {}, argv = {}, testEnv = '') {
    const instance = new Settings(argv, testEnv);
    instance.fromConfigFile(baseSettings);
    instance.inheritFromDefaultEnv();
    instance.init(settings);

    return instance.settings;
  }

  /**
   * Called from Nightwatch main client instance containing either an existing settings object or a new one
   *
   * @param {Object} userSettings
   * @param {Object} argv
   * @returns {Object}
   */
  static fromClient(userSettings = {}, argv = {}) {
    const instance = new Settings(argv);
    instance.init(userSettings);

    return instance.settings;
  }

  static isNightwatchObject(settings) {
    return isDefined(settings['[[@nightwatch_createdAt]]']);
  }

  /**
   * @deprecated
   * @param settings
   */
  static setDefaults(settings) {
    defaultsDeep(settings, Defaults);

    if (settings.unit_testing_mode) {
      settings.unit_tests_mode = true;
    }

    if (!settings.unit_tests_mode) {
      settings.skip_testcases_on_fail = settings.skip_testcases_on_fail || isUndefined(settings.skip_testcases_on_fail);
    }
  }

  static isUsingSeleniumServer(settings) {
    return settings.selenium && settings.selenium.start_process;
  }

  get testWorkersEnabled() {
    const {test_workers} = this.settings;

    return this.argv.parallel === true || test_workers === true || isObject(test_workers) && test_workers.enabled;
  }

  /**
   * @param {Object} [argv] the cli arguments object
   * @param {String} [testEnv] the current test env
   */
  constructor(argv = {}, testEnv = '') {
    this.baseSettings = null;
    this.argv = argv;
    this.testEnv = testEnv || '';

    this.initSettingsObject();
  }

  /**
   * @param {Object|null} [baseSettings] the raw nightwatch config object
   * @param baseSettings
   */
  fromConfigFile(baseSettings) {
    this.baseSettings = baseSettings || {};
    this.copyGenericProperties();
  }

  /**
   * Initialize a new settings object based on the defaults
   */
  initSettingsObject() {
    this.settings = Settings.getDefaults();
  }

  /**
   * Copy all properties from the config file to this.settings that are located outside any test environment
   *  defined as part of the "test_settings" dictionary;
   *
   * This allows to define non-standard properties to the Nightwatch settings object
   */
  copyGenericProperties() {
    Object.keys(this.baseSettings).forEach(key => {
      if (key === 'test_settings') {
        return;
      }

      const copyVal = lodashClone(this.baseSettings[key], true);

      if (isObject(this.settings[key])) {
        Object.assign(this.settings[key], copyVal);
      } else {
        this.settings[key] = copyVal;
      }
    });
  }

  isSettingsDefined(settingName) {
    const {webdriver} = this.settings;

    if (isObject(webdriver[settingName])) {
      const values = Object.values(webdriver[settingName]);

      return values.every(item => isDefined(item));
    }

    return isDefined(webdriver[settingName]);
  }
  /**
   * Tries to set a webdriver setting from a several legacy places if the value is not already set
   *
   * @param {String} newSetting the new property name
   * @param {String|Array} [oldSetting]
   * @param {Object} [opts]
   * @returns {Settings} for chaining
   */
  setWebdriverHttpOption(newSetting, oldSetting, opts = {}) {
    let webdriverOpts = this.settings.webdriver;

    if (this.isSettingsDefined(newSetting)) {
      return this;
    }

    if (oldSetting === undefined) {
      oldSetting = [newSetting];
    } else if (!Array.isArray(oldSetting)) {
      oldSetting = [oldSetting];
    }

    for (let i = 0; i < oldSetting.length; i++) {
      let item = oldSetting[i];
      if (isDefined(this.settings[item])) {
        webdriverOpts[newSetting] = this.settings[item];

        return this;
      }
    }

    if (isDefined(opts.defaultValue)) {
      webdriverOpts[newSetting] = opts.defaultValue;
    }

    return this;
  }

  isUsingSelenium() {
    const {selenium} = this.settings;

    return isObject(selenium);
  }

  isSeleniumServerManaged() {
    return this.isUsingSelenium() && this.settings.selenium.start_process;
  }

  /**
   * Set the connection settings to the Webdriver server and any networking options
   */
  setWebdriverSettings() {
    // if using selenium server, we read settings from the selenium dictionary
    if (this.isSeleniumServerManaged()) {
      lodashMerge(this.settings.webdriver, this.settings.selenium);
    } else if (this.isUsingSelenium()) {
      defaultsDeep(this.settings.webdriver, this.settings.selenium);
    }

    this
      .setWebdriverHttpOption('host', ['seleniumHost', 'selenium_host'], {defaultValue: 'localhost'})
      .setWebdriverHttpOption('port', ['seleniumPort', 'selenium_port'])
      .setWebdriverHttpOption('ssl', ['useSsl', 'use_ssl'])
      .setWebdriverHttpOption('proxy')
      .setWebdriverHttpOption('start_session')
      .setWebdriverHttpOption('timeout_options', 'request_timeout_options')
      .setWebdriverHttpOption('default_path_prefix')
      .setWebdriverHttpOption('username')
      .setWebdriverHttpOption('access_key', ['accessKey', 'access_key', 'password']);

    if (isUndefined(this.settings.webdriver.ssl)) {
      this.settings.webdriver.ssl = this.settings.webdriver.port === 443;
    }

    if (!this.settings.webdriver.host) {
      this.settings.webdriver.host = this.settings.selenium_host || 'localhost';
    }

    this.setServerUrl();
  }

  /**
   * Set the webdriver server url which will be used in case the service is not mananged by Nightwatch
   */
  setServerUrl() {
    const protocol = this.settings.webdriver.ssl ? 'https' : 'http';
    const {port, host, default_path_prefix = ''} = this.settings.webdriver;
    this.settings.webdriver.url = `${protocol}://${host}:${port}${default_path_prefix}`;

    if (isObject(this.settings.selenium)) {
      this.settings.selenium.url = this.settings.webdriver.url;
    }
  }

  mergeOntoExisting(userSettings = {}) {
    lodashMerge(this.settings, userSettings);

    return this;
  }

  /**
   * @returns {Settings}
   */
  adaptSettings() {
    this.setCliOptions();
    this.setScreenshotsOptions();
    this.setUnitTestsMode();
    this.setParallelMode();
    this.setTestRunner();

    if (typeof this.settings.src_folders == 'string') {
      this.settings.src_folders = [this.settings.src_folders];
    }

    if (typeof this.settings.skipgroup == 'string' && this.settings.skipgroup.length > 0) {
      this.settings.skipgroup = this.settings.skipgroup.split(',');
    }

    return this;
  }

  setParallelMode() {
    const Concurrency = require('../runner/concurrency');

    if (Concurrency.isChildProcess()) {
      this.settings.parallel_mode = true;
    }

    if (this.argv.parallel === true && !this.settings.test_workers) {
      this.settings.test_workers = true;
    } else if (isNumber(this.argv.parallel)) {
      if (!isObject(this.settings.test_workers)) {
        this.settings.test_workers = {
          enabled: true
        };
      }

      this.settings.test_workers.workers = this.argv.parallel;
    }

    this.settings.testWorkersEnabled = this.testWorkersEnabled;

    return this;
  }

  setTestRunner() {
    if (Utils.isString(this.settings.test_runner)) {
      this.settings.test_runner = {
        type: this.settings.test_runner,
        options: {}
      };
    } if (!Utils.isObject(this.settings.test_runner)) {
      throw new Error(`Invalid "test_runner" settings specified; received: ${this.settings.test_runner}`);
    }

    return this;
  }

  setUnitTestsMode() {
    const unitTesting = this.settings.unit_tests_mode || this.settings.unit_testing_mode;

    this.settings.unit_testing_mode = this.settings.unit_tests_mode = unitTesting;

    if (unitTesting) {
      this.settings.webdriver.start_process = false;
      this.settings.webdriver.start_session = false;
      this.settings.start_session = false;
      this.settings.detailed_output = false;
      this.settings.output_timestamp = false;
    } else {
      this.settings.skip_testcases_on_fail = this.settings.skip_testcases_on_fail || isUndefined(this.settings.skip_testcases_on_fail);
    }

    return this;
  }

  inheritFromDefaultEnv() {
    if (!this.baseSettings.test_settings) {
      return this;
    }

    const defaultEnvSettings = this.baseSettings.test_settings[Settings.DEFAULT_ENV] || {};
    lodashMerge(this.settings, defaultEnvSettings);

    if (!this.testEnv || this.testEnv === Settings.DEFAULT_ENV) {
      return this;
    }

    const testEnvSettings = this.baseSettings.test_settings[this.testEnv] || {};
    this.inheritFromSuperEnv(testEnvSettings);
    defaultsDeep(testEnvSettings, defaultEnvSettings);
    lodashMerge(this.settings, testEnvSettings);

    return this;
  }

  inheritFromSuperEnv(testEnvSettings) {
    if (testEnvSettings.extends) {
      const superEnv = this.baseSettings.test_settings[testEnvSettings.extends] || {};
      delete testEnvSettings.extends;
      defaultsDeep(testEnvSettings, superEnv);

      return this.inheritFromSuperEnv(testEnvSettings);
    }

    return testEnvSettings;
  }

  /**
   * @param settings
   */
  persistGlobals(settings) {
    if (this.settings.persist_globals === true && isObject(settings.globals)) {
      defaultsDeep(settings.globals, this.settings.globals);
      this.settings.globals = settings.globals;
    }
  }

  setScreenshotsOptions() {
    if (isObject(this.settings.screenshots)) {
      this.settings.screenshots.path = this.settings.screenshots.path ? path.resolve(this.settings.screenshots.path) : '';
    } else {
      const enabled = this.settings.screenshots === true;
      this.settings.screenshots = Object.assign({}, Defaults.screenshots, {enabled});
    }

    this.settings.screenshotsPath = this.settings.screenshots.path;

    return this;
  }

  setCliOptions() {
    if (this.argv.verbose) {
      this.settings.silent = false;
    }

    const cliOverwrites = {
      output_folder: this.argv.output,
      filename_filter: this.argv.filter,
      tag_filter: this.argv.tag,
      skipgroup: this.argv.skipgroup,
      skiptags: this.argv.skiptags,
      enable_fail_fast: this.argv['fail-fast']
    };

    Object.keys(cliOverwrites).forEach(key => {
      if (isDefined(cliOverwrites[key]) && cliOverwrites[key] !== null) {
        this.settings[key] = cliOverwrites[key];
      }
    });

    // TODO: add support for overwriting any setting

    return this;
  }

  sortSettings() {
    const sortedSettings = {};
    Object.keys(this.settings).sort().forEach(key => {
      sortedSettings[key] = this.settings[key];
    });

    this.settings = sortedSettings;

    Object.defineProperty(this.settings, '[[@nightwatch_createdAt]]', {
      value: new Date().valueOf(),
      enumerable: false,
      configurable: false,
      writable: false
    });
  }

  setColorOutput() {
    const {isCI, CIRCLE, JENKINS, NETLIFY, TRAVIS, GITLAB, BUILDKITE} = CI_Info;
    let coloringSupport = CIRCLE || JENKINS || NETLIFY || TRAVIS || GITLAB || BUILDKITE;

    if (isCI && !coloringSupport) {
      this.settings.disable_colors = true;
    }
  }

  /**
   * Validates and parses the test settings
   *
   * @param {Object} [userSettings]
   */
  init(userSettings = {}) {
    this.mergeOntoExisting(userSettings);
    this.setWebdriverSettings();

    this.adaptSettings();
    this.sortSettings();

    this.persistGlobals(userSettings);

    dotenv.config(this.settings.dotenv);
    Settings.replaceEnvVariables(this.settings);

    this.setColorOutput();
  }
}

module.exports = Settings;
<commit_msg>fixed small typo<commit_after>const dotenv = require('dotenv');
const path = require('path');
const defaultsDeep = require('lodash.defaultsdeep');
const lodashClone = require('lodash.clone');
const lodashMerge = require('lodash.merge');
const CI_Info = require('ci-info');
const Defaults = require('./defaults.js');
const Utils = require('../utils');
const {isObject, isUndefined, isDefined, isNumber} = Utils;

class Settings {

  static get DEFAULT_ENV() {
    return 'default';
  }

  static get DEFAULTS() {
    return Defaults;
  }

  /**
   * Looks for pattern ${VAR_NAME} in settings
   * @param {Object} [target]
   */
  static replaceEnvVariables(target) {
    for (const key in target) {
      switch (typeof target[key]) {
        case 'object':
          Settings.replaceEnvVariables(target[key]);
          break;

        case 'string':
          target[key] = target[key].replace(/\$\{(\w+)\}/g, function(match, varName) {
            return process.env[varName] || '${' + varName + '}';
          });
          break;
      }
    }

    return this;
  }

  static getDefaults() {
    return lodashClone(Settings.DEFAULTS, true);
  }

  /**
   * Called from the cli runner with data from config file
   *
   * @param {Object} [settings] additional settings which can be passed when called programmatically
   * @param {Object} [baseSettings] settings data from nightwatch config file
   * @param {Object} [argv] cli arguments object
   * @param {String} [testEnv] current test environment
   * @returns {Object}
   */
  static parse(settings = {}, baseSettings = {}, argv = {}, testEnv = '') {
    const instance = new Settings(argv, testEnv);
    instance.fromConfigFile(baseSettings);
    instance.inheritFromDefaultEnv();
    instance.init(settings);

    return instance.settings;
  }

  /**
   * Called from Nightwatch main client instance containing either an existing settings object or a new one
   *
   * @param {Object} userSettings
   * @param {Object} argv
   * @returns {Object}
   */
  static fromClient(userSettings = {}, argv = {}) {
    const instance = new Settings(argv);
    instance.init(userSettings);

    return instance.settings;
  }

  static isNightwatchObject(settings) {
    return isDefined(settings['[[@nightwatch_createdAt]]']);
  }

  /**
   * @deprecated
   * @param settings
   */
  static setDefaults(settings) {
    defaultsDeep(settings, Defaults);

    if (settings.unit_testing_mode) {
      settings.unit_tests_mode = true;
    }

    if (!settings.unit_tests_mode) {
      settings.skip_testcases_on_fail = settings.skip_testcases_on_fail || isUndefined(settings.skip_testcases_on_fail);
    }
  }

  static isUsingSeleniumServer(settings) {
    return settings.selenium && settings.selenium.start_process;
  }

  get testWorkersEnabled() {
    const {test_workers} = this.settings;

    return this.argv.parallel === true || test_workers === true || isObject(test_workers) && test_workers.enabled;
  }

  /**
   * @param {Object} [argv] the cli arguments object
   * @param {String} [testEnv] the current test env
   */
  constructor(argv = {}, testEnv = '') {
    this.baseSettings = null;
    this.argv = argv;
    this.testEnv = testEnv || '';

    this.initSettingsObject();
  }

  /**
   * @param {Object|null} [baseSettings] the raw nightwatch config object
   * @param baseSettings
   */
  fromConfigFile(baseSettings) {
    this.baseSettings = baseSettings || {};
    this.copyGenericProperties();
  }

  /**
   * Initialize a new settings object based on the defaults
   */
  initSettingsObject() {
    this.settings = Settings.getDefaults();
  }

  /**
   * Copy all properties from the config file to this.settings that are located outside any test environment
   *  defined as part of the "test_settings" dictionary;
   *
   * This allows to define non-standard properties to the Nightwatch settings object
   */
  copyGenericProperties() {
    Object.keys(this.baseSettings).forEach(key => {
      if (key === 'test_settings') {
        return;
      }

      const copyVal = lodashClone(this.baseSettings[key], true);

      if (isObject(this.settings[key])) {
        Object.assign(this.settings[key], copyVal);
      } else {
        this.settings[key] = copyVal;
      }
    });
  }

  isSettingsDefined(settingName) {
    const {webdriver} = this.settings;

    if (isObject(webdriver[settingName])) {
      const values = Object.values(webdriver[settingName]);

      return values.every(item => isDefined(item));
    }

    return isDefined(webdriver[settingName]);
  }
  /**
   * Tries to set a webdriver setting from a several legacy places if the value is not already set
   *
   * @param {String} newSetting the new property name
   * @param {String|Array} [oldSetting]
   * @param {Object} [opts]
   * @returns {Settings} for chaining
   */
  setWebdriverHttpOption(newSetting, oldSetting, opts = {}) {
    let webdriverOpts = this.settings.webdriver;

    if (this.isSettingsDefined(newSetting)) {
      return this;
    }

    if (oldSetting === undefined) {
      oldSetting = [newSetting];
    } else if (!Array.isArray(oldSetting)) {
      oldSetting = [oldSetting];
    }

    for (let i = 0; i < oldSetting.length; i++) {
      let item = oldSetting[i];
      if (isDefined(this.settings[item])) {
        webdriverOpts[newSetting] = this.settings[item];

        return this;
      }
    }

    if (isDefined(opts.defaultValue)) {
      webdriverOpts[newSetting] = opts.defaultValue;
    }

    return this;
  }

  isUsingSelenium() {
    const {selenium} = this.settings;

    return isObject(selenium);
  }

  isSeleniumServerManaged() {
    return this.isUsingSelenium() && this.settings.selenium.start_process;
  }

  /**
   * Set the connection settings to the Webdriver server and any networking options
   */
  setWebdriverSettings() {
    // if using selenium server, we read settings from the selenium dictionary
    if (this.isSeleniumServerManaged()) {
      lodashMerge(this.settings.webdriver, this.settings.selenium);
    } else if (this.isUsingSelenium()) {
      defaultsDeep(this.settings.webdriver, this.settings.selenium);
    }

    this
      .setWebdriverHttpOption('host', ['seleniumHost', 'selenium_host'], {defaultValue: 'localhost'})
      .setWebdriverHttpOption('port', ['seleniumPort', 'selenium_port'])
      .setWebdriverHttpOption('ssl', ['useSsl', 'use_ssl'])
      .setWebdriverHttpOption('proxy')
      .setWebdriverHttpOption('start_session')
      .setWebdriverHttpOption('timeout_options', 'request_timeout_options')
      .setWebdriverHttpOption('default_path_prefix')
      .setWebdriverHttpOption('username')
      .setWebdriverHttpOption('access_key', ['accessKey', 'access_key', 'password']);

    if (isUndefined(this.settings.webdriver.ssl)) {
      this.settings.webdriver.ssl = this.settings.webdriver.port === 443;
    }

    if (!this.settings.webdriver.host) {
      this.settings.webdriver.host = this.settings.selenium_host || 'localhost';
    }

    this.setServerUrl();
  }

  /**
   * Set the webdriver server url which will be used in case the service is not managed by Nightwatch
   */
  setServerUrl() {
    const protocol = this.settings.webdriver.ssl ? 'https' : 'http';
    const {port, host, default_path_prefix = ''} = this.settings.webdriver;
    this.settings.webdriver.url = `${protocol}://${host}:${port}${default_path_prefix}`;

    if (isObject(this.settings.selenium)) {
      this.settings.selenium.url = this.settings.webdriver.url;
    }
  }

  mergeOntoExisting(userSettings = {}) {
    lodashMerge(this.settings, userSettings);

    return this;
  }

  /**
   * @returns {Settings}
   */
  adaptSettings() {
    this.setCliOptions();
    this.setScreenshotsOptions();
    this.setUnitTestsMode();
    this.setParallelMode();
    this.setTestRunner();

    if (typeof this.settings.src_folders == 'string') {
      this.settings.src_folders = [this.settings.src_folders];
    }

    if (typeof this.settings.skipgroup == 'string' && this.settings.skipgroup.length > 0) {
      this.settings.skipgroup = this.settings.skipgroup.split(',');
    }

    return this;
  }

  setParallelMode() {
    const Concurrency = require('../runner/concurrency');

    if (Concurrency.isChildProcess()) {
      this.settings.parallel_mode = true;
    }

    if (this.argv.parallel === true && !this.settings.test_workers) {
      this.settings.test_workers = true;
    } else if (isNumber(this.argv.parallel)) {
      if (!isObject(this.settings.test_workers)) {
        this.settings.test_workers = {
          enabled: true
        };
      }

      this.settings.test_workers.workers = this.argv.parallel;
    }

    this.settings.testWorkersEnabled = this.testWorkersEnabled;

    return this;
  }

  setTestRunner() {
    if (Utils.isString(this.settings.test_runner)) {
      this.settings.test_runner = {
        type: this.settings.test_runner,
        options: {}
      };
    } if (!Utils.isObject(this.settings.test_runner)) {
      throw new Error(`Invalid "test_runner" settings specified; received: ${this.settings.test_runner}`);
    }

    return this;
  }

  setUnitTestsMode() {
    const unitTesting = this.settings.unit_tests_mode || this.settings.unit_testing_mode;

    this.settings.unit_testing_mode = this.settings.unit_tests_mode = unitTesting;

    if (unitTesting) {
      this.settings.webdriver.start_process = false;
      this.settings.webdriver.start_session = false;
      this.settings.start_session = false;
      this.settings.detailed_output = false;
      this.settings.output_timestamp = false;
    } else {
      this.settings.skip_testcases_on_fail = this.settings.skip_testcases_on_fail || isUndefined(this.settings.skip_testcases_on_fail);
    }

    return this;
  }

  inheritFromDefaultEnv() {
    if (!this.baseSettings.test_settings) {
      return this;
    }

    const defaultEnvSettings = this.baseSettings.test_settings[Settings.DEFAULT_ENV] || {};
    lodashMerge(this.settings, defaultEnvSettings);

    if (!this.testEnv || this.testEnv === Settings.DEFAULT_ENV) {
      return this;
    }

    const testEnvSettings = this.baseSettings.test_settings[this.testEnv] || {};
    this.inheritFromSuperEnv(testEnvSettings);
    defaultsDeep(testEnvSettings, defaultEnvSettings);
    lodashMerge(this.settings, testEnvSettings);

    return this;
  }

  inheritFromSuperEnv(testEnvSettings) {
    if (testEnvSettings.extends) {
      const superEnv = this.baseSettings.test_settings[testEnvSettings.extends] || {};
      delete testEnvSettings.extends;
      defaultsDeep(testEnvSettings, superEnv);

      return this.inheritFromSuperEnv(testEnvSettings);
    }

    return testEnvSettings;
  }

  /**
   * @param settings
   */
  persistGlobals(settings) {
    if (this.settings.persist_globals === true && isObject(settings.globals)) {
      defaultsDeep(settings.globals, this.settings.globals);
      this.settings.globals = settings.globals;
    }
  }

  setScreenshotsOptions() {
    if (isObject(this.settings.screenshots)) {
      this.settings.screenshots.path = this.settings.screenshots.path ? path.resolve(this.settings.screenshots.path) : '';
    } else {
      const enabled = this.settings.screenshots === true;
      this.settings.screenshots = Object.assign({}, Defaults.screenshots, {enabled});
    }

    this.settings.screenshotsPath = this.settings.screenshots.path;

    return this;
  }

  setCliOptions() {
    if (this.argv.verbose) {
      this.settings.silent = false;
    }

    const cliOverwrites = {
      output_folder: this.argv.output,
      filename_filter: this.argv.filter,
      tag_filter: this.argv.tag,
      skipgroup: this.argv.skipgroup,
      skiptags: this.argv.skiptags,
      enable_fail_fast: this.argv['fail-fast']
    };

    Object.keys(cliOverwrites).forEach(key => {
      if (isDefined(cliOverwrites[key]) && cliOverwrites[key] !== null) {
        this.settings[key] = cliOverwrites[key];
      }
    });

    // TODO: add support for overwriting any setting

    return this;
  }

  sortSettings() {
    const sortedSettings = {};
    Object.keys(this.settings).sort().forEach(key => {
      sortedSettings[key] = this.settings[key];
    });

    this.settings = sortedSettings;

    Object.defineProperty(this.settings, '[[@nightwatch_createdAt]]', {
      value: new Date().valueOf(),
      enumerable: false,
      configurable: false,
      writable: false
    });
  }

  setColorOutput() {
    const {isCI, CIRCLE, JENKINS, NETLIFY, TRAVIS, GITLAB, BUILDKITE} = CI_Info;
    const coloringSupport = CIRCLE || JENKINS || NETLIFY || TRAVIS || GITLAB || BUILDKITE;

    if (isCI && !coloringSupport) {
      this.settings.disable_colors = true;
    }
  }

  /**
   * Validates and parses the test settings
   *
   * @param {Object} [userSettings]
   */
  init(userSettings = {}) {
    this.mergeOntoExisting(userSettings);
    this.setWebdriverSettings();

    this.adaptSettings();
    this.sortSettings();

    this.persistGlobals(userSettings);

    dotenv.config(this.settings.dotenv);
    Settings.replaceEnvVariables(this.settings);

    this.setColorOutput();
  }
}

module.exports = Settings;
<|endoftext|>


<commit_before>module.exports = function(React) {
  return function() {
    var storeNames = Array.prototype.slice.call(arguments);
    return {
      componentWillMount: function() {
        var flux  = this.props.flux || this.context.flux;
        _.each(storeNames, function(store) {
          flux.store(store).on("change", this._setStateFromFlux)
        }, this);
      },

      componentWillUnmount: function() {
        var flux  = this.props.flux || this.context.flux;
        _.each(storeNames, function(store) {
          flux.store(store).off("change", this._setStateFromFlux)
        }, this);
      },

      _setStateFromFlux: function() {
        this.setState(this.getStateFromFlux());
      },

      getInitialState: function() {
        return this.getStateFromFlux();
      }
    }
  };
};
<commit_msg>Remove wrapper function around StoreWatchMixin<commit_after>module.exports = function() {
  var storeNames = Array.prototype.slice.call(arguments);
  return {
    componentWillMount: function() {
      var flux  = this.props.flux || this.context.flux;
      _.each(storeNames, function(store) {
        flux.store(store).on("change", this._setStateFromFlux)
      }, this);
    },

    componentWillUnmount: function() {
      var flux  = this.props.flux || this.context.flux;
      _.each(storeNames, function(store) {
        flux.store(store).off("change", this._setStateFromFlux)
      }, this);
    },

    _setStateFromFlux: function() {
      this.setState(this.getStateFromFlux());
    },

    getInitialState: function() {
      return this.getStateFromFlux();
    }
  }
};
<|endoftext|>


<commit_before>// Write a function to find the longest common prefix string amongst an array of strings.

var longestCommonPrefix = function(strs) { //120 ms runtime
    if(strs.length < 1) return "";
    var sortedArr = strs.sort(),
        a1 = sortedArr[0],
        a2 = sortedArr[sortedArr.length-1],
        len = a1.length,
        i= 0;

    while( (i<len) && a1.charAt(i) === a2.charAt(i) ) i++;
    return a1.substring(0, i);
};

// ------ Unfinished Solution ---------------------------------
// var longestCommonPrefix = function(strs) {
//     if( strs.length === 0 ) return "";
//     var map = {},
//         i;
//     // loop through strings
//         // add every prefix to map
//         // increase the count if it exists, create if it doesnt
//     // loop through map to get highest count prefixes
//     // return longest count pre fix

//     for( i = 0; i < strs.length; i++ ){ // loop through string
//         helper( strs[i], 0, "" );
//     }

//     function helper(string, index, buildUp){
//         if( index === string.length ){
//             return;
//         }else{
//             buildUp = buildUp + string[index];
//             map[buildUp] ? map[buildUp] += 1 : map[buildUp] = 1;
//             index++;
//         }

//     }

//     if ( Object.keys(map).length < 1 ) return "";
//     return Object.keys(map).reduce(function(a, b){ return map[a] > map[b] ? a : b });
// };
<commit_msg>add another solution<commit_after>// Write a function to find the longest common prefix string amongst an array of strings.

var longestCommonPrefix = function(strs) { //120 ms runtime
    if(strs.length < 1) return "";
    var sortedArr = strs.sort(),
        a1 = sortedArr[0],
        a2 = sortedArr[sortedArr.length-1],
        len = a1.length,
        i = 0;

    while( (i<len) && a1.charAt(i) === a2.charAt(i) ) i++;
    return a1.substring(0, i);
};

var longestCommonPrefix = function(strs) { //120 ms runtime
    if( strs.length < 1 ) return "";
    var prefix = strs[0];

    for( var i =0; i < strs.length; i++ ){
        while( strs[i].indexOf(prefix) !== 0 ){
            prefix = prefix.substring(0, prefix.length - 1);
            if( !prefix ) return "";
        }
    }
    return prefix;
};

// ------ Unfinished Solution ---------------------------------
// var longestCommonPrefix = function(strs) {
//     if( strs.length === 0 ) return "";
//     var map = {},
//         i;
//     // loop through strings
//         // add every prefix to map
//         // increase the count if it exists, create if it doesnt
//     // loop through map to get highest count prefixes
//     // return longest count pre fix

//     for( i = 0; i < strs.length; i++ ){ // loop through string
//         helper( strs[i], 0, "" );
//     }

//     function helper(string, index, buildUp){
//         if( index === string.length ){
//             return;
//         }else{
//             buildUp = buildUp + string[index];
//             map[buildUp] ? map[buildUp] += 1 : map[buildUp] = 1;
//             index++;
//         }

//     }

//     if ( Object.keys(map).length < 1 ) return "";
//     return Object.keys(map).reduce(function(a, b){ return map[a] > map[b] ? a : b });
// };
<|endoftext|>


<commit_before>/*global d3 $ */

function GroupedBarChart(opts, getDataCallback, updateCallback){
    this.getDataCallback=getDataCallback;
    this.updateCallback=updateCallback;

    var name=opts.name;
    var id = "#"+name.replace(/\./g,'\\.');
    var margin = {top: 20, right: 20, bottom: 30, left: 40};

    //set param
    this.selection = {global:[]};
    if(opts.args){ // set selection from arguments
        this._decodeArgs(opts.args);
    }

    var widget = this;
    //Make draggable and resizable
    d3.select(id).attr("class","barchart resize-drag");

    d3.select(id).on("divresize",function(){
        widget.update();
    });

    //Add clear button
    this.clearbtn = d3.select(id)
        .append('button')
        .attr('class','clear-btn')
        .on('click',function(){
            d3.event.stopPropagation();

            delete widget.selection.brush; //clear selection
            widget.update(); //redraw itself
            widget.updateCallback(widget._encodeArgs());
        }).html('clear');

    //Add sort button
    this.sortbtn = d3.select(id)
        .append('button')
        .attr('class','sort-btn')
        .on('click',function(){
            d3.event.stopPropagation();
            widget._opts.alpha_order = !widget._opts.alpha_order;
            widget.redraw(widget.lastres);
        });

    //Collapse on dbl click
    d3.select(id).on('dblclick',function(d){
        var currentheight = d3.select(id).style("height");
        if ( currentheight != "40px"){
            widget.restoreHeight =currentheight ;
            d3.select(id).style('height','40px');
        }
        else{
            d3.select(id).style("height",widget.restoreHeight);
        }
    });

    //SVG container
    var svg = d3.select(id).append("svg").append("g");

    //Title
    svg.append('text').attr('y',-5);

    //Axes
    svg.append("g").attr("class", "y axis")
        .attr("transform", "translate(-3,0)");
    svg.append("g").attr("class", "x axis");

    //Scales
    var y0 = d3.scale.ordinal();
    var y1 = d3.scale.ordinal();
    var x = d3.scale.linear();
    if (opts.logaxis){
        x = d3.scale.log();
    }

    //Axis
    var xAxis = d3.svg.axis();
    var yAxis = d3.svg.axis();

    //set default values
    opts.numformat = opts.numformat || ",";
    opts.alpha_order = opts.alpha_order || true;

    xAxis.orient("bottom")
        .ticks(3,opts.numformat);
    yAxis.orient("left");

    //Save vars to "this"
    this.margin = margin;
    this.svg=svg;
    this.y0=y0;
    this.y1=y1;
    this.x=x;
    this.xAxis = xAxis;
    this.yAxis = yAxis;

    this._datasrc = opts.datasrc;
    this._opts = opts;
    this._logaxis = opts.logaxis;
    this._name = name;
}

GroupedBarChart.prototype = {
    getSelection: function(){
        return this.selection;
    },

    _encodeArgs: function(){
        return JSON.stringify(this.getSelection());
    },

    _decodeArgs: function(s){
        this.selection = JSON.parse(s);
    },

    update: function(){
        var widget = this;
        var promises = {};

        //generate promise for each expr
        for (var d in widget._datasrc){
            if (widget._datasrc[d].disabled){
                continue;
            }
            var p = this.getDataCallback(d);
            for (var k in p){
                promises[k] = p[k];
            }
        }

        var promarray = Object.keys(promises).map(function(k){
            return promises[k];
        });

        var promkeys = Object.keys(promises);
        $.when.apply($,promarray).done(function(){
            var results = arguments;
            var res = {};
            promkeys.forEach(function(d,i){
                res[d] = results[i];
            });

            widget.lastres = res;
            widget.redraw(res);
        });
    },

    flattenData: function(res){
        var widget = this;
        return Object.keys(res).reduce(function(prev,curr){
            var label = curr.split('-');
            var c = label[0];

            var isColor  = /^#[0-9A-F]{6}$/i.test(label[0]);
            if(!isColor){
                var colormap = widget._datasrc[label[1]].colormap;
                var cidx = Math.floor(colormap.length/2);
                c = colormap[cidx];
            }

            //Add color
            var row = res[curr].data.map(function(d){
                d.color = c;
                return d;
            });
            return prev.concat(row);
        }, []);
    },

    redraw :function(res){
        var topn = this._opts.topn;
        if(topn !== undefined ){
            var agg = {};
            Object.keys(res).forEach(function(k){
                res[k].data.forEach(function(d){
                    agg[d.cat]= (agg[d.cat] + d.val) || d.val;
                });
            });
            var kvlist =Object.keys(agg)
                .map(function(d){return {cat: d, val:agg[d]};});
            kvlist.sort(function(x,y) { return y.val - x.val; });
            kvlist = kvlist.slice(0,topn);
            var kvhash = {};
            kvlist.forEach(function(d){ kvhash[d.cat] = d.val; });
            Object.keys(res).forEach(function(k){
                res[k].data = res[k].data.filter(function(d){
                    return (d.cat in kvhash);
                });
            });
            console.log(res);
        }

        var fdata = this.flattenData(res);

        var x =this.x;
        var y0 =this.y0;
        var y1 =this.y1;
        var svg =this.svg;
        var selection = this.selection;

        //update the axis and svgframe
        this.updateYAxis(fdata);
        this.updateXAxis(fdata);
        this.updateSVG();

        var widget = this;

        //bind data
        var bars = this.svg.selectAll('.bar').data(fdata);

        //append new bars
        bars.enter()
            .append('rect')
            .attr('class', 'bar')
            .on('click', function(d) { widget.clickFunc(d);})//toggle callback
            .append("svg:title"); //tooltip

        //set shape
        bars.attr('x', 0)
            .attr('y', function(d){return widget.y0(d.cat) + //category
                                   widget.y1(d.color);}) //selection group
            .style('fill', function(d){
                if (!widget.selection.brush || //no selection
                    widget.selection.brush.findIndex(function(b){
                        return (b.cat == d.cat); }) != -1){//in selection
                    return d.color;
                }
                else{
                    return 'gray';
                }
            })
            .attr('height',function(d){
                return widget.y1.rangeBand()-1;
            })
            .transition().duration(250)
            .attr('width',function(d){
                var w = widget.x(d.val);
                if(isNaN(w) && d.val <=0 ){
                    w = 0;
                }
                return w;
            });

        //add tool tip
        bars.select('title').text(function(d){
            return d3.format(widget._opts.numformat)(d.val);
        });

        //remove bars with no data
        bars.exit().remove();
    },

    clickFunc:function(d){
        var widget = this;
        if(!widget.selection.brush){
            widget.selection.brush = [];
        }

        var idx = widget.selection.brush.findIndex(function(b){
            return (b.cat == d.cat);
        });

        if (idx != -1){
            widget.selection.brush.splice(idx,1);
        }
        else{
            if(d3.event.shiftKey){
                widget.selection.brush.push({id:d.id, cat:d.cat});
            }
            else{
                widget.selection.brush = [{id:d.id, cat:d.cat}];
            }
        }

        if(widget.selection.brush.length < 1){
            delete widget.selection.brush;
        }

        widget.update(); //redraw itself
        widget.updateCallback(widget._encodeArgs());
    },

    updateSVG : function(){
        var svg = this.svg;
        var margin = this.margin;

        var svgframe = d3.select(svg.node().parentNode);
        var width=d3.select(svgframe.node().parentNode).style('width');
        width = parseFloat(width);

        //calculate width and height in side the margin
        width = width-margin.left-margin.right;
        var height = this.totalheight;

        //resize the frame
        svgframe.attr("width", width + margin.left + margin.right);
        svgframe.attr("height", height + margin.top + margin.bottom);

        svg.attr("transform", "translate("+margin.left+","+margin.top+")");

        this.width = width;
        this.height = height;
    },

    updateXAxis: function(data){
        var margin = this.margin;
        var x=this.x;
        var xAxis=this.xAxis;
        var svg=this.svg;

        var svgframe = d3.select(svg.node().parentNode);
        var rect=svgframe.node().parentNode.getBoundingClientRect();
        var width = rect.width - this.margin.left-this.margin.right;

        var d = [d3.min(data, function(d) {return +d.val;}),
                 d3.max(data, function(d) {return +d.val;})];

        if(this._opts.logaxis){ // prevent zeros for log
            d[0] = Math.max(d[0]-1e-6,1e-6);
        }
        else{
            d[0] = Math.min(d[0],d[1]-Math.abs(d[1]/2));
            d[0] = d[0]-0.1*Math.abs(d[0]);
        }

        //set domain from options
        if(this._opts.domain){
            if(this._opts.domain.min !== undefined){
                d[0] = this._opts.domain.min;
            }

            if(this._opts.domain.max !== undefined){
                d[1] = this._opts.domain.max;
            }
        }

        //set domain
        x.domain(d);
        x.range([0,width]);

        xAxis.scale(x);

        //move and draw the axis
        svg.select('.x.axis')
            .attr("transform", "translate(0,"+this.totalheight+")")
            .call(xAxis);
        this.width=width;
    },

    updateYAxis:function(data){
        var y0=this.y0;
        var y1=this.y1;
        var yAxis=this.yAxis;
        var svg = this.svg;
        var opts = this._opts;
        var sortbtn = this.sortbtn;

        //Sort y axis
        if (opts.alpha_order){
            y0.domain(data.map(function(d){return d.cat;}).sort());
            if (y0.domain().every(function(d) {return !isNaN(d);})){
                y0.domain(y0.domain().sort(function(a,b){return a-b;}));
            }
            sortbtn.html('#');
        }
        else{ //sort by data value
            var d = data.sort(function(x,y){ return y.val - x.val;});
            y0.domain(d.map(function(d){return d.cat;}));
            sortbtn.html('A');
        }

        y1.domain(data.map(function(d){return d.color;}));
        var totalheight = y0.domain().length* y1.domain().length * 18;

        y0.rangeRoundBands([0, totalheight]);
        y1.rangeRoundBands([0, y0.rangeBand()]);
        yAxis.scale(y0);
        svg.select('.y.axis').call(yAxis);

        //enable axis click
        var widget = this;
        svg.select('.y.axis').selectAll('.tick')
            .on('click',function(d){
                var obj = data.filter(function(e){return e.cat==d;})[0];
                widget.clickFunc(obj);
            });

        this.totalheight = totalheight;
        this.margin.left = svg.select('.y.axis').node().getBBox().width+3;

        //update title with cat count
        svg.select('text').text(this._opts.title+' ('+y0.domain().length+')');
    }
};
<commit_msg>check attribute<commit_after>/*global d3 $ */

function GroupedBarChart(opts, getDataCallback, updateCallback){
    this.getDataCallback=getDataCallback;
    this.updateCallback=updateCallback;

    var name=opts.name;
    var id = "#"+name.replace(/\./g,'\\.');
    var margin = {top: 20, right: 20, bottom: 30, left: 40};

    //set param
    this.selection = {global:[]};
    if(opts.args){ // set selection from arguments
        this._decodeArgs(opts.args);
    }

    var widget = this;
    //Make draggable and resizable
    d3.select(id).attr("class","barchart resize-drag");

    d3.select(id).on("divresize",function(){
        widget.update();
    });

    //Add clear button
    this.clearbtn = d3.select(id)
        .append('button')
        .attr('class','clear-btn')
        .on('click',function(){
            d3.event.stopPropagation();

            delete widget.selection.brush; //clear selection
            widget.update(); //redraw itself
            widget.updateCallback(widget._encodeArgs());
        }).html('clear');

    //Add sort button
    this.sortbtn = d3.select(id)
        .append('button')
        .attr('class','sort-btn')
        .on('click',function(){
            d3.event.stopPropagation();
            widget._opts.alpha_order = !widget._opts.alpha_order;
            widget.redraw(widget.lastres);
        });

    //Collapse on dbl click
    d3.select(id).on('dblclick',function(d){
        var currentheight = d3.select(id).style("height");
        if ( currentheight != "40px"){
            widget.restoreHeight =currentheight ;
            d3.select(id).style('height','40px');
        }
        else{
            d3.select(id).style("height",widget.restoreHeight);
        }
    });

    //SVG container
    var svg = d3.select(id).append("svg").append("g");

    //Title
    svg.append('text').attr('y',-5);

    //Axes
    svg.append("g").attr("class", "y axis")
        .attr("transform", "translate(-3,0)");
    svg.append("g").attr("class", "x axis");

    //Scales
    var y0 = d3.scale.ordinal();
    var y1 = d3.scale.ordinal();
    var x = d3.scale.linear();
    if (opts.logaxis){
        x = d3.scale.log();
    }

    //Axis
    var xAxis = d3.svg.axis();
    var yAxis = d3.svg.axis();

    //set default values
    opts.numformat = opts.numformat || ",";
    if(!opts.hasOwnProperty('alpha_order')) {
        opts.alpha_order = true;
    }

    xAxis.orient("bottom")
        .ticks(3,opts.numformat);
    yAxis.orient("left");

    //Save vars to "this"
    this.margin = margin;
    this.svg=svg;
    this.y0=y0;
    this.y1=y1;
    this.x=x;
    this.xAxis = xAxis;
    this.yAxis = yAxis;

    this._datasrc = opts.datasrc;
    this._opts = opts;
    this._logaxis = opts.logaxis;
    this._name = name;
}

GroupedBarChart.prototype = {
    getSelection: function(){
        return this.selection;
    },

    _encodeArgs: function(){
        return JSON.stringify(this.getSelection());
    },

    _decodeArgs: function(s){
        this.selection = JSON.parse(s);
    },

    update: function(){
        var widget = this;
        var promises = {};

        //generate promise for each expr
        for (var d in widget._datasrc){
            if (widget._datasrc[d].disabled){
                continue;
            }
            var p = this.getDataCallback(d);
            for (var k in p){
                promises[k] = p[k];
            }
        }

        var promarray = Object.keys(promises).map(function(k){
            return promises[k];
        });

        var promkeys = Object.keys(promises);
        $.when.apply($,promarray).done(function(){
            var results = arguments;
            var res = {};
            promkeys.forEach(function(d,i){
                res[d] = results[i];
            });

            widget.lastres = res;
            widget.redraw(res);
        });
    },

    flattenData: function(res){
        var widget = this;
        return Object.keys(res).reduce(function(prev,curr){
            var label = curr.split('-');
            var c = label[0];

            var isColor  = /^#[0-9A-F]{6}$/i.test(label[0]);
            if(!isColor){
                var colormap = widget._datasrc[label[1]].colormap;
                var cidx = Math.floor(colormap.length/2);
                c = colormap[cidx];
            }

            //Add color
            var row = res[curr].data.map(function(d){
                d.color = c;
                return d;
            });
            return prev.concat(row);
        }, []);
    },

    redraw :function(res){
        var topn = this._opts.topn;
        if(topn !== undefined ){
            var agg = {};
            Object.keys(res).forEach(function(k){
                res[k].data.forEach(function(d){
                    agg[d.cat]= (agg[d.cat] + d.val) || d.val;
                });
            });
            var kvlist =Object.keys(agg)
                .map(function(d){return {cat: d, val:agg[d]};});
            kvlist.sort(function(x,y) { return y.val - x.val; });
            kvlist = kvlist.slice(0,topn);
            var kvhash = {};
            kvlist.forEach(function(d){ kvhash[d.cat] = d.val; });
            Object.keys(res).forEach(function(k){
                res[k].data = res[k].data.filter(function(d){
                    return (d.cat in kvhash);
                });
            });
            console.log(res);
        }

        var fdata = this.flattenData(res);

        var x =this.x;
        var y0 =this.y0;
        var y1 =this.y1;
        var svg =this.svg;
        var selection = this.selection;

        //update the axis and svgframe
        this.updateYAxis(fdata);
        this.updateXAxis(fdata);
        this.updateSVG();

        var widget = this;

        //bind data
        var bars = this.svg.selectAll('.bar').data(fdata);

        //append new bars
        bars.enter()
            .append('rect')
            .attr('class', 'bar')
            .on('click', function(d) { widget.clickFunc(d);})//toggle callback
            .append("svg:title"); //tooltip

        //set shape
        bars.attr('x', 0)
            .attr('y', function(d){return widget.y0(d.cat) + //category
                                   widget.y1(d.color);}) //selection group
            .style('fill', function(d){
                if (!widget.selection.brush || //no selection
                    widget.selection.brush.findIndex(function(b){
                        return (b.cat == d.cat); }) != -1){//in selection
                    return d.color;
                }
                else{
                    return 'gray';
                }
            })
            .attr('height',function(d){
                return widget.y1.rangeBand()-1;
            })
            .transition().duration(250)
            .attr('width',function(d){
                var w = widget.x(d.val);
                if(isNaN(w) && d.val <=0 ){
                    w = 0;
                }
                return w;
            });

        //add tool tip
        bars.select('title').text(function(d){
            return d3.format(widget._opts.numformat)(d.val);
        });

        //remove bars with no data
        bars.exit().remove();
    },

    clickFunc:function(d){
        var widget = this;
        if(!widget.selection.brush){
            widget.selection.brush = [];
        }

        var idx = widget.selection.brush.findIndex(function(b){
            return (b.cat == d.cat);
        });

        if (idx != -1){
            widget.selection.brush.splice(idx,1);
        }
        else{
            if(d3.event.shiftKey){
                widget.selection.brush.push({id:d.id, cat:d.cat});
            }
            else{
                widget.selection.brush = [{id:d.id, cat:d.cat}];
            }
        }

        if(widget.selection.brush.length < 1){
            delete widget.selection.brush;
        }

        widget.update(); //redraw itself
        widget.updateCallback(widget._encodeArgs());
    },

    updateSVG : function(){
        var svg = this.svg;
        var margin = this.margin;

        var svgframe = d3.select(svg.node().parentNode);
        var width=d3.select(svgframe.node().parentNode).style('width');
        width = parseFloat(width);

        //calculate width and height in side the margin
        width = width-margin.left-margin.right;
        var height = this.totalheight;

        //resize the frame
        svgframe.attr("width", width + margin.left + margin.right);
        svgframe.attr("height", height + margin.top + margin.bottom);

        svg.attr("transform", "translate("+margin.left+","+margin.top+")");

        this.width = width;
        this.height = height;
    },

    updateXAxis: function(data){
        var margin = this.margin;
        var x=this.x;
        var xAxis=this.xAxis;
        var svg=this.svg;

        var svgframe = d3.select(svg.node().parentNode);
        var rect=svgframe.node().parentNode.getBoundingClientRect();
        var width = rect.width - this.margin.left-this.margin.right;

        var d = [d3.min(data, function(d) {return +d.val;}),
                 d3.max(data, function(d) {return +d.val;})];

        if(this._opts.logaxis){ // prevent zeros for log
            d[0] = Math.max(d[0]-1e-6,1e-6);
        }
        else{
            d[0] = Math.min(d[0],d[1]-Math.abs(d[1]/2));
            d[0] = d[0]-0.1*Math.abs(d[0]);
        }

        //set domain from options
        if(this._opts.domain){
            if(this._opts.domain.min !== undefined){
                d[0] = this._opts.domain.min;
            }

            if(this._opts.domain.max !== undefined){
                d[1] = this._opts.domain.max;
            }
        }

        //set domain
        x.domain(d);
        x.range([0,width]);

        xAxis.scale(x);

        //move and draw the axis
        svg.select('.x.axis')
            .attr("transform", "translate(0,"+this.totalheight+")")
            .call(xAxis);
        this.width=width;
    },

    updateYAxis:function(data){
        var y0=this.y0;
        var y1=this.y1;
        var yAxis=this.yAxis;
        var svg = this.svg;
        var opts = this._opts;
        var sortbtn = this.sortbtn;

        //Sort y axis
        if (opts.alpha_order){
            y0.domain(data.map(function(d){return d.cat;}).sort());
            if (y0.domain().every(function(d) {return !isNaN(d);})){
                y0.domain(y0.domain().sort(function(a,b){return a-b;}));
            }
            sortbtn.html('#');
        }
        else{ //sort by data value
            var d = data.sort(function(x,y){ return y.val - x.val;});
            y0.domain(d.map(function(d){return d.cat;}));
            sortbtn.html('A');
        }

        y1.domain(data.map(function(d){return d.color;}));
        var totalheight = y0.domain().length* y1.domain().length * 18;

        y0.rangeRoundBands([0, totalheight]);
        y1.rangeRoundBands([0, y0.rangeBand()]);
        yAxis.scale(y0);
        svg.select('.y.axis').call(yAxis);

        //enable axis click
        var widget = this;
        svg.select('.y.axis').selectAll('.tick')
            .on('click',function(d){
                var obj = data.filter(function(e){return e.cat==d;})[0];
                widget.clickFunc(obj);
            });

        this.totalheight = totalheight;
        this.margin.left = svg.select('.y.axis').node().getBBox().width+3;

        //update title with cat count
        svg.select('text').text(this._opts.title+' ('+y0.domain().length+')');
    }
};
<|endoftext|>


<commit_before>// Replace CSS
function smothReplaceCSS() {
    oldCSS = $('link[href*="primo_library_wro"]');
    newCSS = oldCSS.clone();
    newCSS.attr("href", "http://nextbase.github.io/primo-suit/css/build/main.css");
    newCSS.insertAfter(oldCSS);
    setTimeout(oldCSS.remove(), 3000);
}

function smothReplaceOldCSS() {
    oldCSS = $('link[href*="main.css"]');
    newCSS = oldCSS.clone();
    newCSS.attr("href", "../wro/primo_library_wro_82SNU_en_US.css?");
    newCSS.insertAfter(oldCSS);
    setTimeout(oldCSS.remove(), 3000);
}

$(document).ready(function () {
    // Sitemap Expand
    var sitemap_toggle = $('#sitemap-button');
    var sitemap_block = $('#footer');

    sitemap_toggle.click(function (e) {
        e.preventDefault();
        sitemap_block.toggleClass('sitemap-expanded');
    });

    function expandableFacet(trigger_query, parent_query) {
        $(trigger_query).bind('click', function (e) {
            $(this).closest(parent_query).toggleClass('expanded');
            e.preventDefault();
        });
    };

    function expandBlocks(selectors) {
        if (selectors.constructor === Array) {
            $.each((selectors), function (index, value) {
                $(value).addClass('expanded');
            });
        } else {
            $(value).addClass('expanded');
        }
    };
    // Replace CSS
    smothReplaceCSS();

    // Search Facets Expand
    expandableFacet("#facetListTopLevel h3", ".EXLFacetList");

    // Secondary Facets Expand
    expandableFacet("#facetList .EXLFacetContainer h4", ".EXLFacetContainer");

    // Initialize with expanded blocks
    var $blocks = ["#facetListTopLevel", "#exlidFacet0", "#exlidFacet1"];
    expandBlocks($blocks);

});<commit_msg>Debugging<commit_after>// Replace CSS
function smothReplaceCSS() {
    oldCSS = $('link[href*="primo_library_wro"]');
    newCSS = oldCSS.clone();
    newCSS.attr("href", "http://nextbase.github.io/primo-suit/css/build/main.css");
    newCSS.insertAfter(oldCSS);
    setTimeout(function() {
        console.log("TIMEOUT: Replacing the old Primo CSS with the new one.");
        oldCSS.remove();
    }, 3000);
}

function smothReplaceOldCSS() {
    oldCSS = $('link[href*="main.css"]');
    newCSS = oldCSS.clone();
    newCSS.attr("href", "../wro/primo_library_wro_82SNU_en_US.css?");
    newCSS.insertAfter(oldCSS);
    setTimeout(function() {
        console.log("TIMEOUT: Bringing back the Primo CSS.");
        oldCSS.remove();
    }, 3000);
}

$(document).ready(function () {
    // Sitemap Expand
    var sitemap_toggle = $('#sitemap-button');
    var sitemap_block = $('#footer');

    sitemap_toggle.click(function (e) {
        e.preventDefault();
        sitemap_block.toggleClass('sitemap-expanded');
    });

    function expandableFacet(trigger_query, parent_query) {
        $(trigger_query).bind('click', function (e) {
            $(this).closest(parent_query).toggleClass('expanded');
            e.preventDefault();
        });
    };

    function expandBlocks(selectors) {
        if (selectors.constructor === Array) {
            $.each((selectors), function (index, value) {
                $(value).addClass('expanded');
            });
        } else {
            $(value).addClass('expanded');
        }
    };
    // Replace CSS
    smothReplaceCSS();

    // Search Facets Expand
    expandableFacet("#facetListTopLevel h3", ".EXLFacetList");

    // Secondary Facets Expand
    expandableFacet("#facetList .EXLFacetContainer h4", ".EXLFacetContainer");

    // Initialize with expanded blocks
    var $blocks = ["#facetListTopLevel", "#exlidFacet0", "#exlidFacet1"];
    expandBlocks($blocks);

});<|endoftext|>


<commit_before>(function () {
    var squareUnitSize = 100;

    function normalizePosition(position) {
        var modulus = position % squareUnitSize;
        var newPosition = position - modulus;
        return newPosition;
    }

    function normalizeSize(size) {
        var modulus = size % squareUnitSize;
        var newSize = size - modulus;
        if (newSize === 0) {
            return squareUnitSize;
        }
        if (size === newSize) {
            return size;
        }
        return newSize + squareUnitSize;
    }

    Ext.define('faceid.view.Portlet', {
        extend: 'Ext.Panel',
        title: '-',
        draggable: true,
        resizable: true,
        closable: true,
        height: squareUnitSize,
        width: squareUnitSize,
        x: 0,
        y: 0,
        listeners: {
            resize: function (thisPanel, width, height) {
                console.log('resizing portlet', thisPanel);
                thisPanel.suspendEvents();
                thisPanel.setSize(normalizeSize(width), normalizeSize(height));
                thisPanel.resumeEvents();
            },
            move: function (thisPanel, x, y) {
                console.log('moving portlet', thisPanel);
                thisPanel.suspendEvents();
                thisPanel.setPosition(normalizePosition(x), normalizePosition(y));
                thisPanel.resumeEvents();
            }

        }
    });
})();
<commit_msg>-using a virtual grid<commit_after>(function () {
    var squareUnitSize = 100;

    function normalizePosition(position) {
        var modulus = position % squareUnitSize;
        var newPosition = position - modulus;
        return newPosition;
    }

    function normalizeSize(size) {
        var modulus = size % squareUnitSize;
        var newSize = size - modulus;
        if (newSize === 0) {
            return squareUnitSize;
        }
        if (size === newSize) {
            return size;
        }
        return newSize + squareUnitSize;
    }

    Ext.define('faceid.view.Portlet', {
        extend: 'Ext.Panel',
        title: '-',
        draggable: true,
        resizable: true,
        closable: true,
        height: squareUnitSize,
        width: squareUnitSize,
        x: 0,
        y: 0,
        padding: '5 0 0 5',
        listeners: {
            resize: function (thisPanel, width, height) {
                console.log('resizing portlet', thisPanel);
                thisPanel.suspendEvents();
                thisPanel.setSize(normalizeSize(width), normalizeSize(height));
                thisPanel.resumeEvents();
            },
            move: function (thisPanel, x, y) {
                console.log('moving portlet', thisPanel);
                thisPanel.suspendEvents();
                thisPanel.setPosition(normalizePosition(x), normalizePosition(y));
                thisPanel.resumeEvents();
            }

        }
    });
})();
<|endoftext|>


<commit_before>var app = angular.module('halleApp.homeController', []);

// Controler da pagina incial
app.controller('homeController', function($scope, $rootScope, $ionicPopup, $state, $stateParams, $cordovaContacts, $ionicPlatform, DeletePhoneResource, InvitePhoneNumberResource, MessageReceiveResource) {
  $scope.contacts = {};
  // mensagem de erro
  $scope.error = false;
  $scope.msgError = "";
  // mensagem de OK
  $scope.Success = false;
  $scope.msgSuccess = "";
  // Acessando o storage local
  var storage = new getLocalStorage();
  var token = storage.get();

  // INIT home
  $scope.goHome = function() {
    $state.go("home.friendslist");
  }
  // FINAL home

  // INIT getAllContacts
  $scope.getAllContacts = function() {

    var name = "";
    var phoneFriend = "";

    $scope.phoneContacts = [];
    function onSuccess(contacts) {
      alert('onSucess');
      alert(contacts.length);
      alert(contacts);
      for (var i = 0; i < contacts.length; i++) {
        var item = contacts[i];
        alert(item);
        alert(item.displayName + ' - ' + item.phoneNumbers)
        if (item.displayName != null && item.phoneNumbers != null) {
          var p = item.phoneNumbers[0].value.replace(/ /g,'');
          var p1 = p.replace(/-/g,'');

          if (p1.startsWith('+')) {
            nameFriend = item.displayName;
            phoneFriend = p1;

            // acessando o recurso de API
            InvitePhoneNumberResource.save({ token: token, name: nameFriend, phone: phoneFriend })
            .$promise
              .then(function(data) {
                $scope.Success = true;
                $scope.msgSuccess =  data.message;
                alert('Importado - ' + nameFriend + ' - ' + phoneFriend);
            },
            function(error) {
              $scope.msgError =  error.data.message;
              alert('Erro na importação - ' + error);
            });
          }
        }
      }
    };
    function onError(contactError) {
      alert(contactError);
    };
    var options = {};
    options.multiple = true;
    $cordovaContacts.find(options).then(onSuccess, onError);

     /*

      $cordovaContacts.find(opts).then(function(allContacts) {
          $scope.contacts = allContacts;

          alert(allContacts.length);
          angular.forEach(allContacts, function(item, index){

            if (item.displayName != null && item.phoneNumbers != null) {
              var p = item.phoneNumbers[0].value.replace(/ /g,'');
              var p1 = p.replace(/-/g,'');
              if (p1.startsWith('+')) {

                nameFriend = item.displayName;
                phoneFriend = p1;

                // acessando o recurso de API
                InvitePhoneNumberResource.save({ token: token, name: nameFriend, phone: phoneFriend })
                .$promise
                  .then(function(data) {
                    $scope.Success = true;
                    $scope.msgSuccess =  data.message;
                  },
                  function(error) {
                    $scope.msgError =  error.data.message;
                  });

              }

            }
          });
      });
      */
  };
  // FINAL getAllContacts

 // INIT DELETE PHONE - A confirm dialog
 $scope.showDelete = function() {
   var confirmPopup = $ionicPopup.confirm({
     title: '' + $rootScope.message.title + '',
     template: $rootScope.message.deletePhone,
     cancelText: $rootScope.message.cancel,
     okText: ''+$rootScope.message.exit+''
   });

   confirmPopup.then(function(res) {
     // Tudo ok vamos iniciar a troca da senha

     if(res) {
       // removendo a conta
       DeletePhoneResource.delete({ token: token })
        .$promise
        .then(function(data) {

          if (data.code === "200") {
            $state.go("login");
          }

        }, function(error) {
          $scope.error = true;

          if (error.data === null) {
            $scope.msgError = $rootScope.message.deleteError;
          }
          else {
            $scope.msgError =  error.data.message;
          }
       });

       // Acessando o storage local
       storage.remove();

       // redirecionando para o Login
       $state.go("login");
     }
   });
 };
 // END DELETE PHONE

 // INIT EXIT - A confirm dialog
 $scope.showExit = function() {
   var confirmPopup = $ionicPopup.confirm({
     title: '' + $rootScope.message.title + '',
     template: $rootScope.message.exitMessage,
     cancelText: $rootScope.message.cancel,
     okText: ''+$rootScope.message.exit+''
   });

   confirmPopup.then(function(res) {
     if(res) {
       // Acessando o storage local
       storage.remove();

       // redirecionando para o Login
       $state.go("login");
     }
   });
 };
 // END EXIT

});
<commit_msg>Mais uma versão da importação de contatos.<commit_after>var app = angular.module('halleApp.homeController', []);

// Controler da pagina incial
app.controller('homeController', function($scope, $rootScope, $ionicPopup, $state, $stateParams, $cordovaContacts, $ionicPlatform, DeletePhoneResource, InvitePhoneNumberResource, MessageReceiveResource) {
  $scope.contacts = {};
  // mensagem de erro
  $scope.error = false;
  $scope.msgError = "";
  // mensagem de OK
  $scope.Success = false;
  $scope.msgSuccess = "";
  // Acessando o storage local
  var storage = new getLocalStorage();
  var token = storage.get();

  // INIT home
  $scope.goHome = function() {
    $state.go("home.friendslist");
  }
  // FINAL home

  // INIT getAllContacts
  $scope.getAllContacts = function() {

    var name = "";
    var phoneFriend = "";

    $scope.phoneContacts = [];
    function onSuccess(contacts) {
      alert('Passo 1 - onSucess');
      alert('Passo 2 - ' + contacts.length);
      alert('Passo 3 - ' + contacts);
      for (var i = 0; i < contacts.length; i++) {
        alert('Passo 4 - ' + contacts[i]);
        var item = contacts[i];
        alert('Passo 5 - ' + item.displayName + ' - ' + item.phoneNumbers)
        if (item.displayName != null && item.phoneNumbers != null) {
          var p = item.phoneNumbers[0].value.replace(/ /g,'');
          var p1 = p.replace(/-/g,'');

          if (p1.startsWith('+')) {
            nameFriend = item.displayName;
            phoneFriend = p1;

            // acessando o recurso de API
            InvitePhoneNumberResource.save({ token: token, name: nameFriend, phone: phoneFriend })
            .$promise
              .then(function(data) {
                $scope.Success = true;
                $scope.msgSuccess =  data.message;
                alert('Importado - ' + nameFriend + ' - ' + phoneFriend);
            },
            function(error) {
              $scope.msgError =  error.data.message;
              alert('Erro na importação - ' + error);
            });
          }
        }
      }
    };
    function onError(contactError) {
      alert(contactError);
    };
    var options = {};
    options.multiple = true;
    $cordovaContacts.find(options).then(onSuccess, onError);

     /*

      $cordovaContacts.find(opts).then(function(allContacts) {
          $scope.contacts = allContacts;

          alert(allContacts.length);
          angular.forEach(allContacts, function(item, index){

            if (item.displayName != null && item.phoneNumbers != null) {
              var p = item.phoneNumbers[0].value.replace(/ /g,'');
              var p1 = p.replace(/-/g,'');
              if (p1.startsWith('+')) {

                nameFriend = item.displayName;
                phoneFriend = p1;

                // acessando o recurso de API
                InvitePhoneNumberResource.save({ token: token, name: nameFriend, phone: phoneFriend })
                .$promise
                  .then(function(data) {
                    $scope.Success = true;
                    $scope.msgSuccess =  data.message;
                  },
                  function(error) {
                    $scope.msgError =  error.data.message;
                  });

              }

            }
          });
      });
      */
  };
  // FINAL getAllContacts

 // INIT DELETE PHONE - A confirm dialog
 $scope.showDelete = function() {
   var confirmPopup = $ionicPopup.confirm({
     title: '' + $rootScope.message.title + '',
     template: $rootScope.message.deletePhone,
     cancelText: $rootScope.message.cancel,
     okText: ''+$rootScope.message.exit+''
   });

   confirmPopup.then(function(res) {
     // Tudo ok vamos iniciar a troca da senha

     if(res) {
       // removendo a conta
       DeletePhoneResource.delete({ token: token })
        .$promise
        .then(function(data) {

          if (data.code === "200") {
            $state.go("login");
          }

        }, function(error) {
          $scope.error = true;

          if (error.data === null) {
            $scope.msgError = $rootScope.message.deleteError;
          }
          else {
            $scope.msgError =  error.data.message;
          }
       });

       // Acessando o storage local
       storage.remove();

       // redirecionando para o Login
       $state.go("login");
     }
   });
 };
 // END DELETE PHONE

 // INIT EXIT - A confirm dialog
 $scope.showExit = function() {
   var confirmPopup = $ionicPopup.confirm({
     title: '' + $rootScope.message.title + '',
     template: $rootScope.message.exitMessage,
     cancelText: $rootScope.message.cancel,
     okText: ''+$rootScope.message.exit+''
   });

   confirmPopup.then(function(res) {
     if(res) {
       // Acessando o storage local
       storage.remove();

       // redirecionando para o Login
       $state.go("login");
     }
   });
 };
 // END EXIT

});
<|endoftext|>


<commit_before>// UserVoice widget setup
$(document).ready(function() {
    'use strict';
    var data,
        defaults,
        uvBtnId = '#uvSubmitIdea',
        $uvBtn = $(uvBtnId);

    // Return if the submit an idea button doesn't exist
    if (!$uvBtn.length) {
        return;
    }

    // Include the UserVoice JavaScript SDK (only needed once on a page)
    window.UserVoice=window.UserVoice||[];(function(){var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/PeUfQwynga75cBUaAV8Ew.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s)})();

    //
    // UserVoice Javascript SDK developer documentation:
    // https://www.uservoice.com/o/javascript-sdk
    //

    // UV crashes hard when it receives undefined as a value. Need to ensure defaults.
    defaults = {
        userEmail: 'Unknown email address',
        userName: 'Unknown name'
    }

    data = _.defaults(defaults, $uvBtn.data());

    // Widget configuration
    window.UserVoice.push(['set', {
        accent_color: 'rgb(15, 64, 147)',
        contact_enabled: false,
        smartvote_enabled: false,
    }]);

    // Identify the user and pass traits
    window.UserVoice.push(['identify', {
        email: data.userEmail,
        name: data.userName,
    }]);

    window.UserVoice.push(['addTrigger', uvBtnId]);

    $uvBtn.click(function() {
        ga_track_event('Toolbar', 'Submit an Idea', data.domain);
    });
});
<commit_msg>Fix dimagimon errors<commit_after>// UserVoice widget setup
$(document).ready(function() {
    'use strict';
    var data,
        defaults,
        uvBtnId = '#uvSubmitIdea',
        $uvBtn = $(uvBtnId);

    if (!$uvBtn.length) {
        return;
    }

    // Include the UserVoice JavaScript SDK (only needed once on a page)
    window.UserVoice=window.UserVoice||[];(function(){var uv=document.createElement('script');uv.type='text/javascript';uv.async=true;uv.src='//widget.uservoice.com/PeUfQwynga75cBUaAV8Ew.js';var s=document.getElementsByTagName('script')[0];s.parentNode.insertBefore(uv,s)})();

    //
    // UserVoice Javascript SDK developer documentation:
    // https://www.uservoice.com/o/javascript-sdk
    //

    // UV crashes hard when it receives undefined as a value. Need to ensure defaults.
    defaults = {
        userEmail: 'Unknown email address',
        userName: 'Unknown name'
    };

    data = _.defaults(defaults, $uvBtn.data());

    // Widget configuration
    window.UserVoice.push(['set', {
        accent_color: 'rgb(15, 64, 147)',
        contact_enabled: false,
        smartvote_enabled: false,
    }]);

    // Identify the user and pass traits
    window.UserVoice.push(['identify', {
        email: data.userEmail,
        name: data.userName,
    }]);

    window.UserVoice.push(['addTrigger', uvBtnId]);

    $uvBtn.click(function() {
        ga_track_event('Toolbar', 'Submit an Idea', data.domain);
    });
});
<|endoftext|>


<commit_before>// GLOBAL VARIABLS
var GRID_SIZE = 5;
var SOLUTION_LENGTH = 1;
var startingGrid;
var solution;
var grid;

// Define some objects
function Tile(x_, y_) {
	var tile = {
		pos: {
			x: x_,
			y: y_
		},
		on: true,
		up: false,
		right: false,
		down: false,
		left: false,
	};

	return tile;
}

// Generate a random grid
function Grid(size) {
	var DIR_PROB = 0.25;

	// Initialize a random directed graph
	var tiles = [];
	for(var x = 0; x < size; x++) {
		tiles[x] = [];
		for(var y = 0; y < size; y++) {
			var tile = Tile(x, y);

			var tileUp = y > 0;
			var tileDown = y < size - 1;
			var tileLeft = x > 0;
			var tileRight = x < size - 1;

			// Randomize directions
			if(Math.random() < 0.5) {
				if(tileLeft && Math.random() < DIR_PROB)
					tile.left = true;

				if(tileRight && Math.random() < DIR_PROB)
					tile.right = true;
			} else {
				if(tileUp && Math.random() < DIR_PROB)
					tile.up = true;

				if(tileDown && Math.random() < DIR_PROB)
					tile.down = true;
			}

			if(!tile.up && !tile.down && !tile.left && !tile.right) {
				tile.up = tileUp;
				tile.right = tileRight;
				tile.down = tileDown;
				tile.left = tileLeft;
			}

			tiles[x][y] = tile;
		}
	}

	var grid = {
		tiles: tiles,
		size: size
	};

	return grid;
}

// Utility functions
function isSolution(solution) {
	var gridCopy = copyGrid(grid);

	for(var s = 0; s < solution.length; s++) {
		var x = solution[s].x;
		var y = solution[s].y;
		toggle(gridCopy, x, y);
	}
	return isSolved(gridCopy);
}

function isSolved(grid) {
	for(var x = 0; x < grid.size; x++) {
		for(var y = 0; y < grid.size; y++) {
			if(!grid.tiles[x][y].on) return false;
		}
	}

	return true;
}

function isDirected(tile, size) {
	var tileUp = tile.pos.y > 0;
	var tileDown = tile.pos.y < size - 1;
	var tileLeft = tile.pos.x > 0;
	var tileRight = tile.pos.x < size - 1;

	var undirected = (tileUp) ? tile.up : true;
	undirected = undirected && ((tileRight) ? tile.right : true);
	undirected = undirected && ((tileDown) ? tile.down : true);
	undirected = undirected && ((tileLeft) ? tile.left : true);

	return !undirected;
}

function getBestScore() {
	var bestScore = document.getElementById("best-score");
	var score = parseInt(bestScore.innerHTML.substr(18));
	return score;

}

function setBestScore(score) {
	var bestScore = document.getElementById("best-score");
	bestScore.innerHTML = "Best:" + score;
}

function getNowScore() {
	var nowScore = document.getElementById("now-score");
	var score = parseInt(nowScore.innerHTML.substr(17));
	return score;
}

function setNowScore(score) {
	var nowScore = document.getElementById("now-score");
	nowScore.innerHTML = "Now:" + score;
}

function copyGrid(grid) {
	// This is dirty
	return JSON.parse(JSON.stringify(grid));
}

// COOOOOOOOOKIE crisp
function getBestScoreCookie() {
	// Assuming we only store one cookie
	console.log(document.cookie);
	if(document.cookie != "") {
		var bestScoreStr = "bestScore=";
		var scoreStr = document.cookie.substring(bestScoreStr.length, document.cookie.length-1);
		var score = parseInt(scoreStr);
		return score;
	}

	return Math.NaN;
}

function setBestScoreCookie(bestScore) {
	var d = new Date();
	d.setTime(d.getTime() + (100 * 24 * 60 * 60 * 1000));
	document.cookie = "bestScore=" + bestScore + "; expires=" + d.toUTCString();
}

// Implementation
var handleClickWrapper = function(grid) {
	return function(evt) {
		handleClick(evt, grid);
	}
}
function listenForClicks(grid) {
	var tiles = document.getElementsByClassName("tile");
	for(var t = 0; t < tiles.length; t++) {
		var tileDiv = tiles[t];
		tileDiv.addEventListener("click", handleClick, false);
	}
}

function bindUtilityButtons() {
	var resetBtn = document.getElementById("reset");
	resetBtn.addEventListener("click", function(evt) {
		grid = reset(grid, startingGrid);
	}, false);

	var newGridBtn = document.getElementById("new");
	newGridBtn.addEventListener("click", function(evt) {
		grid = reset(grid);
	}, false);
}

function handleClick(evt) {
	var tileDiv = evt.target;
	var id = tileDiv.id;
	var index = parseInt(id.substr(1));

	var x = index % grid.size;
	var y = (index - x) / grid.size;
	toggle(grid, x, y);
	displayGrid(grid);

	// Update score
	setNowScore(getNowScore() + 1);

	if(isSolved(grid)) {
		setTimeout(function() {
			handleWin();
		}, 200);
	}
}

function handleWin() {
	var tileDivs = document.getElementsByClassName("tile");
	for(var t = 0; t < tileDivs.length; t++) {
		tileDivs[t].className = "tile on";
		tileDivs[t].removeEventListener("click", handleClick, false);
	}

	fancyFlashEffect();
	setTimeout(displayWinTiles, 1200);

	var bestScore = getBestScoreCookie();
	var nowScore = getNowScore();
	if (isNaN(bestScore) || nowScore < bestScore) {
		setBestScore(nowScore);
		setBestScoreCookie(nowScore);
	}
}

function offon(tile) {
	tile.className = "tile";

	setTimeout(function() {
		tile.className = "tile on";
	}, 200);
}

function fancyFlashEffect() {
	var tileDivs = document.getElementsByClassName("tile");

	for(var y = 0; y < 4*GRID_SIZE; y++) {
		(function(y) {
			setTimeout(function() {
				var boundedY = y % (2 * GRID_SIZE);
				for(var ty = boundedY; ty >= 0; ty--) {
					var tx = boundedY - ty;
					var index = ty * GRID_SIZE + tx;

					if(index >= 0 && index < GRID_SIZE * GRID_SIZE - 1)
						offon(tileDivs[index]);
				}
			}, y * 50);
		})(y);
	}
}

function facebookListener () {
	var url = "https://facebook.com/dialog/feed?";
	var appID = "774394885948709";
	var link = "http://williamg.me";
	var description = "I turned on all the lights in only 10 moves! Can you beat me?";
	url += "app_id=" + appID;
	url += "&redirect_uri=" + link;
	url += "&description=" + description;
	url += "&link=" + link;

	window.open(encodeURI(url));
}

function twitterListener() {
	var url = "https://twitter.com/share?"
	var link = "http://williamg.me"
	var text = "I turned on all the lights in only 10 moves! Can you beat me?";
	url += "url=" + link;
	url += "&text=" + text;

	window.open(encodeURI(url));
}

function displayWinTiles() {
	var tileDivs = document.getElementsByClassName("tile");
	tileDivs[6].innerHTML = "Y";
	tileDivs[7].innerHTML = "O";
	tileDivs[8].innerHTML = "U";
	tileDivs[11].innerHTML = "W";
	tileDivs[12].innerHTML = "I";
	tileDivs[13].innerHTML = "N";
	tileDivs[16].className += " twitter";
	tileDivs[18].className += " facebook";

	var facebook = tileDivs[18];
	facebook.addEventListener("click", facebookListener, false);

	var twitter = tileDivs[16];
	twitter.addEventListener("click", twitterListener, false);
}

function toggle(grid, x, y) {
	if(x < 0 || x >= grid.size || y < 0 || y >= grid.size) {
		console.log("ERROR: Coordinates (" + x + ", " + y + ") out of bounds!");
		return;
	}

	var tile = grid.tiles[x][y];
	tile.on = !tile.on;

	if(tile.left) {
		var left = grid.tiles[x-1][y];
		left.on = !left.on;
	}

	if(tile.right) {
		var right = grid.tiles[x+1][y];
		right.on = !right.on;
	}

	if(tile.up) {
		var up = grid.tiles[x][y-1];
		up.on = !up.on;
	}

	if(tile.down) {
		var down = grid.tiles[x][y+1];
		down.on = !down.on;
	}
}

function unSolve(numMoves) {
	var moves = [];

	for(var m = 0; m < numMoves; m++) {
		// Generate random place
		var x = Math.floor(Math.random() * grid.size);
		var y = Math.floor(Math.random() * grid.size);
		toggle(grid, x, y);

		moves[numMoves - m - 1] = {x: x, y: y};
	}

	return moves;
}

function displayGrid() {
	var tiles = document.getElementsByClassName('tile');

	for(var t = 0; t < tiles.length; t++) {
		var x = t % grid.size;
		var y = (t - x) / grid.size;

		var tile = grid.tiles[x][y];
		var div = tiles[t];
		div.className = "tile";
		div.innerHTML = "";

		if(tile.on)
			div.className += " on";

		// Determine if this is a directed tile or not (edges make this tricky)
		if(!isDirected(tile, grid.size)) continue;

		if(tile.left)
			div.className += " left";
		if(tile.right)
			div.className += " right";
		if(tile.up)
			div.className += " up";
		if(tile.down)
			div.className += " down";
	}
}

function reset(oldGrid, newGrid) {
	grid = newGrid;

	if(grid == undefined) {
		grid = Grid(GRID_SIZE);
		solution = unSolve(SOLUTION_LENGTH);
	}

	listenForClicks();

	var tileDivs = document.getElementsByClassName('tile');

	for(var t = 0; t < tileDivs.length; t++) {
		var x = t % grid.size;
		var y = (t - x) / grid.size;

		if(oldGrid == undefined || !oldGrid.tiles[x][y].on) {
			tileDivs[t].className += " on";
		}
	}

	var facebook = tileDivs[18];
	facebook.removeEventListener("click", facebookListener, false);

	var twitter = tileDivs[16];
	twitter.removeEventListener("click", twitterListener, false);

	setNowScore(0);
	setTimeout(displayGrid.bind(this, grid), 200);
	startingGrid = copyGrid(grid);
	return grid;
}

var bestScore = getBestScoreCookie();
setBestScoreCookie(bestScore);

if(isNaN(bestScore)) setBestScore("--");
else setBestScore(bestScore);

bindUtilityButtons();
grid = reset();

if(!isSolution(solution)) console.log("Error! Bad solution!");
<commit_msg>Fixed cookie bug pt. 2<commit_after>// GLOBAL VARIABLS
var GRID_SIZE = 5;
var SOLUTION_LENGTH = 1;
var startingGrid;
var solution;
var grid;

// Define some objects
function Tile(x_, y_) {
	var tile = {
		pos: {
			x: x_,
			y: y_
		},
		on: true,
		up: false,
		right: false,
		down: false,
		left: false,
	};

	return tile;
}

// Generate a random grid
function Grid(size) {
	var DIR_PROB = 0.25;

	// Initialize a random directed graph
	var tiles = [];
	for(var x = 0; x < size; x++) {
		tiles[x] = [];
		for(var y = 0; y < size; y++) {
			var tile = Tile(x, y);

			var tileUp = y > 0;
			var tileDown = y < size - 1;
			var tileLeft = x > 0;
			var tileRight = x < size - 1;

			// Randomize directions
			if(Math.random() < 0.5) {
				if(tileLeft && Math.random() < DIR_PROB)
					tile.left = true;

				if(tileRight && Math.random() < DIR_PROB)
					tile.right = true;
			} else {
				if(tileUp && Math.random() < DIR_PROB)
					tile.up = true;

				if(tileDown && Math.random() < DIR_PROB)
					tile.down = true;
			}

			if(!tile.up && !tile.down && !tile.left && !tile.right) {
				tile.up = tileUp;
				tile.right = tileRight;
				tile.down = tileDown;
				tile.left = tileLeft;
			}

			tiles[x][y] = tile;
		}
	}

	var grid = {
		tiles: tiles,
		size: size
	};

	return grid;
}

// Utility functions
function isSolution(solution) {
	var gridCopy = copyGrid(grid);

	for(var s = 0; s < solution.length; s++) {
		var x = solution[s].x;
		var y = solution[s].y;
		toggle(gridCopy, x, y);
	}
	return isSolved(gridCopy);
}

function isSolved(grid) {
	for(var x = 0; x < grid.size; x++) {
		for(var y = 0; y < grid.size; y++) {
			if(!grid.tiles[x][y].on) return false;
		}
	}

	return true;
}

function isDirected(tile, size) {
	var tileUp = tile.pos.y > 0;
	var tileDown = tile.pos.y < size - 1;
	var tileLeft = tile.pos.x > 0;
	var tileRight = tile.pos.x < size - 1;

	var undirected = (tileUp) ? tile.up : true;
	undirected = undirected && ((tileRight) ? tile.right : true);
	undirected = undirected && ((tileDown) ? tile.down : true);
	undirected = undirected && ((tileLeft) ? tile.left : true);

	return !undirected;
}

function getBestScore() {
	var bestScore = document.getElementById("best-score");
	var score = parseInt(bestScore.innerHTML.substr(18));
	return score;

}

function setBestScore(score) {
	var bestScore = document.getElementById("best-score");
	bestScore.innerHTML = "Best:" + score;
}

function getNowScore() {
	var nowScore = document.getElementById("now-score");
	var score = parseInt(nowScore.innerHTML.substr(17));
	return score;
}

function setNowScore(score) {
	var nowScore = document.getElementById("now-score");
	nowScore.innerHTML = "Now:" + score;
}

function copyGrid(grid) {
	// This is dirty
	return JSON.parse(JSON.stringify(grid));
}

// COOOOOOOOOKIE crisp
function getBestScoreCookie() {
	// Assuming we only store one cookie
	console.log(document.cookie);
	if(document.cookie != "") {
		var bestScoreStr = "bestScore=";
		var scoreStr = document.cookie.substr(bestScoreStr.length);
		var score = parseInt(scoreStr);
		return score;
	}

	return Math.NaN;
}

function setBestScoreCookie(bestScore) {
	var d = new Date();
	d.setTime(d.getTime() + (100 * 24 * 60 * 60 * 1000));
	document.cookie = "bestScore=" + bestScore + "; expires=" + d.toUTCString();
}

// Implementation
var handleClickWrapper = function(grid) {
	return function(evt) {
		handleClick(evt, grid);
	}
}
function listenForClicks(grid) {
	var tiles = document.getElementsByClassName("tile");
	for(var t = 0; t < tiles.length; t++) {
		var tileDiv = tiles[t];
		tileDiv.addEventListener("click", handleClick, false);
	}
}

function bindUtilityButtons() {
	var resetBtn = document.getElementById("reset");
	resetBtn.addEventListener("click", function(evt) {
		grid = reset(grid, startingGrid);
	}, false);

	var newGridBtn = document.getElementById("new");
	newGridBtn.addEventListener("click", function(evt) {
		grid = reset(grid);
	}, false);
}

function handleClick(evt) {
	var tileDiv = evt.target;
	var id = tileDiv.id;
	var index = parseInt(id.substr(1));

	var x = index % grid.size;
	var y = (index - x) / grid.size;
	toggle(grid, x, y);
	displayGrid(grid);

	// Update score
	setNowScore(getNowScore() + 1);

	if(isSolved(grid)) {
		setTimeout(function() {
			handleWin();
		}, 200);
	}
}

function handleWin() {
	var tileDivs = document.getElementsByClassName("tile");
	for(var t = 0; t < tileDivs.length; t++) {
		tileDivs[t].className = "tile on";
		tileDivs[t].removeEventListener("click", handleClick, false);
	}

	fancyFlashEffect();
	setTimeout(displayWinTiles, 1200);

	var bestScore = getBestScoreCookie();
	var nowScore = getNowScore();
	if (isNaN(bestScore) || nowScore < bestScore) {
		setBestScore(nowScore);
		setBestScoreCookie(nowScore);
	}
}

function offon(tile) {
	tile.className = "tile";

	setTimeout(function() {
		tile.className = "tile on";
	}, 200);
}

function fancyFlashEffect() {
	var tileDivs = document.getElementsByClassName("tile");

	for(var y = 0; y < 4*GRID_SIZE; y++) {
		(function(y) {
			setTimeout(function() {
				var boundedY = y % (2 * GRID_SIZE);
				for(var ty = boundedY; ty >= 0; ty--) {
					var tx = boundedY - ty;
					var index = ty * GRID_SIZE + tx;

					if(index >= 0 && index < GRID_SIZE * GRID_SIZE - 1)
						offon(tileDivs[index]);
				}
			}, y * 50);
		})(y);
	}
}

function facebookListener () {
	var url = "https://facebook.com/dialog/feed?";
	var appID = "774394885948709";
	var link = "http://williamg.me";
	var description = "I turned on all the lights in only 10 moves! Can you beat me?";
	url += "app_id=" + appID;
	url += "&redirect_uri=" + link;
	url += "&description=" + description;
	url += "&link=" + link;

	window.open(encodeURI(url));
}

function twitterListener() {
	var url = "https://twitter.com/share?"
	var link = "http://williamg.me"
	var text = "I turned on all the lights in only 10 moves! Can you beat me?";
	url += "url=" + link;
	url += "&text=" + text;

	window.open(encodeURI(url));
}

function displayWinTiles() {
	var tileDivs = document.getElementsByClassName("tile");
	tileDivs[6].innerHTML = "Y";
	tileDivs[7].innerHTML = "O";
	tileDivs[8].innerHTML = "U";
	tileDivs[11].innerHTML = "W";
	tileDivs[12].innerHTML = "I";
	tileDivs[13].innerHTML = "N";
	tileDivs[16].className += " twitter";
	tileDivs[18].className += " facebook";

	var facebook = tileDivs[18];
	facebook.addEventListener("click", facebookListener, false);

	var twitter = tileDivs[16];
	twitter.addEventListener("click", twitterListener, false);
}

function toggle(grid, x, y) {
	if(x < 0 || x >= grid.size || y < 0 || y >= grid.size) {
		console.log("ERROR: Coordinates (" + x + ", " + y + ") out of bounds!");
		return;
	}

	var tile = grid.tiles[x][y];
	tile.on = !tile.on;

	if(tile.left) {
		var left = grid.tiles[x-1][y];
		left.on = !left.on;
	}

	if(tile.right) {
		var right = grid.tiles[x+1][y];
		right.on = !right.on;
	}

	if(tile.up) {
		var up = grid.tiles[x][y-1];
		up.on = !up.on;
	}

	if(tile.down) {
		var down = grid.tiles[x][y+1];
		down.on = !down.on;
	}
}

function unSolve(numMoves) {
	var moves = [];

	for(var m = 0; m < numMoves; m++) {
		// Generate random place
		var x = Math.floor(Math.random() * grid.size);
		var y = Math.floor(Math.random() * grid.size);
		toggle(grid, x, y);

		moves[numMoves - m - 1] = {x: x, y: y};
	}

	return moves;
}

function displayGrid() {
	var tiles = document.getElementsByClassName('tile');

	for(var t = 0; t < tiles.length; t++) {
		var x = t % grid.size;
		var y = (t - x) / grid.size;

		var tile = grid.tiles[x][y];
		var div = tiles[t];
		div.className = "tile";
		div.innerHTML = "";

		if(tile.on)
			div.className += " on";

		// Determine if this is a directed tile or not (edges make this tricky)
		if(!isDirected(tile, grid.size)) continue;

		if(tile.left)
			div.className += " left";
		if(tile.right)
			div.className += " right";
		if(tile.up)
			div.className += " up";
		if(tile.down)
			div.className += " down";
	}
}

function reset(oldGrid, newGrid) {
	grid = newGrid;

	if(grid == undefined) {
		grid = Grid(GRID_SIZE);
		solution = unSolve(SOLUTION_LENGTH);
	}

	listenForClicks();

	var tileDivs = document.getElementsByClassName('tile');

	for(var t = 0; t < tileDivs.length; t++) {
		var x = t % grid.size;
		var y = (t - x) / grid.size;

		if(oldGrid == undefined || !oldGrid.tiles[x][y].on) {
			tileDivs[t].className += " on";
		}
	}

	var facebook = tileDivs[18];
	facebook.removeEventListener("click", facebookListener, false);

	var twitter = tileDivs[16];
	twitter.removeEventListener("click", twitterListener, false);

	setNowScore(0);
	setTimeout(displayGrid.bind(this, grid), 200);
	startingGrid = copyGrid(grid);
	return grid;
}

var bestScore = getBestScoreCookie();
setBestScoreCookie(bestScore);

if(isNaN(bestScore)) setBestScore("--");
else setBestScore(bestScore);

bindUtilityButtons();
grid = reset();

if(!isSolution(solution)) console.log("Error! Bad solution!");
<|endoftext|>