<commit_before>goog.provide('ngeo.Query');

goog.require('ngeo');
goog.require('ol.format.WMSGetFeatureInfo');
goog.require('ol.source.ImageWMS');
goog.require('ol.source.TileWMS');

/**
 * @typedef {{
 *     resultSource: (ngeo.QueryResultSource),
 *     source: (ngeox.QuerySource)
 * }}
 */
ngeo.QueryCacheItem;

/**
 * @enum {string}
 */
ngeo.QueryInfoFormatType = {
  GML: 'application/vnd.ogc.gml'
};

/**
 * @typedef {{
 *     sources: (Array.<ngeo.QueryResultSource>),
 *     total: (number)
 * }}
 */
ngeo.QueryResult;

/**
 * @typedef {{
 *     features: (Array.<ol.Feature>),
 *     id: (number|string),
 *     label: (string),
 *     pending: (boolean)
 * }}
 */
ngeo.QueryResultSource;

/**
 * The `ngeoQueryResult` is the value service where the features of the query
 * result are added.
 */
ngeo.module.value('ngeoQueryResult', /** @type {ngeo.QueryResult} */ ({
  sources: [],
  total: 0
}));

/**
 * The Query service provides a way to send WMS GetFeatureInfo requests from
 * visible layer objects within a map. Those do not necessarily need to have
 * a WMS source. The Query service requires source configuration in order
 * for layers to actually be considered queryable.
 *
 * To know more about the specification of a source configuration, see
 * `ngeox.QuerySource`
 *
 * @constructor
 * @param {angular.$http} $http Angular $http service.
 * @param {ngeo.QueryResult} ngeoQueryResult The ngeo query result service.
 * @param {ngeox.QueryOptions|undefined} ngeoQueryOptions The options to
 *     configure the ngeo query service with.
 * @param {ngeo.LayerHelper} ngeoLayerHelper Ngeo Layer Helper.
 * @ngdoc service
 * @ngname ngeoQuery
 * @ngInject
 */
ngeo.Query = function($http, ngeoQueryResult, ngeoQueryOptions,
    ngeoLayerHelper) {

  var options = ngeoQueryOptions !== undefined ? ngeoQueryOptions : {};

  /**
   * @type {number}
   * @private
   */
  this.limit_ = options.limit !== undefined ? options.limit : 50;

  /**
   * @type {string}
   * @private
   */
  this.sourceIdProperty_ = options.sourceIdProperty !== undefined ?
      options.sourceIdProperty : ngeo.Query.DEFAULT_SOURCE_ID_PROPERTY_;

  /**
   * @type {string}
   * @private
   */
  this.sourceIdsProperty_ = options.sourceIdsProperty !== undefined ?
      options.sourceIdsProperty : ngeo.Query.DEFAULT_SOURCE_IDS_PROPERTY_;

  /**
   * @type {ngeo.LayerHelper}
   * @private
   */
  this.ngeoLayerHelper_ = ngeoLayerHelper;

  /**
   * @type {angular.$http}
   * @private
   */
  this.$http_ = $http;

  /**
   * @type {ngeo.QueryResult}
   * @private
   */
  this.result_ = ngeoQueryResult;

  /**
   * @type {Array.<ngeox.QuerySource>}
   * @private
   */
  this.sources_ = [];

  /**
   * @type {Object.<number|string, ngeo.QueryCacheItem>}
   * @private
   */
  this.cache_ = {};
};

/**
 * @const
 * @private
 */
ngeo.Query.DEFAULT_SOURCE_ID_PROPERTY_ = 'querySourceId';

/**
 * @const
 * @private
 */
ngeo.Query.DEFAULT_SOURCE_IDS_PROPERTY_ = 'querySourceIds';

/**
 * Adds a new source to the query service.
 *
 * A source must at least have an `id` configured.  That id is then used to
 * associate the the corresponding layer object within a map.
 *
 * A source will require a `ol.source.ImageWMS` or `ol.source.TileWMS` object.
 * You can either set it directly in the config, or use the one from a given
 * layer or let the query service create one from you using other source config
 * such as `url` and `params`.
 *
 * A source can be set with either a `format` and/or `infoFormat`, which will
 * determine how the returned features of a query will be read.
 *
 * This method will also create a result entry in the `ngeoQueryResult`
 * value service.
 *
 * @param {ngeox.QuerySource} source The source to add to the query service.
 * @export
 */
ngeo.Query.prototype.addSource = function(source) {
  var sourceId = source.id;

  goog.asserts.assert(sourceId, 'source.id should be thruthy');
  goog.asserts.assert(!this.cache_[sourceId],
      'no other source with the same id should be present');

  // == wmsSource ==
  // if the source doesn't have a wmsSource property set, it must at least have
  // a layer that has one or have the required configuration options in order
  // to create one.
  if (!source.wmsSource) {
    if (source.layer &&
        (source.layer instanceof ol.layer.Image ||
         source.layer instanceof ol.layer.Tile)) {
      var wmsSource = source.layer.getSource();
      if (wmsSource &&
          (wmsSource instanceof ol.source.ImageWMS ||
           wmsSource instanceof ol.source.TileWMS)) {
        source.wmsSource =
            /** @type {ol.source.ImageWMS|ol.source.TileWMS} */ (wmsSource);
      }
    } else {
      var url = source.url;
      var params = source.params;
      goog.asserts.assert(url,
          'url must be set when no layer or wmsSource is set in the source');
      goog.asserts.assert(params,
          'parmas must be set when no layer or wmsSource is set in the source');
      source.wmsSource = new ol.source.ImageWMS({
        url: url,
        params: params
      });
    }
  }
  goog.asserts.assert(source.wmsSource, 'wmsSource should be thruthy');

  // == format ==
  if (!source.format) {
    // GML is the default infoFormat if the source doesn't have one defined
    if (!source.infoFormat) {
      source.infoFormat = ngeo.QueryInfoFormatType.GML;
    }

    var layers = source.wmsSource.getParams()['LAYERS'].split(',');

    if (source.infoFormat === ngeo.QueryInfoFormatType.GML) {
      source.format = new ol.format.WMSGetFeatureInfo({
        layers: layers
      });
    }
  } else if (!source.infoFormat) {
    // == infoFormat ==
    var format = source.format;
    if (format instanceof ol.format.WMSGetFeatureInfo) {
      source.infoFormat = ngeo.QueryInfoFormatType.GML;
    }
  }
  goog.asserts.assert(source.format, 'format should be thruthy');

  this.sources_.push(source);

  var sourceLabel = source.label !== undefined ? source.label : sourceId;

  var sourceIdentifierAttributeField =
      source.identifierAttributeField !== undefined ?
      source.identifierAttributeField : sourceId;

  var resultSource = /** @type {ngeo.QueryResultSource} */ ({
    'features': [],
    'id': sourceId,
    'identifierAttributeField': sourceIdentifierAttributeField,
    'label': sourceLabel,
    'pending': false
  });

  this.result_.sources.push(resultSource);

  var cacheItem = {
    'source': source,
    'resultSource': resultSource
  };
  this.cache_[sourceId] = cacheItem;
};

/**
 * Add multiple sources at once in the order they are given.
 * @param {Array.<ngeox.QuerySource>} sources The sources to add to the query
 *     service.
 * @export
 */
ngeo.Query.prototype.addSources = function(sources) {
  sources.forEach(this.addSource, this);
};

/**
 * Clear the results.
 * @export
 */
ngeo.Query.prototype.clear = function() {
  this.clearResult_();
};

/**
 * Issue a new request using a given map and a given object, which can be
 * a coordinate or extent.
 *
 * NOTE: only coordinates are currently supported.
 *
 * @param {ol.Map} map The ol3 map object to fetch the layers from.
 * @param {ol.Coordinate|ol.Extent} object The coordinate or extent to issue
 *     the request with.
 * @export
 */
ngeo.Query.prototype.issue = function(map, object) {

  this.clearResult_();

  if (object.length === 2) {
    this.issueWMSGetFeatureInfoRequests_(map, object);
  }
};

/**
 * Issue a new WMS GetFeatureInfo request using a given map and a coordinate.
 * For each visible layer of the map, if that layer has a source configured
 * within this query service, then a query will be sent and the results
 * will be stocked in the `ngeoQueryResult`.
 *
 * If multiple sources share the same url and use GML as info format, then
 * only one request will be sent for all these sources.
 *
 * NOTE: Only GML info format are currently supported.
 *
 * @param {ol.Map} map The ol3 map object to fetch the layers from.
 * @param {ol.Coordinate} coordinate The coordinate to issue the request with.
 * @private
 */
ngeo.Query.prototype.issueWMSGetFeatureInfoRequests_ = function(
    map, coordinate) {

  var view = map.getView();
  var projCode = view.getProjection().getCode();

  var id;
  var ids;
  var infoFormat;
  var url;
  var item;
  var wmsGetFeatureInfoUrl;

  var itemsByUrl =
      /** @type {Object.<string, Array.<ngeo.QueryCacheItem>>} */ ({});

  var resolution = /** @type {number} */ (view.getResolution());

  var layers = this.ngeoLayerHelper_.getFlatLayers(map.getLayerGroup());

  layers.forEach(function(layer) {

    // Skip layers that are not visible
    if (!layer.getVisible()) {
      return;
    }

    // Skip layers that don't have one or more sources configured
    id = this.getLayerSourceId_(layer);
    ids = this.getLayerSourceIds_(layer);
    if ((!id || !this.cache_[id]) && !ids.length) {
      return;
    }

    if (id) {
      ids.push(id);
    }

    for (var i = 0, len = ids.length; i < len; i++) {
      id = ids[i];
      item = this.cache_[id];

      // If `validateLayerParams` is set, then the source config layer in the
      // LAYERS params must be in the current LAYERS params of the layer
      // wms source object.
      if (item.source.validateLayerParams) {
        goog.asserts.assert(
            layer instanceof ol.layer.Image ||
            layer instanceof ol.layer.Tile,
            'The layer should be an Image or Tile when using the ' +
            'validateLayerParams option.'
        );
        var layerSource = layer.getSource();
        goog.asserts.assert(
            layerSource instanceof ol.source.ImageWMS ||
            layerSource instanceof ol.source.TileWMS,
            'The layer source should be a WMS one when using the ' +
            'validateLayerParams option.'
        );
        var layerLayers = layerSource.getParams()['LAYERS'].split(',');
        var cfgLayer = item.source.wmsSource.getParams()['LAYERS'];
        if (layerLayers.indexOf(cfgLayer) === -1) {
          continue;
        }
      }

      item['resultSource'].pending = true;
      infoFormat = item.source.infoFormat;

      // Sources that use GML as info format are combined together if they
      // share the same server url
      if (infoFormat === ngeo.QueryInfoFormatType.GML) {
        url = item.source.wmsSource.getUrl();
        goog.asserts.assertString(url);
        if (!itemsByUrl[url]) {
          itemsByUrl[url] = [];
        }
        itemsByUrl[url].push(item);
      } else {
        // TODO - support other kinds of infoFormats
        item['resultSource'].pending = false;
      }
    }
  }, this);

  goog.object.forEach(itemsByUrl, function(items) {

    infoFormat = items[0].source.infoFormat;
    var layers = items[0].source.wmsSource.getParams()['LAYERS'].split(',');

    wmsGetFeatureInfoUrl = items[0].source.wmsSource.getGetFeatureInfoUrl(
        coordinate, resolution, projCode, {
          'INFO_FORMAT': infoFormat,
          'FEATURE_COUNT': this.limit_
        });

    goog.asserts.assert(
        wmsGetFeatureInfoUrl, 'WMS GetFeatureInfo url should be thruty');

    for (var i = 1, len = items.length; i < len; i++) {
      layers = layers.concat(
          items[i].source.wmsSource.getParams()['LAYERS'].split(','));
    }

    var lyrStr = layers.join(',');

    wmsGetFeatureInfoUrl =
        goog.uri.utils.setParam(wmsGetFeatureInfoUrl, 'LAYERS', lyrStr);
    wmsGetFeatureInfoUrl =
        goog.uri.utils.setParam(wmsGetFeatureInfoUrl, 'QUERY_LAYERS', lyrStr);

    this.$http_.get(wmsGetFeatureInfoUrl).then(function(items, response) {
      items.forEach(function(item) {
        var format = item.source.format;
        var features = format.readFeatures(response.data);
        item['resultSource'].pending = false;
        item['resultSource'].features = features;
        this.result_.total += features.length;
      }, this);
    }.bind(this, items));
  }, this);
};

