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