<commit_before>(function(root) {

  'use strict';

  var availity = root.availity;

  availity.ui.controller('AvValFieldController', function($element, avValAdapter, $attrs, avVal, $log, $timeout, $scope, $sniffer) {

    this.ngModel = null;
    this.rule = null;
    this.avValForm = null;
    this.avValInvalid = false;

    var self = this;

    var placeholder = $element[0].placeholder;
    var noEvent = {};

    this.createId = function() {
      this.ngModel.avId = availity.uuid('avVal');
    };

    this.setNgModel = function(ngModel) {
      this.ngModel = ngModel;
    };

    this.setRule = function(rule) {
      this.rule = rule;
    };

    this.avValForm = function(avValForm) {
      this.avValForm = avValForm;
    };

    this.updateModel = function(results) {

      var self = this;
      var validationKeys = [];

      this.ngModel.avResults = results;

      // set state for each violation
      angular.forEach(results.violations, function (result) {
        var key = 'av-' + result.contraintName;
        validationKeys.push(key);
        self.ngModel.$setValidity(key, result.valid);
      });

      // set overall state for validation state
      this.ngModel.$setValidity('av', this.ngModel.avResults.isValid);

      // store violations
      this.ngModel.avViolations = this.ngModel.avResults.violations;

      // record the id and violation count in the av-form controller.  this determines if the form is
      // valid if sum of violations for all form inputs === zero
      this.avValForm.record(this.ngModel.avId, this.ngModel.avResults.violations.length);

      // remove violation keys that are no longer falsy
      angular.forEach(this.ngModel.$error, function(value, key) {

        if(_.indexOf(validationKeys, key) === -1 && key.lastIndexOf('av-', 0) === 0) {
          self.ngModel.$setValidity(key, true);
        }
      });
    };

    this.updateView = function() {
      if(this.ngModel.$dirty || $scope.avValShow) {
        avValAdapter.element($element, this.ngModel, this.ngModel.avResults.isValid);
        avValAdapter.message($element, this.ngModel);
      }
    };

    this.validate = function(value) {

      $log.info('validating value [' + value + ']');

      var rulesKey = self.avValForm.rulesKey;
      var results = avVal.validate(rulesKey, $element, value, self.rule);

      // validate function is called within the context of angular so fn.call and set the context
      // to "this"
      if(results) {
        self.updateModel.call(self, results);
        self.updateView.call(self);
      }

      return results;
    };

    this.validateModel = function(value) {

      self.validate(value, true);
      return value;

    };

    this.validateView = function(value) {

      var results = self.validate(value);

      if(self.avValForm.avValInvalid || self.avValInvalid) {
        // allows invalid data from view to update model for dirty saving
        return value;
      }

      // prevent invalid data from view to update model
      return results.isValid ? value : undefined;

    };

    this.onDebounce = function() {

      var value = $element.val().trim();

      if(this.isCheckbox()) {
        this.ngModel.$setViewValue($element[0].checked);
      } else if(this.isRadio()) {
        this.ngModel.$setViewValue($attrs.value);
      }else {
        this.ngModel.$setViewValue(value);
      }

    };

    this.isRadio = function() {
      return $element.is('input') && $attrs.type === 'radio';
    };

    this.isCheckbox = function() {
      return $element.is('input') && $attrs.type === 'checkbox';
    };

    this.reset = function() {

      var violations = this.ngModel.avResults.violations;
      violations.splice(0, violations.length);

      avValAdapter.message($element, this.ngModel);
      avValAdapter.reset($element);

    };

    this.event = function(event, avValDebounce) {

      var self = this;

      $element.unbind('input');

      var debounce;

      $element.on(event, function() {

        // https://github.com/angular/angular.js/blob/v1.2.27/src/ng/directive/input.js#L508
        if($sniffer.msie <= 11 && (event || noEvent).type === 'input' && $element[0].placeholder !== placeholder) {
          placeholder = $element[0].placeholder;
          return;
        }

        $timeout.cancel(debounce);
        debounce = $timeout(function() {
          $scope.$apply(function() {
            self.onDebounce();
          });
        }, avValDebounce);

      });

    };

  });

  // Events:
  //
  //  click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown
  //  keyup keypress submit focus blur copy cut paste
  availity.ui.directive('avValField', function($log, $timeout, avVal, avValAdapter, AV_VAL) {
    return {
      restrict: 'A',
      controller: 'AvValFieldController',
      require: ['^avValForm', 'ngModel', 'avValField'],
      scope: {
        avValDebounce: '@?',
        avValOn: '@?',
        avValShow: '=?'
      },
      link: function(scope, element, attrs, controllers) {

        var rule = attrs.avValField;
        var avValForm = controllers[0];
        var ngModel = controllers[1];
        var avValField = controllers[2];

        var avValOn = scope.avValOn || avValForm.avValOn || 'input';

        if(!ngModel && !rule) {
          $log.error('avValField requires ngModel and a validation rule to run.');
          return;
        }

        // Allows fields to update with invalid data for dirty form saving
        avValField.avValInvalid = attrs.avValInvalid || false;

        // CACHE THE MODEL
        avValField.setNgModel(ngModel);

        avValField.avValForm(avValForm);

        avValField.setRule(rule);

        avValField.createId();

        // DEBOUNCE
        var avValDebounce = parseInt(scope.avValDebounce || (avValForm.avValDebounce || AV_VAL.DEBOUNCE), 10);
        avValDebounce = _.isNumber(avValDebounce) ? avValDebounce : AV_VAL.DEBOUNCE;

        var debounceAllowed = !avValField.isRadio() && !avValField.isCheckbox() && avValOn !== 'blur';

        if(!debounceAllowed) {
          avValDebounce = 0;
        }

        // EVENT LISTENER
        avValField.event(avValOn, avValDebounce);

        // (view to model)
        ngModel.$parsers.push(avValField.validateView);

        // (model to view) - added to beginning of array because formatters
        // are processed in reverse order thus allowing the model to be transformed
        // before the validation framework check for validity.
        ngModel.$formatters.unshift(avValField.validateModel);

        scope.$on(AV_VAL.EVENTS.REVALIDATE, function() {
          avValField.validate(ngModel.$viewValue);
        });

        // SUBMITTED EVENT
        scope.$on(AV_VAL.EVENTS.SUBMITTED, function() {
          ngModel.$dirty = true;
          avValField.validate(ngModel.$viewValue);
        });

        // - Removes all errors on page,
        // - does not reset view or model values.  This is to be handled by the app.
        scope.$on(AV_VAL.EVENTS.RESET, function () {
          avValField.reset();
        });

        scope.$on('$destroy', function () {
          avValForm.unrecord(ngModel.avId);
        });

      }
    };
  });

})(window);
<commit_msg>update comments about validation key and promises<commit_after>(function(root) {

  'use strict';

  var availity = root.availity;

  availity.ui.controller('AvValFieldController', function($element, avValAdapter, $attrs, avVal, $log, $timeout, $scope, $sniffer) {

    this.ngModel = null;
    this.rule = null;
    this.avValForm = null;
    this.avValInvalid = false;

    var self = this;

    var placeholder = $element[0].placeholder;
    var noEvent = {};

    this.createId = function() {
      this.ngModel.avId = availity.uuid('avVal');
    };

    this.setNgModel = function(ngModel) {
      this.ngModel = ngModel;
    };

    this.setRule = function(rule) {
      this.rule = rule;
    };

    this.avValForm = function(avValForm) {
      this.avValForm = avValForm;
    };

    this.updateModel = function(results) {

      var self = this;
      var validationKeys = [];

      this.ngModel.avResults = results;

      // set state for each violation
      angular.forEach(results.violations, function (result) {
        var key = 'av-' + result.contraintName;
        validationKeys.push(key);
        self.ngModel.$setValidity(key, result.valid);
      });

      // set overall state for validation state
      this.ngModel.$setValidity('av', this.ngModel.avResults.isValid);

      // store violations
      this.ngModel.avViolations = this.ngModel.avResults.violations;

      // record the id and violation count in the av-form controller.  this determines if the form is
      // valid if sum of violations for all form inputs === zero
      this.avValForm.record(this.ngModel.avId, this.ngModel.avResults.violations.length);

      // remove violation keys that are no longer falsy
      angular.forEach(this.ngModel.$error, function(value, key) {

        if(_.indexOf(validationKeys, key) === -1 && key.lastIndexOf('av-', 0) === 0) {
          self.ngModel.$setValidity(key, true);
        }
      });
    };

    this.updateView = function() {
      if(this.ngModel.$dirty || $scope.avValShow) {
        avValAdapter.element($element, this.ngModel, this.ngModel.avResults.isValid);
        avValAdapter.message($element, this.ngModel);
      }
    };

    this.validate = function(value) {

      $log.info('validating value [' + value + ']');

      var rulesKey = self.avValForm.rulesKey;
      var results = avVal.validate(rulesKey, $element, value, self.rule);

      // Results can be undefined if the form validation key is not resolved on page load due
      // to promises.  When the promise resolves the validation on the form should re-run with
      // valid results.
      if(results) {

        // The validate function is called within the context of angular so fn.call and set the context
        // to "this"
        self.updateModel.call(self, results);
        self.updateView.call(self);

      }

      return results;
    };

    this.validateModel = function(value) {

      self.validate(value, true);
      return value;

    };

    this.validateView = function(value) {

      var results = self.validate(value);

      if(self.avValForm.avValInvalid || self.avValInvalid) {
        // allows invalid data from view to update model for dirty saving
        return value;
      }

      // prevent invalid data from view to update model
      return results.isValid ? value : undefined;

    };

    this.onDebounce = function() {

      var value = $element.val().trim();

      if(this.isCheckbox()) {
        this.ngModel.$setViewValue($element[0].checked);
      } else if(this.isRadio()) {
        this.ngModel.$setViewValue($attrs.value);
      }else {
        this.ngModel.$setViewValue(value);
      }

    };

    this.isRadio = function() {
      return $element.is('input') && $attrs.type === 'radio';
    };

    this.isCheckbox = function() {
      return $element.is('input') && $attrs.type === 'checkbox';
    };

    this.reset = function() {

      var violations = this.ngModel.avResults.violations;
      violations.splice(0, violations.length);

      avValAdapter.message($element, this.ngModel);
      avValAdapter.reset($element);

    };

    this.event = function(event, avValDebounce) {

      var self = this;

      $element.unbind('input');

      var debounce;

      $element.on(event, function() {

        // https://github.com/angular/angular.js/blob/v1.2.27/src/ng/directive/input.js#L508
        if($sniffer.msie <= 11 && (event || noEvent).type === 'input' && $element[0].placeholder !== placeholder) {
          placeholder = $element[0].placeholder;
          return;
        }

        $timeout.cancel(debounce);
        debounce = $timeout(function() {
          $scope.$apply(function() {
            self.onDebounce();
          });
        }, avValDebounce);

      });

    };

  });

  // Events:
  //
  //  click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown
  //  keyup keypress submit focus blur copy cut paste
  availity.ui.directive('avValField', function($log, $timeout, avVal, avValAdapter, AV_VAL) {
    return {
      restrict: 'A',
      controller: 'AvValFieldController',
      require: ['^avValForm', 'ngModel', 'avValField'],
      scope: {
        avValDebounce: '@?',
        avValOn: '@?',
        avValShow: '=?'
      },
      link: function(scope, element, attrs, controllers) {

        var rule = attrs.avValField;
        var avValForm = controllers[0];
        var ngModel = controllers[1];
        var avValField = controllers[2];

        var avValOn = scope.avValOn || avValForm.avValOn || 'input';

        if(!ngModel && !rule) {
          $log.error('avValField requires ngModel and a validation rule to run.');
          return;
        }

        // Allows fields to update with invalid data for dirty form saving
        avValField.avValInvalid = attrs.avValInvalid || false;

        // CACHE THE MODEL
        avValField.setNgModel(ngModel);

        avValField.avValForm(avValForm);

        avValField.setRule(rule);

        avValField.createId();

        // DEBOUNCE
        var avValDebounce = parseInt(scope.avValDebounce || (avValForm.avValDebounce || AV_VAL.DEBOUNCE), 10);
        avValDebounce = _.isNumber(avValDebounce) ? avValDebounce : AV_VAL.DEBOUNCE;

        var debounceAllowed = !avValField.isRadio() && !avValField.isCheckbox() && avValOn !== 'blur';

        if(!debounceAllowed) {
          avValDebounce = 0;
        }

        // EVENT LISTENER
        avValField.event(avValOn, avValDebounce);

        // (view to model)
        ngModel.$parsers.push(avValField.validateView);

        // (model to view) - added to beginning of array because formatters
        // are processed in reverse order thus allowing the model to be transformed
        // before the validation framework check for validity.
        ngModel.$formatters.unshift(avValField.validateModel);

        scope.$on(AV_VAL.EVENTS.REVALIDATE, function() {
          avValField.validate(ngModel.$viewValue);
        });

        // SUBMITTED EVENT
        scope.$on(AV_VAL.EVENTS.SUBMITTED, function() {
          ngModel.$dirty = true;
          avValField.validate(ngModel.$viewValue);
        });

        // - Removes all errors on page,
        // - does not reset view or model values.  This is to be handled by the app.
        scope.$on(AV_VAL.EVENTS.RESET, function () {
          avValField.reset();
        });

        scope.$on('$destroy', function () {
          avValForm.unrecord(ngModel.avId);
        });

      }
    };
  });

})(window);
<|endoftext|>