/**
 * Clear every features for all result sources and reset the total counter
 * as well.
 * @private
 */
ngeo.Query.prototype.clearResult_ = function() {
  this.result_.total = 0;
  this.result_.sources.forEach(function(source) {
    source.features.length = 0;
  }, this);
};

/**
 * Returns the source id from an ol3 layer object.
 * @param {ol.layer.Base} layer The ol3 layer object.
 * @return {number|string} id The id of the source bound to that layer.
 * @private
 */
ngeo.Query.prototype.getLayerSourceId_ = function(layer) {
  var id = layer.get(this.sourceIdProperty_);
  id = goog.isNumber(id) || goog.isString(id) ? id : '';
  return id;
};

/**
 * Returns the source ids from an ol3 layer object.
 * @param {ol.layer.Base} layer The ol3 layer object.
 * @return {Array.<number|string>} ids The ids of the sources bound to that
 *     layer.
 * @private
 */
ngeo.Query.prototype.getLayerSourceIds_ = function(layer) {
  var ids = layer.get(this.sourceIdsProperty_) || [];
  goog.asserts.assertArray(ids);
  var clone = ids.slice()
  return clone;
};

ngeo.module.service('ngeoQuery', ngeo.Query);
<commit_msg>Add missing require<commit_after>goog.provide('ngeo.Query');

goog.require('ngeo');
goog.require('ngeo.LayerHelper');
goog.require('ol.format.WMSGetFeatureInfo');
goog.require('ol.source.ImageWMS');
goog.require('ol.source.TileWMS');

/**
 * @typedef {{
 *     resultSource: (ngeo.QueryResultSource),
 *     source: (ngeox.QuerySource)
 * }}
 */
ngeo.QueryCacheItem;

/**
 * @enum {string}
 */
ngeo.QueryInfoFormatType = {
  GML: 'application/vnd.ogc.gml'
};

/**
 * @typedef {{
 *     sources: (Array.<ngeo.QueryResultSource>),
 *     total: (number)
 * }}
 */
ngeo.QueryResult;

/**
 * @typedef {{
 *     features: (Array.<ol.Feature>),
 *     id: (number|string),
 *     label: (string),
 *     pending: (boolean)
 * }}
 */
ngeo.QueryResultSource;

/**
 * The `ngeoQueryResult` is the value service where the features of the query
 * result are added.
 */
ngeo.module.value('ngeoQueryResult', /** @type {ngeo.QueryResult} */ ({
  sources: [],
  total: 0
}));

/**
 * The Query service provides a way to send WMS GetFeatureInfo requests from
 * visible layer objects within a map. Those do not necessarily need to have
 * a WMS source. The Query service requires source configuration in order
 * for layers to actually be considered queryable.
 *
 * To know more about the specification of a source configuration, see
 * `ngeox.QuerySource`
 *
 * @constructor
 * @param {angular.$http} $http Angular $http service.
 * @param {ngeo.QueryResult} ngeoQueryResult The ngeo query result service.
 * @param {ngeox.QueryOptions|undefined} ngeoQueryOptions The options to
 *     configure the ngeo query service with.
 * @param {ngeo.LayerHelper} ngeoLayerHelper Ngeo Layer Helper.
 * @ngdoc service
 * @ngname ngeoQuery
 * @ngInject
 */
ngeo.Query = function($http, ngeoQueryResult, ngeoQueryOptions,
    ngeoLayerHelper) {

  var options = ngeoQueryOptions !== undefined ? ngeoQueryOptions : {};

  /**
   * @type {number}
   * @private
   */
  this.limit_ = options.limit !== undefined ? options.limit : 50;

  /**
   * @type {string}
   * @private
   */
  this.sourceIdProperty_ = options.sourceIdProperty !== undefined ?
      options.sourceIdProperty : ngeo.Query.DEFAULT_SOURCE_ID_PROPERTY_;

  /**
   * @type {string}
   * @private
   */
  this.sourceIdsProperty_ = options.sourceIdsProperty !== undefined ?
      options.sourceIdsProperty : ngeo.Query.DEFAULT_SOURCE_IDS_PROPERTY_;

  /**
   * @type {ngeo.LayerHelper}
   * @private
   */
  this.ngeoLayerHelper_ = ngeoLayerHelper;

  /**
   * @type {angular.$http}
   * @private
   */
  this.$http_ = $http;

  /**
   * @type {ngeo.QueryResult}
   * @private
   */
  this.result_ = ngeoQueryResult;

  /**
   * @type {Array.<ngeox.QuerySource>}
   * @private
   */
  this.sources_ = [];

  /**
   * @type {Object.<number|string, ngeo.QueryCacheItem>}
   * @private
   */
  this.cache_ = {};
};

/**
 * @const
 * @private
 */
ngeo.Query.DEFAULT_SOURCE_ID_PROPERTY_ = 'querySourceId';

/**
 * @const
 * @private
 */
ngeo.Query.DEFAULT_SOURCE_IDS_PROPERTY_ = 'querySourceIds';

/**
 * Adds a new source to the query service.
 *
 * A source must at least have an `id` configured.  That id is then used to
 * associate the the corresponding layer object within a map.
 *
 * A source will require a `ol.source.ImageWMS` or `ol.source.TileWMS` object.
 * You can either set it directly in the config, or use the one from a given
 * layer or let the query service create one from you using other source config
 * such as `url` and `params`.
 *
 * A source can be set with either a `format` and/or `infoFormat`, which will
 * determine how the returned features of a query will be read.
 *
 * This method will also create a result entry in the `ngeoQueryResult`
 * value service.
 *
 * @param {ngeox.QuerySource} source The source to add to the query service.
 * @export
 */
ngeo.Query.prototype.addSource = function(source) {
  var sourceId = source.id;

  goog.asserts.assert(sourceId, 'source.id should be thruthy');
  goog.asserts.assert(!this.cache_[sourceId],
      'no other source with the same id should be present');

  // == wmsSource ==
  // if the source doesn't have a wmsSource property set, it must at least have
  // a layer that has one or have the required configuration options in order
  // to create one.
  if (!source.wmsSource) {
    if (source.layer &&
        (source.layer instanceof ol.layer.Image ||
         source.layer instanceof ol.layer.Tile)) {
      var wmsSource = source.layer.getSource();
      if (wmsSource &&
          (wmsSource instanceof ol.source.ImageWMS ||
           wmsSource instanceof ol.source.TileWMS)) {
        source.wmsSource =
            /** @type {ol.source.ImageWMS|ol.source.TileWMS} */ (wmsSource);
      }
    } else {
      var url = source.url;
      var params = source.params;
      goog.asserts.assert(url,
          'url must be set when no layer or wmsSource is set in the source');
      goog.asserts.assert(params,
          'parmas must be set when no layer or wmsSource is set in the source');
      source.wmsSource = new ol.source.ImageWMS({
        url: url,
        params: params
      });
    }
  }
  goog.asserts.assert(source.wmsSource, 'wmsSource should be thruthy');

  // == format ==
  if (!source.format) {
    // GML is the default infoFormat if the source doesn't have one defined
    if (!source.infoFormat) {
      source.infoFormat = ngeo.QueryInfoFormatType.GML;
    }

    var layers = source.wmsSource.getParams()['LAYERS'].split(',');

    if (source.infoFormat === ngeo.QueryInfoFormatType.GML) {
      source.format = new ol.format.WMSGetFeatureInfo({
        layers: layers
      });
    }
  } else if (!source.infoFormat) {
    // == infoFormat ==
    var format = source.format;
    if (format instanceof ol.format.WMSGetFeatureInfo) {
      source.infoFormat = ngeo.QueryInfoFormatType.GML;
    }
  }
  goog.asserts.assert(source.format, 'format should be thruthy');

  this.sources_.push(source);

  var sourceLabel = source.label !== undefined ? source.label : sourceId;

  var sourceIdentifierAttributeField =
      source.identifierAttributeField !== undefined ?
      source.identifierAttributeField : sourceId;

  var resultSource = /** @type {ngeo.QueryResultSource} */ ({
    'features': [],
    'id': sourceId,
    'identifierAttributeField': sourceIdentifierAttributeField,
    'label': sourceLabel,
    'pending': false
  });

  this.result_.sources.push(resultSource);

  var cacheItem = {
    'source': source,
    'resultSource': resultSource
  };
  this.cache_[sourceId] = cacheItem;
};

/**
 * Add multiple sources at once in the order they are given.
 * @param {Array.<ngeox.QuerySource>} sources The sources to add to the query
 *     service.
 * @export
 */
ngeo.Query.prototype.addSources = function(sources) {
  sources.forEach(this.addSource, this);
};

/**
 * Clear the results.
 * @export
 */
ngeo.Query.prototype.clear = function() {
  this.clearResult_();
};

/**
 * Issue a new request using a given map and a given object, which can be
 * a coordinate or extent.
 *
 * NOTE: only coordinates are currently supported.
 *
 * @param {ol.Map} map The ol3 map object to fetch the layers from.
 * @param {ol.Coordinate|ol.Extent} object The coordinate or extent to issue
 *     the request with.
 * @export
 */
ngeo.Query.prototype.issue = function(map, object) {

  this.clearResult_();

  if (object.length === 2) {
    this.issueWMSGetFeatureInfoRequests_(map, object);
  }
};

/**
 * Issue a new WMS GetFeatureInfo request using a given map and a coordinate.
 * For each visible layer of the map, if that layer has a source configured
 * within this query service, then a query will be sent and the results
 * will be stocked in the `ngeoQueryResult`.
 *
 * If multiple sources share the same url and use GML as info format, then
 * only one request will be sent for all these sources.
 *
 * NOTE: Only GML info format are currently supported.
 *
 * @param {ol.Map} map The ol3 map object to fetch the layers from.
 * @param {ol.Coordinate} coordinate The coordinate to issue the request with.
 * @private
 */
