2009-03-16 21:27:29 -07:00
|
|
|
let Cc = Components.classes;
|
|
|
|
let Ci = Components.interfaces;
|
|
|
|
let Cu = Components.utils;
|
2010-11-11 10:48:00 -08:00
|
|
|
let Cr = Components.results;
|
2008-04-18 06:41:49 -07:00
|
|
|
|
2011-10-10 17:40:17 -07:00
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
Cu.import("resource://gre/modules/NetUtil.jsm");
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm")
|
2010-05-21 10:00:27 -07:00
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
// TODO: Take into account ppi in these units?
|
|
|
|
|
2011-10-17 12:02:27 -07:00
|
|
|
// The ratio of velocity that is retained every ms.
|
|
|
|
const kPanDeceleration = 0.999;
|
|
|
|
|
|
|
|
// The number of ms to consider events over for a swipe gesture.
|
2011-10-19 09:17:59 -07:00
|
|
|
const kSwipeLength = 500;
|
|
|
|
|
|
|
|
// The number of pixels to move before we consider a drag to be more than
|
|
|
|
// just a click with jitter.
|
|
|
|
const kDragThreshold = 10;
|
|
|
|
|
|
|
|
// The number of pixels to move to break out of axis-lock
|
|
|
|
const kLockBreakThreshold = 100;
|
2011-10-17 12:02:27 -07:00
|
|
|
|
|
|
|
// Minimum speed to move during kinetic panning. 0.015 pixels/ms is roughly
|
|
|
|
// equivalent to a pixel every 4 frames at 60fps.
|
|
|
|
const kMinKineticSpeed = 0.015;
|
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
// Maximum kinetic panning speed. 9 pixels/ms is equivalent to 150 pixels per
|
2011-10-17 12:02:27 -07:00
|
|
|
// frame at 60fps.
|
2011-10-19 09:17:59 -07:00
|
|
|
const kMaxKineticSpeed = 9;
|
2011-10-17 12:02:27 -07:00
|
|
|
|
|
|
|
// The maximum magnitude of disparity allowed between axes acceleration. If
|
|
|
|
// it's larger than this, lock the slow-moving axis.
|
|
|
|
const kAxisLockRatio = 5;
|
|
|
|
|
2011-10-10 17:40:17 -07:00
|
|
|
function dump(a) {
|
2011-10-12 14:31:04 -07:00
|
|
|
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a);
|
|
|
|
}
|
|
|
|
|
2011-10-13 13:47:29 -07:00
|
|
|
function sendMessageToJava(aMessage) {
|
2011-10-12 14:31:04 -07:00
|
|
|
let bridge = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge);
|
2011-10-24 10:05:18 -07:00
|
|
|
return bridge.handleGeckoMessage(JSON.stringify(aMessage));
|
2009-07-17 16:17:57 -07:00
|
|
|
}
|
|
|
|
|
2011-10-24 14:08:21 -07:00
|
|
|
function resolveGeckoURI(aURI) {
|
|
|
|
if (aURI.indexOf("chrome://") == 0) {
|
|
|
|
let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]);
|
|
|
|
return registry.convertChromeURL(Services.io.newURI(aURI, null, null)).spec;
|
|
|
|
} else if (aURI.indexOf("resource://") == 0) {
|
|
|
|
let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
|
|
|
|
return handler.resolveURI(Services.io.newURI(aURI, null, null));
|
|
|
|
}
|
|
|
|
return aURI;
|
|
|
|
}
|
2011-10-13 13:06:41 -07:00
|
|
|
|
|
|
|
var BrowserApp = {
|
|
|
|
_tabs: [],
|
|
|
|
_selectedTab: null,
|
|
|
|
|
|
|
|
deck: null,
|
|
|
|
|
|
|
|
startup: function startup() {
|
|
|
|
window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
|
|
|
|
dump("zerdatime " + Date.now() + " - browser chrome startup finished.");
|
|
|
|
|
|
|
|
this.deck = document.getElementById("browsers");
|
|
|
|
BrowserEventHandler.init();
|
|
|
|
|
2011-10-24 12:20:27 -07:00
|
|
|
Services.obs.addObserver(this, "Tab:Add", false);
|
|
|
|
Services.obs.addObserver(this, "Tab:Load", false);
|
|
|
|
Services.obs.addObserver(this, "Tab:Select", false);
|
|
|
|
Services.obs.addObserver(this, "Tab:Close", false);
|
2011-10-13 11:13:41 -07:00
|
|
|
Services.obs.addObserver(this, "session-back", false);
|
|
|
|
Services.obs.addObserver(this, "session-reload", false);
|
2011-10-13 13:06:41 -07:00
|
|
|
|
|
|
|
let uri = "about:support";
|
2011-10-15 13:27:03 -07:00
|
|
|
if ("arguments" in window && window.arguments[0])
|
|
|
|
uri = window.arguments[0];
|
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
// XXX maybe we don't do this if the launch was kicked off from external
|
|
|
|
Services.io.offline = false;
|
|
|
|
let newTab = this.addTab(uri);
|
|
|
|
newTab.active = true;
|
2011-10-19 11:17:00 -07:00
|
|
|
|
|
|
|
Downloads.init();
|
2011-10-24 17:34:17 -07:00
|
|
|
|
|
|
|
// Broadcast a UIReady message so add-ons know we are finished with startup
|
|
|
|
let event = document.createEvent("Events");
|
|
|
|
event.initEvent("UIReady", true, false);
|
|
|
|
window.dispatchEvent(event);
|
2011-10-13 11:13:41 -07:00
|
|
|
},
|
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
shutdown: function shutdown() {
|
2011-10-13 11:13:41 -07:00
|
|
|
},
|
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
get tabs() {
|
|
|
|
return this._tabs;
|
|
|
|
},
|
2011-10-10 17:40:17 -07:00
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
get selectedTab() {
|
|
|
|
return this._selectedTab;
|
|
|
|
},
|
2011-10-10 17:40:17 -07:00
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
set selectedTab(aTab) {
|
|
|
|
this._selectedTab = aTab;
|
|
|
|
if (!aTab)
|
|
|
|
return;
|
2010-09-22 16:27:11 -07:00
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
this.deck.selectedPanel = aTab.browser;
|
|
|
|
},
|
2009-08-05 14:06:38 -07:00
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
get selectedBrowser() {
|
|
|
|
if (this._selectedTab)
|
|
|
|
return this._selectedTab.browser;
|
|
|
|
return null;
|
|
|
|
},
|
2010-11-01 16:36:07 -07:00
|
|
|
|
2011-10-14 22:35:15 -07:00
|
|
|
getTabForId: function getTabForId(aId) {
|
|
|
|
let tabs = this._tabs;
|
|
|
|
for (let i=0; i < tabs.length; i++) {
|
|
|
|
if (tabs[i].id == aId)
|
|
|
|
return tabs[i];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
getTabForBrowser: function getTabForBrowser(aBrowser) {
|
|
|
|
let tabs = this._tabs;
|
|
|
|
for (let i = 0; i < tabs.length; i++) {
|
|
|
|
if (tabs[i].browser == aBrowser)
|
|
|
|
return tabs[i];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
|
|
|
getBrowserForWindow: function getBrowserForWindow(aWindow) {
|
|
|
|
let tabs = this._tabs;
|
|
|
|
for (let i = 0; i < tabs.length; i++) {
|
|
|
|
if (tabs[i].browser.contentWindow == aWindow)
|
|
|
|
return tabs[i].browser;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
|
|
|
getBrowserForDocument: function getBrowserForDocument(aDocument) {
|
|
|
|
let tabs = this._tabs;
|
|
|
|
for (let i = 0; i < tabs.length; i++) {
|
|
|
|
if (tabs[i].browser.contentDocument == aDocument)
|
|
|
|
return tabs[i].browser;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
2010-11-16 14:50:34 -08:00
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
loadURI: function loadURI(aURI, aParams) {
|
|
|
|
let browser = this.selectedBrowser;
|
|
|
|
if (!browser)
|
|
|
|
return;
|
2010-11-16 14:50:34 -08:00
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
let flags = aParams.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
|
|
|
let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null;
|
|
|
|
let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null;
|
|
|
|
let charset = "charset" in aParams ? aParams.charset : null;
|
|
|
|
browser.loadURIWithFlags(aURI, flags, referrerURI, charset, postData);
|
|
|
|
},
|
|
|
|
|
|
|
|
addTab: function addTab(aURI) {
|
|
|
|
let newTab = new Tab(aURI);
|
|
|
|
this._tabs.push(newTab);
|
|
|
|
return newTab;
|
|
|
|
},
|
|
|
|
|
|
|
|
closeTab: function closeTab(aTab) {
|
|
|
|
if (aTab == this.selectedTab)
|
|
|
|
this.selectedTab = null;
|
|
|
|
|
|
|
|
aTab.destroy();
|
|
|
|
this._tabs.splice(this._tabs.indexOf(aTab), 1);
|
|
|
|
},
|
|
|
|
|
2011-10-24 12:20:27 -07:00
|
|
|
selectTab: function selectTab(aTab) {
|
|
|
|
if (aTab != null) {
|
|
|
|
this.selectedTab = aTab;
|
|
|
|
aTab.active = true;
|
|
|
|
let message = {
|
|
|
|
gecko: {
|
|
|
|
type: "Tab:Selected",
|
|
|
|
tabID: aTab.id
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
sendMessageToJava(message);
|
2011-10-14 23:22:25 -07:00
|
|
|
}
|
2011-10-14 22:35:15 -07:00
|
|
|
},
|
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
observe: function(aSubject, aTopic, aData) {
|
|
|
|
let browser = this.selectedBrowser;
|
|
|
|
if (!browser)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (aTopic == "session-back")
|
|
|
|
browser.goBack();
|
|
|
|
else if (aTopic == "session-reload")
|
|
|
|
browser.reload();
|
2011-10-24 12:20:27 -07:00
|
|
|
else if (aTopic == "Tab:Add") {
|
2011-10-14 22:35:15 -07:00
|
|
|
let newTab = this.addTab(aData);
|
|
|
|
newTab.active = true;
|
2011-10-24 12:20:27 -07:00
|
|
|
} else if (aTopic == "Tab:Load")
|
2011-10-14 22:35:15 -07:00
|
|
|
browser.loadURI(aData);
|
2011-10-24 12:20:27 -07:00
|
|
|
else if (aTopic == "Tab:Select")
|
|
|
|
this.selectTab(this.getTabForId(parseInt(aData)));
|
|
|
|
else if (aTopic == "Tab:Close")
|
|
|
|
this.closeTab(this.getTabForId(parseInt(aData)));
|
2011-10-13 13:06:41 -07:00
|
|
|
}
|
2011-10-10 17:40:17 -07:00
|
|
|
}
|
2008-11-21 21:12:25 -08:00
|
|
|
|
2010-03-26 08:11:03 -07:00
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
function nsBrowserAccess() {
|
2011-10-10 17:40:17 -07:00
|
|
|
}
|
2010-06-09 16:07:12 -07:00
|
|
|
|
2011-10-10 17:40:17 -07:00
|
|
|
nsBrowserAccess.prototype = {
|
|
|
|
openURI: function browser_openURI(aURI, aOpener, aWhere, aContext) {
|
2011-10-12 14:31:04 -07:00
|
|
|
dump("nsBrowserAccess::openURI");
|
2011-10-13 13:06:41 -07:00
|
|
|
let browser = BrowserApp.selectedBrowser;
|
|
|
|
if (!browser)
|
|
|
|
browser = BrowserApp.addTab("about:blank").browser;
|
2011-10-12 14:31:04 -07:00
|
|
|
|
|
|
|
// Why does returning the browser.contentWindow not work here?
|
|
|
|
Services.io.offline = false;
|
|
|
|
browser.loadURI(aURI.spec, null, null);
|
|
|
|
return null;
|
2009-01-08 22:51:13 -08:00
|
|
|
},
|
|
|
|
|
2011-10-10 17:40:17 -07:00
|
|
|
openURIInFrame: function browser_openURIInFrame(aURI, aOpener, aWhere, aContext) {
|
2011-10-12 14:31:04 -07:00
|
|
|
dump("nsBrowserAccess::openURIInFrame");
|
|
|
|
return null;
|
2011-03-02 06:13:25 -08:00
|
|
|
},
|
|
|
|
|
2011-10-10 17:40:17 -07:00
|
|
|
isTabContentWindow: function(aWindow) {
|
2011-10-13 13:06:41 -07:00
|
|
|
return BrowserApp.getBrowserForWindow(aWindow) != null;
|
2008-07-11 11:51:28 -07:00
|
|
|
},
|
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow])
|
2011-10-10 17:40:17 -07:00
|
|
|
};
|
2008-04-18 06:41:49 -07:00
|
|
|
|
2011-10-14 23:22:25 -07:00
|
|
|
|
|
|
|
let gTabIDFactory = 0;
|
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
function Tab(aURL) {
|
|
|
|
this.browser = null;
|
2011-10-14 23:22:25 -07:00
|
|
|
this.id = 0;
|
2011-10-13 13:06:41 -07:00
|
|
|
this.create(aURL);
|
2011-10-10 17:40:17 -07:00
|
|
|
}
|
2009-11-30 12:19:24 -08:00
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
Tab.prototype = {
|
|
|
|
create: function(aURL) {
|
|
|
|
if (this.browser)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.browser = document.createElement("browser");
|
|
|
|
this.browser.setAttribute("type", "content");
|
|
|
|
BrowserApp.deck.appendChild(this.browser);
|
|
|
|
this.browser.stop();
|
|
|
|
|
|
|
|
let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
|
|
|
|
Ci.nsIWebProgress.NOTIFY_LOCATION |
|
|
|
|
Ci.nsIWebProgress.NOTIFY_PROGRESS;
|
|
|
|
this.browser.addProgressListener(this, flags);
|
|
|
|
this.browser.loadURI(aURL);
|
2011-10-14 23:22:25 -07:00
|
|
|
|
|
|
|
this.id = ++gTabIDFactory;
|
2011-10-14 22:35:15 -07:00
|
|
|
let message = {
|
|
|
|
gecko: {
|
2011-10-24 12:20:27 -07:00
|
|
|
type: "Tab:Added",
|
2011-10-14 23:22:25 -07:00
|
|
|
tabID: this.id,
|
2011-10-14 22:35:15 -07:00
|
|
|
uri: aURL
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
sendMessageToJava(message);
|
2011-10-13 13:06:41 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
destroy: function() {
|
|
|
|
if (!this.browser)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.browser.removeProgressListener(this);
|
|
|
|
BrowserApp.deck.removeChild(this.browser);
|
|
|
|
this.browser = null;
|
2011-10-24 12:20:27 -07:00
|
|
|
let message = {
|
|
|
|
gecko: {
|
|
|
|
type: "Tab:Closed",
|
|
|
|
tabID: this.id
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
sendMessageToJava(message);
|
2011-10-13 13:06:41 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
set active(aActive) {
|
|
|
|
if (!this.browser)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (aActive) {
|
|
|
|
this.browser.setAttribute("type", "content-primary");
|
2011-10-20 15:48:26 -07:00
|
|
|
this.browser.focus();
|
2011-10-13 13:06:41 -07:00
|
|
|
BrowserApp.selectedTab = this;
|
|
|
|
} else {
|
|
|
|
this.browser.setAttribute("type", "content");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
get active() {
|
|
|
|
if (!this.browser)
|
|
|
|
return false;
|
|
|
|
return this.browser.getAttribute("type") == "content-primary";
|
|
|
|
},
|
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
|
2011-10-14 22:35:15 -07:00
|
|
|
let browser = BrowserApp.getBrowserForWindow(aWebProgress.DOMWindow);
|
2011-10-13 13:06:41 -07:00
|
|
|
let windowID = aWebProgress.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
2011-10-14 22:35:15 -07:00
|
|
|
|
|
|
|
let uri = "";
|
|
|
|
if (browser)
|
|
|
|
uri = browser.currentURI.spec;
|
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
let message = {
|
|
|
|
gecko: {
|
|
|
|
type: "onStateChange",
|
2011-10-14 23:22:25 -07:00
|
|
|
tabID: this.id,
|
2011-10-12 14:31:04 -07:00
|
|
|
windowID: windowID,
|
2011-10-14 22:35:15 -07:00
|
|
|
uri: uri,
|
2011-10-12 14:31:04 -07:00
|
|
|
state: aStateFlags
|
|
|
|
}
|
|
|
|
};
|
2011-10-10 17:40:17 -07:00
|
|
|
|
2011-10-13 13:47:29 -07:00
|
|
|
sendMessageToJava(message);
|
2011-10-12 14:31:04 -07:00
|
|
|
},
|
2011-10-10 17:40:17 -07:00
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
onLocationChange: function(aWebProgress, aRequest, aLocationURI) {
|
2011-10-14 13:07:17 -07:00
|
|
|
let browser = BrowserApp.getBrowserForWindow(aWebProgress.DOMWindow);
|
|
|
|
let uri = browser.currentURI.spec;
|
|
|
|
let windowID = aWebProgress.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
|
|
|
|
|
|
|
let message = {
|
|
|
|
gecko: {
|
|
|
|
type: "onLocationChange",
|
2011-10-14 23:22:25 -07:00
|
|
|
tabID: this.id,
|
2011-10-14 13:07:17 -07:00
|
|
|
windowID: windowID,
|
|
|
|
uri: uri
|
|
|
|
}
|
|
|
|
};
|
2011-10-12 14:31:04 -07:00
|
|
|
|
2011-10-14 13:07:17 -07:00
|
|
|
sendMessageToJava(message);
|
2011-10-12 14:31:04 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
onSecurityChange: function(aBrowser, aWebProgress, aRequest, aState) {
|
|
|
|
dump("progressListener.onSecurityChange");
|
|
|
|
},
|
|
|
|
|
|
|
|
onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
|
2011-10-13 13:06:41 -07:00
|
|
|
let windowID = aWebProgress.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
2011-10-12 14:31:04 -07:00
|
|
|
let message = {
|
|
|
|
gecko: {
|
|
|
|
type: "onProgressChange",
|
2011-10-14 23:22:25 -07:00
|
|
|
tabID: this.id,
|
2011-10-12 14:31:04 -07:00
|
|
|
windowID: windowID,
|
|
|
|
current: aCurTotalProgress,
|
|
|
|
total: aMaxTotalProgress
|
|
|
|
}
|
|
|
|
};
|
2010-11-16 14:50:34 -08:00
|
|
|
|
2011-10-13 13:47:29 -07:00
|
|
|
sendMessageToJava(message);
|
2011-10-12 14:31:04 -07:00
|
|
|
},
|
|
|
|
onStatusChange: function(aBrowser, aWebProgress, aRequest, aStatus, aMessage) {
|
2011-10-19 09:17:59 -07:00
|
|
|
//dump("progressListener.onStatusChange");
|
2011-10-12 14:31:04 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference])
|
2011-10-10 17:40:17 -07:00
|
|
|
};
|
2010-04-08 07:43:04 -07:00
|
|
|
|
2009-08-12 14:40:43 -07:00
|
|
|
|
2011-10-13 13:06:41 -07:00
|
|
|
var BrowserEventHandler = {
|
|
|
|
init: function init() {
|
|
|
|
window.addEventListener("click", this, true);
|
|
|
|
window.addEventListener("mousedown", this, true);
|
|
|
|
window.addEventListener("mouseup", this, true);
|
|
|
|
window.addEventListener("mousemove", this, true);
|
|
|
|
|
|
|
|
BrowserApp.deck.addEventListener("MozMagnifyGestureStart", this, true);
|
|
|
|
BrowserApp.deck.addEventListener("MozMagnifyGestureUpdate", this, true);
|
|
|
|
BrowserApp.deck.addEventListener("DOMContentLoaded", this, true);
|
2011-10-14 13:07:17 -07:00
|
|
|
BrowserApp.deck.addEventListener("DOMLinkAdded", this, true);
|
|
|
|
BrowserApp.deck.addEventListener("DOMTitleChanged", this, true);
|
2011-10-13 13:06:41 -07:00
|
|
|
},
|
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
handleEvent: function(aEvent) {
|
|
|
|
switch (aEvent.type) {
|
2011-10-13 13:06:41 -07:00
|
|
|
case "DOMContentLoaded": {
|
|
|
|
let browser = BrowserApp.getBrowserForDocument(aEvent.target);
|
2011-10-20 15:48:26 -07:00
|
|
|
browser.focus();
|
|
|
|
|
2011-10-14 22:35:15 -07:00
|
|
|
let tabID = BrowserApp.getTabForBrowser(browser).id;
|
2011-10-14 11:23:41 -07:00
|
|
|
|
2011-10-13 13:47:29 -07:00
|
|
|
sendMessageToJava({
|
2011-10-12 14:31:04 -07:00
|
|
|
gecko: {
|
|
|
|
type: "DOMContentLoaded",
|
2011-10-14 22:35:15 -07:00
|
|
|
tabID: tabID,
|
2011-10-12 14:31:04 -07:00
|
|
|
windowID: 0,
|
2011-10-23 21:11:29 -07:00
|
|
|
uri: browser.currentURI.spec,
|
2011-10-13 13:06:41 -07:00
|
|
|
title: browser.contentTitle
|
2011-10-12 14:31:04 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
2011-10-13 13:06:41 -07:00
|
|
|
}
|
2011-10-14 13:07:17 -07:00
|
|
|
|
2011-10-14 22:35:15 -07:00
|
|
|
case "DOMLinkAdded": {
|
2011-10-14 13:07:17 -07:00
|
|
|
let target = aEvent.originalTarget;
|
|
|
|
if (!target.href || target.disabled)
|
|
|
|
return;
|
2011-10-14 22:35:15 -07:00
|
|
|
|
2011-10-24 12:20:27 -07:00
|
|
|
let browser = BrowserApp.getBrowserForDocument(target.ownerDocument);
|
|
|
|
let tabID = BrowserApp.getTabForBrowser(browser).id;
|
|
|
|
|
2011-10-14 13:07:17 -07:00
|
|
|
let json = {
|
|
|
|
type: "DOMLinkAdded",
|
2011-10-24 12:20:27 -07:00
|
|
|
tabID: tabID,
|
2011-10-14 13:07:17 -07:00
|
|
|
windowId: target.ownerDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
|
2011-10-24 14:08:21 -07:00
|
|
|
href: resolveGeckoURI(target.href),
|
2011-10-14 13:07:17 -07:00
|
|
|
charset: target.ownerDocument.characterSet,
|
|
|
|
title: target.title,
|
|
|
|
rel: target.rel
|
|
|
|
};
|
|
|
|
|
|
|
|
// rel=icon can also have a sizes attribute
|
|
|
|
if (target.hasAttribute("sizes"))
|
|
|
|
json.sizes = target.getAttribute("sizes");
|
|
|
|
|
|
|
|
sendMessageToJava({ gecko: json });
|
|
|
|
break;
|
2011-10-14 22:35:15 -07:00
|
|
|
}
|
2011-10-14 13:07:17 -07:00
|
|
|
|
2011-10-14 22:35:15 -07:00
|
|
|
case "DOMTitleChanged": {
|
|
|
|
let browser = BrowserApp.getBrowserForDocument(aEvent.target);
|
|
|
|
let tabID = BrowserApp.getTabForBrowser(browser).id;
|
2011-10-14 13:07:17 -07:00
|
|
|
sendMessageToJava({
|
|
|
|
gecko: {
|
|
|
|
type: "DOMTitleChanged",
|
2011-10-14 22:35:15 -07:00
|
|
|
tabID: tabID,
|
2011-10-14 13:07:17 -07:00
|
|
|
title: aEvent.target.title
|
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
2011-10-14 22:35:15 -07:00
|
|
|
}
|
2011-10-14 13:07:17 -07:00
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
case "click":
|
|
|
|
if (this.blockClick) {
|
|
|
|
aEvent.stopPropagation();
|
|
|
|
aEvent.preventDefault();
|
2011-10-10 17:40:17 -07:00
|
|
|
}
|
2011-10-12 14:31:04 -07:00
|
|
|
break;
|
2011-10-14 13:07:17 -07:00
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
case "mousedown":
|
|
|
|
this.startX = aEvent.clientX;
|
|
|
|
this.startY = aEvent.clientY;
|
|
|
|
this.blockClick = false;
|
2011-10-19 09:17:59 -07:00
|
|
|
|
2011-10-15 14:44:05 -07:00
|
|
|
this.firstMovement = true;
|
2011-10-19 09:17:59 -07:00
|
|
|
this.edx = 0;
|
|
|
|
this.edy = 0;
|
|
|
|
this.lockXaxis = false;
|
|
|
|
this.lockYaxis = false;
|
2011-10-17 12:02:27 -07:00
|
|
|
|
|
|
|
this.motionBuffer = [];
|
2011-10-15 14:44:05 -07:00
|
|
|
this._updateLastPosition(aEvent.clientX, aEvent.clientY, 0, 0);
|
|
|
|
this.panElement = this._findScrollableElement(aEvent.originalTarget,
|
|
|
|
true);
|
|
|
|
|
|
|
|
if (this.panElement)
|
|
|
|
this.panning = true;
|
2011-10-20 15:48:26 -07:00
|
|
|
break;
|
2011-10-13 08:13:59 -07:00
|
|
|
|
2011-10-20 15:48:26 -07:00
|
|
|
case "mousemove":
|
2011-10-12 14:31:04 -07:00
|
|
|
aEvent.stopPropagation();
|
|
|
|
aEvent.preventDefault();
|
2011-10-14 13:07:17 -07:00
|
|
|
|
2011-10-15 14:44:05 -07:00
|
|
|
if (!this.panning)
|
|
|
|
break;
|
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
this.edx += aEvent.clientX - this.lastX;
|
|
|
|
this.edy += aEvent.clientY - this.lastY;
|
2011-10-15 14:44:05 -07:00
|
|
|
|
|
|
|
// If this is the first panning motion, check if we can
|
|
|
|
// move in the given direction. If we can't, find an
|
|
|
|
// ancestor that can.
|
|
|
|
// We do this per-axis, as often the very first movement doesn't
|
|
|
|
// reflect the direction of movement for both axes.
|
|
|
|
if (this.firstMovement &&
|
2011-10-19 09:17:59 -07:00
|
|
|
(Math.abs(this.edx) > kDragThreshold ||
|
|
|
|
Math.abs(this.edy) > kDragThreshold)) {
|
2011-10-15 14:44:05 -07:00
|
|
|
this.firstMovement = false;
|
|
|
|
let originalElement = this.panElement;
|
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
// Decide if we want to lock an axis while scrolling
|
|
|
|
if (Math.abs(this.edx) > Math.abs(this.edy) * kAxisLockRatio)
|
|
|
|
this.lockYaxis = true;
|
|
|
|
else if (Math.abs(this.edy) > Math.abs(this.edx) * kAxisLockRatio)
|
|
|
|
this.lockXaxis = true;
|
|
|
|
|
2011-10-15 14:44:05 -07:00
|
|
|
// See if we've reached the extents of this element and if so,
|
|
|
|
// find an element above it that can be scrolled.
|
|
|
|
while (this.panElement &&
|
|
|
|
!this._elementCanScroll(this.panElement,
|
2011-10-19 09:17:59 -07:00
|
|
|
-this.edx,
|
|
|
|
-this.edy)) {
|
2011-10-15 14:44:05 -07:00
|
|
|
this.panElement =
|
|
|
|
this._findScrollableElement(this.panElement, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are no scrollable elements above the element whose
|
|
|
|
// extents we've reached, let that element be scrolled.
|
|
|
|
if (!this.panElement)
|
|
|
|
this.panElement = originalElement;
|
|
|
|
}
|
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
// Only scroll once we've moved past the drag threshold
|
|
|
|
if (!this.firstMovement) {
|
|
|
|
this._scrollElementBy(this.panElement,
|
|
|
|
this.lockXaxis ? 0 : -this.edx,
|
|
|
|
this.lockYaxis ? 0 : -this.edy);
|
|
|
|
|
|
|
|
// Note, it's important that this happens after the scrollElementBy,
|
|
|
|
// as this will modify the clientX/clientY to be relative to the
|
|
|
|
// correct element.
|
|
|
|
this._updateLastPosition(aEvent.clientX, aEvent.clientY,
|
|
|
|
this.lockXaxis ? 0 : this.edx,
|
|
|
|
this.lockYaxis ? 0 : this.edy);
|
|
|
|
|
|
|
|
// Allow breaking out of axis lock if we move past a certain threshold
|
|
|
|
if (this.lockXaxis) {
|
|
|
|
if (Math.abs(this.edx) > kLockBreakThreshold)
|
|
|
|
this.lockXaxis = false;
|
|
|
|
} else {
|
|
|
|
this.edx = 0;
|
|
|
|
}
|
2011-10-15 14:44:05 -07:00
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
if (this.lockYaxis) {
|
|
|
|
if (Math.abs(this.edy) > kLockBreakThreshold)
|
|
|
|
this.lockYaxis = false;
|
|
|
|
} else {
|
|
|
|
this.edy = 0;
|
|
|
|
}
|
|
|
|
}
|
2011-10-12 14:31:04 -07:00
|
|
|
break;
|
2011-10-14 13:07:17 -07:00
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
case "mouseup":
|
2011-10-15 14:44:05 -07:00
|
|
|
if (!this.panning)
|
|
|
|
break;
|
|
|
|
|
2011-10-13 08:13:59 -07:00
|
|
|
this.panning = false;
|
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
if (Math.abs(aEvent.clientX - this.startX) > kDragThreshold ||
|
|
|
|
Math.abs(aEvent.clientY - this.startY) > kDragThreshold) {
|
2011-10-12 14:31:04 -07:00
|
|
|
this.blockClick = true;
|
2011-10-15 14:44:05 -07:00
|
|
|
aEvent.stopPropagation();
|
|
|
|
aEvent.preventDefault();
|
2011-10-13 08:13:59 -07:00
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
// Calculate a regression line for the last few motion events in
|
|
|
|
// the same direction to estimate the velocity. This ought to do a
|
|
|
|
// reasonable job of accounting for jitter/bad events.
|
|
|
|
this.panLastTime = Date.now();
|
|
|
|
|
|
|
|
// Variables required for calculating regression on each axis
|
|
|
|
// 'p' will be sum of positions
|
|
|
|
// 't' will be sum of times
|
|
|
|
// 'tp' will be sum of times * positions
|
|
|
|
// 'tt' will be sum of time^2's
|
|
|
|
// 'n' is the number of data-points
|
|
|
|
let xSums = { p: 0, t: 0, tp: 0, tt: 0, n: 0 };
|
|
|
|
let ySums = { p: 0, t: 0, tp: 0, tt: 0, n: 0 };
|
|
|
|
let lastDx = 0;
|
|
|
|
let lastDy = 0;
|
|
|
|
|
|
|
|
// Variables to find the absolute x,y (relative to the first event)
|
|
|
|
let edx = 0; // Sum of x changes
|
|
|
|
let edy = 0; // Sum of y changes
|
|
|
|
|
|
|
|
// For convenience
|
|
|
|
let mb = this.motionBuffer;
|
|
|
|
|
|
|
|
// First collect the variables necessary to calculate the line
|
|
|
|
for (let i = 0; i < mb.length; i++) {
|
|
|
|
|
|
|
|
// Sum up total movement so far
|
|
|
|
let dx = edx + mb[i].dx;
|
|
|
|
let dy = edy + mb[i].dy;
|
|
|
|
edx += mb[i].dx;
|
|
|
|
edy += mb[i].dy;
|
|
|
|
|
|
|
|
// Don't consider events before direction changes
|
|
|
|
if ((xSums.n > 0) &&
|
|
|
|
((mb[i].dx < 0 && lastDx > 0) ||
|
|
|
|
(mb[i].dx > 0 && lastDx < 0))) {
|
|
|
|
xSums = { p: 0, t: 0, tp: 0, tt: 0, n: 0 };
|
|
|
|
}
|
|
|
|
if ((ySums.n > 0) &&
|
|
|
|
((mb[i].dy < 0 && lastDy > 0) ||
|
|
|
|
(mb[i].dy > 0 && lastDy < 0))) {
|
|
|
|
ySums = { p: 0, t: 0, tp: 0, tt: 0, n: 0 };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mb[i].dx != 0)
|
|
|
|
lastDx = mb[i].dx;
|
|
|
|
if (mb[i].dy != 0)
|
|
|
|
lastDy = mb[i].dy;
|
|
|
|
|
|
|
|
// Only consider events that happened in the last kSwipeLength ms
|
|
|
|
let timeDelta = this.panLastTime - mb[i].time;
|
|
|
|
if (timeDelta > kSwipeLength)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
xSums.p += dx;
|
|
|
|
xSums.t += timeDelta;
|
|
|
|
xSums.tp += timeDelta * dx;
|
|
|
|
xSums.tt += timeDelta * timeDelta;
|
|
|
|
xSums.n ++;
|
|
|
|
|
|
|
|
ySums.p += dy;
|
|
|
|
ySums.t += timeDelta;
|
|
|
|
ySums.tp += timeDelta * dy;
|
|
|
|
ySums.tt += timeDelta * timeDelta;
|
|
|
|
ySums.n ++;
|
2011-10-13 08:13:59 -07:00
|
|
|
}
|
2011-10-19 09:17:59 -07:00
|
|
|
|
|
|
|
// If we don't have enough usable motion events, bail out
|
|
|
|
if (xSums.n < 2 && ySums.n < 2)
|
2011-10-17 12:02:27 -07:00
|
|
|
break;
|
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
// Calculate the slope of the regression line.
|
|
|
|
// The intercept of the regression line is commented for reference.
|
|
|
|
let sx = 0;
|
|
|
|
if (xSums.n > 1)
|
|
|
|
sx = ((xSums.n * xSums.tp) - (xSums.t * xSums.p)) /
|
|
|
|
((xSums.n * xSums.tt) - (xSums.t * xSums.t));
|
|
|
|
//let ix = (xSums.p - (sx * xSums.t)) / xSums.n;
|
|
|
|
|
|
|
|
let sy = 0;
|
|
|
|
if (ySums.n > 1)
|
|
|
|
sy = ((ySums.n * ySums.tp) - (ySums.t * ySums.p)) /
|
|
|
|
((ySums.n * ySums.tt) - (ySums.t * ySums.t));
|
|
|
|
//let iy = (ySums.p - (sy * ySums.t)) / ySums.n;
|
|
|
|
|
|
|
|
// The slope of the regression line is the projected acceleration
|
|
|
|
this.panX = -sx;
|
|
|
|
this.panY = -sy;
|
|
|
|
|
2011-10-17 12:02:27 -07:00
|
|
|
if (Math.abs(this.panX) > kMaxKineticSpeed)
|
|
|
|
this.panX = (this.panX > 0) ? kMaxKineticSpeed : -kMaxKineticSpeed;
|
|
|
|
if (Math.abs(this.panY) > kMaxKineticSpeed)
|
|
|
|
this.panY = (this.panY > 0) ? kMaxKineticSpeed : -kMaxKineticSpeed;
|
|
|
|
|
|
|
|
// If we have (near) zero acceleration, bail out.
|
|
|
|
if (Math.abs(this.panX) < kMinKineticSpeed &&
|
|
|
|
Math.abs(this.panY) < kMinKineticSpeed)
|
2011-10-15 14:44:05 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
// Check if this element can scroll - if not, let's bail out.
|
|
|
|
if (!this.panElement || !this._elementCanScroll(
|
|
|
|
this.panElement, -this.panX, -this.panY))
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Fire off the first kinetic panning event
|
|
|
|
this._scrollElementBy(this.panElement, -this.panX, -this.panY);
|
2011-10-13 08:13:59 -07:00
|
|
|
|
|
|
|
// TODO: Use the kinetic scrolling prefs
|
|
|
|
this.panAccumulatedDeltaX = 0;
|
|
|
|
this.panAccumulatedDeltaY = 0;
|
|
|
|
|
|
|
|
let self = this;
|
2011-10-15 14:44:05 -07:00
|
|
|
let panElement = this.panElement;
|
2011-10-13 08:13:59 -07:00
|
|
|
let callback = {
|
|
|
|
onBeforePaint: function kineticPanCallback(timeStamp) {
|
2011-10-15 14:44:05 -07:00
|
|
|
if (self.panning || self.panElement != panElement)
|
2011-10-13 08:13:59 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
let timeDelta = timeStamp - self.panLastTime;
|
|
|
|
self.panLastTime = timeStamp;
|
|
|
|
|
|
|
|
// Adjust deceleration
|
2011-10-17 12:02:27 -07:00
|
|
|
self.panX *= Math.pow(kPanDeceleration, timeDelta);
|
|
|
|
self.panY *= Math.pow(kPanDeceleration, timeDelta);
|
2011-10-13 08:13:59 -07:00
|
|
|
|
|
|
|
// Calculate panning motion
|
|
|
|
let dx = self.panX * timeDelta;
|
|
|
|
let dy = self.panY * timeDelta;
|
|
|
|
|
|
|
|
// We only want to set integer scroll coordinates
|
|
|
|
self.panAccumulatedDeltaX += dx - Math.floor(dx);
|
|
|
|
self.panAccumulatedDeltaY += dy - Math.floor(dy);
|
|
|
|
|
|
|
|
dx = Math.floor(dx);
|
|
|
|
dy = Math.floor(dy);
|
|
|
|
|
|
|
|
if (Math.abs(self.panAccumulatedDeltaX) >= 1.0) {
|
|
|
|
let adx = Math.floor(self.panAccumulatedDeltaX);
|
|
|
|
dx += adx;
|
|
|
|
self.panAccumulatedDeltaX -= adx;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Math.abs(self.panAccumulatedDeltaY) >= 1.0) {
|
|
|
|
let ady = Math.floor(self.panAccumulatedDeltaY);
|
|
|
|
dy += ady;
|
|
|
|
self.panAccumulatedDeltaY -= ady;
|
|
|
|
}
|
|
|
|
|
2011-10-15 14:44:05 -07:00
|
|
|
self._scrollElementBy(panElement, -dx, -dy);
|
2011-10-13 08:13:59 -07:00
|
|
|
|
2011-10-17 12:02:27 -07:00
|
|
|
if (Math.abs(self.panX) >= kMinKineticSpeed ||
|
|
|
|
Math.abs(self.panY) >= kMinKineticSpeed)
|
2011-10-13 08:13:59 -07:00
|
|
|
window.mozRequestAnimationFrame(this);
|
2011-10-20 19:27:50 -07:00
|
|
|
}
|
2011-10-13 08:13:59 -07:00
|
|
|
};
|
|
|
|
|
2011-10-17 12:02:27 -07:00
|
|
|
// If one axis is moving a lot slower than the other, lock it.
|
|
|
|
if (Math.abs(this.panX) < Math.abs(this.panY) / kAxisLockRatio)
|
2011-10-13 08:13:59 -07:00
|
|
|
this.panX = 0;
|
2011-10-17 12:02:27 -07:00
|
|
|
else if (Math.abs(this.panY) < Math.abs(this.panX) / kAxisLockRatio)
|
2011-10-13 08:13:59 -07:00
|
|
|
this.panY = 0;
|
|
|
|
|
2011-10-15 14:44:05 -07:00
|
|
|
// Start the panning animation
|
|
|
|
window.mozRequestAnimationFrame(callback);
|
2011-10-12 14:31:04 -07:00
|
|
|
}
|
|
|
|
break;
|
2011-10-14 13:07:17 -07:00
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
case "MozMagnifyGestureStart":
|
|
|
|
this._pinchDelta = 0;
|
2011-10-19 09:13:36 -07:00
|
|
|
this.zoomCallbackFired = true;
|
2011-10-12 14:31:04 -07:00
|
|
|
break;
|
2011-10-14 13:07:17 -07:00
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
case "MozMagnifyGestureUpdate":
|
|
|
|
if (!aEvent.delta)
|
|
|
|
break;
|
|
|
|
|
2011-10-19 09:13:36 -07:00
|
|
|
this._pinchDelta += aEvent.delta;
|
|
|
|
|
|
|
|
if ((Math.abs(this._pinchDelta) >= 1) && this.zoomCallbackFired) {
|
|
|
|
// pinchDelta is the difference in pixels since the last call, so can
|
|
|
|
// be viewed as the number of extra/fewer pixels visible.
|
|
|
|
//
|
|
|
|
// We can work out the new zoom level by looking at the window width
|
|
|
|
// and height, and the existing zoom level.
|
|
|
|
let currentZoom = BrowserApp.selectedBrowser.markupDocumentViewer.fullZoom;
|
|
|
|
let currentSize = Math.sqrt(Math.pow(window.innerWidth, 2) + Math.pow(window.innerHeight, 2));
|
|
|
|
let newZoom = ((currentSize * currentZoom) + this._pinchDelta) / currentSize;
|
|
|
|
|
|
|
|
let self = this;
|
|
|
|
let callback = {
|
|
|
|
onBeforePaint: function zoomCallback(timeStamp) {
|
|
|
|
BrowserApp.selectedBrowser.markupDocumentViewer.fullZoom = newZoom;
|
|
|
|
self.zoomCallbackFired = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-10-12 14:31:04 -07:00
|
|
|
this._pinchDelta = 0;
|
2011-10-19 09:13:36 -07:00
|
|
|
|
|
|
|
// Use mozRequestAnimationFrame to stop from flooding fullZoom
|
|
|
|
this.zoomCallbackFired = false;
|
|
|
|
window.mozRequestAnimationFrame(callback);
|
2011-10-12 14:31:04 -07:00
|
|
|
}
|
|
|
|
break;
|
2010-09-21 07:21:00 -07:00
|
|
|
}
|
2011-10-12 14:31:04 -07:00
|
|
|
},
|
2009-10-23 21:04:51 -07:00
|
|
|
|
2011-10-15 14:44:05 -07:00
|
|
|
_updateLastPosition: function(x, y, dx, dy) {
|
2011-10-12 14:31:04 -07:00
|
|
|
this.lastX = x;
|
|
|
|
this.lastY = y;
|
2011-10-13 08:13:59 -07:00
|
|
|
this.lastTime = Date.now();
|
|
|
|
|
2011-10-19 09:17:59 -07:00
|
|
|
this.motionBuffer.push({ dx: dx, dy: dy, time: this.lastTime });
|
2011-10-15 14:44:05 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
_findScrollableElement: function(elem, checkElem) {
|
|
|
|
// Walk the DOM tree until we find a scrollable element
|
|
|
|
let scrollable = false;
|
|
|
|
while (elem) {
|
|
|
|
/* Element is scrollable if its scroll-size exceeds its client size, and:
|
|
|
|
* - It has overflow 'auto' or 'scroll'
|
|
|
|
* - It's a textarea
|
|
|
|
* - It's an HTML/BODY node
|
|
|
|
*/
|
|
|
|
if (checkElem) {
|
|
|
|
if (((elem.scrollHeight > elem.clientHeight) ||
|
|
|
|
(elem.scrollWidth > elem.clientWidth)) &&
|
|
|
|
(elem.style.overflow == 'auto' ||
|
|
|
|
elem.style.overflow == 'scroll' ||
|
|
|
|
elem.localName == 'textarea' ||
|
|
|
|
elem.localName == 'html' ||
|
|
|
|
elem.localName == 'body')) {
|
|
|
|
scrollable = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
checkElem = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Propagate up iFrames
|
|
|
|
if (!elem.parentNode && elem.documentElement &&
|
|
|
|
elem.documentElement.ownerDocument)
|
|
|
|
elem = elem.documentElement.ownerDocument.defaultView.frameElement;
|
|
|
|
else
|
|
|
|
elem = elem.parentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!scrollable)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return elem;
|
|
|
|
},
|
|
|
|
|
|
|
|
_scrollElementBy: function(elem, x, y) {
|
|
|
|
elem.scrollTop = elem.scrollTop + y;
|
|
|
|
elem.scrollLeft = elem.scrollLeft + x;
|
|
|
|
},
|
|
|
|
|
|
|
|
_elementCanScroll: function(elem, x, y) {
|
|
|
|
let scrollX = true;
|
|
|
|
let scrollY = true;
|
|
|
|
|
|
|
|
if (x < 0) {
|
|
|
|
if (elem.scrollLeft <= 0) {
|
|
|
|
scrollX = false;
|
|
|
|
}
|
|
|
|
} else if (elem.scrollLeft >= (elem.scrollWidth - elem.clientWidth)) {
|
|
|
|
scrollX = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (y < 0) {
|
|
|
|
if (elem.scrollTop <= 0) {
|
|
|
|
scrollY = false;
|
|
|
|
}
|
|
|
|
} else if (elem.scrollTop >= (elem.scrollHeight - elem.clientHeight)) {
|
|
|
|
scrollY = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return scrollX || scrollY;
|
2011-10-12 14:31:04 -07:00
|
|
|
}
|
2010-12-29 07:40:51 -08:00
|
|
|
};
|