<commit_before>angular.module("leaflet-directive").directive('geojson', function ($log, $rootScope, leafletData, leafletHelpers) {
    return {
        restrict: "A",
        scope: false,
        replace: false,
        require: 'leaflet',

        link: function(scope, element, attrs, controller) {
            var safeApply = leafletHelpers.safeApply,
                isDefined = leafletHelpers.isDefined,
                leafletScope  = controller.getLeafletScope(),
                leafletGeoJSON = {};

            controller.getMap().then(function(map) {
                leafletScope.$watch("geojson", function(geojson) {
                    if (isDefined(leafletGeoJSON) && map.hasLayer(leafletGeoJSON)) {
                        map.removeLayer(leafletGeoJSON);
                    }

                    if (!(isDefined(geojson) && isDefined(geojson.data))) {
                        return;
                    }

                    var resetStyleOnMouseout = geojson.resetStyleOnMouseout,
                        onEachFeature = geojson.onEachFeature;

                    if (!onEachFeature) {
                        onEachFeature = function(feature, layer) {
                            if (leafletHelpers.LabelPlugin.isLoaded() && isDefined(geojson.label)) {
                                layer.bindLabel(feature.properties.description);
                            }

                            layer.on({
                                mouseover: function(e) {
                                    safeApply(leafletScope, function() {
                                        geojson.selected = feature;
                                        $rootScope.$broadcast('leafletDirectiveMap.geojsonMouseover', e);
                                    });
                                },
                                mouseout: function(e) {
                                    if (resetStyleOnMouseout) {
                                        leafletGeoJSON.resetStyle(e.target);
                                    }
                                    safeApply(leafletScope, function() {
                                        geojson.selected = undefined;
                                        $rootScope.$broadcast('leafletDirectiveMap.geojsonMouseout', e);
                                    });
                                },
                                click: function(e) {
                                    safeApply(leafletScope, function() {
                                        geojson.selected = feature;
                                        $rootScope.$broadcast('leafletDirectiveMap.geojsonClick', geojson.selected, e);
                                    });
                                }
                            });
                        };
                    }

                    geojson.options = {
                        style: geojson.style,
                        filter: geojson.filter,
                        onEachFeature: onEachFeature,
                        pointToLayer: geojson.pointToLayer
                    };

                    leafletGeoJSON = L.geoJson(geojson.data, geojson.options);
                    leafletData.setGeoJSON(leafletGeoJSON);
                    leafletGeoJSON.addTo(map);
                });
            });
        }
    };
});
<commit_msg>Fixed setting geoJSON on multiple maps in DOM<commit_after>angular.module("leaflet-directive").directive('geojson', function ($log, $rootScope, leafletData, leafletHelpers) {
    return {
        restrict: "A",
        scope: false,
        replace: false,
        require: 'leaflet',

        link: function(scope, element, attrs, controller) {
            var safeApply = leafletHelpers.safeApply,
                isDefined = leafletHelpers.isDefined,
                leafletScope  = controller.getLeafletScope(),
                leafletGeoJSON = {};

            controller.getMap().then(function(map) {
                leafletScope.$watch("geojson", function(geojson) {
                    if (isDefined(leafletGeoJSON) && map.hasLayer(leafletGeoJSON)) {
                        map.removeLayer(leafletGeoJSON);
                    }

                    if (!(isDefined(geojson) && isDefined(geojson.data))) {
                        return;
                    }

                    var resetStyleOnMouseout = geojson.resetStyleOnMouseout,
                        onEachFeature = geojson.onEachFeature;

                    if (!onEachFeature) {
                        onEachFeature = function(feature, layer) {
                            if (leafletHelpers.LabelPlugin.isLoaded() && isDefined(geojson.label)) {
                                layer.bindLabel(feature.properties.description);
                            }

                            layer.on({
                                mouseover: function(e) {
                                    safeApply(leafletScope, function() {
                                        geojson.selected = feature;
                                        $rootScope.$broadcast('leafletDirectiveMap.geojsonMouseover', e);
                                    });
                                },
                                mouseout: function(e) {
                                    if (resetStyleOnMouseout) {
                                        leafletGeoJSON.resetStyle(e.target);
                                    }
                                    safeApply(leafletScope, function() {
                                        geojson.selected = undefined;
                                        $rootScope.$broadcast('leafletDirectiveMap.geojsonMouseout', e);
                                    });
                                },
                                click: function(e) {
                                    safeApply(leafletScope, function() {
                                        geojson.selected = feature;
                                        $rootScope.$broadcast('leafletDirectiveMap.geojsonClick', geojson.selected, e);
                                    });
                                }
                            });
                        };
                    }

                    geojson.options = {
                        style: geojson.style,
                        filter: geojson.filter,
                        onEachFeature: onEachFeature,
                        pointToLayer: geojson.pointToLayer
                    };

                    leafletGeoJSON = L.geoJson(geojson.data, geojson.options);
                    leafletData.setGeoJSON(leafletGeoJSON, attrs.id);
                    leafletGeoJSON.addTo(map);
                });
            });
        }
    };
});
<|endoftext|>


<commit_before>
var Emitter = require('../lib/emitter');
var expect = require('expect.js');

describe('emitter', function(){

  it('should return handles for subs', function(){
    var e = new Emitter;
    var c = 0;
    var h1 = e.on('test', function(){
      c++;
    });
    var h2 = e.on('test', function(){
      c--;
    });
    e.emit('test');
    e.emit('test');
    h2.destroy();
    expect(c).to.be(0);
    e.emit('test');
    e.emit('test');
    expect(c).to.be(2);
  });

});
<commit_msg>test: remove emitter tests<commit_after><|endoftext|>


<commit_before>'use strict';

/* eslint-disable no-console */
const chai = require('chai');
const sinonChai = require('sinon-chai');
const sinon = require('sinon');

const wsData = require('../exports');

chai.should();
chai.use(sinonChai);

describe('safeRequire', () => {
  beforeEach(() => {
    sinon.spy(console, 'debug');
  });

  afterEach(() => {
    console.debug.restore();
    delete process.env.LOG_LEVEL;
  });

  it('should log if debug is provided and no file exists', () => {
    process.env.LOG_LEVEL = 'DEBUG';
    const safeRequire = require('../safeRequire');
    safeRequire('poop.js');
    // check that console.debug was called
    console.debug.should.have.been.called;
  });

  it('should not log if debug is non-existant and no file exists', () => {
    const safeRequire = require('../safeRequire');
    safeRequire('poop.js');
    // check that console.debug was called
    console.debug.should.not.have.been.called;
  });

  it('should not log if log level is error and no file exists', () => {
    process.env.LOG_LEVEL = 'ERROR';
    const safeRequire = require('../safeRequire');
    safeRequire('poop.js');
    // check that console.debug was called
    console.debug.should.not.have.been.called;
  });
});

describe('Worldstate Data', () => {
  it('should be an object', () => {
    wsData.should.be.an('object');
  });

  it('should have conclave data', () => {
    wsData.should.have.property('conclave');
    wsData.conclave.should.not.be.empty;
  });

  it('should have events data', () => {
    wsData.should.have.property('events');
    wsData.events.should.not.be.empty;
  });

  it('should have factions data', () => {
    wsData.should.have.property('factions');
    wsData.factions.should.not.be.empty;
  });

  it('should have fissureModifiers data', () => {
    wsData.should.have.property('fissureModifiers');
    wsData.fissureModifiers.should.not.be.empty;
  });

  it('should have languages data', () => {
    wsData.should.have.property('languages');
    wsData.languages.should.not.be.empty;
  });

  it('should have missionTypes data', () => {
    wsData.should.have.property('missionTypes');
    wsData.missionTypes.should.not.be.empty;
  });

  it('should have operationTypes data', () => {
    wsData.should.have.property('operationTypes');
    wsData.operationTypes.should.not.be.empty;
  });

  it('should have persistentEnemy data', () => {
    wsData.should.have.property('persistentEnemy');
    wsData.persistentEnemy.should.not.be.empty;
  });

  it('should have solNodes data', () => {
    wsData.should.have.property('solNodes');
    wsData.solNodes.should.not.be.empty;
  });

  it('should have sortie data', () => {
    wsData.should.have.property('arcanes');
    wsData.sortie.should.not.be.empty;
  });

  it('should have syndicates data', () => {
    wsData.should.have.property('syndicates');
    wsData.syndicates.should.not.be.empty;
  });

  it('should have tutorials data', () => {
    wsData.should.have.property('tutorials');
    wsData.tutorials.should.not.be.empty;
  });

  it('should have upgradeTypes data', () => {
    wsData.should.have.property('upgradeTypes');
    wsData.upgradeTypes.should.not.be.empty;
  });
});
<commit_msg>chore: lint<commit_after>'use strict';

/* eslint-disable no-console */
const chai = require('chai');
const sinonChai = require('sinon-chai');
const sinon = require('sinon');

const wsData = require('../exports');

chai.should();
chai.use(sinonChai);

describe('safeRequire', () => {
  beforeEach(() => {
    sinon.spy(console, 'debug');
  });

  afterEach(() => {
    console.debug.restore();
    delete process.env.LOG_LEVEL;
  });

  it('should log if debug is provided and no file exists', () => {
    process.env.LOG_LEVEL = 'DEBUG';
    const safeRequire = require('../safeRequire');
    safeRequire('poop.js');
    // check that console.debug was called
    console.debug.should.have.been.called;
  });

  it('should not log if debug is non-existant and no file exists', () => {
    const safeRequire = require('../safeRequire');
    safeRequire('poop.js');
    // check that console.debug was called
    console.debug.should.not.have.been.called;
  });

  it('should not log if log level is error and no file exists', () => {
    process.env.LOG_LEVEL = 'ERROR';
    const safeRequire = require('../safeRequire');
    safeRequire('poop.js');
    // check that console.debug was called
    console.debug.should.not.have.been.called;
  });
});