ngeo.Query.prototype.issueWMSGetFeatureInfoRequests_ = function(
    map, coordinate) {

  var view = map.getView();
  var projCode = view.getProjection().getCode();

  var id;
  var ids;
  var infoFormat;
  var url;
  var item;
  var wmsGetFeatureInfoUrl;

  var itemsByUrl =
      /** @type {Object.<string, Array.<ngeo.QueryCacheItem>>} */ ({});

  var resolution = /** @type {number} */ (view.getResolution());

  var layers = this.ngeoLayerHelper_.getFlatLayers(map.getLayerGroup());

  layers.forEach(function(layer) {

    // Skip layers that are not visible
    if (!layer.getVisible()) {
      return;
    }

    // Skip layers that don't have one or more sources configured
    id = this.getLayerSourceId_(layer);
    ids = this.getLayerSourceIds_(layer);
    if ((!id || !this.cache_[id]) && !ids.length) {
      return;
    }

    if (id) {
      ids.push(id);
    }

    for (var i = 0, len = ids.length; i < len; i++) {
      id = ids[i];
      item = this.cache_[id];

      // If `validateLayerParams` is set, then the source config layer in the
      // LAYERS params must be in the current LAYERS params of the layer
      // wms source object.
      if (item.source.validateLayerParams) {
        goog.asserts.assert(
            layer instanceof ol.layer.Image ||
            layer instanceof ol.layer.Tile,
            'The layer should be an Image or Tile when using the ' +
            'validateLayerParams option.'
        );
        var layerSource = layer.getSource();
        goog.asserts.assert(
            layerSource instanceof ol.source.ImageWMS ||
            layerSource instanceof ol.source.TileWMS,
            'The layer source should be a WMS one when using the ' +
            'validateLayerParams option.'
        );
        var layerLayers = layerSource.getParams()['LAYERS'].split(',');
        var cfgLayer = item.source.wmsSource.getParams()['LAYERS'];
        if (layerLayers.indexOf(cfgLayer) === -1) {
          continue;
        }
      }

      item['resultSource'].pending = true;
      infoFormat = item.source.infoFormat;

      // Sources that use GML as info format are combined together if they
      // share the same server url
      if (infoFormat === ngeo.QueryInfoFormatType.GML) {
        url = item.source.wmsSource.getUrl();
        goog.asserts.assertString(url);
        if (!itemsByUrl[url]) {
          itemsByUrl[url] = [];
        }
        itemsByUrl[url].push(item);
      } else {
        // TODO - support other kinds of infoFormats
        item['resultSource'].pending = false;
      }
    }
  }, this);

  goog.object.forEach(itemsByUrl, function(items) {

    infoFormat = items[0].source.infoFormat;
    var layers = items[0].source.wmsSource.getParams()['LAYERS'].split(',');

    wmsGetFeatureInfoUrl = items[0].source.wmsSource.getGetFeatureInfoUrl(
        coordinate, resolution, projCode, {
          'INFO_FORMAT': infoFormat,
          'FEATURE_COUNT': this.limit_
        });

    goog.asserts.assert(
        wmsGetFeatureInfoUrl, 'WMS GetFeatureInfo url should be thruty');

    for (var i = 1, len = items.length; i < len; i++) {
      layers = layers.concat(
          items[i].source.wmsSource.getParams()['LAYERS'].split(','));
    }

    var lyrStr = layers.join(',');

    wmsGetFeatureInfoUrl =
        goog.uri.utils.setParam(wmsGetFeatureInfoUrl, 'LAYERS', lyrStr);
    wmsGetFeatureInfoUrl =
        goog.uri.utils.setParam(wmsGetFeatureInfoUrl, 'QUERY_LAYERS', lyrStr);

    this.$http_.get(wmsGetFeatureInfoUrl).then(function(items, response) {
      items.forEach(function(item) {
        var format = item.source.format;
        var features = format.readFeatures(response.data);
        item['resultSource'].pending = false;
        item['resultSource'].features = features;
        this.result_.total += features.length;
      }, this);
    }.bind(this, items));
  }, this);
};

/**
 * Clear every features for all result sources and reset the total counter
 * as well.
 * @private
 */
ngeo.Query.prototype.clearResult_ = function() {
  this.result_.total = 0;
  this.result_.sources.forEach(function(source) {
    source.features.length = 0;
  }, this);
};

/**
 * Returns the source id from an ol3 layer object.
 * @param {ol.layer.Base} layer The ol3 layer object.
 * @return {number|string} id The id of the source bound to that layer.
 * @private
 */
ngeo.Query.prototype.getLayerSourceId_ = function(layer) {
  var id = layer.get(this.sourceIdProperty_);
  id = goog.isNumber(id) || goog.isString(id) ? id : '';
  return id;
};

/**
 * Returns the source ids from an ol3 layer object.
 * @param {ol.layer.Base} layer The ol3 layer object.
 * @return {Array.<number|string>} ids The ids of the sources bound to that
 *     layer.
 * @private
 */
ngeo.Query.prototype.getLayerSourceIds_ = function(layer) {
  var ids = layer.get(this.sourceIdsProperty_) || [];
  goog.asserts.assertArray(ids);
  var clone = ids.slice()
  return clone;
};

ngeo.module.service('ngeoQuery', ngeo.Query);
<|endoftext|>


<commit_before>module.exports = function systemConf(props) {
  const conf = {
    defaultJSExtensions: true,
    defaultExtension: 'js'
  };

  if (props.js === 'js' || props.js === 'babel') {
    conf.transpiler = 'babel';
  }

  if (props.js === 'typescript') {
    conf.transpiler = 'typescript';
    conf.typescriptOptions = {
      sourceMap: true,
      emitDecoratorMetadata: true,
      experimentalDecorators: true,
      removeComments: false,
      noImplicitAny: false
    };
    conf.packages = {
      src: { defaultExtension: 'ts' },
      typings: { defaultExtension: 'ts' }
    };

    if (props.framework === 'react') {
      conf.typescriptOptions.jsx = 2;
      conf.packages.src.defaultExtension = 'tsx';
    }

    if (props.framework === 'angular1') {
      conf.packages['.tmp'] = { defaultExtension: 'ts' };
    }
  }

  return conf;
};
<commit_msg>add reference to an issue to configure tsx<commit_after>module.exports = function systemConf(props) {
  const conf = {
    defaultJSExtensions: true,
    defaultExtension: 'js'
  };

  if (props.js === 'js' || props.js === 'babel') {
    conf.transpiler = 'babel';
  }

  if (props.js === 'typescript') {
    conf.transpiler = 'typescript';
    conf.typescriptOptions = {
      sourceMap: true,
      emitDecoratorMetadata: true,
      experimentalDecorators: true,
      removeComments: false,
      noImplicitAny: false
    };
    conf.packages = {
      src: { defaultExtension: 'ts' },
      typings: { defaultExtension: 'ts' }
    };

    if (props.framework === 'react') {
      // https://github.com/Microsoft/TypeScript/issues/5918
      conf.typescriptOptions.jsx = 2;
      conf.packages.src.defaultExtension = 'tsx';
    }

    if (props.framework === 'angular1') {
      conf.packages['.tmp'] = { defaultExtension: 'ts' };
    }
  }

  return conf;
};
<|endoftext|>


<commit_before>const {contains, curry, filter, forEach, pluck} = require('ramda');
const asArray = require('./asArray');

module.exports = curry((virtualNode, destination) => {
  if (virtualNode.isCustomVirtualNode) {
    const outputVirtualNodes = filter(({output}) => contains('output', asArray(output)), virtualNode.virtualNodes);
    forEach((audioNode) => audioNode.connect(destination), pluck('audioNode', outputVirtualNodes));
  } else {
    virtualNode.audioNode.connect(destination);
  }
  virtualNode.connected = true;
});
<commit_msg>update src<commit_after>const {contains, curry, mapObj} = require('ramda');
const asArray = require('./asArray');

module.exports = curry((virtualNode, destination) => {
  if (virtualNode.isCustomVirtualNode) {
    mapObj(childVirtualNode => {
      const {output} = childVirtualNode;
      if (contains('output', asArray(output))) {
        childVirtualNode.audioNode.connect(destination);
      }
    }, virtualNode.virtualNodes);
  } else {
    virtualNode.audioNode.connect(destination);
  }
  virtualNode.connected = true;
});
<|endoftext|>


<commit_before>var fs = require('fs');

var cc = process.argv[2];
var year = process.argv[3];
var month = process.argv[4];
var nameReport = cc + "_" + year + "_" + month;
var text = "";

var monthArray = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var testDict = [
        {"tcpConnect":
            {"description": "For every item given as input we perform a TCP connect. If the connection is succesful, we record 'success' for the test." ,
             "conclusions": "Ability to determine that a specific host:port is blocked."}
        },
        {"dnsInjection":
            {"description": "For every domain in the input list we perform a A query towards the target IP address using UDP on port 53.\n\nWe wait for a DNS response until the timeout (by default 3 seconds) is reached.\n\nIf we have received an answer by that time we say that that hostname is being injected (since the endpoint is not a DNS resolver the answer we got must have been injected by the censorship middle boxes).\n\nIf we don't receive an answer that means everything is as usual and we mark that hostname as not being injected." ,
             "conclusions": "If DNS injection is being done and on which domains."}
        }
];

var pushDescription = function (test) {
    text += "### Test: " + test + "\n";
    text += "#### Description\n";
    text += testDict[0][test].description + "\n";
    text += "#### Possible conclusions\n";
    text += testDict[0][test].conclusions + "\n";
    text += "#### Results\n";
}

var tcpConnectResults = function (json) {
    var total = 0;
    var success = 0;
    var media = 0;
    var mediaMin = 100;
    var mediaMax = 0;
    var alwaysWith = 0;
    var alwaysWithout = 0;
    var maxUrl = "", maxTot = 0, maxSuc = 0;
    var minUrl = "", minTot = 0, minSuc = 0;

    json.tcpConnect.domains.forEach(function (d) {
        total += d.totalTests;
        success += d.totalSucceded;
        if (d.totalTests === d.totalSucceded)
            alwaysWith++;
        if (d.totalSucceded === 0)
            alwaysWithout++;
        media += (d.totalSucceded*100/d.totalTests)
        if (d.totalSucceded*100/d.totalTests > mediaMax) {
            mediaMax = d.totalSucceded*100/d.totalTests;
            maxUrl = d.url;
            maxTot = d.totalTests;
            maxSuc = d.totalSucceded;
        }
        if (d.totalSucceded*100/d.totalTests < mediaMin) {
            mediaMin = d.totalSucceded*100/d.totalTests;
            minUrl = d.url;
            minTot = d.totalTests;
            minSuc = d.totalSucceded;
        }
    });

    text += "* Test executed: " + total + "\n";
    text += "* Test concluded with success: " + success + " (" + (success*100/total).toFixed(2) +"%)\n\n\n";
    text += "* Max percentual of success: " + mediaMax.toFixed(2) + "%\n";
    text += "\t* Item: " + maxUrl + " - " + maxSuc + " over " + maxTot + "\n";
    text += "* Min percentual of success: " + mediaMin.toFixed(2) + "%\n";
    text += "\t* Item: " + minUrl + " - " + minSuc + " over " + minTot + "\n";
    text += "* Median percentual of success: " + (media/json.tcpConnect.domains.length).toFixed(2) + "%\n\n\n";
    text += "* Domains concluded always with success: " + alwaysWith + "\n";
    text += "* Domains concluded always without success: " + alwaysWithout + "\n";
}

text += "# OONI Report\n";
text += "## " + cc + " - " + monthArray[month] + " " + year + "\n";
text += "*******\n";

fs.readFile("output/" + cc + "_" + year + "_" + month + ".json", function (err, data) {
    if (err)
        console.error(err);
    var json = JSON.parse(data);
    if (json.tcpConnect !== undefined) {
        pushDescription("tcpConnect");
        tcpConnectResults(json);
        text += "*******\n";
    }
    fs.writeFile("./reports/" + nameReport + ".md", text, function(err) {
        if (err)
            return console.error(err);
        console.log("Report created!");
    });
});
<commit_msg>added dnsInjection report<commit_after>var fs = require('fs');

var cc = process.argv[2];
var year = process.argv[3];
var month = process.argv[4];
var nameReport = cc + "_" + year + "_" + month;
var text = "";

var monthArray = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var testDict = [
        {"tcpConnect":
            {"description": "For every item given as input we perform a TCP connect. If the connection is succesful, we record 'success' for the test." ,
             "conclusions": "Ability to determine that a specific host:port is blocked."}
        },
        {"dnsInjection":
            {"description": "For every domain in the input list we perform a A query towards the target IP address using UDP on port 53.\n\nWe wait for a DNS response until the timeout (by default 3 seconds) is reached.\n\nIf we have received an answer by that time we say that that hostname is being injected (since the endpoint is not a DNS resolver the answer we got must have been injected by the censorship middle boxes).\n\nIf we don't receive an answer that means everything is as usual and we mark that hostname as not being injected." ,
             "conclusions": "If DNS injection is being done and on which domains."}
        }
];

var pushDescription = function (test) {
    text += "### Test: " + test + "\n";
    text += "#### Description\n";
    testDict.forEach(function (d) {
        if (d[test] !== undefined)
            text += d[test].description + "\n";
    });
    text += "#### Possible conclusions\n";
    testDict.forEach(function (d) {
        if (d[test] !== undefined)
            text += d[test].conclusions + "\n";
    });
    text += "#### Results\n";
}