<commit_before>/** Handle interactions with Browsochrones and the cluster */

import Browsochrones, {createGrid} from 'browsochrones'
import {crc32} from 'crc'
import dbg from 'debug'
const debug = dbg('scenario-editor:browsochrones')
import uuid from 'uuid'

import convertToR5Modification from './convert-to-r5-modification'
import authenticatedFetch from './authenticated-fetch'
import timeout from './timeout'

/** How often to retry 202 response from the server */
const RETRY_TIMEOUT_MILLISECONDS = 10 * 1000

export const PROFILE_REQUEST_DEFAULTS = {
  date: (new Date()).toISOString().split('T')[0],
  fromTime: 25200,
  toTime: 32400,
  accessModes: 'WALK',
  directModes: 'WALK',
  egressModes: 'WALK',
  transitModes: 'TRANSIT',
  walkSpeed: 1.3888888888888888,
  bikeSpeed: 4.166666666666667,
  carSpeed: 20,
  streetTime: 90,
  maxWalkTime: 20,
  maxBikeTime: 20,
  maxCarTime: 45,
  minBikeTime: 10,
  minCarTime: 10,
  suboptimalMinutes: 5,
  reachabilityThreshold: 0,
  bikeSafe: 1,
  bikeSlope: 1,
  bikeTime: 1,
  bikeTrafficStress: 4,
  boardingAssumption: 'RANDOM',
  monteCarloDraws: 220
}

// Map from scenario ID to object containing Browsochrones instance, hash of last used scenario
// x, y coords of origin
// TODO LRU cache
let instances = new Map()

// Grids are shared among all Browsochrones instances
// array should contain { name, grid }
let grids = null

/** { name: <Browsochrones grid object>}, TODO combine with grids, don't build these in every thread */
let processedGrids = null

/** export a function to get the grids so that they can be retrieved after being set */
export const getGrids = () => processedGrids

/** get a new Browsochrones instance with grids already loaded */
async function newInstance () {
  if (grids == null) throw new Error('Grids are not loaded!')

  let browsochrones = new Browsochrones()

  await grids.map(({ grid, name }) => browsochrones.putGrid(name, grid))

  return {
    // CRC of the scenario, used to detect when stop trees and metadata must be refetched
    scenarioHash: null,
    origin: { x: NaN, y: NaN },
    staticSiteRequest: null,
    browsochrones
  }
}

/** Attempt to fetch a file from the server, retrying on a 202 (Worker starting up) response */
async function retryFetch (url, opts, timeoutMs = RETRY_TIMEOUT_MILLISECONDS) {
  let res = await authenticatedFetch(url, opts)

  if (res.status === 202) {
    debug(`202 response from server, retrying in ${timeoutMs / 1000}s`)
    await timeout(timeoutMs)
    return retryFetch(url, opts, timeoutMs)
  }

  return res
}

/** Set the grids being used, in the format [{name, grid}...] */
export function setGrids (newGrids) {
  if (instances.size > 0) throw new Error('attempt to set grids after Browsochrones instances have been created')
  grids = newGrids
  processedGrids = new Map()

  // make a local copy, making sure to slice since the process of creating a grid is destructive
  grids.forEach(({ name, grid }) => processedGrids.set(name, createGrid(grid.slice())))
}

/**
 * Get an R5 profile request for a scenario.
 * TODO we need to add a lot more options to this function (date, time, worker version)
 */
function getStaticSiteRequest ({ modifications, bundleId, scenarioUniqueId, workerVersion }) {
  return {
    transportNetworkId: bundleId, // TODO is this necessary?
    workerVersion,
    bucket: null, // don't save to S3
    jobId: uuid.v4(),
    request: {
      ...PROFILE_REQUEST_DEFAULTS,
      scenario: {
        id: scenarioUniqueId,
        modifications: modifications.map(convertToR5Modification)
      }
    }
  }
}

/** Make sure a scenario is loaded and we are ready to generate isochrones, paths, etc. with Browsochrones */
async function ensureScenarioLoaded ({ scenarioId, bundleId, modifications, workerVersion }) {
  // ensure we have a Browsochrones instance for this scenario id
  if (!instances.has(scenarioId)) {
    debug(`no browsochrones instance for scenario ${scenarioId}, creating one and loading grids`)
    instances.set(scenarioId, await newInstance())
    debug(`created browsochrones instance for scenario ${scenarioId}`)
  }

  let instance = instances.get(scenarioId)

  // hash the modifications to detect any changes that have taken place
  let scenarioHash = crc32(JSON.stringify(modifications))

  // Maybe one day we'll check if anything relevant (the street network) has changed,
  // that would be a performance win
  if (scenarioHash !== instance.scenarioHash || workerVersion !== instance.workerVersion) {
    debug(`scenario ${scenarioId} has changed, updating browsochrones`)
    let scenarioUniqueId = `${scenarioId}-${scenarioHash}`
    let request = getStaticSiteRequest({ modifications, bundleId, scenarioUniqueId, workerVersion })
    // TODO don't duplicate these fields
    let { jobId } = request
    let body = {
      request,
      graphId: bundleId,
      workerVersion,
      jobId
    }

    let metadata = await retryFetch('/api/analysis/enqueue/single', {
      method: 'POST',
      body: JSON.stringify({ ...body, type: 'static-metadata' })
    }).then(res => res.json())

    // technically we shouldn't need to retry here as the worker is started once the above request
    // succeeds, but why not.
    let stopTrees = await retryFetch('/api/analysis/enqueue/single', {
      method: 'POST',
      body: JSON.stringify({ ...body, type: 'static-stop-trees' })
    }).then(res => res.arrayBuffer())

    let { browsochrones } = instance

    await Promise.all([
      browsochrones.setQuery(metadata),
      browsochrones.setStopTrees(stopTrees),
      browsochrones.setTransitiveNetwork(metadata.transitive) // TODO shouldn't have to do this.
    ])

    instance.staticSiteRequest = request
    instance.scenarioHash = scenarioHash
    instance.workerVersion = workerVersion
    instance.origin = { x: -1, y: -1 }

    instance.indicator = instance.spectrogramData = null

    debug(`(re-)loaded scenario ${scenarioId}`)
  }
}

/**
 * Ensure that an origin is loaded and a surface has been generated.
 * Assumes that the proper scenario/stop trees have been loaded with ensureScenarioLoaded
 */
async function ensureOriginLoaded ({ scenarioId, x, y }) {
  let instance = instances.get(scenarioId)

  if (instance.origin.x !== x || instance.origin.y !== y) {
    debug(`fetching origin (${x}, ${y}) for scenario ID ${scenarioId}`)

    // TODO don't duplicate these fields
    let { workerVersion, jobId, transportNetworkId } = instance.staticSiteRequest
    let body = {
      request: instance.staticSiteRequest,
      graphId: transportNetworkId,
      workerVersion,
      jobId,
      x,
      y,
      type: 'static'
    }

    // always possible that the worker has died in the meantime, use retryFetch
    let origin = await retryFetch('/api/analysis/enqueue/single', {
      method: 'POST',
      body: JSON.stringify(body)
    }).then(res => res.arrayBuffer())

    await instance.browsochrones.setOrigin(origin, { x, y })
    await instance.browsochrones.generateSurface()
    instance.origin = { x, y }
    instance.indicator = instance.spectrogramData = null
  }
}

/** Get the spectrogram data, caching if possible */
async function getSpectrogramData ({ scenarioId, indicator }) {
  // NB no checks if this is the current scenario etc, assume call to ensureScenarioLoaded and ensureOriginLoaded
  let instance = instances.get(scenarioId)

  // this catches nulls as well
  if (instance.indicator !== indicator) {
    instance.spectrogramData = await instance.browsochrones.getSpectrogramData(indicator)
    instance.indicator = indicator
  }

  return instance.spectrogramData
}