describe('Worldstate Data', () => {
  it('should be an object', () => {
    wsData.should.be.an('object');
  });

  it('should have conclave data', () => {
    wsData.should.have.property('conclave');
    wsData.conclave.should.not.be.empty;
  });

  it('should have events data', () => {
    wsData.should.have.property('events');
    wsData.events.should.not.be.empty;
  });

  it('should have factions data', () => {
    wsData.should.have.property('factions');
    wsData.factions.should.not.be.empty;
  });

  it('should have fissureModifiers data', () => {
    wsData.should.have.property('fissureModifiers');
    wsData.fissureModifiers.should.not.be.empty;
  });

  it('should have languages data', () => {
    wsData.should.have.property('languages');
    wsData.languages.should.not.be.empty;
  });

  it('should have missionTypes data', () => {
    wsData.should.have.property('missionTypes');
    wsData.missionTypes.should.not.be.empty;
  });

  it('should have operationTypes data', () => {
    wsData.should.have.property('operationTypes');
    wsData.operationTypes.should.not.be.empty;
  });

  it('should have persistentEnemy data', () => {
    wsData.should.have.property('persistentEnemy');
    wsData.persistentEnemy.should.not.be.empty;
  });

  it('should have solNodes data', () => {
    wsData.should.have.property('solNodes');
    wsData.solNodes.should.not.be.empty;
  });

  it('should have sortie data', () => {
    wsData.should.have.property('arcanes');
    wsData.sortie.should.not.be.empty;
  });

  it('should have syndicates data', () => {
    wsData.should.have.property('syndicates');
    wsData.syndicates.should.not.be.empty;
  });

  it('should have tutorials data', () => {
    wsData.should.have.property('tutorials');
    wsData.tutorials.should.not.be.empty;
  });

  it('should have upgradeTypes data', () => {
    wsData.should.have.property('upgradeTypes');
    wsData.upgradeTypes.should.not.be.empty;
  });
});
<|endoftext|>


<commit_before>emmet = require("../../lib/emmet-matcher-zen-coding/lib/emmet.js");
<commit_msg>Switch to better-matcher<commit_after>emmet = require("../../lib/emmet-better-matcher/lib/emmet.js");
<|endoftext|>


<commit_before>import i18next from 'i18next';
import assert from 'power-assert';
import _ from 'lodash';
import EventEmitter from 'eventemitter2';
import { expect } from 'chai';
import NativePromise from 'native-promise-only';

import i18Defaults from '../lib/i18n';
import FormBuilder from '../lib/FormBuilder';
import AllComponents from '../lib/components';
import Components from '../lib/components/Components';

Components.setComponents(AllComponents);

if (process) {
  // Do not handle unhandled rejections.
  process.on('unhandledRejection', (err, p) => {
    console.warn('Unhandled rejection!', err);
  });
}

// Make sure that the Option is available from the window.
global.Option = global.window.Option;

// Stub out the toLocaleString method so it works in mocha.
// eslint-disable-next-line no-extend-native
Number.prototype.toLocaleString = function(local, options) {
  if (options && options.style === 'currency') {
    switch (local) {
      case 'en':
      case 'en-US':
        return '$100.00';
      case 'en-GB':
        return 'US$100.00';
      case 'fr':
        return '100,00 $US';
      case 'de':
        return '100,00 $';
    }
  }
  else {
    switch (local) {
      case 'en':
      case 'en-US':
        return '12,345.679';
      case 'en-GB':
        return '12,345.679';
      case 'fr':
        return '12 345,679';
      case 'de':
        return '12.345,679';
    }
  }
};

let formBuilderElement = null;
let formBuilder = null;

function onNext(cmp, event, cb) {
  expect(cmp.events).to.be.an('object');
  expect(cmp.events.once).to.be.a('function');
  const fullEvent = `${cmp.options.namespace}.${event}`;
  cmp.events.once(fullEvent, cb);
}

const Harness = {
  builderBefore(done, options = {}) {
    var html;    // Unsure what _your code_ needs here -- using `undefined` to trigger default value
    var opt = { url: 'http://localhost/' };
    this.jsdom = require('jsdom-global')(html, opt);
    window.confirm = () => true;
    formBuilderElement = document.createElement('div');
    document.body.appendChild(formBuilderElement);
    formBuilder = new FormBuilder(formBuilderElement, { display: 'form', components: [] }, options);
    formBuilder.instance.ready.then(() => done());
  },

  builderAfter(done) {
    formBuilder.instance.destroy();
    document.body.removeChild(formBuilderElement);
    done();
  },

  getBuilder() {
    return formBuilder.instance;
  },

  saveComponent() {
    const click = new MouseEvent('click', {
      view: window,
      bubbles: true,
      cancelable: true
    });

    const saveBtn = formBuilder.instance.componentEdit.querySelector('[ref="saveButton"]');
    if (saveBtn) {
      saveBtn.dispatchEvent(click);
    }
  },

  buildComponent(type, container) {
    // Get the builder sidebar component.
    const webformBuilder = formBuilder.instance;
    let builderGroup = null;
    let groupName = '';

    _.each(webformBuilder.groups, (group, key) => {
      if (group.components[type]) {
        groupName = key;
        return false;
      }
    });

    if (!groupName) {
      return;
    }
    const openedGroup = document.getElementById(`group-${groupName}"`);
    if (openedGroup) {
      openedGroup.classList.remove('in');
    }
    const group = document.getElementById(`group-${groupName}`);
    group && group.classList.add('in');

    let component = webformBuilder.element.querySelector(`span[data-type='${type}']`);
    if (component) {
      component = component && component.cloneNode(true);
      const element = container || webformBuilder.element.querySelector('.drag-container.formio-builder-form');
      element.appendChild(component);
      builderGroup = document.getElementById(`group-container-${groupName}`);
      webformBuilder.onDrop(component, element, builderGroup);
    }
    else {
      return;
    }

    return webformBuilder;
  },

  setComponentProperty(property, before, after, cb) {
    const component = _.cloneDeep(formBuilder.editForm.submission);
    assert.equal(_.get(component.data, property), before);
    _.set(component.data, property, after);
    formBuilder.off('updateComponent');
    formBuilder.on('updateComponent', () => {
      const preview = formBuilder.componentPreview.innerHTML;
      assert.equal(_.get(formBuilder.editForm.submission.data, property), after);
      cb(preview);
    });
    formBuilder.editForm.submission = component;
  },

  testBuilderProperty(property, before, after, previewRegEx, cb) {
    Harness.testVisibility(formBuilder.editForm, `.formio-component-${property}`, true);
    Harness.setComponentProperty(property, before, after, (preview) => {
      if (previewRegEx) {
        assert(preview.match(previewRegEx), `${property} not set correctly`);
      }
      Harness.getInputValue(formBuilder.editForm, `data[${property}]`, after);
      cb();
    });
  },

  getDate() {
    let timestamp = (new Date()).getTime();
    timestamp = parseInt(timestamp / 1000, 10);
    return (new Date(timestamp * 1000)).toISOString();
  },
  testCreate(Component, componentSettings, options = {}) {
    const compSettings = _.cloneDeep(componentSettings);
    const component = new Component(compSettings, _.merge({
      events: new EventEmitter({
        wildcard: false,
        maxListeners: 0
      })
    }, options));
    component.pristine = false;
    return new Promise((resolve, reject) => {
      i18next.init(i18Defaults, (err) => {
        if (err) {
          return reject(err);
        }
        // Need a parent element to redraw.
        const parent = document.createElement('div');
        const element = document.createElement('div');
        parent.appendChild(element);
        component.build(element);
        assert(Boolean(component.element), `No ${component.type} element created.`);
        return resolve(component);
      });
    });
  },
  testConditionals(form, submission, hidden, done) {
    form.on('change', () => {
      form.everyComponent((comp) => {
        if (hidden.includes(comp.component.key)) {
          // Should be hidden.
          assert(!comp.visible, 'Element should not be visible');
          assert.equal(comp.element.childElementCount, 0, 'Hidden elements should not have children');
        }
        else {
          // Should be visible.
          assert(comp.visible, 'Element should not be hidden');
          assert.notEqual(comp.element.childElementCount, 0, 'Element must be visible');
        }
      });
      done();
    });
    form.submission = submission;
  },
  testVisibility(component, query, visible) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    if (visible) {
      assert((element.style.visibility === '') || (element.style.visibility === 'visible'), 'Element must be visible');
    }
    else {
      assert(element.style.visibility === 'hidden', 'Element must be hidden');
    }
  },
  testComponentVisibility(component, query, visible) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    const isHidden = element.className.includes('formio-hidden');
    if (visible) {
      assert(!isHidden, 'Element must be visible');
    }
    else {
      assert(isHidden, 'Element must be hidden');
    }
  },
  clickElement(component, query) {
    const clickEvent = new MouseEvent('click', {
      view: window,
      bubbles: true,
      cancelable: true
    });
    let element = query;
    if (typeof query === 'string') {
      element = this.testElement(component, query, true);
    }
    return element ? element.dispatchEvent(clickEvent) : null;
  },
  dispatchEvent(eventType, element, query, beforeDispatch) {
    const event = new Event(eventType);
    const el = element.querySelector(query);
    assert(el, 'Element is not found');
    beforeDispatch && beforeDispatch(el);
    el.dispatchEvent(event);
    return el;
  },
  testElements(component, query, number) {
    const elements = component.element.querySelectorAll(query);
    if (number !== undefined) {
      assert.equal(elements.length, number);
    }
    return elements;
  },
  testElement(component, query, exists) {
    const element = component.element.querySelector(query);
    if (exists !== undefined) {
      assert.equal(Boolean(element), Boolean(exists));
    }
    return element;
  },
  testInnerHtml(component, query, content) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    assert.equal(element.innerHTML.trim(), content);
  },
  testAttribute(component, query, attribute, value) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    assert.equal(element.getAttribute(attribute), value);
  },
  testHasClass(component, query, className) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    assert(element.className.split(' ').includes(className));
  },
  testElementAttribute(element, attribute, expected) {
    if (element !== undefined && element.getAttribute(attribute)) {
      assert.equal(expected, element.getAttribute(attribute));
    }
    return element;
  },
  testSetGet(component, value) {
    const originValue = _.cloneDeep(value);
    component.setValue(value);
    assert.deepEqual(component.getValue(), originValue);
    return component;
  },
  setInputValue(component, name, value) {
    const inputEvent = new Event('input', {bubbles: true, cancelable: true});
    const element = component.element.querySelector(`input[name="${name}"]`);
    assert(element, `${name} input not found`);
    element.value = value;
    return element.dispatchEvent(inputEvent);
  },
  getInputValue(component, name, value, valueProperty = 'value') {
    const element = component.element.querySelector(`[name="${name}"]`);
    assert(element, `${name} input not found`);
    assert.equal(value, element[valueProperty]);
  },
  testSetInput(component, input, output, visible, index = 0) {
    component.setValue(input);
    assert.deepEqual(component.getValue(), output);
    assert.deepEqual(component.refs.input[index].value, visible);
    return component;
  },
  testSubmission(form, submission, onChange) {
    if (onChange) {
      form.on('change', onChange);
    }
    this.testSetGet(form, submission);
    assert.deepEqual(form.data, submission.data);
  },
  testErrors(form, submission, errors, done) {
    form.on('error', (err) => {
      _.each(errors, (error, index) => {
        error.component = form.getComponent(error.component).component;
        assert.deepEqual(err[index], error);
      });
      form.off('error');
      done();
    });

    this.testSetGet(form, submission);
    assert.deepEqual(form.data, submission.data);
    form.submit();
  },
  testValid(component, value) {
    return new Promise((resolve, reject) => {
      let resolved = false;
      component.on('componentChange', (change) => {
        if (resolved) {return}
        const valid = component.checkValidity();
        if (valid) {
          assert.equal(component.dataValue, value);
          resolve();
        }
        else {
          reject('Component should be valid');
        }
        resolved = true;
      });
      component.setValue(value);
      component.triggerChange();
    });
  },
  testInvalid(component, value, field, expectedError) {
    return new Promise((resolve, reject) => {
      let resolved = false;
      component.on('componentChange', (change) => {
        if (resolved) {return}
        if(component.checkValidity()) {
          reject('Component should not be valid');
          resolved = true;
        }
      });
      component.on('componentError', (error) => {
        if (resolved) {return}
        assert.equal(error.component.key, field);
        assert.equal(error.message, expectedError);
        resolve();
        resolved = true;
      });

      // Set the value.
      component.setValue(value);
      component.triggerChange();
    });
  },
  testComponent(component, test, done) {
    let testBad = true;
    component.on('componentChange', (change) => {
      const valid = component.checkValidity();
      if (valid && !testBad) {
        assert.equal(change.value, test.good.value);
        done();
      }
    });
    component.on('componentError', (error) => {
      if (!testBad) {
        return done(new Error('Validation Error'));
      }
      testBad = false;
      assert.equal(error.component.key, test.bad.field);
      assert.equal(error.message, test.bad.error);
      component.setValue(test.good.value);
    });

    // Set the value.
    component.pristine = false;
    component.setValue(test.bad.value);
  },
  testWizardPrevPage(form, errors, onPrevPage) {
    if (errors) {
      form.on('error', (err) => {
        _.each(errors, (error, index) => {
          error.component = form.getComponent(error.component).component;
          assert.deepEqual(err[index], error);
        });
      });
    }
    if (onPrevPage) {
      form.on('prevPage', onPrevPage);
    }
    return form.prevPage();
  },
  testWizardNextPage(form, errors, onNextPage) {
    if (errors) {
      form.on('error', (err) => {
        _.each(errors, (error, index) => {
          error.component = form.getComponent(error.component).component;
          assert.deepEqual(err[index], error);
        });
      });
    }
    if (onNextPage) {
      form.on('nextPage', onNextPage);
    }
    return form.nextPage();
  },
  testNumberBlur(cmp, inv, outv, display, index = 0) {
    const input = _.get(cmp, ['refs', 'input', index], {});
    input.value = inv;
    input.dispatchEvent(new Event('blur'));
    assert.strictEqual(cmp.getValueAt(index), outv);
    assert.strictEqual(input.value, display);
  },
  onNext
};
export default Harness;
<commit_msg>changed error test<commit_after>import i18next from 'i18next';
import assert from 'power-assert';
import _ from 'lodash';
import EventEmitter from 'eventemitter2';
import { expect } from 'chai';
import NativePromise from 'native-promise-only';

