summaryrefslogtreecommitdiff
path: root/common/content/tabs.js
diff options
context:
space:
mode:
Diffstat (limited to 'common/content/tabs.js')
-rw-r--r--common/content/tabs.js405
1 files changed, 185 insertions, 220 deletions
diff --git a/common/content/tabs.js b/common/content/tabs.js
index dbabc928..a05a2979 100644
--- a/common/content/tabs.js
+++ b/common/content/tabs.js
@@ -43,6 +43,8 @@ const Tabs = Module("tabs", {
setTimeout(function () { dactyl.focusContent(true); }, 10); // just make sure, that no widget has focus
},
+ get allTabs() Array.slice(config.tabbrowser.tabContainer.childNodes),
+
/**
* @property {Object} The previously accessed tab or null if no tab
* other than the current one has been accessed.
@@ -60,6 +62,21 @@ const Tabs = Module("tabs", {
},
/**
+ * @property {number} The number of tabs in the current window.
+ */
+ get count() config.tabbrowser.mTabs.length,
+
+ /**
+ * @property {Object} The local options store for the current tab.
+ */
+ get options() {
+ let store = this.localStore;
+ if (!("options" in store))
+ store.options = {};
+ return store.options;
+ },
+
+ /**
* @property {boolean} Whether the tab numbering XBL binding has been
* applied.
*/
@@ -75,20 +92,7 @@ const Tabs = Module("tabs", {
".tabbrowser-tab[busy] > .tab-icon > .tab-icon-image { list-style-image: url('chrome://global/skin/icons/loading_16.png') !important; }");
},
- /**
- * @property {number} The number of tabs in the current window.
- */
- get count() config.tabbrowser.mTabs.length,
-
- /**
- * @property {Object} The local options store for the current tab.
- */
- get options() {
- let store = this.localStore;
- if (!("options" in store))
- store.options = {};
- return store.options;
- },
+ get visibleTabs() config.tabbrowser.visibleTabs || this.allTabs.filter(function (tab) !tab.hidden),
/**
* Returns the local state store for the tab at the specified
@@ -123,37 +127,33 @@ const Tabs = Module("tabs", {
get closedTabs() services.get("json").decode(services.get("sessionStore").getClosedTabData(window)),
/**
- * Returns the index of <b>tab</b> or the index of the currently
- * selected tab if <b>tab</b> is not specified. This is a 0-based
- * index.
+ * Clones the specified <b>tab</b> and append it to the tab list.
*
- * @param {Object} tab A tab from the current tab list.
- * @returns {number}
+ * @param {Object} tab The tab to clone.
+ * @param {boolean} activate Whether to select the newly cloned tab.
*/
- index: function (tab) {
- if (tab)
- return Array.indexOf(config.tabbrowser.mTabs, tab);
- else
- return config.tabbrowser.mTabContainer.selectedIndex;
+ cloneTab: function (tab, activate) {
+ let newTab = config.tabbrowser.addTab();
+ Tabs.copyTab(newTab, tab);
+
+ if (activate)
+ config.tabbrowser.mTabContainer.selectedItem = newTab;
+
+ return newTab;
},
- // TODO: implement filter
/**
- * Returns an array of all tabs in the tab list.
+ * Detaches the specified <b>tab</b> and open it in a new window. If no
+ * tab is specified the currently selected tab is detached.
*
- * @returns {Object[]}
+ * @param {Object} tab The tab to detach.
*/
- // FIXME: why not return the tab element?
- // : unused? Remove me.
- get: function () {
- let buffers = [];
- for (let [i, browser] in this.browsers) {
- let title = browser.contentTitle || "(Untitled)";
- let uri = browser.currentURI.spec;
- let number = i + 1;
- buffers.push([number, title, uri]);
- }
- return buffers;
+ detachTab: function (tab) {
+ if (!tab)
+ tab = config.tabbrowser.mTabContainer.selectedItem;
+
+ services.get("windowWatcher")
+ .openWindow(window, window.getBrowserURL(), null, "chrome,dialog=no,all", tab);
},
/**
@@ -172,6 +172,26 @@ const Tabs = Module("tabs", {
},
/**
+ * If TabView exists, returns the Panorama window. If the Panorama
+ * is has not yet initialized, this function will not return until
+ * it has.
+ *
+ * @returns {Window}
+ */
+ getGroups: function () {
+ if ("_groups" in this)
+ return this._groups;
+ if (window.TabView && TabView._initFrame)
+ TabView._initFrame();
+ let iframe = document.getElementById("tab-view");
+ this._groups = this._groups = iframe ? iframe.contentWindow : null;
+ if (this._groups)
+ while (!this._groups.TabItems)
+ dactyl.threadYield(false, true);
+ return this._groups;
+ },
+
+ /**
* Returns the tab at the specified <b>index</b> or the currently
* selected tab if <b>index</b> is not specified. This is a 0-based
* index.
@@ -180,13 +200,73 @@ const Tabs = Module("tabs", {
* @returns {Object}
*/
getTab: function (index) {
- if (index != undefined)
+ if (index != null)
return config.tabbrowser.mTabs[index];
else
return config.tabbrowser.mCurrentTab;
},
/**
+ * Returns the index of <b>tab</b> or the index of the currently
+ * selected tab if <b>tab</b> is not specified. This is a 0-based
+ * index.
+ *
+ * @param {<xul:tab/>} tab A tab from the current tab list.
+ * @param {boolean} visible Whether to consider only visible tabs.
+ * @returns {number}
+ */
+ index: function (tab, visible) {
+ let tabs = this[visible ? "visibleTabs" : "allTabs"];
+ return tabs.indexOf(tab || config.tabbrowser.mCurrentTab);
+ },
+
+ /**
+ * @param spec can either be:
+ * - an absolute integer
+ * - "" for the current tab
+ * - "+1" for the next tab
+ * - "-3" for the tab, which is 3 positions left of the current
+ * - "$" for the last tab
+ */
+ indexFromSpec: function (spec, wrap) {
+ if (spec instanceof Node)
+ return this.allTabs.indexOf(spec);
+
+ let tabs = this.visibleTabs;
+ let position = this.index(null, true);
+
+ if (spec == null || spec === "")
+ return position;
+
+ if (typeof spec === "number")
+ position = spec;
+ else if (spec === "$")
+ position = tabs.length - 1;
+ else if (/^[+-]\d+$/.test(spec))
+ position += parseInt(spec, 10);
+ else if (/^\d+$/.test(spec))
+ position = parseInt(spec, 10);
+ else
+ return -1;
+
+ if (position >= tabs.length)
+ position = wrap ? position % tabs.length : tabs.length - 1;
+ else if (position < 0)
+ position = wrap ? (position % tabs.length) + tabs.length : 0;
+
+ return this.allTabs.indexOf(tabs[position]);
+ },
+
+ /**
+ * Removes all tabs from the tab list except the specified <b>tab</b>.
+ *
+ * @param {Object} tab The tab to keep.
+ */
+ keepOnly: function (tab) {
+ config.tabbrowser.removeAllTabsBut(tab);
+ },
+
+ /**
* Lists all tabs matching <b>filter</b>.
*
* @param {string} filter A filter matching a substring of the tab's
@@ -206,7 +286,7 @@ const Tabs = Module("tabs", {
* list.
*/
move: function (tab, spec, wrap) {
- let index = Tabs.indexFromSpec(spec, wrap);
+ let index = tabs.indexFromSpec(spec, wrap);
config.tabbrowser.moveTabTo(tab, index);
},
@@ -223,96 +303,31 @@ const Tabs = Module("tabs", {
*/
// FIXME: what is quitOnLastTab {1,2} all about then, eh? --djk
remove: function (tab, count, focusLeftTab, quitOnLastTab) {
- let removeOrBlankTab = {
- Firefox: function (tab) {
- if (config.tabbrowser.mTabs.length > 1)
- config.tabbrowser.removeTab(tab);
- else {
- if (buffer.URL != "about:blank" ||
- window.getWebNavigation().sessionHistory.count > 0) {
- dactyl.open("about:blank", dactyl.NEW_BACKGROUND_TAB);
- config.tabbrowser.removeTab(tab);
- }
- else
- dactyl.beep();
- }
- },
- Thunderbird: function (tab) {
- if (config.tabbrowser.mTabs.length > 1)
- config.tabbrowser.removeTab(tab);
- else
- dactyl.beep();
- },
- Songbird: function (tab) {
- if (config.tabbrowser.mTabs.length > 1)
- config.tabbrowser.removeTab(tab);
- else {
- if (buffer.URL != "about:blank" || window.getWebNavigation().sessionHistory.count > 0) {
- dactyl.open("about:blank", dactyl.NEW_BACKGROUND_TAB);
- config.tabbrowser.removeTab(tab);
- }
- else
- dactyl.beep();
- }
- }
- }[config.hostApplication] || function () {};
-
- if (typeof count != "number" || count < 1)
- count = 1;
+ count = Math.max(count, 1);
- if (quitOnLastTab >= 1 && config.tabbrowser.mTabs.length <= count) {
+ if (quitOnLastTab >= 1 && this.count <= count) {
if (dactyl.windows.length > 1)
window.close();
else
dactyl.quit(quitOnLastTab == 2);
-
return;
}
- let index = this.index(tab);
- if (focusLeftTab) {
- let lastRemovedTab = 0;
- for (let i = index; i > index - count && i >= 0; i--) {
- removeOrBlankTab(this.getTab(i));
- lastRemovedTab = i > 0 ? i : 1;
- }
- config.tabbrowser.mTabContainer.selectedIndex = lastRemovedTab - 1;
- }
- else {
- let i = index + count - 1;
- if (i >= this.count)
- i = this.count - 1;
+ let tabs = this.visibleTabs
+ if (tabs.indexOf(tab) < 0)
+ tabs = this.allTabs;
+ let index = tabs.indexOf(tab);
- for (; i >= index; i--)
- removeOrBlankTab(this.getTab(i));
- config.tabbrowser.mTabContainer.selectedIndex = index;
- }
- },
+ let next = index + (focusLeftTab ? -count : count);
+ if (!(next in tabs))
+ next = index + (focusLeftTab ? 1 : -1);
+ if (next in tabs)
+ config.tabbrowser.mTabContainer.selectedItem = tabs[next];
- /**
- * Removes all tabs from the tab list except the specified <b>tab</b>.
- *
- * @param {Object} tab The tab to keep.
- */
- keepOnly: function (tab) {
- config.tabbrowser.removeAllTabsBut(tab);
- },
-
- /**
- * Selects the tab at the position specified by <b>spec</b>.
- *
- * @param {string} spec See {@link Tabs.indexFromSpec}
- * @param {boolean} wrap Whether an out of bounds <b>spec</b> causes
- * the selection position to wrap around the start/end of the tab
- * list.
- */
- select: function (spec, wrap) {
- let index = Tabs.indexFromSpec(spec, wrap);
- // FIXME:
- if (index == -1)
- dactyl.beep();
+ if (focusLeftTab)
+ tabs.slice(Math.max(0, index+1 - count), index+1).forEach(config.removeTab);
else
- config.tabbrowser.mTabContainer.selectedIndex = index;
+ tabs.slice(index, index + count).forEach(config.removeTab);
},
/**
@@ -354,6 +369,40 @@ const Tabs = Module("tabs", {
},
/**
+ * Selects the tab at the position specified by <b>spec</b>.
+ *
+ * @param {string} spec See {@link Tabs.indexFromSpec}
+ * @param {boolean} wrap Whether an out of bounds <b>spec</b> causes
+ * the selection position to wrap around the start/end of the tab
+ * list.
+ */
+ select: function (spec, wrap) {
+ let index = tabs.indexFromSpec(spec, wrap);
+ if (index == -1)
+ dactyl.beep();
+ else
+ config.tabbrowser.mTabContainer.selectedIndex = index;
+ },
+
+ /**
+ * Selects the alternate tab.
+ */
+ selectAlternateTab: function () {
+ dactyl.assert(tabs.alternate != null && tabs.getTab() != tabs.alternate,
+ "E23: No alternate page");
+
+ // NOTE: this currently relies on v.tabs.index() returning the
+ // currently selected tab index when passed null
+ let index = tabs.index(tabs.alternate);
+
+ // TODO: since a tab close is more like a bdelete for us we
+ // should probably reopen the closed tab when a 'deleted'
+ // alternate is selected
+ dactyl.assert(index >= 0, "E86: Buffer does not exist"); // TODO: This should read "Buffer N does not exist"
+ tabs.select(index);
+ },
+
+ /**
* Stops loading the specified tab.
*
* @param {Object} tab The tab to stop loading.
@@ -398,7 +447,7 @@ const Tabs = Module("tabs", {
}
else {
buffer = this._lastBufferSwitchArgs;
- if (allowNonUnique === undefined || allowNonUnique == null) // XXX
+ if (allowNonUnique == null) // XXX
allowNonUnique = this._lastBufferSwitchSpecial;
}
@@ -407,14 +456,12 @@ const Tabs = Module("tabs", {
return;
}
- if (!count || count < 1)
- count = 1;
- if (typeof reverse != "boolean")
- reverse = false;
+ count = Math.max(1, count || 1);
+ reverse = Boolean(reverse);
let matches = buffer.match(/^(\d+):?/);
if (matches) {
- tabs.select(parseInt(matches[1], 10) - 1, false); // make it zero-based
+ tabs.select(this.allTabs[parseInt(matches[1], 10) - 1], false);
return;
}
@@ -424,8 +471,9 @@ const Tabs = Module("tabs", {
let nbrowsers = config.tabbrowser.browsers.length;
for (let [i, ] in tabs.browsers) {
let index = (i + first) % nbrowsers;
- let url = config.tabbrowser.getBrowserAtIndex(index).contentDocument.location.href;
- let title = config.tabbrowser.getBrowserAtIndex(index).contentDocument.title.toLowerCase();
+ let browser = config.tabbrowser.getBrowserAtIndex(index);
+ let url = browser.contentDocument.location.href;
+ let title = browser.contentDocument.title.toLowerCase();
if (url == buffer) {
tabs.select(index, false);
return;
@@ -434,71 +482,21 @@ const Tabs = Module("tabs", {
if (url.indexOf(buffer) >= 0 || title.indexOf(lowerBuffer) >= 0)
matches.push(index);
}
+
if (matches.length == 0)
dactyl.echoerr("E94: No matching buffer for " + buffer);
else if (matches.length > 1 && !allowNonUnique)
dactyl.echoerr("E93: More than one match for " + buffer);
else {
- if (reverse) {
+ let index = (count - 1) % matches.length;
+ if (reverse)
index = matches.length - count;
- while (index < 0)
- index += matches.length;
- }
- else
- index = (count - 1) % matches.length;
- tabs.select(matches[index], false);
+ index = Array.indexOf(config.tabbrowser.browsers, matches[index]);
+ tabs.select(index, false);
}
},
- /**
- * Clones the specified <b>tab</b> and append it to the tab list.
- *
- * @param {Object} tab The tab to clone.
- * @param {boolean} activate Whether to select the newly cloned tab.
- */
- cloneTab: function (tab, activate) {
- let newTab = config.tabbrowser.addTab();
- Tabs.copyTab(newTab, tab);
-
- if (activate)
- config.tabbrowser.mTabContainer.selectedItem = newTab;
-
- return newTab;
- },
-
- /**
- * Detaches the specified <b>tab</b> and open it in a new window. If no
- * tab is specified the currently selected tab is detached.
- *
- * @param {Object} tab The tab to detach.
- */
- detachTab: function (tab) {
- if (!tab)
- tab = config.tabbrowser.mTabContainer.selectedItem;
-
- services.get("windowWatcher")
- .openWindow(window, window.getBrowserURL(), null, "chrome,dialog=no,all", tab);
- },
-
- /**
- * Selects the alternate tab.
- */
- selectAlternateTab: function () {
- dactyl.assert(tabs.alternate != null && tabs.getTab() != tabs.alternate,
- "E23: No alternate page");
-
- // NOTE: this currently relies on v.tabs.index() returning the
- // currently selected tab index when passed null
- let index = tabs.index(tabs.alternate);
-
- // TODO: since a tab close is more like a bdelete for us we
- // should probably reopen the closed tab when a 'deleted'
- // alternate is selected
- dactyl.assert(index >= 0, "E86: Buffer does not exist"); // TODO: This should read "Buffer N does not exist"
- tabs.select(index);
- },
-
// NOTE: when restarting a session FF selects the first tab and then the
// tab that was selected when the session was created. As a result the
// alternate after a restart is often incorrectly tab 1 when there
@@ -520,41 +518,6 @@ const Tabs = Module("tabs", {
let tabState = services.get("sessionStore").getTabState(from);
services.get("sessionStore").setTabState(to, tabState);
- },
-
- /**
- * @param spec can either be:
- * - an absolute integer
- * - "" for the current tab
- * - "+1" for the next tab
- * - "-3" for the tab, which is 3 positions left of the current
- * - "$" for the last tab
- */
- indexFromSpec: function (spec, wrap) {
- let position = config.tabbrowser.mTabContainer.selectedIndex;
- let length = config.tabbrowser.mTabs.length;
- let last = length - 1;
-
- if (spec === undefined || spec === "")
- return position;
-
- if (typeof spec === "number")
- position = spec;
- else if (spec === "$")
- position = last;
- else if (/^[+-]\d+$/.test(spec))
- position += parseInt(spec, 10);
- else if (/^\d+$/.test(spec))
- position = parseInt(spec, 10);
- else
- return -1;
-
- if (position > last)
- position = wrap ? position % length : last;
- else if (position < 0)
- position = wrap ? (position % length) + length : 0;
-
- return position;
}
}, {
commands: function () {
@@ -1037,7 +1000,7 @@ const Tabs = Module("tabs", {
let pref = "browser.tabStrip.autoHide";
if (options.getPref(pref) == null) // Try for FF 3.0 & 3.1
pref = "browser.tabs.autoHide";
- options.safeSetPref(pref, value == 1);
+ options.safeSetPref(pref, value == 1, "See 'showtabline' option.");
tabStrip.collapsed = false;
}
@@ -1098,8 +1061,10 @@ const Tabs = Module("tabs", {
restriction = 2;
}
- options.safeSetPref("browser.link.open_newwindow", open, "See 'popups' option.");
- options.safeSetPref("browser.link.open_newwindow.restriction", restriction, "See 'popups' option.");
+ options.safeSetPref("browser.link.open_newwindow", open,
+ "See 'popups' option.");
+ options.safeSetPref("browser.link.open_newwindow.restriction", restriction,
+ "See 'popups' option.");
return values;
},
completer: function (context) [