/** Get isochrones and accessibility numbers for a scenario and a given cutoff. Modifications is in scenario-editor format. */
export async function getIsochronesAndAccessibility ({ scenarioId, bundleId, modifications, isochroneCutoff, origin, indicator, workerVersion }) {
  await ensureScenarioLoaded({ scenarioId, modifications, bundleId, workerVersion })
  let { browsochrones } = instances.get(scenarioId)
  let { x, y } = browsochrones.latLonToOriginPoint(origin)
  await ensureOriginLoaded({ scenarioId, x, y })

  return {
    isochrone: await browsochrones.getIsochrone(isochroneCutoff, false),
    accessibility: await browsochrones.getAccessibilityForGrid(indicator, isochroneCutoff),
    spectrogramData: await getSpectrogramData({ scenarioId, indicator }),
    indicator,
    isochroneCutoff
  }
}

/** clear all browsochrones instances when changing projects */
export function clear () {
  instances = new Map()
}
<commit_msg>fix(analysis): increase monte carlo draws to 1000.<commit_after>/** Handle interactions with Browsochrones and the cluster */

import Browsochrones, {createGrid} from 'browsochrones'
import {crc32} from 'crc'
import dbg from 'debug'
const debug = dbg('scenario-editor:browsochrones')
import uuid from 'uuid'

import convertToR5Modification from './convert-to-r5-modification'
import authenticatedFetch from './authenticated-fetch'
import timeout from './timeout'

/** How often to retry 202 response from the server */
const RETRY_TIMEOUT_MILLISECONDS = 10 * 1000

export const PROFILE_REQUEST_DEFAULTS = {
  date: (new Date()).toISOString().split('T')[0],
  fromTime: 25200,
  toTime: 32400,
  accessModes: 'WALK',
  directModes: 'WALK',
  egressModes: 'WALK',
  transitModes: 'TRANSIT',
  walkSpeed: 1.3888888888888888,
  bikeSpeed: 4.166666666666667,
  carSpeed: 20,
  streetTime: 90,
  maxWalkTime: 20,
  maxBikeTime: 20,
  maxCarTime: 45,
  minBikeTime: 10,
  minCarTime: 10,
  suboptimalMinutes: 5,
  reachabilityThreshold: 0,
  bikeSafe: 1,
  bikeSlope: 1,
  bikeTime: 1,
  bikeTrafficStress: 4,
  boardingAssumption: 'RANDOM',
  monteCarloDraws: 1000
}

// Map from scenario ID to object containing Browsochrones instance, hash of last used scenario
// x, y coords of origin
// TODO LRU cache
let instances = new Map()

// Grids are shared among all Browsochrones instances
// array should contain { name, grid }
let grids = null

/** { name: <Browsochrones grid object>}, TODO combine with grids, don't build these in every thread */
let processedGrids = null

/** export a function to get the grids so that they can be retrieved after being set */
export const getGrids = () => processedGrids

/** get a new Browsochrones instance with grids already loaded */
async function newInstance () {
  if (grids == null) throw new Error('Grids are not loaded!')

  let browsochrones = new Browsochrones()

  await grids.map(({ grid, name }) => browsochrones.putGrid(name, grid))

  return {
    // CRC of the scenario, used to detect when stop trees and metadata must be refetched
    scenarioHash: null,
    origin: { x: NaN, y: NaN },
    staticSiteRequest: null,
    browsochrones
  }
}

/** Attempt to fetch a file from the server, retrying on a 202 (Worker starting up) response */
async function retryFetch (url, opts, timeoutMs = RETRY_TIMEOUT_MILLISECONDS) {
  let res = await authenticatedFetch(url, opts)

  if (res.status === 202) {
    debug(`202 response from server, retrying in ${timeoutMs / 1000}s`)
    await timeout(timeoutMs)
    return retryFetch(url, opts, timeoutMs)
  }

  return res
}

/** Set the grids being used, in the format [{name, grid}...] */
export function setGrids (newGrids) {
  if (instances.size > 0) throw new Error('attempt to set grids after Browsochrones instances have been created')
  grids = newGrids
  processedGrids = new Map()

  // make a local copy, making sure to slice since the process of creating a grid is destructive
  grids.forEach(({ name, grid }) => processedGrids.set(name, createGrid(grid.slice())))
}

/**
 * Get an R5 profile request for a scenario.
 * TODO we need to add a lot more options to this function (date, time, worker version)
 */
function getStaticSiteRequest ({ modifications, bundleId, scenarioUniqueId, workerVersion }) {
  return {
    transportNetworkId: bundleId, // TODO is this necessary?
    workerVersion,
    bucket: null, // don't save to S3
    jobId: uuid.v4(),
    request: {
      ...PROFILE_REQUEST_DEFAULTS,
      scenario: {
        id: scenarioUniqueId,
        modifications: modifications.map(convertToR5Modification)
      }
    }
  }
}

/** Make sure a scenario is loaded and we are ready to generate isochrones, paths, etc. with Browsochrones */
async function ensureScenarioLoaded ({ scenarioId, bundleId, modifications, workerVersion }) {
  // ensure we have a Browsochrones instance for this scenario id
  if (!instances.has(scenarioId)) {
    debug(`no browsochrones instance for scenario ${scenarioId}, creating one and loading grids`)
    instances.set(scenarioId, await newInstance())
    debug(`created browsochrones instance for scenario ${scenarioId}`)
  }

  let instance = instances.get(scenarioId)

  // hash the modifications to detect any changes that have taken place
  let scenarioHash = crc32(JSON.stringify(modifications))

  // Maybe one day we'll check if anything relevant (the street network) has changed,
  // that would be a performance win
  if (scenarioHash !== instance.scenarioHash || workerVersion !== instance.workerVersion) {
    debug(`scenario ${scenarioId} has changed, updating browsochrones`)
    let scenarioUniqueId = `${scenarioId}-${scenarioHash}`
    let request = getStaticSiteRequest({ modifications, bundleId, scenarioUniqueId, workerVersion })
    // TODO don't duplicate these fields
    let { jobId } = request
    let body = {
      request,
      graphId: bundleId,
      workerVersion,
      jobId
    }

    let metadata = await retryFetch('/api/analysis/enqueue/single', {
      method: 'POST',
      body: JSON.stringify({ ...body, type: 'static-metadata' })
    }).then(res => res.json())

    // technically we shouldn't need to retry here as the worker is started once the above request
    // succeeds, but why not.
    let stopTrees = await retryFetch('/api/analysis/enqueue/single', {
      method: 'POST',
      body: JSON.stringify({ ...body, type: 'static-stop-trees' })
    }).then(res => res.arrayBuffer())

    let { browsochrones } = instance

    await Promise.all([
      browsochrones.setQuery(metadata),
      browsochrones.setStopTrees(stopTrees),
      browsochrones.setTransitiveNetwork(metadata.transitive) // TODO shouldn't have to do this.
    ])

    instance.staticSiteRequest = request
    instance.scenarioHash = scenarioHash
    instance.workerVersion = workerVersion
    instance.origin = { x: -1, y: -1 }

    instance.indicator = instance.spectrogramData = null

    debug(`(re-)loaded scenario ${scenarioId}`)
  }
}

/**
 * Ensure that an origin is loaded and a surface has been generated.
 * Assumes that the proper scenario/stop trees have been loaded with ensureScenarioLoaded
 */
async function ensureOriginLoaded ({ scenarioId, x, y }) {
  let instance = instances.get(scenarioId)

  if (instance.origin.x !== x || instance.origin.y !== y) {
    debug(`fetching origin (${x}, ${y}) for scenario ID ${scenarioId}`)

    // TODO don't duplicate these fields
    let { workerVersion, jobId, transportNetworkId } = instance.staticSiteRequest
    let body = {
      request: instance.staticSiteRequest,
      graphId: transportNetworkId,
      workerVersion,
      jobId,
      x,
      y,
      type: 'static'
    }

    // always possible that the worker has died in the meantime, use retryFetch
    let origin = await retryFetch('/api/analysis/enqueue/single', {
      method: 'POST',
      body: JSON.stringify(body)
    }).then(res => res.arrayBuffer())

    await instance.browsochrones.setOrigin(origin, { x, y })
    await instance.browsochrones.generateSurface()
    instance.origin = { x, y }
    instance.indicator = instance.spectrogramData = null
  }
}

/** Get the spectrogram data, caching if possible */
async function getSpectrogramData ({ scenarioId, indicator }) {
  // NB no checks if this is the current scenario etc, assume call to ensureScenarioLoaded and ensureOriginLoaded
  let instance = instances.get(scenarioId)

  // this catches nulls as well
  if (instance.indicator !== indicator) {
    instance.spectrogramData = await instance.browsochrones.getSpectrogramData(indicator)
    instance.indicator = indicator
  }

  return instance.spectrogramData
}

/** Get isochrones and accessibility numbers for a scenario and a given cutoff. Modifications is in scenario-editor format. */
export async function getIsochronesAndAccessibility ({ scenarioId, bundleId, modifications, isochroneCutoff, origin, indicator, workerVersion }) {
  await ensureScenarioLoaded({ scenarioId, modifications, bundleId, workerVersion })
  let { browsochrones } = instances.get(scenarioId)
  let { x, y } = browsochrones.latLonToOriginPoint(origin)
  await ensureOriginLoaded({ scenarioId, x, y })

  return {
    isochrone: await browsochrones.getIsochrone(isochroneCutoff, false),
    accessibility: await browsochrones.getAccessibilityForGrid(indicator, isochroneCutoff),
    spectrogramData: await getSpectrogramData({ scenarioId, indicator }),
    indicator,
    isochroneCutoff
  }
}

/** clear all browsochrones instances when changing projects */
export function clear () {
  instances = new Map()
}
<|endoftext|>


<commit_before>const { reopenClosed } = require('../templates')

/**
 * Reopen a closed issue and post a comment saying what happened and why
 * @param {object} param0
 * @param {Data} data
 */
module.exports = async ({context, config, issue}, data) => {
  if (issue.state === 'closed' && config.reopenClosed) {
    await context.github.issues.edit(context.repo({
      number: issue.number,
      state: 'open'
    }))

    const body = reopenClosed(context.repo(data))
    await context.github.issues.createComment(context.repo({
      number: issue.number,
      body
    }))
  }
}

/**
 * @typedef Data
 * @property {string} keyword
 * @property {string} sha
 * @property {string} filename
 */
<commit_msg>Can return instead of await<commit_after>const { reopenClosed } = require('../templates')

/**
 * Reopen a closed issue and post a comment saying what happened and why
 * @param {object} param0
 * @param {Data} data
 */
module.exports = async ({context, config, issue}, data) => {
  if (issue.state === 'closed' && config.reopenClosed) {
    await context.github.issues.edit(context.repo({
      number: issue.number,
      state: 'open'
    }))

    const body = reopenClosed(context.repo(data))
    return context.github.issues.createComment(context.repo({
      number: issue.number,
      body
    }))
  }
}

/**
 * @typedef Data
 * @property {string} keyword
 * @property {string} sha
 * @property {string} filename
 */
<|endoftext|>


<commit_before>'use babel';