import i18Defaults from '../lib/i18n';
import FormBuilder from '../lib/FormBuilder';
import AllComponents from '../lib/components';
import Components from '../lib/components/Components';

Components.setComponents(AllComponents);

if (process) {
  // Do not handle unhandled rejections.
  process.on('unhandledRejection', (err, p) => {
    console.warn('Unhandled rejection!', err);
  });
}

// Make sure that the Option is available from the window.
global.Option = global.window.Option;

// Stub out the toLocaleString method so it works in mocha.
// eslint-disable-next-line no-extend-native
Number.prototype.toLocaleString = function(local, options) {
  if (options && options.style === 'currency') {
    switch (local) {
      case 'en':
      case 'en-US':
        return '$100.00';
      case 'en-GB':
        return 'US$100.00';
      case 'fr':
        return '100,00 $US';
      case 'de':
        return '100,00 $';
    }
  }
  else {
    switch (local) {
      case 'en':
      case 'en-US':
        return '12,345.679';
      case 'en-GB':
        return '12,345.679';
      case 'fr':
        return '12 345,679';
      case 'de':
        return '12.345,679';
    }
  }
};

let formBuilderElement = null;
let formBuilder = null;

function onNext(cmp, event, cb) {
  expect(cmp.events).to.be.an('object');
  expect(cmp.events.once).to.be.a('function');
  const fullEvent = `${cmp.options.namespace}.${event}`;
  cmp.events.once(fullEvent, cb);
}