var tcpConnectResults = function (json) {
    var total = 0;
    var success = 0;
    var media = 0;
    var mediaMin = 100;
    var mediaMax = 0;
    var alwaysWith = 0;
    var alwaysWithout = 0;
    var maxUrl = "", maxTot = 0, maxSuc = 0;
    var minUrl = "", minTot = 0, minSuc = 0;

    json.tcpConnect.domains.forEach(function (d) {
        total += d.totalTests;
        success += d.totalSucceded;
        if (d.totalTests === d.totalSucceded)
            alwaysWith++;
        if (d.totalSucceded === 0)
            alwaysWithout++;
        media += (d.totalSucceded*100/d.totalTests)
        if (d.totalSucceded*100/d.totalTests > mediaMax) {
            mediaMax = d.totalSucceded*100/d.totalTests;
            maxUrl = d.url;
            maxTot = d.totalTests;
            maxSuc = d.totalSucceded;
        }
        if (d.totalSucceded*100/d.totalTests < mediaMin) {
            mediaMin = d.totalSucceded*100/d.totalTests;
            minUrl = d.url;
            minTot = d.totalTests;
            minSuc = d.totalSucceded;
        }
    });

    text += "* Test executed: " + total + "\n";
    text += "* Test concluded with success: " + success + " (" + (success*100/total).toFixed(2) +"%)\n\n\n";
    text += "* Max percentual of success: " + mediaMax.toFixed(2) + "%\n";
    text += "\t* Item: " + maxUrl + " - " + maxSuc + " over " + maxTot + "\n";
    text += "* Min percentual of success: " + mediaMin.toFixed(2) + "%\n";
    text += "\t* Item: " + minUrl + " - " + minSuc + " over " + minTot + "\n";
    text += "* Median percentual of success: " + (media/json.tcpConnect.domains.length).toFixed(2) + "%\n\n\n";
    text += "* Domains concluded always with success: " + alwaysWith + "\n";
    text += "* Domains concluded always without success: " + alwaysWithout + "\n";
}

var dnsInjectionResults = function (json) {
    var total = 0;
    var notInjected = 0;
    var media = 0;
    var mediaMin = 100;
    var mediaMax = 0;
    var alwaysWith = 0;
    var alwaysWithout = 0;
    var maxUrl = "", maxTot = 0, maxSuc = 0;
    var minUrl = "", minTot = 0, minSuc = 0;

    json.tcpConnect.domains.forEach(function (d) {
        total += d.totalTests;
        notInjected += d.totalNotInjected;
        if (d.totalTests === d.totalNotInjected)
            alwaysWith++;
        if (d.totalNotInjected === 0)
            alwaysWithout++;
        media += (d.totalNotInjected*100/d.totalTests)
        if (d.totalNotInjected*100/d.totalTests > mediaMax) {
            mediaMax = d.totalNotInjected*100/d.totalTests;
            maxUrl = d.url;
            maxTot = d.totalTests;
            maxSuc = d.totalNotInjected;
        }
        if (d.totalNotInjected*100/d.totalTests < mediaMin) {
            mediaMin = d.totalNotInjected*100/d.totalTests;
            minUrl = d.url;
            minTot = d.totalTests;
            minSuc = d.totalNotInjected;
        }
    });

    text += "* Test executed: " + total + "\n";
    text += "* Test concluded with no injection: " + notInjected + " (" + (notInjected*100/total).toFixed(2) +"%)\n\n\n";
    text += "* Max percentual of no injection: " + mediaMax.toFixed(2) + "%\n";
    text += "\t* Item: " + maxUrl + " - " + maxSuc + " over " + maxTot + "\n";
    text += "* Min percentual of no injection: " + mediaMin.toFixed(2) + "%\n";
    text += "\t* Item: " + minUrl + " - " + minSuc + " over " + minTot + "\n";
    text += "* Median percentual of no injection: " + (media/json.dnsInjection.domains.length).toFixed(2) + "%\n\n\n";
    text += "* Domains concluded always with no injection: " + alwaysWith + "\n";
    text += "* Domains concluded always without no injection: " + alwaysWithout + "\n";
}

text += "# OONI Report\n";
text += "## " + cc + " - " + monthArray[month] + " " + year + "\n";
text += "*******\n";

fs.readFile("output/" + cc + "_" + year + "_" + month + ".json", function (err, data) {
    if (err)
        console.error(err);
    var json = JSON.parse(data);
    if (json.tcpConnect !== undefined) {
        pushDescription("tcpConnect");
        tcpConnectResults(json);
        text += "*******\n";
    }
    if (json.dnsInjection !== undefined) {
        pushDescription("dnsInjection");
        dnsInjectionResults(json);
        text += "*******\n";
    }
    fs.writeFile("./reports/" + nameReport + ".md", text, function(err) {
        if (err)
            return console.error(err);
        console.log("Report created!");
    });
});
<|endoftext|>


<commit_before>/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/*Memo Management*/
addContactButtonContent = "or Party Code* :<input type='number' id='fake_contact_ID' />" + addContactButtonContent;
$('#contact_ID_input_box').after(addContactButtonContent);
$("#fake_contact_ID").change(function () {
    var fake_contact_ID = $("#fake_contact_ID").val();
    $("#field-contact_ID").val(fake_contact_ID);
    var contact_ID = $("#field-contact_ID").val();
    if (!contact_ID) {
        $("#field-contact_ID").val("");
        $("#fake_contact_ID").val('');
        alert(fake_contact_ID + " is not a valid Party Code");
    }
    set_dues_unpaid();
});

// setting issue date
var objDate = new Date();
if ($('#field-issue_date').val() == "")
    $('#field-issue_date').val(CurrentDate + " " + objDate.getHours() + ":" + objDate.getMinutes() + ":" + objDate.getSeconds());
$('#field-issue_date').change(function () {
    var objDate = new Date();
    var date_selected = $('#field-issue_date').val();
    $('#field-issue_date').val(date_selected + " " + objDate.getHours() + ":" + objDate.getMinutes() + ":" + objDate.getSeconds());
});

$('[name="dues_unpaid"]').after('0');
$('[name="total"]').after('0');
$('[name="due"]').after('0');
$('[name="dues_unpaid"],[name="total"],[name="due"]').attr('type', 'hidden');
function set_dues_unpaid() {
    var contact_ID = $('[name="contact_ID"]').val();
    if (contact_ID == '') {
        $('[name="dues_unpaid"]').val('');
        $("#dues_unpaid").html('0');
        return 0;
    }
    $.ajax({
        url: previousDueFinderUrl + '/' + contact_ID + '/' + memo_ID
    })
            .done(function (data) {
                $('[name="dues_unpaid"]').val(data);
                $("#dues_unpaid").html(data);
            });
}
set_dues_unpaid();
$('[name="contact_ID"]').change(function () {
    set_dues_unpaid();
});
function TotalCal() {
    var discount = parseInt($('[name="discount"]').val());
    discount = (isNaN(discount)) ? 0 : discount;
    var book_return = parseInt($('[name="book_return"]').val());
    book_return = (isNaN(book_return)) ? 0 : book_return;
    var sub_total = parseInt($('[name="sub_total"]').val());
    sub_total = (isNaN(sub_total)) ? 0 : sub_total;
    var dues_unpaid = parseInt($('[name="dues_unpaid"]').val());
    dues_unpaid = (isNaN(dues_unpaid)) ? 0 : dues_unpaid;
    var total = sub_total - discount - book_return + dues_unpaid;
    $('[name="total"]').val(total);
    $("#total").html(total);
//    alert(sub_total+"+"+discount+"-"+dues_unpaid+"= " + total);
}
function DueCal() {
    var cash = parseInt($('[name="cash"]').val());
    cash = (isNaN(cash)) ? 0 : cash;
    var bank_pay = parseInt($('[name="bank_pay"]').val());
    bank_pay = (isNaN(bank_pay)) ? 0 : bank_pay;
    var total = parseInt($('[name="total"]').val());
    total = (isNaN(total)) ? 0 : total;
    var due = total - cash - bank_pay;
    $('[name="due"]').val(due);
    $("#due").html(due);
}
setInterval(function () {
    TotalCal();
    DueCal();
}, 1000);
<commit_msg>Party name is changeing now as the party code entered<commit_after>/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/*Memo Management*/
addContactButtonContent = "or Party Code* :<input type='number' id='fake_contact_ID' />" + addContactButtonContent;
$('#contact_ID_input_box').after(addContactButtonContent);
$("#fake_contact_ID").change(function () {
    var fake_contact_ID = $("#fake_contact_ID").val();
    $("#field-contact_ID").val(fake_contact_ID);
    //setting dropdown value
    var PartyName = $("#field-contact_ID [value=\"" + fake_contact_ID + "\"]").html();
    $("#contact_ID_input_box .chzn-single").attr("class","chzn-single");
    $('#contact_ID_input_box .chzn-single>span').html(PartyName);

    var contact_ID = $("#field-contact_ID").val();
    if (!contact_ID) {
        $("#field-contact_ID").val("");
        $("#fake_contact_ID").val('');
    $("#contact_ID_input_box .chzn-single").attr("class","chzn-single  chzn-default");
        alert(fake_contact_ID + " is not a valid Party Code");
    }
    set_dues_unpaid();
});

// setting issue date
var objDate = new Date();
if ($('#field-issue_date').val() == "")
    $('#field-issue_date').val(CurrentDate + " " + objDate.getHours() + ":" + objDate.getMinutes() + ":" + objDate.getSeconds());
$('#field-issue_date').change(function () {
    var objDate = new Date();
    var date_selected = $('#field-issue_date').val();
    $('#field-issue_date').val(date_selected + " " + objDate.getHours() + ":" + objDate.getMinutes() + ":" + objDate.getSeconds());
});

$('[name="dues_unpaid"]').after('0');
$('[name="total"]').after('0');
$('[name="due"]').after('0');
$('[name="dues_unpaid"],[name="total"],[name="due"]').attr('type', 'hidden');
function set_dues_unpaid() {
    var contact_ID = $('[name="contact_ID"]').val();
    if (contact_ID == '') {
        $('[name="dues_unpaid"]').val('');
        $("#dues_unpaid").html('0');
        return 0;
    }
    $.ajax({
        url: previousDueFinderUrl + '/' + contact_ID + '/' + memo_ID
    })
            .done(function (data) {
                $('[name="dues_unpaid"]').val(data);
                $("#dues_unpaid").html(data);
            });
}
set_dues_unpaid();
$('[name="contact_ID"]').change(function () {
    set_dues_unpaid();
});
function TotalCal() {
    var discount = parseInt($('[name="discount"]').val());
    discount = (isNaN(discount)) ? 0 : discount;
    var book_return = parseInt($('[name="book_return"]').val());
    book_return = (isNaN(book_return)) ? 0 : book_return;
    var sub_total = parseInt($('[name="sub_total"]').val());
    sub_total = (isNaN(sub_total)) ? 0 : sub_total;
    var dues_unpaid = parseInt($('[name="dues_unpaid"]').val());
    dues_unpaid = (isNaN(dues_unpaid)) ? 0 : dues_unpaid;
    var total = sub_total - discount - book_return + dues_unpaid;
    $('[name="total"]').val(total);
    $("#total").html(total);
//    alert(sub_total+"+"+discount+"-"+dues_unpaid+"= " + total);
}
function DueCal() {
    var cash = parseInt($('[name="cash"]').val());
    cash = (isNaN(cash)) ? 0 : cash;
    var bank_pay = parseInt($('[name="bank_pay"]').val());
    bank_pay = (isNaN(bank_pay)) ? 0 : bank_pay;
    var total = parseInt($('[name="total"]').val());
    total = (isNaN(total)) ? 0 : total;
    var due = total - cash - bank_pay;
    $('[name="due"]').val(due);
    $("#due").html(due);
}
setInterval(function () {
    TotalCal();
    DueCal();
}, 1000);
<|endoftext|>