/** @jsx etch.dom */
/* @flow*/
import etch from 'etch';
import { Emitter, CompositeDisposable } from 'atom';
import type { TesterState } from '../types';

export default class ConsoleOutputView {
  properties: {
    state: TesterState;
    onclick: Function;
  }
  refs: any;
  element: any;
  panel: any;
  emitter: Emitter;
  disposables: CompositeDisposable;

  constructor(properties :Object) {
    this.properties = properties;
    this.emitter = new Emitter();
    this.disposables = new CompositeDisposable();

    console.log('properties', properties);
    etch.initialize(this);

    this.disposables.add(atom.tooltips.add(this.refs.failed, { title: 'Failed Tests' }));
    this.disposables.add(atom.tooltips.add(this.refs.skipped, { title: 'Skipped Tests' }));
    this.disposables.add(atom.tooltips.add(this.refs.passed, { title: 'Passed Tests' }));
    this.disposables.add(atom.tooltips.add(this.refs.beaker, { title: 'Click to toggle Tester' }));
  }

  render() {
    let messages = this.properties.state.messages;
    if (!messages || messages.constructor !== Array) {
      messages = [];
    }
    const failedTests = messages.filter(result => result.state === 'failed').length;
    const skippedTests = messages.filter(result => result.state === 'skipped').length;
    const passedTests = messages.filter(result => result.state === 'passed').length;
    return (

         0 ?
          'tester-bottom-status tester-status-failed highlight-error' :
          'tester-bottom-status tester-status-failed highlight'}>
          {failedTests}

         0 ?
          'tester-bottom-status tester-status-skipped highlight-warning' :
          'tester-bottom-status tester-status-skipped highlight'}>
          {skippedTests}

         0 ?
          'tester-bottom-status tester-status-passed highlight-success' :
          'tester-bottom-status tester-status-passed highlight'}>
          {passedTests}

         0 ?
          'tester-tiny loading loading-spinner-tiny inline-block' :
          'tester-tiny loading loading-spinner-tiny inline-block idle'}/>

    );
  }

  update(newState :TesterState) {
    console.log('newState', newState);
    if (this.properties.state !== newState) {
      return etch.update(this);
    }
    return Promise.resolve();
  }

  async destroy() {
    await etch.destroy(this);
    this.disposables.dispose();
  }

  getElement() {
    return this.element;
  }
}
<commit_msg>fix state results props<commit_after>'use babel';

/** @jsx etch.dom */
/* @flow*/
import etch from 'etch';
import { Emitter, CompositeDisposable } from 'atom';
import type { TesterState } from '../types';

export default class ConsoleOutputView {
  properties: {
    state: {results: TesterState};
    onclick: Function;
  }
  refs: any;
  element: any;
  panel: any;
  emitter: Emitter;
  disposables: CompositeDisposable;

  constructor(properties :Object) {
    this.properties = properties;
    this.emitter = new Emitter();
    this.disposables = new CompositeDisposable();

    console.log('properties', properties);
    etch.initialize(this);

    this.disposables.add(atom.tooltips.add(this.refs.failed, { title: 'Failed Tests' }));
    this.disposables.add(atom.tooltips.add(this.refs.skipped, { title: 'Skipped Tests' }));
    this.disposables.add(atom.tooltips.add(this.refs.passed, { title: 'Passed Tests' }));
    this.disposables.add(atom.tooltips.add(this.refs.beaker, { title: 'Click to toggle Tester' }));
  }

  render() {
    let messages = this.properties.state.results.messages;
    if (!messages || messages.constructor !== Array) {
      messages = [];
    }
    const failedTests = messages.filter(result => result.state === 'failed').length;
    const skippedTests = messages.filter(result => result.state === 'skipped').length;
    const passedTests = messages.filter(result => result.state === 'passed').length;
    return (

         0 ?
          'tester-bottom-status tester-status-failed highlight-error' :
          'tester-bottom-status tester-status-failed highlight'}>
          {failedTests}

         0 ?
          'tester-bottom-status tester-status-skipped highlight-warning' :
          'tester-bottom-status tester-status-skipped highlight'}>
          {skippedTests}

         0 ?
          'tester-bottom-status tester-status-passed highlight-success' :
          'tester-bottom-status tester-status-passed highlight'}>
          {passedTests}

         0 ?
          'tester-tiny loading loading-spinner-tiny inline-block' :
          'tester-tiny loading loading-spinner-tiny inline-block idle'}/>

    );
  }

  update(newState :TesterState) {
    console.log('newState', newState);
    if (this.properties.state !== newState) {
      return etch.update(this);
    }
    return Promise.resolve();
  }

  async destroy() {
    await etch.destroy(this);
    this.disposables.dispose();
  }

  getElement() {
    return this.element;
  }
}
<|endoftext|>


<commit_before>'use babel';
/**
  Stack Explorer Component
  @description Creates a tabbed side panel that displays information about
    the program's stacks, has the following tabs:
      - Stacktrace: show the stacktrace @ the current source line
      - Goroutines: show the running goroutines @ the current source line
      - Variables: shows the variables in the current scope
      - Breakpoints: view breakpoints and deactivate them

    Should be able to click on stack/goroutine frames and go to the source,
    and should be able to do the same for breakpoints
**/

import { CompositeDisposable } from 'atom';
import { BreakpointStore, DebuggerStateStore as StateStore } from '../stores';

export default class StackExplorerPanel {
  constructor() {
    this.disposables = new CompositeDisposable();
    this.element = null;
    this.$selectedTab = null;

    // Register Flux Stores
    this._stateStoreRm = StateStore
    .addListener(this.updateStack.bind(this)).remove;

    this._bpStoreRm = BreakpointStore
    .addListener(this.updateBreakpoints.bind(this)).remove;

    this.render();
  }

  render() {
    let $panelContainer = document.createElement('div');
    this._$panelContainer = $panelContainer;

    $panelContainer.classList.add('delve-stack-panel');
    $panelContainer.appendChild(this.buildTabBar());

    this.element = atom.workspace.addRightPanel({
      item: $panelContainer,
      priority: 200
    });
  }

  buildTabBar() {
    let $tabBar = document.createElement('ul');
    $tabBar.classList.add('tab-container');

    let $stackTab = document.createElement('li');
    $stackTab.classList.add('tab');
    $stackTab.classList.add('stacktrace-tab');
    $stackTab.innerHTML = '
Stacktrace
';
    $tabBar.appendChild($stackTab);

    let $grTab = document.createElement('li');
    $grTab.classList.add('tab');
    $grTab.classList.add('goroutine-tab');
    $grTab.innerHTML = '
Goroutines
';
    $tabBar.appendChild($grTab);

    let $varTab = document.createElement('li');
    $varTab.classList.add('tab');
    $varTab.classList.add('variables-tab');
    $varTab.innerHTML = '
Variables
';
    $tabBar.appendChild($varTab);

    let $bpTab = document.createElement('li');
    $bpTab.classList.add('tab');
    $bpTab.classList.add('breakpoints-tab');
    $bpTab.innerHTML = '
Breakpoints
';
    $tabBar.appendChild($bpTab);

    // Default active is stacktrace
    $stackTab.classList.add('active');
    this.$selectedTab = $stackTab;

    for (let i = 0; i < $tabBar.children.length; i++) {
      const $tab = $tabBar.children[i];
      $tab.addEventListener('click', () => {
        $tab.classList.add('active');
        if (this.$selectedTab) this.$selectedTab.classList.remove('active');
        this.$selectedTab = $tab;
      });
    }

    return $tabBar;
  }

  updateStack() {}
  updateBreakpoints() {}

  destroy() {
    this._stateStoreRm();
    this._bpStoreRm();

    if (this.element) this.element.destroy();
  }
}
<commit_msg>Add props for state details, impl updating fn<commit_after>'use babel';
/**
  Stack Explorer Component
  @description Creates a tabbed side panel that displays information about
    the program's stacks, has the following tabs:
      - Stacktrace: show the stacktrace @ the current source line
      - Goroutines: show the running goroutines @ the current source line
      - Variables: shows the variables in the current scope
      - Breakpoints: view breakpoints and deactivate them

    Should be able to click on stack/goroutine frames and go to the source,
    and should be able to do the same for breakpoints
**/

import { CompositeDisposable } from 'atom';
import { BreakpointStore, DebuggerStateStore as StateStore } from '../stores';

export default class StackExplorerPanel {
  constructor() {
    this.disposables = new CompositeDisposable();
    this.element = null;
    this.$selectedTab = null;
    this.state = null;
    this.stacktrace = [];
    this.goroutines = [];
    this.variables = [];
    this.breakpoints = [];

    // Register Flux Stores
    this._stateStoreRm = StateStore
    .addListener(this.updateState.bind(this)).remove;

    this._bpStoreRm = BreakpointStore
    .addListener(this.updateBreakpoints.bind(this)).remove;

    this.render();
  }

  render() {
    let $panelContainer = document.createElement('div');
    this._$panelContainer = $panelContainer;

    $panelContainer.classList.add('delve-stack-panel');
    $panelContainer.appendChild(this.buildTabBar());

    this.element = atom.workspace.addRightPanel({
      item: $panelContainer,
      priority: 200
    });
  }

  buildTabBar() {
    let $tabBar = document.createElement('ul');
    $tabBar.classList.add('tab-container');

    let $stackTab = document.createElement('li');
    $stackTab.classList.add('tab');
    $stackTab.classList.add('stacktrace-tab');
    $stackTab.innerHTML = '
Stacktrace
';
    $tabBar.appendChild($stackTab);

    let $grTab = document.createElement('li');
    $grTab.classList.add('tab');
    $grTab.classList.add('goroutine-tab');
    $grTab.innerHTML = '
Goroutines
';
    $tabBar.appendChild($grTab);

    let $varTab = document.createElement('li');
    $varTab.classList.add('tab');
    $varTab.classList.add('variables-tab');
    $varTab.innerHTML = '
Variables
';
    $tabBar.appendChild($varTab);

    let $bpTab = document.createElement('li');
    $bpTab.classList.add('tab');
    $bpTab.classList.add('breakpoints-tab');
    $bpTab.innerHTML = '
Breakpoints
';
    $tabBar.appendChild($bpTab);

    // Default active is stacktrace
    $stackTab.classList.add('active');
    this.$selectedTab = $stackTab;

    for (let i = 0; i < $tabBar.children.length; i++) {
      const $tab = $tabBar.children[i];
      $tab.addEventListener('click', () => {
        $tab.classList.add('active');
        if (this.$selectedTab) this.$selectedTab.classList.remove('active');
        this.$selectedTab = $tab;
      });
    }

    return $tabBar;
  }

  updateState() {
    this.stacktrace = StateStore.getStacktrace();
    this.state = StateStore.getState();
    this.goroutines = StateStore.getGoroutines();
  }

  updateBreakpoints() {
    this.breakpoints = BreakpointStore.getBreakpoints();
  }

