Home Reference Source Test

src/core/arbiter.js

/**
 * @file arbiter.js
 * Contains logic for the dispatcher
 */

import dispatcher from './dispatcher.js';
import Input from './classes/Input.js';
import interpreter from './interpreter.js';
import util from './util.js';

/**
 * Function that handles event flow, negotiating with the interpreter, and dispatcher.
 * 1. Receiving all touch events in the window.
 * 2. Determining which gestures are linked to the target element.
 * 3. Negotiating with the Interpreter what event should occur.
 * 4. Sending events to the dispatcher to emit events to the target.
 * @param {Event} event - The event emitted from the window object.
 * @param {Object} region - The region object of the current listener.
 */
function arbiter(event, region) {
  var state = region.state;

  /*
   Return if a gesture is not in progress and won't be. Also catches the case where a previous
   event is in a partial state (2 finger pan, waits for both inputs to reach touchend)
   */
  if (state.inputs.length === 0 && util.normalizeEvent(event.type) !== 'start') {
    return;
  }

  /*
   Check for 'stale' or events that lost focus (e.g. a pan goes off screen/off region.
   Does not affect mobile devices.
   */
  if (typeof event.buttons !== 'undefined' && util.normalizeEvent(event.type) !== 'end' && event.buttons === 0) {
    state.resetInputs();
    return;
  }

  //Update the state with the new events. If the event is stopped, return;
  if (!state.updateInputs(event, region.element)) {
    return;
  }

  //Retrieve the initial target from any one of the inputs
  var bindings = state.retrieveBindingsByInitialPos();
  if (bindings.length > 0) {
    if (region.preventDefault) {
      util.setMSPreventDefault(region.element);
      event.preventDefault ? event.preventDefault() : (event.returnValue = false);
    } else {
      util.removeMSPreventDefault(region.element);
    }

    var toBeDispatched = {};
    var gestures = interpreter(bindings, event, state);

    //Determine the deepest path index to emit the event from, to avoid duplicate events being fired.
    gestures.forEach(gesture => {
      //var id = (gesture.binding.gesture.id) ? gesture.binding.gesture.id : gesture.binding.gesture.type;
      var id = gesture.binding.gesture.id;
      if (toBeDispatched[id]) {
        var path = util.getPropagationPath(event);
        if (util.getPathIndex(path, gesture.binding.element) < util.getPathIndex(path, toBeDispatched[id].binding.element)) {
          toBeDispatched[id] = gesture;
        }
      } else {
        toBeDispatched[id] = gesture;
      }
    });

    Object.keys(toBeDispatched).forEach(index => {
      var gesture = toBeDispatched[index];
      dispatcher(gesture.binding, gesture.data, gesture.events);
    });
  }

  var endCount = 0;
  state.inputs.forEach(input => {
    if (input.getCurrentEventType() === 'end') {
      endCount++;
    }
  });

  if (endCount === state.inputs.length) {
    state.resetInputs();
  }

}
/*arbiter*/

export default arbiter;