Fork me on GitHub
Show:

File: ../src/maps/markers/marker.js

define([
  'aeris/util',
  'aeris/maps/extensions/mapextensionobject',
  'aeris/togglebehavior',
  'aeris/errors/validationerror',
  'aeris/maps/strategy/markers/marker'
], function(_, MapExtensionObject, ToggleBehavior, ValidationError, MarkerStrategy) {
  /**
   * A marked location on a map.
   *
   * A Marker is a type of {aeris.ViewModel}, which means that it can bind its attributes to a data model ({aeris.Model} or {Backbone.Model}). This allows you to easily bind data from an API to a marker, or {aeris.maps.markercollections.MarkerCollection}.
   *
   * For example, say you have a data model called `Place`, which receives data from an API like so:
   *
   * <code class="example">
   *    var place = new Place();
   *    place.fetch();
   *    //...
   *    place.toJSON === {
   *      id: 1,
   *      description: 'Joe\'s bar and grill.',
   *      category: 'restaurant',
   *      location: {
   *        lat: 45.23,
   *        long: -90.87
   *      }
   *    }
   * </code>
   *
   * You can now bind a {aeris.maps.Marker} to the place data:
   *
   * <code class="example">
   *    var placeMarker = new aeris.maps.Marker(null, {
   *      data: place,
   *
   *      // Use attribute transforms to translate raw data
   *      // into marker attributes.
   *      // Any changes to the Place model will be reflected
   *      // in the placeMarker, using these attributeTransforms.
   *      attributeTransforms: {
   *
   *        // Format position as [lat, lon]
   *        position: function() {
   *          return [
   *            this.getDataAttribute('location.lat'),
   *            this.getDataAttribute('location.long')
   *          ];
   *        },
   *
   *        // Use data description as marker title
   *        title: function() {
   *          return this.getDataAttribute('description');
   *        },
   *
   *        // Choose a icon url based on the
   *        // data category
   *        url: function() {
   *          var category = this.getDataAttribute('category');
   *
   *          if (category === 'restaurant') {
   *            return 'restaurant_icon.png';
   *          }
   *          else {
   *            return 'some_other_place_icon.png'
   *          }
   *        }
   *      }
   *    });
   * </code>
   *
   * @publicApi
   * @class aeris.maps.markers.Marker
   *
   * @extends aeris.maps.extensions.MapExtensionObject
   * @uses aeris.maps.ToggleBehavior
   * @publicApi
   *
   * @constructor
   *
   * @param {Object=} opt_attrs
   * @param {aeris.maps.LatLon} opt_attrs.position The lat/lon position to set the Marker.
   * @param {Boolean=} opt_attrs.clickable Whether the user can click the marker. Default is true.
   * @param {Boolean=} opt_attrs.draggable Whether the user can drag the marker. Default is true.
   * @param {string=} opt_attrs.url URL to the icon.
   * @param {number=} opt_attrs.width Width of the icon, in pixels.
   * @param {number=} opt_attrs.height Height of the icon, in pixels.
   *
   * @param {Object=} opt_options
   * @param {aeris.maps.AbstractStrategy} opt_options.strategy
   */
  var Marker = function(opt_attrs, opt_options) {
    var options = _.defaults(opt_options || {}, {
      strategy: MarkerStrategy
    });

    var attrs = _.defaults(opt_attrs || {}, {
      /**
       * @attribute position
       * @type {aeris.maps.LatLon}
       */
      position: [45, -90],

      /**
       * Icon url.
       *
       * @attribute url
       * @type {string}
       */
      url: '//iwx.aerisapi.com/v1.6/wnmapapp/css/assets/markers/earthquake/quake_mini.png',

      /**
       * Pixels between the marker's lat/lon
       * position and the left side of the icon image.
       *
       * @attribute offsetX
       * @type {number}
       */
      offsetX: 9,

      /**
       * Pixels between the marker's lat/lon
       * position and the top of the icon image.
       *
       * @attribute offsetY
       * @type {number}
       */
      offsetY: 9,

      /**
       * Whether to allow click events
       * on the marker.
       *
       * @attribute clickable
       * @type {Boolean}
       */
      clickable: true,

      /**
       * Whether to allow drag events
       * on the marker.
       *
       * @attribute draggable
       * @type {Boolean}
       */
      draggable: false,

      /**
       * Marker title.
       *
       * @attribute title
       * @type {string}
       * @default ''
       */
      title: ''
    });

    // Default selected styles to base styles
    attrs.selectedUrl || (attrs.selectedUrl = attrs.url);
    attrs.selectedOffsetX || (attrs.selectedOffsetX = attrs.offsetX);
    attrs.selectedOffsetY || (attrs.selectedOffsetY = attrs.offsetY);

    MapExtensionObject.call(this, attrs, options);
    ToggleBehavior.call(this);
  };
  _.inherits(Marker, MapExtensionObject);
  _.extend(Marker.prototype, ToggleBehavior.prototype);


  /**
   * @method validate
   * @protected
   */
  Marker.prototype.validate = function(attrs) {
    if (
      attrs.position && (
        !_.isArray(attrs.position) || !_.isNumber(attrs.position[0]) || !_.isNumber(attrs.position[1]) ||
        attrs.position.length !== 2
      )
    ) {
      return new ValidationError(attrs.position.toString() + ' is not a valid latLon position');
    }
    if (!_.isString(attrs.title)) {
      return new ValidationError(attrs.title + ' is not a valid marker title.');
    }
  };


  /**
   * @param {aeris.maps.LatLon} latLon
   * @method setPosition
   */
  Marker.prototype.setPosition = function(latLon) {
    this.set('position', latLon, { validate: true });
  };


  /**
   * @return {aeris.maps.LatLon}
   * @method getPosition
   */
  Marker.prototype.getPosition = function() {
    return this.get('position');
  };


  /**
   * @param {string} url
   * @method setUrl
   */
  Marker.prototype.setUrl = function(url) {
    this.set('url', url, { validate: true });
  };


  /**
   * Return the url of the marker icon.
   *
   * @method getUrl
   * @return {string}
  */
  Marker.prototype.getUrl = function() {
    return this.get('url');
  };

  /**
   * @method setSelectedUrl
   * @param {string} selectedUrl
   */
  Marker.prototype.setSelectedUrl = function(selectedUrl) {
    this.set('selectedUrl', selectedUrl, { validate: true });
  };

  /**
   * @method getSelectedUrl
   * @return {string}
   */
  Marker.prototype.getSelectedUrl = function() {
    return this.get('selectedUrl');
  };


  /**
   * This method method may be overriden to return
   * an arbitrary "type" category for the marker.
   * Used by MarkerClusterer strategies to split up
   * a single MarkerColection into several cluster sets.
   *
   * @method getType
   * @return {?string}
   */
  Marker.prototype.getType = function() {
    return null;
  };


  return _.expose(Marker, 'aeris.maps.markers.Marker');
});