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.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
323
gaia/tv_apps/remote-control-client/js/panel_element.js
Executable file
323
gaia/tv_apps/remote-control-client/js/panel_element.js
Executable file
|
|
@ -0,0 +1,323 @@
|
|||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
var DEBUG = false;
|
||||
|
||||
var defaultOptions = {
|
||||
touchReportPeriod: 60, // milliseconds
|
||||
dblClickTimeThreshold: 250, // milliseconds
|
||||
clickTimeThreshold: 200, // milliseconds
|
||||
clickMoveThreshold: 10, // pixels
|
||||
swipeMoveThreshold: 25, // pixels
|
||||
touchingClass: null // CSS class
|
||||
};
|
||||
|
||||
function PanelElement(elem, options) {
|
||||
if (typeof options === 'function') {
|
||||
options = {
|
||||
handler: options
|
||||
};
|
||||
} else if (!options || typeof options !== 'object') {
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.options = {};
|
||||
for (var key in defaultOptions) {
|
||||
this.options[key] = (typeof options[key] === 'undefined') ?
|
||||
defaultOptions[key] : options[key];
|
||||
}
|
||||
|
||||
if (typeof options.handler === 'function') {
|
||||
this._handler = options.handler.bind(elem);
|
||||
} else {
|
||||
this._handler = function(type, detail) {};
|
||||
}
|
||||
|
||||
this._element = elem;
|
||||
|
||||
this._hasMouseDown = false;
|
||||
this._eventIdentifier = null;
|
||||
this._startX = 0;
|
||||
this._startY = 0;
|
||||
this._prevDx = null;
|
||||
this._prevDy = null;
|
||||
this._startTime = 0;
|
||||
this._waitForClickTimer = null;
|
||||
this._pendingClickTimer = null;
|
||||
this._reportTimer = null;
|
||||
this._pendingMoveEvent = null;
|
||||
this._touchIdentifier = null;
|
||||
this._panelX = 0;
|
||||
this._panelY = 0;
|
||||
this._panelWidth = 0;
|
||||
this._panelHeight = 0;
|
||||
|
||||
this._element.addEventListener('mousedown', this);
|
||||
window.addEventListener('mousemove', this);
|
||||
window.addEventListener('mouseup', this);
|
||||
|
||||
this._element.addEventListener('touchstart', this);
|
||||
this._element.addEventListener('touchmove', this);
|
||||
this._element.addEventListener('touchend', this);
|
||||
|
||||
window.addEventListener('resize', this);
|
||||
this.updatePanelInfo();
|
||||
}
|
||||
|
||||
PanelElement.prototype = {
|
||||
uninit: function() {
|
||||
window.removeEventListener('resize', this);
|
||||
|
||||
this._element.removeEventListener('touchend', this);
|
||||
this._element.removeEventListener('touchmove', this);
|
||||
this._element.removeEventListener('touchstart', this);
|
||||
|
||||
window.removeEventListener('mouseup', this);
|
||||
window.removeEventListener('mousemove', this);
|
||||
this._element.removeEventListener('mousedown', this);
|
||||
|
||||
this._element = null;
|
||||
this._handler = null;
|
||||
this.options = null;
|
||||
},
|
||||
|
||||
updatePanelInfo: function() {
|
||||
var rect = this._element.getBoundingClientRect();
|
||||
this._panelX = Math.round(rect.left);
|
||||
this._panelY = Math.round(rect.top);
|
||||
this._panelWidth = Math.round(rect.width);
|
||||
this._panelHeight = Math.round(rect.height);
|
||||
},
|
||||
|
||||
handleEvent: function(evt) {
|
||||
var touches, touch;
|
||||
|
||||
switch(evt.type) {
|
||||
case 'resize':
|
||||
this.updatePanelInfo();
|
||||
break;
|
||||
case 'mousedown':
|
||||
if (evt.button === 0) {
|
||||
this._hasMouseDown = true;
|
||||
this.onStart(evt.clientX, evt.clientY);
|
||||
evt.preventDefault();
|
||||
}
|
||||
break;
|
||||
case 'mousemove':
|
||||
if (this._hasMouseDown) {
|
||||
this.onMove(evt.clientX, evt.clientY);
|
||||
evt.preventDefault();
|
||||
}
|
||||
break;
|
||||
case 'mouseup':
|
||||
if (this._hasMouseDown && evt.button === 0) {
|
||||
this._hasMouseDown = false;
|
||||
this.onEnd(evt.clientX, evt.clientY);
|
||||
evt.preventDefault();
|
||||
}
|
||||
break;
|
||||
case 'touchstart':
|
||||
if (this._touchIdentifier !== null) {
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
touches = evt.changedTouches;
|
||||
if (touches.length == 1) {
|
||||
touch = touches[0];
|
||||
this._touchIdentifier = touch.identifier;
|
||||
this.onStart(touch.pageX - this._panelX,
|
||||
touch.pageY - this._panelY);
|
||||
evt.preventDefault();
|
||||
}
|
||||
break;
|
||||
case 'touchmove':
|
||||
case 'touchend':
|
||||
touches = evt.changedTouches;
|
||||
touch = null;
|
||||
for(var i = 0; i < touches.length; i++) {
|
||||
if (touches[i].identifier == this._touchIdentifier) {
|
||||
touch = touches[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (touch) {
|
||||
if (evt.type == 'touchend') {
|
||||
this._touchIdentifier = null;
|
||||
this.onEnd(touch.pageX - this._panelX,
|
||||
touch.pageY - this._panelY);
|
||||
} else {
|
||||
this.onMove(touch.pageX - this._panelX,
|
||||
touch.pageY - this._panelY);
|
||||
}
|
||||
}
|
||||
evt.preventDefault();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onStart: function(x, y) {
|
||||
document.activeElement.blur();
|
||||
|
||||
if (this.options.touchingClass) {
|
||||
this._element.classList.add(this.options.touchingClass);
|
||||
}
|
||||
|
||||
this._startX = x;
|
||||
this._startY = y;
|
||||
this._startTime = Date.now();
|
||||
|
||||
var handleTouchStart = function() {
|
||||
this._waitForClickTimer = null;
|
||||
this.handleTouch('touchstart', 0, 0);
|
||||
}.bind(this);
|
||||
|
||||
if (this.options.clickTimeThreshold) {
|
||||
this._waitForClickTimer = setTimeout(handleTouchStart,
|
||||
this.options.clickTimeThreshold);
|
||||
} else {
|
||||
handleTouchStart();
|
||||
}
|
||||
},
|
||||
|
||||
onMove: function(x, y) {
|
||||
var dx = x - this._startX;
|
||||
var dy = y - this._startY;
|
||||
|
||||
if (this._waitForClickTimer) {
|
||||
var clickMoveThreshold = this.options.clickMoveThreshold;
|
||||
if (Math.abs(dx) <= clickMoveThreshold &&
|
||||
Math.abs(dy) <= clickMoveThreshold) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(this._waitForClickTimer);
|
||||
this._waitForClickTimer = null;
|
||||
this.handleTouch('touchstart', 0, 0);
|
||||
}
|
||||
|
||||
this.handleTouch('touchmove', dx, dy);
|
||||
},
|
||||
|
||||
onEnd: function(x, y) {
|
||||
var dx = x - this._startX;
|
||||
var dy = y - this._startY;
|
||||
|
||||
if (this._waitForClickTimer) {
|
||||
clearTimeout(this._waitForClickTimer);
|
||||
this._waitForClickTimer = null;
|
||||
|
||||
if (this.options.dblClickTimeThreshold) {
|
||||
if (this._pendingClickTimer) {
|
||||
clearTimeout(this._pendingClickTimer);
|
||||
this._pendingClickTimer = null;
|
||||
this.handleTouch('dblclick');
|
||||
} else {
|
||||
this._pendingClickTimer = setTimeout(function() {
|
||||
this._pendingClickTimer = null;
|
||||
this.handleTouch('click');
|
||||
}.bind(this), this.options.dblClickTimeThreshold);
|
||||
}
|
||||
} else {
|
||||
this.handleTouch('click');
|
||||
}
|
||||
} else {
|
||||
var direction;
|
||||
var distance = Math.round(Math.sqrt(dx * dx + dy * dy));
|
||||
if (distance >= this.options.swipeMoveThreshold) {
|
||||
var angle = Math.atan2(dy, dx) * 180 / Math.PI;
|
||||
if (angle < 0) {
|
||||
angle += 360;
|
||||
}
|
||||
if (angle >= 315 || angle < 45) {
|
||||
direction = 'right';
|
||||
} else if (angle >= 45 && angle < 135) {
|
||||
direction = 'down';
|
||||
} else if (angle >= 135 && angle < 225) {
|
||||
direction = 'left';
|
||||
} else if (angle >= 225 && angle < 315) {
|
||||
direction = 'up';
|
||||
}
|
||||
}
|
||||
|
||||
this.handleTouch('touchend', dx, dy, direction);
|
||||
}
|
||||
|
||||
if (this.options.touchingClass) {
|
||||
this._element.classList.remove(this.options.touchingClass);
|
||||
}
|
||||
},
|
||||
|
||||
handleTouch: function(type, dx, dy, swipe) {
|
||||
if (DEBUG) {
|
||||
console.log('[PanelElement] handling ' + type);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'touchstart':
|
||||
this._prevDx = undefined;
|
||||
this._prevDy = undefined;
|
||||
// A simple unique identifier for server to distinguish touch events
|
||||
// between different clients.
|
||||
this._eventIdentifier =
|
||||
String(Date.now()) + String(Math.floor(Math.random() * 100));
|
||||
|
||||
this._handler(type, {
|
||||
width: this._panelWidth,
|
||||
height: this._panelHeight,
|
||||
identifier: this._eventIdentifier
|
||||
});
|
||||
|
||||
this._reportTimer = setInterval(function() {
|
||||
if (this._pendingMoveEvent) {
|
||||
this._handler(
|
||||
this._pendingMoveEvent.type,
|
||||
this._pendingMoveEvent.detail
|
||||
);
|
||||
this._pendingMoveEvent = null;
|
||||
}
|
||||
}.bind(this), this.options.touchReportPeriod);
|
||||
|
||||
break;
|
||||
case 'touchmove':
|
||||
if (dx === this._prevDx && dy === this._prevDy) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._prevDx = dx;
|
||||
this._prevDy = dy;
|
||||
|
||||
this._pendingMoveEvent = {
|
||||
type: type,
|
||||
detail: {
|
||||
dx: dx,
|
||||
dy: dy,
|
||||
identifier: this._eventIdentifier,
|
||||
duration: Date.now() - this._startTime
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
case 'touchend':
|
||||
this._pendingMoveEvent = null;
|
||||
clearInterval(this._reportTimer);
|
||||
this._reportTimer = null;
|
||||
|
||||
this._handler(type, {
|
||||
dx: dx,
|
||||
dy: dy,
|
||||
identifier: this._eventIdentifier,
|
||||
duration: Date.now() - this._startTime,
|
||||
swipe: swipe
|
||||
});
|
||||
|
||||
this._eventIdentifier = null;
|
||||
break;
|
||||
case 'click':
|
||||
case 'dblclick':
|
||||
this._handler(type, {});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.PanelElement = PanelElement;
|
||||
}(window));
|
||||
Loading…
Reference in a new issue