Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
207 lines
5.3 KiB
JavaScript
Executable file
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));
|