const Harness = {
  builderBefore(done, options = {}) {
    var html;    // Unsure what _your code_ needs here -- using `undefined` to trigger default value
    var opt = { url: 'http://localhost/' };
    this.jsdom = require('jsdom-global')(html, opt);
    window.confirm = () => true;
    formBuilderElement = document.createElement('div');
    document.body.appendChild(formBuilderElement);
    formBuilder = new FormBuilder(formBuilderElement, { display: 'form', components: [] }, options);
    formBuilder.instance.ready.then(() => done());
  },

  builderAfter(done) {
    formBuilder.instance.destroy();
    document.body.removeChild(formBuilderElement);
    done();
  },

  getBuilder() {
    return formBuilder.instance;
  },

  saveComponent() {
    const click = new MouseEvent('click', {
      view: window,
      bubbles: true,
      cancelable: true
    });

    const saveBtn = formBuilder.instance.componentEdit.querySelector('[ref="saveButton"]');
    if (saveBtn) {
      saveBtn.dispatchEvent(click);
    }
  },

  buildComponent(type, container) {
    // Get the builder sidebar component.
    const webformBuilder = formBuilder.instance;
    let builderGroup = null;
    let groupName = '';

    _.each(webformBuilder.groups, (group, key) => {
      if (group.components[type]) {
        groupName = key;
        return false;
      }
    });

    if (!groupName) {
      return;
    }
    const openedGroup = document.getElementById(`group-${groupName}"`);
    if (openedGroup) {
      openedGroup.classList.remove('in');
    }
    const group = document.getElementById(`group-${groupName}`);
    group && group.classList.add('in');

    let component = webformBuilder.element.querySelector(`span[data-type='${type}']`);
    if (component) {
      component = component && component.cloneNode(true);
      const element = container || webformBuilder.element.querySelector('.drag-container.formio-builder-form');
      element.appendChild(component);
      builderGroup = document.getElementById(`group-container-${groupName}`);
      webformBuilder.onDrop(component, element, builderGroup);
    }
    else {
      return;
    }

    return webformBuilder;
  },

  setComponentProperty(property, before, after, cb) {
    const component = _.cloneDeep(formBuilder.editForm.submission);
    assert.equal(_.get(component.data, property), before);
    _.set(component.data, property, after);
    formBuilder.off('updateComponent');
    formBuilder.on('updateComponent', () => {
      const preview = formBuilder.componentPreview.innerHTML;
      assert.equal(_.get(formBuilder.editForm.submission.data, property), after);
      cb(preview);
    });
    formBuilder.editForm.submission = component;
  },

  testBuilderProperty(property, before, after, previewRegEx, cb) {
    Harness.testVisibility(formBuilder.editForm, `.formio-component-${property}`, true);
    Harness.setComponentProperty(property, before, after, (preview) => {
      if (previewRegEx) {
        assert(preview.match(previewRegEx), `${property} not set correctly`);
      }
      Harness.getInputValue(formBuilder.editForm, `data[${property}]`, after);
      cb();
    });
  },

  getDate() {
    let timestamp = (new Date()).getTime();
    timestamp = parseInt(timestamp / 1000, 10);
    return (new Date(timestamp * 1000)).toISOString();
  },
  testCreate(Component, componentSettings, options = {}) {
    const compSettings = _.cloneDeep(componentSettings);
    const component = new Component(compSettings, _.merge({
      events: new EventEmitter({
        wildcard: false,
        maxListeners: 0
      })
    }, options));
    component.pristine = false;
    return new Promise((resolve, reject) => {
      i18next.init(i18Defaults, (err) => {
        if (err) {
          return reject(err);
        }
        // Need a parent element to redraw.
        const parent = document.createElement('div');
        const element = document.createElement('div');
        parent.appendChild(element);
        component.build(element);
        assert(Boolean(component.element), `No ${component.type} element created.`);
        return resolve(component);
      });
    });
  },
  testConditionals(form, submission, hidden, done) {
    form.on('change', () => {
      form.everyComponent((comp) => {
        if (hidden.includes(comp.component.key)) {
          // Should be hidden.
          assert(!comp.visible, 'Element should not be visible');
          assert.equal(comp.element.childElementCount, 0, 'Hidden elements should not have children');
        }
        else {
          // Should be visible.
          assert(comp.visible, 'Element should not be hidden');
          assert.notEqual(comp.element.childElementCount, 0, 'Element must be visible');
        }
      });
      done();
    });
    form.submission = submission;
  },
  testVisibility(component, query, visible) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    if (visible) {
      assert((element.style.visibility === '') || (element.style.visibility === 'visible'), 'Element must be visible');
    }
    else {
      assert(element.style.visibility === 'hidden', 'Element must be hidden');
    }
  },
  testComponentVisibility(component, query, visible) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    const isHidden = element.className.includes('formio-hidden');
    if (visible) {
      assert(!isHidden, 'Element must be visible');
    }
    else {
      assert(isHidden, 'Element must be hidden');
    }
  },
  clickElement(component, query) {
    const clickEvent = new MouseEvent('click', {
      view: window,
      bubbles: true,
      cancelable: true
    });
    let element = query;
    if (typeof query === 'string') {
      element = this.testElement(component, query, true);
    }
    return element ? element.dispatchEvent(clickEvent) : null;
  },
  dispatchEvent(eventType, element, query, beforeDispatch) {
    const event = new Event(eventType);
    const el = element.querySelector(query);
    assert(el, 'Element is not found');
    beforeDispatch && beforeDispatch(el);
    el.dispatchEvent(event);
    return el;
  },
  testElements(component, query, number) {
    const elements = component.element.querySelectorAll(query);
    if (number !== undefined) {
      assert.equal(elements.length, number);
    }
    return elements;
  },
  testElement(component, query, exists) {
    const element = component.element.querySelector(query);
    if (exists !== undefined) {
      assert.equal(Boolean(element), Boolean(exists));
    }
    return element;
  },
  testInnerHtml(component, query, content) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    assert.equal(element.innerHTML.trim(), content);
  },
  testAttribute(component, query, attribute, value) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    assert.equal(element.getAttribute(attribute), value);
  },
  testHasClass(component, query, className) {
    const element = component.element.querySelector(query);
    assert(element, `${query} not found`);
    assert(element.className.split(' ').includes(className));
  },
  testElementAttribute(element, attribute, expected) {
    if (element !== undefined && element.getAttribute(attribute)) {
      assert.equal(expected, element.getAttribute(attribute));
    }
    return element;
  },
  testSetGet(component, value) {
    const originValue = _.cloneDeep(value);
    component.setValue(value);
    assert.deepEqual(component.getValue(), originValue);
    return component;
  },
  setInputValue(component, name, value) {
    const inputEvent = new Event('input', {bubbles: true, cancelable: true});
    const element = component.element.querySelector(`input[name="${name}"]`);
    assert(element, `${name} input not found`);
    element.value = value;
    return element.dispatchEvent(inputEvent);
  },
  getInputValue(component, name, value, valueProperty = 'value') {
    const element = component.element.querySelector(`[name="${name}"]`);
    assert(element, `${name} input not found`);
    assert.equal(value, element[valueProperty]);
  },
  testSetInput(component, input, output, visible, index = 0) {
    component.setValue(input);
    assert.deepEqual(component.getValue(), output);
    assert.deepEqual(component.refs.input[index].value, visible);
    return component;
  },
  testSubmission(form, submission, onChange) {
    if (onChange) {
      form.on('change', onChange);
    }
    this.testSetGet(form, submission);
    assert.deepEqual(form.data, submission.data);
  },
  testErrors(form, submission, errors, done) {
    form.on('error', (err) => {
      _.each(errors, (error, index) => {
        error.component = form.getComponent(error.component).component;
        assert.deepEqual(err[index].component, error.component);
        assert.equal(err[index].message, error.message);
      });
      form.off('error');
      done();
    });

    this.testSetGet(form, submission);
    assert.deepEqual(form.data, submission.data);
    form.submit();
  },
  testValid(component, value) {
    return new Promise((resolve, reject) => {
      let resolved = false;
      component.on('componentChange', (change) => {
        if (resolved) {return}
        const valid = component.checkValidity();
        if (valid) {
          assert.equal(component.dataValue, value);
          resolve();
        }
        else {
          reject('Component should be valid');
        }
        resolved = true;
      });
      component.setValue(value);
      component.triggerChange();
    });
  },
  testInvalid(component, value, field, expectedError) {
    return new Promise((resolve, reject) => {
      let resolved = false;
      component.on('componentChange', (change) => {
        if (resolved) {return}
        if(component.checkValidity()) {
          reject('Component should not be valid');
          resolved = true;
        }
      });
      component.on('componentError', (error) => {
        if (resolved) {return}
        assert.equal(error.component.key, field);
        assert.equal(error.message, expectedError);
        resolve();
        resolved = true;
      });

      // Set the value.
      component.setValue(value);
      component.triggerChange();
    });
  },
  testComponent(component, test, done) {
    let testBad = true;
    component.on('componentChange', (change) => {
      const valid = component.checkValidity();
      if (valid && !testBad) {
        assert.equal(change.value, test.good.value);
        done();
      }
    });
    component.on('componentError', (error) => {
      if (!testBad) {
        return done(new Error('Validation Error'));
      }
      testBad = false;
      assert.equal(error.component.key, test.bad.field);
      assert.equal(error.message, test.bad.error);
      component.setValue(test.good.value);
    });

    // Set the value.
    component.pristine = false;
    component.setValue(test.bad.value);
  },
  testWizardPrevPage(form, errors, onPrevPage) {
    if (errors) {
      form.on('error', (err) => {
        _.each(errors, (error, index) => {
          error.component = form.getComponent(error.component).component;
          assert.deepEqual(err[index], error);
        });
      });
    }
    if (onPrevPage) {
      form.on('prevPage', onPrevPage);
    }
    return form.prevPage();
  },
  testWizardNextPage(form, errors, onNextPage) {
    if (errors) {
      form.on('error', (err) => {
        _.each(errors, (error, index) => {
          error.component = form.getComponent(error.component).component;
          assert.deepEqual(err[index], error);
        });
      });
    }
    if (onNextPage) {
      form.on('nextPage', onNextPage);
    }
    return form.nextPage();
  },
  testNumberBlur(cmp, inv, outv, display, index = 0) {
    const input = _.get(cmp, ['refs', 'input', index], {});
    input.value = inv;
    input.dispatchEvent(new Event('blur'));
    assert.strictEqual(cmp.getValueAt(index), outv);
    assert.strictEqual(input.value, display);
  },
  onNext
};
export default Harness;
<|endoftext|>


<commit_before>'use strict';

const Wappalyzer = require('./wappalyzer');
const request = require('request');
const url = require('url');
const fs = require('fs');
const Browser = require('zombie');

const json = JSON.parse(fs.readFileSync(__dirname + '/apps.json'));

class Driver {
  constructor(pageUrl, options) {
    this.options = Object.assign({}, {
      debug: false,
      delay: 500,
      maxDepth: 3,
      maxUrls: 10,
      maxWait: 1000,
      recursive: false,
      requestTimeout: 3000,
      userAgent: 'Mozilla/5.0 (compatible; Wappalyzer)',
    }, options || {});

    this.options.debug = Boolean(this.options.debug);
    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.recursive = Boolean(this.options.recursive);
    this.options.requestTimeout = parseInt(this.options.requestTimeout, 10);

    this.origPageUrl = url.parse(pageUrl);
    this.analyzedPageUrls = [];
    this.apps = [];
    this.meta = {};

    this.wappalyzer = new Wappalyzer();

    this.wappalyzer.apps = json.apps;
    this.wappalyzer.categories = json.categories;

    this.wappalyzer.parseJsPatterns();

    this.wappalyzer.driver.log = (message, source, type) => this.log(message, source, type);
    this.wappalyzer.driver.displayApps = (detected, meta, context) => this.displayApps(detected, meta, context);
  }

  analyze() {
    this.time = {
      start: new Date().getTime(),
      last: new Date().getTime(),
    }

    return this.crawl(this.origPageUrl);
  }

  log(message, source, type) {
    this.options.debug && console.log('[wappalyzer ' + type + ']', '[' + source + ']', message);
  }

  displayApps(detected, meta) {
    this.timer('displayApps');

    this.meta = meta;

    Object.keys(detected).forEach(appName => {
      const app = detected[appName];

      var categories = [];

      app.props.cats.forEach(id => {
        var category = {};

        category[id] = json.categories[id].name;

        categories.push(category)
      });

      if ( !this.apps.some(detectedApp => detectedApp.name === app.name) ) {
        this.apps.push({
          name: app.name,
          confidence: app.confidenceTotal.toString(),
          version: app.version,
          icon: app.props.icon || 'default.svg',
          website: app.props.website,
          categories
        });
      }
    });
  }

  fetch(pageUrl, index, depth) {
    this.timer('fetch');

    return new Promise(resolve => {
      // Return when the URL is a duplicate or maxUrls has been reached
      if ( this.analyzedPageUrls.indexOf(pageUrl.href) !== -1 || this.analyzedPageUrls.length >= this.options.maxUrls ) {
        return resolve();
      }

      this.analyzedPageUrls.push(pageUrl.href);

      this.wappalyzer.log('depth: ' + depth + '; delay: ' + ( this.options.delay * index ) + 'ms; url: ' + pageUrl.href, 'driver');

      const browser = new Browser({
        silent: true,
        userAgent: this.options.userAgent,
        waitDuration: this.options.maxWait,
      });

      this.sleep(this.options.delay * index)
        .then(() => {
          this.timer('browser.visit start');

          browser.visit(pageUrl.href, this.options.requestTimeout, error => {
            this.timer('browser.visit end');

            if ( !browser.resources['0'] || !browser.resources['0'].response ) {
              this.wappalyzer.log('No response from server', 'browser', 'error');

              return resolve();
            }

            browser.wait(this.options.maxWait, () => {
              this.timer('browser.wait end');

              const headers = this.getHeaders(browser);
              const html    = this.getHtml(browser);
              const scripts = this.getScripts(browser);
              const js      = this.getJs(browser);

              this.wappalyzer.analyze(pageUrl, {
                headers,
                html,
                js,
                scripts
              });

              const links = browser.body.getElementsByTagName('a');

              resolve(links);
            });
          });
        });
    });
  }

  getHeaders(browser) {
    const headers = {};

    browser.resources['0'].response.headers._headers.forEach(header => {
      if ( !headers[header[0]] ){
        headers[header[0]] = [];
      }

      headers[header[0]].push(header[1]);
    });

    return headers;
  }

  getHtml(browser) {
    let html = '';

    try {
      html = browser.html();
    } catch ( e ) {
      this.wappalyzer.log(error.message, 'browser', 'error');
    }

    return html;
  }

  getScripts(browser) {
    const scripts = Array.prototype.slice
      .apply(browser.document.scripts)
      .filter(s => s.src)
      .map(s => s.src);

    return scripts;
  }

  getJs(browser) {
    const patterns = this.wappalyzer.jsPatterns;
    const js = {};

    Object.keys(patterns).forEach(appName => {
      js[appName] = {};

      Object.keys(patterns[appName]).forEach(chain => {
        js[appName][chain] = {};

        patterns[appName][chain].forEach((pattern, index) => {
          const properties = chain.split('.');

          let value = properties.reduce((parent, property) => {
            return parent && parent.hasOwnProperty(property) ? parent[property] : null;
          }, browser.window);

          value = typeof value === 'string' ? value : !!value;

          if ( value ) {
            js[appName][chain][index] = value;
          }
        });
      });
    });

    return js;
  }

