<commit_before>/**
 * Copyright 2019 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// import to install chromedriver and geckodriver
require('chromedriver'); // eslint-disable-line no-unused-vars
require('geckodriver'); // eslint-disable-line no-unused-vars

const chrome = require('selenium-webdriver/chrome');
const firefox = require('selenium-webdriver/firefox');
const puppeteer = require('puppeteer');
const {
  SeleniumWebDriverController,
} = require('./selenium-webdriver-controller');
const {AmpDriver, AmpdocEnvironment} = require('./amp-driver');
const {Builder, Capabilities} = require('selenium-webdriver');
const {clearLastExpectError, getLastExpectError} = require('./expect');
const {installRepl, uninstallRepl} = require('./repl');
const {isTravisBuild} = require('../../travis');
const {PuppeteerController} = require('./puppeteer-controller');

/** Should have something in the name, otherwise nothing is shown. */
const SUB = ' ';
const TEST_TIMEOUT = 20000;
const SETUP_TIMEOUT = 30000;
const DEFAULT_E2E_INITIAL_RECT = {width: 800, height: 600};
const defaultBrowsers = new Set(['chrome', 'firefox']);
/**
 * TODO(cvializ): Firefox now experimentally supports puppeteer.
 * When it's more mature we might want to support it.
 * {@link https://github.com/GoogleChrome/puppeteer/blob/master/experimental/puppeteer-firefox/README.md}
 */
const PUPPETEER_BROWSERS = new Set(['chrome']);

/**
 * @typedef {{
 *  browsers: string,
 *  headless: boolean,
 *  engine: string,
 * }}
 */
let DescribesConfigDef;

/**
 * @typedef {{
 *  headless: boolean,
 * }}
 */
let PuppeteerConfigDef;

/**
 * @typedef {{
 *  headless: boolean,
 * }}
 */
let SeleniumConfigDef;

/** @const {?DescribesConfigDef} */
let describesConfig = null;

/**
 * Map the browserName to the capabilities name. (firefox for example has a
 * prefix.
 *
 * @enum {string}
 */
const capabilitiesKeys = {
  'chrome': 'chromeOptions',
  'firefox': 'moz:firefoxOptions',
};

/**
 * Configure all tests. This may only be called once, since it is only read once
 * and writes after reading will not have any effect.
 * @param {!DescribesConfigDef} config
 */
function configure(config) {
  if (describesConfig) {
    throw new Error('describes.config should only be called once');
  }

  describesConfig = Object.assign({}, config);
}

/**
 * Retrieve the describes config if set.
 * If not set, it sets the config to an empty object and returns it.
 * After getting the config the first time, the config may not be changed.
 * @return {!DescribesConfigDef}
 */
function getConfig() {
  if (!describesConfig) {
    describesConfig = {};
  }

  return describesConfig;
}

/**
 * Configure and launch a Puppeteer instance
 * @param {!PuppeteerConfigDef=} opt_config
 * @return {!Promise}
 */
async function createPuppeteer(opt_config = {}) {
  const browser = await puppeteer.launch({
    headless: opt_config.headless || false,
    devtools: false,
    defaultViewport: null,
    timeout: 0,
  });
  return browser;
}

/**
 * Configure and launch a Selenium instance
 * @param {string} browserName
 * @param {!SeleniumConfigDef=} opt_config
 * @return {!SeleniumDriver}
 */
async function createSelenium(browserName, opt_config = {}) {
  // TODO(estherkim): implement sessions
  // See https://w3c.github.io/webdriver/#sessions
  switch (browserName) {
    case 'firefox':
      return createDriver(browserName, getFirefoxArgs(opt_config));
    case 'chrome':
    default:
      return createDriver(browserName, getChromeArgs(opt_config));
  }
}

async function createDriver(browserName, args) {
  const capabilities = Capabilities[browserName]();
  capabilities.set(capabilitiesKeys[browserName], {'args': args});
  const builder = new Builder().withCapabilities(capabilities);
  switch (browserName) {
    case 'firefox':
      const options = new firefox.Options();
      // for some reason firefox.Options().addArguments() doesn't like arrays
      args.forEach(arg => {
        options.addArguments(arg);
      });
      builder.setFirefoxOptions(options);
    case 'chrome':
      builder.setChromeOptions(new chrome.Options().addArguments(args));
  }
  const driver = await builder.build();
  return driver;
}

/**
 * Configure chrome args.
 *
 * @param {!SeleniumConfigDef} config
 * @return {!Array<string>}
 */
function getChromeArgs(config) {
  const args = ['--no-sandbox', '--disable-gpu'];

  // TODO(cvializ,estherkim,sparhami):
  // figure out why headless causes more flakes
  if (config.headless) {
    args.push('--headless');
  }
  return args;
}

/**
 * Configure firefox args.
 *
 * @param {!SeleniumConfigDef} config
 * @return {!Array<string>}
 */
function getFirefoxArgs(config) {
  const args = [];

  if (config.headless) {
    args.push('--headless');
  }
  return args;
}

/**
 * @typedef {{
 *  browsers: (!Array<string>|undefined),
 *  environments: (!Array<!AmpdocEnvironment>|undefined),
 *  testUrl: string,
 *  initialRect: ({{width: number, height:number}}|undefined)
 * }}
 */
let TestSpec;

/**
 * An end2end test using Selenium Web Driver or Puppeteer
 */
const endtoend = describeEnv(spec => new EndToEndFixture(spec));

/**
 * Maps an environment enum value to a `describes.repeated` variant object.
 */
const EnvironmentVariantMap = {
  [AmpdocEnvironment.SINGLE]: {
    name: 'Standalone environment',
    value: {environment: 'single'},
  },
  [AmpdocEnvironment.VIEWER_DEMO]: {
    name: 'Viewer environment',
    value: {environment: 'viewer-demo'},
  },
  [AmpdocEnvironment.SHADOW_DEMO]: {
    name: 'Shadow environment',
    value: {environment: 'shadow-demo'},
  },
  [AmpdocEnvironment.A4A_FIE]: {
    name: 'AMPHTML ads FIE environment',
    value: {environment: 'a4a-fie'},
  },
  [AmpdocEnvironment.A4A_INABOX]: {
    name: 'AMPHTML ads inabox environment',
    value: {environment: 'a4a-inabox'},
  },
  [AmpdocEnvironment.A4A_INABOX_FRIENDLY]: {
    name: 'AMPHTML ads inabox friendly frame environment',
    value: {environment: 'a4a-inabox-friendly'},
  },
  [AmpdocEnvironment.A4A_INABOX_SAFEFRAME]: {
    name: 'AMPHTML ads inabox safeframe environment',
    value: {environment: 'a4a-inabox-safeframe'},
  },
};

const envPresets = {
  'ampdoc-preset': [
    AmpdocEnvironment.SINGLE,
    AmpdocEnvironment.VIEWER_DEMO,
    AmpdocEnvironment.SHADOW_DEMO,
  ],
  'amp4ads-preset': [
    AmpdocEnvironment.A4A_FIE,
    AmpdocEnvironment.A4A_INABOX,
    AmpdocEnvironment.A4A_INABOX_FRIENDLY,
    AmpdocEnvironment.A4A_INABOX_SAFEFRAME,
  ],
};
envPresets['ampdoc-amp4ads-preset'] = envPresets['ampdoc-preset'].concat(
  envPresets['amp4ads-preset']
);

/**
 * Helper class to skip E2E tests in a specific AMP environment.
 * Must be instantiated using it.configure().
 *
 * Example usage:
 * it.configure().skipViewerDemo().skipShadowDemo().run('Should ...', ...);
 */
class ItConfig {
  constructor(it, env) {
    this.it = it;
    this.env = env;
    this.skip = false;
  }

  skipShadowDemo() {
    this.skip = this.skip ? this.skip : this.env.environment == 'shadow-demo';
    return this;
  }

  skipSingle() {
    this.skip = this.skip ? this.skip : this.env.environment == 'single';
    return this;
  }

  skipViewerDemo() {
    this.skip = this.skip ? this.skip : this.env.environment == 'viewer-demo';
    return this;
  }

  skipA4aFie() {
    this.skip = this.skip ? this.skip : this.env.environment == 'a4a-fie';
    return this;
  }

  run(name, fn) {
    if (this.skip) {
      return this.it.skip(name, fn);
    }

    this.it(name, function() {
      return fn.apply(this, arguments);
    });
  }
}

/**
 * Returns a wrapped version of Mocha's describe(), it() and only() methods
 * that also sets up the provided fixtures and returns the corresponding
 * environment objects of each fixture to the test method.
 * @param {function(!Object):!Array<?Fixture>} factory
 * @return {function()}
 */