  destroy() {
    this._stateStoreRm();
    this._bpStoreRm();

    if (this.element) this.element.destroy();
  }
}
<|endoftext|>


<commit_before>'use strict'

const Router = require('express').Router
const passport = require('passport')
const SamlStrategy = require('passport-saml').Strategy
const config = require('../../../config')
const models = require('../../../models')
const logger = require('../../../logger')
const { urlencodedParser } = require('../../utils')
const fs = require('fs')
const intersection = function (array1, array2) { return array1.filter((n) => array2.includes(n)) }

let samlAuth = module.exports = Router()

passport.use(new SamlStrategy({
  callbackUrl: config.serverURL + '/auth/saml/callback',
  entryPoint: config.saml.idpSsoUrl,
  issuer: config.saml.issuer || config.serverURL,
  cert: fs.readFileSync(config.saml.idpCert, 'utf-8'),
  identifierFormat: config.saml.identifierFormat
}, function (user, done) {
  // check authorization if needed
  if (config.saml.externalGroups && config.saml.groupAttribute) {
    var externalGroups = intersection(config.saml.externalGroups, user[config.saml.groupAttribute])
    if (externalGroups.length > 0) {
      logger.error('saml permission denied: ' + externalGroups.join(', '))
      return done('Permission denied', null)
    }
  }
  if (config.saml.requiredGroups && config.saml.groupAttribute) {
    if (intersection(config.saml.requiredGroups, user[config.saml.groupAttribute]).length === 0) {
      logger.error('saml permission denied')
      return done('Permission denied', null)
    }
  }
  // user creation
  var uuid = user[config.saml.attribute.id] || user.nameID
  var profile = {
    provider: 'saml',
    id: 'SAML-' + uuid,
    username: user[config.saml.attribute.username] || user.nameID,
    emails: user[config.saml.attribute.email] ? [user[config.saml.attribute.email]] : []
  }
  if (profile.emails.length === 0 && config.saml.identifierFormat === 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress') {
    profile.emails.push(user.nameID)
  }
  var stringifiedProfile = JSON.stringify(profile)
  models.User.findOrCreate({
    where: {
      profileid: profile.id.toString()
    },
    defaults: {
      profile: stringifiedProfile
    }
  }).spread(function (user, created) {
    if (user) {
      var needSave = false
      if (user.profile !== stringifiedProfile) {
        user.profile = stringifiedProfile
        needSave = true
      }
      if (needSave) {
        user.save().then(function () {
          if (config.debug) { logger.debug('user login: ' + user.id) }
          return done(null, user)
        })
      } else {
        if (config.debug) { logger.debug('user login: ' + user.id) }
        return done(null, user)
      }
    }
  }).catch(function (err) {
    logger.error('saml auth failed: ' + err)
    return done(err, null)
  })
}))

samlAuth.get('/auth/saml',
  passport.authenticate('saml', {
    successReturnToOrRedirect: config.serverURL + '/',
    failureRedirect: config.serverURL + '/'
  })
)

samlAuth.post('/auth/saml/callback', urlencodedParser,
  passport.authenticate('saml', {
    successReturnToOrRedirect: config.serverURL + '/',
    failureRedirect: config.serverURL + '/'
  })
)

samlAuth.get('/auth/saml/metadata', function (req, res) {
  res.type('application/xml')
  res.send(passport._strategy('saml').generateServiceProviderMetadata())
})
<commit_msg>refactor: fix lint on lib/web/auth/saml/index.js<commit_after>'use strict'

const Router = require('express').Router
const passport = require('passport')
const SamlStrategy = require('passport-saml').Strategy
const config = require('../../../config')
const models = require('../../../models')
const logger = require('../../../logger')
const { urlencodedParser } = require('../../utils')
const fs = require('fs')
const intersection = function (array1, array2) { return array1.filter((n) => array2.includes(n)) }

const samlAuth = module.exports = Router()

passport.use(new SamlStrategy({
  callbackUrl: config.serverURL + '/auth/saml/callback',
  entryPoint: config.saml.idpSsoUrl,
  issuer: config.saml.issuer || config.serverURL,
  cert: fs.readFileSync(config.saml.idpCert, 'utf-8'),
  identifierFormat: config.saml.identifierFormat
}, function (user, done) {
  // check authorization if needed
  if (config.saml.externalGroups && config.saml.groupAttribute) {
    var externalGroups = intersection(config.saml.externalGroups, user[config.saml.groupAttribute])
    if (externalGroups.length > 0) {
      logger.error('saml permission denied: ' + externalGroups.join(', '))
      return done('Permission denied', null)
    }
  }
  if (config.saml.requiredGroups && config.saml.groupAttribute) {
    if (intersection(config.saml.requiredGroups, user[config.saml.groupAttribute]).length === 0) {
      logger.error('saml permission denied')
      return done('Permission denied', null)
    }
  }
  // user creation
  var uuid = user[config.saml.attribute.id] || user.nameID
  var profile = {
    provider: 'saml',
    id: 'SAML-' + uuid,
    username: user[config.saml.attribute.username] || user.nameID,
    emails: user[config.saml.attribute.email] ? [user[config.saml.attribute.email]] : []
  }
  if (profile.emails.length === 0 && config.saml.identifierFormat === 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress') {
    profile.emails.push(user.nameID)
  }
  var stringifiedProfile = JSON.stringify(profile)
  models.User.findOrCreate({
    where: {
      profileid: profile.id.toString()
    },
    defaults: {
      profile: stringifiedProfile
    }
  }).spread(function (user, created) {
    if (user) {
      var needSave = false
      if (user.profile !== stringifiedProfile) {
        user.profile = stringifiedProfile
        needSave = true
      }
      if (needSave) {
        user.save().then(function () {
          if (config.debug) { logger.debug('user login: ' + user.id) }
          return done(null, user)
        })
      } else {
        if (config.debug) { logger.debug('user login: ' + user.id) }
        return done(null, user)
      }
    }
  }).catch(function (err) {
    logger.error('saml auth failed: ' + err)
    return done(err, null)
  })
}))

samlAuth.get('/auth/saml',
  passport.authenticate('saml', {
    successReturnToOrRedirect: config.serverURL + '/',
    failureRedirect: config.serverURL + '/'
  })
)

samlAuth.post('/auth/saml/callback', urlencodedParser,
  passport.authenticate('saml', {
    successReturnToOrRedirect: config.serverURL + '/',
    failureRedirect: config.serverURL + '/'
  })
)

samlAuth.get('/auth/saml/metadata', function (req, res) {
  res.type('application/xml')
  res.send(passport._strategy('saml').generateServiceProviderMetadata())
})
<|endoftext|>


<commit_before>if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') {
    module.exports = OAuth;
    var CryptoJS = require("crypto-js");
}

/**
 * Constructor
 * @param {Object} opts consumer key and secret
 */
function OAuth(opts) {
    if(!(this instanceof OAuth)) {
        return new OAuth(opts);
    }

    if(!opts) {
        opts = {};
    }

    if(!opts.consumer) {
        throw new Error('consumer option is required');
    }

    this.consumer            = opts.consumer;
    this.signature_method    = opts.signature_method || 'HMAC-SHA1';
    this.nonce_length        = opts.nonce_length || 32;
    this.version             = opts.version || '1.0';
    this.parameter_seperator = opts.parameter_seperator || ', ';

    if(typeof opts.last_ampersand === 'undefined') {
        this.last_ampersand = true;
    } else {
        this.last_ampersand = opts.last_ampersand;
    }

    switch (this.signature_method) {
        case 'HMAC-SHA1':
            this.hash = function(base_string, key) {
                return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;
        case 'PLAINTEXT':
            this.hash = function(base_string, key) {
                return key;
            };
            break;
        case 'RSA-SHA1':
            throw new Error('oauth-1.0a does not support this signature method right now. Coming Soon...');
        default:
            throw new Error('The OAuth 1.0a protocol defines three signature methods: HMAC-SHA1, RSA-SHA1, and PLAINTEXT only');
    }
}

/**
 * OAuth request authorize
 * @param  {Object} request data
 * {
 *     method,
 *     url,
 *     data
 * }
 * @param  {Object} public and secret token
 * @return {Object} OAuth Authorized data
 */
OAuth.prototype.authorize = function(request, token) {
    var oauth_data = {
        oauth_consumer_key: this.consumer.public,
        oauth_nonce: this.getNonce(),
        oauth_signature_method: this.signature_method,
        oauth_timestamp: this.getTimeStamp(),
        oauth_version: this.version
    };

    if(!token) {
        token = {};
    }

    if(token.public) {
        oauth_data.oauth_token = token.public;
    }

    oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data);

    return oauth_data;
};

/**
 * Create a OAuth Signature
 * @param  {Object} request data
 * @param  {Object} token_secret public and secret token
 * @param  {Object} oauth_data   OAuth data
 * @return {String} Signature
 */
OAuth.prototype.getSignature = function(request, token_secret, oauth_data) {
    return this.hash(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret));
};

/**
 * Base String = Method + Base Url + ParameterString
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {String} Base String
 */
OAuth.prototype.getBaseString = function(request, oauth_data) {
    return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data));
};

/**
 * Get data from url
 * -> merge with oauth data
 * -> percent encode key & value
 * -> sort
 *
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {Object} Parameter string data
 */
OAuth.prototype.getParameterString = function(request, oauth_data) {
    var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url)))));

    var data_str = '';

    //base_string_data to string
    for(var key in base_string_data) {
        data_str += key + '=' + base_string_data[key] + '&';
    }

    //remove the last character
    data_str = data_str.substr(0, data_str.length - 1);
    return data_str;
};

/**
 * Create a Signing Key
 * @param  {String} token_secret Secret Token
 * @return {String} Signing Key
 */
OAuth.prototype.getSigningKey = function(token_secret) {
    token_secret = token_secret || '';

    if(!this.last_ampersand && !token_secret) {
        return this.percentEncode(this.consumer.secret);
    }

    return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret);
};

/**
 * Get base url
 * @param  {String} url
 * @return {String}
 */
OAuth.prototype.getBaseUrl = function(url) {
    return url.split('?')[0];
};

/**
 * Get data from String
 * @param  {String} string
 * @return {Object}
 */
OAuth.prototype.deParam = function(string) {
    var arr = decodeURIComponent(string).split('&');
    var data = {};

    for(var i = 0; i < arr.length; i++) {
        var item = arr[i].split('=');
        data[item[0]] = item[1];
    }
    return data;
};

/**
 * Get data from url
 * @param  {String} url
 * @return {Object}
 */
OAuth.prototype.deParamUrl = function(url) {
    var tmp = url.split('?');

    if (tmp.length === 1)
        return {};

    return this.deParam(tmp[1]);
};

/**
 * Percent Encode
 * @param  {String} str
 * @return {String} percent encoded string
 */
