text: goog.provide('ol.interaction.Translate');
  goog.provide('ol.interaction.TranslateEvent');
  
  goog.require('ol.events');
  goog.require('ol.events.Event');
  goog.require('ol.array');
  goog.require('ol.interaction.Pointer');
  
  
  /**
   * @enum {string}
   */
  ol.interaction.TranslateEventType = {
    /**
     * Triggered upon feature translation start.
     * @event ol.interaction.TranslateEvent#translatestart
     * @api
     */
    TRANSLATESTART: 'translatestart',
    /**
     * Triggered upon feature translation.
     * @event ol.interaction.TranslateEvent#translating
     * @api
     */
    TRANSLATING: 'translating',
    /**
     * Triggered upon feature translation end.
     * @event ol.interaction.TranslateEvent#translateend
     * @api
     */
    TRANSLATEEND: 'translateend'
  };
  
  
  /**
   * @classdesc
   * Events emitted by {@link ol.interaction.Translate} instances are instances of
   * this type.
   *
   * @constructor
   * @extends {ol.events.Event}
   * @implements {oli.interaction.TranslateEvent}
   * @param {ol.interaction.TranslateEventType} type Type.
   * @param {ol.Collection.<ol.Feature>} features The features translated.
   * @param {ol.Coordinate} coordinate The event coordinate.
   */
  ol.interaction.TranslateEvent = function(type, features, coordinate) {
  
    goog.base(this, type);
  
    /**
     * The features being translated.
     * @type {ol.Collection.<ol.Feature>}
     * @api
     */
    this.features = features;
  
    /**
     * The coordinate of the drag event.
     * @const
     * @type {ol.Coordinate}
     * @api
     */
    this.coordinate = coordinate;
  };
  goog.inherits(ol.interaction.TranslateEvent, ol.events.Event);
  
  
  /**
   * @classdesc
   * Interaction for translating (moving) features.
   *
   * @constructor
   * @extends {ol.interaction.Pointer}
   * @fires ol.interaction.TranslateEvent
   * @param {olx.interaction.TranslateOptions} options Options.
   * @api
   */
  ol.interaction.Translate = function(options) {
    goog.base(this, {
      handleDownEvent: ol.interaction.Translate.handleDownEvent_,
      handleDragEvent: ol.interaction.Translate.handleDragEvent_,
      handleMoveEvent: ol.interaction.Translate.handleMoveEvent_,
      handleUpEvent: ol.interaction.Translate.handleUpEvent_
    });
  
  
    /**
     * @type {string|undefined}
     * @private
     */
    this.previousCursor_ = undefined;
  
  
    /**
     * The last position we translated to.
     * @type {ol.Coordinate}
     * @private
     */
    this.lastCoordinate_ = null;
  
  
    /**
     * @type {ol.Collection.<ol.Feature>}
     * @private
     */
    this.features_ = options.features !== undefined ? options.features : null;
  
    /**
     * @type {ol.Feature}
     * @private
     */
    this.lastFeature_ = null;
  };
  goog.inherits(ol.interaction.Translate, ol.interaction.Pointer);
  
  
  /**
   * @param {ol.MapBrowserPointerEvent} event Event.
   * @return {boolean} Start drag sequence?
   * @this {ol.interaction.Translate}
   * @private
   */
  ol.interaction.Translate.handleDownEvent_ = function(event) {
    this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map);
    if (!this.lastCoordinate_ && this.lastFeature_) {
      this.lastCoordinate_ = event.coordinate;
      ol.interaction.Translate.handleMoveEvent_.call(this, event);
      this.dispatchEvent(
          new ol.interaction.TranslateEvent(
              ol.interaction.TranslateEventType.TRANSLATESTART, this.features_,
              event.coordinate));
      return true;
    }
    return false;
  };
  
  
  /**
   * @param {ol.MapBrowserPointerEvent} event Event.
   * @return {boolean} Stop drag sequence?
   * @this {ol.interaction.Translate}
   * @private
   */
  ol.interaction.Translate.handleUpEvent_ = function(event) {
    if (this.lastCoordinate_) {
      this.lastCoordinate_ = null;
      ol.interaction.Translate.handleMoveEvent_.call(this, event);
      this.dispatchEvent(
          new ol.interaction.TranslateEvent(
              ol.interaction.TranslateEventType.TRANSLATEEND, this.features_,
              event.coordinate));
      return true;
    }
    return false;
  };
  
  
  /**
   * @param {ol.MapBrowserPointerEvent} event Event.
   * @this {ol.interaction.Translate}
   * @private
   */
  ol.interaction.Translate.handleDragEvent_ = function(event) {
    if (this.lastCoordinate_) {
      var newCoordinate = event.coordinate;
      var deltaX = newCoordinate[0] - this.lastCoordinate_[0];
      var deltaY = newCoordinate[1] - this.lastCoordinate_[1];
  
      if (this.features_) {
        this.features_.forEach(function(feature) {
          var geom = feature.getGeometry();
          geom.translate(deltaX, deltaY);
          feature.setGeometry(geom);
        });
      } else if (this.lastFeature_) {
        var geom = this.lastFeature_.getGeometry();
        geom.translate(deltaX, deltaY);
        this.lastFeature_.setGeometry(geom);
      }
  
      this.lastCoordinate_ = newCoordinate;
      this.dispatchEvent(
          new ol.interaction.TranslateEvent(
              ol.interaction.TranslateEventType.TRANSLATING, this.features_,
              newCoordinate));
    }
  };
  
  
  /**
   * @param {ol.MapBrowserEvent} event Event.
   * @this {ol.interaction.Translate}
   * @private
   */
  ol.interaction.Translate.handleMoveEvent_ = function(event) {
    var elem = event.map.getTargetElement();
    var intersectingFeature = event.map.forEachFeatureAtPixel(event.pixel,
        function(feature) {
          return feature;
        });
  
    if (intersectingFeature) {
      var isSelected = false;
  
      if (this.features_ &&
          ol.array.includes(this.features_.getArray(), intersectingFeature)) {
        isSelected = true;
      }
  
      this.previousCursor_ = elem.style.cursor;
  
      // WebKit browsers don't support the grab icons without a prefix
      elem.style.cursor = this.lastCoordinate_ ?
          '-webkit-grabbing' : (isSelected ? '-webkit-grab' : 'pointer');
  
      // Thankfully, attempting to set the standard ones will silently fail,
      // keeping the prefixed icons
      elem.style.cursor = !this.lastCoordinate_ ?
          'grabbing' : (isSelected ? 'grab' : 'pointer');
  
    } else {
      elem.style.cursor = this.previousCursor_ !== undefined ?
          this.previousCursor_ : '';
      this.previousCursor_ = undefined;
    }
  };
  
  
  /**
   * Tests to see if the given coordinates intersects any of our selected
   * features.
   * @param {ol.Pixel} pixel Pixel coordinate to test for intersection.
   * @param {ol.Map} map Map to test the intersection on.
   * @return {ol.Feature} Returns the feature found at the specified pixel
   * coordinates.
   * @private
   */
  ol.interaction.Translate.prototype.featuresAtPixel_ = function(pixel, map) {
    var found = null;
  
    var intersectingFeature = map.forEachFeatureAtPixel(pixel,
        function(feature) {
          return feature;
        });
  
    if (this.features_ &&
        ol.array.includes(this.features_.getArray(), intersectingFeature)) {
      found = intersectingFeature;
    }
  
    return found;
  };
  