function describeEnv(factory) {
  /**
   * @param {string} suiteName
   * @param {!Object} spec
   * @param {function(!Object)} fn
   * @param {function(string, function())} describeFunc
   * @return {function()}
   */
  const templateFunc = function(suiteName, spec, fn, describeFunc) {
    const fixture = factory(spec);
    let environments = spec.environments || 'ampdoc-preset';
    if (typeof environments === 'string') {
      environments = envPresets[environments];
    }
    if (!environments) {
      throw new Error('Invalid environment preset: ' + spec.environments);
    }
    const variants = Object.create(null);
    environments.forEach(environment => {
      const o = EnvironmentVariantMap[environment];
      variants[o.name] = o.value;
    });

    // Use chrome as default if no browser is specified
    if (!Array.isArray(spec.browsers)) {
      spec.browsers = ['chrome'];
    }

    function createBrowserDescribe() {
      const allowedBrowsers = getAllowedBrowsers();

      spec.browsers
        .filter(x => allowedBrowsers.has(x))
        .forEach(browserName => {
          describe(browserName, function() {
            createVariantDescribe(browserName);
          });
        });
    }

    function getAllowedBrowsers() {
      const {engine, browsers} = getConfig();

      const allowedBrowsers = browsers
        ? new Set(browsers.split(',').map(x => x.trim()))
        : defaultBrowsers;

      if (engine === 'puppeteer') {
        const result = intersect(allowedBrowsers, PUPPETEER_BROWSERS);
        if (result.size === 0) {
          const browsersList = Array.from(allowedBrowsers).join(',');
          throw new Error(
            `browsers ${browsersList} not supported by Puppeteer`
          );
        }
        return result;
      }

      return allowedBrowsers;
    }

    function createVariantDescribe(browserName) {
      for (const name in variants) {
        it.configure = function() {
          return new ItConfig(it, variants[name]);
        };

        describe(name ? ` ${name} ` : SUB, function() {
          doTemplate.call(this, name, variants[name], browserName);
        });
      }
    }

    return describeFunc(suiteName, function() {
      createBrowserDescribe();
    });

    function doTemplate(name, variant, browserName) {
      const env = Object.create(variant);
      this.timeout(TEST_TIMEOUT);
      beforeEach(async function() {
        this.timeout(SETUP_TIMEOUT);
        await fixture.setup(env, browserName);

        // don't install for CI
        if (!isTravisBuild()) {
          installRepl(global, env);
        }
      });

      afterEach(async function() {
        // If there is an async expect error, throw it in the final state.
        const lastExpectError = getLastExpectError();
        if (lastExpectError) {
          this.test.error(lastExpectError);
          clearLastExpectError();
        }

        await fixture.teardown(env);
        for (const key in env) {
          delete env[key];
        }

        if (!isTravisBuild()) {
          uninstallRepl();
        }
      });

      describe(SUB, function() {
        fn.call(this, env);
      });
    }
  };

  /**
   * @param {string} name
   * @param {!Object} spec
   * @param {function(!Object)} fn
   * @return {function()}
   */
  const mainFunc = function(name, spec, fn) {
    return templateFunc(name, spec, fn, describe);
  };

  /**
   * @param {string} name
   * @param {!Object} spec
   * @param {function(!Object)} fn
   * @return {function()}
   */
  mainFunc.only = function(name, spec, fn) {
    return templateFunc(name, spec, fn, describe./*OK*/ only);
  };

  mainFunc.skip = function(name, variants, fn) {
    return templateFunc(name, variants, fn, describe.skip);
  };

  return mainFunc;
}

class EndToEndFixture {
  /** @param {!TestSpec} spec */
  constructor(spec) {
    /** @const */
    this.spec = spec;
  }

  /**
   * @param {!Object} env
   * @param {string} browserName
   */
  async setup(env, browserName) {
    const config = getConfig();
    const controller = await getController(config, browserName);
    const ampDriver = new AmpDriver(controller);
    env.controller = controller;
    env.ampDriver = ampDriver;

    const {
      testUrl,
      experiments = [],
      initialRect = DEFAULT_E2E_INITIAL_RECT,
    } = this.spec;
    const {environment} = env;

    await toggleExperiments(ampDriver, testUrl, experiments);

    const {width, height} = initialRect;
    await controller.setWindowRect({width, height});

    await ampDriver.navigateToEnvironment(environment, testUrl);
  }

  async teardown(env) {
    const {controller} = env;
    if (controller) {
      await controller.switchToParent();
      await controller.dispose();
    }
  }
}

/**
 * Get the controller object for the configured engine.
 * @param {!DescribesConfigDef} describesConfig
 * @param {string} browserName
 * @return {!SeleniumWebDriverController}
 */
async function getController(
  {engine = 'selenium', headless = false},
  browserName
) {
  if (engine == 'puppeteer') {
    const browser = await createPuppeteer({headless});
    return new PuppeteerController(browser);
  }

  if (engine == 'selenium') {
    const driver = await createSelenium(browserName, {headless});
    return new SeleniumWebDriverController(driver);
  }
}

/**
 * Toggle the given experiments for the given test URL domain.
 * @param {!AmpDriver} ampDriver
 * @param {string} testUrl
 * @param {!Array<string>} experiments
 * @return {!Promise}
 */
async function toggleExperiments(ampDriver, testUrl, experiments) {
  if (!experiments.length) {
    return;
  }

  await ampDriver.navigateToEnvironment(AmpdocEnvironment.SINGLE, testUrl);

  for (const experiment of experiments) {
    await ampDriver.toggleExperiment(experiment, true);
  }
}

/**
 * Intersection of two sets
 * @param {Set<T>} a
 * @param {Set<T>} b
 * @return {Set<T>}
 * @template T
 */
function intersect(a, b) {
  return new Set(Array.from(a).filter(aItem => b.has(aItem)));
}

