/*!
* 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