  crawl(pageUrl, index = 1, depth = 1) {
    this.timer('crawl');

    return new Promise(resolve => {
      this.fetch(pageUrl, index, depth)
        .then(links => {
          if ( links && Boolean(this.options.recursive) && depth < this.options.maxDepth ) {
            links = Array.from(links)
              .filter(link => link.hostname === this.origPageUrl.hostname)
              .map(link => { link.hash = ''; return link });

            return Promise.all(links.map((link, index) => this.crawl(link, index + 1, depth + 1)));
          } else {
            return Promise.resolve();
          }
        })
        .then(() => {
          this.timer('done');

          resolve({
            applications: this.apps,
            meta: this.meta
          });
        });
    });
  }

  sleep(ms) {
    return ms ? new Promise(resolve => setTimeout(resolve, ms)) : Promise.resolve();
  }

  timer(step) {
    const time = new Date().getTime();
    const sinceStart = ( Math.round(( time - this.time.start ) / 10) / 100) + 's';
    const sinceLast = ( Math.round(( time - this.time.last ) / 10) / 100) + 's';

    this.wappalyzer.log('[' + step + '] Time lapsed: ' + sinceLast + ' / ' + sinceStart, 'driver');

    this.time.last = time;
  }
};

module.exports = Driver;
<commit_msg>NPM driver: skip non-HTML pages, return analyzed URLs<commit_after>'use strict';

const Wappalyzer = require('./wappalyzer');
const request = require('request');
const url = require('url');
const fs = require('fs');
const Browser = require('zombie');

const json = JSON.parse(fs.readFileSync(__dirname + '/apps.json'));

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

class Driver {
  constructor(pageUrl, options) {
    this.options = Object.assign({}, {
      debug: false,
      delay: 500,
      maxDepth: 3,
      maxUrls: 10,
      maxWait: 1000,
      recursive: false,
      requestTimeout: 3000,
      userAgent: 'Mozilla/5.0 (compatible; Wappalyzer)',
    }, options || {});

    this.options.debug = Boolean(this.options.debug);
    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.recursive = Boolean(this.options.recursive);
    this.options.requestTimeout = parseInt(this.options.requestTimeout, 10);

    this.origPageUrl = url.parse(pageUrl);
    this.analyzedPageUrls = [];
    this.apps = [];
    this.meta = {};

    this.wappalyzer = new Wappalyzer();

    this.wappalyzer.apps = json.apps;
    this.wappalyzer.categories = json.categories;

    this.wappalyzer.parseJsPatterns();

    this.wappalyzer.driver.log = (message, source, type) => this.log(message, source, type);
    this.wappalyzer.driver.displayApps = (detected, meta, context) => this.displayApps(detected, meta, context);
  }

  analyze() {
    this.time = {
      start: new Date().getTime(),
      last: new Date().getTime(),
    }

    return this.crawl(this.origPageUrl);
  }

  log(message, source, type) {
    this.options.debug && console.log('[wappalyzer ' + type + ']', '[' + source + ']', message);
  }

  displayApps(detected, meta) {
    this.timer('displayApps');

    this.meta = meta;

    Object.keys(detected).forEach(appName => {
      const app = detected[appName];

      var categories = [];

      app.props.cats.forEach(id => {
        var category = {};

        category[id] = json.categories[id].name;

        categories.push(category)
      });

      if ( !this.apps.some(detectedApp => detectedApp.name === app.name) ) {
        this.apps.push({
          name: app.name,
          confidence: app.confidenceTotal.toString(),
          version: app.version,
          icon: app.props.icon || 'default.svg',
          website: app.props.website,
          categories
        });
      }
    });
  }

  fetch(pageUrl, index, depth) {
    return new Promise(resolve => {
      // Return when the URL is a duplicate or maxUrls has been reached
      if ( this.analyzedPageUrls.indexOf(pageUrl.href) !== -1 || this.analyzedPageUrls.length >= this.options.maxUrls ) {
        return resolve();
      }

      this.timer('fetch url: ' + pageUrl.pathname + '; depth: ' + depth + '; delay: ' + ( this.options.delay * index ) + 'ms');

      this.analyzedPageUrls.push(pageUrl.href);

      const browser = new Browser({
        silent: true,
        userAgent: this.options.userAgent,
        waitDuration: this.options.maxWait,
      });

      this.sleep(this.options.delay * index)
        .then(() => {
          this.timer('browser.visit start url: ' + pageUrl.pathname);

          browser.visit(pageUrl.href, this.options.requestTimeout, error => {
            this.timer('browser.visit end url: ' + pageUrl.pathname);

            pageUrl.canonical = pageUrl.protocol + '//' + pageUrl.host + pageUrl.pathname;

            if ( !browser.resources['0'] || !browser.resources['0'].response ) {
              this.wappalyzer.log('No response from server', 'browser', 'error');

              return resolve();
            }

            browser.wait(this.options.maxWait, () => {
              this.timer('browser.wait end url: ' + pageUrl.pathname);

              const headers = this.getHeaders(browser);

              const contentType = headers.hasOwnProperty('content-type') ? headers['content-type'].shift() : null;

              if ( !contentType || !/\btext\/html\b/.test(contentType) ) {
                this.wappalyzer.log('Skipping ' + pageUrl.pathname + ' of content type ' + contentType, 'driver');

                this.analyzedPageUrls.splice(this.analyzedPageUrls.indexOf(pageUrl.href), 1);

                return resolve();
              }

              const html    = this.getHtml(browser);
              const scripts = this.getScripts(browser);
              const js      = this.getJs(browser);

              this.wappalyzer.analyze(pageUrl, {
                headers,
                html,
                js,
                scripts
              });

              const links = browser.body.getElementsByTagName('a');

              resolve(links);
            });
          });
        });
    });
  }

  getHeaders(browser) {
    const headers = {};

    browser.resources['0'].response.headers._headers.forEach(header => {
      if ( !headers[header[0]] ){
        headers[header[0]] = [];
      }

      headers[header[0]].push(header[1]);
    });

    return headers;
  }

  getHtml(browser) {
    let html = '';

    try {
      html = browser.html();
    } catch ( e ) {
      this.wappalyzer.log(error.message, 'browser', 'error');
    }

    return html;
  }

  getScripts(browser) {
    const scripts = Array.prototype.slice
      .apply(browser.document.scripts)
      .filter(s => s.src)
      .map(s => s.src);

    return scripts;
  }

  getJs(browser) {
    const patterns = this.wappalyzer.jsPatterns;
    const js = {};

    Object.keys(patterns).forEach(appName => {
      js[appName] = {};

      Object.keys(patterns[appName]).forEach(chain => {
        js[appName][chain] = {};

        patterns[appName][chain].forEach((pattern, index) => {
          const properties = chain.split('.');

          let value = properties.reduce((parent, property) => {
            return parent && parent.hasOwnProperty(property) ? parent[property] : null;
          }, browser.window);

          value = typeof value === 'string' ? value : !!value;

          if ( value ) {
            js[appName][chain][index] = value;
          }
        });
      });
    });

    return js;
  }

  crawl(pageUrl, index = 1, depth = 1) {
    return new Promise(resolve => {
      this.fetch(pageUrl, index, depth)
        .then(links => {
          if ( links && Boolean(this.options.recursive) && depth < this.options.maxDepth ) {
            links = Array.from(links)
              .filter(link => link.hostname === this.origPageUrl.hostname)
              .filter(link => extensions.test(link.pathname))
              .map(link => { link.hash = ''; return link });

            return Promise.all(links.map((link, index) => this.crawl(link, index + 1, depth + 1)));
          } else {
            return Promise.resolve();
          }
        })
        .then(() => {
          resolve({
            urls: this.analyzedPageUrls,
            applications: this.apps,
            meta: this.meta
          });
        });
    });
  }

  sleep(ms) {
    return ms ? new Promise(resolve => setTimeout(resolve, ms)) : Promise.resolve();
  }

  timer(step) {
    const time = new Date().getTime();
    const sinceStart = ( Math.round(( time - this.time.start ) / 10) / 100) + 's';
    const sinceLast = ( Math.round(( time - this.time.last ) / 10) / 100) + 's';

    this.wappalyzer.log('[' + step + '] Time lapsed: ' + sinceLast + ' / ' + sinceStart, 'driver');

    this.time.last = time;
  }
};

module.exports = Driver;
<|endoftext|>


<commit_before>'use strict';

var _ = require('underscore');
var express = require('express');
var http = require('http');
var https = require('https');
var httpProxy = require('http-proxy');
var net = require('net');
var url = require('url');

var ssl = require('./ssl');

module.exports = {

	createAppServer: function(port, host) {

		var app = express();

		app.get('/check', function(req, res) {

			var ipAddress = req.connection.remoteAddress;

			res.status(200).json({
				ipAddress: ipAddress,
				headers: req.headers
			});
		});

		app.http = http.createServer(app).listen(port, host);
		app.https = https.createServer({ cert: ssl.cert, key: ssl.key }, app).listen(port + 1, host);

		return app;
	},

	createProxyServer: function(port, host, options) {

		options || (options = {});
		options = _.defaults(options, { tunnel: true });
		options.localAddress = host;

		if (options.proxyAuth) {
			options.auth = options.proxyAuth;
		}

		var proxy = httpProxy.createProxyServer(_.omit(options, 'tunnel'));

		proxy.http = http.createServer(function(req, res) {

			if (options.proxyAuth && !reqIsAuthenticated(req, options.proxyAuth)) {
				res.writeHead(407);
				return res.end();
			}

			var target = _.omit(url.parse(req.url), 'path');

			proxy.web(req, res, {
				target: target
			});

		}).listen(port, host);

		proxy.https = https.createServer({
			cert: ssl.cert,
			key: ssl.key
		}, function(req, res) {

			if (options.proxyAuth && !reqIsAuthenticated(req, options.proxyAuth)) {
				res.writeHead(407);
				return res.end();
			}

			var target = _.omit(url.parse(req.url), 'path');

			target.https = true;
			target.rejectUnauthorized = false;

			proxy.web(req, res, {
				target: target
			});

		}).listen(port + 1, host);

		if (options.tunnel) {
			var onConnect = _.bind(setupTunnel, undefined, host);
			proxy.http.on('connect', onConnect);
			proxy.https.on('connect', onConnect);
		} else {
			proxy.http.on('connect', methodNotAllowed);
			proxy.https.on('connect', methodNotAllowed);
		}

		return proxy;
	}
};

