SelfHost/Test Environment/admin/spiderbasic/dojo/util/touch.js
2025-12-15 19:46:13 +01:00

141 lines
4.3 KiB
JavaScript

define([
'dojo/on',
'dojo/query'
], function (on, query) {
// This module exposes useful functions for working with touch devices.
var util = {
// Overridable defaults related to extension events defined below.
tapRadius: 10,
dbltapTime: 250,
selector: function (selector, eventType, children) {
// summary:
// Reimplementation of on.selector, taking an iOS quirk into account
return function (target, listener) {
var bubble = eventType.bubble;
if (bubble) {
// the event type doesn't naturally bubble, but has a bubbling form, use that
eventType = bubble;
}
else if (children !== false) {
// for normal bubbling events we default to allowing children of the selector
children = true;
}
return on(target, eventType, function (event) {
var eventTarget = event.target;
// iOS tends to report the text node an event was fired on, rather than
// the top-level element; this may end up causing errors in selector engines
if (eventTarget.nodeType === 3) {
eventTarget = eventTarget.parentNode;
}
// there is a selector, so make sure it matches
while (!query.matches(eventTarget, selector, target)) {
if (eventTarget === target || !children || !(eventTarget = eventTarget.parentNode)) {
return;
}
}
return listener.call(eventTarget, event);
});
};
},
countCurrentTouches: function (evt, node) {
// summary:
// Given a touch event and a DOM node, counts how many current touches
// presently lie within that node. Useful in cases where an accurate
// count is needed but tracking changedTouches won't suffice because
// other handlers stop events from bubbling high enough.
if (!('touches' in evt)) {
// Not a touch event (perhaps called from a mouse event on a
// platform supporting touch events)
return -1;
}
var i, numTouches, touch;
for (i = 0, numTouches = 0; (touch = evt.touches[i]); ++i) {
if (node.contains(touch.target)) {
++numTouches;
}
}
return numTouches;
}
};
function handleTapStart(target, listener, evt, prevent) {
// Common function for handling tap detection.
// The passed listener will only be fired when and if a touchend is fired
// which confirms the overall gesture resembled a tap.
if (evt.targetTouches.length > 1) {
return; // ignore multitouch
}
var start = evt.changedTouches[0],
startX = start.screenX,
startY = start.screenY;
prevent && evt.preventDefault();
var endListener = on(target, 'touchend', function (evt) {
var end = evt.changedTouches[0];
if (!evt.targetTouches.length) {
// only call listener if this really seems like a tap
if (Math.abs(end.screenX - startX) < util.tapRadius &&
Math.abs(end.screenY - startY) < util.tapRadius) {
prevent && evt.preventDefault();
listener.call(this, evt);
}
endListener.remove();
}
});
}
function tap(target, listener) {
// Function usable by dojo/on as a synthetic tap event.
return on(target, 'touchstart', function (evt) {
handleTapStart(target, listener, evt);
});
}
function dbltap(target, listener) {
// Function usable by dojo/on as a synthetic double-tap event.
var first, timeout;
return on(target, 'touchstart', function (evt) {
if (!first) {
// first potential tap: detect as usual, but with specific logic
handleTapStart(target, function (evt) {
first = evt.changedTouches[0];
timeout = setTimeout(function () {
first = timeout = null;
}, util.dbltapTime);
}, evt);
}
else {
handleTapStart(target, function (evt) {
// bail out if first was cleared between 2nd touchstart and touchend
if (!first) {
return;
}
var second = evt.changedTouches[0];
// only call listener if both taps occurred near the same place
if (Math.abs(second.screenX - first.screenX) < util.tapRadius &&
Math.abs(second.screenY - first.screenY) < util.tapRadius) {
timeout && clearTimeout(timeout);
first = timeout = null;
listener.call(this, evt);
}
}, evt, true);
}
});
}
util.tap = tap;
util.dbltap = dbltap;
return util;
});