OAuth.prototype.percentEncode = function(str) {
    return encodeURIComponent(str)
        .replace(/\!/g, "%21")
        .replace(/\*/g, "%2A")
        .replace(/\'/g, "%27")
        .replace(/\(/g, "%28")
        .replace(/\)/g, "%29");
};

/**
 * Percent Encode Object
 * @param  {Object} data
 * @return {Object} percent encoded data
 */
OAuth.prototype.percentEncodeData = function(data) {
    var result = {};

    for(var key in data) {
        result[this.percentEncode(key)] = this.percentEncode(data[key]);
    }

    return result;
};

/**
 * Get OAuth data as Header
 * @param  {Object} oauth_data
 * @return {String} Header data key - value
 */
OAuth.prototype.toHeader = function(oauth_data) {
    oauth_data = this.sortObject(oauth_data);

    var header_value = 'OAuth ';

    for(var key in oauth_data) {
        if (key.indexOf('oauth_') === -1)
            continue;
        header_value += this.percentEncode(key) + '="' + this.percentEncode(oauth_data[key]) + '"' + this.parameter_seperator;
    }

    return {
        Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length) //cut the last chars
    };
};

/**
 * Create a random word characters string with input length
 * @return {String} a random word characters string
 */
OAuth.prototype.getNonce = function() {
    var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    var result = '';

    for(var i = 0; i < this.nonce_length; i++) {
        result += word_characters[parseInt(Math.random() * word_characters.length, 10)];
    }

    return result;
};

/**
 * Get Current Unix TimeStamp
 * @return {Int} current unix timestamp
 */
OAuth.prototype.getTimeStamp = function() {
    return parseInt(new Date().getTime()/1000, 10);
};

////////////////////// HELPER FUNCTIONS //////////////////////

/**
 * Merge object
 * @param  {Object} obj1
 * @param  {Object} obj2
 * @return {Object}
 */
OAuth.prototype.mergeObject = function(obj1, obj2) {
    var merged_obj = obj1;
    for(var key in obj2) {
        merged_obj[key] = obj2[key];
    }
    return merged_obj;
};

/**
 * Sort object by key
 * @param  {Object} data
 * @return {Object} sorted object
 */
OAuth.prototype.sortObject = function(data) {
    var keys = Object.keys(data);
    var result = {};

    keys.sort();

    for(var i = 0; i < keys.length; i++) {
        var key = keys[i];
        result[key] = data[key];
    }

    return result;
};
<commit_msg>fix empty request.data error https://github.com/ddo/oauth-1.0a/issues/8<commit_after>if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') {
    module.exports = OAuth;
    var CryptoJS = require("crypto-js");
}

/**
 * Constructor
 * @param {Object} opts consumer key and secret
 */
function OAuth(opts) {
    if(!(this instanceof OAuth)) {
        return new OAuth(opts);
    }

    if(!opts) {
        opts = {};
    }

    if(!opts.consumer) {
        throw new Error('consumer option is required');
    }

    this.consumer            = opts.consumer;
    this.signature_method    = opts.signature_method || 'HMAC-SHA1';
    this.nonce_length        = opts.nonce_length || 32;
    this.version             = opts.version || '1.0';
    this.parameter_seperator = opts.parameter_seperator || ', ';

    if(typeof opts.last_ampersand === 'undefined') {
        this.last_ampersand = true;
    } else {
        this.last_ampersand = opts.last_ampersand;
    }

    switch (this.signature_method) {
        case 'HMAC-SHA1':
            this.hash = function(base_string, key) {
                return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;
        case 'PLAINTEXT':
            this.hash = function(base_string, key) {
                return key;
            };
            break;
        case 'RSA-SHA1':
            throw new Error('oauth-1.0a does not support this signature method right now. Coming Soon...');
        default:
            throw new Error('The OAuth 1.0a protocol defines three signature methods: HMAC-SHA1, RSA-SHA1, and PLAINTEXT only');
    }
}

/**
 * OAuth request authorize
 * @param  {Object} request data
 * {
 *     method,
 *     url,
 *     data
 * }
 * @param  {Object} public and secret token
 * @return {Object} OAuth Authorized data
 */
OAuth.prototype.authorize = function(request, token) {
    var oauth_data = {
        oauth_consumer_key: this.consumer.public,
        oauth_nonce: this.getNonce(),
        oauth_signature_method: this.signature_method,
        oauth_timestamp: this.getTimeStamp(),
        oauth_version: this.version
    };

    if(!token) {
        token = {};
    }

    if(token.public) {
        oauth_data.oauth_token = token.public;
    }

    if(!request.data) {
        request.data = {};
    }

    oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data);

    return oauth_data;
};

/**
 * Create a OAuth Signature
 * @param  {Object} request data
 * @param  {Object} token_secret public and secret token
 * @param  {Object} oauth_data   OAuth data
 * @return {String} Signature
 */
OAuth.prototype.getSignature = function(request, token_secret, oauth_data) {
    return this.hash(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret));
};

/**
 * Base String = Method + Base Url + ParameterString
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {String} Base String
 */
OAuth.prototype.getBaseString = function(request, oauth_data) {
    return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data));
};

/**
 * Get data from url
 * -> merge with oauth data
 * -> percent encode key & value
 * -> sort
 *
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {Object} Parameter string data
 */
OAuth.prototype.getParameterString = function(request, oauth_data) {
    var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url)))));

    var data_str = '';

    //base_string_data to string
    for(var key in base_string_data) {
        data_str += key + '=' + base_string_data[key] + '&';
    }

    //remove the last character
    data_str = data_str.substr(0, data_str.length - 1);
    return data_str;
};

/**
 * Create a Signing Key
 * @param  {String} token_secret Secret Token
 * @return {String} Signing Key
 */
OAuth.prototype.getSigningKey = function(token_secret) {
    token_secret = token_secret || '';

    if(!this.last_ampersand && !token_secret) {
        return this.percentEncode(this.consumer.secret);
    }

    return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret);
};

/**
 * Get base url
 * @param  {String} url
 * @return {String}
 */
OAuth.prototype.getBaseUrl = function(url) {
    return url.split('?')[0];
};

/**
 * Get data from String
 * @param  {String} string
 * @return {Object}
 */
OAuth.prototype.deParam = function(string) {
    var arr = decodeURIComponent(string).split('&');
    var data = {};

    for(var i = 0; i < arr.length; i++) {
        var item = arr[i].split('=');
        data[item[0]] = item[1];
    }
    return data;
};

/**
 * Get data from url
 * @param  {String} url
 * @return {Object}
 */
OAuth.prototype.deParamUrl = function(url) {
    var tmp = url.split('?');

    if (tmp.length === 1)
        return {};

    return this.deParam(tmp[1]);
};

/**
 * Percent Encode
 * @param  {String} str
 * @return {String} percent encoded string
 */
OAuth.prototype.percentEncode = function(str) {
    return encodeURIComponent(str)
        .replace(/\!/g, "%21")
        .replace(/\*/g, "%2A")
        .replace(/\'/g, "%27")
        .replace(/\(/g, "%28")
        .replace(/\)/g, "%29");
};

/**
 * Percent Encode Object
 * @param  {Object} data
 * @return {Object} percent encoded data
 */
OAuth.prototype.percentEncodeData = function(data) {
    var result = {};

    for(var key in data) {
        result[this.percentEncode(key)] = this.percentEncode(data[key]);
    }

    return result;
};

/**
 * Get OAuth data as Header
 * @param  {Object} oauth_data
 * @return {String} Header data key - value
 */
OAuth.prototype.toHeader = function(oauth_data) {
    oauth_data = this.sortObject(oauth_data);

    var header_value = 'OAuth ';

    for(var key in oauth_data) {
        if (key.indexOf('oauth_') === -1)
            continue;
        header_value += this.percentEncode(key) + '="' + this.percentEncode(oauth_data[key]) + '"' + this.parameter_seperator;
    }

    return {
        Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length) //cut the last chars
    };
};

/**
 * Create a random word characters string with input length
 * @return {String} a random word characters string
 */
OAuth.prototype.getNonce = function() {
    var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    var result = '';

    for(var i = 0; i < this.nonce_length; i++) {
        result += word_characters[parseInt(Math.random() * word_characters.length, 10)];
    }

    return result;
};

/**
 * Get Current Unix TimeStamp
 * @return {Int} current unix timestamp
 */
OAuth.prototype.getTimeStamp = function() {
    return parseInt(new Date().getTime()/1000, 10);
};

////////////////////// HELPER FUNCTIONS //////////////////////

/**
 * Merge object
 * @param  {Object} obj1
 * @param  {Object} obj2
 * @return {Object}
 */
OAuth.prototype.mergeObject = function(obj1, obj2) {
    var merged_obj = obj1;
    for(var key in obj2) {
        merged_obj[key] = obj2[key];
    }
    return merged_obj;
};

/**
 * Sort object by key
 * @param  {Object} data
 * @return {Object} sorted object
 */
OAuth.prototype.sortObject = function(data) {
    var keys = Object.keys(data);
    var result = {};

    keys.sort();

    for(var i = 0; i < keys.length; i++) {
        var key = keys[i];
        result[key] = data[key];
    }

    return result;
};
<|endoftext|>


<commit_before>var model = require('./model');

var METHODS = [
    'create',
    'read',
    'update',
    'delete'
];

for (var i in METHODS) {
    (function(method) {
        // operations
        exports[method] = function (link) {
            model(createRequest(method, link), createResponseHandler(method, link));
        };

        // listeners
        var serverEvent = 'crud.' + method;
        M.on(serverEvent, function (request, callback) {

            request.method = method;
            request.options = request.options || {};

            if (!callback) {
                callback = function(err) {
                    if (err) {
                        console.error('Error executing server operation: ' + serverEvent);
                        console.error('******************')
                        console.error(err);
                        console.error('------------------')
                        console.error(request);
                        console.error('******************')
                    }
                };
            }
            model(request, function (err, data, count) {

                // handle error
                if (err) {
                    return callback (err);
                }

                // handle no data
                if (!data) {
                    return callback ("No data");
                }

                // handle array
                if (data.constructor.name === "Array") {
                    return callback (null, data, count);
                }

                // handle cursor
                if (typeof data.toArray === "function") {
                    return data.toArray (function (err, docs) {
                        callback (err, docs, count);
                    });
                }

                // for non read operations
                callback (err, data, count);
            });
        });
    })(METHODS[i]);
}

// private functions
function createRequest (method, link) {

    var data = link.data || {};

    // template id is mandatory
    if (!data.t) {
        return null;
    }

    var request = {
        role: link.session.crudRole,
        options: {},
        templateId: data.t,
        method: method,
        session: link.session,
        // TODO remove this when updates on linked fields are possible
        noJoins: data.noJoins
    };

    // query
    request.query = data.q || {};
    request.query._tp = data.t;

    // update
    if (data.d && data.d.constructor.name === 'Object') {

        // set type
        if (method === 'create') {
            data.d._tp = data.t;
        }

        request.data = data.d;
    }

    // options
    if (data.o && data.o.constructor.name === 'Object') {
        request.options = data.o;
    } else {
        request.options = {};
    }

    return request;
}