module.exports = {
  TestSpec,
  endtoend,
  configure,
};
<commit_msg>Toggle experiments correctly for inabox e2e tests (#24134)<commit_after>/**
 * Copyright 2019 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// import to install chromedriver and geckodriver
require('chromedriver'); // eslint-disable-line no-unused-vars
require('geckodriver'); // eslint-disable-line no-unused-vars

const chrome = require('selenium-webdriver/chrome');
const firefox = require('selenium-webdriver/firefox');
const puppeteer = require('puppeteer');
const {
  SeleniumWebDriverController,
} = require('./selenium-webdriver-controller');
const {AmpDriver, AmpdocEnvironment} = require('./amp-driver');
const {Builder, Capabilities} = require('selenium-webdriver');
const {clearLastExpectError, getLastExpectError} = require('./expect');
const {installRepl, uninstallRepl} = require('./repl');
const {isTravisBuild} = require('../../travis');
const {PuppeteerController} = require('./puppeteer-controller');

/** Should have something in the name, otherwise nothing is shown. */
const SUB = ' ';
const TEST_TIMEOUT = 20000;
const SETUP_TIMEOUT = 30000;
const DEFAULT_E2E_INITIAL_RECT = {width: 800, height: 600};
const defaultBrowsers = new Set(['chrome', 'firefox']);
/**
 * TODO(cvializ): Firefox now experimentally supports puppeteer.
 * When it's more mature we might want to support it.
 * {@link https://github.com/GoogleChrome/puppeteer/blob/master/experimental/puppeteer-firefox/README.md}
 */
const PUPPETEER_BROWSERS = new Set(['chrome']);

/**
 * @typedef {{
 *  browsers: string,
 *  headless: boolean,
 *  engine: string,
 * }}
 */
let DescribesConfigDef;

/**
 * @typedef {{
 *  headless: boolean,
 * }}
 */
let PuppeteerConfigDef;

/**
 * @typedef {{
 *  headless: boolean,
 * }}
 */
let SeleniumConfigDef;

/** @const {?DescribesConfigDef} */
let describesConfig = null;

/**
 * Map the browserName to the capabilities name. (firefox for example has a
 * prefix.
 *
 * @enum {string}
 */
const capabilitiesKeys = {
  'chrome': 'chromeOptions',
  'firefox': 'moz:firefoxOptions',
};

/**
 * Configure all tests. This may only be called once, since it is only read once
 * and writes after reading will not have any effect.
 * @param {!DescribesConfigDef} config
 */
function configure(config) {
  if (describesConfig) {
    throw new Error('describes.config should only be called once');
  }

  describesConfig = Object.assign({}, config);
}

/**
 * Retrieve the describes config if set.
 * If not set, it sets the config to an empty object and returns it.
 * After getting the config the first time, the config may not be changed.
 * @return {!DescribesConfigDef}
 */
function getConfig() {
  if (!describesConfig) {
    describesConfig = {};
  }

  return describesConfig;
}

/**
 * Configure and launch a Puppeteer instance
 * @param {!PuppeteerConfigDef=} opt_config
 * @return {!Promise}
 */
async function createPuppeteer(opt_config = {}) {
  const browser = await puppeteer.launch({
    headless: opt_config.headless || false,
    devtools: false,
    defaultViewport: null,
    timeout: 0,
  });
  return browser;
}

/**
 * Configure and launch a Selenium instance
 * @param {string} browserName
 * @param {!SeleniumConfigDef=} opt_config
 * @return {!SeleniumDriver}
 */
async function createSelenium(browserName, opt_config = {}) {
  // TODO(estherkim): implement sessions
  // See https://w3c.github.io/webdriver/#sessions
  switch (browserName) {
    case 'firefox':
      return createDriver(browserName, getFirefoxArgs(opt_config));
    case 'chrome':
    default:
      return createDriver(browserName, getChromeArgs(opt_config));
  }
}

async function createDriver(browserName, args) {
  const capabilities = Capabilities[browserName]();
  capabilities.set(capabilitiesKeys[browserName], {'args': args});
  const builder = new Builder().withCapabilities(capabilities);
  switch (browserName) {
    case 'firefox':
      const options = new firefox.Options();
      // for some reason firefox.Options().addArguments() doesn't like arrays
      args.forEach(arg => {
        options.addArguments(arg);
      });
      builder.setFirefoxOptions(options);
    case 'chrome':
      builder.setChromeOptions(new chrome.Options().addArguments(args));
  }
  const driver = await builder.build();
  return driver;
}

/**
 * Configure chrome args.
 *
 * @param {!SeleniumConfigDef} config
 * @return {!Array<string>}
 */
function getChromeArgs(config) {
  const args = ['--no-sandbox', '--disable-gpu'];

  // TODO(cvializ,estherkim,sparhami):
  // figure out why headless causes more flakes
  if (config.headless) {
    args.push('--headless');
  }
  return args;
}

/**
 * Configure firefox args.
 *
 * @param {!SeleniumConfigDef} config
 * @return {!Array<string>}
 */
function getFirefoxArgs(config) {
  const args = [];

  if (config.headless) {
    args.push('--headless');
  }
  return args;
}

/**
 * @typedef {{
 *  browsers: (!Array<string>|undefined),
 *  environments: (!Array<!AmpdocEnvironment>|undefined),
 *  testUrl: string,
 *  initialRect: ({{width: number, height:number}}|undefined)
 * }}
 */
let TestSpec;

/**
 * An end2end test using Selenium Web Driver or Puppeteer
 */
const endtoend = describeEnv(spec => new EndToEndFixture(spec));

/**
 * Maps an environment enum value to a `describes.repeated` variant object.
 */
const EnvironmentVariantMap = {
  [AmpdocEnvironment.SINGLE]: {
    name: 'Standalone environment',
    value: {environment: 'single'},
  },
  [AmpdocEnvironment.VIEWER_DEMO]: {
    name: 'Viewer environment',
    value: {environment: 'viewer-demo'},
  },
  [AmpdocEnvironment.SHADOW_DEMO]: {
    name: 'Shadow environment',
    value: {environment: 'shadow-demo'},
  },
  [AmpdocEnvironment.A4A_FIE]: {
    name: 'AMPHTML ads FIE environment',
    value: {environment: 'a4a-fie'},
  },
  [AmpdocEnvironment.A4A_INABOX]: {
    name: 'AMPHTML ads inabox environment',
    value: {environment: 'a4a-inabox'},
  },
  [AmpdocEnvironment.A4A_INABOX_FRIENDLY]: {
    name: 'AMPHTML ads inabox friendly frame environment',
    value: {environment: 'a4a-inabox-friendly'},
  },
  [AmpdocEnvironment.A4A_INABOX_SAFEFRAME]: {
    name: 'AMPHTML ads inabox safeframe environment',
    value: {environment: 'a4a-inabox-safeframe'},
  },
};

const envPresets = {
  'ampdoc-preset': [
    AmpdocEnvironment.SINGLE,
    AmpdocEnvironment.VIEWER_DEMO,
    AmpdocEnvironment.SHADOW_DEMO,
  ],
  'amp4ads-preset': [
    AmpdocEnvironment.A4A_FIE,
    AmpdocEnvironment.A4A_INABOX,
    AmpdocEnvironment.A4A_INABOX_FRIENDLY,
    AmpdocEnvironment.A4A_INABOX_SAFEFRAME,
  ],
};
envPresets['ampdoc-amp4ads-preset'] = envPresets['ampdoc-preset'].concat(
  envPresets['amp4ads-preset']
);

/**
 * Helper class to skip E2E tests in a specific AMP environment.
 * Must be instantiated using it.configure().
 *
 * Example usage:
 * it.configure().skipViewerDemo().skipShadowDemo().run('Should ...', ...);
 */
class ItConfig {
  constructor(it, env) {
    this.it = it;
    this.env = env;
    this.skip = false;
  }

  skipShadowDemo() {
    this.skip = this.skip ? this.skip : this.env.environment == 'shadow-demo';
    return this;
  }

  skipSingle() {
    this.skip = this.skip ? this.skip : this.env.environment == 'single';
    return this;
  }

  skipViewerDemo() {
    this.skip = this.skip ? this.skip : this.env.environment == 'viewer-demo';
    return this;
  }

  skipA4aFie() {
    this.skip = this.skip ? this.skip : this.env.environment == 'a4a-fie';
    return this;
  }

  run(name, fn) {
    if (this.skip) {
      return this.it.skip(name, fn);
    }

    this.it(name, function() {
      return fn.apply(this, arguments);
    });
  }
}

/**
 * Returns a wrapped version of Mocha's describe(), it() and only() methods
 * that also sets up the provided fixtures and returns the corresponding
 * environment objects of each fixture to the test method.
 * @param {function(!Object):!Array<?Fixture>} factory
 * @return {function()}
 */
function describeEnv(factory) {
  /**
   * @param {string} suiteName
   * @param {!Object} spec
   * @param {function(!Object)} fn
   * @param {function(string, function())} describeFunc
   * @return {function()}
   */
  const templateFunc = function(suiteName, spec, fn, describeFunc) {
    const fixture = factory(spec);
    let environments = spec.environments || 'ampdoc-preset';
    if (typeof environments === 'string') {
      environments = envPresets[environments];
    }
    if (!environments) {
      throw new Error('Invalid environment preset: ' + spec.environments);
    }
    const variants = Object.create(null);
    environments.forEach(environment => {
      const o = EnvironmentVariantMap[environment];
      variants[o.name] = o.value;
    });

    // Use chrome as default if no browser is specified
    if (!Array.isArray(spec.browsers)) {
      spec.browsers = ['chrome'];
    }

    function createBrowserDescribe() {
      const allowedBrowsers = getAllowedBrowsers();

      spec.browsers
        .filter(x => allowedBrowsers.has(x))
        .forEach(browserName => {
          describe(browserName, function() {
            createVariantDescribe(browserName);
          });
        });
    }

    function getAllowedBrowsers() {
      const {engine, browsers} = getConfig();

      const allowedBrowsers = browsers
        ? new Set(browsers.split(',').map(x => x.trim()))
        : defaultBrowsers;

      if (engine === 'puppeteer') {
        const result = intersect(allowedBrowsers, PUPPETEER_BROWSERS);
        if (result.size === 0) {
          const browsersList = Array.from(allowedBrowsers).join(',');
          throw new Error(
            `browsers ${browsersList} not supported by Puppeteer`
          );
        }
        return result;
      }

      return allowedBrowsers;
    }

    function createVariantDescribe(browserName) {
      for (const name in variants) {
        it.configure = function() {
          return new ItConfig(it, variants[name]);
        };

        describe(name ? ` ${name} ` : SUB, function() {
          doTemplate.call(this, name, variants[name], browserName);
        });
      }
    }

    return describeFunc(suiteName, function() {
      createBrowserDescribe();
    });

    function doTemplate(name, variant, browserName) {
      const env = Object.create(variant);
      this.timeout(TEST_TIMEOUT);
      beforeEach(async function() {
        this.timeout(SETUP_TIMEOUT);
        await fixture.setup(env, browserName);

        // don't install for CI
        if (!isTravisBuild()) {
          installRepl(global, env);
        }
      });

      afterEach(async function() {
        // If there is an async expect error, throw it in the final state.
        const lastExpectError = getLastExpectError();
        if (lastExpectError) {
          this.test.error(lastExpectError);
          clearLastExpectError();
        }

        await fixture.teardown(env);
        for (const key in env) {
          delete env[key];
        }

        if (!isTravisBuild()) {
          uninstallRepl();
        }
      });

      describe(SUB, function() {
        fn.call(this, env);
      });
    }
  };

  /**
   * @param {string} name
   * @param {!Object} spec
   * @param {function(!Object)} fn
   * @return {function()}
   */
  const mainFunc = function(name, spec, fn) {
    return templateFunc(name, spec, fn, describe);
  };

  /**
   * @param {string} name
   * @param {!Object} spec
   * @param {function(!Object)} fn
   * @return {function()}
   */
  mainFunc.only = function(name, spec, fn) {
    return templateFunc(name, spec, fn, describe./*OK*/ only);
  };

  mainFunc.skip = function(name, variants, fn) {
    return templateFunc(name, variants, fn, describe.skip);
  };

  return mainFunc;
}

class EndToEndFixture {
  /** @param {!TestSpec} spec */
  constructor(spec) {
    /** @const */
    this.spec = spec;
  }

  /**
   * @param {!Object} env
   * @param {string} browserName
   */
  async setup(env, browserName) {
    const config = getConfig();
    const controller = await getController(config, browserName);
    const ampDriver = new AmpDriver(controller);
    env.controller = controller;
    env.ampDriver = ampDriver;

    const {
      testUrl,
      experiments = [],
      initialRect = DEFAULT_E2E_INITIAL_RECT,
    } = this.spec;
    const {environment} = env;

    const url = new URL(testUrl);
    if (experiments.length > 0) {
      if (environment.includes('inabox')) {
        // inabox experiments are toggled at server side using <meta> tag
        url.searchParams.set('exp', experiments.join(','));
      } else {
        // AMP doc experiments are toggled via cookies
        await toggleExperiments(ampDriver, url.href, experiments);
      }
    }

    const {width, height} = initialRect;
    await controller.setWindowRect({width, height});

    await ampDriver.navigateToEnvironment(environment, url.href);
  }

  async teardown(env) {
    const {controller} = env;
    if (controller) {
      await controller.switchToParent();
      await controller.dispose();
    }
  }
}

/**
 * Get the controller object for the configured engine.
 * @param {!DescribesConfigDef} describesConfig
 * @param {string} browserName
 * @return {!SeleniumWebDriverController}
 */
async function getController(
  {engine = 'selenium', headless = false},
  browserName
) {
  if (engine == 'puppeteer') {
    const browser = await createPuppeteer({headless});
    return new PuppeteerController(browser);
  }

  if (engine == 'selenium') {
    const driver = await createSelenium(browserName, {headless});
    return new SeleniumWebDriverController(driver);
  }
}

/**
 * Toggle the given experiments for the given test URL domain.
 * @param {!AmpDriver} ampDriver
 * @param {string} testUrl
 * @param {!Array<string>} experiments
 * @return {!Promise}
 */
async function toggleExperiments(ampDriver, testUrl, experiments) {
  await ampDriver.navigateToEnvironment(AmpdocEnvironment.SINGLE, testUrl);

  for (const experiment of experiments) {
    await ampDriver.toggleExperiment(experiment, true);
  }
}

/**
 * Intersection of two sets
 * @param {Set<T>} a
 * @param {Set<T>} b
 * @return {Set<T>}
 * @template T
 */
function intersect(a, b) {
  return new Set(Array.from(a).filter(aItem => b.has(aItem)));
}

module.exports = {
  TestSpec,
  endtoend,
  configure,
};
<|endoftext|>


<commit_before>import hopp from 'hopp'

export const css =
  hopp([ 'src/css/*.css' ])
    .dest('dist')

export const js =
  hopp([ 'src/js/*.js' ])
    .dest('dist')

export default [
  'js',
  'css'
]<commit_msg>Fix example hoppfile destinations<commit_after>import hopp from 'hopp'

export const css =
  hopp([ 'src/css/*.css' ])
    .dest('dist/css')

export const js =
  hopp([ 'src/js/*.js' ]) // create fs streams
    // .babel()              // pipe to (create babel stream)
    .dest('dist/js')         // pipe to (create dest stream)

export default [
  'js',
  'css'
]<|endoftext|>


<commit_before>function js_set_handler(cid, name, cb, obj) {
  obj[name] = function(e) {
    e.persist();
    cb(cid, e);
  };
}

function js_createClass(obj) {
  // Constructor passes through to React
  var cls = function() {
    React.Component.apply(this, arguments); // (super(props))
    // TODO(joel) necessary?
    this.dispatch = this.dispatch.bind(this);
  };

  // Copy parent prototype
  cls.prototype = Object.create(React.Component.prototype);

  cls.prototype.render = function() {
    var gimmeResult = {};
    obj.render([this.props.componentId, this], gimmeResult);
    return gimmeResult.value;
  };

  cls.prototype.dispatch = function(e, name) {
    cls[name].call(this, e)
  };

  cls.displayName = obj.displayName;

  // Point constructor to the right place
  cls.prototype.constructor = cls;

  cls.prototype.componentWillMount = function() {
    obj.componentWillMount(this.props.componentId);
  };

  cls.prototype.componentWillUnmount = function() {
    obj.componentWillUnmount(this.props.componentId);
  };

  cls.prototype.getChildContext = function() {
    return obj.childContext
  };

  // TODO this is weird - why is it not on the prototype?
  cls.childContextTypes = obj.childContextTypes;

  return cls;
}
<commit_msg>Update react-stubs to new version of react-haskell.<commit_after>function js_set_handler(cid, name, cb, obj) {
  obj[name] = function(e) {
    // TODO(joel) either persist or force, not both?
    e.persist();
    cb(e);
  };
}

function js_createClass(obj) {
  // Constructor passes through to React
  var cls = function() {
    React.Component.apply(this, arguments); // (super(props))
    // TODO(joel) necessary?
    this.dispatch = this.dispatch.bind(this);
  };

  // Copy parent prototype
  cls.prototype = Object.create(React.Component.prototype);

  cls.prototype.render = function() {
    var gimmeResult = {};
    obj.render([this.props.componentId, this], gimmeResult);
    return gimmeResult.value;
  };

  cls.prototype.dispatch = function(e, name) {
    cls[name].call(this, e)
  };

  cls.displayName = obj.displayName;

  // Point constructor to the right place
  cls.prototype.constructor = cls;

  cls.prototype.componentWillMount = function() {
    obj.componentWillMount(this.props.componentId);
  };

  cls.prototype.componentWillUnmount = function() {
    obj.componentWillUnmount(this.props.componentId);
  };

  cls.prototype.getChildContext = function() {
    return obj.childContext
  };

  // TODO this is weird - why is it not on the prototype?
  cls.childContextTypes = obj.childContextTypes;

  return cls;
}
<|endoftext|>


<commit_before>define([
	'jquery',
	'knockout',
	'classes/ActiveAbility',
	'Utils'
], function($, ko, ActiveAbility, Utils){

	function FindFood(data){

		var self = this;

		ActiveAbility.call(this, data);

		this.init = function(data){

			self.skillStages = [
				"poor",
				"good",
				"great",
				"exceptional"
			];

		}

		this.doOnLevelUp = function(){
			//Advance to next skill stage (if possible)
			var idxOf = self.skillStages.indexOf(self.skillLevel());
			if(idxOf < (self.skillStages.length - 1)){
				idxOf++;
				self.skillLevel( self.skillStages[idxOf] );
			}else{
				self.canTrainNextLevel(0);
				self.didLevelUp = 0;
			}
		}

		this.getTrainCost = function(){
			/*var baseCost = (self.skillStages.indexOf(self.skillLevel()) + 1) * 1000
			return baseCost + ((self.skillProgress() + 1) * 200);*/
			return (300 * self.skillLevel());
		}

		this.init(data);
	}

	FindFood.prototype = Object.create(ActiveAbility.prototype);
	FindFood.prototype.constructor = FindFood;

	return FindFood;
});<commit_msg>Fixed stupid programmer error when calculating find food upgrade costs<commit_after>define([
	'jquery',
	'knockout',
	'classes/ActiveAbility',
	'Utils'
], function($, ko, ActiveAbility, Utils){

	function FindFood(data){

		var self = this;

		ActiveAbility.call(this, data);

		this.init = function(data){

			self.skillStages = [
				"poor",
				"good",
				"great",
				"exceptional"
			];

		}

		this.doOnLevelUp = function(){
			//Advance to next skill stage (if possible)
			var idxOf = self.skillStages.indexOf(self.skillLevel());
			if(idxOf < (self.skillStages.length - 1)){
				idxOf++;
				self.skillLevel( self.skillStages[idxOf] );
			}else{
				self.canTrainNextLevel(0);
				self.didLevelUp = 0;
			}
		}

		this.getTrainCost = function(){
			var baseCost = (self.skillStages.indexOf(self.skillLevel()) + 1) * 300;
			//return baseCost + ((self.skillProgress() + 1) * 200);
			return baseCost;
		}

		this.init(data);
	}

	FindFood.prototype = Object.create(ActiveAbility.prototype);
	FindFood.prototype.constructor = FindFood;

	return FindFood;
});<|endoftext|>


<commit_before>function screens_lock(parent_task, callback) {
    if (typeof (parent_task) === "function") {
        callback = parent_task;
        parent_task = null;
    }
    var task = { state: true, lock: 0, parent: parent_task};
    var next = task;
    while (next) {
        next.lock++;
        next = next.parent;
    }
    if (callback) {
        callback(task);
    }
}

function screens_unlock(task, callback) {
    if (callback) {
        task.callback = callback;
    }
    var next = task;
    while (next) {
        next.lock--;
        if (next.lock <= 0 && next.state) {
            next.lock = 0;
            if (next.callback) {
                next.callback(next);
            }
            next.state = false;
        }
        next = next.parent;
    }
}

function screens_forEach(task, array, callback, abortCallback) {
    var index = 0;
    screens_lock(task, (task) => {
        function iterate() {
            var exit = false;
            while (index < array.length && !exit) {
                screens_lock(task, (task) => {
                    var lock = task.lock;
                    var result = callback(task, array[index], index, array);
                    if(result) {
                        screens_unlock(task);
                        if(abortCallback) {
                            abortCallback(task, array[index], index, array);
                        }
                        exit = true;
                        return;
                    }
                    index++;
                    if (lock !== task.lock) {
                        screens_unlock(task, () => {
                            setTimeout(iterate, 0);
                        });
                        exit = true;
                        return;
                    }
                    screens_unlock(task);
                });
            }
            if(!exit) {
                screens_unlock(task);
            }
        }
        setTimeout(() => {
            iterate();
        }, 0);
    });
}

async function screens_async(task, promise) {
    var result;
    screens_lock(task, async (task) => {
        try {
            result = await promise;
            screens_unlock(task);
        }
        catch(err) {
            screens_unlock(task);
            throw err;
        }
    });
    return result;
}

function screens_platform() {
    var platform = "browser";
    if (typeof module !== 'undefined' && this && this.module !== module) {
        platform = global.platform || "server";
    } else if (typeof importScripts !== 'undefined') {
        platform = "client";
    }
    return platform;
}

function screens_setup(package_name, component_name, child_name, node) {
    var children = [];
    var id = package_name + "." + component_name;
    var component_id = id;
    if (child_name) {
        node = node[child_name];
        id += "." + child_name;
    } else {
        node = screens(id);
        for (var key in node) {
            children.push(key);
        }
    }
    /* Create component proxy */
    var component_obj = new Proxy(() => {
        return {};
    }, {
            get: function (object, property) {
                if (Reflect.has(object, property)) {
                    return Reflect.get(object, property);
                } else if (property in screens) {
                    return screens[property];
                } else {
                    var get = Reflect.get(object, "get");
                    if (get && get.enabled) {
                        return get(object, property);
                    }
                }
                return undefined;
            },
            apply: function (object, thisArg, argumentsList) {
                var apply = Reflect.get(object, "apply");
                if (apply) {
                    return apply.apply(thisArg, argumentsList);
                }
            }
        });
    component_obj.id = id;
    component_obj.__package = package_name;
    component_obj.__component = component_name;
    if (child_name) {
        screens[package_name][component_name][child_name] = component_obj;
    } else {
        screens[package_name][component_name] = component_obj;
    }
    if (typeof node !== "function") {
        throw "Component " + id + " cannot be loaded stack: " + new Error().stack;
    }
    var platform = node(component_obj, child_name);
    if (platform && screens.platform !== platform) {
        component_obj.apply = function (object, thisArg, argumentsList) {
            return function () {
                var args = Array.prototype.slice.call(argumentsList);
                args.unshift(id);
                me.core.message["send_" + platform].apply(null, args);
            };
        };
        component_obj.get = function (object, property) {
            return function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(id + "." + property);
                me.core.message["send_" + platform].apply(null, args);
            };
        };
    }
    component_obj.require = platform;
    var init = component_obj.init;
    if (component_obj.get) {
        component_obj.get.enabled = true;
    }
    console.log(screens.platform + ": Loaded " + component_obj.id);
    screens.components.push(component_obj.id);
    /* Load child components */
    var initializers = children.map(function (child) {
        return screens_setup(package_name, component_name, child, node);
    });
    initializers.unshift(init);
    initializers = initializers.reduce((a, b) => a.concat(b), []);
    return initializers;
}