function methodNotAllowed(req, client, head) {
	client.write('HTTP/' + req.httpVersion + ' 405 Method Not Allowed\r\n\r\n');
	client.end();
}

function setupTunnel(localAddress, req, client, head) {

	var reqUri = url.parse(req.url.indexOf('://') !== -1 ? req.url : 'http://' + req.url);

	var server = net.connect({
		host: reqUri.hostname,
		port: reqUri.port,
		localAddress: localAddress
	}, function() {
		client.write(
			'HTTP/1.1 200 Connection Established\r\n' +
			'Proxy-agent: Node.js-Proxy\r\n' +
			'\r\n'
		);
		server.write(head);
		server.pipe(client);
		client.pipe(server);
	});
}

function reqIsAuthenticated(req, auth) {

	var authHeader = req.headers['proxy-authorization'] || req.headers['authorization'] || null;

	if (!authHeader) {
		return false;
	}

	var parts = (new Buffer(authHeader.substr('Basic '.length), 'base64')).toString().split(':');
	return parts[0] === auth.user && parts[1] === auth.pass;
}
<commit_msg>Fixed deprecated Buffer usage<commit_after>'use strict';

var _ = require('underscore');
var express = require('express');
var http = require('http');
var https = require('https');
var httpProxy = require('http-proxy');
var net = require('net');
var url = require('url');

var ssl = require('./ssl');

module.exports = {

	createAppServer: function(port, host) {

		var app = express();

		app.get('/check', function(req, res) {

			var ipAddress = req.connection.remoteAddress;

			res.status(200).json({
				ipAddress: ipAddress,
				headers: req.headers
			});
		});

		app.http = http.createServer(app).listen(port, host);
		app.https = https.createServer({ cert: ssl.cert, key: ssl.key }, app).listen(port + 1, host);

		return app;
	},

	createProxyServer: function(port, host, options) {

		options || (options = {});
		options = _.defaults(options, { tunnel: true });
		options.localAddress = host;

		if (options.proxyAuth) {
			options.auth = options.proxyAuth;
		}

		var proxy = httpProxy.createProxyServer(_.omit(options, 'tunnel'));

		proxy.http = http.createServer(function(req, res) {

			if (options.proxyAuth && !reqIsAuthenticated(req, options.proxyAuth)) {
				res.writeHead(407);
				return res.end();
			}

			var target = _.omit(url.parse(req.url), 'path');

			proxy.web(req, res, {
				target: target
			});

		}).listen(port, host);

		proxy.https = https.createServer({
			cert: ssl.cert,
			key: ssl.key
		}, function(req, res) {

			if (options.proxyAuth && !reqIsAuthenticated(req, options.proxyAuth)) {
				res.writeHead(407);
				return res.end();
			}

			var target = _.omit(url.parse(req.url), 'path');

			target.https = true;
			target.rejectUnauthorized = false;

			proxy.web(req, res, {
				target: target
			});

		}).listen(port + 1, host);

		if (options.tunnel) {
			var onConnect = _.bind(setupTunnel, undefined, host);
			proxy.http.on('connect', onConnect);
			proxy.https.on('connect', onConnect);
		} else {
			proxy.http.on('connect', methodNotAllowed);
			proxy.https.on('connect', methodNotAllowed);
		}

		return proxy;
	}
};

function methodNotAllowed(req, client, head) {
	client.write('HTTP/' + req.httpVersion + ' 405 Method Not Allowed\r\n\r\n');
	client.end();
}

function setupTunnel(localAddress, req, client, head) {

	var reqUri = url.parse(req.url.indexOf('://') !== -1 ? req.url : 'http://' + req.url);

	var server = net.connect({
		host: reqUri.hostname,
		port: reqUri.port,
		localAddress: localAddress
	}, function() {
		client.write(
			'HTTP/1.1 200 Connection Established\r\n' +
			'Proxy-agent: Node.js-Proxy\r\n' +
			'\r\n'
		);
		server.write(head);
		server.pipe(client);
		client.pipe(server);
	});
}

function reqIsAuthenticated(req, auth) {

	var authHeader = req.headers['proxy-authorization'] || req.headers['authorization'] || null;

	if (!authHeader) {
		return false;
	}

	var parts = Buffer.from(authHeader.substr('Basic '.length), 'base64').toString().split(':');
	return parts[0] === auth.user && parts[1] === auth.pass;
}
<|endoftext|>


<commit_before>import fs from 'fs-extra';
import path from 'path';
import temp from 'temp';

import React from 'react';
import ReactDom from 'react-dom';
import sinon from 'sinon';

import Repository from '../lib/models/repository';
import GitShellOutStrategy from '../lib/git-shell-out-strategy';
import WorkerManager from '../lib/worker-manager';
import ContextMenuInterceptor from '../lib/context-menu-interceptor';

assert.autocrlfEqual = (actual, expected, ...args) => {
  const newActual = actual.replace(/\r\n/g, '\n');
  const newExpected = expected.replace(/\r\n/g, '\n');
  return assert.equal(newActual, newExpected, ...args);
};

// cloning a repo into a folder and then copying it
// for each subsequent request to clone makes cloning
// 2-3x faster on macOS and 5-10x faster on Windows
const cachedClonedRepos = {};
function copyCachedRepo(repoName) {
  const workingDirPath = temp.mkdirSync('git-fixture-');
  fs.copySync(cachedClonedRepos[repoName], workingDirPath);
  return fs.realpathSync(workingDirPath);
}

export async function cloneRepository(repoName = 'three-files') {
  if (!cachedClonedRepos[repoName]) {
    const cachedPath = temp.mkdirSync('git-fixture-cache-');
    const git = new GitShellOutStrategy(cachedPath);
    await git.clone(path.join(__dirname, 'fixtures', `repo-${repoName}`, 'dot-git'), {noLocal: true});
    await git.exec(['config', '--local', 'core.autocrlf', 'false']);
    await git.exec(['config', '--local', 'commit.gpgsign', 'false']);
    await git.exec(['config', '--local', 'user.email', 'nope@nah.com']);
    await git.exec(['config', '--local', 'user.name', 'Someone']);
    await git.exec(['checkout', '--', '.']); // discard \r in working directory
    cachedClonedRepos[repoName] = cachedPath;
  }
  return copyCachedRepo(repoName);
}

export async function sha(directory) {
  const git = new GitShellOutStrategy(directory);
  const head = await git.getHeadCommit();
  return head.sha;
}

/*
 * Initialize an empty repository at a temporary path.
 */
export async function initRepository(repoName) {
  const workingDirPath = temp.mkdirSync('git-fixture-');
  const git = new GitShellOutStrategy(workingDirPath);
  await git.exec(['init']);
  return fs.realpathSync(workingDirPath);
}

export async function setUpLocalAndRemoteRepositories(repoName = 'multiple-commits', options = {}) {
  /* eslint-disable no-param-reassign */
  if (typeof repoName === 'object') {
    options = repoName;
    repoName = 'multiple-commits';
  }
  /* eslint-enable no-param-reassign */
  const baseRepoPath = await cloneRepository(repoName);
  const baseGit = new GitShellOutStrategy(baseRepoPath);

  // create remote bare repo with all commits
  const remoteRepoPath = temp.mkdirSync('git-remote-fixture-');
  const remoteGit = new GitShellOutStrategy(remoteRepoPath);
  await remoteGit.clone(baseRepoPath, {noLocal: true, bare: true});

  // create local repo with one fewer commit
  if (options.remoteAhead) { await baseGit.exec(['reset', 'HEAD~']); }
  const localRepoPath = temp.mkdirSync('git-local-fixture-');
  const localGit = new GitShellOutStrategy(localRepoPath);
  await localGit.clone(baseRepoPath, {noLocal: true});
  await localGit.exec(['remote', 'set-url', 'origin', remoteRepoPath]);
  await localGit.exec(['config', '--local', 'commit.gpgsign', 'false']);
  return {baseRepoPath, remoteRepoPath, localRepoPath};
}

export async function getHeadCommitOnRemote(remotePath) {
  const workingDirPath = temp.mkdirSync('git-fixture-');
  const git = new GitShellOutStrategy(workingDirPath);
  await git.clone(remotePath, {noLocal: true});
  return git.getHeadCommit();
}

export async function buildRepository(workingDirPath) {
  const repository = new Repository(workingDirPath);
  await repository.getLoadPromise();
  // eslint-disable-next-line jasmine/no-global-setup
  afterEach(async () => {
    const repo = await repository;
    repo && repo.destroy();
  });
  return repository;
}

export function assertDeepPropertyVals(actual, expected) {
  function extractObjectSubset(actualValue, expectedValue) {
    if (actualValue !== Object(actualValue)) { return actualValue; }

    const actualSubset = Array.isArray(actualValue) ? [] : {};
    for (const key of Object.keys(expectedValue)) {
      if (actualValue.hasOwnProperty(key)) {
        actualSubset[key] = extractObjectSubset(actualValue[key], expectedValue[key]);
      }
    }

    return actualSubset;
  }

  assert.deepEqual(extractObjectSubset(actual, expected), expected);
}

export function assertEqualSets(a, b) {
  assert.equal(a.size, b.size, 'Sets are a different size');
  a.forEach(item => assert(b.has(item), 'Sets have different elements'));
}

export function assertEqualSortedArraysByKey(arr1, arr2, key) {
  const sortFn = (a, b) => a[key] < b[key];
  assert.deepEqual(arr1.sort(sortFn), arr2.sort(sortFn));
}

let activeRenderers = [];
export function createRenderer() {
  let instance;
  let lastInstance;
  let node = document.createElement('div');
  // ref function should be reference equal over renders to avoid React
  // calling the "old" one with `null` and the "new" one with the instance
  const setTopLevelRef = c => { instance = c; };
  const renderer = {
    render(appWithoutRef) {
      lastInstance = instance;
      const app = React.cloneElement(appWithoutRef, {ref: setTopLevelRef});
      ReactDom.render(app, node);
    },

    get instance() {
      return instance;
    },

    get lastInstance() {
      return lastInstance;
    },

    get node() {
      return node;
    },

    unmount() {
      if (node) {
        lastInstance = instance;
        ReactDom.unmountComponentAtNode(node);
        node = null;
      }
    },
  };
  activeRenderers.push(renderer);
  return renderer;
}

