VulpesOS/gaia/apps/system/js/touch_forwarder.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

207 lines
5.3 KiB
JavaScript
Executable file

/*
*
* A simple helper encapsulating the APZC compliant logic
* to forward touch events.
*
* var tf = new TouchForwarder(element);
* tf.forward(evt);
*/
(function(exports) {
'use strict';
const kSignificant = 10;
var TouchForwarder = function TouchForwarder(destinationElement) {
this.destination = destinationElement || null;
this._resetState();
};
TouchForwarder.prototype.forward = function(e) {
var element = this.destination;
var touch;
// Should not forward to a frame that's not displayed
if (element.getAttribute('aria-hidden') == 'true') {
return;
}
switch (e.type) {
case 'touchstart':
sendTouchEvent(element, e);
touch = e.touches[0];
this._startX = touch.clientX;
this._startY = touch.clientY;
this._shouldTap = true;
break;
case 'touchmove':
sendTouchEvent(element, e);
touch = e.touches[0];
this._updateShouldTap(touch);
break;
case 'touchend':
sendTouchEvent(element, e);
touch = e.changedTouches[0];
this._updateShouldTap(touch);
if (this._shouldTap) {
sendTapMouseEvents(element, touch.clientX, touch.clientY);
}
this._resetState();
break;
case 'click':
sendTapMouseEvents(element, e.clientX, e.clientY);
break;
case 'touchcancel':
sendTouchEvent(element, e);
this._resetState();
break;
}
};
TouchForwarder.prototype._resetState = function() {
this._startX = null;
this._startY = null;
this._shouldTap = false;
};
TouchForwarder.prototype._updateShouldTap = function(touch) {
var deltaX = Math.abs(touch.clientX - this._startX);
var deltaY = Math.abs(touch.clientY - this._startY);
if (deltaX > kSignificant || deltaY > kSignificant) {
this._shouldTap = false;
}
};
function sendTouchEvent(element, e) {
if (!element) {
return;
}
// Only IFRAMEs support sendTouchEvent().
var args = createSendTouchEventArgumentList(e);
if (element.sendTouchEvent) {
element.sendTouchEvent.apply(element, args);
} else {
sendTouchEventToElement.apply(element, args);
}
}
function sendTapMouseEvents(el, x, y) {
if (!el) {
return;
}
// Only IFRAME supports sendMouseEvent().
if (el.sendMouseEvent) {
el.sendMouseEvent('mousemove', x, y, 0, 0, 0);
el.sendMouseEvent('mousedown', x, y, 0, 1, 0);
el.sendMouseEvent('mouseup', x, y, 0, 1, 0);
} else {
// For normal elements, we must synthesize our own 'click' event too.
var mouseProps = {
clientX: x,
clientY: y,
bubbles: true,
cancelable: true
};
el.dispatchEvent(new MouseEvent('mousemove', mouseProps));
el.dispatchEvent(new MouseEvent('mousedown', mouseProps));
el.dispatchEvent(new MouseEvent('mouseup', mouseProps));
el.dispatchEvent(new MouseEvent('click', mouseProps));
}
}
/**
* Like HTMLIFrameElement.sendTouchEvent(), but for all other elements.
*
* @this {HTMLElement}
*/
function sendTouchEventToElement(
type, identifiers, xs, ys, rxs, rys, rs, fs, count, modifiers) {
var touchEvent = document.createEvent('TouchEvent');
var touches = document.createTouchList(identifiers.map((ident, idx) => {
return document.createTouch(
/* view: */ window,
/* target: */ this,
/* identifier: */ ident,
/* pageX: */ xs[idx],
/* pageY: */ ys[idx],
/* screenX: */ xs[idx],
/* screenY: */ ys[idx],
/* clientX: */ xs[idx],
/* clientY: */ ys[idx],
/* radiusX: */ rxs[idx],
/* radiusY: */ rys[idx],
/* rotationAngle: */ rs[idx],
/* force: */ fs[idx]
);
}));
var emptyTouches = document.createTouchList([]);
touchEvent.initTouchEvent(
/* type: */ type,
/* canBubble: */ true,
/* cancelable: */ true,
/* view: */ window,
/* detail: */ 0,
/* ctrlKey: */ modifiers & 2,
/* altKey: */ modifiers & 1,
/* shiftKey: */ modifiers & 4,
/* metaKey: */ modifiers & 8,
/* touches: */
(type === 'touchend' || type === 'touchcancel') ? emptyTouches : touches,
/* targetTouches: */
(type === 'touchend' || type === 'touchcancel') ? emptyTouches : touches,
/* changedTouches: */ touches
);
/* jshint validthis:true */
this.dispatchEvent(touchEvent);
}
/**
* Prepare an argument list suitable for HTMLIFrameElement.sendTouchEvent().
*/
function createSendTouchEventArgumentList(e) {
var type = e.type;
var relevantTouches = (e.type === 'touchend') ?
e.changedTouches : e.touches;
var identifiers = [];
var xs = [];
var ys = [];
var rxs = [];
var rys = [];
var rs = [];
var fs = [];
var modifiers = 0;
for (var i = 0; i < relevantTouches.length; i++) {
var t = relevantTouches[i];
identifiers.push(t.identifier);
xs.push(t.pageX);
ys.push(t.pageY);
rxs.push(t.radiusX);
rys.push(t.radiusY);
rs.push(t.rotationAngle);
fs.push(t.force);
}
return [type, identifiers, xs, ys, rxs, rys, rs, fs, xs.length, modifiers];
}
exports.TouchForwarder = TouchForwarder;
}(window));