function screens_push(package_name, component_name, callback) {
    var file_name = package_name + "_" + component_name;
    var code_name = package_name + "." + component_name;
    console.log(screens.platform + ": Loading " + code_name);
    if (package_name in screens) {
        if (screens[package_name][component_name]) {
            if (callback) {
                callback();
                return null;
            }
        }
    }
    else {
        screens[package_name] = {};
    }
    var item = {
        package_name: package_name,
        component_name: component_name,
        callback: callback
    };
    return item;
}

function screens_init(task, items) {
    screens.forEach(task, items, (task, item) => {
        var initializers = item.initializers;
        if (initializers) {
            console.log("initializing: " + item.package_name + "." + item.component_name);
            do {
                var init = initializers.shift();
                if (init) {
                    try {
                        init(task);
                    }
                    catch (err) {
                        var message = err.message || err;
                        console.error("Failed to initialise component: " + item.package_name + "." + item.component_name + " with error: " + message + " stack: " + err.stack);
                    }
                }
            } while (init);
            console.log("initialized: " + item.package_name + "." + item.component_name + (task.lock > 1 ? " waiting..." : ""));
        }
    });
}

function screens_import(callback, path) {
    if (screens.platform === "server" || screens.platform === "service") {
        require(path);
        callback();
    }
    else if (screens.platform === "client") {
        importScripts(path);
        callback();
    }
    else if (screens.platform === "browser") {
        var scripts = document.getElementsByTagName("script");
        for (var i = scripts.length; i--;) {
            if (scripts[i].src === path) {
                callback();
                return;
            }
        }
        var ref = scripts[0];
        var script = document.createElement("script");
        script.src = path;
        script.async = true;
        script.onload = callback;
        ref.parentNode.insertBefore(script, ref);
    }
}