<commit_before>var util = require('util');
var fs = require('fs');
var http = require('http');
var path = require('path');
var child_process = require('child_process');

//https://forums.aws.amazon.com/message.jspa?messageID=633802
process.env.PATH = process.env.PATH + ':/var/task';

var SPARTA_BINARY_NAME = 'Sparta.lambda.amd64';
var SPARTA_BINARY_PATH = path.join('/tmp', SPARTA_BINARY_NAME);
var MAXIMUM_RESPAWN_COUNT = 5;

var golangProcess = null;
var failCount = 0;

function makeRequest(path, event, context) {
  var requestBody = {
    event: event,
    context: context
  };
  var stringified = JSON.stringify(requestBody);
  var contentLength = Buffer.byteLength(stringified);
  var options = {
    host: 'localhost',
    port: 9999,
    path: path,
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': contentLength
    }
  };
  var req = http.request(options, function(res) {
    res.setEncoding('utf8');
    var body = '';
    res.on('data', function(chunk) {
      body += chunk;
    });
    res.on('end', function() {
      var err = (res.statusCode >= 400) ? new Error(body) : null;
      var doneValue = ((res.statusCode >= 200) && (res.statusCode <= 299)) ? body : null;
      context.done(err, doneValue);
    });
  });
  req.on('error', function(e) {
    context.done(e, null);
  });
  req.write(stringified);
  req.end();
}

var log = function(obj_or_string)
{
  if (typeof(obj_or_string) !== 'object')
  {
    obj_or_string = {msg: obj_or_string};
  }
  if (obj_or_string.stack)
  {
    console.error();
  }
  else
  {
    console.log(JSON.stringify(obj_or_string));
  }
};

// Move the file to /tmp to temporarily work around
// https://forums.aws.amazon.com/message.jspa?messageID=583910
var ensureGoLangBinary = function(callback)
{
    try
    {
      log('Checking for binary: ' + SPARTA_BINARY_PATH);
      fs.statSync(SPARTA_BINARY_PATH);
      setImmediate(callback, null);
    }
    catch (e)
    {
      log('Copying golang binary');
      var command = util.format('cp ./%s %s; chmod +x %s',
                                SPARTA_BINARY_NAME,
                                SPARTA_BINARY_PATH,
                                SPARTA_BINARY_PATH);
      child_process.exec(command, function (err, stdout) {
        if (err)
        {
          console.error(err);
          process.exit(1);
        }
        else
        {
          log(stdout.toString('utf-8'));
        }
        callback(err, stdout);
      });
    }
};

var createForwarder = function(path) {
  var forwardToGolangProcess = function(event, context)
  {
    if (!golangProcess) {
      ensureGoLangBinary(function() {
        golangProcess = child_process.spawn(SPARTA_BINARY_PATH, ['execute', '--signal', process.pid], {});

        golangProcess.stdout.on('data', function(buf) {
          // Just throw it away...
          console.log(buf.toString('utf-8'));
        });
        golangProcess.stderr.on('data', function(buf) {
          // Just throw it away...
          console.log(buf.toString('utf-8'));
        });

        var terminationHandler = function(eventName) {
          return function(value) {
            console.error(util.format('Sparta %s: %s\n', eventName.toUpperCase(), JSON.stringify(value)));
            failCount += 1;
            if (failCount > MAXIMUM_RESPAWN_COUNT) {
              process.exit(1);
            }
            golangProcess = null;
            forwardToGolangProcess(null, null);
          };
        };
        golangProcess.on('error', terminationHandler('error'));
        golangProcess.on('exit', terminationHandler('exit'));
        process.on('exit', function() {
          if (golangProcess) {
            golangProcess.kill();
          }
        });
        var golangProcessReadyHandler = function() {
          process.removeListener('SIGUSR2', golangProcessReadyHandler);
          forwardToGolangProcess(event, context);
        };
        process.on('SIGUSR2', golangProcessReadyHandler);
      });
    }
    else if (event && context)
    {
      makeRequest(path, event, context);
    }
  };
  return forwardToGolangProcess;
};

exports.main = createForwarder('/');
// Additional golang handlers to be dynamically appended below<commit_msg>Use proper encoding<commit_after>var util = require('util');
var fs = require('fs');
var http = require('http');
var path = require('path');
var child_process = require('child_process');

//https://forums.aws.amazon.com/message.jspa?messageID=633802
process.env.PATH = process.env.PATH + ':/var/task';

var SPARTA_BINARY_NAME = 'Sparta.lambda.amd64';
var SPARTA_BINARY_PATH = path.join('/tmp', SPARTA_BINARY_NAME);
var MAXIMUM_RESPAWN_COUNT = 5;

var golangProcess = null;
var failCount = 0;

function makeRequest(path, event, context) {
  var requestBody = {
    event: event,
    context: context
  };
  var stringified = JSON.stringify(requestBody);
  var contentLength = Buffer.byteLength(stringified, 'utf-8');
  var options = {
    host: 'localhost',
    port: 9999,
    path: path,
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': contentLength
    }
  };
  var req = http.request(options, function(res) {
    res.setEncoding('utf8');
    var body = '';
    res.on('data', function(chunk) {
      body += chunk;
    });
    res.on('end', function() {
      var err = (res.statusCode >= 400) ? new Error(body) : null;
      var doneValue = ((res.statusCode >= 200) && (res.statusCode <= 299)) ? body : null;
      context.done(err, doneValue);
    });
  });
  req.on('error', function(e) {
    context.done(e, null);
  });
  req.write(stringified);
  req.end();
}

var log = function(obj_or_string)
{
  if (typeof(obj_or_string) !== 'object')
  {
    obj_or_string = {msg: obj_or_string};
  }
  if (obj_or_string.stack)
  {
    console.error();
  }
  else
  {
    console.log(JSON.stringify(obj_or_string));
  }
};

// Move the file to /tmp to temporarily work around
// https://forums.aws.amazon.com/message.jspa?messageID=583910
var ensureGoLangBinary = function(callback)
{
    try
    {
      log('Checking for binary: ' + SPARTA_BINARY_PATH);
      fs.statSync(SPARTA_BINARY_PATH);
      setImmediate(callback, null);
    }
    catch (e)
    {
      log('Copying golang binary');
      var command = util.format('cp ./%s %s; chmod +x %s',
                                SPARTA_BINARY_NAME,
                                SPARTA_BINARY_PATH,
                                SPARTA_BINARY_PATH);
      child_process.exec(command, function (err, stdout) {
        if (err)
        {
          console.error(err);
          process.exit(1);
        }
        else
        {
          log(stdout.toString('utf-8'));
        }
        callback(err, stdout);
      });
    }
};

var createForwarder = function(path) {
  var forwardToGolangProcess = function(event, context)
  {
    if (!golangProcess) {
      ensureGoLangBinary(function() {
        golangProcess = child_process.spawn(SPARTA_BINARY_PATH, ['execute', '--signal', process.pid], {});

        golangProcess.stdout.on('data', function(buf) {
          // Just throw it away...
          console.log(buf.toString('utf-8'));
        });
        golangProcess.stderr.on('data', function(buf) {
          // Just throw it away...
          console.log(buf.toString('utf-8'));
        });

        var terminationHandler = function(eventName) {
          return function(value) {
            console.error(util.format('Sparta %s: %s\n', eventName.toUpperCase(), JSON.stringify(value)));
            failCount += 1;
            if (failCount > MAXIMUM_RESPAWN_COUNT) {
              process.exit(1);
            }
            golangProcess = null;
            forwardToGolangProcess(null, null);
          };
        };
        golangProcess.on('error', terminationHandler('error'));
        golangProcess.on('exit', terminationHandler('exit'));
        process.on('exit', function() {
          if (golangProcess) {
            golangProcess.kill();
          }
        });
        var golangProcessReadyHandler = function() {
          process.removeListener('SIGUSR2', golangProcessReadyHandler);
          forwardToGolangProcess(event, context);
        };
        process.on('SIGUSR2', golangProcessReadyHandler);
      });
    }
    else if (event && context)
    {
      makeRequest(path, event, context);
    }
  };
  return forwardToGolangProcess;
};

exports.main = createForwarder('/');
// Additional golang handlers to be dynamically appended below<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

const sane = require('sane');
const { resolve: resolvePath } = require('path');
const { spawn } = require('child_process');
const flowBinPath = require('flow-bin');

process.env.PATH += ':./node_modules/.bin';

var cmd = resolvePath(__dirname);
var srcDir = resolvePath(cmd, '../src');

function exec(command, options) {
  return new Promise((resolve, reject) => {
    var child = spawn(command, options, {
      cmd: cmd,
      env: process.env,
      stdio: 'inherit'
    });
    child.on('exit', code => {
      if (code === 0) {
        resolve(true);
      } else {
        reject(new Error('Error code: ' + code));
      }
    });
  });
}

var flowServer = spawn(flowBinPath, ['server'], {
  cmd: cmd,
  env: process.env
});

var watcher = sane(srcDir, { glob: ['**/*.js', '**/*.graphql'] })
  .on('ready', startWatch)
  .on('add', changeFile)
  .on('delete', deleteFile)
  .on('change', changeFile);

process.on('SIGINT', () => {
  console.log(CLEARLINE + yellow(invert('stopped watching')));
  watcher.close();
  flowServer.kill();
  process.exit();
});

var isChecking;
var needsCheck;
var toCheck = {};
var timeout;

function startWatch() {
  process.stdout.write(CLEARSCREEN + green(invert('watching...')));
}

function changeFile(filepath, root, stat) {
  if (!stat.isDirectory()) {
    toCheck[filepath] = true;
    debouncedCheck();
  }
}

function deleteFile(filepath) {
  delete toCheck[filepath];
  debouncedCheck();
}

function debouncedCheck() {
  needsCheck = true;
  clearTimeout(timeout);
  timeout = setTimeout(guardedCheck, 250);
}

function guardedCheck() {
  if (isChecking || !needsCheck) {
    return;
  }
  isChecking = true;
  var filepaths = Object.keys(toCheck);
  toCheck = {};
  needsCheck = false;
  checkFiles(filepaths).then(() => {
    isChecking = false;
    process.nextTick(guardedCheck);
  });
}

function checkFiles(filepaths) {
  console.log('\u001b[2J');

  return parseFiles(filepaths)
    .then(() => runTests(filepaths))
    .then(testSuccess =>
      lintFiles(filepaths).then(lintSuccess =>
        typecheckStatus().then(
          typecheckSuccess => testSuccess && lintSuccess && typecheckSuccess
        )
      )
    )
    .catch(() => false)
    .then(success => {
      process.stdout.write(
        '\n' + (success ? '' : '\x07') + green(invert('watching...'))
      );
    });
}

// Checking steps

function parseFiles(filepaths) {
  console.log('Checking Syntax');

  return Promise.all(
    filepaths.map(filepath => {
      if (isJS(filepath) && !isTest(filepath)) {
        return exec('babel', [
          '--optional',
          'runtime',
          '--out-file',
          '/dev/null',
          srcPath(filepath),
        ]);
      }
    })
  );
}

function runTests(filepaths) {
  console.log('\nRunning Tests');

  return exec(
    'mocha',
    ['--reporter', 'progress'].concat(
      allTests(filepaths)
        ? filepaths.map(srcPath)
        : ['src/**/__tests__/**/*-test.js']
    )
  ).catch(() => false);
}

function lintFiles(filepaths) {
  console.log('Linting Code\n');

  return filepaths.reduce(
    (prev, filepath) =>
      prev.then(prevSuccess => {
        if (isJS(filepath)) {
          process.stdout.write('  ' + filepath + ' ...');
          return exec('eslint', [srcPath(filepath)])
            .catch(() => false)
            .then(success => {
              console.log(
                CLEARLINE + '  ' + (success ? CHECK : X) + ' ' + filepath
              );
              return prevSuccess && success;
            });
        }
        return prevSuccess;
      }),
    Promise.resolve(true)
  );
}

