273 lines
8.6 KiB
JavaScript
273 lines
8.6 KiB
JavaScript
/*!
|
|
The MIT License (MIT)
|
|
Copyright (c) 2014-current Andrea Giammarchi
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
CSS
|
|
Disables all pan/zoom behaviors and fire pointer events in JavaScript instead.
|
|
.disablePanZoom {
|
|
-ms-touch-action: none;
|
|
touch-action: none;
|
|
}
|
|
JS
|
|
Disables text selection
|
|
element.addEventListener("selectstart", function(e) { e.preventDefault(); }, false);
|
|
*/
|
|
|
|
(function (navigator, document, pointerEnabled) {
|
|
|
|
// highly experimental, should work on IE10 and IE11 only
|
|
// normal IE mouse events won't be affected
|
|
|
|
if (!(
|
|
(pointerEnabled = !!navigator.pointerEnabled) ||
|
|
navigator.msPointerEnabled
|
|
) ||
|
|
'ontouchend' in document
|
|
) return;
|
|
|
|
var
|
|
// shortcuts
|
|
ADD_EVENT_LISTENER = 'addEventListener',
|
|
REMOVE_EVENT_LISTENER = 'removeEventListener',
|
|
// used to force-simulate touch
|
|
SET_CURRENT_CAPTURE = pointerEnabled ?
|
|
'setPointerCapture' : 'msSetPointerCapture',
|
|
RELEASE_CURRENT_CAPTURE = pointerEnabled ?
|
|
'releasePointerCapture' : 'msReleasePointerCapture',
|
|
// shortcut for common replacements
|
|
ElementPrototype = Element.prototype,
|
|
defineProperties = Object.defineProperties,
|
|
defineProperty = Object.defineProperty,
|
|
// for types too
|
|
type = function (type) {
|
|
var lo = type.toLowerCase(),
|
|
ms = 'MS' + type;
|
|
handler[ms] = handler[lo];
|
|
return pointerEnabled ? lo: ms;
|
|
},
|
|
// these are calls to the passed event
|
|
commonMethod = function (name) {
|
|
return {
|
|
value: function () {
|
|
Event[name].call(this);
|
|
this._[name]();
|
|
}
|
|
};
|
|
},
|
|
// DOM Level 0 accessors
|
|
createAccessor = function (type) {
|
|
var ontype = '_on' + type;
|
|
return {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get: function () {
|
|
return this[ontype] || null;
|
|
},
|
|
set: function (callback) {
|
|
if (this[ontype]) {
|
|
this[REMOVE_EVENT_LISTENER](type, this[ontype]);
|
|
}
|
|
this[ontype] = callback;
|
|
if (callback) {
|
|
this[ADD_EVENT_LISTENER](type, callback);
|
|
}
|
|
}
|
|
};
|
|
},
|
|
// these are common DOM overrides
|
|
commonOverride = function (proto, name) {
|
|
var original = proto[name];
|
|
defineProperty(proto, name, {
|
|
configurable: true,
|
|
value: function (type, eventHandler, capture) {
|
|
if (type in types) {
|
|
original.call(
|
|
this,
|
|
types[type],
|
|
handleEvent,
|
|
capture
|
|
);
|
|
}
|
|
original.call(this, type, eventHandler, capture);
|
|
}
|
|
});
|
|
},
|
|
// these are delegated properties
|
|
commonProperty = function (name) {
|
|
return {
|
|
get: function () {
|
|
return this._[name];
|
|
}
|
|
};
|
|
},
|
|
// generates similar functions for similar cases
|
|
upOrCancel = function (type) {
|
|
return function (e) {
|
|
var pointerId = e.pointerId,
|
|
touch = touches[pointerId],
|
|
currentTarget = e.currentTarget;
|
|
delete touches[pointerId];
|
|
if (RELEASE_CURRENT_CAPTURE in currentTarget) {
|
|
currentTarget[RELEASE_CURRENT_CAPTURE](e.pointerId);
|
|
}
|
|
dispatchEvent(type, e, touch);
|
|
delete changedTouches[pointerId];
|
|
};
|
|
},
|
|
// shortcut for all events
|
|
dispatchEvent = function (type, e, touch) {
|
|
var c = document.createEvent('Event');
|
|
c.initEvent(type, true, true);
|
|
_.value = e;
|
|
TouchEventProperties.currentTarget.value = touch.currentTarget;
|
|
defineProperties(c, TouchEventProperties);
|
|
touch.currentTarget.dispatchEvent(c);
|
|
},
|
|
get = function (name, object) {
|
|
function returnID(id) {
|
|
return object[id];
|
|
}
|
|
return function get() {
|
|
_.value = Object.keys(object).map(returnID);
|
|
return defineProperty(this, name, _)[name];
|
|
};
|
|
},
|
|
// basically says if it's touch or not
|
|
humanReadablePointerType = function (e) {
|
|
switch(e.pointerType) {
|
|
case 'mouse':
|
|
case e.MSPOINTER_TYPE_MOUSE:
|
|
return 'mouse';
|
|
}
|
|
// pen is just fine as touch
|
|
return 'touch';
|
|
},
|
|
// recycle common descriptors too
|
|
_ = {value: null},
|
|
|
|
// the list of touches / changedTouches
|
|
touches = Object.create(null),
|
|
changedTouches = Object.create(null),
|
|
// TODO: targetTouches = Object.create(null),
|
|
|
|
Event = document.createEvent('Event'),
|
|
// all properties per each event
|
|
// defined at runtime .. not so fast
|
|
// but still OKish in terms of RAM and CPU
|
|
TouchEventProperties = {
|
|
_: _,
|
|
touches: {
|
|
configurable: true,
|
|
get: get('touches', touches)
|
|
},
|
|
changedTouches: {
|
|
configurable: true,
|
|
get: get('changedTouches', changedTouches)
|
|
},
|
|
currentTarget: {value:null},
|
|
// almost everything is mirrored
|
|
relatedTarget: commonProperty('relatedTarget'),
|
|
target: commonProperty('target'),
|
|
altKey: commonProperty('altKey'),
|
|
metaKey: commonProperty('metaKey'),
|
|
ctrlKey: commonProperty('ctrlKey'),
|
|
shiftKey: commonProperty('shiftKey'),
|
|
// including methods
|
|
preventDefault: commonMethod('preventDefault'),
|
|
stopPropagation: commonMethod('stopPropagation'),
|
|
stopImmediatePropagation: commonMethod('stopImmediatePropagation')
|
|
},
|
|
// all types translated
|
|
types = Object.create(null),
|
|
// boosted up eventListener
|
|
handleEvent = function (e) {
|
|
if (humanReadablePointerType(e) === 'touch') {
|
|
// invoke normalized methods
|
|
handler[e.type](e);
|
|
}
|
|
},
|
|
// the unique handler for all the things
|
|
handler = {
|
|
pointerdown: function (e) {
|
|
var touch = new Touch(e),
|
|
pointerId = e.pointerId,
|
|
currentTarget = e.currentTarget;
|
|
changedTouches[pointerId] = touches[pointerId] = touch;
|
|
if (SET_CURRENT_CAPTURE in currentTarget) {
|
|
currentTarget[SET_CURRENT_CAPTURE](e.pointerId);
|
|
}
|
|
dispatchEvent('touchstart', e, touch);
|
|
},
|
|
pointermove: function (e) {
|
|
var pointerId = e.pointerId,
|
|
touch = touches[pointerId];
|
|
touch._ = e;
|
|
dispatchEvent('touchmove', e, touch);
|
|
changedTouches[pointerId]._ = e;
|
|
},
|
|
pointerup: upOrCancel('touchend'),
|
|
pointercancel: upOrCancel('touchcancel')
|
|
},
|
|
accessors = {
|
|
ontouchstart: createAccessor('touchstart'),
|
|
ontouchmove: createAccessor('touchmove'),
|
|
ontouchend: createAccessor('touchend'),
|
|
ontouchcancel: createAccessor('touchcancel')
|
|
}
|
|
;
|
|
|
|
// facade for initial events info
|
|
function Touch(_) {
|
|
// the event needs to be refreshed
|
|
// each touchmove
|
|
this._ = _;
|
|
this.currentTarget = _.currentTarget;
|
|
}
|
|
|
|
// all common properties
|
|
defineProperties(
|
|
Touch.prototype,
|
|
{
|
|
identifier: commonProperty('pointerId'),
|
|
target: commonProperty('target'),
|
|
screenX: commonProperty('screenX'),
|
|
screenY: commonProperty('screenY'),
|
|
clientX: commonProperty('clientX'),
|
|
clientY: commonProperty('clientY'),
|
|
pageX: commonProperty('pageX'),
|
|
pageY: commonProperty('pageY')
|
|
}
|
|
);
|
|
|
|
types.touchstart = type('PointerDown');
|
|
types.touchmove = type('PointerMove');
|
|
types.touchend = type('PointerUp');
|
|
types.touchcancel = type('PointerCancel');
|
|
|
|
commonOverride(document, ADD_EVENT_LISTENER);
|
|
commonOverride(document, REMOVE_EVENT_LISTENER);
|
|
commonOverride(ElementPrototype, ADD_EVENT_LISTENER);
|
|
commonOverride(ElementPrototype, REMOVE_EVENT_LISTENER);
|
|
|
|
// make these available as DOM Level 0
|
|
defineProperties(document, accessors);
|
|
defineProperties(ElementPrototype, accessors);
|
|
|
|
}(navigator, document)); |