function screens_load(task, items, callback) {
    screens.lock(task, (task) => {
        if (items && items.length) {
            if (screens.platform === "server" || screens.platform === "service") {
                items.map((item) => {
                    var path = "../code/" + item.package_name + "/" + item.package_name + "_" + item.component_name;
                    screens.lock(task, (task) => {
                        screens_import(() => {
                            item.initializers = screens_setup(item.package_name, item.component_name);
                            screens.unlock(task);
                        }, path);
                    });
                });
            }
            else {
                var first = true;
                var paths = items.map((item) => {
                    var path = item.package_name + "_" + item.component_name;
                    if (first) {
                        path = "/packages/code/" + item.package_name + "/" + path;
                        first = false;
                    }
                    return path + ".js";
                });
                var path = paths.join(",") + "?platform=" + screens.platform;
                screens.lock(task, (task) => {
                    screens_import(() => {
                        var firstItem = items[0];
                        if (firstItem.component_name === "*") {
                            items = Object.keys(screens[firstItem.package_name]).map((component_name) => {
                                return {
                                    package_name: firstItem.package_name,
                                    component_name: component_name
                                };
                            });
                        }
                        items.map((item) => {
                            item.initializers = screens_setup(item.package_name, item.component_name);
                        });
                        screens.unlock(task);
                    }, path);
                });
            }
        }
        screens.unlock(task, () => {
            callback(items);
        });
    });
}

function screens_include(packages, callback) {
    if (typeof packages === "string" && packages) {
        var names = packages.split(".");
        var package_name = names[0];
        var component_name = names[1];
        packages = {};
        packages[package_name] = [component_name];
    }
    var collection = {};
    screens.lock((task) => {
        Object.entries(packages).forEach(([package_name, components]) => {
            screens.lock(task, (task) => {
                var items = [];
                components.forEach((component_name) => {
                    items.push(screens_push(package_name, component_name));
                });
                screens_load(task, items, (items) => {
                    collection[package_name] = items;
                });
                screens.unlock(task);
            });
        });
        screens.unlock(task, () => {
            screens.lock((task) => {
                screens.forEach(task, Object.keys(packages), (task, package_name) => {
                    screens_init(task, collection[package_name]);
                });
                screens.unlock(task, () => {
                    if(callback) {
                        callback();
                    }
                });
            });
        });
    });
}

var screens = new Proxy(() => {
    return {};
}, {
        apply: function (object, thisArg, argumentsList) {
            return argumentsList[0].split('.').reduce((parent, name) => {
                return parent[name];
            }, screens);
        }
    });

Object.assign(screens, {
    components: [],
    id: "package",
    platform: screens_platform(),
    include: screens_include,
    lock: screens_lock,
    unlock: screens_unlock,
    forEach: screens_forEach,
    async: screens_async
});

var platform = screens_platform();
if (platform === "server" || platform === "service") {
    global.screens = screens;
    global.__json__ = {};
}
<commit_msg>bug fix<commit_after>function screens_lock(parent_task, callback) {
    if (typeof (parent_task) === "function") {
        callback = parent_task;
        parent_task = null;
    }
    var task = { state: true, lock: 0, parent: parent_task};
    var next = task;
    while (next) {
        next.lock++;
        next = next.parent;
    }
    if (callback) {
        callback(task);
    }
}

function screens_unlock(task, callback) {
    if (callback) {
        task.callback = callback;
    }
    var next = task;
    while (next) {
        next.lock--;
        if (next.lock <= 0 && next.state) {
            next.lock = 0;
            if (next.callback) {
                next.callback(next);
            }
            next.state = false;
        }
        next = next.parent;
    }
}

function screens_forEach(task, array, callback, abortCallback) {
    var index = 0;
    screens_lock(task, (task) => {
        function iterate() {
            var exit = false;
            while (index < array.length && !exit) {
                screens_lock(task, (task) => {
                    var lock = task.lock;
                    var result = callback(task, array[index], index, array);
                    if(result) {
                        screens_unlock(task);
                        if(abortCallback) {
                            abortCallback(task, array[index], index, array);
                        }
                        exit = true;
                        return;
                    }
                    index++;
                    if (lock !== task.lock) {
                        screens_unlock(task, () => {
                            setTimeout(iterate, 0);
                        });
                        exit = true;
                        return;
                    }
                    screens_unlock(task);
                });
            }
            if(!exit) {
                screens_unlock(task);
            }
        }
        setTimeout(() => {
            iterate();
        }, 0);
    });
}

async function screens_async(task, promise) {
    var result;
    screens_lock(task, async (task) => {
        try {
            result = await promise;
            screens_unlock(task);
        }
        catch(err) {
            screens_unlock(task);
            throw err;
        }
    });
    return result;
}

function screens_platform() {
    var platform = "browser";
    if (typeof module !== 'undefined' && this && this.module !== module) {
        platform = global.platform || "server";
    } else if (typeof importScripts !== 'undefined') {
        platform = "client";
    }
    return platform;
}

function screens_setup(package_name, component_name, child_name, node) {
    var children = [];
    var id = package_name + "." + component_name;
    var component_id = id;
    if (child_name) {
        node = node[child_name];
        id += "." + child_name;
    } else {
        node = screens(id);
        for (var key in node) {
            children.push(key);
        }
    }
    /* Create component proxy */
    var component_obj = new Proxy(() => {
        return {};
    }, {
            get: function (object, property) {
                if (Reflect.has(object, property)) {
                    return Reflect.get(object, property);
                } else if (property in screens) {
                    return screens[property];
                } else {
                    var get = Reflect.get(object, "get");
                    if (get && get.enabled) {
                        return get(object, property);
                    }
                }
                return undefined;
            },
            apply: function (object, thisArg, argumentsList) {
                var apply = Reflect.get(object, "apply");
                if (apply) {
                    return apply.apply(thisArg, argumentsList);
                }
            }
        });
    component_obj.id = id;
    component_obj.__package = package_name;
    component_obj.__component = component_name;
    if (child_name) {
        screens[package_name][component_name][child_name] = component_obj;
    } else {
        screens[package_name][component_name] = component_obj;
    }
    if (typeof node !== "function") {
        throw "Component " + id + " cannot be loaded stack: " + new Error().stack;
    }
    var platform = node(component_obj, child_name);
    if (platform && screens.platform !== platform) {
        component_obj.apply = function (object, thisArg, argumentsList) {
            return function () {
                var args = Array.prototype.slice.call(argumentsList);
                args.unshift(id);
                me.core.message["send_" + platform].apply(null, args);
            };
        };
        component_obj.get = function (object, property) {
            return function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(id + "." + property);
                me.core.message["send_" + platform].apply(null, args);
            };
        };
    }
    component_obj.require = platform;
    var init = component_obj.init;
    if (component_obj.get) {
        component_obj.get.enabled = true;
    }
    console.log(screens.platform + ": Loaded " + component_obj.id);
    screens.components.push(component_obj.id);
    /* Load child components */
    var initializers = children.map(function (child) {
        return screens_setup(package_name, component_name, child, node);
    });
    initializers.unshift(init);
    initializers = initializers.reduce((a, b) => a.concat(b), []);
    return initializers;
}

function screens_push(package_name, component_name, callback) {
    var file_name = package_name + "_" + component_name;
    var code_name = package_name + "." + component_name;
    console.log(screens.platform + ": Loading " + code_name);
    if (package_name in screens) {
        if (screens[package_name][component_name]) {
            if (callback) {
                callback();
                return null;
            }
        }
    }
    else {
        screens[package_name] = {};
    }
    var item = {
        package_name: package_name,
        component_name: component_name,
        callback: callback
    };
    return item;
}

function screens_init(task, items) {
    screens.forEach(task, items, (task, item) => {
        var initializers = item.initializers;
        if (initializers) {
            console.log("initializing: " + item.package_name + "." + item.component_name);
            do {
                var init = initializers.shift();
                if (init) {
                    try {
                        init(task);
                    }
                    catch (err) {
                        var message = err.message || err;
                        console.error("Failed to initialise component: " + item.package_name + "." + item.component_name + " with error: " + message + " stack: " + err.stack);
                    }
                }
            } while (init);
            console.log("initialized: " + item.package_name + "." + item.component_name + (task.lock > 1 ? " waiting..." : ""));
        }
    });
}