function createResponseHandler (method, link) {
    return function(err, results, readCount) {

        if (err) {
            return link.send(err.statusCode || 500, err.message || err.toString() || err);
        }

        link.res.headers['content-type'] = 'application/json; charset=utf-8';

        // TODO How can this be fixed using a better way?
        var constructorNameOfResults = results.constructor.name;
        if (results && constructorNameOfResults === "Object" && typeof results.toArray === "function") {
            constructorNameOfResults = "Cursor";
        }

         // if we have an array or a cursor, set X-Mono-CRUD-Count response header
        if (["Cursor", "Array"].indexOf(constructorNameOfResults) !== -1) {
            link.res.headers['X-Mono-CRUD-Count'] = (readCount || 0).toString();
        }

        if (method === 'read' && constructorNameOfResults === 'Cursor') {

            // stream result
            var stream = results.stream();
            link.stream.start(200);

            stream.on('end', function() {
                link.stream.end();
            });
            stream.on('error', function(err) {
                link.stream.error(500, err.toString());
            });
            stream.on('data', function(data) {
                link.stream.data(data);
            });

        } else {
            link.send(200, results);
        }
    };
}
<commit_msg>Modified conditions for converting cursors to arrays<commit_after>var model = require('./model');

var METHODS = [
    'create',
    'read',
    'update',
    'delete'
];

for (var i in METHODS) {
    (function(method) {
        // operations
        exports[method] = function (link) {
            model(createRequest(method, link), createResponseHandler(method, link));
        };

        // listeners
        var serverEvent = 'crud.' + method;
        M.on(serverEvent, function (request, callback) {

            request.method = method;
            request.options = request.options || {};

            if (!callback) {
                callback = function(err) {
                    if (err) {
                        console.error('Error executing server operation: ' + serverEvent);
                        console.error('******************')
                        console.error(err);
                        console.error('------------------')
                        console.error(request);
                        console.error('******************')
                    }
                };
            }
            model(request, function (err, data, count) {

                // handle error
                if (err) {
                    return callback (err);
                }

                // handle array
                if (data && data.constructor.name === "Array") {
                    return callback (null, data, count);
                }

                // handle cursor
                if (data && typeof data.toArray === "function") {
                    return data.toArray (function (err, docs) {
                        callback (err, docs, count);
                    });
                }

                // for non read operations
                callback (err, data, count);
            });
        });
    })(METHODS[i]);
}

// private functions
function createRequest (method, link) {

    var data = link.data || {};

    // template id is mandatory
    if (!data.t) {
        return null;
    }

    var request = {
        role: link.session.crudRole,
        options: {},
        templateId: data.t,
        method: method,
        session: link.session,
        // TODO remove this when updates on linked fields are possible
        noJoins: data.noJoins
    };

    // query
    request.query = data.q || {};
    request.query._tp = data.t;

    // update
    if (data.d && data.d.constructor.name === 'Object') {

        // set type
        if (method === 'create') {
            data.d._tp = data.t;
        }

        request.data = data.d;
    }

    // options
    if (data.o && data.o.constructor.name === 'Object') {
        request.options = data.o;
    } else {
        request.options = {};
    }

    return request;
}

function createResponseHandler (method, link) {
    return function(err, results, readCount) {

        if (err) {
            return link.send(err.statusCode || 500, err.message || err.toString() || err);
        }

        link.res.headers['content-type'] = 'application/json; charset=utf-8';

        // TODO How can this be fixed using a better way?
        var constructorNameOfResults = results.constructor.name;
        if (results && constructorNameOfResults === "Object" && typeof results.toArray === "function") {
            constructorNameOfResults = "Cursor";
        }

         // if we have an array or a cursor, set X-Mono-CRUD-Count response header
        if (["Cursor", "Array"].indexOf(constructorNameOfResults) !== -1) {
            link.res.headers['X-Mono-CRUD-Count'] = (readCount || 0).toString();
        }

        if (method === 'read' && constructorNameOfResults === 'Cursor') {

            // stream result
            var stream = results.stream();
            link.stream.start(200);

            stream.on('end', function() {
                link.stream.end();
            });
            stream.on('error', function(err) {
                link.stream.error(500, err.toString());
            });
            stream.on('data', function(data) {
                link.stream.data(data);
            });

        } else {
            link.send(200, results);
        }
    };
}
<|endoftext|>


<commit_before>var SocketIO = require("socket.io");
var io;

/*
 *  Init
 * */
exports.init = function (link) {
    init();
};

/*
 *  Listen
 * */
exports.listen = function (link) {

    // call listen function
    listen(link.data.options, function (err, data) {

        // handle error
        if (err) { return link.send(400, err); }

        // send success response
        link.send(200, data);
    });
};

/*
 *  Emit
 * */
exports.emit = function (link) {

    // call emit function
    emit(link.data.options, function (err, data) {

        // handle error
        if (err) { return link.send(400, err); }

        // send success response
        link.send(200, data);
    });
};

/*
 *  init ()
 *  This function set the io variable
 *
 * */
function init () {
    // Socket.io server listens to our app
    io = SocketIO.listen(M.app.server);
}

/*
 *  emit (object, function)
 *  Emit some event and data
 *
 *  options:
 *   - event: the event name
 *   - data:  data that must be emited
 *
 *  callback:
 *   - callback function
 * */
function emit (options, callback) {

    // options must be an object
    options = options || {};

    // callback must be a function
    callback = callback || function () {};

    // init io, if it is not inited
    if (!io) { init(); }

    // emit that event and data
    io.sockets.emit(options.event, options.data);
    callback();
}

/*
 *  listen (object, function)
 *  Listen on some event and callback the socket
 *
 *  options:
 *   - event: the event name that listen to
 *
 *  callback: the function that must be fired
 *            on that event
 *
 * */
function listen (options, callback) {

    // options must be an object
    options = options || {};

    // callback must be a function
    callback = callback || function () {};

    // init io, if it is not inited
    if (!io) { init(); }

    // listen on that event
    io.sockets.on(options.event, callback);
}

/*
 *  The events interface:
 *   - sockets.init
 *   - sockets.emit
 *   - sockets.listen
 * */
M.on("sockets.init", init);
M.on("sockets.emit", emit);
M.on("sockets.listen", listen);

/*
 *  Emits ready, so the custom server script must listen it
 * */
M.emit("sockets.ready");
<commit_msg>Init function with callback and options<commit_after>var SocketIO = require("socket.io");
var io;

/*
 *  Init
 * */
exports.init = function (link) {
    init(link.data.options, function (err, data) {
        // handle error
        if (err) { return link.send(400, err); }

        // send success response
        link.send(200, data);
    });
};

/*
 *  Listen
 * */
exports.listen = function (link) {

    // call listen function
    listen(link.data.options, function (err, data) {

        // handle error
        if (err) { return link.send(400, err); }

        // send success response
        link.send(200, data);
    });
};

/*
 *  Emit
 * */
exports.emit = function (link) {

    // call emit function
    emit(link.data.options, function (err, data) {

        // handle error
        if (err) { return link.send(400, err); }

        // send success response
        link.send(200, data);
    });
};

/*
 *  init ()
 *  This function set the io variable
 *
 * */
function init (options, callback) {

    // options must be an object
    options = options || {};

    // callback must be a function
    callback = callback || function () {};

    if (io && !options.force) {
        return callback();
    }

    // Socket.io server listens to our app
    io = SocketIO.listen(M.app.server);

    callback();
}

/*
 *  emit (object, function)
 *  Emit some event and data
 *
 *  options:
 *   - event: the event name
 *   - data:  data that must be emited
 *
 *  callback:
 *   - callback function
 * */
function emit (options, callback) {

    // options must be an object
    options = options || {};

    // callback must be a function
    callback = callback || function () {};

    // init io, if it is not inited
    if (!io) { init(); }

    // emit that event and data
    io.sockets.emit(options.event, options.data);
    callback();
}

/*
 *  listen (object, function)
 *  Listen on some event and callback the socket
 *
 *  options:
 *   - event: the event name that listen to
 *
 *  callback: the function that must be fired
 *            on that event
 *
 * */
function listen (options, callback) {

    // options must be an object
    options = options || {};

    // callback must be a function
    callback = callback || function () {};

    // init io, if it is not inited
    if (!io) { init(); }

    // listen on that event
    io.sockets.on(options.event, callback);
}

/*
 *  The events interface:
 *   - sockets.init
 *   - sockets.emit
 *   - sockets.listen
 * */
M.on("sockets.init", init);
M.on("sockets.emit", emit);
M.on("sockets.listen", listen);

/*
 *  Emits ready, so the custom server script must listen it
 * */
M.emit("sockets.ready");
<|endoftext|>


<commit_before>import { Component } from 'react';
import { axios } from '../axios';

export class Polling extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: false,
      data: null,
      error: null
    };
  }

  async componentDidMount() {
    await this.fetch();
    await this.fetchWithTimeout();
  }

  fetch() {
    this.setState({
      loading: true
    });
    return axios
      .get(this.props.url)
      .then((response) => {
        this.setState({ data: response.data, error: null, loading: false });
      })
      .catch((e) => {
        console.log(e);
        let {
          response: { data: error }
        } = e;
        if (typeof error === 'object') {
          error = error.error;
        }
        this.setState({ data: null, error, loading: false });
      });
  }

  fetchWithTimeout() {
    if (this.props.interval < 0) return;
    this.timeout = setTimeout(async () => {
      await this.fetch();
      this.fetchWithTimeout();
    }, this.props.interval);
  }

  componentWillUnmount() {
    clearTimeout(this.timeout);
  }
  render() {
    return this.props.children({
      data: this.state.data || null,
      error: this.state.error || null,
      loading: this.state.loading
    });
  }
}

export default Polling;
<commit_msg>fix polling in case of network error<commit_after>import { Component } from 'react';
import { axios } from '../axios';

export class Polling extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: false,
      data: null,
      error: null
    };
  }

  async componentDidMount() {
    await this.fetch();
    await this.fetchWithTimeout();
  }

  fetch() {
    this.setState({
      loading: true
    });
    return axios
      .get(this.props.url)
      .then((response) => {
        this.setState({ data: response.data, error: null, loading: false });
      })
      .catch((e) => {
        let error = e.message;
        if (e.response) {
          if (typeof e.response.data === 'string') {
            error = e.response.data;
          } else if (
            typeof e.response.data === 'object' &&
            e.response.data !== null
          ) {
            error = e.response.data.error;
          }
        }
        this.setState({ data: null, error, loading: false });
      });
  }

  fetchWithTimeout() {
    if (this.props.interval < 0) return;
    this.timeout = setTimeout(async () => {
      await this.fetch();
      this.fetchWithTimeout();
    }, this.props.interval);
  }

  componentWillUnmount() {
    clearTimeout(this.timeout);
  }
  render() {
    return this.props.children({
      data: this.state.data || null,
      error: this.state.error || null,
      loading: this.state.loading
    });
  }
}

