2008-04-18 06:41:49 -07:00
|
|
|
<?xml version="1.0"?>
|
|
|
|
<!DOCTYPE bindings PUBLIC "-//MOZILLA//DTD XBL V1.0//EN" "http://www.mozilla.org/xbl">
|
|
|
|
|
|
|
|
<bindings
|
|
|
|
xmlns="http://www.mozilla.org/xbl"
|
|
|
|
xmlns:xbl="http://www.mozilla.org/xbl"
|
|
|
|
xmlns:html="http://www.w3.org/1999/xhtml"
|
|
|
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
|
|
|
|
|
|
<binding id="deckbrowser">
|
|
|
|
<content>
|
2008-07-24 11:50:32 -07:00
|
|
|
<xul:deck flex="1" selectedIndex="0">
|
2008-06-21 11:51:41 -07:00
|
|
|
<xul:stack anonid="cstack" flex="1" style="overflow: hidden;">
|
|
|
|
<html:canvas anonid="ccanvas"
|
|
|
|
moz-opaque="true"
|
|
|
|
style="-moz-stack-sizing: ignore;"/>
|
2008-05-17 23:35:30 -07:00
|
|
|
</xul:stack>
|
2008-07-24 11:50:32 -07:00
|
|
|
<xul:deck anonid="display-list" flex="1"/>
|
2008-06-21 11:51:41 -07:00
|
|
|
</xul:deck>
|
2008-04-18 06:41:49 -07:00
|
|
|
</content>
|
|
|
|
|
2008-07-24 11:50:32 -07:00
|
|
|
<resources>
|
|
|
|
<stylesheet src="chrome://global/content/deckbrowser.css"/>
|
|
|
|
</resources>
|
|
|
|
|
|
|
|
<implementation implements="nsIObserver">
|
2008-08-15 10:02:52 -07:00
|
|
|
<constructor><![CDATA[
|
2008-06-21 11:51:41 -07:00
|
|
|
this._zoomLevel = 1;
|
2008-04-18 06:41:49 -07:00
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
// panning
|
|
|
|
this._stack.addEventListener("mousedown", this.stackEventHandler, true);
|
2008-06-24 18:03:37 -07:00
|
|
|
// need mouseup handled on the window to catch mouseups on e.g. the toolbar
|
|
|
|
window.addEventListener("mouseup", this.stackEventHandler, true);
|
2008-06-21 11:51:41 -07:00
|
|
|
this._stack.addEventListener("mousemove", this.stackEventHandler, true);
|
2008-04-18 06:41:49 -07:00
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
// zoom
|
2008-07-11 18:22:12 -07:00
|
|
|
// FIXME: dblclicks don't work on the device
|
|
|
|
// this._stack.addEventListener("dblclick", this.stackEventHandler, true);
|
2008-06-21 11:51:41 -07:00
|
|
|
this._stack.addEventListener("DOMMouseScroll", this.stackEventHandler, true);
|
2008-07-04 00:33:05 -07:00
|
|
|
|
2008-07-24 11:50:32 -07:00
|
|
|
var self = this;
|
|
|
|
var obs = Components.classes["@mozilla.org/observer-service;1"].
|
|
|
|
getService(Components.interfaces.nsIObserverService);
|
|
|
|
obs.addObserver(function(subject, topic, data) self.destroyEarliestBrowser(),
|
|
|
|
"memory-pressure", false);
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
this._dragStartTimeout = -1;
|
2008-08-15 10:02:52 -07:00
|
|
|
|
|
|
|
this.PAN_EVENTS_TO_TRACK = 2;
|
|
|
|
this._panEventTracker = new Array(this.PAN_EVENTS_TO_TRACK);
|
|
|
|
this._panEventTrackerIndex = 0;
|
|
|
|
]]></constructor>
|
2008-06-21 11:51:41 -07:00
|
|
|
|
2008-08-19 23:09:22 -07:00
|
|
|
<property name="dragData" readonly="true">
|
|
|
|
<getter>
|
|
|
|
<![CDATA[
|
|
|
|
if (!this.currentTab.dragData) {
|
|
|
|
this.currentTab.dragData = {
|
|
|
|
dragging: false,
|
|
|
|
dragX: 0,
|
|
|
|
dragY: 0,
|
|
|
|
sX: 0,
|
|
|
|
sY: 0,
|
|
|
|
pageX: 0,
|
|
|
|
pageY: 0,
|
|
|
|
oldPageX: 0,
|
|
|
|
oldPageY: 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.currentTab.dragData;
|
|
|
|
]]>
|
|
|
|
</getter>
|
|
|
|
</property>
|
2008-05-07 16:57:23 -07:00
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
<field name="_stack">
|
|
|
|
document.getAnonymousElementByAttribute(this, "anonid", "cstack");
|
2008-05-07 16:57:23 -07:00
|
|
|
</field>
|
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
<field name="_canvas">
|
|
|
|
document.getAnonymousElementByAttribute(this, "anonid", "ccanvas");
|
2008-04-18 06:41:49 -07:00
|
|
|
</field>
|
|
|
|
|
|
|
|
<property name="browser" readonly="true">
|
|
|
|
<getter>
|
2008-07-24 11:50:32 -07:00
|
|
|
<![CDATA[
|
|
|
|
return this.getBrowserForDisplay(this.displayList.selectedPanel);
|
|
|
|
]]>
|
|
|
|
</getter>
|
|
|
|
</property>
|
|
|
|
|
|
|
|
<property name="displayList" readonly="true">
|
|
|
|
<getter>
|
|
|
|
return document.getAnonymousElementByAttribute(this, "anonid", "display-list");
|
|
|
|
</getter>
|
|
|
|
</property>
|
|
|
|
|
2008-07-25 11:10:35 -07:00
|
|
|
<field name="tabList">
|
|
|
|
null
|
|
|
|
</field>
|
2008-07-24 11:50:32 -07:00
|
|
|
|
|
|
|
<field name="progressListenerCreator"/>
|
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
<method name="updateCanvasState">
|
2008-07-16 06:36:28 -07:00
|
|
|
<parameter name="aNewDoc"/>
|
2008-06-21 11:51:41 -07:00
|
|
|
<body><![CDATA[
|
2008-07-16 06:36:28 -07:00
|
|
|
if (aNewDoc)
|
|
|
|
this._updateViewState();
|
2008-06-21 11:51:41 -07:00
|
|
|
|
|
|
|
if (this._updateTimeout)
|
|
|
|
clearTimeout(this._updateTimeout);
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
this._updateTimeout = setTimeout(function () {
|
2008-08-20 12:26:29 -07:00
|
|
|
if (!self.dragData.dragging && !self.dragData.kineticId)
|
2008-06-24 18:18:16 -07:00
|
|
|
self._browserToCanvas();
|
2008-06-21 11:51:41 -07:00
|
|
|
}, 100);
|
|
|
|
]]></body>
|
2008-04-18 06:41:49 -07:00
|
|
|
</method>
|
|
|
|
|
2008-07-16 06:36:28 -07:00
|
|
|
<method name="_updateViewState">
|
|
|
|
<body><![CDATA[
|
|
|
|
// Reset the pan data.
|
|
|
|
this.dragData.pageX = 0;
|
|
|
|
this.dragData.pageY = 0;
|
|
|
|
|
|
|
|
this._zoomed = false;
|
|
|
|
|
2008-07-21 14:12:18 -07:00
|
|
|
this.zoomToPage();
|
2008-07-16 06:36:28 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2008-07-24 11:50:32 -07:00
|
|
|
<field name="currentTab"/>
|
|
|
|
|
|
|
|
<method name="getDisplayForTab">
|
|
|
|
<parameter name="tab"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var tabList = this.tabList.childNodes;
|
|
|
|
for (var t = 0; t < tabList.length; t++) {
|
|
|
|
if (tab == tabList[t])
|
|
|
|
return this.displayList.childNodes[t];
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
]]>
|
|
|
|
</body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="getTabForDisplay">
|
|
|
|
<parameter name="display"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var displayList = this.displayList.childNodes;
|
|
|
|
for (var t = 0; t < displayList.length; t++) {
|
|
|
|
if (display == displayList[t])
|
|
|
|
return this.tabList.childNodes[t];
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
]]>
|
|
|
|
</body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="getBrowserForDisplay">
|
|
|
|
<parameter name="display"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
if (!display)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
var browser = display.firstChild;
|
|
|
|
if (browser && browser.localName == "browser")
|
|
|
|
return browser;
|
|
|
|
|
|
|
|
browser = display.lastChild;
|
|
|
|
return (browser && browser.localName == "browser") ? browser : null;
|
|
|
|
]]>
|
|
|
|
</body>
|
|
|
|
</method>
|
|
|
|
|
2008-08-19 23:09:22 -07:00
|
|
|
<method name="setLoading">
|
|
|
|
<parameter name="browser"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var tab = this.getTabForDisplay(browser.parentNode);
|
|
|
|
if (tab)
|
|
|
|
tab.dragData = null;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2008-07-24 11:50:32 -07:00
|
|
|
<method name="updateBrowser">
|
|
|
|
<parameter name="browser"/>
|
|
|
|
<parameter name="done"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var display = browser.parentNode;
|
|
|
|
var domWin = browser.contentWindow;
|
|
|
|
display.url = domWin.location.toString();
|
|
|
|
|
|
|
|
if (!done || domWin.location == "about:blank")
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.restoreBrowserState(display);
|
|
|
|
|
|
|
|
var tab = this.getTabForDisplay(display);
|
|
|
|
if (tab) {
|
|
|
|
tab.updateTab(browser);
|
|
|
|
|
|
|
|
var canvas = display.firstChild;
|
|
|
|
if (canvas.localName == "canvas")
|
|
|
|
display.removeChild(canvas);
|
|
|
|
}
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="selectTab">
|
|
|
|
<parameter name="tab"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var currentTab = this.currentTab;
|
|
|
|
this.currentTab = tab;
|
|
|
|
|
|
|
|
if (currentTab) {
|
|
|
|
var currentDisplay = this.getDisplayForTab(currentTab);
|
|
|
|
var currentBrowser = this.getBrowserForDisplay(currentDisplay);
|
|
|
|
if (currentBrowser) {
|
|
|
|
currentDisplay.url = currentBrowser.contentWindow.location.toString();
|
|
|
|
currentBrowser.setAttribute("type", "content");
|
|
|
|
currentTab.updateTab(currentBrowser);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var display = this.getDisplayForTab(tab);
|
|
|
|
var browser = this.getBrowserForDisplay(display);
|
|
|
|
if (!browser) {
|
|
|
|
browser = this.createBrowser(true, tab, display);
|
|
|
|
browser.loadURI(display.url, null, null, false);
|
|
|
|
}
|
|
|
|
display.lastAccess = Date.now();
|
|
|
|
|
|
|
|
browser.setAttribute("type", "content-primary");
|
|
|
|
this.displayList.selectedPanel = display;
|
2008-08-19 23:09:22 -07:00
|
|
|
|
|
|
|
var event = document.createEvent("Events");
|
|
|
|
event.initEvent("TabSelect", true, false);
|
|
|
|
tab.dispatchEvent(event);
|
2008-07-24 11:50:32 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="newTab">
|
|
|
|
<parameter name="makeFront"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var browser = this.createBrowser(makeFront, null, null);
|
2008-08-19 23:09:22 -07:00
|
|
|
if (!browser)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
var tab = this.getTabForDisplay(browser.parentNode);
|
|
|
|
|
|
|
|
var evt = document.createEvent("Events");
|
|
|
|
evt.initEvent("TabOpen", true, false);
|
|
|
|
tab.dispatchEvent(evt);
|
|
|
|
|
|
|
|
return tab;
|
2008-07-24 11:50:32 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="removeTab">
|
|
|
|
<parameter name="tab"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
if (!tab)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var display = this.getDisplayForTab(tab);
|
|
|
|
if (display)
|
|
|
|
display.removeChild(display);
|
|
|
|
tab.parentNode.removeChild(tab);
|
2008-08-19 23:09:22 -07:00
|
|
|
|
|
|
|
var evt = document.createEvent("Events");
|
|
|
|
evt.initEvent("TabClose", true, false);
|
|
|
|
tab.dispatchEvent(evt);
|
2008-07-24 11:50:32 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="createBrowser">
|
|
|
|
<parameter name="makeFront"/>
|
|
|
|
<parameter name="tab"/>
|
|
|
|
<parameter name="display"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
|
|
|
|
|
|
var browser = document.createElementNS(XUL_NS, "browser");
|
|
|
|
browser.className = "deckbrowser-browser";
|
|
|
|
browser.setAttribute("style", "overflow: hidden");
|
|
|
|
browser.setAttribute("contextmenu", this.getAttribute("contextmenu"));
|
|
|
|
browser.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
|
|
|
|
browser.flex = 1;
|
|
|
|
|
|
|
|
if (makeFront)
|
|
|
|
browser.setAttribute("type", "content-primary");
|
|
|
|
else
|
|
|
|
browser.setAttribute("type", "content");
|
|
|
|
|
|
|
|
var nextDisplay;
|
|
|
|
var displayList = this.displayList;
|
|
|
|
if (tab) {
|
|
|
|
var nextTab = tab.nextSibling;
|
|
|
|
if (nextTab)
|
|
|
|
nextDisplay = this.getDisplayForTab(nextTab);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!display) {
|
|
|
|
display = document.createElementNS(XUL_NS, "deck");
|
|
|
|
displayList.insertBefore(display, nextDisplay);
|
|
|
|
}
|
|
|
|
display.appendChild(browser);
|
|
|
|
display.selectedIndex = 0;
|
|
|
|
|
|
|
|
if (this.progressListenerCreator) {
|
|
|
|
var listener = this.progressListenerCreator(this, browser);
|
|
|
|
browser.addProgressListener(listener);
|
|
|
|
display.progressListener = listener;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tab) {
|
|
|
|
tab = document.createElementNS(XUL_NS, "richlistitem");
|
|
|
|
tab.setAttribute("type", "documenttab");
|
|
|
|
this.tabList.appendChild(tab);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tab == this.tabList.selectedItem) {
|
|
|
|
// already selected, but need to update the selected panel
|
|
|
|
display.lastAccess = Date.now();
|
|
|
|
displayList.selectedPanel = display;
|
|
|
|
}
|
|
|
|
else if (makeFront) {
|
|
|
|
this.tabList.selectedItem = tab;
|
|
|
|
}
|
|
|
|
|
|
|
|
return browser;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="destroyBrowser">
|
|
|
|
<parameter name="browser"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
if (!browser || browser == this.browser)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var display = browser.parentNode;
|
|
|
|
this.saveBrowserState(display);
|
|
|
|
|
|
|
|
var domWin = browser.contentWindow;
|
|
|
|
|
|
|
|
var tab = this.getTabForDisplay(display);
|
|
|
|
if (tab)
|
|
|
|
tab.markInvalid();
|
|
|
|
|
|
|
|
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
|
|
|
var canvas = document.createElementNS(XHTML_NS, "canvas");
|
|
|
|
canvas.setAttribute("width", domWin.innerWidth);
|
|
|
|
canvas.setAttribute("height", domWin.innerHeight);
|
|
|
|
|
|
|
|
var ctx = canvas.getContext("2d");
|
|
|
|
ctx.drawWindow(domWin, 0, 0, domWin.innerWidth, domWin.innerHeight, "rgba(0,0,0,0)");
|
|
|
|
display.insertBefore(canvas, display.firstChild);
|
|
|
|
|
|
|
|
display.lastAccess = Date.now();
|
|
|
|
display.progressListener = null;
|
|
|
|
display.removeChild(browser);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="destroyEarliestBrowser">
|
|
|
|
<body><![CDATA[
|
|
|
|
var earliestBrowser = null;
|
|
|
|
var earliest = Date.now();
|
|
|
|
var displayList = this.displayList.childNodes;
|
|
|
|
for (var t = 0; t < displayList.length; t++) {
|
|
|
|
var display = displayList[t];
|
|
|
|
var browser = this.getBrowserForDisplay(display);
|
|
|
|
if (browser &&
|
|
|
|
display != this.displayList.selectedItem &&
|
|
|
|
display.lastAccess < earliest) {
|
|
|
|
earliestBrowser = browser;
|
|
|
|
earliest = display.lastAccess;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (earliestBrowser)
|
|
|
|
this.destroyBrowser(earliestBrowser);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="saveBrowserState">
|
|
|
|
<parameter name="display"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var state = { };
|
|
|
|
|
|
|
|
var browser = this.getBrowserForDisplay(display);
|
|
|
|
var doc = browser.contentDocument;
|
|
|
|
if (doc instanceof HTMLDocument) {
|
|
|
|
var tags = ["input", "textarea", "select"];
|
|
|
|
|
|
|
|
for (var t = 0; t < tags.length; t++) {
|
|
|
|
var elements = doc.getElementsByTagName(tags[t]);
|
|
|
|
for (var e = 0; e < elements.length; e++) {
|
|
|
|
var element = elements[e];
|
|
|
|
var id;
|
2008-07-25 11:10:35 -07:00
|
|
|
if (element.id)
|
2008-07-24 11:50:32 -07:00
|
|
|
id = "#" + element.id;
|
|
|
|
else if (element.name)
|
|
|
|
id = "$" + element.name;
|
|
|
|
|
|
|
|
if (id)
|
|
|
|
state[id] = element.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-22 06:33:00 -07:00
|
|
|
state._scrollX = browser.contentWindow.scrollX;
|
|
|
|
state._scrollY = browser.contentWindow.scrollY;
|
2008-07-24 11:50:32 -07:00
|
|
|
|
|
|
|
display.state = state;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="restoreBrowserState">
|
|
|
|
<parameter name="display"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var state = display.state;
|
|
|
|
if (!state)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var browser = this.getBrowserForDisplay(display);
|
|
|
|
var doc = browser.contentDocument;
|
|
|
|
for (item in state) {
|
|
|
|
var elem = null;
|
|
|
|
if (item.charAt(0) == "#") {
|
|
|
|
elem = doc.getElementById(item.substring(1));
|
|
|
|
}
|
|
|
|
else if (item.charAt(0) == "$") {
|
|
|
|
var list = doc.getElementsByName(item.substring(1));
|
|
|
|
if (list.length)
|
|
|
|
elem = list[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (elem)
|
|
|
|
elem.value = state[item];
|
|
|
|
}
|
|
|
|
|
|
|
|
this.browser.contentWindow.scrollTo(state._scrollX, state._scrollY);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
<method name="_browserToCanvas">
|
|
|
|
<body><![CDATA[
|
|
|
|
// FIXME: canvas needs to know it's actual width/height
|
|
|
|
var rect = this._canvas.getBoundingClientRect();
|
|
|
|
var w = rect.right - rect.left;
|
|
|
|
var h = rect.bottom - rect.top;
|
|
|
|
this._canvas.width = w;
|
|
|
|
this._canvas.height = h;
|
|
|
|
|
|
|
|
var ctx = this._canvas.getContext("2d");
|
|
|
|
|
|
|
|
ctx.clearRect(0,0,w,h);
|
|
|
|
|
|
|
|
ctx.save();
|
|
|
|
ctx.scale(this._zoomLevel, this._zoomLevel);
|
2008-07-11 15:22:38 -07:00
|
|
|
ctx.drawWindow(this.browser.contentWindow,
|
|
|
|
this.dragData.pageX, this.dragData.pageY,
|
2008-06-21 11:51:41 -07:00
|
|
|
w / this._zoomLevel, h / this._zoomLevel,
|
|
|
|
"white");
|
|
|
|
ctx.restore();
|
|
|
|
]]></body>
|
2008-04-18 06:41:49 -07:00
|
|
|
</method>
|
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
<method name="_updateCanvasPosition">
|
|
|
|
<body><![CDATA[
|
2008-07-11 15:22:38 -07:00
|
|
|
this._canvas.style.marginLeft = this.dragData.dragX + "px";
|
|
|
|
this._canvas.style.marginRight = -this.dragData.dragX + "px";
|
|
|
|
this._canvas.style.marginTop = this.dragData.dragY + "px";
|
|
|
|
this._canvas.style.marginBottom = -this.dragData.dragY + "px";
|
2008-04-18 06:41:49 -07:00
|
|
|
|
2008-07-17 05:49:05 -07:00
|
|
|
// Force a sync redraw
|
|
|
|
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
2008-08-27 14:28:03 -07:00
|
|
|
.processUpdates();
|
2008-06-21 11:51:41 -07:00
|
|
|
]]></body>
|
2008-04-18 06:41:49 -07:00
|
|
|
</method>
|
|
|
|
|
2008-07-21 14:12:18 -07:00
|
|
|
<method name="_clampZoomLevel">
|
|
|
|
<parameter name="aZoomLevel"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
const min = 0.2;
|
|
|
|
const max = 2.0;
|
|
|
|
|
|
|
|
return Math.min(Math.max(min, aZoomLevel), max);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<property name="zoomLevel" onget="return this._zoomLevel;">
|
|
|
|
<setter><![CDATA[
|
|
|
|
this._zoomLevel = this._clampZoomLevel(val);
|
|
|
|
|
|
|
|
this._browserToCanvas();
|
|
|
|
|
|
|
|
return val;
|
|
|
|
]]></setter>
|
|
|
|
</property>
|
2008-05-08 15:59:16 -07:00
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
<method name="zoom">
|
|
|
|
<parameter name="aDirection"/>
|
|
|
|
<body><![CDATA[
|
2008-07-21 14:12:18 -07:00
|
|
|
if (aDirection == 0)
|
|
|
|
return;
|
2008-05-17 23:35:30 -07:00
|
|
|
|
2008-07-21 14:12:18 -07:00
|
|
|
var zoomDelta = 0.05; // 1/20
|
|
|
|
if (aDirection >= 0)
|
|
|
|
zoomDelta *= -1;
|
2008-05-17 23:35:30 -07:00
|
|
|
|
2008-07-21 14:12:18 -07:00
|
|
|
this._zoomLevel = this._clampZoomLevel(this._zoomLevel + zoomDelta);
|
2008-05-17 23:35:30 -07:00
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
this._browserToCanvas();
|
|
|
|
]]></body>
|
|
|
|
</method>
|
2008-05-22 13:46:20 -07:00
|
|
|
|
2008-07-21 14:12:18 -07:00
|
|
|
<method name="zoomToPage">
|
|
|
|
<body><![CDATA[
|
|
|
|
// Adjust the zoomLevel to fit the page contents in our window
|
|
|
|
// width
|
|
|
|
var [contentWidth, ] = this._contentAreaDimensions;
|
|
|
|
var canvasRect = this._canvas.getBoundingClientRect();
|
|
|
|
var canvasWidth = canvasRect.right - canvasRect.left;
|
|
|
|
|
2008-07-24 11:50:32 -07:00
|
|
|
if (contentWidth)
|
|
|
|
this._zoomLevel = canvasWidth / contentWidth;
|
2008-07-21 14:12:18 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="zoomToElement">
|
|
|
|
<parameter name="aElement"/>
|
|
|
|
<body><![CDATA[
|
2008-07-24 12:37:38 -07:00
|
|
|
const margin = 15;
|
2008-07-21 14:12:18 -07:00
|
|
|
|
|
|
|
// scale to the element's width
|
|
|
|
var elRect = this._getPagePosition(aElement);
|
2008-08-19 20:32:03 -07:00
|
|
|
var zoomLevel = this.browser.boxObject.width / (elRect.width + (2 * margin));
|
|
|
|
this._zoomLevel = Math.min(zoomLevel, 10);
|
2008-07-21 14:12:18 -07:00
|
|
|
|
|
|
|
// pan to the element
|
|
|
|
this._panTo(Math.max(elRect.x - margin, 0),
|
|
|
|
Math.max(0, elRect.y - margin));
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
/**
|
|
|
|
* Retrieve the content element for a given point (relative to the top
|
|
|
|
* left corner of the browser window).
|
|
|
|
*/
|
|
|
|
<method name="elementFromPoint">
|
|
|
|
<parameter name="aX"/>
|
|
|
|
<parameter name="aY"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var cdoc = this.browser.contentDocument;
|
|
|
|
|
2008-08-19 22:30:07 -07:00
|
|
|
var [x, y] = this._clientToContentCoords(aX, aY);
|
|
|
|
var element = cdoc.elementFromPoint(x, y);
|
2008-07-16 03:21:24 -07:00
|
|
|
|
|
|
|
// Reset scroll state
|
|
|
|
this.browser.contentWindow.scrollTo(0, 0);
|
|
|
|
|
|
|
|
return element;
|
2008-07-11 15:22:38 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="_getPagePosition">
|
|
|
|
<parameter name="aElement"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var r = aElement.getBoundingClientRect();
|
2008-07-16 04:45:31 -07:00
|
|
|
var retVal = {
|
2008-07-11 15:22:38 -07:00
|
|
|
width: r.right - r.left,
|
|
|
|
height: r.bottom - r.top,
|
|
|
|
x: r.left,
|
|
|
|
y: r.top
|
|
|
|
};
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
<method name="_redispatchMouseEvent">
|
|
|
|
<parameter name="aEvent"/>
|
|
|
|
<parameter name="aType"/>
|
|
|
|
<body><![CDATA[
|
2008-07-11 15:22:38 -07:00
|
|
|
if (!(aEvent instanceof MouseEvent)) {
|
|
|
|
Components.utils.reportError("_redispatchMouseEvent called with a non-mouse event");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-19 22:30:07 -07:00
|
|
|
var [x, y] = this._clientToContentCoords(aEvent.clientX, aEvent.clientY);
|
2008-07-04 00:33:05 -07:00
|
|
|
|
2008-07-16 03:21:24 -07:00
|
|
|
var cwin = this.browser.contentWindow;
|
2008-07-11 15:22:38 -07:00
|
|
|
var cwu = cwin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
2008-06-21 11:51:41 -07:00
|
|
|
cwu.sendMouseEvent(aType || aEvent.type,
|
2008-08-19 22:30:07 -07:00
|
|
|
x, y,
|
2008-06-21 11:51:41 -07:00
|
|
|
aEvent.button || 0,
|
2008-07-11 15:22:38 -07:00
|
|
|
aEvent.detail || 1,
|
2008-06-21 11:51:41 -07:00
|
|
|
0);
|
2008-07-11 15:22:38 -07:00
|
|
|
|
|
|
|
// Reset scroll state
|
|
|
|
cwin.scrollTo(0, 0);
|
2008-06-21 11:51:41 -07:00
|
|
|
]]></body>
|
2008-05-06 07:56:21 -07:00
|
|
|
</method>
|
|
|
|
|
2008-08-19 22:30:07 -07:00
|
|
|
<!-- Given a set of client coordinates (relative to the app window),
|
|
|
|
scrolls the content window as close to the clicked on content item
|
|
|
|
as possible, and returns the remaining offsets into the content
|
|
|
|
window if the object isn't at 0, 0. Callers should be sure to reset
|
|
|
|
scroll state after calling this method.
|
|
|
|
-->
|
|
|
|
<method name="_clientToContentCoords">
|
|
|
|
<parameter name="aClientX"/>
|
|
|
|
<parameter name="aClientY"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
// Need to adjust for the toolbar height, etc.
|
|
|
|
var browserTop = this.browser.getBoundingClientRect().top;
|
|
|
|
|
|
|
|
var clickOffsetX = (aClientX / this._zoomLevel) + this.dragData.pageX;
|
|
|
|
var clickOffsetY = ((aClientY - browserTop) / this._zoomLevel) + this.dragData.pageY;
|
|
|
|
|
|
|
|
// Scroll the browser so that the event is targeted properly
|
|
|
|
var cwin = this.browser.contentWindow;
|
|
|
|
cwin.scrollTo(clickOffsetX, clickOffsetY);
|
|
|
|
|
|
|
|
// Might not have been able to scroll all the way if we're zoomed in,
|
|
|
|
// so we need to account for that difference.
|
|
|
|
var pageOffsetX = clickOffsetX - cwin.scrollX;
|
|
|
|
var pageOffsetY = clickOffsetY - cwin.scrollY;
|
|
|
|
|
|
|
|
return [pageOffsetX, pageOffsetY];
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
<property name="_contentAreaDimensions" readonly="true">
|
|
|
|
<getter>
|
|
|
|
var cdoc = this.browser.contentDocument;
|
|
|
|
|
2008-07-24 12:50:59 -07:00
|
|
|
// Return the document width/height for XUL documents (which is
|
|
|
|
// essentially the same as the viewport width/height).
|
|
|
|
if (cdoc instanceof XULDocument)
|
|
|
|
return [cdoc.width, cdoc.height];
|
|
|
|
|
|
|
|
// These might not exist yet depending on page load state
|
2008-07-11 15:22:38 -07:00
|
|
|
var body = cdoc.body || {};
|
|
|
|
var html = cdoc.documentElement || {};
|
|
|
|
|
2008-07-16 06:36:28 -07:00
|
|
|
var w = Math.max(body.scrollWidth, html.scrollWidth);
|
|
|
|
var h = Math.max(body.scrollHeight, html.scrollHeight);
|
|
|
|
|
|
|
|
if (isNaN(w) || isNaN(h))
|
|
|
|
throw "Can't get content width/height";
|
2008-07-11 15:22:38 -07:00
|
|
|
|
2008-07-16 06:36:28 -07:00
|
|
|
return [w, h];
|
2008-07-11 15:22:38 -07:00
|
|
|
</getter>
|
|
|
|
</property>
|
|
|
|
|
|
|
|
<property name="_effectiveCanvasDimensions" readonly="true">
|
|
|
|
<getter><![CDATA[
|
|
|
|
return [this._canvas.width / this._zoomLevel,
|
|
|
|
this._canvas.height / this._zoomLevel];
|
|
|
|
]]></getter>
|
|
|
|
</property>
|
|
|
|
|
2008-07-25 11:10:35 -07:00
|
|
|
<property name="scrollX" readonly="true">
|
|
|
|
<getter><![CDATA[
|
|
|
|
return this.dragData.pageX - this.dragData.dragX / this._zoomLevel;
|
|
|
|
]]></getter>
|
|
|
|
</property>
|
|
|
|
|
|
|
|
<property name="scrollY" readonly="true">
|
|
|
|
<getter><![CDATA[
|
|
|
|
return this.dragData.pageY - this.dragData.dragY / this._zoomLevel;
|
|
|
|
]]></getter>
|
|
|
|
</property>
|
|
|
|
|
2008-07-11 20:09:35 -07:00
|
|
|
<field name="_fireOverpan">
|
|
|
|
0
|
|
|
|
</field>
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
/**
|
|
|
|
* Given a set of page coordinates, constrain them such that they
|
|
|
|
* fit within the rect defined by [0,0] and [x,y], where x and y are
|
|
|
|
* the maximum values that can be used for the canvas' .top and .left
|
|
|
|
* such that it is still within the scrollable area of the page, taking
|
|
|
|
* into account the current zoomLevel.
|
|
|
|
*/
|
|
|
|
<method name="_constrainPanCoords">
|
|
|
|
<parameter name="aX"/>
|
|
|
|
<parameter name="aY"/>
|
2008-06-21 11:51:41 -07:00
|
|
|
<body><![CDATA[
|
2008-07-11 20:09:35 -07:00
|
|
|
const OVERPAN_LIMIT = 30;
|
|
|
|
const OVERPAN_LEFT = 1;
|
|
|
|
const OVERPAN_RIGHT = 2;
|
|
|
|
const OVERPAN_TOP = 3;
|
|
|
|
const OVERPAN_BOTTOM = 4;
|
|
|
|
|
|
|
|
var origX = aX;
|
|
|
|
var origY = aY;
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
var [contentAreaWidth, contentAreaHeight] = this._contentAreaDimensions;
|
|
|
|
var [canvasW, canvasH] = this._effectiveCanvasDimensions;
|
2008-07-04 00:33:05 -07:00
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
var offscreenWidth = contentAreaWidth - canvasW;
|
2008-06-21 11:51:41 -07:00
|
|
|
if (offscreenWidth <= 0) {
|
|
|
|
// Content is narrower than viewport, no need to pan horizontally
|
2008-07-11 15:22:38 -07:00
|
|
|
aX = 0;
|
2008-07-11 20:09:35 -07:00
|
|
|
|
|
|
|
// Check for an overpan
|
|
|
|
if (origX < -OVERPAN_LIMIT)
|
|
|
|
this._fireOverpan = OVERPAN_LEFT;
|
|
|
|
else if (origX > OVERPAN_LIMIT)
|
|
|
|
this._fireOverpan = OVERPAN_RIGHT;
|
2008-06-21 11:51:41 -07:00
|
|
|
} else {
|
2008-07-11 15:22:38 -07:00
|
|
|
var newPageX = Math.min(this.dragData.pageX + aX, offscreenWidth);
|
|
|
|
newPageX = Math.max(newPageX, 0);
|
|
|
|
aX = newPageX - this.dragData.pageX;
|
2008-07-11 20:09:35 -07:00
|
|
|
|
|
|
|
// Check for an overpan
|
|
|
|
if (origX < -OVERPAN_LIMIT && aX <= 0 && newPageX == 0)
|
|
|
|
this._fireOverpan = OVERPAN_LEFT;
|
|
|
|
else if (origX > OVERPAN_LIMIT && aX >= 0 && (offscreenWidth - newPageX) == 0)
|
|
|
|
this._fireOverpan = OVERPAN_RIGHT;
|
2008-06-21 11:51:41 -07:00
|
|
|
}
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
var offscreenHeight = contentAreaHeight - canvasH;
|
2008-06-21 11:51:41 -07:00
|
|
|
if (offscreenHeight <= 0) {
|
|
|
|
// Content is shorter than viewport, no need to pan vertically
|
2008-07-11 15:22:38 -07:00
|
|
|
aY = 0;
|
2008-07-11 20:09:35 -07:00
|
|
|
|
|
|
|
// Check for an overpan
|
|
|
|
if (origY < -OVERPAN_LIMIT)
|
|
|
|
this._fireOverpan = OVERPAN_TOP;
|
|
|
|
else if (origY > OVERPAN_LIMIT)
|
|
|
|
this._fireOverpan = OVERPAN_BOTTOM;
|
2008-06-21 11:51:41 -07:00
|
|
|
} else {
|
2008-07-11 15:22:38 -07:00
|
|
|
// min of 0, max of contentAreaHeight - canvasHeight
|
|
|
|
var newPageY = Math.min(this.dragData.pageY + aY, offscreenHeight);
|
|
|
|
newPageY = Math.max(newPageY, 0);
|
|
|
|
aY = newPageY - this.dragData.pageY;
|
2008-07-11 20:09:35 -07:00
|
|
|
|
|
|
|
// Check for an overpan
|
|
|
|
if (origY < -OVERPAN_LIMIT && aY <= 0 && newPageY == 0)
|
|
|
|
this._fireOverpan = OVERPAN_TOP;
|
|
|
|
else if (origY > OVERPAN_LIMIT && aY >= 0 && (offscreenHeight - newPageY) == 0)
|
|
|
|
this._fireOverpan = OVERPAN_BOTTOM;
|
2008-06-21 11:51:41 -07:00
|
|
|
}
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
return [aX, aY];
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="_moveCanvas">
|
|
|
|
<parameter name="aDx"/>
|
|
|
|
<parameter name="aDy"/>
|
|
|
|
<body><![CDATA[
|
2008-07-29 23:26:53 -07:00
|
|
|
// Constrain offsets to the actual scrollWidth/scrollHeight
|
|
|
|
var [x, y] = this._constrainPanCoords(aDx, aDy);
|
|
|
|
|
|
|
|
// Canvas needs to move up for content to scroll down
|
|
|
|
this.dragData.dragX = -x;
|
|
|
|
this.dragData.dragY = -y;
|
|
|
|
|
|
|
|
this._updateCanvasPosition();
|
2008-06-21 11:51:41 -07:00
|
|
|
]]></body>
|
2008-05-06 07:56:21 -07:00
|
|
|
</method>
|
|
|
|
|
2008-08-15 10:02:52 -07:00
|
|
|
<method name="_startKinetic">
|
|
|
|
<body><![CDATA[
|
|
|
|
var p2 = this._panEventTracker[this._panEventTrackerIndex];
|
|
|
|
var p1 = this._panEventTracker[(this._panEventTrackerIndex + 1) % this.PAN_EVENTS_TO_TRACK];
|
|
|
|
if (p2 && p1) {
|
|
|
|
var dx = p2.x - p1.x;
|
|
|
|
var dy = p2.y - p1.y;
|
|
|
|
var dt = p2.t - p1.t;
|
|
|
|
if (dt > 0) {
|
|
|
|
this.dragData.velocityX = -dx / dt;
|
|
|
|
this.dragData.velocityY = -dy / dt;
|
|
|
|
this.dragData.originalX = this.dragData.dragX;
|
|
|
|
this.dragData.originalY = this.dragData.dragY;
|
|
|
|
var [canvasWidth, canvasHeight] = this._effectiveCanvasDimensions;
|
|
|
|
var [contentAreaWidth, contentAreaHeight] = this._contentAreaDimensions;
|
|
|
|
let destX = Math.min(this.dragData.pageX - this.dragData.dragX + this.dragData.velocityX * 800, contentAreaWidth - canvasWidth);
|
|
|
|
let destY = Math.min(this.dragData.pageY - this.dragData.dragY + this.dragData.velocityY * 800, contentAreaHeight - canvasHeight);
|
|
|
|
destX = Math.max(destX, 0);
|
|
|
|
destY = Math.max(destY, 0);
|
|
|
|
this.dragData.destinationX = -destX + this.dragData.pageX;
|
|
|
|
this.dragData.destinationY = -destY + this.dragData.pageY;
|
|
|
|
this.dragData.kineticId = window.setInterval(this._doKinetic, dt / (this.PAN_EVENTS_TO_TRACK - 1), this, dt / (this.PAN_EVENTS_TO_TRACK - 1));
|
|
|
|
} else {
|
|
|
|
this._endPan();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this._endPan()
|
|
|
|
}
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="_doKinetic">
|
|
|
|
<parameter name="self"/>
|
|
|
|
<parameter name="dt"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
let startX = self.dragData.dragX;
|
|
|
|
let startY = self.dragData.dragY;
|
|
|
|
const stopTheshold = 3; //Distance from the destination point that we'll consider to be "there"
|
|
|
|
if (Math.abs(self.dragData.destinationX - self.dragData.dragX) < stopTheshold) {
|
|
|
|
self.dragData.velocityX = 1/dt;
|
|
|
|
}
|
|
|
|
if (Math.abs(self.dragData.destinationY - self.dragData.dragY) < stopTheshold) {
|
|
|
|
self.dragData.velocityY = 1/dt;
|
|
|
|
}
|
|
|
|
let dx = self.dragData.originalX == self.dragData.destinationX ? 0 :
|
|
|
|
self.dragData.velocityX * dt *(Math.sqrt(Math.abs(self.dragData.destinationX - self.dragData.dragX)) /
|
|
|
|
Math.sqrt(Math.abs(self.dragData.destinationX - self.dragData.originalX)));
|
|
|
|
let dy = self.dragData.originalY == self.dragData.destinationY ? 0 :
|
|
|
|
self.dragData.velocityY * dt * (Math.sqrt(Math.abs(self.dragData.destinationY - self.dragData.dragY)) /
|
|
|
|
Math.sqrt(Math.abs(self.dragData.destinationY - self.dragData.originalY)));
|
|
|
|
let nextX = self.dragData.dragX - dx;
|
2008-08-19 23:09:22 -07:00
|
|
|
let nextY = self.dragData.dragY - dy;
|
2008-08-15 10:02:52 -07:00
|
|
|
|
|
|
|
if((self.dragData.originalX > nextX &&
|
|
|
|
nextX > self.dragData.destinationX) ||
|
|
|
|
(self.dragData.originalX < nextX &&
|
|
|
|
nextX < self.dragData.destinationX))
|
|
|
|
self.dragData.dragX = nextX;
|
|
|
|
else
|
|
|
|
self.dragData.dragX = self.dragData.destinationX;
|
|
|
|
if((self.dragData.originalY > nextY &&
|
|
|
|
nextY > self.dragData.destinationY) ||
|
2008-08-19 23:09:22 -07:00
|
|
|
(self.dragData.originalY < nextY &&
|
2008-08-15 10:02:52 -07:00
|
|
|
nextY < self.dragData.destinationY))
|
|
|
|
self.dragData.dragY = nextY;
|
|
|
|
else
|
|
|
|
self.dragData.dragY = self.dragData.destinationY;
|
2008-08-19 23:09:22 -07:00
|
|
|
|
2008-08-15 10:02:52 -07:00
|
|
|
self._updateCanvasPosition();
|
2008-08-19 23:09:22 -07:00
|
|
|
|
2008-08-15 10:02:52 -07:00
|
|
|
let actualDx = startX - self.dragData.dragX;
|
|
|
|
let actualDy = startY - self.dragData.dragY
|
2008-08-19 23:09:22 -07:00
|
|
|
if ((actualDx / (self.dragData.destinationX - self.dragData.originalX) < 0 && actualDy / (self.dragData.destinationY - self.dragData.originalY) < 0) || ( Math.abs(actualDx) < 4 && Math.abs(actualDy) < 4)) {
|
2008-08-15 10:02:52 -07:00
|
|
|
self._endKinetic();
|
|
|
|
}
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="_endKinetic">
|
|
|
|
<body><![CDATA[
|
|
|
|
window.clearInterval(this.dragData.kineticId);
|
|
|
|
this.dragData.kineticId = 0;
|
|
|
|
|
|
|
|
// dragX/dragY are guaranteed to be within the correct bounds, so just
|
|
|
|
// update pageX/pageY directly.
|
|
|
|
this.dragData.pageX -= this.dragData.dragX / this._zoomLevel;
|
|
|
|
this.dragData.pageY -= this.dragData.dragY / this._zoomLevel;
|
|
|
|
|
|
|
|
// relocate the canvas to 0x0 in the window
|
|
|
|
this.dragData.dragX = 0;
|
|
|
|
this.dragData.dragY = 0;
|
|
|
|
|
|
|
|
// update canvas position and draw the canvas at the new location
|
|
|
|
this._browserToCanvas();
|
|
|
|
this._updateCanvasPosition();
|
|
|
|
|
|
|
|
this.dragData.dragging = false;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
<!-- Pans directly to a given X/Y (in page coordinates) -->
|
|
|
|
<method name="_panTo">
|
|
|
|
<parameter name="aX"/>
|
|
|
|
<parameter name="aY"/>
|
|
|
|
<body><![CDATA[
|
2008-07-16 04:45:31 -07:00
|
|
|
var [deltaX, deltaY] = this._constrainPanCoords(aX - this.dragData.pageX,
|
|
|
|
aY - this.dragData.pageY);
|
|
|
|
this.dragData.pageX += deltaX;
|
|
|
|
this.dragData.pageY += deltaY;
|
2008-07-11 15:22:38 -07:00
|
|
|
|
|
|
|
this._browserToCanvas();
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
<method name="_dragStartTimer">
|
|
|
|
<body><![CDATA[
|
|
|
|
this.dragData.lastMouseEvent = Date.now() - 10;
|
|
|
|
this.dragData.dragging = true;
|
2008-07-11 15:22:38 -07:00
|
|
|
this._dragStartTimeout = -1;
|
2008-06-21 11:51:41 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
2008-05-17 23:35:30 -07:00
|
|
|
|
2008-06-24 18:03:37 -07:00
|
|
|
<method name="_endPan">
|
|
|
|
<body><![CDATA[
|
2008-07-11 15:22:38 -07:00
|
|
|
// dragX/dragY are garanteed to be within the correct bounds, so just
|
|
|
|
// update pageX/pageY directly.
|
|
|
|
this.dragData.pageX -= this.dragData.dragX / this._zoomLevel;
|
|
|
|
this.dragData.pageY -= this.dragData.dragY / this._zoomLevel;
|
2008-06-24 18:03:37 -07:00
|
|
|
|
|
|
|
// relocate the canvas to 0x0 in the window
|
2008-07-11 15:22:38 -07:00
|
|
|
this.dragData.dragX = 0;
|
|
|
|
this.dragData.dragY = 0;
|
2008-06-24 18:03:37 -07:00
|
|
|
|
|
|
|
// update canvas position and draw the canvas at the new location
|
|
|
|
this._browserToCanvas();
|
2008-07-17 05:49:05 -07:00
|
|
|
this._updateCanvasPosition();
|
2008-06-24 18:03:37 -07:00
|
|
|
|
|
|
|
this.dragData.dragging = false;
|
2008-07-11 20:09:35 -07:00
|
|
|
|
|
|
|
// Do we need to fire a content overpan event
|
|
|
|
if (this._fireOverpan > 0) {
|
|
|
|
var event = document.createEvent("UIEvents");
|
|
|
|
event.initUIEvent("overpan", true, false, window, this._fireOverpan);
|
|
|
|
this.dispatchEvent(event);
|
|
|
|
}
|
|
|
|
this._fireOverpan = 0;
|
2008-06-24 18:03:37 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
<field name="stackEventHandler">
|
|
|
|
<![CDATA[
|
|
|
|
({
|
|
|
|
deckbrowser: this,
|
2008-05-20 12:58:10 -07:00
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
handleEvent: function seh_handleEvent(aEvent) {
|
2008-09-02 00:57:13 -07:00
|
|
|
if (!(aEvent.type in this)) {
|
2008-07-11 15:22:38 -07:00
|
|
|
Components.reportError("MouseController called with unknown event type " + aEvent.type + "\n");
|
2008-05-22 13:46:20 -07:00
|
|
|
return;
|
|
|
|
}
|
2008-06-21 11:51:41 -07:00
|
|
|
this[aEvent.type](aEvent);
|
|
|
|
},
|
|
|
|
|
|
|
|
mousedown: function seh_mousedown(aEvent) {
|
|
|
|
if (aEvent.button != 0)
|
2008-07-11 15:22:38 -07:00
|
|
|
return;
|
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
// cancel any pending canvas updates, since we're going to update again
|
|
|
|
if (this._updateTimeout)
|
|
|
|
clearTimeout(this._updateTimeout);
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
var zoomLevel = this.deckbrowser._zoomLevel;
|
|
|
|
var dragData = this.deckbrowser.dragData;
|
2008-06-21 11:51:41 -07:00
|
|
|
|
|
|
|
// The start of the current portion drag
|
2008-07-11 15:22:38 -07:00
|
|
|
dragData.sX = aEvent.screenX;
|
|
|
|
dragData.sY = aEvent.screenY;
|
2008-06-21 11:51:41 -07:00
|
|
|
|
|
|
|
// The total delta between current mouse position and sX/sY
|
2008-07-11 15:22:38 -07:00
|
|
|
dragData.dragX = 0;
|
|
|
|
dragData.dragY = 0;
|
2008-06-21 11:51:41 -07:00
|
|
|
|
|
|
|
//this.deckbrowser._updateCanvasPosition();
|
|
|
|
|
|
|
|
var self = this.deckbrowser;
|
2008-07-11 15:22:38 -07:00
|
|
|
this.deckbrowser._dragStartTimeout = setTimeout(function () {
|
2008-06-21 11:51:41 -07:00
|
|
|
self._dragStartTimer();
|
|
|
|
}, 200);
|
2008-07-11 15:22:38 -07:00
|
|
|
|
|
|
|
this._lastMouseDown = aEvent;
|
2008-08-15 10:02:52 -07:00
|
|
|
for (var i = 0; i < self.PAN_EVENTS_TO_TRACK; i++) {
|
|
|
|
self._panEventTracker[i] = null;
|
|
|
|
}
|
|
|
|
if (self.dragData.kineticId)
|
|
|
|
self._endPan();
|
2008-06-21 11:51:41 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
mouseup: function seh_mouseup(aEvent) {
|
2008-08-15 12:37:22 -07:00
|
|
|
var skipKinetic = false;
|
2008-06-24 18:03:37 -07:00
|
|
|
if (aEvent.button == 0 && this.deckbrowser.dragData.dragging) {
|
2008-08-15 12:37:22 -07:00
|
|
|
if (this.deckbrowser._fireOverpan) {
|
|
|
|
this.deckbrowser._endPan();
|
|
|
|
skipKinetic = true;
|
|
|
|
} else {
|
|
|
|
this.deckbrowser.dragData.dragging = false;
|
|
|
|
}
|
2008-06-24 18:03:37 -07:00
|
|
|
} else if (aEvent.originalTarget == this.deckbrowser._canvas) {
|
|
|
|
// Mouseup on canvas that isn't releasing from a drag
|
|
|
|
// cancel scrollStart timer
|
2008-07-11 15:22:38 -07:00
|
|
|
clearTimeout(this.deckbrowser._dragStartTimeout);
|
|
|
|
this.deckbrowser._dragStartTimeout = -1;
|
2008-06-21 11:51:41 -07:00
|
|
|
|
2008-06-24 18:03:37 -07:00
|
|
|
// send mousedown & mouseup
|
2008-07-11 15:22:38 -07:00
|
|
|
this.deckbrowser._redispatchMouseEvent(this._lastMouseDown);
|
|
|
|
this._lastMouseDown = null;
|
2008-06-21 11:51:41 -07:00
|
|
|
this.deckbrowser._redispatchMouseEvent(aEvent);
|
2008-07-11 18:22:12 -07:00
|
|
|
|
|
|
|
// FIXME: dblclick events don't fire on the n810, check to see if
|
|
|
|
// we should treat this as a double-click
|
|
|
|
if (this._lastMouseUp &&
|
|
|
|
(aEvent.timeStamp - this._lastMouseUp.timeStamp) < 400 &&
|
|
|
|
Math.abs(aEvent.clientX - this._lastMouseUp.clientX) < 30 &&
|
|
|
|
Math.abs(aEvent.clientY - this._lastMouseUp.clientY) < 30) {
|
|
|
|
this.dblclick(aEvent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._lastMouseUp = aEvent;
|
2008-05-08 15:59:16 -07:00
|
|
|
}
|
2008-08-15 12:37:22 -07:00
|
|
|
|
|
|
|
if (!skipKinetic)
|
|
|
|
this.deckbrowser._startKinetic();
|
2008-08-15 10:02:52 -07:00
|
|
|
for (var i = 0; i < this.deckbrowser.PAN_EVENTS_TO_TRACK; i++) {
|
|
|
|
this.deckbrowser._panEventTracker[i] = null;
|
|
|
|
}
|
2008-06-21 11:51:41 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
mousemove: function seh_mousemove(aEvent) {
|
2008-07-04 00:33:05 -07:00
|
|
|
if (!this.deckbrowser.dragData.dragging) {
|
|
|
|
// If we've moved more than N pixels lets go ahead and assume we're dragging
|
|
|
|
// and not wait for the timeout to complete.
|
2008-07-11 15:22:38 -07:00
|
|
|
if (this.deckbrowser._dragStartTimeout != -1 &&
|
2008-07-04 00:33:05 -07:00
|
|
|
(Math.abs(this.deckbrowser.dragData.sX - aEvent.screenX) > 10 ||
|
|
|
|
Math.abs(this.deckbrowser.dragData.sY - aEvent.screenY) > 10)) {
|
2008-07-11 15:22:38 -07:00
|
|
|
clearTimeout(this.deckbrowser._dragStartTimeout);
|
2008-07-04 00:33:05 -07:00
|
|
|
this.deckbrowser._dragStartTimer();
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2008-05-06 07:56:21 -07:00
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
var dx = this.deckbrowser.dragData.sX - aEvent.screenX;
|
|
|
|
var dy = this.deckbrowser.dragData.sY - aEvent.screenY;
|
2008-06-21 11:51:41 -07:00
|
|
|
|
2008-07-17 06:37:15 -07:00
|
|
|
// Filter out noise in big panning operations which are
|
|
|
|
// almost certainly intended to be on-axis horizontal or
|
|
|
|
// vertical pans.
|
|
|
|
if (Math.abs(dx) > 40 || Math.abs(dy) > 40) {
|
|
|
|
if (Math.abs(dx/dy) < 0.3) // dx is a lot less than dy, probably a vertical drag
|
|
|
|
dx = 0;
|
|
|
|
else if (Math.abs(dy/dx) < 0.3) // probably a horizontal drag
|
|
|
|
dy = 0;
|
|
|
|
}
|
2008-08-15 10:02:52 -07:00
|
|
|
let now = Date.now();
|
|
|
|
if (this.deckbrowser.dragData.dragging) {
|
|
|
|
this.deckbrowser._panEventTrackerIndex = (this.deckbrowser._panEventTrackerIndex + 1) % this.deckbrowser.PAN_EVENTS_TO_TRACK;
|
2008-08-19 23:09:22 -07:00
|
|
|
|
2008-08-15 10:02:52 -07:00
|
|
|
var pt = new Object();
|
|
|
|
pt.x = aEvent.screenX;
|
|
|
|
pt.y = aEvent.screenY;
|
|
|
|
pt.t = now;
|
|
|
|
|
|
|
|
this.deckbrowser._panEventTracker[this.deckbrowser._panEventTrackerIndex] = pt;
|
2008-08-19 23:09:22 -07:00
|
|
|
|
2008-08-15 10:02:52 -07:00
|
|
|
}
|
2008-07-11 15:22:38 -07:00
|
|
|
this.deckbrowser._moveCanvas(dx, dy);
|
2008-06-21 11:51:41 -07:00
|
|
|
|
2008-08-15 10:02:52 -07:00
|
|
|
if (now - this.deckbrowser.dragData.lastMouseEvent < 75) { // FIXME: make this a constant
|
2008-06-21 11:51:41 -07:00
|
|
|
//dump("dropping event\n");
|
|
|
|
return false;
|
2008-05-22 13:46:20 -07:00
|
|
|
}
|
|
|
|
|
2008-08-15 10:02:52 -07:00
|
|
|
this.deckbrowser.dragData.lastMouseEvent = now;
|
2008-06-21 11:51:41 -07:00
|
|
|
|
|
|
|
aEvent.preventDefault();
|
2008-07-11 15:22:38 -07:00
|
|
|
return true;
|
2008-06-21 11:51:41 -07:00
|
|
|
},
|
2008-07-11 15:22:38 -07:00
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
DOMMouseScroll: function seh_DOMMouseScroll(aEvent) {
|
|
|
|
this.deckbrowser.zoom(aEvent.detail);
|
|
|
|
},
|
2008-07-11 15:22:38 -07:00
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
dblclick: function seh_dblclick(aEvent) {
|
2008-07-11 15:22:38 -07:00
|
|
|
var target = aEvent.originalTarget;
|
2008-07-16 03:21:24 -07:00
|
|
|
var dragData = this.deckbrowser.dragData;
|
2008-07-11 15:22:38 -07:00
|
|
|
|
2008-06-21 11:51:41 -07:00
|
|
|
if (this.deckbrowser._zoomed) {
|
2008-07-16 03:21:24 -07:00
|
|
|
// reset zoom, pan state
|
2008-07-16 06:36:28 -07:00
|
|
|
this.deckbrowser._zoomLevel = this._oldZoomLevel;
|
2008-07-16 03:21:24 -07:00
|
|
|
[dragData.pageX, dragData.pageY] = [dragData.oldPageX, dragData.oldPageY];
|
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
this.deckbrowser._browserToCanvas();
|
2008-06-21 11:51:41 -07:00
|
|
|
this.deckbrowser._zoomed = false;
|
|
|
|
} else {
|
2008-07-22 06:11:56 -07:00
|
|
|
var element = this.deckbrowser.elementFromPoint(aEvent.clientX, aEvent.clientY);
|
2008-08-19 22:30:07 -07:00
|
|
|
if (!element) {
|
|
|
|
Components.utils.reportError("elementFromPoint returned null\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the nearest non-inline ancestor
|
|
|
|
while (element.parentNode) {
|
|
|
|
var display = window.getComputedStyle(element, "").getPropertyValue("display");
|
|
|
|
var zoomable = /table/.test(display) || /block/.test(display);
|
|
|
|
if (zoomable)
|
|
|
|
break;
|
|
|
|
|
|
|
|
element = element.parentNode;
|
|
|
|
}
|
2008-07-22 06:11:56 -07:00
|
|
|
|
2008-07-16 03:21:24 -07:00
|
|
|
// Remember pageX/pageY
|
|
|
|
[dragData.oldPageX, dragData.oldPageY] = [dragData.pageX, dragData.pageY];
|
2008-07-16 06:36:28 -07:00
|
|
|
this._oldZoomLevel = this.deckbrowser._zoomLevel;
|
2008-07-16 03:21:24 -07:00
|
|
|
|
2008-07-11 15:22:38 -07:00
|
|
|
this.deckbrowser.zoomToElement(element);
|
2008-06-21 11:51:41 -07:00
|
|
|
this.deckbrowser._zoomed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
]]>
|
|
|
|
</field>
|
2008-04-18 06:41:49 -07:00
|
|
|
</implementation>
|
|
|
|
</binding>
|
|
|
|
|
2008-07-24 11:50:32 -07:00
|
|
|
<binding id="documenttab"
|
|
|
|
extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
|
|
|
|
<content>
|
|
|
|
<xul:stack anonid="page" class="documenttab-container" flex="1">
|
|
|
|
<html:canvas anonid="canvas" class="documenttab-canvas" width="80" height="60"/>
|
|
|
|
<xul:vbox align="start">
|
|
|
|
<xul:image anonid="close" class="documenttab-close"/>
|
|
|
|
</xul:vbox>
|
|
|
|
</xul:stack>
|
|
|
|
</content>
|
|
|
|
<implementation>
|
|
|
|
<method name="updateTab">
|
|
|
|
<parameter name="browser"/>
|
|
|
|
<body>
|
|
|
|
<![CDATA[
|
|
|
|
let canvas = document.getAnonymousElementByAttribute(this, "anonid", "canvas");
|
|
|
|
|
|
|
|
let domWin = browser.contentWindow;
|
|
|
|
let width = domWin.innerWidth;
|
|
|
|
let height = domWin.innerHeight;
|
|
|
|
|
|
|
|
let ctx = canvas.getContext("2d");
|
|
|
|
ctx.clearRect(0, 0, 80, 60);
|
|
|
|
ctx.restore(); // XXXndeakin remove this
|
|
|
|
ctx.save();
|
|
|
|
ctx.scale(80 / width, 60 / height);
|
|
|
|
ctx.drawWindow(domWin, 0, 0, width, height, "rgba(0,0,0,0)");
|
|
|
|
ctx.restore();
|
|
|
|
]]>
|
|
|
|
</body>
|
|
|
|
</method>
|
|
|
|
<method name="markInvalid">
|
|
|
|
<parameter name="browser"/>
|
|
|
|
<body>
|
|
|
|
<![CDATA[
|
|
|
|
let canvas = document.getAnonymousElementByAttribute(this, "anonid", "canvas");
|
|
|
|
let ctx = canvas.getContext("2d");
|
|
|
|
|
|
|
|
ctx.save();
|
|
|
|
ctx.strokeStyle = "red";
|
|
|
|
ctx.moveTo(63, 43);
|
|
|
|
ctx.lineTo(78, 58);
|
|
|
|
ctx.moveTo(78, 43);
|
|
|
|
ctx.lineTo(63, 58);
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.restore();
|
|
|
|
]]>
|
|
|
|
</body>
|
|
|
|
</method>
|
|
|
|
</implementation>
|
|
|
|
</binding>
|
|
|
|
|
2008-04-18 06:41:49 -07:00
|
|
|
</bindings>
|