<commit_before>// Copyright 2014-2018, University of Colorado Boulder

/**
 * A node that represents a 2D surface on which energy chunks reside.  The surface contains z-dimension information,
 * and can thus be used to create an effect of layering in order to get a bit of a 3D appearance when used in
 * conjunction with other slices.  The slice itself is generally invisible, but can be shown using when needed for
 * debugging.
 *
 * @author John Blanco
 * @author Andrew Adare
 */
define( function( require ) {
  'use strict';

  // modules
  var EFACQueryParameters = require( 'ENERGY_FORMS_AND_CHANGES/common/EFACQueryParameters' );
  var EnergyChunkNode = require( 'ENERGY_FORMS_AND_CHANGES/common/view/EnergyChunkNode' );
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );

  /**
   * @param {EnergyChunkContainerSlice} slice
   * @param {ModelViewTransform2} modelViewTransform
   * @constructor
   */
  function EnergyChunkContainerSliceNode( slice, modelViewTransform ) {

    var self = this;
    this.modelViewTransform = modelViewTransform;
    Node.call( this );

    // define a function that will add and remove energy chunk nodes as energy come and go in the model
    function addEnergyChunkNode( addedChunk ) {
      var energyChunkNode = new EnergyChunkNode( addedChunk, modelViewTransform );
      self.addChild( energyChunkNode );
      slice.energyChunkList.addItemRemovedListener( function removalListener( removedChunk ) {
        if ( removedChunk === addedChunk ) {
          self.removeChild( energyChunkNode );
          energyChunkNode.dispose();
          slice.energyChunkList.removeItemRemovedListener( removalListener );
        }
      } );
    }

    // add the initial energy chunks
    slice.energyChunkList.forEach( addEnergyChunkNode );

    // listen for the arrival of new energy chunks and create a node for each
    slice.energyChunkList.addItemAddedListener( addEnergyChunkNode );

    if ( EFACQueryParameters.showHelperShapes ) {

      // for debug - add an outline of the slice bounds, note that this does not update if the slice's bounds change
      var outlineNode = new Rectangle( modelViewTransform.modelToViewBounds( slice.bounds ), {
        lineWidth: 1,
        stroke: 'red'
      } );
      this.addChild( outlineNode );

      // move the outlines as the slice moves
      slice.anchorPointProperty.lazyLink( function( newPosition, oldPosition ) {
        outlineNode.translate(
          modelViewTransform.modelToViewDeltaX( newPosition.x - oldPosition.x ),
          modelViewTransform.modelToViewDeltaY( newPosition.y - oldPosition.y )
        );
      } );
    }
  }

  energyFormsAndChanges.register( 'EnergyChunkContainerSliceNode', EnergyChunkContainerSliceNode );

  return inherit( Node, EnergyChunkContainerSliceNode );
} );
<commit_msg>Convert to ES6, see https://github.com/phetsims/energy-forms-and-changes/issues/211<commit_after>// Copyright 2014-2019, University of Colorado Boulder

/**
 * A node that represents a 2D surface on which energy chunks reside. The surface contains z-dimension information,
 * and can thus be used to create an effect of layering in order to get a bit of a 3D appearance when used in
 * conjunction with other slices. The slice is generally invisible, but can be shown using when needed for debugging.
 *
 * @author John Blanco
 * @author Andrew Adare
 */
define( require => {
  'use strict';

  // modules
  const EFACQueryParameters = require( 'ENERGY_FORMS_AND_CHANGES/common/EFACQueryParameters' );
  const EnergyChunkNode = require( 'ENERGY_FORMS_AND_CHANGES/common/view/EnergyChunkNode' );
  const energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  const Node = require( 'SCENERY/nodes/Node' );
  const Rectangle = require( 'SCENERY/nodes/Rectangle' );

  class EnergyChunkContainerSliceNode extends Node {

    /**
     * @param {EnergyChunkContainerSlice} slice
     * @param {ModelViewTransform2} modelViewTransform
     */
    constructor( slice, modelViewTransform ) {
      super();

      this.modelViewTransform = modelViewTransform;

      // define a function that will add and remove energy chunk nodes as energy come and go in the model
      const addEnergyChunkNode = addedChunk => {
        const energyChunkNode = new EnergyChunkNode( addedChunk, modelViewTransform );
        this.addChild( energyChunkNode );
        const removalListener = removedChunk => {
          if ( removedChunk === addedChunk ) {
            this.removeChild( energyChunkNode );
            energyChunkNode.dispose();
            slice.energyChunkList.removeItemRemovedListener( removalListener );
          }
        };
        slice.energyChunkList.addItemRemovedListener( removalListener );
      };

      // add the initial energy chunks
      slice.energyChunkList.forEach( addEnergyChunkNode );

      // listen for the arrival of new energy chunks and create a node for each
      slice.energyChunkList.addItemAddedListener( addEnergyChunkNode );

      if ( EFACQueryParameters.showHelperShapes ) {

        // for debug - add an outline of the slice bounds, note that this does not update if the slice's bounds change
        const outlineNode = new Rectangle( modelViewTransform.modelToViewBounds( slice.bounds ), {
          lineWidth: 1,
          stroke: 'red'
        } );
        this.addChild( outlineNode );

        // move the outlines as the slice moves
        slice.anchorPointProperty.lazyLink( ( newPosition, oldPosition ) => {
          outlineNode.translate(
            modelViewTransform.modelToViewDeltaX( newPosition.x - oldPosition.x ),
            modelViewTransform.modelToViewDeltaY( newPosition.y - oldPosition.y )
          );
        } );
      }
    }
  }

  return energyFormsAndChanges.register( 'EnergyChunkContainerSliceNode', EnergyChunkContainerSliceNode );
} );
<|endoftext|>


