219 строки
7.3 KiB
JavaScript
219 строки
7.3 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
// Shamelessly taken from the implementation in browser/base/content/browser.js
|
|
|
|
let gGestureSupport = {
|
|
_tabs: null,
|
|
_lastSelectedTab: null,
|
|
|
|
load: function GS_load() {
|
|
gGestureSupport.init(true);
|
|
|
|
let conversations = document.getElementById("conversations");
|
|
if (conversations) {
|
|
let tabs = conversations.tabContainer;
|
|
let selectHandler = function() {
|
|
gGestureSupport._lastSelectedTab = gGestureSupport._selectedTab;
|
|
gGestureSupport._selectedTab = this.selectedItem;
|
|
};
|
|
tabs.addEventListener("select", selectHandler);
|
|
gGestureSupport._selectedTab = tabs.selectedItem;
|
|
gGestureSupport._tabs = tabs;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Add or remove mouse gesture event listeners
|
|
*
|
|
* @param aAddListener
|
|
* True to add/init listeners and false to remove/uninit
|
|
*/
|
|
init: function GS_init(aAddListener) {
|
|
const gestureEvents = ["SwipeGesture",
|
|
"MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
|
|
"RotateGestureStart", "RotateGestureUpdate", "RotateGesture"];
|
|
|
|
let addRemove = aAddListener ? window.addEventListener :
|
|
window.removeEventListener;
|
|
|
|
for each (let event in gestureEvents)
|
|
addRemove("Moz" + event, this, true);
|
|
},
|
|
|
|
/**
|
|
* Dispatch events based on the type of mouse gesture event. For now, make
|
|
* sure to stop propagation of every gesture event so that web content cannot
|
|
* receive gesture events.
|
|
*
|
|
* @param aEvent
|
|
* The gesture event to handle
|
|
*/
|
|
handleEvent: function GS_handleEvent(aEvent) {
|
|
aEvent.stopPropagation();
|
|
|
|
// Create a preference object with some defaults
|
|
let def = function(aThreshold, aLatched)
|
|
({ threshold: aThreshold, latched: !!aLatched });
|
|
|
|
switch (aEvent.type) {
|
|
case "MozSwipeGesture":
|
|
this.onSwipe(aEvent);
|
|
break;
|
|
case "MozMagnifyGestureStart":
|
|
this._setupGesture(aEvent, "pinch", def(150, 1), "out", "in");
|
|
break;
|
|
case "MozRotateGestureStart":
|
|
this._setupGesture(aEvent, "twist", def(25, 0), "right", "left");
|
|
break;
|
|
case "MozMagnifyGestureUpdate":
|
|
case "MozRotateGestureUpdate":
|
|
this._doUpdate(aEvent);
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Called at the start of "pinch" and "twist" gestures to setup all of the
|
|
* information needed to process the gesture
|
|
*
|
|
* @param aEvent
|
|
* The continual motion start event to handle
|
|
* @param aGesture
|
|
* Name of the gesture to handle
|
|
* @param aPref
|
|
* Preference object with the names of preferences and defaults
|
|
* @param aInc
|
|
* Command to trigger for increasing motion (without gesture name)
|
|
* @param aDec
|
|
* Command to trigger for decreasing motion (without gesture name)
|
|
*/
|
|
_setupGesture: function GS__setupGesture(aEvent, aGesture, aPref, aInc, aDec) {
|
|
// Keep track of the total deltas and latching behavior
|
|
let offset = 0;
|
|
let latchDir = aEvent.delta > 0 ? 1 : -1;
|
|
let isLatched = false;
|
|
|
|
// Create the update function here to capture closure state
|
|
this._doUpdate = function GS__doUpdate(aEvent) {
|
|
// Update the offset with new event data
|
|
offset += aEvent.delta;
|
|
|
|
// Check if the cumulative deltas exceed the threshold
|
|
if (Math.abs(offset) > aPref["threshold"]) {
|
|
// Trigger the action if we don't care about latching; otherwise, make
|
|
// sure either we're not latched and going the same direction of the
|
|
// initial motion; or we're latched and going the opposite way
|
|
let sameDir = (latchDir ^ offset) >= 0;
|
|
if (!aPref["latched"] || (isLatched ^ sameDir)) {
|
|
this._doAction(aEvent, [aGesture, offset > 0 ? aInc : aDec]);
|
|
|
|
// We must be getting latched or leaving it, so just toggle
|
|
isLatched = !isLatched;
|
|
}
|
|
|
|
// Reset motion counter to prepare for more of the same gesture
|
|
offset = 0;
|
|
}
|
|
};
|
|
|
|
// The start event also contains deltas, so handle an update right away
|
|
this._doUpdate(aEvent);
|
|
},
|
|
|
|
/**
|
|
* Determine what action to do for the gesture based on which keys are
|
|
* pressed and which commands are set
|
|
*
|
|
* @param aEvent
|
|
* The original gesture event to convert into a fake click event
|
|
* @param aGesture
|
|
* Array of gesture name parts (to be joined by periods)
|
|
* @return Name of the command found for the event's keys and gesture. If no
|
|
* command is found, no value is returned (undefined).
|
|
*/
|
|
_doAction: function GS__doAction(aEvent, aGesture) {
|
|
let gesture = aGesture.join("-");
|
|
switch (gesture) {
|
|
case "pinch-out":
|
|
document.getElementById("cmd_textZoomEnlarge").doCommand();
|
|
break;
|
|
case "pinch-in":
|
|
document.getElementById("cmd_textZoomReduce").doCommand();
|
|
break;
|
|
case "twist-left":
|
|
if (this._tabs)
|
|
this._tabs.selectedIndex--;
|
|
break;
|
|
case "twist-right":
|
|
if (this._tabs)
|
|
this._tabs.selectedIndex++;
|
|
break;
|
|
case "swipe-down":
|
|
// This gesture isn't available if there's no browser.
|
|
if (!getBrowser())
|
|
break;
|
|
if (aEvent.originalTarget.ownerDocument == getBrowser().contentDocument)
|
|
getBrowser().contentWindow.focus();
|
|
if ("scrollToNextSection" in getBrowser())
|
|
getBrowser().scrollToNextSection();
|
|
else
|
|
goDoCommand("cmd_scrollBottom");
|
|
break;
|
|
case "swipe-up":
|
|
// This gesture isn't available if there's no browser.
|
|
if (!getBrowser())
|
|
break;
|
|
if (aEvent.originalTarget.ownerDocument == getBrowser().contentDocument)
|
|
getBrowser().contentWindow.focus();
|
|
if ("scrollToPreviousSection" in getBrowser())
|
|
getBrowser().scrollToPreviousSection();
|
|
else
|
|
goDoCommand("cmd_scrollTop");
|
|
break;
|
|
case "swipe-left":
|
|
case "swipe-right":
|
|
var newIndex = -1;
|
|
if (this._lastSelectedTab)
|
|
newIndex = this._tabs.getIndexOfItem(this._lastSelectedTab);
|
|
if (newIndex == -1)
|
|
newIndex =
|
|
gesture == "swipe-right" ? this._tabs.childNodes.length - 1 : 0;
|
|
this._tabs.selectedIndex = newIndex;
|
|
break;
|
|
default:
|
|
dump("mac gesture: "+ gesture +"\n");
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Convert continual motion events into an action if it exceeds a threshold
|
|
* in a given direction. This function will be set by _setupGesture to
|
|
* capture state that needs to be shared across multiple gesture updates.
|
|
*
|
|
* @param aEvent
|
|
* The continual motion update event to handle
|
|
*/
|
|
_doUpdate: function(aEvent) {},
|
|
|
|
/**
|
|
* Convert the swipe gesture into a browser action based on the direction
|
|
*
|
|
* @param aEvent
|
|
* The swipe event to handle
|
|
*/
|
|
onSwipe: function GS_onSwipe(aEvent) {
|
|
// Figure out which one (and only one) direction was triggered
|
|
for each (let dir in ["UP", "RIGHT", "DOWN", "LEFT"]) {
|
|
if (aEvent.direction == aEvent["DIRECTION_" + dir]) {
|
|
this._doAction(aEvent, ["swipe", dir.toLowerCase()]);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
this.addEventListener("load", gGestureSupport.load);
|