Source: ui/gestureemitter.js

var _ = require("lodash");
var binder = require("../events").binder;
var hasEmitter = require("../events").hasEmitter;
var validate = require("../utils/validate");

/**
 * Base class for gesture emitters.
 *
 * The purpose of this class is to abstract away different types of gesture and UI events
 * and re-emit the events in a normalized structures ({@link DecksEvent}), via a decks {@link Emitter}.
 *
 * @class
 * @abstract
 * @mixes binder
 * @mixes hasEmitter
 * @param {!Object} options - gesture configuration options
 * @param {?(Emitter|Object)} [options.emitter={}] - Emitter instance or options on which to emit events
 * @param {!Element} options.element - Element for which to listen for and emit gesture/DOM events
 * @param {!Hammer} options.hammer - Hammer instance for the element
 * @param {boolean} options.enabled - whether this {@link GestureEmitter} is enabled (binds to/re-emits gesture events)
 */
function GestureEmitter(options) {
  if (!(this instanceof GestureEmitter)) {
    return new GestureEmitter(options);
  }

  options = _.merge({}, this.defaultOptions, options);

  /**
   * Indicates if this emitter is enabled (listens for events, and forwards them on the {@link Emitter})
   */
  this.enabled = !!options.enabled;

  this.setEmitter(options.emitter || {});
  this.setElement(options.element);
  this.setHammer(options.hammer);

  // Note: don't this.bind() here - let subclass bind after setting up other options
}

_.extend(GestureEmitter.prototype, binder, hasEmitter, /** @lends GestureEmitter.prototype */ {
  /**
   * Default options
   */
  defaultOptions: {
    /**
     * Whether this {@link GestureEmitter} is enabled - defaults to false
     */
    enabled: false
  },

  /**
   * Sets the Element monitored by this {@link GestureEmitter}
   *
   * @param {!Element} element - element to monitor
   * @return {undefined}
   */
  setElement: function setElement(element) {
    validate(element, "element", { isElement: true, isNotSet: this.element });
    this.element = element;
  },

  /**
   * Sets the Hammer instance for this {@link GestureEmitter}
   *
   * The Hammer instance also wraps the element, and recognizes gestures.
   *
   * @param hammer
   * @return {undefined}
   */
  setHammer: function setHammer(hammer) {
    validate(hammer, "hammer", { isRequired: true, isNotSet: this.hammer });
    this.hammer = hammer;
  },

  /**
   * Gets a map of Element/DOM event names to method names for which to bind.
   *
   * This method should be implemented by a subclass, if the subclass is interested in DOM element-level
   * events.
   *
   * @return {undefined}
   */
  getElementEvents: function getElementEvents() {
    return {};
  },

  /**
   * Binds all of the Element-level events specified by {@link GestureEmitter#getElementEvents}
   *
   * @return {undefined}
   */
  bindElementEvents: function bindElementEvents() {
    if (!this.enabled) {
      return;
    }
    this.bindEvents(this.element, this.getElementEvents());
  },

  /**
   * Unbinds all the Element-level events specified by {@link GestureEmitter#getElementEvents}
   *
   * @return {undefined}
   */
  unbindElementEvents: function unbindElementEvents() {
    if (!this.enabled) {
      return;
    }
    this.unbindEvents(this.element, this.getElementEvents());
  },

  /**
   * Gets a map of Hammer event names to method names for which to bind.
   *
   * This method should be implemented by the subclass, if the subclass is interested in Hammer
   * events.
   *
   * @return {undefined}
   */
  getHammerEvents: function getHammerEvents() {
    return {};
  },

  /**
   * Binds to Hammer events specified by {@link GestureEmitter#getHammerEvents}
   *
   * @return {undefined}
   */
  bindHammerEvents: function bindHammerEvents() {
    if (!this.enabled) {
      return;
    }
    this.bindEvents(this.hammer, this.getHammerEvents());
  },

  /**
   * Unbinds from hammer events specified by {@link GestureEmitter#getHammerEvents}
   *
   * @return {undefined}
   */
  unbindHammerEvents: function unbindHammerEvents() {
    if (!this.enabled) {
      return;
    }
    this.unbindEvents(this.hammer, this.getHammerEvents());
  },

  /**
   * Binds all events (element and Hammer)
   *
   * @return {undefined}
   */
  bind: function bind() {
    this.bindElementEvents();
    this.bindHammerEvents();
  },

  /**
   * Unbinds all events (element and Hammer)
   *
   * @return {undefined}
   */
  unbind: function unbind() {
    this.unbindElementEvents();
    this.unbindHammerEvents();
  },

  /**
   * Destroys this instance by unbinding from all bound events (Element-level, and Hammer).
   *
   * @return {undefined}
   */
  destroy: function destroy() {
    this.unbind();
  }
});

module.exports = GestureEmitter;