|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |  | /*! * PEP v0.4.3 | https://github.com/jquery/PEP
 * Copyright jQuery Foundation and other contributors | http://jquery.org/license
 */
(function (global, factory) {  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :  typeof define === 'function' && define.amd ? define(factory) :  (global.PointerEventsPolyfill = factory());}(this, function () { 'use strict';
  /**   * This is the constructor for new PointerEvents.   *   * New Pointer Events must be given a type, and an optional dictionary of   * initialization properties.   *   * Due to certain platform requirements, events returned from the constructor   * identify as MouseEvents.   *   * @constructor   * @param {String} inType The type of the event to create.   * @param {Object} [inDict] An optional dictionary of initial event properties.   * @return {Event} A new PointerEvent of type `inType`, initialized with properties from `inDict`.   */  var MOUSE_PROPS = [    'bubbles',    'cancelable',    'view',    'detail',    'screenX',    'screenY',    'clientX',    'clientY',    'ctrlKey',    'altKey',    'shiftKey',    'metaKey',    'button',    'relatedTarget',    'pageX',    'pageY'  ];
  var MOUSE_DEFAULTS = [    false,    false,    null,    null,    0,    0,    0,    0,    false,    false,    false,    false,    0,    null,    0,    0  ];
  function PointerEvent(inType, inDict) {    inDict = inDict || Object.create(null);
    var e = document.createEvent('Event');    e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false);
    // define inherited MouseEvent properties
    // skip bubbles and cancelable since they're set above in initEvent()
    for (var i = 2, p; i < MOUSE_PROPS.length; i++) {      p = MOUSE_PROPS[i];      e[p] = inDict[p] || MOUSE_DEFAULTS[i];    }    e.buttons = inDict.buttons || 0;
    // Spec requires that pointers without pressure specified use 0.5 for down
    // state and 0 for up state.
    var pressure = 0;
    if (inDict.pressure && e.buttons) {      pressure = inDict.pressure;    } else {      pressure = e.buttons ? 0.5 : 0;    }
    // add x/y properties aliased to clientX/Y
    e.x = e.clientX;    e.y = e.clientY;
    // define the properties of the PointerEvent interface
    e.pointerId = inDict.pointerId || 0;    e.width = inDict.width || 0;    e.height = inDict.height || 0;    e.pressure = pressure;    e.tiltX = inDict.tiltX || 0;    e.tiltY = inDict.tiltY || 0;    e.twist = inDict.twist || 0;    e.tangentialPressure = inDict.tangentialPressure || 0;    e.pointerType = inDict.pointerType || '';    e.hwTimestamp = inDict.hwTimestamp || 0;    e.isPrimary = inDict.isPrimary || false;    return e;  }
  /**   * This module implements a map of pointer states   */  var USE_MAP = window.Map && window.Map.prototype.forEach;  var PointerMap = USE_MAP ? Map : SparseArrayMap;
  function SparseArrayMap() {    this.array = [];    this.size = 0;  }
  SparseArrayMap.prototype = {    set: function(k, v) {      if (v === undefined) {        return this.delete(k);      }      if (!this.has(k)) {        this.size++;      }      this.array[k] = v;    },    has: function(k) {      return this.array[k] !== undefined;    },    delete: function(k) {      if (this.has(k)) {        delete this.array[k];        this.size--;      }    },    get: function(k) {      return this.array[k];    },    clear: function() {      this.array.length = 0;      this.size = 0;    },
    // return value, key, map
    forEach: function(callback, thisArg) {      return this.array.forEach(function(v, k) {        callback.call(thisArg, v, k, this);      }, this);    }  };
  var CLONE_PROPS = [
    // MouseEvent
    'bubbles',    'cancelable',    'view',    'detail',    'screenX',    'screenY',    'clientX',    'clientY',    'ctrlKey',    'altKey',    'shiftKey',    'metaKey',    'button',    'relatedTarget',
    // DOM Level 3
    'buttons',
    // PointerEvent
    'pointerId',    'width',    'height',    'pressure',    'tiltX',    'tiltY',    'pointerType',    'hwTimestamp',    'isPrimary',
    // event instance
    'type',    'target',    'currentTarget',    'which',    'pageX',    'pageY',    'timeStamp'  ];
  var CLONE_DEFAULTS = [
    // MouseEvent
    false,    false,    null,    null,    0,    0,    0,    0,    false,    false,    false,    false,    0,    null,
    // DOM Level 3
    0,
    // PointerEvent
    0,    0,    0,    0,    0,    0,    '',    0,    false,
    // event instance
    '',    null,    null,    0,    0,    0,    0  ];
  var BOUNDARY_EVENTS = {    'pointerover': 1,    'pointerout': 1,    'pointerenter': 1,    'pointerleave': 1  };
  var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');
  /**   * This module is for normalizing events. Mouse and Touch events will be   * collected here, and fire PointerEvents that have the same semantics, no   * matter the source.   * Events fired:   *   - pointerdown: a pointing is added   *   - pointerup: a pointer is removed   *   - pointermove: a pointer is moved   *   - pointerover: a pointer crosses into an element   *   - pointerout: a pointer leaves an element   *   - pointercancel: a pointer will no longer generate events   */  var dispatcher = {    pointermap: new PointerMap(),    eventMap: Object.create(null),    captureInfo: Object.create(null),
    // Scope objects for native events.
    // This exists for ease of testing.
    eventSources: Object.create(null),    eventSourceList: [],    /**     * Add a new event source that will generate pointer events.     *     * `inSource` must contain an array of event names named `events`, and     * functions with the names specified in the `events` array.     * @param {string} name A name for the event source     * @param {Object} source A new source of platform events.     */    registerSource: function(name, source) {      var s = source;      var newEvents = s.events;      if (newEvents) {        newEvents.forEach(function(e) {          if (s[e]) {            this.eventMap[e] = s[e].bind(s);          }        }, this);        this.eventSources[name] = s;        this.eventSourceList.push(s);      }    },    register: function(element) {      var l = this.eventSourceList.length;      for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
        // call eventsource register
        es.register.call(es, element);      }    },    unregister: function(element) {      var l = this.eventSourceList.length;      for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
        // call eventsource register
        es.unregister.call(es, element);      }    },    contains: /*scope.external.contains || */function(container, contained) {      try {        return container.contains(contained);      } catch (ex) {
        // most likely: https://bugzilla.mozilla.org/show_bug.cgi?id=208427
        return false;      }    },
    // EVENTS
    down: function(inEvent) {      inEvent.bubbles = true;      this.fireEvent('pointerdown', inEvent);    },    move: function(inEvent) {      inEvent.bubbles = true;      this.fireEvent('pointermove', inEvent);    },    up: function(inEvent) {      inEvent.bubbles = true;      this.fireEvent('pointerup', inEvent);    },    enter: function(inEvent) {      inEvent.bubbles = false;      this.fireEvent('pointerenter', inEvent);    },    leave: function(inEvent) {      inEvent.bubbles = false;      this.fireEvent('pointerleave', inEvent);    },    over: function(inEvent) {      inEvent.bubbles = true;      this.fireEvent('pointerover', inEvent);    },    out: function(inEvent) {      inEvent.bubbles = true;      this.fireEvent('pointerout', inEvent);    },    cancel: function(inEvent) {      inEvent.bubbles = true;      this.fireEvent('pointercancel', inEvent);    },    leaveOut: function(event) {      this.out(event);      this.propagate(event, this.leave, false);    },    enterOver: function(event) {      this.over(event);      this.propagate(event, this.enter, true);    },
    // LISTENER LOGIC
    eventHandler: function(inEvent) {
      // This is used to prevent multiple dispatch of pointerevents from
      // platform events. This can happen when two elements in different scopes
      // are set up to create pointer events, which is relevant to Shadow DOM.
      if (inEvent._handledByPE) {        return;      }      var type = inEvent.type;      var fn = this.eventMap && this.eventMap[type];      if (fn) {        fn(inEvent);      }      inEvent._handledByPE = true;    },
    // set up event listeners
    listen: function(target, events) {      events.forEach(function(e) {        this.addEvent(target, e);      }, this);    },
    // remove event listeners
    unlisten: function(target, events) {      events.forEach(function(e) {        this.removeEvent(target, e);      }, this);    },    addEvent: /*scope.external.addEvent || */function(target, eventName) {      target.addEventListener(eventName, this.boundHandler);    },    removeEvent: /*scope.external.removeEvent || */function(target, eventName) {      target.removeEventListener(eventName, this.boundHandler);    },
    // EVENT CREATION AND TRACKING
    /**     * Creates a new Event of type `inType`, based on the information in     * `inEvent`.     *     * @param {string} inType A string representing the type of event to create     * @param {Event} inEvent A platform event with a target     * @return {Event} A PointerEvent of type `inType`     */    makeEvent: function(inType, inEvent) {
      // relatedTarget must be null if pointer is captured
      if (this.captureInfo[inEvent.pointerId]) {        inEvent.relatedTarget = null;      }      var e = new PointerEvent(inType, inEvent);      if (inEvent.preventDefault) {        e.preventDefault = inEvent.preventDefault;      }      e._target = e._target || inEvent.target;      return e;    },
    // make and dispatch an event in one call
    fireEvent: function(inType, inEvent) {      var e = this.makeEvent(inType, inEvent);      return this.dispatchEvent(e);    },    /**     * Returns a snapshot of inEvent, with writable properties.     *     * @param {Event} inEvent An event that contains properties to copy.     * @return {Object} An object containing shallow copies of `inEvent`'s     *    properties.     */    cloneEvent: function(inEvent) {      var eventCopy = Object.create(null);      var p;      for (var i = 0; i < CLONE_PROPS.length; i++) {        p = CLONE_PROPS[i];        eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
        // Work around SVGInstanceElement shadow tree
        // Return the <use> element that is represented by the instance for Safari, Chrome, IE.
        // This is the behavior implemented by Firefox.
        if (HAS_SVG_INSTANCE && (p === 'target' || p === 'relatedTarget')) {          if (eventCopy[p] instanceof SVGElementInstance) {            eventCopy[p] = eventCopy[p].correspondingUseElement;          }        }      }
      // keep the semantics of preventDefault
      if (inEvent.preventDefault) {        eventCopy.preventDefault = function() {          inEvent.preventDefault();        };      }      return eventCopy;    },    getTarget: function(inEvent) {      var capture = this.captureInfo[inEvent.pointerId];      if (!capture) {        return inEvent._target;      }      if (inEvent._target === capture || !(inEvent.type in BOUNDARY_EVENTS)) {        return capture;      }    },    propagate: function(event, fn, propagateDown) {      var target = event.target;      var targets = [];
      // Order of conditions due to document.contains() missing in IE.
      while (target !== document && !target.contains(event.relatedTarget)) {        targets.push(target);        target = target.parentNode;
        // Touch: Do not propagate if node is detached.
        if (!target) {          return;        }      }      if (propagateDown) {        targets.reverse();      }      targets.forEach(function(target) {        event.target = target;        fn.call(this, event);      }, this);    },    setCapture: function(inPointerId, inTarget, skipDispatch) {      if (this.captureInfo[inPointerId]) {        this.releaseCapture(inPointerId, skipDispatch);      }
      this.captureInfo[inPointerId] = inTarget;      this.implicitRelease = this.releaseCapture.bind(this, inPointerId, skipDispatch);      document.addEventListener('pointerup', this.implicitRelease);      document.addEventListener('pointercancel', this.implicitRelease);
      var e = new PointerEvent('gotpointercapture');      e.pointerId = inPointerId;      e._target = inTarget;
      if (!skipDispatch) {        this.asyncDispatchEvent(e);      }    },    releaseCapture: function(inPointerId, skipDispatch) {      var t = this.captureInfo[inPointerId];      if (!t) {        return;      }
      this.captureInfo[inPointerId] = undefined;      document.removeEventListener('pointerup', this.implicitRelease);      document.removeEventListener('pointercancel', this.implicitRelease);
      var e = new PointerEvent('lostpointercapture');      e.pointerId = inPointerId;      e._target = t;
      if (!skipDispatch) {        this.asyncDispatchEvent(e);      }    },    /**     * Dispatches the event to its target.     *     * @param {Event} inEvent The event to be dispatched.     * @return {Boolean} True if an event handler returns true, false otherwise.     */    dispatchEvent: /*scope.external.dispatchEvent || */function(inEvent) {      var t = this.getTarget(inEvent);      if (t) {        return t.dispatchEvent(inEvent);      }    },    asyncDispatchEvent: function(inEvent) {      requestAnimationFrame(this.dispatchEvent.bind(this, inEvent));    }  };  dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
  var targeting = {    shadow: function(inEl) {      if (inEl) {        return inEl.shadowRoot || inEl.webkitShadowRoot;      }    },    canTarget: function(shadow) {      return shadow && Boolean(shadow.elementFromPoint);    },    targetingShadow: function(inEl) {      var s = this.shadow(inEl);      if (this.canTarget(s)) {        return s;      }    },    olderShadow: function(shadow) {      var os = shadow.olderShadowRoot;      if (!os) {        var se = shadow.querySelector('shadow');        if (se) {          os = se.olderShadowRoot;        }      }      return os;    },    allShadows: function(element) {      var shadows = [];      var s = this.shadow(element);      while (s) {        shadows.push(s);        s = this.olderShadow(s);      }      return shadows;    },    searchRoot: function(inRoot, x, y) {      if (inRoot) {        var t = inRoot.elementFromPoint(x, y);        var st, sr;
        // is element a shadow host?
        sr = this.targetingShadow(t);        while (sr) {
          // find the the element inside the shadow root
          st = sr.elementFromPoint(x, y);          if (!st) {
            // check for older shadows
            sr = this.olderShadow(sr);          } else {
            // shadowed element may contain a shadow root
            var ssr = this.targetingShadow(st);            return this.searchRoot(ssr, x, y) || st;          }        }
        // light dom element is the target
        return t;      }    },    owner: function(element) {      var s = element;
      // walk up until you hit the shadow root or document
      while (s.parentNode) {        s = s.parentNode;      }
      // the owner element is expected to be a Document or ShadowRoot
      if (s.nodeType !== Node.DOCUMENT_NODE && s.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {        s = document;      }      return s;    },    findTarget: function(inEvent) {      var x = inEvent.clientX;      var y = inEvent.clientY;
      // if the listener is in the shadow root, it is much faster to start there
      var s = this.owner(inEvent.target);
      // if x, y is not in this root, fall back to document search
      if (!s.elementFromPoint(x, y)) {        s = document;      }      return this.searchRoot(s, x, y);    }  };
  var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);  var map = Array.prototype.map.call.bind(Array.prototype.map);  var toArray = Array.prototype.slice.call.bind(Array.prototype.slice);  var filter = Array.prototype.filter.call.bind(Array.prototype.filter);  var MO = window.MutationObserver || window.WebKitMutationObserver;  var SELECTOR = '[touch-action]';  var OBSERVER_INIT = {    subtree: true,    childList: true,    attributes: true,    attributeOldValue: true,    attributeFilter: ['touch-action']  };
  function Installer(add, remove, changed, binder) {    this.addCallback = add.bind(binder);    this.removeCallback = remove.bind(binder);    this.changedCallback = changed.bind(binder);    if (MO) {      this.observer = new MO(this.mutationWatcher.bind(this));    }  }
  Installer.prototype = {    watchSubtree: function(target) {
      // Only watch scopes that can target find, as these are top-level.
      // Otherwise we can see duplicate additions and removals that add noise.
      //
      // TODO(dfreedman): For some instances with ShadowDOMPolyfill, we can see
      // a removal without an insertion when a node is redistributed among
      // shadows. Since it all ends up correct in the document, watching only
      // the document will yield the correct mutations to watch.
      if (this.observer && targeting.canTarget(target)) {        this.observer.observe(target, OBSERVER_INIT);      }    },    enableOnSubtree: function(target) {      this.watchSubtree(target);      if (target === document && document.readyState !== 'complete') {        this.installOnLoad();      } else {        this.installNewSubtree(target);      }    },    installNewSubtree: function(target) {      forEach(this.findElements(target), this.addElement, this);    },    findElements: function(target) {      if (target.querySelectorAll) {        return target.querySelectorAll(SELECTOR);      }      return [];    },    removeElement: function(el) {      this.removeCallback(el);    },    addElement: function(el) {      this.addCallback(el);    },    elementChanged: function(el, oldValue) {      this.changedCallback(el, oldValue);    },    concatLists: function(accum, list) {      return accum.concat(toArray(list));    },
    // register all touch-action = none nodes on document load
    installOnLoad: function() {      document.addEventListener('readystatechange', function() {        if (document.readyState === 'complete') {          this.installNewSubtree(document);        }      }.bind(this));    },    isElement: function(n) {      return n.nodeType === Node.ELEMENT_NODE;    },    flattenMutationTree: function(inNodes) {
      // find children with touch-action
      var tree = map(inNodes, this.findElements, this);
      // make sure the added nodes are accounted for
      tree.push(filter(inNodes, this.isElement));
      // flatten the list
      return tree.reduce(this.concatLists, []);    },    mutationWatcher: function(mutations) {      mutations.forEach(this.mutationHandler, this);    },    mutationHandler: function(m) {      if (m.type === 'childList') {        var added = this.flattenMutationTree(m.addedNodes);        added.forEach(this.addElement, this);        var removed = this.flattenMutationTree(m.removedNodes);        removed.forEach(this.removeElement, this);      } else if (m.type === 'attributes') {        this.elementChanged(m.target, m.oldValue);      }    }  };
  function shadowSelector(v) {    return 'body /shadow-deep/ ' + selector(v);  }  function selector(v) {    return '[touch-action="' + v + '"]';  }  function rule(v) {    return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + '; }';  }  var attrib2css = [    'none',    'auto',    'pan-x',    'pan-y',    {      rule: 'pan-x pan-y',      selectors: [        'pan-x pan-y',        'pan-y pan-x'      ]    }  ];  var styles = '';
  // only install stylesheet if the browser has touch action support
  var hasNativePE = window.PointerEvent || window.MSPointerEvent;
  // only add shadow selectors if shadowdom is supported
  var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot;
  function applyAttributeStyles() {    if (hasNativePE) {      attrib2css.forEach(function(r) {        if (String(r) === r) {          styles += selector(r) + rule(r) + '\n';          if (hasShadowRoot) {            styles += shadowSelector(r) + rule(r) + '\n';          }        } else {          styles += r.selectors.map(selector) + rule(r.rule) + '\n';          if (hasShadowRoot) {            styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n';          }        }      });
      var el = document.createElement('style');      el.textContent = styles;      document.head.appendChild(el);    }  }
  var pointermap = dispatcher.pointermap;
  // radius around touchend that swallows mouse events
  var DEDUP_DIST = 25;
  // left, middle, right, back, forward
  var BUTTON_TO_BUTTONS = [1, 4, 2, 8, 16];
  var HAS_BUTTONS = false;  try {    HAS_BUTTONS = new MouseEvent('test', { buttons: 1 }).buttons === 1;  } catch (e) {}
  // handler block for native mouse events
  var mouseEvents = {    POINTER_ID: 1,    POINTER_TYPE: 'mouse',    events: [      'mousedown',      'mousemove',      'mouseup',      'mouseover',      'mouseout'    ],    register: function(target) {      dispatcher.listen(target, this.events);    },    unregister: function(target) {      dispatcher.unlisten(target, this.events);    },    lastTouches: [],
    // collide with the global mouse listener
    isEventSimulatedFromTouch: function(inEvent) {      var lts = this.lastTouches;      var x = inEvent.clientX;      var y = inEvent.clientY;      for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
        // simulated mouse events will be swallowed near a primary touchend
        var dx = Math.abs(x - t.x);        var dy = Math.abs(y - t.y);        if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {          return true;        }      }    },    prepareEvent: function(inEvent) {      var e = dispatcher.cloneEvent(inEvent);
      // forward mouse preventDefault
      var pd = e.preventDefault;      e.preventDefault = function() {        inEvent.preventDefault();        pd();      };      e.pointerId = this.POINTER_ID;      e.isPrimary = true;      e.pointerType = this.POINTER_TYPE;      return e;    },    prepareButtonsForMove: function(e, inEvent) {      var p = pointermap.get(this.POINTER_ID);
      // Update buttons state after possible out-of-document mouseup.
      if (inEvent.which === 0 || !p) {        e.buttons = 0;      } else {        e.buttons = p.buttons;      }      inEvent.buttons = e.buttons;    },    mousedown: function(inEvent) {      if (!this.isEventSimulatedFromTouch(inEvent)) {        var p = pointermap.get(this.POINTER_ID);        var e = this.prepareEvent(inEvent);        if (!HAS_BUTTONS) {          e.buttons = BUTTON_TO_BUTTONS[e.button];          if (p) { e.buttons |= p.buttons; }          inEvent.buttons = e.buttons;        }        pointermap.set(this.POINTER_ID, inEvent);        if (!p || p.buttons === 0) {          dispatcher.down(e);        } else {          dispatcher.move(e);        }      }    },    mousemove: function(inEvent) {      if (!this.isEventSimulatedFromTouch(inEvent)) {        var e = this.prepareEvent(inEvent);        if (!HAS_BUTTONS) { this.prepareButtonsForMove(e, inEvent); }        e.button = -1;        pointermap.set(this.POINTER_ID, inEvent);        dispatcher.move(e);      }    },    mouseup: function(inEvent) {      if (!this.isEventSimulatedFromTouch(inEvent)) {        var p = pointermap.get(this.POINTER_ID);        var e = this.prepareEvent(inEvent);        if (!HAS_BUTTONS) {          var up = BUTTON_TO_BUTTONS[e.button];
          // Produces wrong state of buttons in Browsers without `buttons` support
          // when a mouse button that was pressed outside the document is released
          // inside and other buttons are still pressed down.
          e.buttons = p ? p.buttons & ~up : 0;          inEvent.buttons = e.buttons;        }        pointermap.set(this.POINTER_ID, inEvent);
        // Support: Firefox <=44 only
        // FF Ubuntu includes the lifted button in the `buttons` property on
        // mouseup.
        // https://bugzilla.mozilla.org/show_bug.cgi?id=1223366
        e.buttons &= ~BUTTON_TO_BUTTONS[e.button];        if (e.buttons === 0) {          dispatcher.up(e);        } else {          dispatcher.move(e);        }      }    },    mouseover: function(inEvent) {      if (!this.isEventSimulatedFromTouch(inEvent)) {        var e = this.prepareEvent(inEvent);        if (!HAS_BUTTONS) { this.prepareButtonsForMove(e, inEvent); }        e.button = -1;        pointermap.set(this.POINTER_ID, inEvent);        dispatcher.enterOver(e);      }    },    mouseout: function(inEvent) {      if (!this.isEventSimulatedFromTouch(inEvent)) {        var e = this.prepareEvent(inEvent);        if (!HAS_BUTTONS) { this.prepareButtonsForMove(e, inEvent); }        e.button = -1;        dispatcher.leaveOut(e);      }    },    cancel: function(inEvent) {      var e = this.prepareEvent(inEvent);      dispatcher.cancel(e);      this.deactivateMouse();    },    deactivateMouse: function() {      pointermap.delete(this.POINTER_ID);    }  };
  var captureInfo = dispatcher.captureInfo;  var findTarget = targeting.findTarget.bind(targeting);  var allShadows = targeting.allShadows.bind(targeting);  var pointermap$1 = dispatcher.pointermap;
  // This should be long enough to ignore compat mouse events made by touch
  var DEDUP_TIMEOUT = 2500;  var CLICK_COUNT_TIMEOUT = 200;  var ATTRIB = 'touch-action';  var INSTALLER;
  // handler block for native touch events
  var touchEvents = {    events: [      'touchstart',      'touchmove',      'touchend',      'touchcancel'    ],    register: function(target) {      INSTALLER.enableOnSubtree(target);    },    unregister: function() {
      // TODO(dfreedman): is it worth it to disconnect the MO?
    },    elementAdded: function(el) {      var a = el.getAttribute(ATTRIB);      var st = this.touchActionToScrollType(a);      if (st) {        el._scrollType = st;        dispatcher.listen(el, this.events);
        // set touch-action on shadows as well
        allShadows(el).forEach(function(s) {          s._scrollType = st;          dispatcher.listen(s, this.events);        }, this);      }    },    elementRemoved: function(el) {      el._scrollType = undefined;      dispatcher.unlisten(el, this.events);
      // remove touch-action from shadow
      allShadows(el).forEach(function(s) {        s._scrollType = undefined;        dispatcher.unlisten(s, this.events);      }, this);    },    elementChanged: function(el, oldValue) {      var a = el.getAttribute(ATTRIB);      var st = this.touchActionToScrollType(a);      var oldSt = this.touchActionToScrollType(oldValue);
      // simply update scrollType if listeners are already established
      if (st && oldSt) {        el._scrollType = st;        allShadows(el).forEach(function(s) {          s._scrollType = st;        }, this);      } else if (oldSt) {        this.elementRemoved(el);      } else if (st) {        this.elementAdded(el);      }    },    scrollTypes: {      EMITTER: 'none',      XSCROLLER: 'pan-x',      YSCROLLER: 'pan-y',      SCROLLER: /^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/    },    touchActionToScrollType: function(touchAction) {      var t = touchAction;      var st = this.scrollTypes;      if (t === 'none') {        return 'none';      } else if (t === st.XSCROLLER) {        return 'X';      } else if (t === st.YSCROLLER) {        return 'Y';      } else if (st.SCROLLER.exec(t)) {        return 'XY';      }    },    POINTER_TYPE: 'touch',    firstTouch: null,    isPrimaryTouch: function(inTouch) {      return this.firstTouch === inTouch.identifier;    },    setPrimaryTouch: function(inTouch) {
      // set primary touch if there no pointers, or the only pointer is the mouse
      if (pointermap$1.size === 0 || (pointermap$1.size === 1 && pointermap$1.has(1))) {        this.firstTouch = inTouch.identifier;        this.firstXY = { X: inTouch.clientX, Y: inTouch.clientY };        this.scrolling = false;        this.cancelResetClickCount();      }    },    removePrimaryPointer: function(inPointer) {      if (inPointer.isPrimary) {        this.firstTouch = null;        this.firstXY = null;        this.resetClickCount();      }    },    clickCount: 0,    resetId: null,    resetClickCount: function() {      var fn = function() {        this.clickCount = 0;        this.resetId = null;      }.bind(this);      this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT);    },    cancelResetClickCount: function() {      if (this.resetId) {        clearTimeout(this.resetId);      }    },    typeToButtons: function(type) {      var ret = 0;      if (type === 'touchstart' || type === 'touchmove') {        ret = 1;      }      return ret;    },    touchToPointer: function(inTouch) {      var cte = this.currentTouchEvent;      var e = dispatcher.cloneEvent(inTouch);
      // We reserve pointerId 1 for Mouse.
      // Touch identifiers can start at 0.
      // Add 2 to the touch identifier for compatibility.
      var id = e.pointerId = inTouch.identifier + 2;      e.target = captureInfo[id] || findTarget(e);      e.bubbles = true;      e.cancelable = true;      e.detail = this.clickCount;      e.button = 0;      e.buttons = this.typeToButtons(cte.type);      e.width = (inTouch.radiusX || inTouch.webkitRadiusX || 0) * 2;      e.height = (inTouch.radiusY || inTouch.webkitRadiusY || 0) * 2;      e.pressure = inTouch.force || inTouch.webkitForce || 0.5;      e.isPrimary = this.isPrimaryTouch(inTouch);      e.pointerType = this.POINTER_TYPE;
      // forward modifier keys
      e.altKey = cte.altKey;      e.ctrlKey = cte.ctrlKey;      e.metaKey = cte.metaKey;      e.shiftKey = cte.shiftKey;
      // forward touch preventDefaults
      var self = this;      e.preventDefault = function() {        self.scrolling = false;        self.firstXY = null;        cte.preventDefault();      };      return e;    },    processTouches: function(inEvent, inFunction) {      var tl = inEvent.changedTouches;      this.currentTouchEvent = inEvent;      for (var i = 0, t; i < tl.length; i++) {        t = tl[i];        inFunction.call(this, this.touchToPointer(t));      }    },
    // For single axis scrollers, determines whether the element should emit
    // pointer events or behave as a scroller
    shouldScroll: function(inEvent) {      if (this.firstXY) {        var ret;        var scrollAxis = inEvent.currentTarget._scrollType;        if (scrollAxis === 'none') {
          // this element is a touch-action: none, should never scroll
          ret = false;        } else if (scrollAxis === 'XY') {
          // this element should always scroll
          ret = true;        } else {          var t = inEvent.changedTouches[0];
          // check the intended scroll axis, and other axis
          var a = scrollAxis;          var oa = scrollAxis === 'Y' ? 'X' : 'Y';          var da = Math.abs(t['client' + a] - this.firstXY[a]);          var doa = Math.abs(t['client' + oa] - this.firstXY[oa]);
          // if delta in the scroll axis > delta other axis, scroll instead of
          // making events
          ret = da >= doa;        }        this.firstXY = null;        return ret;      }    },    findTouch: function(inTL, inId) {      for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {        if (t.identifier === inId) {          return true;        }      }    },
    // In some instances, a touchstart can happen without a touchend. This
    // leaves the pointermap in a broken state.
    // Therefore, on every touchstart, we remove the touches that did not fire a
    // touchend event.
    // To keep state globally consistent, we fire a
    // pointercancel for this "abandoned" touch
    vacuumTouches: function(inEvent) {      var tl = inEvent.touches;
      // pointermap.size should be < tl.length here, as the touchstart has not
      // been processed yet.
      if (pointermap$1.size >= tl.length) {        var d = [];        pointermap$1.forEach(function(value, key) {
          // Never remove pointerId == 1, which is mouse.
          // Touch identifiers are 2 smaller than their pointerId, which is the
          // index in pointermap.
          if (key !== 1 && !this.findTouch(tl, key - 2)) {            var p = value.out;            d.push(p);          }        }, this);        d.forEach(this.cancelOut, this);      }    },    touchstart: function(inEvent) {      this.vacuumTouches(inEvent);      this.setPrimaryTouch(inEvent.changedTouches[0]);      this.dedupSynthMouse(inEvent);      if (!this.scrolling) {        this.clickCount++;        this.processTouches(inEvent, this.overDown);      }    },    overDown: function(inPointer) {      pointermap$1.set(inPointer.pointerId, {        target: inPointer.target,        out: inPointer,        outTarget: inPointer.target      });      dispatcher.enterOver(inPointer);      dispatcher.down(inPointer);    },    touchmove: function(inEvent) {      if (!this.scrolling) {        if (this.shouldScroll(inEvent)) {          this.scrolling = true;          this.touchcancel(inEvent);        } else {          inEvent.preventDefault();          this.processTouches(inEvent, this.moveOverOut);        }      }    },    moveOverOut: function(inPointer) {      var event = inPointer;      var pointer = pointermap$1.get(event.pointerId);
      // a finger drifted off the screen, ignore it
      if (!pointer) {        return;      }      var outEvent = pointer.out;      var outTarget = pointer.outTarget;      dispatcher.move(event);      if (outEvent && outTarget !== event.target) {        outEvent.relatedTarget = event.target;        event.relatedTarget = outTarget;
        // recover from retargeting by shadow
        outEvent.target = outTarget;        if (event.target) {          dispatcher.leaveOut(outEvent);          dispatcher.enterOver(event);        } else {
          // clean up case when finger leaves the screen
          event.target = outTarget;          event.relatedTarget = null;          this.cancelOut(event);        }      }      pointer.out = event;      pointer.outTarget = event.target;    },    touchend: function(inEvent) {      this.dedupSynthMouse(inEvent);      this.processTouches(inEvent, this.upOut);    },    upOut: function(inPointer) {      if (!this.scrolling) {        dispatcher.up(inPointer);        dispatcher.leaveOut(inPointer);      }      this.cleanUpPointer(inPointer);    },    touchcancel: function(inEvent) {      this.processTouches(inEvent, this.cancelOut);    },    cancelOut: function(inPointer) {      dispatcher.cancel(inPointer);      dispatcher.leaveOut(inPointer);      this.cleanUpPointer(inPointer);    },    cleanUpPointer: function(inPointer) {      pointermap$1.delete(inPointer.pointerId);      this.removePrimaryPointer(inPointer);    },
    // prevent synth mouse events from creating pointer events
    dedupSynthMouse: function(inEvent) {      var lts = mouseEvents.lastTouches;      var t = inEvent.changedTouches[0];
      // only the primary finger will synth mouse events
      if (this.isPrimaryTouch(t)) {
        // remember x/y of last touch
        var lt = { x: t.clientX, y: t.clientY };        lts.push(lt);        var fn = (function(lts, lt) {          var i = lts.indexOf(lt);          if (i > -1) {            lts.splice(i, 1);          }        }).bind(null, lts, lt);        setTimeout(fn, DEDUP_TIMEOUT);      }    }  };
  INSTALLER = new Installer(touchEvents.elementAdded, touchEvents.elementRemoved,    touchEvents.elementChanged, touchEvents);
  var pointermap$2 = dispatcher.pointermap;  var HAS_BITMAP_TYPE = window.MSPointerEvent &&    typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number';  var msEvents = {    events: [      'MSPointerDown',      'MSPointerMove',      'MSPointerUp',      'MSPointerOut',      'MSPointerOver',      'MSPointerCancel',      'MSGotPointerCapture',      'MSLostPointerCapture'    ],    register: function(target) {      dispatcher.listen(target, this.events);    },    unregister: function(target) {      dispatcher.unlisten(target, this.events);    },    POINTER_TYPES: [      '',      'unavailable',      'touch',      'pen',      'mouse'    ],    prepareEvent: function(inEvent) {      var e = inEvent;      if (HAS_BITMAP_TYPE) {        e = dispatcher.cloneEvent(inEvent);        e.pointerType = this.POINTER_TYPES[inEvent.pointerType];      }      return e;    },    cleanup: function(id) {      pointermap$2.delete(id);    },    MSPointerDown: function(inEvent) {      pointermap$2.set(inEvent.pointerId, inEvent);      var e = this.prepareEvent(inEvent);      dispatcher.down(e);    },    MSPointerMove: function(inEvent) {      var e = this.prepareEvent(inEvent);      dispatcher.move(e);    },    MSPointerUp: function(inEvent) {      var e = this.prepareEvent(inEvent);      dispatcher.up(e);      this.cleanup(inEvent.pointerId);    },    MSPointerOut: function(inEvent) {      var e = this.prepareEvent(inEvent);      dispatcher.leaveOut(e);    },    MSPointerOver: function(inEvent) {      var e = this.prepareEvent(inEvent);      dispatcher.enterOver(e);    },    MSPointerCancel: function(inEvent) {      var e = this.prepareEvent(inEvent);      dispatcher.cancel(e);      this.cleanup(inEvent.pointerId);    },    MSLostPointerCapture: function(inEvent) {      var e = dispatcher.makeEvent('lostpointercapture', inEvent);      dispatcher.dispatchEvent(e);    },    MSGotPointerCapture: function(inEvent) {      var e = dispatcher.makeEvent('gotpointercapture', inEvent);      dispatcher.dispatchEvent(e);    }  };
  function applyPolyfill() {
    // only activate if this platform does not have pointer events
    if (!window.PointerEvent) {      window.PointerEvent = PointerEvent;
      if (window.navigator.msPointerEnabled) {        var tp = window.navigator.msMaxTouchPoints;        Object.defineProperty(window.navigator, 'maxTouchPoints', {          value: tp,          enumerable: true        });        dispatcher.registerSource('ms', msEvents);      } else {        Object.defineProperty(window.navigator, 'maxTouchPoints', {          value: 0,          enumerable: true        });        dispatcher.registerSource('mouse', mouseEvents);        if (window.ontouchstart !== undefined) {          dispatcher.registerSource('touch', touchEvents);        }      }
      dispatcher.register(document);    }  }
  var n = window.navigator;  var s;  var r;  var h;  function assertActive(id) {    if (!dispatcher.pointermap.has(id)) {      var error = new Error('InvalidPointerId');      error.name = 'InvalidPointerId';      throw error;    }  }  function assertConnected(elem) {    var parent = elem.parentNode;    while (parent && parent !== elem.ownerDocument) {      parent = parent.parentNode;    }    if (!parent) {      var error = new Error('InvalidStateError');      error.name = 'InvalidStateError';      throw error;    }  }  function inActiveButtonState(id) {    var p = dispatcher.pointermap.get(id);    return p.buttons !== 0;  }  if (n.msPointerEnabled) {    s = function(pointerId) {      assertActive(pointerId);      assertConnected(this);      if (inActiveButtonState(pointerId)) {        dispatcher.setCapture(pointerId, this, true);        this.msSetPointerCapture(pointerId);      }    };    r = function(pointerId) {      assertActive(pointerId);      dispatcher.releaseCapture(pointerId, true);      this.msReleasePointerCapture(pointerId);    };  } else {    s = function setPointerCapture(pointerId) {      assertActive(pointerId);      assertConnected(this);      if (inActiveButtonState(pointerId)) {        dispatcher.setCapture(pointerId, this);      }    };    r = function releasePointerCapture(pointerId) {      assertActive(pointerId);      dispatcher.releaseCapture(pointerId);    };  }  h = function hasPointerCapture(pointerId) {    return !!dispatcher.captureInfo[pointerId];  };
  function applyPolyfill$1() {    if (window.Element && !Element.prototype.setPointerCapture) {      Object.defineProperties(Element.prototype, {        'setPointerCapture': {          value: s        },        'releasePointerCapture': {          value: r        },        'hasPointerCapture': {          value: h        }      });    }  }
  applyAttributeStyles();  applyPolyfill();  applyPolyfill$1();
  var pointerevents = {    dispatcher: dispatcher,    Installer: Installer,    PointerEvent: PointerEvent,    PointerMap: PointerMap,    targetFinding: targeting  };
  return pointerevents;
}));
 |