function typecheckStatus() {
  console.log('\nType Checking\n');
  return exec(flowBinPath, ['status']).catch(() => false);
}

// Filepath

function srcPath(filepath) {
  return resolvePath(srcDir, filepath);
}

// Predicates

function isJS(filepath) {
  return filepath.indexOf('.js') === filepath.length - 3;
}

function allTests(filepaths) {
  return filepaths.length > 0 && filepaths.every(isTest);
}

var TEST_PATH_RX = /^(?:.*?\/)?__tests__\/.+?-test\.js$/;

function isTest(filepath) {
  return TEST_PATH_RX.test(filepath);
}

// Print helpers

var CLEARSCREEN = '\u001b[2J';
var CLEARLINE = '\r\x1B[K';
var CHECK = green('\u2713');
var X = red('\u2718');

function invert(str) {
  return `\u001b[7m ${str} \u001b[27m`;
}

function red(str) {
  return `\x1B[K\u001b[1m\u001b[31m${str}\u001b[39m\u001b[22m`;
}

function green(str) {
  return `\x1B[K\u001b[1m\u001b[32m${str}\u001b[39m\u001b[22m`;
}

function yellow(str) {
  return `\x1B[K\u001b[1m\u001b[33m${str}\u001b[39m\u001b[22m`;
}
<commit_msg>Do not run duplicating parser step on watch (#1745)<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

const sane = require('sane');
const { resolve: resolvePath } = require('path');
const { spawn } = require('child_process');
const flowBinPath = require('flow-bin');

process.env.PATH += ':./node_modules/.bin';

var cmd = resolvePath(__dirname);
var srcDir = resolvePath(cmd, '../src');

function exec(command, options) {
  return new Promise((resolve, reject) => {
    var child = spawn(command, options, {
      cmd: cmd,
      env: process.env,
      stdio: 'inherit'
    });
    child.on('exit', code => {
      if (code === 0) {
        resolve(true);
      } else {
        reject(new Error('Error code: ' + code));
      }
    });
  });
}

var flowServer = spawn(flowBinPath, ['server'], {
  cmd: cmd,
  env: process.env
});

var watcher = sane(srcDir, { glob: ['**/*.js', '**/*.graphql'] })
  .on('ready', startWatch)
  .on('add', changeFile)
  .on('delete', deleteFile)
  .on('change', changeFile);

process.on('SIGINT', () => {
  console.log(CLEARLINE + yellow(invert('stopped watching')));
  watcher.close();
  flowServer.kill();
  process.exit();
});

var isChecking;
var needsCheck;
var toCheck = {};
var timeout;

function startWatch() {
  process.stdout.write(CLEARSCREEN + green(invert('watching...')));
}

function changeFile(filepath, root, stat) {
  if (!stat.isDirectory()) {
    toCheck[filepath] = true;
    debouncedCheck();
  }
}

function deleteFile(filepath) {
  delete toCheck[filepath];
  debouncedCheck();
}

function debouncedCheck() {
  needsCheck = true;
  clearTimeout(timeout);
  timeout = setTimeout(guardedCheck, 250);
}

function guardedCheck() {
  if (isChecking || !needsCheck) {
    return;
  }
  isChecking = true;
  var filepaths = Object.keys(toCheck);
  toCheck = {};
  needsCheck = false;
  checkFiles(filepaths).then(() => {
    isChecking = false;
    process.nextTick(guardedCheck);
  });
}

function checkFiles(filepaths) {
  console.log('\u001b[2J');

  return runTests(filepaths)
    .then(testSuccess =>
      lintFiles(filepaths).then(lintSuccess =>
        typecheckStatus().then(
          typecheckSuccess => testSuccess && lintSuccess && typecheckSuccess
        )
      )
    )
    .catch(() => false)
    .then(success => {
      process.stdout.write(
        '\n' + (success ? '' : '\x07') + green(invert('watching...'))
      );
    });
}

// Checking steps
function runTests(filepaths) {
  console.log('\nRunning Tests');

  return exec(
    'mocha',
    ['--reporter', 'progress'].concat(
      allTests(filepaths)
        ? filepaths.map(srcPath)
        : ['src/**/__tests__/**/*-test.js']
    )
  ).catch(() => false);
}

function lintFiles(filepaths) {
  console.log('Linting Code\n');

  return filepaths.reduce(
    (prev, filepath) =>
      prev.then(prevSuccess => {
        if (isJS(filepath)) {
          process.stdout.write('  ' + filepath + ' ...');
          return exec('eslint', [srcPath(filepath)])
            .catch(() => false)
            .then(success => {
              console.log(
                CLEARLINE + '  ' + (success ? CHECK : X) + ' ' + filepath
              );
              return prevSuccess && success;
            });
        }
        return prevSuccess;
      }),
    Promise.resolve(true)
  );
}

function typecheckStatus() {
  console.log('\nType Checking\n');
  return exec(flowBinPath, ['status']).catch(() => false);
}

// Filepath

function srcPath(filepath) {
  return resolvePath(srcDir, filepath);
}

// Predicates

function isJS(filepath) {
  return filepath.indexOf('.js') === filepath.length - 3;
}

function allTests(filepaths) {
  return filepaths.length > 0 && filepaths.every(isTest);
}

var TEST_PATH_RX = /^(?:.*?\/)?__tests__\/.+?-test\.js$/;

function isTest(filepath) {
  return TEST_PATH_RX.test(filepath);
}

// Print helpers

var CLEARSCREEN = '\u001b[2J';
var CLEARLINE = '\r\x1B[K';
var CHECK = green('\u2713');
var X = red('\u2718');

function invert(str) {
  return `\u001b[7m ${str} \u001b[27m`;
}

function red(str) {
  return `\x1B[K\u001b[1m\u001b[31m${str}\u001b[39m\u001b[22m`;
}

function green(str) {
  return `\x1B[K\u001b[1m\u001b[32m${str}\u001b[39m\u001b[22m`;
}

function yellow(str) {
  return `\x1B[K\u001b[1m\u001b[33m${str}\u001b[39m\u001b[22m`;
}
<|endoftext|>


<commit_before>/**
 * @author Tzuyang Tsai (shiyou0130011)
 * @license Apache-2.0
 */

/**
 * @constructor
 * @param variables {String|Object} An url string or an object
 */
function URLVariables(variables) {
	if (!(this instanceof URLVariables)) {
		return new URLVariables(variables)
	}
	this.decode(variables)
}
/**
 * Converts the url string or object to the specified URLVariables object.
 * @param {String|Object}	An url string or an object
 * @example var u = new URLVariables;
 *          u.decode("a=1&b=2&c=3&a=2")
 *          u.decode({a: [1, 2], b: 2, c: 3})
 * @return {URLVariables}	this
 */
URLVariables.prototype.decode = function (source) {
	switch (typeof source) {
		case "string":
			var url = source
			var urlvar
			if(url.indexOf("?") >= 0){
				url = url.substr(url.indexOf("?") + 1)
			}else if(url.indexOf("=") < 0){
				url = ""
			}

			if(url.length > 0){
				urlvar = url[url.length-1].split("&")
			}else{
				urlvar = {}
			}

			for (var i in urlvar) {
				var data = urlvar[i].split("=")
				var name = data[0] === undefined? "": decodeURIComponent(data[0]),
				    value = data[1] === undefined? "": decodeURIComponent(data[1])
				if (!(this[name] instanceof Array)) {
					if (this[name]) {
						this[name] = [this[name]]
					}
					this[name] = [value]

				}else{
					this[name].push(value)
				}
			}
			break
		case "object":
			for(var i in source) {
				if (this[i] != undefined) {
					if (!(this[i] instanceof Array)) {
						this[i] = [this[i]]
					}
				}else{
					this[i] = []
				}

				if (source[i] instanceof Array) {
					[].push.apply(this[i], source[i])
				}else{
					this[i].push(source[i])
				}
			}
	}
	return this
}

/**
 * @return {String} The string containing all variables, in the MIME content encoding application/x-www-form-urlencoded.
 */
URLVariables.prototype.toString = function () {
	var urlvar_keys = []
	for(var i in this) {
		if (typeof this[i] != "function" && !URLVariables.prototype[i])
			urlvar_keys.push(i)
	}
	urlvar_keys.sort()

	var l = []
	for (i in urlvar_keys) {
		var key = urlvar_keys[i]
		if (!(this[key] instanceof Array)){
			this[key] = [this[key]]
		}

		this[key].sort()
		this[key].forEach(function(val, index, array) {
			l.push(encodeURIComponent(key) + "=" + encodeURIComponent(val))
		})
	}
	return l.join("&")
}<commit_msg>更新版本號<commit_after>/**
 * @author Tzuyang Tsai (shiyou0130011)
 * @version 1.1
 * @license Apache-2.0
 */

/**
 * @constructor
 * @param variables {String|Object} An url string or an object
 */
function URLVariables(variables) {
	if (!(this instanceof URLVariables)) {
		return new URLVariables(variables)
	}
	this.decode(variables)
}
/**
 * Converts the url string or object to the specified URLVariables object.
 * @param {String|Object}	An url string or an object
 * @example var u = new URLVariables;
 *          u.decode("a=1&b=2&c=3&a=2")
 *          u.decode({a: [1, 2], b: 2, c: 3})
 * @return {URLVariables}	this
 */
URLVariables.prototype.decode = function (source) {
	switch (typeof source) {
		case "string":
			var url = source
			var urlvar
			if(url.indexOf("?") >= 0){
				url = url.substr(url.indexOf("?") + 1)
			}else if(url.indexOf("=") < 0){
				url = ""
			}

			if(url.length > 0){
				urlvar = url[url.length-1].split("&")
			}else{
				urlvar = {}
			}

			for (var i in urlvar) {
				var data = urlvar[i].split("=")
				var name = data[0] === undefined? "": decodeURIComponent(data[0]),
				    value = data[1] === undefined? "": decodeURIComponent(data[1])
				if (!(this[name] instanceof Array)) {
					if (this[name]) {
						this[name] = [this[name]]
					}
					this[name] = [value]

				}else{
					this[name].push(value)
				}
			}
			break
		case "object":
			for(var i in source) {
				if (this[i] != undefined) {
					if (!(this[i] instanceof Array)) {
						this[i] = [this[i]]
					}
				}else{
					this[i] = []
				}

				if (source[i] instanceof Array) {
					[].push.apply(this[i], source[i])
				}else{
					this[i].push(source[i])
				}
			}
	}
	return this
}

/**
 * @return {String} The string containing all variables, in the MIME content encoding application/x-www-form-urlencoded.
 */
URLVariables.prototype.toString = function () {
	var urlvar_keys = []
	for(var i in this) {
		if (typeof this[i] != "function" && !URLVariables.prototype[i])
			urlvar_keys.push(i)
	}
	urlvar_keys.sort()

	var l = []
	for (i in urlvar_keys) {
		var key = urlvar_keys[i]
		if (!(this[key] instanceof Array)){
			this[key] = [this[key]]
		}

		this[key].sort()
		this[key].forEach(function(val, index, array) {
			l.push(encodeURIComponent(key) + "=" + encodeURIComponent(val))
		})
	}
	return l.join("&")
}<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Feed from '../feed/Feed';
import {
  getFeedContentFromState,
  getFeedLoadingFromState,
  getFeedHasMoreFromState
} from '../helpers/stateHelpers';
import EmptyFeed from '../statics/EmptyFeed';

export default class UserProfileFeed extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    const { feed, posts, getUserFeedContent, getMoreUserFeedContent } = this.props;
    const username = this.props.params.name;

    const content = getFeedContentFromState('feed', username, feed, posts);
    const isFetching = getFeedLoadingFromState('feed', username, feed);
    const hasMore = getFeedHasMoreFromState('feed', username, feed);
    const loadContentAction = () => getUserFeedContent({
      sortBy: 'feed',
      username,
      limit: this.props.limit,
    });
    const loadMoreContentAction = () => getMoreUserFeedContent({
      sortBy: 'feed',
      username,
      limit: this.props.limit,
    });

    return (
      <di>
        <Feed
          content={content}
          isFetching={isFetching}
          hasMore={hasMore}
          loadContent={loadContentAction}
          loadMoreContent={loadMoreContentAction}
          route={this.props.route}
        />

        { (content.length === 0 && !isFetching) &&
          <EmptyFeed />
        }
      </di>
    );
  }
}
<commit_msg>fix typo in div element<commit_after>import React, { Component } from 'react';
import Feed from '../feed/Feed';
import {
  getFeedContentFromState,
  getFeedLoadingFromState,
  getFeedHasMoreFromState
} from '../helpers/stateHelpers';
import EmptyFeed from '../statics/EmptyFeed';