export function isProcessAlive(pid) {
  if (typeof pid !== 'number') {
    throw new Error(`PID must be a number. Got ${pid}`);
  }
  let alive = true;
  try {
    return process.kill(pid, 0);
  } catch (e) {
    alive = false;
  }
  return alive;
}

// eslint-disable-next-line jasmine/no-global-setup
beforeEach(function() {
  global.sinon = sinon.sandbox.create();
});

// eslint-disable-next-line jasmine/no-global-setup
afterEach(function() {
  activeRenderers.forEach(r => r.unmount());
  activeRenderers = [];

  ContextMenuInterceptor.dispose();

  global.sinon.restore();
});

// eslint-disable-next-line jasmine/no-global-setup
after(() => {
  if (!process.env.ATOM_GITHUB_SHOW_RENDERER_WINDOW) {
    WorkerManager.reset(true);
  }
});
<commit_msg>Set git config in a few other cases<commit_after>import fs from 'fs-extra';
import path from 'path';
import temp from 'temp';

import React from 'react';
import ReactDom from 'react-dom';
import sinon from 'sinon';

import Repository from '../lib/models/repository';
import GitShellOutStrategy from '../lib/git-shell-out-strategy';
import WorkerManager from '../lib/worker-manager';
import ContextMenuInterceptor from '../lib/context-menu-interceptor';

assert.autocrlfEqual = (actual, expected, ...args) => {
  const newActual = actual.replace(/\r\n/g, '\n');
  const newExpected = expected.replace(/\r\n/g, '\n');
  return assert.equal(newActual, newExpected, ...args);
};

// cloning a repo into a folder and then copying it
// for each subsequent request to clone makes cloning
// 2-3x faster on macOS and 5-10x faster on Windows
const cachedClonedRepos = {};
function copyCachedRepo(repoName) {
  const workingDirPath = temp.mkdirSync('git-fixture-');
  fs.copySync(cachedClonedRepos[repoName], workingDirPath);
  return fs.realpathSync(workingDirPath);
}

export async function cloneRepository(repoName = 'three-files') {
  if (!cachedClonedRepos[repoName]) {
    const cachedPath = temp.mkdirSync('git-fixture-cache-');
    const git = new GitShellOutStrategy(cachedPath);
    await git.clone(path.join(__dirname, 'fixtures', `repo-${repoName}`, 'dot-git'), {noLocal: true});
    await git.exec(['config', '--local', 'core.autocrlf', 'false']);
    await git.exec(['config', '--local', 'commit.gpgsign', 'false']);
    await git.exec(['config', '--local', 'user.email', 'nope@nah.com']);
    await git.exec(['config', '--local', 'user.name', 'Someone']);
    await git.exec(['checkout', '--', '.']); // discard \r in working directory
    cachedClonedRepos[repoName] = cachedPath;
  }
  return copyCachedRepo(repoName);
}

export async function sha(directory) {
  const git = new GitShellOutStrategy(directory);
  const head = await git.getHeadCommit();
  return head.sha;
}

/*
 * Initialize an empty repository at a temporary path.
 */
export async function initRepository(repoName) {
  const workingDirPath = temp.mkdirSync('git-fixture-');
  const git = new GitShellOutStrategy(workingDirPath);
  await git.exec(['init']);
  await git.exec(['config', '--local', 'user.email', 'nope@nah.com']);
  await git.exec(['config', '--local', 'user.name', 'Someone']);
  return fs.realpathSync(workingDirPath);
}

export async function setUpLocalAndRemoteRepositories(repoName = 'multiple-commits', options = {}) {
  /* eslint-disable no-param-reassign */
  if (typeof repoName === 'object') {
    options = repoName;
    repoName = 'multiple-commits';
  }
  /* eslint-enable no-param-reassign */
  const baseRepoPath = await cloneRepository(repoName);
  const baseGit = new GitShellOutStrategy(baseRepoPath);

  // create remote bare repo with all commits
  const remoteRepoPath = temp.mkdirSync('git-remote-fixture-');
  const remoteGit = new GitShellOutStrategy(remoteRepoPath);
  await remoteGit.clone(baseRepoPath, {noLocal: true, bare: true});

  // create local repo with one fewer commit
  if (options.remoteAhead) { await baseGit.exec(['reset', 'HEAD~']); }
  const localRepoPath = temp.mkdirSync('git-local-fixture-');
  const localGit = new GitShellOutStrategy(localRepoPath);
  await localGit.clone(baseRepoPath, {noLocal: true});
  await localGit.exec(['remote', 'set-url', 'origin', remoteRepoPath]);
  await localGit.exec(['config', '--local', 'commit.gpgsign', 'false']);
  await localGit.exec(['config', '--local', 'user.email', 'nope@nah.com']);
  await localGit.exec(['config', '--local', 'user.name', 'Someone']);
  return {baseRepoPath, remoteRepoPath, localRepoPath};
}

export async function getHeadCommitOnRemote(remotePath) {
  const workingDirPath = temp.mkdirSync('git-fixture-');
  const git = new GitShellOutStrategy(workingDirPath);
  await git.clone(remotePath, {noLocal: true});
  return git.getHeadCommit();
}

export async function buildRepository(workingDirPath) {
  const repository = new Repository(workingDirPath);
  await repository.getLoadPromise();
  // eslint-disable-next-line jasmine/no-global-setup
  afterEach(async () => {
    const repo = await repository;
    repo && repo.destroy();
  });
  return repository;
}

export function assertDeepPropertyVals(actual, expected) {
  function extractObjectSubset(actualValue, expectedValue) {
    if (actualValue !== Object(actualValue)) { return actualValue; }

    const actualSubset = Array.isArray(actualValue) ? [] : {};
    for (const key of Object.keys(expectedValue)) {
      if (actualValue.hasOwnProperty(key)) {
        actualSubset[key] = extractObjectSubset(actualValue[key], expectedValue[key]);
      }
    }

    return actualSubset;
  }

  assert.deepEqual(extractObjectSubset(actual, expected), expected);
}

export function assertEqualSets(a, b) {
  assert.equal(a.size, b.size, 'Sets are a different size');
  a.forEach(item => assert(b.has(item), 'Sets have different elements'));
}

export function assertEqualSortedArraysByKey(arr1, arr2, key) {
  const sortFn = (a, b) => a[key] < b[key];
  assert.deepEqual(arr1.sort(sortFn), arr2.sort(sortFn));
}

let activeRenderers = [];
export function createRenderer() {
  let instance;
  let lastInstance;
  let node = document.createElement('div');
  // ref function should be reference equal over renders to avoid React
  // calling the "old" one with `null` and the "new" one with the instance
  const setTopLevelRef = c => { instance = c; };
  const renderer = {
    render(appWithoutRef) {
      lastInstance = instance;
      const app = React.cloneElement(appWithoutRef, {ref: setTopLevelRef});
      ReactDom.render(app, node);
    },

    get instance() {
      return instance;
    },

    get lastInstance() {
      return lastInstance;
    },

    get node() {
      return node;
    },

    unmount() {
      if (node) {
        lastInstance = instance;
        ReactDom.unmountComponentAtNode(node);
        node = null;
      }
    },
  };
  activeRenderers.push(renderer);
  return renderer;
}

export function isProcessAlive(pid) {
  if (typeof pid !== 'number') {
    throw new Error(`PID must be a number. Got ${pid}`);
  }
  let alive = true;
  try {
    return process.kill(pid, 0);
  } catch (e) {
    alive = false;
  }
  return alive;
}

// eslint-disable-next-line jasmine/no-global-setup
beforeEach(function() {
  global.sinon = sinon.sandbox.create();
});

// eslint-disable-next-line jasmine/no-global-setup
afterEach(function() {
  activeRenderers.forEach(r => r.unmount());
  activeRenderers = [];

  ContextMenuInterceptor.dispose();

  global.sinon.restore();
});

// eslint-disable-next-line jasmine/no-global-setup
after(() => {
  if (!process.env.ATOM_GITHUB_SHOW_RENDERER_WINDOW) {
    WorkerManager.reset(true);
  }
});
<|endoftext|>


