2009-02-23 13:35:25 -08:00
|
|
|
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; js2-strict-trailing-comma-warning: nil -*-
|
2008-11-21 21:12:25 -08:00
|
|
|
/*
|
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla Mobile Browser.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Mozilla Corporation.
|
2009-02-27 11:44:35 -08:00
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2008, 2009
|
2008-11-21 21:12:25 -08:00
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Stuart Parmenter <stuart@mozilla.com>
|
|
|
|
* Brad Lassey <blassey@mozilla.com>
|
|
|
|
* Mark Finkle <mfinkle@mozilla.com>
|
|
|
|
* Gavin Sharp <gavin.sharp@gmail.com>
|
2009-02-09 10:43:41 -08:00
|
|
|
* Ben Combee <combee@mozilla.com>
|
2008-11-21 21:12:25 -08:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
2009-02-23 13:35:25 -08:00
|
|
|
function getScrollboxFromElement(elem) {
|
|
|
|
// check element for scrollable interface, if not found check parent until we get to root
|
|
|
|
let scrollbox = null;
|
|
|
|
|
|
|
|
while (elem.parentNode) {
|
|
|
|
try {
|
|
|
|
if ("scrollBoxObject" in elem && elem.scrollBoxObject) {
|
|
|
|
scrollbox = elem.scrollBoxObject;
|
|
|
|
break;
|
|
|
|
}
|
2009-03-20 15:41:55 -07:00
|
|
|
else if (elem.boxObject) {
|
2009-02-23 13:35:25 -08:00
|
|
|
scrollbox = elem.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
// an exception is OK, we just don't want to propogate it
|
|
|
|
}
|
|
|
|
elem = elem.parentNode;
|
|
|
|
}
|
|
|
|
return scrollbox;
|
|
|
|
}
|
2008-11-21 21:12:25 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Everything that is registed in _modules gets called with each event that the
|
|
|
|
* InputHandler is registered to listen for.
|
|
|
|
*
|
|
|
|
* When one of the handlers decides it wants to handle the event, it should call
|
|
|
|
* grab() on its owner which will cause it to receive all of the events until it
|
|
|
|
* calls ungrab(). Calling grab will notify the other handlers via a
|
|
|
|
* cancelPending() notification. This tells them to stop what they're doing and
|
|
|
|
* give up hope for being the one to process the events.
|
|
|
|
*/
|
|
|
|
|
|
|
|
function InputHandler() {
|
2009-03-04 15:50:43 -08:00
|
|
|
/* used to stop everything if mouse leaves window on desktop */
|
|
|
|
window.addEventListener("mouseout", this, true);
|
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
/* these handle dragging of both chrome elements and content */
|
|
|
|
window.addEventListener("mousedown", this, true);
|
|
|
|
window.addEventListener("mouseup", this, true);
|
|
|
|
window.addEventListener("mousemove", this, true);
|
|
|
|
window.addEventListener("click", this, true);
|
|
|
|
|
2008-11-21 21:12:25 -08:00
|
|
|
let stack = document.getElementById("browser-container");
|
|
|
|
stack.addEventListener("DOMMouseScroll", this, true);
|
|
|
|
|
2009-03-13 06:47:10 -07:00
|
|
|
let browserCanvas = document.getElementById("browser-canvas");
|
|
|
|
browserCanvas.addEventListener("keydown", this, true);
|
|
|
|
browserCanvas.addEventListener("keyup", this, true);
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-04-17 14:08:53 -07:00
|
|
|
let useEarlyMouseMoves = gPrefService.getBoolPref("browser.ui.panning.fixup.mousemove");
|
|
|
|
|
2009-03-13 06:47:10 -07:00
|
|
|
this._modules.push(new ChromeInputModule(this, browserCanvas));
|
2009-04-17 14:08:53 -07:00
|
|
|
this._modules.push(new ContentPanningModule(this, browserCanvas, useEarlyMouseMoves));
|
2009-02-27 11:44:35 -08:00
|
|
|
this._modules.push(new ContentClickingModule(this));
|
2008-11-21 21:12:25 -08:00
|
|
|
this._modules.push(new ScrollwheelModule(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
InputHandler.prototype = {
|
|
|
|
_modules : [],
|
|
|
|
_grabbed : null,
|
2009-02-27 11:44:35 -08:00
|
|
|
_ignoreEvents: false,
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
grab: function grab(obj) {
|
2008-11-21 21:12:25 -08:00
|
|
|
this._grabbed = obj;
|
|
|
|
|
|
|
|
for each(mod in this._modules) {
|
|
|
|
if (mod != obj)
|
|
|
|
mod.cancelPending();
|
|
|
|
}
|
|
|
|
// only send events to this object
|
|
|
|
// call cancel on all modules
|
|
|
|
},
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
ungrab: function ungrab(obj) {
|
2008-11-21 21:12:25 -08:00
|
|
|
this._grabbed = null;
|
|
|
|
},
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
startListening: function startListening() {
|
|
|
|
this._ignoreEvents = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
stopListening: function stopListening() {
|
|
|
|
this._ignoreEvents = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
handleEvent: function handleEvent(aEvent) {
|
|
|
|
if (this._ignoreEvents)
|
|
|
|
return;
|
|
|
|
|
2009-03-04 15:50:43 -08:00
|
|
|
// relatedTarget should only be NULL if we move out of window
|
|
|
|
// if so, ungrab and reset everything. We don't always get
|
|
|
|
// mouseout events if the mouse movement causes other window
|
|
|
|
// activity, but this catches many of the cases
|
|
|
|
if (aEvent.type == "mouseout" && !aEvent.relatedTarget) {
|
|
|
|
this.grab(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-11-21 21:12:25 -08:00
|
|
|
if (this._grabbed) {
|
|
|
|
this._grabbed.handleEvent(aEvent);
|
2009-02-23 13:35:25 -08:00
|
|
|
}
|
|
|
|
else {
|
2009-02-09 10:43:41 -08:00
|
|
|
for each(mod in this._modules) {
|
2008-11-21 21:12:25 -08:00
|
|
|
mod.handleEvent(aEvent);
|
2009-02-09 10:43:41 -08:00
|
|
|
// if event got grabbed, don't pass to other handlers
|
|
|
|
if (this._grabbed)
|
|
|
|
break;
|
|
|
|
}
|
2008-11-21 21:12:25 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
/**
|
|
|
|
* Drag Data is used by both chrome and content input modules
|
|
|
|
*/
|
|
|
|
|
|
|
|
function DragData(owner, dragRadius, dragStartTimeoutLength) {
|
|
|
|
this._owner = owner;
|
|
|
|
this._dragRadius = dragRadius;
|
|
|
|
this._dragStartTimeoutLength = dragStartTimeoutLength;
|
|
|
|
this.dragStartTimeout = -1;
|
|
|
|
this.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
DragData.prototype = {
|
|
|
|
reset: function reset() {
|
|
|
|
this.dragging = false;
|
2009-04-02 13:45:27 -07:00
|
|
|
this.sX = null;
|
|
|
|
this.sY = null;
|
|
|
|
this.alreadyLocked = false;
|
2009-03-24 09:35:36 -07:00
|
|
|
this.lockedX = null;
|
|
|
|
this.lockedY = null;
|
|
|
|
|
|
|
|
this.clearDragStartTimeout();
|
|
|
|
},
|
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
setDragPosition: function setDragPosition(screenX, screenY) {
|
2009-03-24 09:35:36 -07:00
|
|
|
this.sX = screenX;
|
|
|
|
this.sY = screenY;
|
2009-04-02 13:45:27 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
setDragStart: function setDragStart(screenX, screenY) {
|
|
|
|
this.setDragPosition(screenX, screenY);
|
|
|
|
this.dragStartTimeout = window.setTimeout(
|
2009-03-24 09:35:36 -07:00
|
|
|
function(dragData, sX, sY) { dragData.clearDragStartTimeout(); dragData._owner._dragStart(sX, sY); },
|
|
|
|
this._dragStartTimeoutLength,
|
|
|
|
this, screenX, screenY);
|
|
|
|
},
|
|
|
|
|
|
|
|
clearDragStartTimeout: function clearDragStartTimeout() {
|
|
|
|
if (this.dragStartTimeout != -1)
|
2009-04-02 13:45:27 -07:00
|
|
|
window.clearTimeout(this.dragStartTimeout);
|
2009-03-24 09:35:36 -07:00
|
|
|
this.dragStartTimeout = -1;
|
|
|
|
},
|
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
lockMouseMove: function lockMouseMove(sX, sY) {
|
|
|
|
if (this.lockedX !== null)
|
|
|
|
sX = this.lockedX;
|
|
|
|
else if (this.lockedY !== null)
|
|
|
|
sY = this.lockedY;
|
|
|
|
return [sX, sY];
|
|
|
|
},
|
|
|
|
|
|
|
|
lockAxis: function lockAxis(sX, sY) {
|
|
|
|
if (this.alreadyLocked)
|
2009-04-13 11:24:54 -07:00
|
|
|
return this.lockMouseMove(sX, sY);
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
// look at difference from stored coord to lock movement, but only
|
|
|
|
// do it if initial movement is sufficient to detect intent
|
|
|
|
let absX = Math.abs(this.sX - sX);
|
|
|
|
let absY = Math.abs(this.sY - sY);
|
|
|
|
|
|
|
|
// lock panning if we move more than half of the drag radius and that direction
|
|
|
|
// contributed more than 2/3rd to the radial movement
|
|
|
|
if ((absX > (this._dragRadius / 2)) && ((absX * absX) > (2 * absY * absY))) {
|
|
|
|
this.lockedY = this.sY;
|
|
|
|
sY = this.sY;
|
|
|
|
}
|
|
|
|
else if ((absY > (this._dragRadius / 2)) && ((absY * absY) > (2 * absX * absX))) {
|
|
|
|
this.lockedX = this.sX;
|
|
|
|
sX = this.sX;
|
|
|
|
}
|
2009-04-02 13:45:27 -07:00
|
|
|
this.alreadyLocked = true;
|
2009-03-24 09:35:36 -07:00
|
|
|
|
|
|
|
return [sX, sY];
|
|
|
|
},
|
|
|
|
|
|
|
|
/* returns true if we go ahead and start a drag */
|
|
|
|
detectEarlyDrag: function detectEarlyDrag(sX, sY) {
|
|
|
|
let dx = this.sX - sX;
|
|
|
|
let dy = this.sY - sY;
|
|
|
|
|
|
|
|
if (!this.dragging && this.dragStartTimeout != -1) {
|
|
|
|
if ((dx*dx + dy*dy) > (this._dragRadius * this._dragRadius)) {
|
|
|
|
this.clearDragStartTimeout();
|
|
|
|
this._owner._dragStart(sX, sY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2008-11-21 21:12:25 -08:00
|
|
|
|
|
|
|
/**
|
2009-02-27 11:44:35 -08:00
|
|
|
* Panning code for chrome elements
|
2008-11-21 21:12:25 -08:00
|
|
|
*/
|
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
function ChromeInputModule(owner, browserCanvas) {
|
2008-11-21 21:12:25 -08:00
|
|
|
this._owner = owner;
|
2009-03-13 06:47:10 -07:00
|
|
|
this._browserCanvas = browserCanvas;
|
2009-03-24 09:35:36 -07:00
|
|
|
this._dragData = new DragData(this, 20, 200);
|
2008-11-21 21:12:25 -08:00
|
|
|
}
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
ChromeInputModule.prototype = {
|
2008-11-21 21:12:25 -08:00
|
|
|
_owner: null,
|
2009-03-02 13:45:36 -08:00
|
|
|
_ignoreNextClick: false,
|
2009-03-24 09:35:36 -07:00
|
|
|
_dragData: null,
|
|
|
|
_clickEvents : [],
|
|
|
|
_targetScrollbox: null,
|
2009-02-27 11:44:35 -08:00
|
|
|
|
|
|
|
handleEvent: function handleEvent(aEvent) {
|
|
|
|
switch (aEvent.type) {
|
|
|
|
case "mousedown":
|
|
|
|
this._onMouseDown(aEvent);
|
|
|
|
break;
|
|
|
|
case "mousemove":
|
|
|
|
this._onMouseMove(aEvent);
|
|
|
|
break;
|
|
|
|
case "mouseup":
|
|
|
|
this._onMouseUp(aEvent);
|
|
|
|
break;
|
2009-03-02 13:45:36 -08:00
|
|
|
case "click":
|
|
|
|
if (this._ignoreNextClick) {
|
|
|
|
aEvent.stopPropagation();
|
|
|
|
aEvent.preventDefault();
|
|
|
|
this._ignoreNextClick = false;
|
|
|
|
}
|
|
|
|
break;
|
2009-02-27 11:44:35 -08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/* If someone else grabs events ahead of us, cancel any pending
|
|
|
|
* timeouts we may have.
|
|
|
|
*/
|
|
|
|
cancelPending: function cancelPending() {
|
|
|
|
this._dragData.reset();
|
2009-03-24 09:35:36 -07:00
|
|
|
this._targetScrollbox = null;
|
2009-02-27 11:44:35 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
_dragStart: function _dragStart(sX, sY) {
|
|
|
|
let dragData = this._dragData;
|
|
|
|
dragData.dragging = true;
|
2009-03-24 09:35:36 -07:00
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
[sX, sY] = dragData.lockAxis(sX, sY);
|
2009-02-27 11:44:35 -08:00
|
|
|
|
|
|
|
// grab all events until we stop the drag
|
|
|
|
ws.dragStart(sX, sY);
|
|
|
|
|
|
|
|
// prevent clicks from being sent once we start drag
|
2009-03-24 09:35:36 -07:00
|
|
|
this._clickEvents = [];
|
2009-02-27 11:44:35 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
_dragStop: function _dragStop(sX, sY) {
|
|
|
|
let dragData = this._dragData;
|
2009-03-24 09:35:36 -07:00
|
|
|
[sX, sY] = dragData.lockMouseMove(sX, sY);
|
|
|
|
if (this._targetScrollbox)
|
|
|
|
this._targetScrollbox.scrollBy(dragData.sX - sX, dragData.sY - sY);
|
|
|
|
this._targetScrollbox = null;
|
2009-02-27 11:44:35 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
_dragMove: function _dragMove(sX, sY) {
|
|
|
|
let dragData = this._dragData;
|
2009-03-24 09:35:36 -07:00
|
|
|
[sX, sY] = dragData.lockMouseMove(sX, sY);
|
|
|
|
if (this._targetScrollbox)
|
|
|
|
this._targetScrollbox.scrollBy(dragData.sX - sX, dragData.sY - sY);
|
2009-04-09 13:09:27 -07:00
|
|
|
dragData.setDragPosition(sX, sY);
|
2009-02-27 11:44:35 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
_onMouseDown: function _onMouseDown(aEvent) {
|
|
|
|
// exit early for events in the content area
|
2009-03-13 06:47:10 -07:00
|
|
|
if (aEvent.target === this._browserCanvas) {
|
2009-02-27 11:44:35 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let dragData = this._dragData;
|
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
this._targetScrollbox = getScrollboxFromElement(aEvent.target);
|
|
|
|
if (!this._targetScrollbox)
|
2009-02-27 11:44:35 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// absorb the event for the scrollable XUL element and make all future events grabbed too
|
|
|
|
this._owner.grab(this);
|
|
|
|
|
|
|
|
aEvent.stopPropagation();
|
|
|
|
aEvent.preventDefault();
|
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
dragData.setDragStart(aEvent.screenX, aEvent.screenY);
|
2009-04-02 13:45:27 -07:00
|
|
|
this._onMouseMove(aEvent); // treat this as a mouse move too
|
2009-02-27 11:44:35 -08:00
|
|
|
|
|
|
|
// store away the event for possible sending later if a drag doesn't happen
|
|
|
|
let clickEvent = document.createEvent("MouseEvent");
|
|
|
|
clickEvent.initMouseEvent(aEvent.type, aEvent.bubbles, aEvent.cancelable,
|
|
|
|
aEvent.view, aEvent.detail,
|
|
|
|
aEvent.screenX, aEvent.screenY, aEvent.clientX, aEvent.clientY,
|
|
|
|
aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKeyArg, aEvent.metaKeyArg,
|
|
|
|
aEvent.button, aEvent.relatedTarget);
|
2009-03-24 09:35:36 -07:00
|
|
|
this._clickEvents.push({event: clickEvent, target: aEvent.target, time: Date.now()});
|
2009-02-27 11:44:35 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
_onMouseUp: function _onMouseUp(aEvent) {
|
|
|
|
// only process if original mousedown was on a scrollable element
|
|
|
|
let dragData = this._dragData;
|
2009-03-24 09:35:36 -07:00
|
|
|
if (!this._targetScrollbox)
|
2009-02-27 11:44:35 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// keep an eye out for mouseups that didn't start with a mousedown
|
2009-03-24 09:35:36 -07:00
|
|
|
if (!(this._clickEvents.length % 2)) {
|
|
|
|
this._clickEvents = [];
|
2009-02-27 11:44:35 -08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
let clickEvent = document.createEvent("MouseEvent");
|
|
|
|
clickEvent.initMouseEvent(aEvent.type, aEvent.bubbles, aEvent.cancelable,
|
|
|
|
aEvent.view, aEvent.detail,
|
|
|
|
aEvent.screenX, aEvent.screenY, aEvent.clientX, aEvent.clientY,
|
|
|
|
aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKeyArg, aEvent.metaKeyArg,
|
|
|
|
aEvent.button, aEvent.relatedTarget);
|
2009-03-24 09:35:36 -07:00
|
|
|
this._clickEvents.push({event: clickEvent, target: aEvent.target, time: Date.now()});
|
2009-02-27 11:44:35 -08:00
|
|
|
|
2009-03-03 12:50:15 -08:00
|
|
|
this._ignoreNextClick = true;
|
|
|
|
this._sendSingleClick();
|
2009-02-27 11:44:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
aEvent.stopPropagation();
|
|
|
|
aEvent.preventDefault();
|
|
|
|
|
|
|
|
if (dragData.dragging)
|
|
|
|
this._dragStop(aEvent.screenX, aEvent.screenY);
|
|
|
|
|
|
|
|
dragData.reset(); // be sure to reset the timer
|
2009-03-24 09:35:36 -07:00
|
|
|
this._targetScrollbox = null;
|
2009-02-27 11:44:35 -08:00
|
|
|
this._owner.ungrab(this);
|
|
|
|
},
|
|
|
|
|
|
|
|
_onMouseMove: function _onMouseMove(aEvent) {
|
|
|
|
let dragData = this._dragData;
|
|
|
|
|
|
|
|
// only process if original mousedown was on a scrollable element
|
2009-03-24 09:35:36 -07:00
|
|
|
if (!this._targetScrollbox)
|
2009-02-27 11:44:35 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
aEvent.stopPropagation();
|
|
|
|
aEvent.preventDefault();
|
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
let sX = aEvent.screenX;
|
|
|
|
let sY = aEvent.screenY;
|
2009-02-27 11:44:35 -08:00
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
if (!dragData.sX)
|
|
|
|
dragData.setDragPosition(aEvent.screenX, aEvent.screenY);
|
|
|
|
|
2009-04-09 13:09:27 -07:00
|
|
|
[sX, sY] = dragData.lockMouseMove(aEvent.screenX, aEvent.screenY);
|
2009-04-02 13:45:27 -07:00
|
|
|
|
|
|
|
dragData.detectEarlyDrag(sX, sY);
|
2009-02-27 11:44:35 -08:00
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
if (!dragData.dragging)
|
|
|
|
return;
|
2009-02-27 11:44:35 -08:00
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
[sX, sY] = dragData.lockMouseMove(sX, sY);
|
|
|
|
this._dragMove(sX, sY);
|
2009-02-27 11:44:35 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// resend original events with our handler out of the loop
|
|
|
|
_sendSingleClick: function _sendSingleClick() {
|
|
|
|
this._owner.grab(this);
|
|
|
|
this._owner.stopListening();
|
|
|
|
|
|
|
|
// send original mouseDown/mouseUps again
|
2009-03-24 09:35:36 -07:00
|
|
|
this._redispatchChromeMouseEvent(this._clickEvents[0].event);
|
|
|
|
this._redispatchChromeMouseEvent(this._clickEvents[1].event);
|
2009-02-27 11:44:35 -08:00
|
|
|
|
|
|
|
this._owner.startListening();
|
|
|
|
this._owner.ungrab(this);
|
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
this._clickEvents = [];
|
2009-02-27 11:44:35 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
_redispatchChromeMouseEvent: function _redispatchChromeMouseEvent(aEvent) {
|
|
|
|
if (!(aEvent instanceof MouseEvent)) {
|
|
|
|
Cu.reportError("_redispatchChromeMouseEvent called with a non-mouse event");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Redispatch the mouse event, ignoring the root scroll frame
|
2009-04-17 14:10:06 -07:00
|
|
|
let cwu = Browser.windowUtils;
|
2009-02-27 11:44:35 -08:00
|
|
|
cwu.sendMouseEvent(aEvent.type, aEvent.clientX, aEvent.clientY,
|
|
|
|
aEvent.button, aEvent.detail, 0, true);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Kinetic panning code for content
|
|
|
|
*/
|
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
function KineticData(owner) {
|
|
|
|
this._owner = owner;
|
2009-04-02 04:11:51 -07:00
|
|
|
this._kineticTimer = null;
|
2009-03-24 09:35:36 -07:00
|
|
|
this.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
KineticData.prototype = {
|
2009-04-02 04:11:51 -07:00
|
|
|
/* const */ _updateInterval : 33, // this would put us at roughly 30fps
|
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
reset: function reset() {
|
2009-04-02 04:11:51 -07:00
|
|
|
if (this._kineticTimer != null) {
|
|
|
|
this._kineticTimer.cancel();
|
|
|
|
this._kineticTimer = null;
|
2009-03-24 09:35:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
this.momentumBuffer = [];
|
2009-04-02 04:11:51 -07:00
|
|
|
this._speedX = 0;
|
|
|
|
this._speedY = 0;
|
2009-04-02 13:45:27 -07:00
|
|
|
},
|
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
_startKineticTimer: function _startKineticTimer() {
|
|
|
|
let callback = {
|
|
|
|
_self: this,
|
|
|
|
notify: function(timer) {
|
|
|
|
let self = this._self;
|
|
|
|
|
|
|
|
const decelerationRate = 0.15;
|
|
|
|
|
|
|
|
// dump(" speeds: " + self._speedX + " " + self._speedY + "\n");
|
|
|
|
|
|
|
|
if (self._speedX == 0 && self._speedY == 0) {
|
|
|
|
self.endKinetic();
|
2009-04-02 04:15:50 -07:00
|
|
|
return;
|
2009-04-02 04:11:51 -07:00
|
|
|
} else {
|
|
|
|
let dx = Math.round(self._speedX * self._updateInterval);
|
|
|
|
let dy = Math.round(self._speedY * self._updateInterval);
|
|
|
|
//dump("dx, dy: " + dx + " " + dy + "\n");
|
|
|
|
|
|
|
|
let panned = self._owner._dragBy(dx, dy);
|
|
|
|
if (!panned) {
|
|
|
|
self.endKinetic();
|
2009-04-02 04:15:50 -07:00
|
|
|
return;
|
|
|
|
}
|
2009-04-02 04:11:51 -07:00
|
|
|
}
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
if (self._speedX < 0) {
|
|
|
|
self._speedX = Math.min(self._speedX + decelerationRate, 0);
|
|
|
|
} else if (self._speedX > 0) {
|
|
|
|
self._speedX = Math.max(self._speedX - decelerationRate, 0);
|
|
|
|
}
|
|
|
|
if (self._speedY < 0) {
|
|
|
|
self._speedY = Math.min(self._speedY + decelerationRate, 0);
|
|
|
|
} else if (self._speedY > 0) {
|
|
|
|
self._speedY = Math.max(self._speedY - decelerationRate, 0);
|
|
|
|
}
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
if (self._speedX == 0 && self._speedY == 0)
|
|
|
|
self.endKinetic();
|
|
|
|
}
|
2009-04-09 13:09:27 -07:00
|
|
|
};
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
this._kineticTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
2009-04-09 13:09:27 -07:00
|
|
|
//initialize our timer with updateInterval
|
2009-04-02 04:11:51 -07:00
|
|
|
this._kineticTimer.initWithCallback(callback,
|
|
|
|
this._updateInterval,
|
|
|
|
this._kineticTimer.TYPE_REPEATING_SLACK);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
startKinetic: function startKinetic(sX, sY) {
|
|
|
|
let mb = this.momentumBuffer;
|
|
|
|
let mblen = this.momentumBuffer.length;
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
// If we don't have at least 2 events we can't really do kinetic panning
|
|
|
|
if (mblen < 2)
|
2009-04-02 13:45:27 -07:00
|
|
|
return false;
|
|
|
|
|
2009-04-14 22:51:10 -07:00
|
|
|
let tempX = 0;
|
|
|
|
let tempY = 0;
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
// build arrays of each movement's speed in pixels/ms
|
|
|
|
let prev = mb[0];
|
|
|
|
for (let i = 1; i < mblen; i++) {
|
|
|
|
let me = mb[i];
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-14 22:51:10 -07:00
|
|
|
tempX += (me.sx - prev.sx) / (me.t - prev.t);
|
|
|
|
tempY += (me.sy - prev.sy) / (me.t - prev.t);
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
prev = me;
|
|
|
|
}
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-14 22:51:10 -07:00
|
|
|
// average the speeds out (this could probably be a bit smarter)
|
|
|
|
this.speedX = tempX / mblen;
|
|
|
|
this.speedY = tempY / mblen;
|
2009-04-02 04:11:51 -07:00
|
|
|
|
|
|
|
// fire off our kinetic timer which will do all the work
|
|
|
|
this._startKineticTimer();
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
return true;
|
2009-04-02 13:45:27 -07:00
|
|
|
},
|
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
endKinetic: function endKinetic() {
|
2009-04-02 13:45:27 -07:00
|
|
|
ws.dragStop();
|
|
|
|
this.reset();
|
|
|
|
|
|
|
|
// Make sure that sidebars don't stay partially open
|
|
|
|
// XXX this should live somewhere else
|
|
|
|
let [leftVis,] = ws.getWidgetVisibility("tabs-container", false);
|
|
|
|
let [rightVis,] = ws.getWidgetVisibility("browser-controls", false);
|
|
|
|
if (leftVis != 0 && leftVis != 1) {
|
|
|
|
let w = document.getElementById("tabs-container").getBoundingClientRect().width;
|
|
|
|
if (leftVis >= 0.6666)
|
|
|
|
ws.panBy(-w, 0, true);
|
|
|
|
else
|
|
|
|
ws.panBy(leftVis * w, 0, true);
|
|
|
|
}
|
|
|
|
else if (rightVis != 0 && rightVis != 1) {
|
|
|
|
let w = document.getElementById("browser-controls").getBoundingClientRect().width;
|
|
|
|
if (rightVis >= 0.6666)
|
|
|
|
ws.panBy(w, 0, true);
|
|
|
|
else
|
|
|
|
ws.panBy(-rightVis * w, 0, true);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
addData: function addData(sx, sy) {
|
|
|
|
let mbLength = this.momentumBuffer.length;
|
|
|
|
// avoid adding duplicates which would otherwise slow down the speed
|
|
|
|
if (mbLength > 0) {
|
|
|
|
let mbLast = this.momentumBuffer[mbLength - 1];
|
|
|
|
if (mbLast.sx == sx && mbLast.sy == sy)
|
|
|
|
return;
|
|
|
|
}
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
this.momentumBuffer.push({'t': Date.now(), 'sx' : sx, 'sy' : sy});
|
2009-03-24 09:35:36 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-04-17 14:08:53 -07:00
|
|
|
function ContentPanningModule(owner, browserCanvas, useEarlyMouseMoves) {
|
2009-02-27 11:44:35 -08:00
|
|
|
this._owner = owner;
|
2009-03-13 06:47:10 -07:00
|
|
|
this._browserCanvas = browserCanvas;
|
2009-04-02 04:11:51 -07:00
|
|
|
this._dragData = new DragData(this, 10, 200);
|
2009-04-02 13:45:27 -07:00
|
|
|
this._kineticData = new KineticData(this);
|
2009-04-17 14:08:53 -07:00
|
|
|
this._useEarlyMouseMoves = useEarlyMouseMoves;
|
2009-02-27 11:44:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
ContentPanningModule.prototype = {
|
|
|
|
_owner: null,
|
2009-03-24 09:35:36 -07:00
|
|
|
_dragData: null,
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
_kineticData: null,
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
handleEvent: function handleEvent(aEvent) {
|
|
|
|
// exit early for events outside displayed content area
|
2009-03-13 06:47:10 -07:00
|
|
|
if (aEvent.target !== this._browserCanvas)
|
2009-02-27 11:44:35 -08:00
|
|
|
return;
|
|
|
|
|
2008-11-21 21:12:25 -08:00
|
|
|
switch (aEvent.type) {
|
|
|
|
case "mousedown":
|
2009-02-23 13:35:25 -08:00
|
|
|
this._onMouseDown(aEvent);
|
2008-11-21 21:12:25 -08:00
|
|
|
break;
|
|
|
|
case "mousemove":
|
2009-02-23 13:35:25 -08:00
|
|
|
this._onMouseMove(aEvent);
|
|
|
|
break;
|
2008-11-21 21:12:25 -08:00
|
|
|
case "mouseup":
|
2009-02-23 13:35:25 -08:00
|
|
|
this._onMouseUp(aEvent);
|
|
|
|
break;
|
2008-11-21 21:12:25 -08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
|
|
|
|
detectEarlyDrag: function detectEarlyDrag() {
|
|
|
|
let dragData = this._dragData;
|
|
|
|
|
|
|
|
if (dragData.dragging)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let mb = this._kineticData.momentumBuffer;
|
|
|
|
if (mb.length < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let mbFirst = mb[0];
|
|
|
|
let mbLast = mb[mb.length - 1];
|
|
|
|
|
|
|
|
let dx = mbFirst.sx - mbLast.sx;
|
|
|
|
let dy = mbFirst.sy - mbLast.sy;
|
|
|
|
|
|
|
|
if (dragData.dragStartTimeout != -1) {
|
|
|
|
if ((dx*dx + dy*dy) > (dragData._dragRadius * dragData._dragRadius)) {
|
|
|
|
dragData.clearDragStartTimeout();
|
|
|
|
dragData._owner._dragStart(mbFirst.sx, mbFirst.sy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2008-11-21 21:12:25 -08:00
|
|
|
/* If someone else grabs events ahead of us, cancel any pending
|
|
|
|
* timeouts we may have.
|
|
|
|
*/
|
2009-02-27 11:44:35 -08:00
|
|
|
cancelPending: function cancelPending() {
|
2009-03-19 14:32:50 -07:00
|
|
|
let dragData = this._dragData;
|
|
|
|
// stop scrolling, pass last coordinate we used
|
2009-04-02 13:45:27 -07:00
|
|
|
this._kineticData.endKinetic(dragData.sX, dragData.sY);
|
|
|
|
this._owner.ungrab(this);
|
2009-03-19 14:32:50 -07:00
|
|
|
dragData.reset();
|
2008-11-21 21:12:25 -08:00
|
|
|
},
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
_dragStart: function _dragStart(sX, sY) {
|
2009-02-23 13:35:25 -08:00
|
|
|
let dragData = this._dragData;
|
|
|
|
dragData.dragging = true;
|
2009-03-24 09:35:36 -07:00
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
[sX, sY] = dragData.lockAxis(sX, sY);
|
2008-11-21 21:12:25 -08:00
|
|
|
|
|
|
|
// grab all events until we stop the drag
|
|
|
|
this._owner.grab(this);
|
2009-02-27 11:44:35 -08:00
|
|
|
ws.dragStart(sX, sY);
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-03-05 15:59:06 -08:00
|
|
|
Browser.canvasBrowser.startPanning();
|
2008-11-21 21:12:25 -08:00
|
|
|
},
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
_dragStop: function _dragStop(sX, sY) {
|
2009-02-23 13:35:25 -08:00
|
|
|
let dragData = this._dragData;
|
|
|
|
|
|
|
|
this._owner.ungrab(this);
|
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
[sX, sY] = dragData.lockMouseMove(sX, sY);
|
|
|
|
|
2009-04-17 14:02:24 -07:00
|
|
|
// start kinetic scrolling here for canvas only
|
|
|
|
if (!this._kineticData.startKinetic(sX, sY))
|
|
|
|
this._kineticData.endKinetic(sX, sY);
|
|
|
|
dragData.reset();
|
2008-11-21 21:12:25 -08:00
|
|
|
},
|
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
_dragBy: function _dragMove(dx, dy) {
|
|
|
|
let panned = ws.dragBy(dx, dy);
|
|
|
|
return panned;
|
|
|
|
},
|
2009-04-02 13:45:27 -07:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
_dragMove: function _dragMove(sX, sY) {
|
2009-02-23 13:35:25 -08:00
|
|
|
let dragData = this._dragData;
|
2009-03-24 09:35:36 -07:00
|
|
|
[sX, sY] = dragData.lockMouseMove(sX, sY);
|
2009-04-02 13:45:27 -07:00
|
|
|
let panned = ws.dragMove(sX, sY);
|
|
|
|
dragData.setDragPosition(sX, sY);
|
|
|
|
return panned;
|
2008-11-21 21:12:25 -08:00
|
|
|
},
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
_onMouseDown: function _onMouseDown(aEvent) {
|
2008-11-21 21:12:25 -08:00
|
|
|
// if we're in the process of kineticly scrolling, stop and start over
|
2009-04-02 04:11:51 -07:00
|
|
|
if (this._kineticData._kineticTimer != null) {
|
2009-04-02 13:45:27 -07:00
|
|
|
this._kineticData.endKinetic(aEvent.screenX, aEvent.screenY);
|
|
|
|
this._owner.ungrab(this);
|
|
|
|
this._dragData.reset();
|
|
|
|
}
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-03-24 09:35:36 -07:00
|
|
|
this._dragData.setDragStart(aEvent.screenX, aEvent.screenY);
|
2009-04-02 13:45:27 -07:00
|
|
|
this._onMouseMove(aEvent); // treat this as a mouse move too
|
2008-11-21 21:12:25 -08:00
|
|
|
},
|
2009-02-23 13:35:25 -08:00
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
_onMouseUp: function _onMouseUp(aEvent) {
|
2008-11-21 21:12:25 -08:00
|
|
|
let dragData = this._dragData;
|
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
if (dragData.dragging) {
|
|
|
|
this._onMouseMove(aEvent); // treat this as a mouse move, incase our x/y are different
|
2008-11-21 21:12:25 -08:00
|
|
|
this._dragStop(aEvent.screenX, aEvent.screenY);
|
2009-04-02 13:45:27 -07:00
|
|
|
}
|
2009-02-23 13:35:25 -08:00
|
|
|
|
|
|
|
dragData.reset(); // be sure to reset the timer
|
2008-11-21 21:12:25 -08:00
|
|
|
},
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
_onMouseMove: function _onMouseMove(aEvent) {
|
2008-11-21 21:12:25 -08:00
|
|
|
// don't do anything if we're in the process of kineticly scrolling
|
2009-04-02 04:11:51 -07:00
|
|
|
if (this._kineticData._kineticTimer != null)
|
2008-11-21 21:12:25 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
let dragData = this._dragData;
|
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
// if we never received a mouseDown, we need to go ahead and set this data
|
|
|
|
if (!dragData.sX)
|
|
|
|
dragData.setDragPosition(aEvent.screenX, aEvent.screenY);
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
let [sX, sY] = dragData.lockMouseMove(aEvent.screenX, aEvent.screenY);
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
// even if we haven't started dragging yet, we should queue up the
|
|
|
|
// mousemoves in case we do start
|
2009-04-17 14:08:53 -07:00
|
|
|
if (this._useEarlyMouseMoves ||
|
|
|
|
dragData.dragging ||
|
|
|
|
dragData.dragStartTimeout != -1)
|
|
|
|
this._kineticData.addData(sX, sY);
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-04-02 04:11:51 -07:00
|
|
|
this.detectEarlyDrag();
|
|
|
|
|
|
|
|
//dragData.detectEarlyDrag(sX, sY);
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2009-04-02 13:45:27 -07:00
|
|
|
if (dragData.dragging)
|
|
|
|
this._dragMove(sX, sY);
|
2008-11-21 21:12:25 -08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mouse click handlers
|
|
|
|
*/
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
function ContentClickingModule(owner) {
|
2008-11-21 21:12:25 -08:00
|
|
|
this._owner = owner;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
ContentClickingModule.prototype = {
|
2008-11-21 21:12:25 -08:00
|
|
|
_clickTimeout : -1,
|
|
|
|
_events : [],
|
|
|
|
_zoomed : false,
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
handleEvent: function handleEvent(aEvent) {
|
2009-02-23 13:35:25 -08:00
|
|
|
// exit early for events outside displayed content area
|
|
|
|
if (aEvent.target !== document.getElementById("browser-canvas"))
|
|
|
|
return;
|
|
|
|
|
2008-11-21 21:12:25 -08:00
|
|
|
switch (aEvent.type) {
|
|
|
|
// UI panning events
|
|
|
|
case "mousedown":
|
|
|
|
this._events.push({event: aEvent, time: Date.now()});
|
|
|
|
|
|
|
|
// we're waiting for a click
|
|
|
|
if (this._clickTimeout != -1) {
|
|
|
|
// if we just got another mousedown, don't send anything until we get another mousedown
|
2009-04-02 13:45:27 -07:00
|
|
|
window.clearTimeout(this._clickTimeout);
|
2009-02-23 13:35:25 -08:00
|
|
|
this.clickTimeout = -1;
|
2008-11-21 21:12:25 -08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "mouseup":
|
|
|
|
// keep an eye out for mouseups that didn't start with a mousedown
|
|
|
|
if (!(this._events.length % 2)) {
|
|
|
|
this._reset();
|
|
|
|
break;
|
|
|
|
}
|
2009-01-08 22:51:13 -08:00
|
|
|
|
2008-11-21 21:12:25 -08:00
|
|
|
this._events.push({event: aEvent, time: Date.now()});
|
|
|
|
|
|
|
|
if (this._clickTimeout == -1) {
|
2009-04-02 13:45:27 -07:00
|
|
|
this._clickTimeout = window.setTimeout(function(self) { self._sendSingleClick(); }, 400, this);
|
2009-02-23 13:35:25 -08:00
|
|
|
}
|
|
|
|
else {
|
2009-04-02 13:45:27 -07:00
|
|
|
window.clearTimeout(this._clickTimeout);
|
2009-03-19 14:32:50 -07:00
|
|
|
this.clickTimeout = -1;
|
2008-11-21 21:12:25 -08:00
|
|
|
this._sendDoubleClick();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/* If someone else grabs events ahead of us, cancel any pending
|
|
|
|
* timeouts we may have.
|
|
|
|
*/
|
2009-02-27 11:44:35 -08:00
|
|
|
cancelPending: function cancelPending() {
|
2008-11-21 21:12:25 -08:00
|
|
|
this._reset();
|
|
|
|
},
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
_reset: function _reset() {
|
2008-11-21 21:12:25 -08:00
|
|
|
if (this._clickTimeout != -1)
|
2009-04-02 13:45:27 -07:00
|
|
|
window.clearTimeout(this._clickTimeout);
|
2008-11-21 21:12:25 -08:00
|
|
|
this._clickTimeout = -1;
|
|
|
|
|
|
|
|
this._events = [];
|
|
|
|
},
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
_sendSingleClick: function _sendSingleClick() {
|
2008-11-21 21:12:25 -08:00
|
|
|
this._owner.grab(this);
|
2009-02-27 11:44:35 -08:00
|
|
|
this._dispatchContentMouseEvent(this._events[0].event);
|
|
|
|
this._dispatchContentMouseEvent(this._events[1].event);
|
2008-11-21 21:12:25 -08:00
|
|
|
this._owner.ungrab(this);
|
|
|
|
|
|
|
|
this._reset();
|
|
|
|
},
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
_sendDoubleClick: function _sendDoubleClick() {
|
2008-11-21 21:12:25 -08:00
|
|
|
this._owner.grab(this);
|
|
|
|
|
|
|
|
function optimalElementForPoint(cX, cY) {
|
2008-12-09 13:43:20 -08:00
|
|
|
var element = Browser.canvasBrowser.elementFromPoint(cX, cY);
|
2008-11-21 21:12:25 -08:00
|
|
|
if (!element)
|
2008-11-22 21:50:28 -08:00
|
|
|
return null;
|
2008-11-21 21:12:25 -08:00
|
|
|
|
|
|
|
// Find the nearest non-inline ancestor
|
|
|
|
while (element.parentNode) {
|
|
|
|
let display = window.getComputedStyle(element, "").getPropertyValue("display");
|
|
|
|
let zoomable = /table/.test(display) || /block/.test(display);
|
|
|
|
if (zoomable)
|
|
|
|
break;
|
|
|
|
|
|
|
|
element = element.parentNode;
|
|
|
|
}
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
|
|
|
|
let firstEvent = this._events[0].event;
|
|
|
|
let zoomElement = optimalElementForPoint(firstEvent.clientX, firstEvent.clientY);
|
|
|
|
|
|
|
|
if (zoomElement) {
|
|
|
|
if (this._zoomed) {
|
2008-11-22 21:50:28 -08:00
|
|
|
// zoom out
|
|
|
|
this._zoomed = false;
|
2008-12-09 13:43:20 -08:00
|
|
|
Browser.canvasBrowser.zoomFromElement(zoomElement);
|
2009-02-23 13:35:25 -08:00
|
|
|
}
|
|
|
|
else {
|
2008-11-22 21:50:28 -08:00
|
|
|
// zoom in
|
|
|
|
this._zoomed = true;
|
2008-12-09 13:43:20 -08:00
|
|
|
Browser.canvasBrowser.zoomToElement(zoomElement);
|
2008-11-21 21:12:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
this._owner.ungrab(this);
|
|
|
|
|
|
|
|
this._reset();
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2009-02-27 11:44:35 -08:00
|
|
|
_dispatchContentMouseEvent: function _dispatchContentMouseEvent(aEvent, aType) {
|
2008-11-21 21:12:25 -08:00
|
|
|
if (!(aEvent instanceof MouseEvent)) {
|
2009-02-27 11:44:35 -08:00
|
|
|
Cu.reportError("_dispatchContentMouseEvent called with a non-mouse event");
|
2008-11-21 21:12:25 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-03-01 14:32:40 -08:00
|
|
|
let cb = Browser.canvasBrowser;
|
|
|
|
var [x, y] = cb._clientToContentCoords(aEvent.clientX, aEvent.clientY);
|
|
|
|
var cwu = cb.contentDOMWindowUtils;
|
2009-03-02 10:55:52 -08:00
|
|
|
|
2008-11-21 21:12:25 -08:00
|
|
|
// Redispatch the mouse event, ignoring the root scroll frame
|
|
|
|
cwu.sendMouseEvent(aType || aEvent.type,
|
|
|
|
x, y,
|
|
|
|
aEvent.button || 0,
|
|
|
|
aEvent.detail || 1,
|
|
|
|
0, true);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scrollwheel zooming handler
|
|
|
|
*/
|
|
|
|
|
|
|
|
function ScrollwheelModule(owner) {
|
|
|
|
this._owner = owner;
|
|
|
|
}
|
|
|
|
|
|
|
|
ScrollwheelModule.prototype = {
|
2009-02-27 11:44:35 -08:00
|
|
|
handleEvent: function handleEvent(aEvent) {
|
2008-11-21 21:12:25 -08:00
|
|
|
switch (aEvent.type) {
|
|
|
|
// UI panning events
|
|
|
|
case "DOMMouseScroll":
|
|
|
|
this._owner.grab(this);
|
2008-12-09 13:43:20 -08:00
|
|
|
Browser.canvasBrowser.zoom(aEvent.detail);
|
2008-11-21 21:12:25 -08:00
|
|
|
this._owner.ungrab(this);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/* If someone else grabs events ahead of us, cancel any pending
|
|
|
|
* timeouts we may have.
|
|
|
|
*/
|
2009-02-27 11:44:35 -08:00
|
|
|
cancelPending: function cancelPending() {
|
2008-11-21 21:12:25 -08:00
|
|
|
}
|
|
|
|
};
|