export default class UserProfileFeed extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    const { feed, posts, getUserFeedContent, getMoreUserFeedContent } = this.props;
    const username = this.props.params.name;

    const content = getFeedContentFromState('feed', username, feed, posts);
    const isFetching = getFeedLoadingFromState('feed', username, feed);
    const hasMore = getFeedHasMoreFromState('feed', username, feed);
    const loadContentAction = () => getUserFeedContent({
      sortBy: 'feed',
      username,
      limit: this.props.limit,
    });
    const loadMoreContentAction = () => getMoreUserFeedContent({
      sortBy: 'feed',
      username,
      limit: this.props.limit,
    });

    return (
      <div>
        <Feed
          content={content}
          isFetching={isFetching}
          hasMore={hasMore}
          loadContent={loadContentAction}
          loadMoreContent={loadMoreContentAction}
          route={this.props.route}
        />

        { (content.length === 0 && !isFetching) &&
          <EmptyFeed />
        }
      </div>
    );
  }
}
<|endoftext|>


<commit_before>/* global define: true */
(function (root, factory) {
    'use strict';
    if (typeof exports === 'object') {
        // CommonJS
        factory(require('rivets'), require('backbone'));
    } else if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['rivets', 'backbone'], factory);
    } else {
        // Browser globals
        factory(root.rivets, root.Backbone);
    }
})
(this, function (rivets, Backbone) {
    'use strict';

    var Model = Backbone.Model,
        Collection = Backbone.Collection;

    /**
     * Resolves path chain
     *
     * for a, 'b:c:d' returns {model: a:b:c, key:'d'}
     *
     * @param {Model}  model
     * @param {String} keypath
     *
     * @returns {{model: Model, key: String}}
     */
    function getKeyPathRoot(model, keypath) {
        keypath = keypath.split(':');

        while (keypath.length > 1) {
            model = model.get(keypath.shift());
        }

        return {
            model: model,
            key: keypath.shift()
        };
    }

    /**
     * @param {Model}  model
     * @param {String} keypath
     * @param {*}      [value]
     *
     * @returns {*}
     */
    function getterSetter(model, keypath, value) {
        var root = getKeyPathRoot(model, keypath);
        model = root.model;

        if (!(model instanceof Model)) {
            return model;
        }

        if (arguments.length === 2) {
            return model.get(root.key);
        }

        model.set(root.key, value);
    }

    /**
     * @param {String} action on or off
     * @returns {Function}
     */
    function onOffFactory(action) {

        /**
         * @param {Model}    model
         * @param {String}   keypath
         * @param {Function} callback
         */
        return function (model, keypath, callback) {
            if (!(model instanceof Model)) {
                return;
            }

            var root = getKeyPathRoot(model, keypath),
                collection = root.model.get(root.key);

            if (collection instanceof Collection) {
                collection[action]('add remove reset', callback);
            } else {
                var eventName = 'change' + (root.key === '*' ? '' : (':' + root.key));
                root.model[action](eventName, callback);
            }
        };
    }

    /**
     * @param {Model|Collection} obj
     * @param {String}           keypath
     * @returns {*}
     */
    function read(obj, keypath) {
        if (obj instanceof Collection) {
            return obj[keypath];
        }

        var value = getterSetter(obj, keypath);

        // rivets cant iterate over Backbone.Collection -> return Array
        if (value instanceof Collection) {
            return value.models;
        }

        return value;
    }

    /**
     * @param {Model|Collection} obj
     * @param {String}           keypath
     * @param {*}                value
     */
    function publish(obj, keypath, value) {
        if (obj instanceof Collection) {
            obj[keypath] = value;
        } else {
            getterSetter(obj, keypath, value);
        }
    }

    // Configure rivets data-bind for Backbone.js
    rivets.adapters[':'] =  {
        subscribe: onOffFactory('on'),
        unsubscribe: onOffFactory('off'),
        read: read,
        publish: publish
    };

    return rivets;
});
<commit_msg>compatibility to rivets 0.7.0 with new sightglass adapters<commit_after>/* global define: true */
(function (root, factory) {
    'use strict';
    if (typeof exports === 'object') {
        // CommonJS
        factory(require('rivets'), require('backbone'));
    } else if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['rivets', 'backbone'], factory);
    } else {
        // Browser globals
        factory(root.rivets, root.Backbone);
    }
})
(this, function (rivets, Backbone) {
    'use strict';

    var Model = Backbone.Model,
        Collection = Backbone.Collection;

    /**
     * Resolves path chain
     *
     * for a, 'b:c:d' returns {model: a:b:c, key:'d'}
     *
     * @param {Model}  model
     * @param {String} keypath
     *
     * @returns {{model: Model, key: String}}
     */
    function getKeyPathRoot(model, keypath) {
        keypath = keypath.split(':');

        while (keypath.length > 1) {
            model = model.get(keypath.shift());
        }

        return {
            model: model,
            key: keypath.shift()
        };
    }

    /**
     * @param {Model}  model
     * @param {String} keypath
     * @param {*}      [value]
     *
     * @returns {*}
     */
    function getterSetter(model, keypath, value) {
        var root = getKeyPathRoot(model, keypath);
        model = root.model;

        if (!(model instanceof Model)) {
            return model;
        }

        if (arguments.length === 2) {
            return model.get(root.key);
        }

        model.set(root.key, value);
    }

    /**
     * @param {String} action on or off
     * @returns {Function}
     */
    function onOffFactory(action) {

        /**
         * @param {Model}    model
         * @param {String}   keypath
         * @param {Function} callback
         */
        return function (model, keypath, callback) {
            if (!(model instanceof Model)) {
                return;
            }

            var root = getKeyPathRoot(model, keypath),
                collection = root.model.get(root.key);

            if (collection instanceof Collection) {
                collection[action]('add remove reset', callback);
            } else {
                var eventName = 'change' + (root.key === '*' ? '' : (':' + root.key));
                root.model[action](eventName, callback);
            }
        };
    }

    /**
     * @param {Model|Collection} obj
     * @param {String}           keypath
     * @returns {*}
     */
    function read(obj, keypath) {
        if (obj instanceof Collection) {
            return obj[keypath];
        }

        var value = getterSetter(obj, keypath);

        // rivets cant iterate over Backbone.Collection -> return Array
        if (value instanceof Collection) {
            return value.models;
        }

        return value;
    }

    /**
     * @param {Model|Collection} obj
     * @param {String}           keypath
     * @param {*}                value
     */
    function publish(obj, keypath, value) {
        if (obj instanceof Collection) {
            obj[keypath] = value;
        } else {
            getterSetter(obj, keypath, value);
        }
    }

    // Configure rivets data-bind for Backbone.js
    // new adapter API using sightglass (rivets >= 0.7.0)
    if (rivets.adapters['.']['observe']) {
      rivets.adapters[':'] =  {
        observe: onOffFactory('on'),
        unobserve: onOffFactory('off'),
        get: read,
        set: publish
      };
    }
    // old adapter API without sightglass
    else {
      rivets.adapters[':'] =  {
        subscribe: onOffFactory('on'),
        unsubscribe: onOffFactory('off'),
        read: read,
        publish: publish
      };
    }

    return rivets;
});
<|endoftext|>


<commit_before>import ValidationError from '../ValidationError';
import { once } from './async';

export default function runTests(options, cb) {
  let { endEarly, tests, args, value, errors, sort, path } = options;

  let callback = once(cb);
  let count = tests.length;

  if (!count) return callback(null, value);

  const nestedErrors = [];
  errors = errors ? errors : [];

  for (let i = 0; i < tests.length; i++) {
    const test = tests[i];

    test(args, function finishTestRun(err) {
      if (err) {
        // always return early for non validation errors
        if (!ValidationError.isError(err)) {
          return callback(err);
        }
        if (endEarly) {
          err.value = value;
          return callback(err);
        }
        nestedErrors.push(err);
      }

      if (--count <= 0) {
        if (nestedErrors.length) {
          if (sort) nestedErrors.sort(sort);

          //show parent errors after the nested ones: name.first, name
          if (errors.length) nestedErrors.push(...errors);
          errors = nestedErrors;
        }

        if (errors.length) {
          callback(new ValidationError(errors, value, path));
          return;
        }

        callback(null, value);
      }
    });
  }
}
<commit_msg>fix: swallowed errors on nested schema with no tests<commit_after>import ValidationError from '../ValidationError';
import { once } from './async';

export default function runTests(options, cb) {
  let { endEarly, tests, args, value, errors, sort, path } = options;

  let callback = once(cb);
  let count = tests.length;

  const nestedErrors = [];
  errors = errors ? errors : [];

  if (!count)
    return errors.length
      ? callback(new ValidationError(errors, value, path))
      : callback(null, value);

  for (let i = 0; i < tests.length; i++) {
    const test = tests[i];

    test(args, function finishTestRun(err) {
      if (err) {
        // always return early for non validation errors
        if (!ValidationError.isError(err)) {
          return callback(err);
        }
        if (endEarly) {
          err.value = value;
          return callback(err);
        }
        nestedErrors.push(err);
      }

      if (--count <= 0) {
        if (nestedErrors.length) {
          if (sort) nestedErrors.sort(sort);

          //show parent errors after the nested ones: name.first, name
          if (errors.length) nestedErrors.push(...errors);
          errors = nestedErrors;
        }

        if (errors.length) {
          callback(new ValidationError(errors, value, path));
          return;
        }

        callback(null, value);
      }
    });
  }
}
<|endoftext|>


<commit_before>import uuid from 'node-uuid';

const YEAR_IN_MS = 1000 * 60 * 60 * 24 * 365;

/**
 * @method setSessionId
 *
 * simple tracking id for the browser session
 *
 * @param {Object} hapi request object
 * @param {Object} hapi response object
 */
export const setSessionId = (request, response) => {
	const sessionId = uuid.v4();
	response.state(
		'session_id',
		sessionId,
		{ encoding: 'none' }
	);
};

/**
 * @method updateTrackId
 *
 * Initialize the trackId for member or anonymous user - the longest-living id
 * we can assigned to a user. Stays in place until login or logout, when it is
 * exchanged for a new trackId
 *
 *  - If the user has a tracking cookie already set, do nothing.
 *  - Otherwise, generate a new uuid and set a tracking cookie.
 *
 * @param {Object} hapi request object
 * @param {Object} hapi response object
 */
export const updateTrackId = (request, response) => {
	let trackId = request.state.track_id;

	if (!trackId) {
		// Generate a new track_id cookie
		trackId = uuid.v4();
		response.state(
			'track_id',
			trackId,
			{
				ttl: YEAR_IN_MS * 20,
				encoding: 'none'
			}
		);
	}
	return trackId;
};

<commit_msg>return sessionId<commit_after>import uuid from 'node-uuid';

const YEAR_IN_MS = 1000 * 60 * 60 * 24 * 365;

