Fork me on GitHub
Show:

File: ../src/api/params/models/params.js

define([
  'aeris/util',
  'aeris/config',
  'aeris/model',
  'aeris/api/params/collections/filtercollection',
  'aeris/api/params/collections/chainedqueries',
  'aeris/errors/validationerror',
  'aeris/helpers/validator/boundsvalidator'
], function(_, aerisConfig, Model, Filters, ChainedQueries, ValidationError, BoundsValidator) {
  /**
   * Represents parameters to be included
   * with a request to the Aeris API.
   *
   * @class aeris.api.params.models.Params
   * @extends aeris.Model
   *
   * @param {Object=} opt_options
   * @param {function():aeris.api.params.collections.FilterCollection=} opt_options.FilterCollectionType Constructor for filter.
   * @param {function():aeris.api.params.collections.ChainedQueries=} opt_options.QueryType Constructor for query attr model.
   *
   * @constructor
   */
  var Params = function(opt_attrs, opt_options) {
    var options = _.defaults(opt_options || {}, {
      FilterCollectionType: Filters,
      QueryType: ChainedQueries,
      validate: true
    });

    var attrs = _.defaults(opt_attrs || {}, {
      /**
       * Location parameter
       *
       * @attribute p
       * @type {string|aeris.maps.LatLon}
       */
      p: null,

      /**
       * @attribute filter
       * @type {aeris.api.params.collections.FilterCollection}
       */
      filter: [],

      /**
       * @attribute query
       * @type {aeris.api.params.collections.ChainedQueries}
       */
      query: [],

      /**
       * @attribute client_id
       * @type {string}
       */
      client_id: aerisConfig.get('apiId'),

      /**
       * @attribute client_secret
       * @type {string}
       */
      client_secret: aerisConfig.get('apiSecret')
    });


    /**
     * @type {function():aeris.api.params.collections.ChainedQuery} Constructor for query attribute object.
     * @private
     * @property QueryType_
     */
    this.QueryType_ = options.QueryType;


    /**
     * @property FilterCollectionType_
     * @private
     * @type {function():aeris.api.params.collections.FilterCollection}
     */
    this.FilterCollectionType_ = options.FilterCollectionType;


    // Process query/filter attrs provided as raw objects
    if (!(attrs.query instanceof this.QueryType_)) {
      attrs.query = new this.QueryType_(attrs.query);
    }
    if (!(attrs.filter instanceof this.FilterCollectionType_)) {
      attrs.filter = new this.FilterCollectionType_(attrs.filter);
    }

    Model.call(this, attrs, options);


    this.proxyEventsForAttr_('query');
    this.proxyEventsForAttr_('filter');

    this.bindToApiKeys_();
  };
  _.inherits(Params, Model);


  /**
   * @method validate
   */
  Params.prototype.validate = function(attrs) {
    var placeError = this.validatePlace_(attrs.p);

    if (placeError) {
      return placeError;
    }

    if (attrs.query && !(attrs.query instanceof this.QueryType_)) {
      return new ValidationError('query', attrs.query + ' is not a valid Query');
    }
  };


  /**
   * Bind client_id/secret params to
   * global apiKey config.
   *
   * @private
   * @method bindToApiKeys_
   */
  Params.prototype.bindToApiKeys_ = function() {
    this.listenTo(aerisConfig, 'change:apiId change:apiSecret', function() {
      this.set({
        client_id: this.get('client_id') || aerisConfig.get('apiId'),
        client_secret: this.get('client_secret') || aerisConfig.get('apiSecret')
      });
    });
  };


  Params.prototype.validatePlace_ = function(p) {
    var NO_ERROR = void 0;
    var isPlaceName = _.isString(p);
    var isZipCode = _.isNumeric(p);
    var isLatLon = _.isArray(p) && _.isNumeric(p[0]);
    var boundsError;

    if (_.isNull(p)) {
      return NO_ERROR;
    }
    if (isPlaceName) {
      return NO_ERROR;
    }
    if (isZipCode) {
      return NO_ERROR;
    }
    if (isLatLon) {
      return NO_ERROR;
    }

    boundsError = this.validateBounds_(p);

    if (boundsError) {
      return boundsError;
    }
  };


  Params.prototype.validateBounds_ = function(bounds) {
    var boundsValidator = new BoundsValidator(bounds);

    if (!boundsValidator.isValid()) {
      return boundsValidator.getLastError();
    }
  };


  /**
   * @method toJSON
   */
  Params.prototype.toJSON = function() {
    var json = Model.prototype.toJSON.apply(this, arguments);


    _.each(json, function(param, paramName) {
      // Clean out null, undefined, and empty arrays
      var isEmptyArray = _.isArray(param) && !param.length;
      if (_.isNull(param) || _.isUndefined(param) || isEmptyArray) {
        delete json[paramName];
        return;
      }

      // Convert dates to UNIX timestamps
      if (param instanceof Date) {
        json[paramName] = Math.ceil(param.getTime() / 1000);
      }
    });

    // Convert filter to comma-separated string
    if (this.get('filter')) {
      json.filter = this.get('filter').toString();
    }

    if (this.get('query')) {
      json.query = this.get('query').toString();
    }

    // Convert place polygon to comma-separated string
    if (_.isArray(this.get('p'))) {
      json.p = this.get('p').join(',');
    }

    return json;
  };


  /**
   * Sets the bound limits within which
   * to search for.
   *
   * If null is passed, will remove the bounds limit
   * parameter altogether.
   *
   * @param {?Array.<Array.<number>>} bounds Array of SW and NE lat/lons.
   * @method setBounds
   */
  Params.prototype.setBounds = function(bounds) {
    if (_.isNull(bounds)) {
      this.unset('p');
      return;
    }

    this.set({
      p: bounds
    }, { validate: true });
  };


  /**
   * Add a filter
   * Delegates to aeris.api.params.collections.FilterCollection#add
   * @method addFilter
   */
  Params.prototype.addFilter = function(filters, opt_options) {
    this.get('filter').add(filters, opt_options);
  };


  /**
   * Remove a filter.
   *
   * Delegates to aeris.api.params.collections.FilterCollection#remove
   * @method removeFilter
   */
  Params.prototype.removeFilter = function(filters, opt_options) {
    this.get('filter').remove(filters, opt_options);
  };

  /**
   * Resets the filters.
   *
   * Delegates to aeris.api.params.collections.FilterCollection#reset
   * @method resetFilter
   */
  Params.prototype.resetFilter = function(filters, opt_options) {
    this.get('filter').reset(filters, opt_options);
  };


  /**
   * Add a query term to Aeris API request.
   *
   * @method addQuery
   * @param {aeris.api.params.models.Query|Array.<aeris.api.params.models.Query>} query
   * @param {Object=} opt_options
   */
  Params.prototype.addQuery = function(query, opt_options) {
    this.get('query').add(query, opt_options);
  };

  /**
   * Remove a query from the Aeris API request
   *
   * @method removeQuery
   * @param {aeris.api.params.models.Query|Array.<aeris.api.params.models.Query>|string|Array.<string>} query model(s), or property (key).
   * @param {Object=} opt_options
   */
  Params.prototype.removeQuery = function(query, opt_options) {
    this.get('query').remove(query, opt_options);
  };

  /**
   * Resets the query for the Aeris API request.
   *
   * @method resetQuery
   * @param {aeris.api.params.models.Query|Array.<aeris.api.params.models.Query>=} opt_query
   * @param {Object=} opt_options
   */
  Params.prototype.resetQuery = function(opt_query, opt_options) {
    this.get('query').reset(opt_query, opt_options);
  };


  /**
   * Returns the query for the Aeris API request.
   *
   * @method getQuery
   * @return {aeris.api.params.collections.ChainedQueries}
   */
  Params.prototype.getQuery = function() {
    return this.get('query');
  };


  /**
   * Proxy events for a nested {aeris.Model|aeris.Collection} object.
   *
   * @method proxyEventsForAttr_
   * @private
   * @param {string} attr Attribute name of the nested object.
   */
  Params.prototype.proxyEventsForAttr_ = function(attr) {
    // Bind to current query model
    if (this.get(attr) && this.get(attr).on) {
      this.listenTo(this.get(attr), 'add remove change reset', function(model, opts) {
        this.trigger('change:' + attr, this, this.get(attr), opts);
        this.trigger('change', this, opts);
      });
    }
  };


  return Params;
});