<commit_before>console.log("Hello world");<commit_msg>Feat/ext ilias (#3)<commit_after>var messageHolderElements = document.getElementsByClassName("message-text");
var messages = [];
var names = [];

for (i = 0; i < messageHolderElements.length; i++) {
  var line = document.getElementsByClassName("message-text")[i].children[0].innerText;
  var name = line.split("]")[1];
  var res = name.split(":")[0];
  names.push(res);
  messages.push(document.getElementsByClassName("message-text")[i].children[1].innerText);
}<|endoftext|>


<commit_before>'use strict';
const _ = require('underscore');
const ArangoError = require('org/arangodb').ArangoError;
const errors = require('org/arangodb').errors;
const internal = require('internal');
const assert = require('assert');
const Module = require('module');
const path = require('path');
const fs = require('fs');
const parameterTypes = require('org/arangodb/foxx/manager-utils').parameterTypes;
const getReadableName = require('org/arangodb/foxx/manager-utils').getReadableName;

const APP_PATH = internal.appPath ? path.resolve(internal.appPath) : undefined;
const STARTUP_PATH = internal.startupPath ? path.resolve(internal.startupPath) : undefined;
const DEV_APP_PATH = internal.devAppPath ? path.resolve(internal.devAppPath) : undefined;

class AppContext {
  constructor(service) {
    this.basePath = path.resolve(service.root, service.path);
    this.comments = [];
    Object.defineProperty(this, '_service', {
      get() {
        return service;
      }
    });
  }

  foxxFilename(filename) {
    return fs.safeJoin(this._prefix, filename);
  }

  path(name) {
    return path.join(this._prefix, name);
  }

  collectionName(name) {
    let fqn = (
      this.collectionPrefix
      + name.replace(/[^a-z0-9]/ig, '_').replace(/(^_+|_+$)/g, '').substr(0, 64)
    );
    if (!fqn.length) {
      throw new Error(`Cannot derive collection name from "${name}"`);
    }
  }

  collection(name) {
    return internal.db._collection(this.collectionName(name));
  }

  get _prefix() {
    return this.basePath;
  }

  get baseUrl() {
    return `/_db/${encodeURIComponent(internal.db._name())}/${this._service.mount.slice(1)}`;
  }

  get collectionPrefix() {
    return this._service.collectionPrefix;
  }

  get mount() {
    return this._service.mount;
  }

  get name() {
    return this._service.name;
  }

  get version() {
    return this._service.version;
  }

  get manifest() {
    return this._service.manifest;
  }

  get isDevelopment() {
    return this._service.isDevelopment;
  }

  get isProduction() {
    return !this.isDevelopment;
  }

  get options() {
    return this._service.options;
  }

  get configuration() {
    return this._service.configuration;
  }

  get dependencies() {
    return this._service.dependencies;
  }
}

Object.defineProperties(AppContext.prototype, {
  comment: {
    value: function (str) {
      this.comments.push(str);
    }
  },
  clearComments: {
    value: function () {
      this.comments.splice(0, this.comments.length);
    }
  }
});

function createConfiguration(definitions) {
  const config = {};
  Object.keys(definitions).forEach(function (name) {
    const def = definitions[name];
    if (def.default !== undefined) {
      config[name] = def.default;
    }
  });
  return config;
}

function createDependencies(definitions, options) {
  const deps = {};
  Object.keys(definitions).forEach(function (name) {
    Object.defineProperty(deps, name, {
      configurable: true,
      enumerable: true,
      get() {
        const mount = options[name];
        return mount ? require('org/arangodb/foxx').getExports(mount) : null;
      }
    });
  });
  return deps;
}

class FoxxService {
  constructor(data) {
    assert(data, 'no arguments');
    assert(data.mount, 'mount path required');
    assert(data.path, `local path required for app "${data.mount}"`);
    assert(data.manifest, `called without manifest for app "${data.mount}"`);

    // mount paths always start with a slash
    this.mount = data.mount;
    this.path = data.path;
    this.isDevelopment = data.isDevelopment || false;

    this.manifest = data.manifest;
    if (!this.manifest.dependencies) {
      this.manifest.dependencies = {};
    }
    if (!this.manifest.configuration) {
      this.manifest.configuration = {};
    }

    this.options = data.options;
    if (!this.options.configuration) {
      this.options.configuration = {};
    }
    if (!this.options.dependencies) {
      this.options.dependencies = {};
    }

    this.configuration = createConfiguration(this.manifest.configuration);
    this.dependencies = createDependencies(this.manifest.dependencies, this.options.dependencies);
    let warnings = this.applyConfiguration(this.options.configuration);
    if (warnings.length) {
      console.warnLines(
        `Stored configuration for app "${data.mount}" has errors:\n${warnings.join('\n  ')}`
      );
    }
    // don't need to apply deps from options -- they work automatically

    if (this.manifest.thumbnail) {
      let thumb = path.resolve(this.root, this.path, this.manifest.thumbnail);
      try {
        this.thumbnail = fs.read64(thumb);
      } catch (e) {
        console.warnLines(
          `Cannot read thumbnail "${thumb}" for app "${data.mount}": ${e.stack}`
        );
      }
    } else {
      this.thumbnail = null;
    }

    let lib = this.manifest.lib || '.';
    this.main = new Module(`foxx:${data.mount}`);
    this.main.filename = path.resolve(this.root, this.path, lib, '.foxx');
    this.main.context.applicationContext = new AppContext(this);
    this.main.context.console = require('org/arangodb/foxx/console')(this.mount);
  }

  applyConfiguration(config) {
    const definitions = this.manifest.configuration;
    const warnings = [];

    _.each(config, function (rawValue, name) {
      const def = definitions[name];
      if (!def) {
        warnings.push(`Unexpected option "${name}"`);
        return;
      }

      if (def.required === false && (rawValue === undefined || rawValue === null)) {
        delete this.options.configuration[name];
        this.configuration[name] = def.default;
        return;
      }

      const validate = parameterTypes[def.type];
      let parsedValue = rawValue;
      let warning;

      if (validate.isJoi) {
        let result = validate.required().validate(rawValue);
        if (result.error) {
          warning = result.error.message.replace(/^"value"/, `"${name}"`);
        } else {
          parsedValue = result.value;
        }
      } else {
        try {
          parsedValue = validate(rawValue);
        } catch (e) {
          warning = `"${name}": ${e.message}`;
        }
      }

      if (warning) {
        warnings.push(warning);
      } else {
        this.options.configuration[name] = rawValue;
        this.configuration[name] = parsedValue;
      }
    }, this);

    return warnings;
  }

  applyDependencies(deps) {
    var definitions = this.manifest.dependencies;
    var warnings = [];

    _.each(deps, function (mount, name) {
      const dfn = definitions[name];
      if (!dfn) {
        warnings.push(`Unexpected dependency "${name}"`);
      } else {
        this.options.dependencies[name] = mount;
      }
    }, this);

    return warnings;
  }

  _PRINT(context) {
    context.output += `[FoxxService "${this.name}" (${this.version}) on ${this.mount}]`;
  }

  toJSON() {
    const result = {
      name: this.name,
      version: this.version,
      manifest: this.manifest,
      path: this.path,
      options: this.options,
      mount: this.mount,
      isSystem: this.isSystem,
      isDevelopment: this.isDevelopment
    };
    if (this.error) {
      result.error = this.error;
    }
    if (this.manifest.author) {
      result.author = this.manifest.author;
    }
    if (this.manifest.description) {
      result.description = this.manifest.description;
    }
    if (this.thumbnail) {
      result.thumbnail = this.thumbnail;
    }
    return result;
  }

  simpleJSON() {
    return {
      name: this.name,
      version: this.version,
      mount: this.mount
    };
  }

  development(isDevelopment) {
    this.isDevelopment = isDevelopment;
  }

  getConfiguration(simple) {
    var config = {};
    var definitions = this.manifest.configuration;
    var options = this.options.configuration;
    _.each(definitions, function (dfn, name) {
      var value = options[name] === undefined ? dfn.default : options[name];
      config[name] = simple ? value : _.extend(_.clone(dfn), {
        title: getReadableName(name),
        current: value
      });
    });
    return config;
  }

  getDependencies(simple) {
    var deps = {};
    var definitions = this.manifest.dependencies;
    var options = this.options.dependencies;
    _.each(definitions, function (dfn, name) {
      deps[name] = simple ? options[name] : {
        definition: dfn,
        title: getReadableName(name),
        current: options[name]
      };
    });
    return deps;
  }

  needsConfiguration() {
    var config = this.getConfiguration();
    var deps = this.getDependencies();
    return _.any(config, function (cfg) {
      return cfg.current === undefined && cfg.required !== false;
    }) || _.any(deps, function (dep) {
      return dep.current === undefined && dep.definition.required !== false;
    });
  }

  run(filename, options) {
    options = options || {};
    filename = path.resolve(this.main.context.__dirname, filename);

    var module = new Module(filename, this.main);
    module.context.applicationContext = _.extend(
      new AppContext(this.main.context.applicationContext._service),
      this.main.context.applicationContext,
      options.appContext
    );

    if (options.preprocess) {
      module.preprocess = options.preprocess;
    }

    if (options.context) {
      Object.keys(options.context).forEach(function (key) {
        module.context[key] = options.context[key];
      });
    }

    try {
      module.load(filename);
      return module.exports;
    } catch(e) {
      if (e instanceof ArangoError) {
        throw e;
      }
      var err = new ArangoError({
        errorNum: errors.ERROR_FAILED_TO_EXECUTE_SCRIPT.code,
        errorMessage: errors.ERROR_FAILED_TO_EXECUTE_SCRIPT.message
        + '\nFile: ' + filename
      });
      err.stack = e.stack;
      err.cause = e;
      throw err;
    }
  }

  get exports() {
    return this.main.exports;
  }

  get name() {
    return this.manifest.name;
  }

  get version() {
    return this.manifest.version;
  }

  get isSystem() {
    return this.mount.charAt(1) === '_';
  }

  get root() {
    if (this.isSystem) {
      return FoxxService._systemAppPath;
    }
    return FoxxService._appPath;
  }

  get collectionPrefix() {
    if (this.isSystem) {
      return '';
    }
    return this.mount.substr(1).replace(/[-.:/]/g, '_') + '_';
  }

  static get _startupPath() {
    return STARTUP_PATH;
  }

  static get _appPath() {
    return APP_PATH ? (
      path.join(APP_PATH, '_db', internal.db._name())
    ) : undefined;
  }

  static get _systemAppPath() {
    return APP_PATH ? (
      path.join(STARTUP_PATH, 'apps', 'system')
    ) : undefined;
  }

  static get _oldAppPath() {
    return APP_PATH ? (
      path.join(APP_PATH, 'databases', internal.db._name())
    ) : undefined;
  }

  static get _devAppPath() {
    return DEV_APP_PATH ? (
      path.join(DEV_APP_PATH, 'databases', internal.db._name())
    ) : undefined;
  }
}

module.exports = FoxxService;
<commit_msg>Fixed applicationContext.collectionName<commit_after>'use strict';
const _ = require('underscore');
const ArangoError = require('org/arangodb').ArangoError;
const errors = require('org/arangodb').errors;
const internal = require('internal');
const assert = require('assert');
const Module = require('module');
const path = require('path');
const fs = require('fs');
const parameterTypes = require('org/arangodb/foxx/manager-utils').parameterTypes;
const getReadableName = require('org/arangodb/foxx/manager-utils').getReadableName;

const APP_PATH = internal.appPath ? path.resolve(internal.appPath) : undefined;
const STARTUP_PATH = internal.startupPath ? path.resolve(internal.startupPath) : undefined;
const DEV_APP_PATH = internal.devAppPath ? path.resolve(internal.devAppPath) : undefined;

class AppContext {
  constructor(service) {
    this.basePath = path.resolve(service.root, service.path);
    this.comments = [];
    Object.defineProperty(this, '_service', {
      get() {
        return service;
      }
    });
  }

  foxxFilename(filename) {
    return fs.safeJoin(this._prefix, filename);
  }

  path(name) {
    return path.join(this._prefix, name);
  }

  collectionName(name) {
    let fqn = (
      this.collectionPrefix
      + name.replace(/[^a-z0-9]/ig, '_').replace(/(^_+|_+$)/g, '').substr(0, 64)
    );
    if (!fqn.length) {
      throw new Error(`Cannot derive collection name from "${name}"`);
    }
    return fqn;
  }

  collection(name) {
    return internal.db._collection(this.collectionName(name));
  }

  get _prefix() {
    return this.basePath;
  }

  get baseUrl() {
    return `/_db/${encodeURIComponent(internal.db._name())}/${this._service.mount.slice(1)}`;
  }

  get collectionPrefix() {
    return this._service.collectionPrefix;
  }

  get mount() {
    return this._service.mount;
  }

  get name() {
    return this._service.name;
  }

  get version() {
    return this._service.version;
  }

  get manifest() {
    return this._service.manifest;
  }

  get isDevelopment() {
    return this._service.isDevelopment;
  }

  get isProduction() {
    return !this.isDevelopment;
  }

  get options() {
    return this._service.options;
  }

  get configuration() {
    return this._service.configuration;
  }

  get dependencies() {
    return this._service.dependencies;
  }
}

Object.defineProperties(AppContext.prototype, {
  comment: {
    value: function (str) {
      this.comments.push(str);
    }
  },
  clearComments: {
    value: function () {
      this.comments.splice(0, this.comments.length);
    }
  }
});

function createConfiguration(definitions) {
  const config = {};
  Object.keys(definitions).forEach(function (name) {
    const def = definitions[name];
    if (def.default !== undefined) {
      config[name] = def.default;
    }
  });
  return config;
}

function createDependencies(definitions, options) {
  const deps = {};
  Object.keys(definitions).forEach(function (name) {
    Object.defineProperty(deps, name, {
      configurable: true,
      enumerable: true,
      get() {
        const mount = options[name];
        return mount ? require('org/arangodb/foxx').getExports(mount) : null;
      }
    });
  });
  return deps;
}

class FoxxService {
  constructor(data) {
    assert(data, 'no arguments');
    assert(data.mount, 'mount path required');
    assert(data.path, `local path required for app "${data.mount}"`);
    assert(data.manifest, `called without manifest for app "${data.mount}"`);

    // mount paths always start with a slash
    this.mount = data.mount;
    this.path = data.path;
    this.isDevelopment = data.isDevelopment || false;

    this.manifest = data.manifest;
    if (!this.manifest.dependencies) {
      this.manifest.dependencies = {};
    }
    if (!this.manifest.configuration) {
      this.manifest.configuration = {};
    }

    this.options = data.options;
    if (!this.options.configuration) {
      this.options.configuration = {};
    }
    if (!this.options.dependencies) {
      this.options.dependencies = {};
    }

    this.configuration = createConfiguration(this.manifest.configuration);
    this.dependencies = createDependencies(this.manifest.dependencies, this.options.dependencies);
    let warnings = this.applyConfiguration(this.options.configuration);
    if (warnings.length) {
      console.warnLines(
        `Stored configuration for app "${data.mount}" has errors:\n${warnings.join('\n  ')}`
      );
    }
    // don't need to apply deps from options -- they work automatically

    if (this.manifest.thumbnail) {
      let thumb = path.resolve(this.root, this.path, this.manifest.thumbnail);
      try {
        this.thumbnail = fs.read64(thumb);
      } catch (e) {
        console.warnLines(
          `Cannot read thumbnail "${thumb}" for app "${data.mount}": ${e.stack}`
        );
      }
    } else {
      this.thumbnail = null;
    }

    let lib = this.manifest.lib || '.';
    this.main = new Module(`foxx:${data.mount}`);
    this.main.filename = path.resolve(this.root, this.path, lib, '.foxx');
    this.main.context.applicationContext = new AppContext(this);
    this.main.context.console = require('org/arangodb/foxx/console')(this.mount);
  }

  applyConfiguration(config) {
    const definitions = this.manifest.configuration;
    const warnings = [];

    _.each(config, function (rawValue, name) {
      const def = definitions[name];
      if (!def) {
        warnings.push(`Unexpected option "${name}"`);
        return;
      }

      if (def.required === false && (rawValue === undefined || rawValue === null)) {
        delete this.options.configuration[name];
        this.configuration[name] = def.default;
        return;
      }

      const validate = parameterTypes[def.type];
      let parsedValue = rawValue;
      let warning;

      if (validate.isJoi) {
        let result = validate.required().validate(rawValue);
        if (result.error) {
          warning = result.error.message.replace(/^"value"/, `"${name}"`);
        } else {
          parsedValue = result.value;
        }
      } else {
        try {
          parsedValue = validate(rawValue);
        } catch (e) {
          warning = `"${name}": ${e.message}`;
        }
      }

      if (warning) {
        warnings.push(warning);
      } else {
        this.options.configuration[name] = rawValue;
        this.configuration[name] = parsedValue;
      }
    }, this);

    return warnings;
  }

  applyDependencies(deps) {
    var definitions = this.manifest.dependencies;
    var warnings = [];

    _.each(deps, function (mount, name) {
      const dfn = definitions[name];
      if (!dfn) {
        warnings.push(`Unexpected dependency "${name}"`);
      } else {
        this.options.dependencies[name] = mount;
      }
    }, this);

    return warnings;
  }

  _PRINT(context) {
    context.output += `[FoxxService "${this.name}" (${this.version}) on ${this.mount}]`;
  }

  toJSON() {
    const result = {
      name: this.name,
      version: this.version,
      manifest: this.manifest,
      path: this.path,
      options: this.options,
      mount: this.mount,
      isSystem: this.isSystem,
      isDevelopment: this.isDevelopment
    };
    if (this.error) {
      result.error = this.error;
    }
    if (this.manifest.author) {
      result.author = this.manifest.author;
    }
    if (this.manifest.description) {
      result.description = this.manifest.description;
    }
    if (this.thumbnail) {
      result.thumbnail = this.thumbnail;
    }
    return result;
  }

  simpleJSON() {
    return {
      name: this.name,
      version: this.version,
      mount: this.mount
    };
  }

  development(isDevelopment) {
    this.isDevelopment = isDevelopment;
  }

  getConfiguration(simple) {
    var config = {};
    var definitions = this.manifest.configuration;
    var options = this.options.configuration;
    _.each(definitions, function (dfn, name) {
      var value = options[name] === undefined ? dfn.default : options[name];
      config[name] = simple ? value : _.extend(_.clone(dfn), {
        title: getReadableName(name),
        current: value
      });
    });
    return config;
  }

  getDependencies(simple) {
    var deps = {};
    var definitions = this.manifest.dependencies;
    var options = this.options.dependencies;
    _.each(definitions, function (dfn, name) {
      deps[name] = simple ? options[name] : {
        definition: dfn,
        title: getReadableName(name),
        current: options[name]
      };
    });
    return deps;
  }

  needsConfiguration() {
    var config = this.getConfiguration();
    var deps = this.getDependencies();
    return _.any(config, function (cfg) {
      return cfg.current === undefined && cfg.required !== false;
    }) || _.any(deps, function (dep) {
      return dep.current === undefined && dep.definition.required !== false;
    });
  }

  run(filename, options) {
    options = options || {};
    filename = path.resolve(this.main.context.__dirname, filename);

    var module = new Module(filename, this.main);
    module.context.applicationContext = _.extend(
      new AppContext(this.main.context.applicationContext._service),
      this.main.context.applicationContext,
      options.appContext
    );

    if (options.preprocess) {
      module.preprocess = options.preprocess;
    }

    if (options.context) {
      Object.keys(options.context).forEach(function (key) {
        module.context[key] = options.context[key];
      });
    }

    try {
      module.load(filename);
      return module.exports;
    } catch(e) {
      if (e instanceof ArangoError) {
        throw e;
      }
      var err = new ArangoError({
        errorNum: errors.ERROR_FAILED_TO_EXECUTE_SCRIPT.code,
        errorMessage: errors.ERROR_FAILED_TO_EXECUTE_SCRIPT.message
        + '\nFile: ' + filename
      });
      err.stack = e.stack;
      err.cause = e;
      throw err;
    }
  }

  get exports() {
    return this.main.exports;
  }

  get name() {
    return this.manifest.name;
  }

  get version() {
    return this.manifest.version;
  }

  get isSystem() {
    return this.mount.charAt(1) === '_';
  }

  get root() {
    if (this.isSystem) {
      return FoxxService._systemAppPath;
    }
    return FoxxService._appPath;
  }

  get collectionPrefix() {
    if (this.isSystem) {
      return '';
    }
    return this.mount.substr(1).replace(/[-.:/]/g, '_') + '_';
  }

  static get _startupPath() {
    return STARTUP_PATH;
  }

  static get _appPath() {
    return APP_PATH ? (
      path.join(APP_PATH, '_db', internal.db._name())
    ) : undefined;
  }

  static get _systemAppPath() {
    return APP_PATH ? (
      path.join(STARTUP_PATH, 'apps', 'system')
    ) : undefined;
  }

  static get _oldAppPath() {
    return APP_PATH ? (
      path.join(APP_PATH, 'databases', internal.db._name())
    ) : undefined;
  }

  static get _devAppPath() {
    return DEV_APP_PATH ? (
      path.join(DEV_APP_PATH, 'databases', internal.db._name())
    ) : undefined;
  }
}

module.exports = FoxxService;
<|endoftext|>


<commit_before>'use strict';

var THREE = require("three");

var parsedPoints = [],
    parsedTags = [],
    parsedData,
    total = 0;

var inputData = module.exports = {
    pointSize: 2,
    pointSizeMultiplier: 1,
    cloudSize2D: 1.5,

    loadData: function(inputData) {
        parsedPoints = [];
        parsedTags = inputData.tags;
        parsedData = inputData;
        total = inputData.totalPoints;

        console.log(inputData);
        console.log(parsedTags);
        console.log(total);

        var i;
        for (i = 0; i < total; i++) {
            var dataPoint = new THREE.Vector3(parsedData.points[i][0], parsedData.points[i][1], parsedData.points[i][2]);
            dataPoint.filename = parsedData.points[i][3];
            dataPoint.meta = {};
            parsedPoints.push(dataPoint);
        }

        for (var tag in parsedTags) {
            if (parsedTags.hasOwnProperty(tag)) {
                for (var value in parsedTags[tag]) {
                    if (parsedTags[tag].hasOwnProperty(value)) {
                        for (var point in parsedTags[tag][value].points) {
                            parsedPoints[parsedTags[tag][value].points[point]].meta[tag] = value;
                        }
                    }
                }
            }
        }

        console.log(parsedPoints);
        // console.log('Loading inputData...');
        //
        // var i;
        // for (i = 0; i < inputData.length; i++) {
        //     var inputDataPoint = new THREE.Vector3(inputData[i][1], inputData[i][2], inputData[i][3]);
        //     if (process.env.inputDATA_SRC) {
        //         inputDataPoint.url = process.env.inputDATA_SRC + inputData[i][4];
        //     } else {
        //         inputDataPoint.url = "audio/" + inputData[i][4];
        //     }

        //     inputDataPoint.meta = this.parseTags(inputData[i][5], i);
        //     inputDataPoint.color = new THREE.Color(inputData[i][6]);
        //     parsedPoints.push(inputDataPoint);
        //     if (i % 1000 === 0) {
        //         console.log('Points loaded: ' + i);
        //     }
        // }
        // this.sortTagValues();
    },

    /**
     * Parses tag JSON into tag objects.
     * @param {array} tags - array of {key: foo, value: bar} objects
     * @param {number} pointIndex - index of current inputDataPoint
     * @returns {array} Array that includes tag information for a point {key: foor, values: []}
     */
    parseTags: function(tags, pointIndex) {
        var meta = [],
            tagKey,
            tagVal,
            tagIndex,
            values,
            valueIndex;

        for (var i = 0; i < tags.length; i++) {
            tagKey = tags[i].key;
            tagVal = tags[i].val;

            // Parse tag for a point object
            tagIndex = this.addTwoPropertyObject(meta, 'key', tagKey, 'values', []);
            meta[tagIndex].values.push(tagVal);

            // Parse tag for parsedTag array
            tagIndex = this.addTwoPropertyObject(parsedTags, 'key', tagKey, 'values', []);
            values = parsedTags[tagIndex].values;
            valueIndex = this.addTwoPropertyObject(values, 'value', tagVal, 'points', []);
            values[valueIndex].points.push(pointIndex);
        }
        // Returns array of tag objects for use as a property of a point object
        return meta;
    },

    /**
     * Function that maps the color to the correct tag value. Wanted tag is usually the one
     * that was used to compute the color og the point.
     * @param {any} inputDataPoint - inputData point object
     * @param {any} tagKey - key value of tag that was used to determine color of the point
     */

    parseTagColors: function(inputDataPoint, tagKey) {
        var metainputData = inputDataPoint.meta,
            value,
            tag;

        for (var i = 0; i < metainputData.length; i++) {
            tag = metainputData[i];
            if (tag.key === tagKey) {
                value = tag.values[0];
            }
        }

        if (!tagColors.has(value)) {
            tagColors.set(value, inputDataPoint.color);
        }
    },

    /**
     * Computes color for every inputDatapoint and sets the color of each point.
     * Used when no color information is provided in inputData JSON
     * @param {JSON} inputData - inputData in JSON format
     */
    computeColorInformation: function(inputData) {
        var maxEuc = 0,
            minEuc = Number.MAX_VALUE,
            maxZ = 0,
            hueOffset = 20,
            hues = [];
        var i;
        for (i = 0; i < inputData.length; i++) {
            var x = Math.pow(parsedPoints[i].x, 2);
            var y = Math.pow(parsedPoints[i].y, 2);
            var z = Math.pow(parsedPoints[i].z, 2);
            var hue = Math.sqrt(x + y + z);
            hues.push(hue);

            maxZ = Math.max(maxZ, z);
            maxEuc = Math.max(maxEuc, hue);
            minEuc = Math.min(minEuc, hue);
        }
        for (i = 0; i < inputData.length; i++) {
            var color = new THREE.Color();
            var lightness = parsedPoints[i].z / (2 * maxZ);
            color.setHSL((hues[i] - minEuc + hueOffset) / (maxEuc - minEuc), 1, lightness + 0.5);
            parsedPoints[i].color = color;

        }
    },

    /**
     * Adds an object with two properties to an array, if it doesnt exist, and returns index of that object.
     * @param {array} array - array to wich object will be added
     * @param {string} firstKey - name (key) of the first property
     * @param {any} firstValue - value of the first property
     * @param {string} secondKey - name (key) of the second property
     * @param {any} secondValue - value of the second property
     * @returns {number} Index of the object
     */
    addTwoPropertyObject: function(array, firstKey, firstValue, secondKey, secondValue) {
        var valueIndex = this.getObjectIndex(array, firstKey, firstValue);
        if (valueIndex === -1) {
            var metaTag = {};
            metaTag[firstKey] = firstValue;
            metaTag[secondKey] = secondValue;
            array.push(metaTag);
            return array.length - 1;
        }
        return valueIndex;
    },

    /**
     * Returns index of an object with desired value of a property
     * @param {array} array - array that will be searched
     * @param {string} propertyName - name of the attribute that will be compared
     * @param {any} value - wanted value of the attribute
     * @returns {number} Index of an object
     */
    getObjectIndex: function(array, propertyName, value) {
        for (var i = 0; i < array.length; i++) {
            var object = array[i];
            if (array[i][propertyName] === value) {
                return i;
            }
        }
        return -1;
    },

    /**
     * Sorts tag values alphabetically
     */
    sortTagValues: function() {
        for (var i = 0; i < parsedTags.length; i++) {
            var values = parsedTags[i].values;
            values.sort(function(a, b) {
                if (a.value < b.value) {
                    return -1;
                }
                if (a.value > b.value) {
                    return 1;
                }
                return 0;
            });
        }
    },

    getTag: function(key) {
        var index = this.getObjectIndex(parsedTags, 'key', key);
        if (index === -1) {
            return undefined;
        }
        return parsedTags[index];
    },

    getTotalPoints: function() {
        return total;
    },

    getUrl: function(index) {
        return parsedPoints[index].filename;
    },

    getPoint: function(index) {
        return parsedPoints[index];
    },

    getColor: function(index) {
        var pointTagValue = parsedPoints[index].meta[parsedData.colorBy];
        return new THREE.Color(parsedTags[parsedData.colorBy][pointTagValue].color);
    },

    getTags: function() {
        return parsedTags;
    },

    getTagColor: function(tag) {
        return parsedTags[parsedData.colorBy][tag];
    }
};<commit_msg>removed unneeded code<commit_after>'use strict';

var THREE = require("three");

var parsedPoints = [],
    parsedTags = [],
    parsedData,
    total = 0;

var inputData = module.exports = {
    pointSize: 2,
    pointSizeMultiplier: 1,
    cloudSize2D: 1.5,

    loadData: function(inputData) {
        parsedPoints = [];
        parsedTags = inputData.tags;
        parsedData = inputData;
        total = inputData.totalPoints;

        console.log(inputData);
        console.log(parsedTags);
        console.log(total);

        var i;
        for (i = 0; i < total; i++) {
            var dataPoint = new THREE.Vector3(parsedData.points[i][0], parsedData.points[i][1], parsedData.points[i][2]);
            dataPoint.filename = parsedData.points[i][3];
            dataPoint.meta = {};
            parsedPoints.push(dataPoint);
        }

        for (var tag in parsedTags) {
            if (parsedTags.hasOwnProperty(tag)) {
                for (var value in parsedTags[tag]) {
                    if (parsedTags[tag].hasOwnProperty(value)) {
                        for (var point in parsedTags[tag][value].points) {
                            parsedPoints[parsedTags[tag][value].points[point]].meta[tag] = value;
                        }
                    }
                }
            }
        }
        console.log(parsedPoints);
    },

    getTotalPoints: function() {
        return total;
    },

    getUrl: function(index) {
        return parsedPoints[index].filename;
    },

    getPoint: function(index) {
        return parsedPoints[index];
    },

    getColor: function(index) {
        var pointTagValue = parsedPoints[index].meta[parsedData.colorBy];
        return new THREE.Color(parsedTags[parsedData.colorBy][pointTagValue].color);
    },

    getTags: function() {
        return parsedTags;
    },

    getTagColor: function(tag) {
        return parsedTags[parsedData.colorBy][tag];
    }
};<|endoftext|>


<commit_before>import React from 'react';

import Icon from './Icon';

export default ({type, url, name, description}) => {

  const caption = encodeURIComponent("Support the " + name + " collective");
  const body = encodeURIComponent("Support the " + name + " collective: " + url);
  const url_encoded = encodeURIComponent(url);

  const link = {
    twitter: `https://twitter.com/intent/tweet?status=${caption}%20${url_encoded}`,
    facebook: `https://www.facebook.com/sharer.php?url=${url_encoded}`,
    mail: `mailto:?subject=${caption}&body=${body}`,
  };

  const w = 650;
  const h = 450;
  let left = (screen.width/2)-(w/2);
  let top = (screen.height/2)-(h/2);

  return (
    <span
      onClick={() => window.open(link[type], 'ShareWindow', 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='+w+', height='+h+', top='+top+', left='+left)}
      className='ShareIcon'>
      <Icon type={type} />
    </span>
  )
};<commit_msg>fix test<commit_after>import React from 'react';

import Icon from './Icon';

export default ({type, url, name, description}) => {

  const caption = encodeURIComponent("Support the " + name + " collective");
  const body = encodeURIComponent("Support the " + name + " collective: " + url + '\n\n' + description);
  const url_encoded = encodeURIComponent(url);

  const link = {
    twitter: `https://twitter.com/intent/tweet?status=${caption}%20${url_encoded}`,
    facebook: `https://www.facebook.com/sharer.php?url=${url_encoded}`,
    mail: `mailto:?subject=${caption}&body=${body}`,
  };

  const w = 650;
  const h = 450;
  let left = (screen.width/2)-(w/2);
  let top = (screen.height/2)-(h/2);

  return (
    <span
      onClick={() => window.open(link[type], 'ShareWindow', 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='+w+', height='+h+', top='+top+', left='+left)}
      className='ShareIcon'>
      <Icon type={type} />
    </span>
  )
};
<|endoftext|>


<commit_before>(function() {

    "use strict";

    /**
     * @module FreeDraw
     * @submodule Utilities
     * @author Adam Timberlake
     * @link https://github.com/Wildhoney/Leaflet.FreeDraw
     */
    L.FreeDraw.Utilities = {

        /**
         * Responsible for converting the multiple polygon points into a MySQL object for
         * geo-spatial queries.
         *
         * @method getMySQLMultiPolygon
         * @param latLngGroups {Array}
         * @return {String}
         */
        getMySQLMultiPolygon: function getMySQLMultiPolygon(latLngGroups) {

            var groups = [];

            latLngGroups.forEach(function forEach(latLngs) {

                var group = [];

                latLngs.forEach(function forEach(latLng) {
                    group.push(latLng.lat + ' ' + latLng.lng);
                });

                groups.push('((' + group.join(',') + '))');

            });

            return 'MULTIPOLYGON(' + groups.join(',') + ')';

        },

        /**
         * Responsible to generating disparate MySQL polygons from the lat/long boundaries.
         *
         * @method getMySQLPolygons
         * @param latLngGroups {L.LatLng[]}
         * @returns {Array}
         */
        getMySQLPolygons: function getMySQLPolygons(latLngGroups) {

            var groups = [];

            latLngGroups.forEach(function forEach(latLngs) {

                var group = [];

                latLngs.forEach(function forEach(latLng) {
                    group.push(latLng.lat + ' ' + latLng.lng);
                });

                groups.push('POLYGON((' + group.join(',') + '))');

            });

            return groups;

        }

    };

})();<commit_msg>swap latitude and longitude for WKT-format http://en.wikipedia.org/wiki/Well-known_text<commit_after>(function() {

    "use strict";

    /**
     * @module FreeDraw
     * @submodule Utilities
     * @author Adam Timberlake
     * @link https://github.com/Wildhoney/Leaflet.FreeDraw
     */
    L.FreeDraw.Utilities = {

        /**
         * Responsible for converting the multiple polygon points into a MySQL object for
         * geo-spatial queries.
         *
         * @method getMySQLMultiPolygon
         * @param latLngGroups {Array}
         * @return {String}
         */
        getMySQLMultiPolygon: function getMySQLMultiPolygon(latLngGroups) {

            var groups = [];

            latLngGroups.forEach(function forEach(latLngs) {

                var group = [];

                latLngs.forEach(function forEach(latLng) {
                    group.push(latLng.lng + ' ' + latLng.lat);
                });

                groups.push('((' + group.join(',') + '))');

            });

            return 'MULTIPOLYGON(' + groups.join(',') + ')';

        },

        /**
         * Responsible to generating disparate MySQL polygons from the lat/long boundaries.
         *
         * @method getMySQLPolygons
         * @param latLngGroups {L.LatLng[]}
         * @returns {Array}
         */
        getMySQLPolygons: function getMySQLPolygons(latLngGroups) {

            var groups = [];

            latLngGroups.forEach(function forEach(latLngs) {

                var group = [];

                latLngs.forEach(function forEach(latLng) {
                    group.push(latLng.lng + ' ' + latLng.lat);
                });

                groups.push('POLYGON((' + group.join(',') + '))');

            });

            return groups;

        }

    };

})();<|endoftext|>


<commit_before>// @flow
import React, { useState } from 'react';
import { View } from 'react-native';
import Heading from './Heading';
import Block from './Block';
import TextInput from './TextInput';
import Row from './Row';
import { SignInButton, SignUpButton } from './buttons';
import { defineMessages } from 'react-intl';
import type { Href } from '../../browser/sitemap';
import useIntl from '../../hooks/useIntl';
import { useAuthMutation } from '../../mutations/AuthMutation';
import Router from 'next/router';
import { setCookie } from '../../browser/cookie';

const messages = defineMessages({
  emailPlaceholder: {
    defaultMessage: 'email',
    id: 'auth.emailPlaceholder',
  },
  passwordPlaceholder: {
    defaultMessage: 'password',
    id: 'auth.passwordPlaceholder',
  },
});

type AuthProps = {|
  redirectUrl?: Href,
|};

export default function Auth(props: AuthProps) {
  const intl = useIntl();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [commit, pending, errors] = useAuthMutation();

  function auth(isSignUp = false) {
    commit({ email, password, isSignUp }, auth => {
      const { token } = auth;
      if (token == null) return;
      setCookie({ token });
      // TODO: Typed useRouter.
      if (Router.query.redirectUrl) {
        Router.replace(Router.query.redirectUrl);
      } else if (props.redirectUrl) {
        // $FlowFixMe Wrong libdef.
        Router.replace(props.redirectUrl);
      } else {
        // $FlowFixMe Wrong libdef.
        Router.replace({
          pathname: Router.pathname,
          query: Router.query,
        });
      }
    });
  }

  return (
    <View>
      <Heading size={1}>Auth</Heading>
      <Block>
        <TextInput
          autoComplete="email"
          disabled={pending}
          error={errors && errors.email}
          focusOnError={errors}
          keyboardType="email-address"
          name="email"
          onChangeText={setEmail}
          placeholder={intl.formatMessage(messages.emailPlaceholder)}
          value={email}
          onSubmitEditing={() => auth()}
        />
        <TextInput
          disabled={pending}
          error={errors && errors.password}
          focusOnError={errors}
          name="password"
          onChangeText={setPassword}
          placeholder={intl.formatMessage(messages.passwordPlaceholder)}
          secureTextEntry
          value={password}
          onSubmitEditing={() => auth()}
        />
        <Row>
          <SignInButton
            disabled={pending}
            onPress={() => auth()}
            color="primary"
          />
          <SignUpButton
            disabled={pending}
            onPress={() => auth(true)}
            color="primary"
          />
        </Row>
      </Block>
    </View>
  );
}
<commit_msg>Be explicit<commit_after>// @flow
import React, { useState } from 'react';
import { View } from 'react-native';
import Heading from './Heading';
import Block from './Block';
import TextInput from './TextInput';
import Row from './Row';
import { SignInButton, SignUpButton } from './buttons';
import { defineMessages } from 'react-intl';
import type { Href } from '../../browser/sitemap';
import useIntl from '../../hooks/useIntl';
import { useAuthMutation } from '../../mutations/AuthMutation';
import Router from 'next/router';
import { setCookie } from '../../browser/cookie';

const messages = defineMessages({
  emailPlaceholder: {
    defaultMessage: 'email',
    id: 'auth.emailPlaceholder',
  },
  passwordPlaceholder: {
    defaultMessage: 'password',
    id: 'auth.passwordPlaceholder',
  },
});

type AuthProps = {|
  redirectUrl?: Href,
|};

export default function Auth(props: AuthProps) {
  const intl = useIntl();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [commit, pending, errors] = useAuthMutation();

  function onSuccess(auth) {
    const { token } = auth;
    if (token == null) return;
    setCookie({ token });
    // TODO: Typed useRouter.
    if (Router.query.redirectUrl) {
      Router.replace(Router.query.redirectUrl);
    } else if (props.redirectUrl) {
      // $FlowFixMe Wrong libdef.
      Router.replace(props.redirectUrl);
    } else {
      // $FlowFixMe Wrong libdef.
      Router.replace({
        pathname: Router.pathname,
        query: Router.query,
      });
    }
  }

  function auth(isSignUp = false) {
    commit({ email, password, isSignUp }, onSuccess);
  }

  return (
    <View>
      <Heading size={1}>Auth</Heading>
      <Block>
        <TextInput
          autoComplete="email"
          disabled={pending}
          error={errors && errors.email}
          focusOnError={errors}
          keyboardType="email-address"
          name="email"
          onChangeText={setEmail}
          placeholder={intl.formatMessage(messages.emailPlaceholder)}
          value={email}
          onSubmitEditing={() => auth()}
        />
        <TextInput
          disabled={pending}
          error={errors && errors.password}
          focusOnError={errors}
          name="password"
          onChangeText={setPassword}
          placeholder={intl.formatMessage(messages.passwordPlaceholder)}
          secureTextEntry
          value={password}
          onSubmitEditing={() => auth()}
        />
        <Row>
          <SignInButton
            disabled={pending}
            onPress={() => auth()}
            color="primary"
          />
          <SignUpButton
            disabled={pending}
            onPress={() => auth(true)}
            color="primary"
          />
        </Row>
      </Block>
    </View>
  );
}
<|endoftext|>


<commit_before>var React = require('react');
var {
  ScrollView,
} = require('react-native');

var CarouselPager = React.createClass({

  scrollToPage(page, animated) {
    if (typeof animated === 'undefined') {
      animated = true;
    }

    if (this.props.isHorizontal) {
      this.refs.scrollView.scrollTo({x: page * this.props.width, y: 0, animated: animated});
    } else {
      this.refs.scrollView.scrollTo({y: page * this.props.height, x: 0, animated: animated});
    }
  },

  _onMomentumScrollEnd(e) {
    if (this.props.isHorizontal) {
      var activePage = e.nativeEvent.contentOffset.x / this.props.width;
    } else {
      var activePage = e.nativeEvent.contentOffset.y / this.props.height;
    }

    this.props.onEnd(activePage);
  },

  render() {
    return <ScrollView ref="scrollView"
      contentContainerStyle={this.props.contentContainerStyle}
      automaticallyAdjustContentInsets={false}
      horizontal={this.props.isHorizontal}
      pagingEnabled={true}
      showsHorizontalScrollIndicator={false}
      showsVerticalScrollIndicator={false}
      bounces={false}
      onScrollBeginDrag={this.props.onBegin}
      onMomentumScrollEnd={this._onMomentumScrollEnd}
      scrollsToTop={false}
    >
      {this.props.children}
    </ScrollView>;
  },
});

module.exports = CarouselPager;
<commit_msg>fix carousel<commit_after>var React = require('react');
var {
  ScrollView,
} = require('react-native');

var CarouselPager = React.createClass({

  scrollToPage(page, animated) {
    if (typeof animated === 'undefined') {
      animated = true;
    }

    if (this.props.isHorizontal) {
      this.refs.scrollView.scrollTo({x: page * this.props.width, y: 0, animated: animated});
    } else {
      this.refs.scrollView.scrollTo({y: page * this.props.height, x: 0, animated: animated});
    }
  },

  _onMomentumScrollEnd(e) {
    if (this.props.isHorizontal) {
      var activePage = e.nativeEvent.contentOffset.x / this.props.width;
    } else {
      var activePage = e.nativeEvent.contentOffset.y / this.props.height;
    }

    this.props.onEnd(activePage);
  },

  render() {
    return <ScrollView ref="scrollView"
      contentContainerStyle={this.props.contentContainerStyle}
      automaticallyAdjustContentInsets={false}
      horizontal={this.props.isHorizontal}
      pagingEnabled={true}
      showsHorizontalScrollIndicator={false}
      showsVerticalScrollIndicator={false}
      bounces={false}
      onScrollBeginDrag={this.props.onBegin}
      onMomentumScrollEnd={this._onMomentumScrollEnd}
      scrollsToTop={false}
      style={{ height: this.props.height, width: this.props.width }}
    >
      {this.props.children}
    </ScrollView>;
  },
});

module.exports = CarouselPager;
<|endoftext|>


<commit_before>
function main(items, active, generator, direction = 'y') {
    var page = this;
    if (!isNode(page)) {
        var page = div();
    }
    function popMenu(item, event) {
        clearTimeout(page.releaseTimer);
        if (page.active) {
            clearTimeout(page.active.releaseTimer);
            remove(page.active);
        }
        if (!item.children || !item.children.length) return;
        var menu = modules.menuList.call(clone, item.children, active, generator);
        var release = function () {
            clear();
            menu.releaseTimer = setTimeout(function () {
                remove(menu);
            }, 0);
            if (page.releaseTimer) {
                page.releaseTimer = setTimeout(e => remove(page), 0);
            }
        };
        var clear = function () {
            clearTimeout(menu.releaseTimer);
            clearTimeout(page.releaseTimer);
        };
        page.active = menu;
        popup(menu, event.target);
        var offleave0 = on("mouseleave")(event.target, release);
        var offenter0 = on("mouseenter")(event.target, clear);
        var offleave1 = on("mouseleave")(menu, release);
        var offenter1 = on("mouseenter")(menu, clear);
        once("remove")(menu, function () {
            offleave0();
            offenter0();
            offleave1();
            offenter1();
        });
    }

    var clone = page.cloneNode();
    clone.className = '';
    clone.removeAttribute('mode');
    clone.innerHTML = page.innerHTML;
    if (!generator || !page.children.length) {
        page.innerHTML = menuList;
        render(page, {
            btn: button,
            menus: items,
            open: active,
            popMenu,
        });
    } else {
        list(page, function (index) {
            var elem = generator(index);
            if (!elem) return;
            on("mouseenter")(elem, function (event) {
                popMenu(this.src[index], event);
            });
            on("click")(elem, function (event) {
                active(this.src[index], event);
            });
            return elem;
        }, direction);
    }
    return page;
}<commit_msg>解决第二次弹出时没有渲染的问题<commit_after>
function main(items, active, generator, direction = 'y') {
    var page = this;
    if (!isNode(page)) {
        var page = div();
    }
    function popMenu(item, event) {
        clearTimeout(page.releaseTimer);
        if (page.active) {
            clearTimeout(page.active.releaseTimer);
            remove(page.active);
        }
        if (!item.children || !item.children.length) return;
        var clone = template.cloneNode();
        clone.innerHTML = template.innerHTML;
        var menu = modules.menuList.call(clone, item.children, active, generator);
        var release = function () {
            clear();
            menu.releaseTimer = setTimeout(function () {
                remove(menu);
            }, 0);
            if (page.releaseTimer) {
                page.releaseTimer = setTimeout(e => remove(page), 0);
            }
        };
        var clear = function () {
            clearTimeout(menu.releaseTimer);
            clearTimeout(page.releaseTimer);
        };
        page.active = menu;
        popup(menu, event.target);
        var offleave0 = on("mouseleave")(event.target, release);
        var offenter0 = on("mouseenter")(event.target, clear);
        var offleave1 = on("mouseleave")(menu, release);
        var offenter1 = on("mouseenter")(menu, clear);
        once("remove")(menu, function () {
            offleave0();
            offenter0();
            offleave1();
            offenter1();
        });
    }

    var template = page.cloneNode();
    template.className = '';
    template.removeAttribute('mode');
    template.innerHTML = page.innerHTML;
    if (!generator || !page.children.length) {
        page.innerHTML = menuList;
        render(page, {
            btn: button,
            menus: items,
            open: active,
            popMenu,
        });
    } else {
        list(page, function (index) {
            var elem = generator(index);
            if (!elem) return;
            on("mouseenter")(elem, function (event) {
                popMenu(this.src[index], event);
            });
            on("click")(elem, function (event) {
                active(this.src[index], event);
            });
            return elem;
        }, direction);
    }
    return page;
}<|endoftext|>


<commit_before>import buffer from '../buffer';

function eventsource({ url, bufferTime = 0 }, { feed, reset, onFinish }) {
  let es;
  let buf;

  function initBuffer() {
    if (buf !== undefined) buf.stop();
    buf = buffer(feed, bufferTime);
  }

  return {
    start: () => {
      es = new EventSource(url);

      es.addEventListener('open', () => {
        console.debug('eventsource: opened');
        initBuffer();
      });

      es.addEventListener('message', event => {
        const e = JSON.parse(event.data);

        if (e.cols !== undefined || e.width !== undefined) {
          initBuffer();
          reset(e.cols ?? e.width, e.rows ?? e.height);
        } else {
          buf.pushEvent(e);
        }
      });

      es.addEventListener('done', () => {
        console.debug('eventsource: closed');
        es.close();
        onFinish();
      });
    },

    stop: () => {
      if (buf !== undefined) buf.stop();
      if (es !== undefined) es.close();
    }
  }
}

export { eventsource };
<commit_msg>Use loading indicator with EventSource (SSE) driver<commit_after>import buffer from '../buffer';

function eventsource({ url, bufferTime = 0 }, { feed, reset, setWaiting, onFinish }) {
  let es;
  let buf;

  function initBuffer() {
    if (buf !== undefined) buf.stop();
    buf = buffer(feed, bufferTime);
  }

  return {
    start: () => {
      es = new EventSource(url);

      es.addEventListener('open', () => {
        console.debug('eventsource: opened');
        setWaiting(false);
        initBuffer();
      });

      es.addEventListener('error', e => {
        console.debug('eventsource: errored');
        console.debug(e);
        setWaiting(true);
      });

      es.addEventListener('message', event => {
        const e = JSON.parse(event.data);

        if (e.cols !== undefined || e.width !== undefined) {
          initBuffer();
          reset(e.cols ?? e.width, e.rows ?? e.height);
        } else {
          buf.pushEvent(e);
        }
      });

      es.addEventListener('done', () => {
        console.debug('eventsource: closed');
        es.close();
        onFinish();
      });
    },

    stop: () => {
      if (buf !== undefined) buf.stop();
      if (es !== undefined) es.close();
    }
  }
}

export { eventsource };
<|endoftext|>


<commit_before>const { URL } = require('url')
const fs = require('fs')
const path = require('path')
const LanguageDetect = require('languagedetect')
const Wappalyzer = require('./wappalyzer')

const {
  setTechnologies,
  setCategories,
  analyze,
  analyzeManyToMany,
  resolve
} = Wappalyzer

const { AWS_LAMBDA_FUNCTION_NAME, CHROMIUM_BIN } = process.env

let puppeteer
let chromiumArgs = [
  '--no-sandbox',
  '--headless',
  '--disable-gpu',
  '--ignore-certificate-errors'
]
let chromiumBin = CHROMIUM_BIN

if (AWS_LAMBDA_FUNCTION_NAME) {
  const chromium = require('chrome-aws-lambda')

  ;({ puppeteer } = chromium)

  chromiumArgs = chromiumArgs.concat(chromium.args)
  chromiumBin = chromium.executablePath
} else {
  puppeteer = require('puppeteer')
}

const languageDetect = new LanguageDetect()

languageDetect.setLanguageType('iso2')

const extensions = /^([^.]+$|\.(asp|aspx|cgi|htm|html|jsp|php)$)/

const errorTypes = {
  RESPONSE_NOT_OK: 'Response was not ok',
  NO_RESPONSE: 'No response from server',
  NO_HTML_DOCUMENT: 'No HTML document'
}

const { apps: technologies, categories } = JSON.parse(
  fs.readFileSync(path.resolve(`${__dirname}/apps.json`))
)

setTechnologies(technologies)
setCategories(categories)

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

function getJs() {
  const dereference = (obj, level = 0) => {
    try {
      // eslint-disable-next-line no-undef
      if (level > 5 || (level && obj === window)) {
        return '[Removed]'
      }

      if (Array.isArray(obj)) {
        obj = obj.map((item) => dereference(item, level + 1))
      }

      if (
        typeof obj === 'function' ||
        (typeof obj === 'object' && obj !== null)
      ) {
        const newObj = {}

        Object.keys(obj).forEach((key) => {
          newObj[key] = dereference(obj[key], level + 1)
        })

        return newObj
      }

      return obj
    } catch (error) {
      return undefined
    }
  }

  // eslint-disable-next-line no-undef
  return dereference(window)
}

function analyzeJs(js) {
  return Array.prototype.concat.apply(
    [],
    js.map(({ name, chain, value }) =>
      analyzeManyToMany(
        Wappalyzer.technologies.find(({ name: _name }) => name === _name),
        'js',
        { [chain]: [value] }
      )
    )
  )
}

class Driver {
  constructor(options = {}) {
    this.options = {
      batchSize: 5,
      debug: false,
      delay: 500,
      htmlMaxCols: 2000,
      htmlMaxRows: 3000,
      maxDepth: 3,
      maxUrls: 10,
      maxWait: 5000,
      recursive: false,
      ...options
    }

    this.options.debug = Boolean(+this.options.debug)
    this.options.recursive = Boolean(+this.options.recursive)
    this.options.delay = this.options.recursive
      ? parseInt(this.options.delay, 10)
      : 0
    this.options.maxDepth = parseInt(this.options.maxDepth, 10)
    this.options.maxUrls = parseInt(this.options.maxUrls, 10)
    this.options.maxWait = parseInt(this.options.maxWait, 10)
    this.options.htmlMaxCols = parseInt(this.options.htmlMaxCols, 10)
    this.options.htmlMaxRows = parseInt(this.options.htmlMaxRows, 10)

    this.destroyed = false
  }

  async init() {
    this.log('Launching browser...')

    try {
      this.browser = await puppeteer.launch({
        args: chromiumArgs,
        executablePath: await chromiumBin
      })

      this.browser.on('disconnected', async () => {
        this.log('Browser disconnected')

        if (!this.destroyed) {
          await this.init()
        }
      })
    } catch (error) {
      throw new Error(error.toString())
    }
  }

  async destroy() {
    this.destroyed = true

    if (this.browser) {
      try {
        await sleep(1)

        await this.browser.close()

        this.log('Browser closed')
      } catch (error) {
        throw new Error(error.toString())
      }
    }
  }

  open(url) {
    return new Site(url, this)
  }

  log(message, source = 'driver') {
    if (this.options.debug) {
      // eslint-disable-next-line no-console
      console.log(`wappalyzer | log | ${source} |`, message)
    }
  }
}

class Site {
  constructor(url, driver) {
    ;({ options: this.options, browser: this.browser } = driver)

    this.driver = driver

    try {
      this.originalUrl = new URL(url)
    } catch (error) {
      throw new Error(error.message || error.toString())
    }

    this.analyzedUrls = {}
    this.detections = []
    this.language = ''

    this.listeners = {}

    this.pages = []
  }

  log(message, source = 'driver', type = 'log') {
    if (this.options.debug) {
      // eslint-disable-next-line no-console
      console[type](`wappalyzer | ${type} | ${source} |`, message)
    }

    this.emit(type, { message, source })
  }

  error(error, source = 'driver') {
    this.log(error, source, 'error')
  }

  on(event, callback) {
    if (!this.listeners[event]) {
      this.listeners[event] = []
    }

    this.listeners[event].push(callback)
  }

  emit(event, params) {
    if (this.listeners[event]) {
      this.listeners[event].forEach((listener) => listener(params))
    }
  }

  async goto(url) {
    // Return when the URL is a duplicate or maxUrls has been reached
    if (
      this.analyzedUrls[url.href] ||
      Object.keys(this.analyzedUrls).length >= this.options.maxUrls
    ) {
      return
    }

    this.log(`Navigate to ${url}`, 'page')

    this.analyzedUrls[url.href] = {
      status: 0
    }

    if (!this.browser) {
      throw new Error('Browser closed')
    }

    const page = await this.browser.newPage()

    this.pages.push(page)

    page.setDefaultTimeout(this.options.maxWait)

    await page.setRequestInterception(true)

    page.on('error', (error) => this.error(error))

    let responseReceived = false

    page.on('request', (request) => {
      try {
        if (
          (responseReceived && request.isNavigationRequest()) ||
          request.frame() !== page.mainFrame() ||
          !['document', 'script'].includes(request.resourceType())
        ) {
          request.abort('blockedbyclient')
        } else {
          request.continue()
        }
      } catch (error) {
        this.error(error)
      }
    })

    page.on('response', (response) => {
      try {
        if (response.url() === url.href) {
          this.analyzedUrls[url.href] = {
            status: response.status()
          }

          const rawHeaders = response.headers()
          const headers = {}

          Object.keys(rawHeaders).forEach((key) => {
            headers[key] = [
              ...(headers[key] || []),
              ...(Array.isArray(rawHeaders[key])
                ? rawHeaders[key]
                : [rawHeaders[key]])
            ]
          })

          this.contentType = headers['content-type'] || null

          if (response.status() >= 300 && response.status() < 400) {
            if (headers.location) {
              url = new URL(headers.location.slice(-1), url)
            }
          } else {
            responseReceived = true

            this.onDetect(analyze({ headers }))
          }
        }
      } catch (error) {
        this.error(error)
      }
    })

    await page.setUserAgent(
      this.options.userAgent ||
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
    )

    try {
      await Promise.race([
        page.goto(url.href, { waitUntil: 'domcontentloaded' }),
        new Promise((resolve, reject) =>
          setTimeout(() => reject(new Error('Timeout')), this.options.maxWait)
        )
      ])
    } catch (error) {
      this.error(error)
    }

    await sleep(1000)

    // Links
    const links = await (
      await page.evaluateHandle(() =>
        Array.from(document.getElementsByTagName('a')).map(
          ({ hash, hostname, href, pathname, protocol, rel }) => ({
            hash,
            hostname,
            href,
            pathname,
            protocol,
            rel
          })
        )
      )
    ).jsonValue()

    // Script tags
    const scripts = await (
      await page.evaluateHandle(() =>
        Array.from(document.getElementsByTagName('script'))
          .map(({ src }) => src)
          .filter((src) => src)
      )
    ).jsonValue()

    // Meta tags
    const meta = await (
      await page.evaluateHandle(() =>
        Array.from(document.querySelectorAll('meta')).reduce((metas, meta) => {
          const key = meta.getAttribute('name') || meta.getAttribute('property')

          if (key) {
            metas[key.toLowerCase()] = [meta.getAttribute('content')]
          }

          return metas
        }, {})
      )
    ).jsonValue()

    // JavaScript
    const win = await page.evaluate(getJs)

    const js = Wappalyzer.technologies
      .filter(({ js }) => Object.keys(js).length)
      .map(({ name, js }) => ({ name, chains: Object.keys(js) }))
      .reduce((technologies, { name, chains }) => {
        chains.forEach((chain) => {
          const value = chain
            .split('.')
            .reduce(
              (value, method) =>
                value && value.hasOwnProperty(method)
                  ? value[method]
                  : undefined,
              win
            )

          if (typeof value !== 'undefined') {
            technologies.push({
              name,
              chain,
              value:
                typeof value === 'string' || typeof value === 'number'
                  ? value
                  : !!value
            })
          }
        })

        return technologies
      }, [])

    // Cookies
    const cookies = (await page.cookies()).reduce(
      (cookies, { name, value }) => ({
        ...cookies,
        [name]: [value]
      }),
      {}
    )

    // HTML
    let html = await page.content()

    if (this.options.htmlMaxCols && this.options.htmlMaxRows) {
      const batches = []
      const rows = html.length / this.options.htmlMaxCols

      for (let i = 0; i < rows; i += 1) {
        if (
          i < this.options.htmlMaxRows / 2 ||
          i > rows - this.options.htmlMaxRows / 2
        ) {
          batches.push(
            html.slice(
              i * this.options.htmlMaxCols,
              (i + 1) * this.options.htmlMaxCols
            )
          )
        }
      }

      html = batches.join('\n')
    }

    // Validate response
    if (!this.analyzedUrls[url.href].status) {
      await page.close()

      this.log('Page closed')

      throw new Error('NO_RESPONSE')
    }

    if (!this.language) {
      this.language = await (
        await page.evaluateHandle(
          () =>
            document.documentElement.getAttribute('lang') ||
            document.documentElement.getAttribute('xml:lang')
        )
      ).jsonValue()
    }

    if (!this.language) {
      try {
        const [attrs] = languageDetect.detect(
          html.replace(/<\/?[^>]+(>|$)/gs, ' '),
          1
        )

        if (attrs) {
          ;[this.language] = attrs
        }
      } catch (error) {
        this.error(error)
      }
    }

    this.onDetect(url, analyzeJs(js))

    this.onDetect(
      url,
      analyze({
        cookies,
        html,
        scripts,
        meta
      })
    )

    const reducedLinks = Array.prototype.reduce.call(
      links,
      (results, link) => {
        if (
          results &&
          Object.prototype.hasOwnProperty.call(
            Object.getPrototypeOf(results),
            'push'
          ) &&
          link.protocol &&
          link.protocol.match(/https?:/) &&
          link.rel !== 'nofollow' &&
          link.hostname === url.hostname &&
          extensions.test(link.pathname)
        ) {
          results.push(new URL(link.href.split('#')[0]))
        }

        return results
      },
      []
    )

    await page.close()

    this.log('Page closed')

    this.emit('goto', url)

    return reducedLinks
  }

  async analyze(url = this.originalUrl, index = 1, depth = 1) {
    try {
      await sleep(this.options.delay * index)

      const links = await this.goto(url)

      if (links && this.options.recursive && depth < this.options.maxDepth) {
        await this.batch(links.slice(0, this.options.maxUrls), depth + 1)
      }
    } catch (error) {
      const type =
        error.message && errorTypes[error.message]
          ? error.message
          : 'UNKNOWN_ERROR'
      const message =
        error.message && errorTypes[error.message]
          ? errorTypes[error.message]
          : 'Unknown error'

      this.analyzedUrls[url.href] = {
        status: 0,
        error: {
          type,
          message
        }
      }

      this.error(error)
    }

    return {
      urls: this.analyzedUrls,
      applications: resolve(this.detections).map(
        ({ name, confidence, version, icon, website, categories }) => ({
          name,
          confidence,
          version,
          icon,
          website,
          categories: categories.reduce(
            (categories, { id, name }) => ({
              ...categories,
              [id]: name
            }),
            {}
          )
        })
      ),
      meta: {
        language: this.language
      }
    }
  }

  async batch(links, depth, batch = 0) {
    if (links.length === 0) {
      return
    }

    const batched = links.splice(0, this.options.batchSize)

    await Promise.all(
      batched.map((link, index) => this.analyze(link, index, depth))
    )

    await this.batch(links, depth, batch + 1)
  }

  onDetect(url, detections = [], language) {
    this.detections = this.detections.concat(detections)

    this.detections.filter(
      ({ technology: { name }, pattern: { regex } }, index) =>
        this.detections.findIndex(
          ({ technology: { name: _name }, pattern: { regex: _regex } }) =>
            name === _name && (!regex || regex.toString() === _regex.toString())
        ) === index
    )
  }

  async destroy() {
    await Promise.all(
      this.pages.map(async (page) => {
        if (page) {
          try {
            await page.close()

            this.log('Page closed')
          } catch (error) {
            // Continue
          }
        }
      })
    )

    this.log('Site closed')
  }
}

module.exports = Driver
<commit_msg>Fix detections caching<commit_after>const { URL } = require('url')
const fs = require('fs')
const path = require('path')
const LanguageDetect = require('languagedetect')
const Wappalyzer = require('./wappalyzer')

const {
  setTechnologies,
  setCategories,
  analyze,
  analyzeManyToMany,
  resolve
} = Wappalyzer

const { AWS_LAMBDA_FUNCTION_NAME, CHROMIUM_BIN } = process.env

let puppeteer
let chromiumArgs = [
  '--no-sandbox',
  '--headless',
  '--disable-gpu',
  '--ignore-certificate-errors'
]
let chromiumBin = CHROMIUM_BIN

if (AWS_LAMBDA_FUNCTION_NAME) {
  const chromium = require('chrome-aws-lambda')

  ;({ puppeteer } = chromium)

  chromiumArgs = chromiumArgs.concat(chromium.args)
  chromiumBin = chromium.executablePath
} else {
  puppeteer = require('puppeteer')
}

const languageDetect = new LanguageDetect()

languageDetect.setLanguageType('iso2')

const extensions = /^([^.]+$|\.(asp|aspx|cgi|htm|html|jsp|php)$)/

const errorTypes = {
  RESPONSE_NOT_OK: 'Response was not ok',
  NO_RESPONSE: 'No response from server',
  NO_HTML_DOCUMENT: 'No HTML document'
}

const { apps: technologies, categories } = JSON.parse(
  fs.readFileSync(path.resolve(`${__dirname}/apps.json`))
)

setTechnologies(technologies)
setCategories(categories)

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

function getJs() {
  const dereference = (obj, level = 0) => {
    try {
      // eslint-disable-next-line no-undef
      if (level > 5 || (level && obj === window)) {
        return '[Removed]'
      }

      if (Array.isArray(obj)) {
        obj = obj.map((item) => dereference(item, level + 1))
      }

      if (
        typeof obj === 'function' ||
        (typeof obj === 'object' && obj !== null)
      ) {
        const newObj = {}

        Object.keys(obj).forEach((key) => {
          newObj[key] = dereference(obj[key], level + 1)
        })

        return newObj
      }

      return obj
    } catch (error) {
      return undefined
    }
  }

  // eslint-disable-next-line no-undef
  return dereference(window)
}

function analyzeJs(js) {
  return Array.prototype.concat.apply(
    [],
    js.map(({ name, chain, value }) =>
      analyzeManyToMany(
        Wappalyzer.technologies.find(({ name: _name }) => name === _name),
        'js',
        { [chain]: [value] }
      )
    )
  )
}

class Driver {
  constructor(options = {}) {
    this.options = {
      batchSize: 5,
      debug: false,
      delay: 500,
      htmlMaxCols: 2000,
      htmlMaxRows: 3000,
      maxDepth: 3,
      maxUrls: 10,
      maxWait: 5000,
      recursive: false,
      ...options
    }

    this.options.debug = Boolean(+this.options.debug)
    this.options.recursive = Boolean(+this.options.recursive)
    this.options.delay = this.options.recursive
      ? parseInt(this.options.delay, 10)
      : 0
    this.options.maxDepth = parseInt(this.options.maxDepth, 10)
    this.options.maxUrls = parseInt(this.options.maxUrls, 10)
    this.options.maxWait = parseInt(this.options.maxWait, 10)
    this.options.htmlMaxCols = parseInt(this.options.htmlMaxCols, 10)
    this.options.htmlMaxRows = parseInt(this.options.htmlMaxRows, 10)

    this.destroyed = false
  }

  async init() {
    this.log('Launching browser...')

    try {
      this.browser = await puppeteer.launch({
        args: chromiumArgs,
        executablePath: await chromiumBin
      })

      this.browser.on('disconnected', async () => {
        this.log('Browser disconnected')

        if (!this.destroyed) {
          await this.init()
        }
      })
    } catch (error) {
      throw new Error(error.toString())
    }
  }

  async destroy() {
    this.destroyed = true

    if (this.browser) {
      try {
        await sleep(1)

        await this.browser.close()

        this.log('Browser closed')
      } catch (error) {
        throw new Error(error.toString())
      }
    }
  }

  open(url) {
    return new Site(url, this)
  }

  log(message, source = 'driver') {
    if (this.options.debug) {
      // eslint-disable-next-line no-console
      console.log(`wappalyzer | log | ${source} |`, message)
    }
  }
}

class Site {
  constructor(url, driver) {
    ;({ options: this.options, browser: this.browser } = driver)

    this.driver = driver

    try {
      this.originalUrl = new URL(url)
    } catch (error) {
      throw new Error(error.message || error.toString())
    }

    this.analyzedUrls = {}
    this.detections = []
    this.language = ''

    this.listeners = {}

    this.pages = []
  }

  log(message, source = 'driver', type = 'log') {
    if (this.options.debug) {
      // eslint-disable-next-line no-console
      console[type](`wappalyzer | ${type} | ${source} |`, message)
    }

    this.emit(type, { message, source })
  }

  error(error, source = 'driver') {
    this.log(error, source, 'error')
  }

  on(event, callback) {
    if (!this.listeners[event]) {
      this.listeners[event] = []
    }

    this.listeners[event].push(callback)
  }

  emit(event, params) {
    if (this.listeners[event]) {
      this.listeners[event].forEach((listener) => listener(params))
    }
  }

  async goto(url) {
    // Return when the URL is a duplicate or maxUrls has been reached
    if (
      this.analyzedUrls[url.href] ||
      Object.keys(this.analyzedUrls).length >= this.options.maxUrls
    ) {
      return
    }

    this.log(`Navigate to ${url}`, 'page')

    this.analyzedUrls[url.href] = {
      status: 0
    }

    if (!this.browser) {
      throw new Error('Browser closed')
    }

    const page = await this.browser.newPage()

    this.pages.push(page)

    page.setDefaultTimeout(this.options.maxWait)

    await page.setRequestInterception(true)

    page.on('error', (error) => this.error(error))

    let responseReceived = false

    page.on('request', (request) => {
      try {
        if (
          (responseReceived && request.isNavigationRequest()) ||
          request.frame() !== page.mainFrame() ||
          !['document', 'script'].includes(request.resourceType())
        ) {
          request.abort('blockedbyclient')
        } else {
          request.continue()
        }
      } catch (error) {
        this.error(error)
      }
    })

    page.on('response', (response) => {
      try {
        if (response.url() === url.href) {
          this.analyzedUrls[url.href] = {
            status: response.status()
          }

          const rawHeaders = response.headers()
          const headers = {}

          Object.keys(rawHeaders).forEach((key) => {
            headers[key] = [
              ...(headers[key] || []),
              ...(Array.isArray(rawHeaders[key])
                ? rawHeaders[key]
                : [rawHeaders[key]])
            ]
          })

          this.contentType = headers['content-type'] || null

          if (response.status() >= 300 && response.status() < 400) {
            if (headers.location) {
              url = new URL(headers.location.slice(-1), url)
            }
          } else {
            responseReceived = true

            this.onDetect(analyze({ headers }))
          }
        }
      } catch (error) {
        this.error(error)
      }
    })

    await page.setUserAgent(
      this.options.userAgent ||
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
    )

    try {
      await Promise.race([
        page.goto(url.href, { waitUntil: 'domcontentloaded' }),
        new Promise((resolve, reject) =>
          setTimeout(() => reject(new Error('Timeout')), this.options.maxWait)
        )
      ])
    } catch (error) {
      this.error(error)
    }

    await sleep(1000)

    // Links
    const links = await (
      await page.evaluateHandle(() =>
        Array.from(document.getElementsByTagName('a')).map(
          ({ hash, hostname, href, pathname, protocol, rel }) => ({
            hash,
            hostname,
            href,
            pathname,
            protocol,
            rel
          })
        )
      )
    ).jsonValue()

    // Script tags
    const scripts = await (
      await page.evaluateHandle(() =>
        Array.from(document.getElementsByTagName('script'))
          .map(({ src }) => src)
          .filter((src) => src)
      )
    ).jsonValue()

    // Meta tags
    const meta = await (
      await page.evaluateHandle(() =>
        Array.from(document.querySelectorAll('meta')).reduce((metas, meta) => {
          const key = meta.getAttribute('name') || meta.getAttribute('property')

          if (key) {
            metas[key.toLowerCase()] = [meta.getAttribute('content')]
          }

          return metas
        }, {})
      )
    ).jsonValue()

    // JavaScript
    const win = await page.evaluate(getJs)

    const js = Wappalyzer.technologies
      .filter(({ js }) => Object.keys(js).length)
      .map(({ name, js }) => ({ name, chains: Object.keys(js) }))
      .reduce((technologies, { name, chains }) => {
        chains.forEach((chain) => {
          const value = chain
            .split('.')
            .reduce(
              (value, method) =>
                value && value.hasOwnProperty(method)
                  ? value[method]
                  : undefined,
              win
            )

          if (typeof value !== 'undefined') {
            technologies.push({
              name,
              chain,
              value:
                typeof value === 'string' || typeof value === 'number'
                  ? value
                  : !!value
            })
          }
        })

        return technologies
      }, [])

    // Cookies
    const cookies = (await page.cookies()).reduce(
      (cookies, { name, value }) => ({
        ...cookies,
        [name]: [value]
      }),
      {}
    )

    // HTML
    let html = await page.content()

    if (this.options.htmlMaxCols && this.options.htmlMaxRows) {
      const batches = []
      const rows = html.length / this.options.htmlMaxCols

      for (let i = 0; i < rows; i += 1) {
        if (
          i < this.options.htmlMaxRows / 2 ||
          i > rows - this.options.htmlMaxRows / 2
        ) {
          batches.push(
            html.slice(
              i * this.options.htmlMaxCols,
              (i + 1) * this.options.htmlMaxCols
            )
          )
        }
      }

      html = batches.join('\n')
    }

    // Validate response
    if (!this.analyzedUrls[url.href].status) {
      await page.close()

      this.log('Page closed')

      throw new Error('NO_RESPONSE')
    }

    if (!this.language) {
      this.language = await (
        await page.evaluateHandle(
          () =>
            document.documentElement.getAttribute('lang') ||
            document.documentElement.getAttribute('xml:lang')
        )
      ).jsonValue()
    }

    if (!this.language) {
      try {
        const [attrs] = languageDetect.detect(
          html.replace(/<\/?[^>]+(>|$)/gs, ' '),
          1
        )

        if (attrs) {
          ;[this.language] = attrs
        }
      } catch (error) {
        this.error(error)
      }
    }

    this.onDetect(analyzeJs(js))

    this.onDetect(
      analyze({
        url,
        cookies,
        html,
        scripts,
        meta
      })
    )

    const reducedLinks = Array.prototype.reduce.call(
      links,
      (results, link) => {
        if (
          results &&
          Object.prototype.hasOwnProperty.call(
            Object.getPrototypeOf(results),
            'push'
          ) &&
          link.protocol &&
          link.protocol.match(/https?:/) &&
          link.rel !== 'nofollow' &&
          link.hostname === url.hostname &&
          extensions.test(link.pathname)
        ) {
          results.push(new URL(link.href.split('#')[0]))
        }

        return results
      },
      []
    )

    await page.close()

    this.log('Page closed')

    this.emit('goto', url)

    return reducedLinks
  }

  async analyze(url = this.originalUrl, index = 1, depth = 1) {
    try {
      await sleep(this.options.delay * index)

      const links = await this.goto(url)

      if (links && this.options.recursive && depth < this.options.maxDepth) {
        await this.batch(links.slice(0, this.options.maxUrls), depth + 1)
      }
    } catch (error) {
      const type =
        error.message && errorTypes[error.message]
          ? error.message
          : 'UNKNOWN_ERROR'
      const message =
        error.message && errorTypes[error.message]
          ? errorTypes[error.message]
          : 'Unknown error'

      this.analyzedUrls[url.href] = {
        status: 0,
        error: {
          type,
          message
        }
      }

      this.error(error)
    }

    return {
      urls: this.analyzedUrls,
      applications: resolve(this.detections).map(
        ({ name, confidence, version, icon, website, categories }) => ({
          name,
          confidence,
          version,
          icon,
          website,
          categories: categories.reduce(
            (categories, { id, name }) => ({
              ...categories,
              [id]: name
            }),
            {}
          )
        })
      ),
      meta: {
        language: this.language
      }
    }
  }

  async batch(links, depth, batch = 0) {
    if (links.length === 0) {
      return
    }

    const batched = links.splice(0, this.options.batchSize)

    await Promise.all(
      batched.map((link, index) => this.analyze(link, index, depth))
    )

    await this.batch(links, depth, batch + 1)
  }

  onDetect(detections = [], language) {
    this.detections = this.detections.concat(detections)

    this.detections.filter(
      ({ technology: { name }, pattern: { regex } }, index) =>
        this.detections.findIndex(
          ({ technology: { name: _name }, pattern: { regex: _regex } }) =>
            name === _name && (!regex || regex.toString() === _regex.toString())
        ) === index
    )
  }

  async destroy() {
    await Promise.all(
      this.pages.map(async (page) => {
        if (page) {
          try {
            await page.close()

            this.log('Page closed')
          } catch (error) {
            // Continue
          }
        }
      })
    )

    this.log('Site closed')
  }
}

module.exports = Driver
<|endoftext|>