/**
 * @method setSessionId
 *
 * simple tracking id for the browser session
 *
 * @param {Object} hapi request object
 * @param {Object} hapi response object
 */
export const setSessionId = (request, response) => {
	const sessionId = uuid.v4();
	response.state(
		'session_id',
		sessionId,
		{ encoding: 'none' }
	);
	return sessionId;
};

/**
 * @method updateTrackId
 *
 * Initialize the trackId for member or anonymous user - the longest-living id
 * we can assigned to a user. Stays in place until login or logout, when it is
 * exchanged for a new trackId
 *
 *  - If the user has a tracking cookie already set, do nothing.
 *  - Otherwise, generate a new uuid and set a tracking cookie.
 *
 * @param {Object} hapi request object
 * @param {Object} hapi response object
 */
export const updateTrackId = (request, response) => {
	let trackId = request.state.track_id;

	if (!trackId) {
		// Generate a new track_id cookie
		trackId = uuid.v4();
		response.state(
			'track_id',
			trackId,
			{
				ttl: YEAR_IN_MS * 20,
				encoding: 'none'
			}
		);
	}
	return trackId;
};

<|endoftext|>


<commit_before>import _ from 'lodash';
import { eachComponent, uniqueKey } from './utils';
export default {
  /**
   * Appends a number to a component.key to keep it unique
   *
   * @param {Object} form
   *   The components parent form.
   * @param {Object} component
   *   The component to uniquify
   */
  uniquify(container, component) {
    let changed = false;
    const formKeys = {};
    eachComponent(container, (comp) => {
      formKeys[comp.key] = true;

      if (['address', 'container', 'datagrid', 'editgrid', 'tree'].includes(component.type) || component.tree || component.arrayTree) {
        return true;
      }
    }, true);

    // Recurse into all child components.
    eachComponent([component], (component) => {
      // Skip key uniquification if this component doesn't have a key.
      if (!component.key) {
        return;
      }

      const newKey = uniqueKey(formKeys, component.key);
      if (newKey !== component.key) {
        component.key = newKey;
        changed = true;
      }

      formKeys[newKey] = true;

      if (['address', 'container', 'datagrid', 'editgrid', 'tree'].includes(component.type) || component.tree || component.arrayTree) {
        return true;
      }
    }, true);
    return changed;
  },

  additionalShortcuts: {
    button: [
      'Enter',
      'Esc'
    ]
  },

  getAlphaShortcuts() {
    return _.range('A'.charCodeAt(), 'Z'.charCodeAt() + 1).map((charCode) => String.fromCharCode(charCode));
  },

  getAdditionalShortcuts(type) {
    return this.additionalShortcuts[type] || [];
  },

  getBindedShortcuts(components, input) {
    const result = [];

    eachComponent(components, (component) => {
      if (component === input) {
        return;
      }

      if (component.shortcut) {
        result.push(component.shortcut);
      }
      if (component.values) {
        component.values.forEach((value) => {
          if (value.shortcut) {
            result.push(value.shortcut);
          }
        });
      }
    }, true);

    return result;
  },

  getAvailableShortcuts(form, component) {
    if (!component) {
      return [];
    }
    return [''].concat(_.difference(
      this.getAlphaShortcuts().concat(this.getAdditionalShortcuts(component.type)),
      this.getBindedShortcuts(form.components, component)),
    ).map((shortcut) => ({
      label: shortcut,
      value: shortcut,
    }));
  }
};
<commit_msg>Fix the uniquify method in the builder<commit_after>import _ from 'lodash';
import { eachComponent, uniqueKey } from './utils';
export default {
  /**
   * Appends a number to a component.key to keep it unique
   *
   * @param {Object} form
   *   The components parent form.
   * @param {Object} component
   *   The component to uniquify
   */
  uniquify(container, component) {
    let changed = false;
    const formKeys = {};
    eachComponent(container, (comp) => {
      formKeys[comp.key] = true;

      if (['address', 'container', 'datagrid', 'editgrid', 'tree'].includes(comp.type) || comp.tree || comp.arrayTree) {
        return true;
      }
    }, true);

    // Recurse into all child components.
    eachComponent([component], (component) => {
      // Skip key uniquification if this component doesn't have a key.
      if (!component.key) {
        return;
      }

      const newKey = uniqueKey(formKeys, component.key);
      if (newKey !== component.key) {
        component.key = newKey;
        changed = true;
      }

      formKeys[newKey] = true;

      if (['address', 'container', 'datagrid', 'editgrid', 'tree'].includes(component.type) || component.tree || component.arrayTree) {
        return true;
      }
    }, true);
    return changed;
  },

  additionalShortcuts: {
    button: [
      'Enter',
      'Esc'
    ]
  },

  getAlphaShortcuts() {
    return _.range('A'.charCodeAt(), 'Z'.charCodeAt() + 1).map((charCode) => String.fromCharCode(charCode));
  },

  getAdditionalShortcuts(type) {
    return this.additionalShortcuts[type] || [];
  },

  getBindedShortcuts(components, input) {
    const result = [];

    eachComponent(components, (component) => {
      if (component === input) {
        return;
      }

      if (component.shortcut) {
        result.push(component.shortcut);
      }
      if (component.values) {
        component.values.forEach((value) => {
          if (value.shortcut) {
            result.push(value.shortcut);
          }
        });
      }
    }, true);

    return result;
  },

  getAvailableShortcuts(form, component) {
    if (!component) {
      return [];
    }
    return [''].concat(_.difference(
      this.getAlphaShortcuts().concat(this.getAdditionalShortcuts(component.type)),
      this.getBindedShortcuts(form.components, component)),
    ).map((shortcut) => ({
      label: shortcut,
      value: shortcut,
    }));
  }
};
<|endoftext|>


<commit_before>import Web3 from 'web3';

const getWeb3 = () => {
    return new Promise((resolve, reject) => {
	// Wait for loading completion to avoid race conditions with web3 injection timing.
	window.addEventListener('load', function() {
	    var web3 = window.web3;

	    // Checking if Web3 has been injected by the browser (Mist/MetaMask)
	    if (typeof web3 !== 'undefined') {
		// Use Mist/MetaMask's provider.
		web3 = new Web3(web3.currentProvider);
		console.log('Injected web3 detected.');

	    } else {
		console.log('No web3 instance injected.');
	    }

	    resolve(web3);
	});
    });
}

export default getWeb3;
<commit_msg>Update getWeb3.js<commit_after>import Web3 from 'web3';

const getWeb3 = () => {
    return new Promise((resolve, reject) => {
	// Wait for loading completion to avoid race conditions with web3 injection timing.
	window.addEventListener('load', function() {
	    var web3 = window.web3;

	    // Checking if Web3 has been injected by the browser (Mist/MetaMask)
	    if (typeof web3 !== 'undefined') {
		// Use Mist/MetaMask's provider.
		web3 = new Web3(web3.currentProvider);
		console.log('Injected web3 detected.');
	    } else {
		console.log('No web3 instance injected.');
	    }

	    resolve(web3);
	});
    });
}

export default getWeb3;
<|endoftext|>


<commit_before>function cleanup() {
  let cssSelector = [
    'head > link',
    'head > style',
    'head > link',
    'head > script',
    'body > :not(script)'
  ]
  document.querySelectorAll(cssSelector.join(','))
    .forEach(element => element.parentNode.removeChild(element))
}

function insertRootContainer() {
  let root = document.createElement('div')
  root.setAttribute('id', 'root')
  let scriptNode = document.querySelector('body script')
  scriptNode.parentNode.insertBefore(root, scriptNode)
}

export { cleanup, insertRootContainer }
<commit_msg>get list item from initial page<commit_after>function cleanup() {
  let cssSelector = [
    'head > link',
    'head > style',
    'head > link',
    'head > script',
    'body > :not(script)'
  ]
  document.querySelectorAll(cssSelector.join(','))
    .forEach(element => element.parentNode.removeChild(element))
}

function getListItems() {
  return [...document.querySelectorAll('table table a')]
}

function insertRootContainer() {
  let root = document.createElement('div')
  root.setAttribute('id', 'root')
  let scriptNode = document.querySelector('body script')
  scriptNode.parentNode.insertBefore(root, scriptNode)
}

export { cleanup, getListItems ,insertRootContainer }
<|endoftext|>


<commit_before>// Vue won't get included in bundle as it is externalized
// https://cli.vuejs.org/guide/build-targets.html#library
import Vue from 'vue';
import buildMediaQuery from './buildMediaQuery';
import defaultScreens from './defaults/screens.json';
import { isUndefined, mapValues, toPairs, has } from './_';

let isSettingUp = false;
let shouldRefreshQueries = false;
let screensComp = null;

export function setupScreens(screens = defaultScreens, forceSetup) {
  if ((screensComp && !forceSetup) || isSettingUp) {
    return;
  }
  isSettingUp = true;
  shouldRefreshQueries = true;
  // Use a private Vue component to store reactive screen matches
  screensComp = new Vue({
    data() {
      return {
        matches: [],
        queries: [],
      };
    },
    methods: {
      refreshQueries() {
        this.queries = mapValues(screens, v => {
          const query = window.matchMedia(buildMediaQuery(v));
          query.addListener(this.refreshMatches);
          return query;
        });
        this.refreshMatches();
      },
      refreshMatches() {
        this.matches = toPairs(this.queries)
          .filter(p => p[1].matches)
          .map(p => p[0]);
      },
    },
  });
  isSettingUp = false;
}

// Global mixin that provides responsive '$screens' utility method
// that refreshes any time the screen matches update
Vue.mixin({
  beforeCreate() {
    if (!isSettingUp) {
      setupScreens();
    }
  },
  mounted() {
    if (shouldRefreshQueries && screensComp) {
      screensComp.refreshQueries();
      shouldRefreshQueries = false;
    }
  },
  computed: {
    $screens() {
      return (config, def) =>
        screensComp.matches.reduce(
          (prev, curr) => (has(config, curr) ? config[curr] : prev),
          isUndefined(def) ? config.default : def,
        );
    },
  },
});
<commit_msg>`screens` improvements<commit_after>// Vue won't get included in bundle as it is externalized
// https://cli.vuejs.org/guide/build-targets.html#library
import Vue from 'vue';
import buildMediaQuery from './buildMediaQuery';
import defaultScreens from './defaults/screens.json';
import { isUndefined, mapValues, toPairs, has } from './_';

let isSettingUp = false;
let shouldRefreshQueries = false;
let screensComp = null;

export function setupScreens(screens = defaultScreens, forceSetup) {
  if ((screensComp && !forceSetup) || isSettingUp) {
    return;
  }
  isSettingUp = true;
  shouldRefreshQueries = true;
  // Use a private Vue component to store reactive screen matches
  screensComp = new Vue({
    data() {
      return {
        matches: [],
        queries: [],
      };
    },
    methods: {
      refreshQueries() {
        if (!window || !window.matchMedia) return;
        this.queries = mapValues(screens, v => {
          const query = window.matchMedia(buildMediaQuery(v));
          query.addEventListener('change', this.refreshMatches);
          return query;
        });
        this.refreshMatches();
      },
      refreshMatches() {
        this.matches = toPairs(this.queries)
          .filter(p => p[1].matches)
          .map(p => p[0]);
      },
    },
  });
  isSettingUp = false;
}

// Global mixin that provides responsive '$screens' utility method
// that refreshes any time the screen matches update
Vue.mixin({
  beforeCreate() {
    if (!isSettingUp) {
      setupScreens();
    }
  },
  mounted() {
    if (shouldRefreshQueries && screensComp) {
      screensComp.refreshQueries();
      shouldRefreshQueries = false;
    }
  },
  computed: {
    $screens() {
      return (config, def) =>
        screensComp.matches.reduce(
          (prev, curr) => (has(config, curr) ? config[curr] : prev),
          isUndefined(def) ? config.default : def,
        );
    },
  },
});
<|endoftext|>