export default Polling;
<|endoftext|>


<commit_before>var monthNames = ["January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
];

window.onload = function () {
    var dailyChart = c3.generate({
        bindto: '#dailyChart',
        types: {
            Monthly: 'bar'
        },
        data: {
            x: 'labels',
            columns: [['labels'], ['Monthly']],
            type: 'bar',
        },
        axis: {
            x: {
                type: 'category',
                tick: {
                    rotate: 75,
                    multiline: false
                },
                height: 80
            }
        },
        legend: {
            //show: false
        },
        tooltip: {
            grouped: false
        }
    })

    var hourlyChart = c3.generate({
        bindto: '#hourlyChart',
        data: {
            x: 'labels',
            columns: [['labels'], ['Views']],
        },
        axis: {
            x: {
                type: 'bar',
                tick: {
                    rotate: 75,
                    multiline: false,
                    culling: {
                        max: 7 // the number of tick texts will be adjusted to less than this value
                    }
                },
                height: 100
            }
        },
        legend: {
            show: false
        },
    })

    var start_date = new Date();
    start_date.setDate(start_date.getDate() - 7); // week worth of data
    var end_date = 'newest';

    $.get('/analytics/pageviews?start-date=' + formatDate(start_date) + '&end-date=' + end_date)
    .done(function (resp) {
        var daily_groups = resp['daily_groups'];
        var groups = Object.keys(daily_groups);
        var labels = ['labels'];
        var columns = [];
        var users = _.range(resp['user_count'] + 1);
        _.each(users, function(user){ columns[user] = [] })

        groups.forEach(function(group){
            // create labels
            date_obj = new Date(group);
            labels.push(monthNames[date_obj.getMonth()] + ' ' + date_obj.getDate());

            // create data
            var users_in_group = [];
            // for users in period add their counts
            Object.keys(daily_groups[group]).forEach(function(str_user_id){
                user_id = parseInt(str_user_id);
                columns[user_id].push(daily_groups[group][user_id]);
                users_in_group.push(user_id);
            });
            // for all other users add 0
            users.filter(function(user_id){
                return users_in_group.indexOf(user_id) < 0;
            })
            .forEach(function(user_id){
                columns[user_id].push(0);
            });
        });

        for(i = 0; i < columns.length; i++){
            columns[i].unshift("U"+i);
        };

        dailyChart.groups([columns.map(function(column){return column[0]})]);
        columns.push(labels);

        dailyChart.load({
            columns: columns
        });

    });

    $.get('/analytics/pageviewshourly?start-date=' + formatDate(start_date) + '&end-date=' + end_date)
    .done(function(resp){
        var hourly_groups = resp['hourly_groups'];
        var groups = Object.keys(hourly_groups);
        var start_date = new Date(groups[0]);
        var end_date = new Date(groups[groups.length - 1]);
        var columns = [];

        columns[0] = getDates(start_date, end_date).map(function(date_obj){
            // manualy pad days
            var hours = date_obj.getHours() < 10 ? '0' + date_obj.getHours() : date_obj.getHours();
            var days = date_obj.getDate() < 10 ? '0' + date_obj.getDate() : date_obj.getDate();
            return monthNames[date_obj.getMonth()] + ' ' + days + ', ' + date_obj.getFullYear() + " " + hours + ":00:00";
        });

        columns[1] = [];
        columns[0].map(function(date_str){
            if(hourly_groups[date_str] != undefined){
                columns[1].push(hourly_groups[date_str]);
            }else{
                columns[1].push(0);
            }
        });

        columns[0].splice(0,1,'labels');
        columns[1].splice(0,1,'Views');
        hourlyChart.load({
            columns: columns
        })
    })
};

function getDates(startDate, stopDate) {
    var dateArray = new Array();
    var currentDate = startDate;
    while (currentDate <= stopDate) {
        dateArray.push( new Date (currentDate) )
        currentDate = currentDate.addHours(1);
    }
    return dateArray;
}
<commit_msg>fix display<commit_after>var monthNames = ["January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
];

window.onload = function () {
    var dailyChart = c3.generate({
        bindto: '#dailyChart',
        types: {
            Monthly: 'bar'
        },
        data: {
            x: 'labels',
            columns: [['labels'], ['Monthly']],
            type: 'bar',
        },
        axis: {
            x: {
                type: 'category',
                tick: {
                    rotate: 75,
                    multiline: false
                },
                height: 80
            }
        },
        legend: {
            //show: false
        },
        tooltip: {
            grouped: false
        }
    })

    var hourlyChart = c3.generate({
        bindto: '#hourlyChart',
        data: {
            x: 'labels',
            columns: [['labels'], ['Views']],
            type: 'bar'
        },
        axis: {
            x: {
                type: 'category',
                tick: {
                    rotate: 75,
                    multiline: false,
                    culling: {
                        max: 7 // the number of tick texts will be adjusted to less than this value
                    }
                },
                height: 100
            }
        },
        legend: {
            show: false
        },
    })

    var start_date = new Date();
    start_date.setDate(start_date.getDate() - 7); // week worth of data
    var end_date = 'newest';

    $.get('/analytics/pageviews?start-date=' + formatDate(start_date) + '&end-date=' + end_date)
    .done(function (resp) {
        var daily_groups = resp['daily_groups'];
        var groups = Object.keys(daily_groups);
        var labels = ['labels'];
        var columns = [];
        var users = _.range(resp['user_count'] + 1);
        _.each(users, function(user){ columns[user] = [] })

        groups.forEach(function(group){
            // create labels
            date_obj = new Date(group);
            labels.push(monthNames[date_obj.getMonth()] + ' ' + date_obj.getDate());

            // create data
            var users_in_group = [];
            // for users in period add their counts
            Object.keys(daily_groups[group]).forEach(function(str_user_id){
                user_id = parseInt(str_user_id);
                columns[user_id].push(daily_groups[group][user_id]);
                users_in_group.push(user_id);
            });
            // for all other users add 0
            users.filter(function(user_id){
                return users_in_group.indexOf(user_id) < 0;
            })
            .forEach(function(user_id){
                columns[user_id].push(0);
            });
        });

        for(i = 0; i < columns.length; i++){
            columns[i].unshift("U"+i);
        };

        dailyChart.groups([columns.map(function(column){return column[0]})]);
        columns.push(labels);

        dailyChart.load({
            columns: columns
        });

    });

    $.get('/analytics/pageviewshourly?start-date=' + formatDate(start_date) + '&end-date=' + end_date)
    .done(function(resp){
        var hourly_groups = resp['hourly_groups'];
        var groups = Object.keys(hourly_groups);
        var start_date = new Date(groups[0]);
        var end_date = new Date(groups[groups.length - 1]);
        var columns = [];

        columns[0] = getDates(start_date, end_date).map(function(date_obj){
            // manualy pad days
            var hours = date_obj.getHours() < 10 ? '0' + date_obj.getHours() : date_obj.getHours();
            var days = date_obj.getDate() < 10 ? '0' + date_obj.getDate() : date_obj.getDate();
            return monthNames[date_obj.getMonth()] + ' ' + days + ', ' + date_obj.getFullYear() + " " + hours + ":00:00";
        });

        columns[1] = [];
        columns[0].map(function(date_str){
            if(hourly_groups[date_str] != undefined){
                columns[1].push(hourly_groups[date_str]);
            }else{
                columns[1].push(0);
            }
        });

        columns[0].splice(0,1,'labels');
        columns[1].splice(0,1,'Views');
        hourlyChart.load({
            columns: columns
        })
    })
};

function getDates(startDate, stopDate) {
    var dateArray = new Array();
    var currentDate = startDate;
    while (currentDate <= stopDate) {
        dateArray.push( new Date (currentDate) )
        currentDate = currentDate.addHours(1);
    }
    return dateArray;
}
<|endoftext|>


<commit_before>import ReactiveBase from "./components/basic/ReactiveBase";
import TextField from "./components/basic/TextField";
import SelectedFilters from "./components/basic/SelectedFilters";
import ToggleButton from "./components/basic/ToggleButton";
import NumberBox from "./components/basic/NumberBox";

import SingleList from "./components/list/SingleList";
import MultiList from "./components/list/MultiList";
import SingleDropdownList from "./components/list/SingleDropdownList";
import MultiDropdownList from "./components/list/MultiDropdownList";

import SingleRange from "./components/range/SingleRange";
import MultiRange from "./components/range/MultiRange";
import SingleDropdownRange from "./components/range/SingleDropdownRange";
import MultiDropdownRange from "./components/range/MultiDropdownRange";

import DataSearch from "./components/search/DataSearch";

import ReactiveList from "./components/result/ReactiveList";

export {
	// basic
	ReactiveBase,
	TextField,
	SelectedFilters,
	ToggleButton,
	NumberBox,

	// list
	SingleList,
	MultiList,
	SingleDropdownList,
	MultiDropdownList,

	// range
	SingleRange,
	MultiRange,
	SingleDropdownRange,
	MultiDropdownRange,

	// search
	DataSearch,

	// result
	ReactiveList
}
<commit_msg>Update exports<commit_after>import ReactiveBase from "./components/basic/ReactiveBase";
import TextField from "./components/basic/TextField";
import SelectedFilters from "./components/basic/SelectedFilters";
import ToggleButton from "./components/basic/ToggleButton";
import NumberBox from "./components/basic/NumberBox";

import SingleList from "./components/list/SingleList";
import MultiList from "./components/list/MultiList";
import SingleDropdownList from "./components/list/SingleDropdownList";
import MultiDropdownList from "./components/list/MultiDropdownList";

import SingleRange from "./components/range/SingleRange";
import MultiRange from "./components/range/MultiRange";
import SingleDropdownRange from "./components/range/SingleDropdownRange";
import MultiDropdownRange from "./components/range/MultiDropdownRange";
import RangeSlider from "./components/range/RangeSlider";

import DataSearch from "./components/search/DataSearch";

import ReactiveList from "./components/result/ReactiveList";

export {
	// basic
	ReactiveBase,
	TextField,
	SelectedFilters,
	ToggleButton,
	NumberBox,

	// list
	SingleList,
	MultiList,
	SingleDropdownList,
	MultiDropdownList,

	// range
	SingleRange,
	MultiRange,
	SingleDropdownRange,
	MultiDropdownRange,
	RangeSlider,

	// search
	DataSearch,

	// result
	ReactiveList
}
<|endoftext|>