function screens_import(callback, path) {
    if (screens.platform === "server" || screens.platform === "service") {
        require(path);
        callback();
    }
    else if (screens.platform === "client") {
        importScripts(path);
        callback();
    }
    else if (screens.platform === "browser") {
        var scripts = document.getElementsByTagName("script");
        for (var i = scripts.length; i--;) {
            if (scripts[i].src === path) {
                callback();
                return;
            }
        }
        var ref = scripts[0];
        var script = document.createElement("script");
        script.src = path;
        script.async = true;
        script.onload = callback;
        ref.parentNode.insertBefore(script, ref);
    }
}

function screens_load(task, items, callback) {
    screens.lock(task, (task) => {
        if (items && items.length) {
            if (screens.platform === "server" || screens.platform === "service") {
                items.map((item) => {
                    if(item.package_name in screens && item.component_name in screens[item.package_name]) {
                        return;
                    }
                    var path = "../code/" + item.package_name + "/" + item.package_name + "_" + item.component_name;
                    screens.lock(task, (task) => {
                        screens_import(() => {
                            item.initializers = screens_setup(item.package_name, item.component_name);
                            screens.unlock(task);
                        }, path);
                    });
                });
            }
            else {
                var first = true;
                var paths = items.map((item) => {
                    if(item.package_name in screens && item.component_name in screens[item.package_name]) {
                        return null;
                    }
                    var path = item.package_name + "_" + item.component_name;
                    if (first) {
                        path = "/packages/code/" + item.package_name + "/" + path;
                        first = false;
                    }
                    return path + ".js";
                });
                paths = paths.filter(Boolean);
                if(paths.length) {
                    var path = paths.join(",") + "?platform=" + screens.platform;
                    screens.lock(task, (task) => {
                        screens_import(() => {
                            var firstItem = items[0];
                            if (firstItem.component_name === "*") {
                                items = Object.keys(screens[firstItem.package_name]).map((component_name) => {
                                    return {
                                        package_name: firstItem.package_name,
                                        component_name: component_name
                                    };
                                });
                            }
                            items.map((item) => {
                                item.initializers = screens_setup(item.package_name, item.component_name);
                            });
                            screens.unlock(task);
                        }, path);
                    });
                }
            }
        }
        screens.unlock(task, () => {
            callback(items);
        });
    });
}

function screens_include(packages, callback) {
    if (typeof packages === "string" && packages) {
        var names = packages.split(".");
        var package_name = names[0];
        var component_name = names[1];
        packages = {};
        packages[package_name] = [component_name];
    }
    var collection = {};
    screens.lock((task) => {
        Object.entries(packages).forEach(([package_name, components]) => {
            screens.lock(task, (task) => {
                var items = [];
                components.forEach((component_name) => {
                    items.push(screens_push(package_name, component_name));
                });
                screens_load(task, items, (items) => {
                    collection[package_name] = items;
                });
                screens.unlock(task);
            });
        });
        screens.unlock(task, () => {
            screens.lock((task) => {
                screens.forEach(task, Object.keys(packages), (task, package_name) => {
                    screens_init(task, collection[package_name]);
                });
                screens.unlock(task, () => {
                    if(callback) {
                        callback();
                    }
                });
            });
        });
    });
}

var screens = new Proxy(() => {
    return {};
}, {
        apply: function (object, thisArg, argumentsList) {
            return argumentsList[0].split('.').reduce((parent, name) => {
                return parent[name];
            }, screens);
        }
    });

Object.assign(screens, {
    components: [],
    id: "package",
    platform: screens_platform(),
    include: screens_include,
    lock: screens_lock,
    unlock: screens_unlock,
    forEach: screens_forEach,
    async: screens_async
});

var platform = screens_platform();
if (platform === "server" || platform === "service") {
    global.screens = screens;
    global.__json__ = {};
}
<|endoftext|>