<commit_before>/* eslint-disable no-param-reassign */
const Event = function Event(config) {
  const overlapRule = { ALLOW: 'allow', TRIM: 'trim', REMOVE: 'remove', SPLIT: 'split' };
  const id = config.id || 0;
  const title = config.title;
  const color = config.color || '#000000';
  const note = config.note || '';
  const since = config.since;
  const till = config.till || config.since;
  const repeats = config.repeats || [];
  const sequels = config.sequels || [];
  const virtual = config.virtual || false;
  const priority = config.priority || 0;
  const overlap = config.overlap || {};
  overlap.internal = overlap.internal || overlapRule.ALLOW;
  overlap.external = overlap.external || overlapRule.ALLOW;

  const offsetDate = (date, scale, step) => {
    switch (scale) {
      case 'year': {
        return date.offsetYear(step);
      }
      case 'month': {
        return date.offsetMonth(step);
      }
      case 'day': {
        return date.offsetDay(step);
      }
      default: {
        throw new Error('Invalid scale string for offsetDate method');
      }
    }
  };

  const getRepeats = (_since, _till) => {
    const qSince = (_till.int() <= _since.int()) ? _till : _since;
    const qTill = (_since.int() >= _till.int()) ? _since : _till;
    let events = [];

    repeats.forEach((pattern, index) => {
      let times = pattern.times;
      let round = 1;
      let cursor = {
        since: offsetDate(since, pattern.cycle, pattern.step),
        till: offsetDate(till, pattern.cycle, pattern.step),
      };
      while (times !== 0 && cursor.since.int() <= qTill.int()) {
        if (cursor.since.int() >= qSince.int()) {
          events = events.concat((new Event({
            id,
            virtual: true,
            priority: priority + (round * (index + 1) * (sequels.length + 1)),
            title,
            color,
            note,
            sequels,
            since: cursor.since,
            till: cursor.till,
          })).query(qSince, qTill));
        }
        cursor = {
          since: offsetDate(cursor.since, pattern.cycle, pattern.step),
          till: offsetDate(cursor.till, pattern.cycle, pattern.step),
        };
        round += 1;
        times -= 1;
      }
    });
    return events;
  };

  const getSequels = (_since, _till) => {
    const qSince = (_till.int() <= _since.int()) ? _till : _since;
    const qTill = (_since.int() >= _till.int()) ? _since : _till;
    let events = [];

    sequels.forEach((sequel, index) => {
      const sequelDates = {
        since: offsetDate(since, sequel.since.scale, sequel.since.offset),
        till: offsetDate(since, sequel.till.scale, sequel.till.offset),
      };
      const realSequel = new Event({
        id,
        virtual: true,
        priority: priority + (index + 1),
        title: sequel.title || title,
        note: sequel.note || note,
        color: sequel.color || color,
        since: sequelDates.since,
        till: sequelDates.till,
      });
      events = events.concat((realSequel).query(qSince, qTill));
    });
    return events;
  };

  const includes = (event, _since, _till) => {
    const qEvent = event || { since, till };
    const qSince = (_till.int() <= _since.int()) ? _till : _since;
    const qTill = (_since.int() >= _till.int()) ? _since : _till;
    let collides = [];
    if (qSince.int() <= qEvent.since.int() && qTill.int() >= qEvent.since.int()) {
      collides = collides.concat(['l']);
    }
    if (qSince.int() <= qEvent.till.int() && qTill.int() >= qEvent.till.int()) {
      collides = collides.concat(['r']);
    }
    if (collides.length) return collides;
    return false;
  };

  const handleOverlaps = (_events, _since, _till) => {
    let events = _events;
    switch (overlap.internal) {
      case overlapRule.ALLOW: {
        break;
      }
      case overlapRule.REMOVE: {
        events = events.reduce((evts, event) => {
          const parallel = evts.find(evt => evt.virtual && includes(event, evt.since, evt.till));
          if (!parallel) return (evts = evts.concat([event]));
          const items = evts.filter(evt => !(evt.virtual && includes(event, evt.since, evt.till)));
          const winner = (parallel.priority > event.priority) ? parallel : event;
          evts = items.concat([winner]);
          return evts;
        }, []);
        break;
      }
      case overlapRule.TRIM: {
        events = events.reduce((evts, event) => {
          const parallel = evts.find(evt => evt.virtual && includes(event, evt.since, evt.till));
          if (!parallel) return (evts = evts.concat([event]));
          const items = evts.filter(evt => !(evt.virtual && includes(event, evt.since, evt.till)));
          let strong;
          let weak;
          if (parallel.priority > event.priority) {
            strong = parallel;
            weak = event;
          } else {
            strong = event;
            weak = parallel;
          }
          evts = items.concat([strong]);
          for (let i = 1; i <= 2; i += 1) {
            const collision = includes(weak, strong.since, strong.till);
            if (!collision) break;
            if (collision.includes('r')) {
              weak = (new Event({
                id,
                virtual: true,
                priority: weak.priority,
                title: weak.title,
                note: weak.note,
                color: weak.color,
                since: weak.since,
                till: strong.since.offsetDay(-1),
              })).query(_since, _till)[0];
            } else if (collision.includes('l')) {
              weak = (new Event({
                id,
                virtual: true,
                priority: weak.priority,
                title: weak.title,
                note: weak.note,
                color: weak.color,
                since: strong.till.offsetDay(1),
                till: weak.till,
              })).query(_since, _till)[0];
            }
          }
          if (weak.till.int() - weak.since.int() >= 0) {
            evts = evts.concat([weak]);
          }
          return evts;
        }, []);
        break;
      }
      case overlapRule.SPLIT: {
        // TODO
        break;
      }
      default:
        break;
    }

    return events;
  };

  const query = (_since, _till) => {
    let events = [];
    events = events.concat(includes(undefined, _since, _till) ? [
      {
        id,
        virtual,
        title,
        priority,
        color,
        note,
        since,
        till,
        sinceInt: since.int(),
        tillInt: till.int(),
      },
    ] : []);
    events = events.concat(getSequels(_since, _till));
    events = events.concat(getRepeats(_since, _till));

    events = handleOverlaps(events, _since, _till);

    return events.sort((a, b) => a.sinceInt - b.sinceInt);
  };

  return { title, color, note, since, till, overlap: overlap.external, query };
};

exports.Event = Event;
<commit_msg>removed redundant priority property from query method return value<commit_after>/* eslint-disable no-param-reassign */
const Event = function Event(config) {
  const overlapRule = { ALLOW: 'allow', TRIM: 'trim', REMOVE: 'remove', SPLIT: 'split' };
  const id = config.id || 0;
  const title = config.title;
  const color = config.color || '#000000';
  const note = config.note || '';
  const since = config.since;
  const till = config.till || config.since;
  const repeats = config.repeats || [];
  const sequels = config.sequels || [];
  const virtual = config.virtual || false;
  const priority = config.priority || 0;
  const overlap = config.overlap || {};
  overlap.internal = overlap.internal || overlapRule.ALLOW;
  overlap.external = overlap.external || overlapRule.ALLOW;

  const offsetDate = (date, scale, step) => {
    switch (scale) {
      case 'year': {
        return date.offsetYear(step);
      }
      case 'month': {
        return date.offsetMonth(step);
      }
      case 'day': {
        return date.offsetDay(step);
      }
      default: {
        throw new Error('Invalid scale string for offsetDate method');
      }
    }
  };

  const getRepeats = (_since, _till) => {
    const qSince = (_till.int() <= _since.int()) ? _till : _since;
    const qTill = (_since.int() >= _till.int()) ? _since : _till;
    let events = [];

    repeats.forEach((pattern, index) => {
      let times = pattern.times;
      let round = 1;
      let cursor = {
        since: offsetDate(since, pattern.cycle, pattern.step),
        till: offsetDate(till, pattern.cycle, pattern.step),
      };
      while (times !== 0 && cursor.since.int() <= qTill.int()) {
        if (cursor.since.int() >= qSince.int()) {
          events = events.concat((new Event({
            id,
            virtual: true,
            priority: priority + (round * (index + 1) * (sequels.length + 1)),
            title,
            color,
            note,
            sequels,
            since: cursor.since,
            till: cursor.till,
          })).query(qSince, qTill));
        }
        cursor = {
          since: offsetDate(cursor.since, pattern.cycle, pattern.step),
          till: offsetDate(cursor.till, pattern.cycle, pattern.step),
        };
        round += 1;
        times -= 1;
      }
    });
    return events;
  };

  const getSequels = (_since, _till) => {
    const qSince = (_till.int() <= _since.int()) ? _till : _since;
    const qTill = (_since.int() >= _till.int()) ? _since : _till;
    let events = [];

    sequels.forEach((sequel, index) => {
      const sequelDates = {
        since: offsetDate(since, sequel.since.scale, sequel.since.offset),
        till: offsetDate(since, sequel.till.scale, sequel.till.offset),
      };
      const realSequel = new Event({
        id,
        virtual: true,
        priority: priority + (index + 1),
        title: sequel.title || title,
        note: sequel.note || note,
        color: sequel.color || color,
        since: sequelDates.since,
        till: sequelDates.till,
      });
      events = events.concat((realSequel).query(qSince, qTill));
    });
    return events;
  };

  const includes = (event, _since, _till) => {
    const qEvent = event || { since, till };
    const qSince = (_till.int() <= _since.int()) ? _till : _since;
    const qTill = (_since.int() >= _till.int()) ? _since : _till;
    let collides = [];
    if (qSince.int() <= qEvent.since.int() && qTill.int() >= qEvent.since.int()) {
      collides = collides.concat(['l']);
    }
    if (qSince.int() <= qEvent.till.int() && qTill.int() >= qEvent.till.int()) {
      collides = collides.concat(['r']);
    }
    if (collides.length) return collides;
    return false;
  };

  const handleOverlaps = (_events, _since, _till) => {
    let events = _events;
    switch (overlap.internal) {
      case overlapRule.ALLOW: {
        break;
      }
      case overlapRule.REMOVE: {
        events = events.reduce((evts, event) => {
          const parallel = evts.find(evt => evt.virtual && includes(event, evt.since, evt.till));
          if (!parallel) return (evts = evts.concat([event]));
          const items = evts.filter(evt => !(evt.virtual && includes(event, evt.since, evt.till)));
          const winner = (parallel.priority > event.priority) ? parallel : event;
          evts = items.concat([winner]);
          return evts;
        }, []);
        break;
      }
      case overlapRule.TRIM: {
        events = events.reduce((evts, event) => {
          const parallel = evts.find(evt => evt.virtual && includes(event, evt.since, evt.till));
          if (!parallel) return (evts = evts.concat([event]));
          const items = evts.filter(evt => !(evt.virtual && includes(event, evt.since, evt.till)));
          let strong;
          let weak;
          if (parallel.priority > event.priority) {
            strong = parallel;
            weak = event;
          } else {
            strong = event;
            weak = parallel;
          }
          evts = items.concat([strong]);
          for (let i = 1; i <= 2; i += 1) {
            const collision = includes(weak, strong.since, strong.till);
            if (!collision) break;
            if (collision.includes('r')) {
              weak = (new Event({
                id,
                virtual: true,
                priority: weak.priority,
                title: weak.title,
                note: weak.note,
                color: weak.color,
                since: weak.since,
                till: strong.since.offsetDay(-1),
              })).query(_since, _till)[0];
            } else if (collision.includes('l')) {
              weak = (new Event({
                id,
                virtual: true,
                priority: weak.priority,
                title: weak.title,
                note: weak.note,
                color: weak.color,
                since: strong.till.offsetDay(1),
                till: weak.till,
              })).query(_since, _till)[0];
            }
          }
          if (weak.till.int() - weak.since.int() >= 0) {
            evts = evts.concat([weak]);
          }
          return evts;
        }, []);
        break;
      }
      case overlapRule.SPLIT: {
        // TODO
        break;
      }
      default:
        break;
    }

    return events;
  };

  const query = (_since, _till) => {
    let events = [];
    events = events.concat(includes(undefined, _since, _till) ? [
      {
        id,
        virtual,
        title,
        color,
        note,
        since,
        till,
        sinceInt: since.int(),
        tillInt: till.int(),
      },
    ] : []);
    events = events.concat(getSequels(_since, _till));
    events = events.concat(getRepeats(_since, _till));

    events = handleOverlaps(events, _since, _till);

    return events.sort((a, b) => a.sinceInt - b.sinceInt);
  };

  return { title, color, note, since, till, overlap: overlap.external, query };
};

exports.Event = Event;
<|endoftext|>