<commit_before>module.exports = function(Company) {
  /**
   * /Company/search endpoint definition
   */
  Company.search = function(query, callback) {
    Company.find(
      {
        where: {
          name: {
            like: '%' + query + '%'
          }
        },
        limit: 50
      },
      function(err, results) {
        callback(results);
      }
    );
  };

  Company.remoteMethod(
    'search',
    {
      http: { verb: 'get' },
      accepts: { arg: 'query', type: 'string' },
      returns: { args: 'results', type: [ 'object' ] }
    }
  );

  /**
   * Method to recalculate company attributes when a new review has been posted.
   */
  Company.prototype.recalculate = function() {
    var self = this;

    var overallRating = 0;
    var overallRatingCount = 0;
    var cultureRating = 0;
    var cultureRatingCount = 0;
    var difficultyRating = 0;
    var difficultyRatingCount = 0;

    self.reviews(null, function(err, reviews) {
      reviews.forEach(
        function(review) {
          if (review.payTypeId === 2) {
            var yearly = review.pay * 12;
            var weekly = yearly / 52;
            review.pay = weekly / 40;
          }

          if ('overallRating' in review && review.overallRating != 0) {
            overallRating += review.overallRating;
            overallRatingCount++;
          }

          if ('cultureRating' in review && review.cultureRating != 0) {
            cultureRating += review.cultureRating;
            cultureRatingCount++;
          }

          if ('difficultyRating' in review && review.difficultyRating != 0) {
            difficultyRating += review.difficultyRating;
            difficultyRatingCount++;
          }

          if (review.payTypeId != 3) {
            if (self.minPay && review.pay < self.minPay) {
              self.minPay = review.pay;
            }

            if (review.pay > self.maxPay) {
              self.maxPay = review.pay;
            }
          }
        }
      );

      if (overallRatingCount == 0) {
        overallRatingCount = 1;
      }

      if (cultureRatingCount == 0) {
        cultureRatingCount = 1;
      }

      if (difficultyRatingCount == 0) {
        difficultyRatingCount = 1;
      }

      self.overallRating = overallRating / overallRatingCount;
      self.cultureRating = cultureRating / cultureRatingCount;
      self.difficultyRating = difficultyRating / difficultyRatingCount;

      self.save();
    });
  };

  /**
  * Method to update company perks, majors, and locations with a new review.
  */
  Company.prototype.updatePML = function(perks, majors, loc) {
    var self = this;
    if (perks.length > 0) {
      perks.forEach(function(perk) {
        if(perk !== null){
          self.perks.findById(perk, function(err, exist_perk) {
            if(exist_perk === undefined) {
              self.perks.add(perk, function(err, add_perk) {});
            }
          });
        }
      });
    }

    if (majors.length > 0) {
      majors.forEach(function(major) {
        if (major !== null) {
          self.majors.findById(major, function(err, exist_major) {
            if(exist_major === undefined) {
              self.majors.add(major, function(err, add_major) {});
            }
          });
        }
      });
    }

    if (loc !== null && loc !== undefined) {
      self.locations.findById(loc, function(err, exist_loc) {
        if(exist_loc === undefined) {
          self.locations.add(loc, function(err, add_loc) {});
        }
      });
    }
  };

  /**
   * Remote method hook
   */
  Company.afterRemote(
    '*.__create__reviews',
    function(ctx, instance, next) {
      instance.created = new Date();
      Company.findOne(
        {
          where: {
            id: instance.companyId
          },
          include: "reviews"
        },
        function(err, company) {
          if (instance.payTypeId != 3 && company.minPay === null) {
            company.minPay = instance.pay;
          }

          if (!instance.perks) {
            instance.perks = [];
          }

          if (!instance.majors) {
            instance.majors = [];
          }

          company.recalculate();
          company.updatePML(instance.perks, instance.majors, instance.location);
        }
      );

      next();
    }
  );

  /**
   * Set new companies to verified: false.
   */
  Company.beforeRemote(
    'Company.create',
    function (ctx, instance, next) {
      delete ctx.args.data.verified;
      next();
    }
  );

  /**
   * Prevent userId from being exposed as part of an include.
   */
  Company.afterRemote(
    '**',
    function(ctx, instance, next) {
      if (instance.reviews) {
        var reviews = instance.reviews();
        if (reviews) {
          reviews.forEach(
            function(review) {
              if (review.anonymous) {
                review.userId = null;
              }
            }
          );
        }
      }

      next();
    }
  );
};
<commit_msg>Bug fixes for Company.search<commit_after>module.exports = function(Company) {
  /**
   * /Company/search endpoint definition
   */
  Company.search = function(query, callback) {
    Company.find(
      {
        where: {
          or: [
            {
              name: {
                like: '%' + query + '%'
              }
            },
            {
              description: {
                like: '%' + query + '%'
              }
            }
          ]
        },
        limit: 50
      },
      function(err, results) {
        callback(null, results);
      }
    );
  };

  Company.remoteMethod(
    'search',
    {
      http: { verb: 'get' },
      accepts: { arg: 'query', type: 'string' },
      returns: { arg: 'results', type: [ 'object' ] }
    }
  );

  /**
   * Method to recalculate company attributes when a new review has been posted.
   */
  Company.prototype.recalculate = function() {
    var self = this;

    var overallRating = 0;
    var overallRatingCount = 0;
    var cultureRating = 0;
    var cultureRatingCount = 0;
    var difficultyRating = 0;
    var difficultyRatingCount = 0;

    self.reviews(null, function(err, reviews) {
      reviews.forEach(
        function(review) {
          if (review.payTypeId === 2) {
            var yearly = review.pay * 12;
            var weekly = yearly / 52;
            review.pay = weekly / 40;
          }

          if ('overallRating' in review && review.overallRating != 0) {
            overallRating += review.overallRating;
            overallRatingCount++;
          }

          if ('cultureRating' in review && review.cultureRating != 0) {
            cultureRating += review.cultureRating;
            cultureRatingCount++;
          }

          if ('difficultyRating' in review && review.difficultyRating != 0) {
            difficultyRating += review.difficultyRating;
            difficultyRatingCount++;
          }

          if (review.payTypeId != 3) {
            if (self.minPay && review.pay < self.minPay) {
              self.minPay = review.pay;
            }

            if (review.pay > self.maxPay) {
              self.maxPay = review.pay;
            }
          }
        }
      );

      if (overallRatingCount == 0) {
        overallRatingCount = 1;
      }

      if (cultureRatingCount == 0) {
        cultureRatingCount = 1;
      }

      if (difficultyRatingCount == 0) {
        difficultyRatingCount = 1;
      }

      self.overallRating = overallRating / overallRatingCount;
      self.cultureRating = cultureRating / cultureRatingCount;
      self.difficultyRating = difficultyRating / difficultyRatingCount;

      self.save();
    });
  };

  /**
  * Method to update company perks, majors, and locations with a new review.
  */
  Company.prototype.updatePML = function(perks, majors, loc) {
    var self = this;
    if (perks.length > 0) {
      perks.forEach(function(perk) {
        if(perk !== null){
          self.perks.findById(perk, function(err, exist_perk) {
            if(exist_perk === undefined) {
              self.perks.add(perk, function(err, add_perk) {});
            }
          });
        }
      });
    }

    if (majors.length > 0) {
      majors.forEach(function(major) {
        if (major !== null) {
          self.majors.findById(major, function(err, exist_major) {
            if(exist_major === undefined) {
              self.majors.add(major, function(err, add_major) {});
            }
          });
        }
      });
    }

    if (loc !== null && loc !== undefined) {
      self.locations.findById(loc, function(err, exist_loc) {
        if(exist_loc === undefined) {
          self.locations.add(loc, function(err, add_loc) {});
        }
      });
    }
  };

  /**
   * Remote method hook
   */
  Company.afterRemote(
    '*.__create__reviews',
    function(ctx, instance, next) {
      instance.created = new Date();
      Company.findOne(
        {
          where: {
            id: instance.companyId
          },
          include: "reviews"
        },
        function(err, company) {
          if (instance.payTypeId != 3 && company.minPay === null) {
            company.minPay = instance.pay;
          }

          if (!instance.perks) {
            instance.perks = [];
          }

          if (!instance.majors) {
            instance.majors = [];
          }

          company.recalculate();
          company.updatePML(instance.perks, instance.majors, instance.location);
        }
      );

      next();
    }
  );

  /**
   * Set new companies to verified: false.
   */
  Company.beforeRemote(
    'Company.create',
    function (ctx, instance, next) {
      delete ctx.args.data.verified;
      next();
    }
  );

  /**
   * Prevent userId from being exposed as part of an include.
   */
  Company.afterRemote(
    '**',
    function(ctx, instance, next) {
      if (instance.reviews) {
        var reviews = instance.reviews();
        if (reviews) {
          reviews.forEach(
            function(review) {
              if (review.anonymous) {
                review.userId = null;
              }
            }
          );
        }
      }

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


<commit_before>/**
 * Copyright 2020 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Get the average of an array
 *
 * @param {Array<number>} array
 * @return {number} average
 */
const average = (array) => {
  if (!array || array.length == 0) {
    return 0;
  }
  return array.reduce((a, b) => a + b) / array.length;
};

/**
 * Sort an array from low to high
 *
 * @param {Array<number>} array
 * @return {Array<number>} array
 */
const sort = (array) => {
  return array.sort((a, b) => a - b);
};

/**
 * Get the median of a sorted array
 *
 * @param {Array<number>} array
 * @return {number} median
 */
const median = (array) => {
  sort(array);
  const {length} = array;
  const mid = Math.floor(length / 2);
  const isEven = length % 2 == 0;
  if (isEven) {
    return average([array[mid], array[mid - 1]]);
  }
  return array[mid];
};

/**
 * Get the median of the lower half of a sorted array
 *
 * @param {Array<number>} array
 * @return {number} q1
 */
const q1 = (array) => {
  sort(array);
  const mid = Math.ceil(array.length / 2);
  const half = array.slice(0, mid);
  return median(half);
};

/**
 * Get the median of the upper half of a sorted array
 *
 * @param {Array<number>} array
 * @return {number} q3
 */
const q3 = (array) => {
  sort(array);
  const mid = Math.ceil(array.length / 2);
  const half = array.slice(mid);
  return median(half);
};

/**
 * Get the percentage change between two numbers,
 * or returns null if invalid
 *
 * @param {number} a
 * @param {number} b
 * @return {number|null} percentage change or null
 */
function percent(a, b) {
  if (a === 0) {
    return b === 0 ? null : Math.round((a / b) * 100) - 100;
  } else {
    return 100 - Math.round((b / a) * 100);
  }
}

/**
 * Given an array, identify outliers using the Tukey method,
 * and return the average after removing them, which is
 * also known as the interquartile mean.
 *
 * @param {Array<*>} results
 * @param {string} metric
 * @return {number}
 */
function trimmedMean(results, metric) {
  const array = results.map((a) => a[metric]);
  const stats = {
    q1: q1(array),
    q3: q3(array),
  };
  const lowerFence = stats.q1 - 1.5 * (stats.q3 - stats.q1);
  const upperFence = stats.q3 + 1.5 * (stats.q3 - stats.q1);
  const trimmedArray = array.filter((a) => a >= lowerFence && a <= upperFence);

  return Math.round(average(trimmedArray));
}

module.exports = {
  percent,
  trimmedMean,
};
<commit_msg>🏗Performance test bug fix (#27539)<commit_after>/**
 * Copyright 2020 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Get the average of an array
 *
 * @param {Array<number>} array
 * @return {number} average
 */
const average = (array) => {
  if (!array || array.length == 0) {
    return 0;
  }
  return array.reduce((a, b) => a + b) / array.length;
};

/**
 * Sort an array from low to high
 *
 * @param {Array<number>} array
 * @return {Array<number>} array
 */
const sort = (array) => {
  return array.sort((a, b) => a - b);
};

/**
 * Get the median of a sorted array
 *
 * @param {Array<number>} array
 * @return {number} median
 */
const median = (array) => {
  sort(array);
  const {length} = array;
  const mid = Math.floor(length / 2);
  const isEven = length % 2 == 0;
  if (isEven) {
    return average([array[mid], array[mid - 1]]);
  }
  return array[mid];
};

/**
 * Get the median of the lower half of a sorted array
 *
 * @param {Array<number>} array
 * @return {number} q1
 */
const q1 = (array) => {
  sort(array);
  const mid = Math.ceil(array.length / 2);
  const half = array.slice(0, mid);
  return median(half);
};

/**
 * Get the median of the upper half of a sorted array
 *
 * @param {Array<number>} array
 * @return {number} q3
 */
const q3 = (array) => {
  sort(array);
  const mid = Math.floor(array.length / 2);
  const half = array.slice(mid);
  return median(half);
};

/**
 * Get the percentage change between two numbers,
 * or returns null if invalid
 *
 * @param {number} a
 * @param {number} b
 * @return {number|null} percentage change or null
 */
function percent(a, b) {
  if (a === 0) {
    return b === 0 ? null : Math.round((a / b) * 100) - 100;
  } else {
    return 100 - Math.round((b / a) * 100);
  }
}

/**
 * Given an array, identify outliers using the Tukey method,
 * and return the average after removing them, which is
 * also known as the interquartile mean.
 *
 * @param {Array<*>} results
 * @param {string} metric
 * @return {number}
 */
function trimmedMean(results, metric) {
  const array = results.map((a) => a[metric]);
  const stats = {
    q1: q1(array),
    q3: q3(array),
  };
  const lowerFence = stats.q1 - 1.5 * (stats.q3 - stats.q1);
  const upperFence = stats.q3 + 1.5 * (stats.q3 - stats.q1);
  const trimmedArray = array.filter((a) => a >= lowerFence && a <= upperFence);

  return Math.round(average(trimmedArray));
}

module.exports = {
  percent,
  trimmedMean,
};
<|endoftext|>


<commit_before>// libraries: jquery, lunr, lodash
// arguments: $, lunr, _

$(document).ready(function() {
  // parseUri 1.2.2
  // (c) Steven Levithan <stevenlevithan.com>
  // MIT License
  function parseUri (str) {
    var  o   = parseUri.options,
      m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
      uri = {},
      i   = 14;

    while (i--) uri[o.key[i]] = m[i] || "";

    uri[o.q.name] = {};
    uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
      if ($1) uri[o.q.name][$1] = $2;
    });

    return uri;
  };
  parseUri.options = {
    strictMode: false,
    key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
    q:   {
      name:   "queryKey",
      parser: /(?:^|&)([^&=]*)=?([^&]*)/g
    },
    parser: {
      strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
      loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
    }
  };

  $("#search-form").submit(function(e) {
    e.preventDefault()
  })

  // list below is the lunr 2.1.3 list minus the intersect with names(Base)
  // (all, any, get, in, is, which) and (do, else, for, let, where, while, with)
  // ideally we'd just filter the original list but it's not available as a variable
  lunr.stopWordFilter = lunr.generateStopWordFilter([
    'a',
    'able',
    'about',
    'across',
    'after',
    'almost',
    'also',
    'am',
    'among',
    'an',
    'and',
    'are',
    'as',
    'at',
    'be',
    'because',
    'been',
    'but',
    'by',
    'can',
    'cannot',
    'could',
    'dear',
    'did',
    'does',
    'either',
    'ever',
    'every',
    'from',
    'got',
    'had',
    'has',
    'have',
    'he',
    'her',
    'hers',
    'him',
    'his',
    'how',
    'however',
    'i',
    'if',
    'into',
    'it',
    'its',
    'just',
    'least',
    'like',
    'likely',
    'may',
    'me',
    'might',
    'most',
    'must',
    'my',
    'neither',
    'no',
    'nor',
    'not',
    'of',
    'off',
    'often',
    'on',
    'only',
    'or',
    'other',
    'our',
    'own',
    'rather',
    'said',
    'say',
    'says',
    'she',
    'should',
    'since',
    'so',
    'some',
    'than',
    'that',
    'the',
    'their',
    'them',
    'then',
    'there',
    'these',
    'they',
    'this',
    'tis',
    'to',
    'too',
    'twas',
    'us',
    'wants',
    'was',
    'we',
    'were',
    'what',
    'when',
    'who',
    'whom',
    'why',
    'will',
    'would',
    'yet',
    'you',
    'your'
  ])

  // add . as a separator, because otherwise "title": "Documenter.Anchors.add!"
  // would not find anything if searching for "add!", only for the entire qualification
  lunr.tokenizer.separator = /[\s\-\.]+/

  // custom trimmer that doesn't strip @ and !, which are used in julia macro and function names
  lunr.trimmer = function (token) {
    return token.update(function (s) {
      return s.replace(/^[^a-zA-Z0-9@!]+/, '').replace(/[^a-zA-Z0-9@!]+$/, '')
    })
  }

  lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'juliaStopWordFilter')
  lunr.Pipeline.registerFunction(lunr.trimmer, 'juliaTrimmer')

  var index = lunr(function () {
    this.ref('location')
    this.field('title',{boost: 100})
    this.field('text')
    documenterSearchIndex['docs'].forEach(function(e) {
        this.add(e)
    }, this)
  })
  var store = {}

  documenterSearchIndex['docs'].forEach(function(e) {
      store[e.location] = {title: e.title, category: e.category}
  })

  $(function(){
    searchresults = $('#documenter-search-results');
    searchinfo = $('#documenter-search-info');
    searchbox = $('#documenter-search-query');
    function update_search(querystring) {
      tokens = lunr.tokenizer(querystring)
      results = index.query(function (q) {
        tokens.forEach(function (t) {
          q.term(t.toString(), {
            fields: ["title"],
            boost: 100,
            usePipeline: false,
            editDistance: 0,
            wildcard: lunr.Query.wildcard.NONE
          })
          q.term(t.toString(), {
            fields: ["title"],
            boost: 10,
            usePipeline: false,
            editDistance: 2,
            wildcard: lunr.Query.wildcard.NONE
          })
          q.term(t.toString(), {
            fields: ["text"],
            boost: 1,
            usePipeline: true,
            editDistance: 0,
            wildcard: lunr.Query.wildcard.NONE
          })
        })
      })
      searchinfo.text("Number of results: " + results.length)
      searchresults.empty()
      results.forEach(function(result) {
        data = store[result.ref]
        link = $(''+data.title+'')
        link.attr('href', documenterBaseURL+'/'+result.ref)
        cat = $('('+data.category+')')
        li = $('
').append(link).append(" ").append(cat)
        searchresults.append(li)
      })
    }

    function update_search_box() {
      querystring = searchbox.val()
      update_search(querystring)
    }

    searchbox.keyup(_.debounce(update_search_box, 250))
    searchbox.change(update_search_box)

    search_query_uri = parseUri(window.location).queryKey["q"]
    if(search_query_uri !== undefined) {
      search_query = decodeURIComponent(search_query_uri.replace(/\+/g, '%20'))
      searchbox.val(search_query)
    }
    update_search_box();
  })
})
<commit_msg>remove "only" from search stop word filter (#1264)<commit_after>// libraries: jquery, lunr, lodash
// arguments: $, lunr, _

$(document).ready(function() {
  // parseUri 1.2.2
  // (c) Steven Levithan <stevenlevithan.com>
  // MIT License
  function parseUri (str) {
    var  o   = parseUri.options,
      m   = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
      uri = {},
      i   = 14;

    while (i--) uri[o.key[i]] = m[i] || "";

    uri[o.q.name] = {};
    uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
      if ($1) uri[o.q.name][$1] = $2;
    });

    return uri;
  };
  parseUri.options = {
    strictMode: false,
    key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
    q:   {
      name:   "queryKey",
      parser: /(?:^|&)([^&=]*)=?([^&]*)/g
    },
    parser: {
      strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
      loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
    }
  };

  $("#search-form").submit(function(e) {
    e.preventDefault()
  })

  // list below is the lunr 2.1.3 list minus the intersect with names(Base)
  // (all, any, get, in, is, only, which) and (do, else, for, let, where, while, with)
  // ideally we'd just filter the original list but it's not available as a variable
  lunr.stopWordFilter = lunr.generateStopWordFilter([
    'a',
    'able',
    'about',
    'across',
    'after',
    'almost',
    'also',
    'am',
    'among',
    'an',
    'and',
    'are',
    'as',
    'at',
    'be',
    'because',
    'been',
    'but',
    'by',
    'can',
    'cannot',
    'could',
    'dear',
    'did',
    'does',
    'either',
    'ever',
    'every',
    'from',
    'got',
    'had',
    'has',
    'have',
    'he',
    'her',
    'hers',
    'him',
    'his',
    'how',
    'however',
    'i',
    'if',
    'into',
    'it',
    'its',
    'just',
    'least',
    'like',
    'likely',
    'may',
    'me',
    'might',
    'most',
    'must',
    'my',
    'neither',
    'no',
    'nor',
    'not',
    'of',
    'off',
    'often',
    'on',
    'or',
    'other',
    'our',
    'own',
    'rather',
    'said',
    'say',
    'says',
    'she',
    'should',
    'since',
    'so',
    'some',
    'than',
    'that',
    'the',
    'their',
    'them',
    'then',
    'there',
    'these',
    'they',
    'this',
    'tis',
    'to',
    'too',
    'twas',
    'us',
    'wants',
    'was',
    'we',
    'were',
    'what',
    'when',
    'who',
    'whom',
    'why',
    'will',
    'would',
    'yet',
    'you',
    'your'
  ])

  // add . as a separator, because otherwise "title": "Documenter.Anchors.add!"
  // would not find anything if searching for "add!", only for the entire qualification
  lunr.tokenizer.separator = /[\s\-\.]+/

  // custom trimmer that doesn't strip @ and !, which are used in julia macro and function names
  lunr.trimmer = function (token) {
    return token.update(function (s) {
      return s.replace(/^[^a-zA-Z0-9@!]+/, '').replace(/[^a-zA-Z0-9@!]+$/, '')
    })
  }

  lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'juliaStopWordFilter')
  lunr.Pipeline.registerFunction(lunr.trimmer, 'juliaTrimmer')

  var index = lunr(function () {
    this.ref('location')
    this.field('title',{boost: 100})
    this.field('text')
    documenterSearchIndex['docs'].forEach(function(e) {
        this.add(e)
    }, this)
  })
  var store = {}

  documenterSearchIndex['docs'].forEach(function(e) {
      store[e.location] = {title: e.title, category: e.category}
  })

  $(function(){
    searchresults = $('#documenter-search-results');
    searchinfo = $('#documenter-search-info');
    searchbox = $('#documenter-search-query');
    function update_search(querystring) {
      tokens = lunr.tokenizer(querystring)
      results = index.query(function (q) {
        tokens.forEach(function (t) {
          q.term(t.toString(), {
            fields: ["title"],
            boost: 100,
            usePipeline: false,
            editDistance: 0,
            wildcard: lunr.Query.wildcard.NONE
          })
          q.term(t.toString(), {
            fields: ["title"],
            boost: 10,
            usePipeline: false,
            editDistance: 2,
            wildcard: lunr.Query.wildcard.NONE
          })
          q.term(t.toString(), {
            fields: ["text"],
            boost: 1,
            usePipeline: true,
            editDistance: 0,
            wildcard: lunr.Query.wildcard.NONE
          })
        })
      })
      searchinfo.text("Number of results: " + results.length)
      searchresults.empty()
      results.forEach(function(result) {
        data = store[result.ref]
        link = $(''+data.title+'')
        link.attr('href', documenterBaseURL+'/'+result.ref)
        cat = $('('+data.category+')')
        li = $('
').append(link).append(" ").append(cat)
        searchresults.append(li)
      })
    }

    function update_search_box() {
      querystring = searchbox.val()
      update_search(querystring)
    }

    searchbox.keyup(_.debounce(update_search_box, 250))
    searchbox.change(update_search_box)

    search_query_uri = parseUri(window.location).queryKey["q"]
    if(search_query_uri !== undefined) {
      search_query = decodeURIComponent(search_query_uri.replace(/\+/g, '%20'))
      searchbox.val(search_query)
    }
    update_search_box();
  })
})
<|endoftext|>


<commit_before>/** @jsx React.DOM */

define([
  "React",
  "models/App",
  "jsx!components/NewAppModalComponent"
], function(React, App, NewAppModalComponent) {
  return React.createClass({
    saveModel: function() {
      this.props.collection.create(this.state.model, {wait: true});
    },
    showModal: function(event) {
      var model = new App();

      this.setState({
        model: model,
        modal: React.renderComponent(
          <NewAppModalComponent model={model} onCreate={this.saveModel} />,
          document.getElementById("lightbox")
        )
      });
    },
    render: function() {
      return (
        <button type="button" className="btn btn-primary header-btn" onClick={this.showModal}>
          + New App
        </button>
      );
    }
  });
});
<commit_msg>bug fix: New app doesn't immediately display app list<commit_after>/** @jsx React.DOM */

define([
  "React",
  "models/App",
  "jsx!components/NewAppModalComponent"
], function(React, App, NewAppModalComponent) {
  return React.createClass({
    saveModel: function() {
      this.props.collection.create(this.state.model);
    },
    showModal: function(event) {
      var model = new App();

      this.setState({
        model: model,
        modal: React.renderComponent(
          <NewAppModalComponent model={model} onCreate={this.saveModel} />,
          document.getElementById("lightbox")
        )
      });
    },
    render: function() {
      return (
        <button type="button" className="btn btn-primary header-btn" onClick={this.showModal}>
          + New App
        </button>
      );
    }
  });
});
<|endoftext|>