1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-20 20:27:58 +01:00
Files
pentadactyl-pm/common/content/tabs.js
2009-04-30 22:21:32 +10:00

1049 lines
36 KiB
JavaScript

/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/
/** @scope modules */
// TODO: many methods do not work with Thunderbird correctly yet
function Tabs() //{{{
{
////////////////////////////////////////////////////////////////////////////////
////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
var tabmail;
var getBrowser = (function () {
if (config.hostApplication == "Thunderbird")
{
return function ()
{
if (!tabmail)
{
tabmail = document.getElementById("tabmail");
tabmail.__defineGetter__("mTabContainer", function () this.tabContainer);
tabmail.__defineGetter__("mTabs", function () this.tabContainer.childNodes);
tabmail.__defineGetter__("mCurrentTab", function () this.tabContainer.selectedItem);
tabmail.__defineGetter__("mStrip", function () this.tabStrip);
tabmail.__defineGetter__("browsers", function () [browser for (browser in Iterator(this.mTabs))]);
}
return tabmail;
};
}
else
return window.getBrowser;
})();
var alternates = [getBrowser().mCurrentTab, null];
// used for the "gb" and "gB" mappings to remember the last :buffer[!] command
var lastBufferSwitchArgs = "";
var lastBufferSwitchSpecial = true;
// @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
function indexFromSpec(spec, wrap)
{
let position = getBrowser().mTabContainer.selectedIndex;
let length = getBrowser().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;
}
function copyTab(to, from)
{
if (!from)
from = getBrowser().mTabContainer.selectedItem;
let tabState = services.get("sessionStore").getTabState(from);
services.get("sessionStore").setTabState(to, tabState);
}
// hide tabs initially
if (config.name == "Vimperator")
getBrowser().mStrip.getElementsByClassName("tabbrowser-tabs")[0].collapsed = true;
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// OPTIONS /////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
options.add(["showtabline", "stal"],
"Control when to show the tab bar of opened web pages",
"number", config.optionDefaults["showtabline"],
{
setter: function (value)
{
let tabStrip = tabs.tabStrip;
if (!tabStrip)
return options["showtabline"]; // XXX
if (value == 0)
{
tabStrip.collapsed = true;
}
else
{
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);
tabStrip.collapsed = false;
}
return value;
},
completer: function (context) [
["0", "Never show tab bar"],
["1", "Show tab bar only if more than one tab is open"],
["2", "Always show tab bar"]
],
validator: Option.validateCompleter
});
if (config.hasTabbrowser)
{
options.add(["activate", "act"],
"Define when tabs are automatically activated",
"stringlist", "homepage,quickmark,tabopen,paste",
{
completer: function (context) [
["homepage", "gH mapping"],
["quickmark", "go and gn mappings"],
["tabopen", ":tabopen[!] command"],
["paste", "P and gP mappings"]
],
validator: Option.validateCompleter
});
options.add(["newtab"],
"Define which commands should output in a new tab by default",
"stringlist", "",
{
completer: function (context) [
["all", "All commands"],
["addons", ":addo[ns] command"],
["downloads", ":downl[oads] command"],
["help", ":h[elp] command"],
["javascript", ":javascript! or :js! command"],
["prefs", ":pref[erences]! or :prefs! command"]
],
validator: Option.validateCompleter
});
options.add(["popups", "pps"],
"Where to show requested popup windows",
"number", 1,
{
setter: function (value)
{
let values = [[0, 1], // always in current tab
[0, 3], // in a new tab
[2, 3], // in a new window if it has specified sizes
[1, 2], // always in new window
[2, 1]];// current tab unless it has specified sizes
options.safeSetPref("browser.link.open_newwindow.restriction", values[value][0]);
options.safeSetPref("browser.link.open_newwindow", values[value][1]);
return value;
},
completer: function (context) [
["0", "Force to open in the current tab"],
["1", "Always open in a new tab"],
["2", "Open in a new window if it has a specific requested size (default in Firefox)"],
["3", "Always open in a new window"],
["4", "Open in the same tab unless it has a specific requested size"]
],
validator: Option.validateCompleter
});
}
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// MAPPINGS ////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
mappings.add([modes.NORMAL], ["g0", "g^"],
"Go to the first tab",
function (count) { tabs.select(0); });
mappings.add([modes.NORMAL], ["g$"],
"Go to the last tab",
function (count) { tabs.select("$"); });
mappings.add([modes.NORMAL], ["gt"],
"Go to the next tab",
function (count) { tabs.select(count > 0 ? count - 1 : "+1", count > 0 ? false : true); },
{ flags: Mappings.flags.COUNT });
mappings.add([modes.NORMAL], ["<C-n>", "<C-Tab>", "<C-PageDown>"],
"Go to the next tab",
function (count) { tabs.select("+" + (count < 1 ? 1 : count), true); },
{ flags: Mappings.flags.COUNT });
mappings.add([modes.NORMAL], ["gT", "<C-p>", "<C-S-Tab>", "<C-PageUp>"],
"Go to previous tab",
function (count) { tabs.select("-" + (count < 1 ? 1 : count), true); },
{ flags: Mappings.flags.COUNT });
if (config.hasTabbrowser)
{
mappings.add([modes.NORMAL], ["b"],
"Open a prompt to switch buffers",
function (count)
{
if (count != -1)
tabs.switchTo(String(count));
else
commandline.open(":", "buffer! ", modes.EX);
},
{ flags: Mappings.flags.COUNT });
mappings.add([modes.NORMAL], ["B"],
"Show buffer list",
function () { tabs.list(false); });
mappings.add([modes.NORMAL], ["d"],
"Delete current buffer",
function (count) { tabs.remove(tabs.getTab(), count, false, 0); },
{ flags: Mappings.flags.COUNT });
mappings.add([modes.NORMAL], ["D"],
"Delete current buffer, focus tab to the left",
function (count) { tabs.remove(tabs.getTab(), count, true, 0); },
{ flags: Mappings.flags.COUNT });
mappings.add([modes.NORMAL], ["gb"],
"Repeat last :buffer[!] command",
function (count) { tabs.switchTo(null, null, count, false); },
{ flags: Mappings.flags.COUNT });
mappings.add([modes.NORMAL], ["gB"],
"Repeat last :buffer[!] command in reverse direction",
function (count) { tabs.switchTo(null, null, count, true); },
{ flags: Mappings.flags.COUNT });
mappings.add([modes.NORMAL], ["u"],
"Undo closing of a tab",
function (count) { commands.get("undo").execute("", false, count); },
{ flags: Mappings.flags.COUNT });
mappings.add([modes.NORMAL], ["<C-^>", "<C-6>"],
"Select the alternate tab or the [count]th tab",
function (count)
{
if (count < 1)
tabs.selectAlternateTab();
else
tabs.switchTo(count.toString(), false);
},
{ flags: Mappings.flags.COUNT });
}
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// COMMANDS ////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
commands.add(["bd[elete]", "bw[ipeout]", "bun[load]", "tabc[lose]"],
"Delete current buffer",
function (args)
{
let special = args.bang;
let count = args.count;
let arg = args.literalArg;
if (arg)
{
arg = arg.toLowerCase();
let removed = 0;
let matches = arg.match(/^(\d+):?/);
if (matches)
{
tabs.remove(tabs.getTab(parseInt(matches[1], 10) - 1));
removed = 1;
}
else
{
let browsers = getBrowser().browsers;
for (let i = browsers.length - 1; i >= 0; i--)
{
let title = browsers[i].contentTitle.toLowerCase() || "";
let uri = browsers[i].currentURI.spec.toLowerCase();
let host = browsers[i].currentURI.host.toLowerCase();
if (host.indexOf(arg) >= 0 || uri == arg ||
(special && (title.indexOf(arg) >= 0 || uri.indexOf(arg) >= 0)))
{
tabs.remove(tabs.getTab(i));
removed++;
}
}
}
if (removed > 0)
liberator.echomsg(removed + " fewer tab(s)", 9);
else
liberator.echoerr("E94: No matching tab for " + arg);
}
else // just remove the current tab
tabs.remove(tabs.getTab(), count > 0 ? count : 1, special, 0);
},
{
argCount: "?",
bang: true,
count: true,
completer: function (context) completion.buffer(context),
literal: 0
});
// TODO: this should open in a new tab positioned directly after the current one, not at the end
commands.add(["tab"],
"Execute a command and tell it to output in a new tab",
function (args)
{
liberator.forceNewTab = true;
liberator.execute(args.string);
liberator.forceNewTab = false;
},
{
argCount: "+",
completer: function (context) completion.ex(context),
literal: 0
});
commands.add(["tabd[o]", "bufd[o]"],
"Execute a command in each tab",
function (args)
{
for (let i = 0; i < tabs.count; i++)
{
tabs.select(i);
liberator.execute(args.string);
}
},
{
argCount: "1",
completer: function (context) completion.ex(context),
literal: 0
});
commands.add(["tabl[ast]", "bl[ast]"],
"Switch to the last tab",
function () tabs.select("$", false),
{ argCount: "0" });
// TODO: "Zero count" if 0 specified as arg
commands.add(["tabp[revious]", "tp[revious]", "tabN[ext]", "tN[ext]", "bp[revious]", "bN[ext]"],
"Switch to the previous tab or go [count] tabs back",
function (args)
{
let count = args.count;
let arg = args[0];
// count is ignored if an arg is specified, as per Vim
if (arg)
{
if (/^\d+$/.test(arg))
tabs.select("-" + arg, true); // FIXME: urgh!
else
liberator.echoerr("E488: Trailing characters");
}
else if (count > 0)
{
tabs.select("-" + count, true);
}
else
{
tabs.select("-1", true);
}
},
{
argCount: "?",
count: true
});
// TODO: "Zero count" if 0 specified as arg
commands.add(["tabn[ext]", "tn[ext]", "bn[ext]"],
"Switch to the next or [count]th tab",
function (args)
{
let count = args.count;
let arg = args[0];
if (arg || count > 0)
{
let index;
// count is ignored if an arg is specified, as per Vim
if (arg)
{
if (/^\d+$/.test(arg))
{
index = arg - 1;
}
else
{
liberator.echoerr("E488: Trailing characters");
return;
}
}
else
{
index = count - 1;
}
if (index < tabs.count)
tabs.select(index, true);
else
liberator.beep();
}
else
{
tabs.select("+1", true);
}
},
{
argCount: "?",
count: true
});
commands.add(["tabr[ewind]", "tabfir[st]", "br[ewind]", "bf[irst]"],
"Switch to the first tab",
function () { tabs.select(0, false); },
{ argCount: "0" });
if (config.hasTabbrowser)
{
// TODO: "Zero count" if 0 specified as arg, multiple args and count ranges?
commands.add(["b[uffer]"],
"Switch to a buffer",
function (args)
{
let special = args.bang;
let count = args.count;
let arg = args.literalArg;
// if a numeric arg is specified any count is ignored; if a
// count and non-numeric arg are both specified then E488
if (arg && count > 0)
{
if (/^\d+$/.test(arg))
tabs.switchTo(arg, special);
else
liberator.echoerr("E488: Trailing characters");
}
else if (count > 0)
{
tabs.switchTo(count.toString(), special);
}
else
{
tabs.switchTo(arg, special);
}
},
{
argCount: "?",
bang: true,
count: true,
completer: function (context) completion.buffer(context),
literal: 0
});
commands.add(["buffers", "files", "ls", "tabs"],
"Show a list of all buffers",
function (args) { tabs.list(args.literalArg); },
{
argCount: "?",
literal: 0
});
commands.add(["quita[ll]", "qa[ll]"],
"Quit " + config.name,
function (args) { liberator.quit(false, args.bang); },
{
argCount: "0",
bang: true
});
commands.add(["reloada[ll]"],
"Reload all tab pages",
function (args) { tabs.reloadAll(args.bang); },
{
argCount: "0",
bang: true
});
// TODO: add count support
commands.add(["tabm[ove]"],
"Move the current tab after tab N",
function (args)
{
let arg = args[0];
// FIXME: tabmove! N should probably produce an error
if (arg && !/^([+-]?\d+)$/.test(arg))
{
liberator.echoerr("E488: Trailing characters");
return;
}
// if not specified, move to after the last tab
tabs.move(getBrowser().mCurrentTab, arg || "$", args.bang);
},
{
argCount: "?",
bang: true
});
commands.add(["tabo[nly]"],
"Close all other tabs",
function () { tabs.keepOnly(getBrowser().mCurrentTab); },
{ argCount: "0" });
commands.add(["tabopen", "t[open]", "tabnew", "tabe[dit]"],
"Open one or more URLs in a new tab",
function (args)
{
let special = args.bang;
args = args.string;
let where = special ? liberator.NEW_TAB : liberator.NEW_BACKGROUND_TAB;
if (/\btabopen\b/.test(options["activate"]))
where = special ? liberator.NEW_BACKGROUND_TAB : liberator.NEW_TAB;
if (args)
liberator.open(args, where);
else
liberator.open("about:blank", where);
},
{
bang: true,
completer: function (context) completion.url(context),
literal: 0
});
commands.add(["tabde[tach]"],
"Detach current tab to its own window",
function () { tabs.detachTab(null); },
{ argCount: "0" });
commands.add(["tabd[uplicate]"],
"Duplicate current tab",
function (args)
{
let tab = tabs.getTab();
let activate = args.bang ? true : false;
if (/\btabopen\b/.test(options["activate"]))
activate = !activate;
for (let i in util.range(0, Math.max(1, args.count)))
tabs.cloneTab(tab, activate);
},
{
argCount: "0",
bang: true,
count: true
});
}
/* Why not xulmus? */
if (liberator.has("session") && config.name != "Xulmus")
{
// TODO: extract common functionality of "undoall"
commands.add(["u[ndo]"],
"Undo closing of a tab",
function (args)
{
let count = args.count;
args = args[0];
if (count < 1)
count = 1;
if (args)
{
count = 0;
for (let [i, item] in Iterator(tabs.closedTabs))
{
if (item.state.entries[0].url == args)
{
count = i + 1;
break;
}
}
if (!count)
{
liberator.echoerr("Exxx: No matching closed tab");
return;
}
}
window.undoCloseTab(count - 1);
},
{
argCount: "?",
completer: function (context)
{
context.anchored = false;
context.compare = CompletionContext.Sort.unsorted;
context.keys = { text: function (item) item.state.entries[0].url, description: "title" };
context.completions = tabs.closedTabs;
},
count: true,
literal: 0
});
commands.add(["undoa[ll]"],
"Undo closing of all closed tabs",
function (args)
{
for (let i in Iterator(tabs.closedTabs))
window.undoCloseTab(0);
},
{ argCount: "0" });
commands.add(["wqa[ll]", "wq", "xa[ll]"],
"Save the session and quit",
function () { liberator.quit(true); },
{ argCount: "0" });
}
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// PUBLIC SECTION //////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
return {
get alternate() alternates[1],
get browsers()
{
let browsers = getBrowser().browsers;
for (let i = 0; i < browsers.length; i++)
yield [i, browsers[i]];
},
get tabsBound() {
return Boolean(styles.get(true, "tab-binding"))
},
set tabsBound(val) {
let fragment = liberator.has("MacUnix") ? "tab-mac" : "tab";
if (!val)
styles.removeSheet(true, "tab-binding");
else if (!this.tabsBound)
styles.addSheet(true, "tab-binding", "chrome://browser/content/browser.xul",
".tabbrowser-tab { -moz-binding: url(chrome://liberator/content/bindings.xml#" + fragment + ") !important; }");
},
get count() getBrowser().mTabs.length,
get options()
{
let store = this.localStore;
if (!("options" in store))
store.options = {};
return store.options;
},
getLocalStore: function (tabIndex)
{
let tab = this.getTab(tabIndex);
if (!tab.liberatorStore)
tab.liberatorStore = {};
return tab.liberatorStore;
},
get localStore() this.getLocalStore(),
get tabStrip()
{
let tabStrip = null;
// FIXME: why is this app specific conditional code here?
if (config.hostApplication == "Firefox")
tabStrip = getBrowser().mStrip.getElementsByClassName("tabbrowser-tabs")[0];
else if (/^(Thunderbird|Songbird)$/.test(config.hostApplication))
tabStrip = getBrowser().mStrip;
return tabStrip;
},
// @returns the index of the currently selected tab starting with 0
index: function (tab)
{
if (tab)
return Array.indexOf(getBrowser().mTabs, tab);
return getBrowser().mTabContainer.selectedIndex;
},
// TODO: implement filter
// @returns an array of tabs which match filter
get: function (filter)
{
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;
},
getContentIndex: function (content)
{
for (let [i, browser] in this.browsers)
{
if (browser.contentWindow == content || browser.contentDocument == content)
return i;
}
return -1;
},
getTab: function (index)
{
if (index != undefined)
return getBrowser().mTabs[index];
else
return getBrowser().mCurrentTab;
},
get closedTabs()
{
return services.get("json").decode(services.get("sessionStore").getClosedTabData(window));
},
list: function (filter)
{
completion.listCompleter("buffer", filter);
},
// wrap causes the movement to wrap around the start and end of the tab list
// NOTE: position is a 0 based index
move: function (tab, spec, wrap)
{
let index = indexFromSpec(spec, wrap);
getBrowser().moveTabTo(tab, index);
},
// quitOnLastTab = 1: quit without saving session
// quitOnLastTab = 2: quit and save session
remove: function (tab, count, focusLeftTab, quitOnLastTab)
{
let removeOrBlankTab = {
Firefox: function (tab)
{
if (getBrowser().mTabs.length > 1)
getBrowser().removeTab(tab);
else
{
if (buffer.URL != "about:blank" ||
window.getWebNavigation().sessionHistory.count > 0)
{
liberator.open("about:blank", liberator.NEW_BACKGROUND_TAB);
getBrowser().removeTab(tab);
}
else
liberator.beep();
}
},
Thunderbird: function (tab)
{
if (getBrowser().mTabs.length > 1)
getBrowser().removeTab(tab);
else
liberator.beep();
},
Songbird: function (tab)
{
if (getBrowser().mTabs.length > 1)
getBrowser().removeTab(tab);
else
{
if (buffer.URL != "about:blank" ||
window.getWebNavigation().sessionHistory.count > 0)
{
liberator.open("about:blank", liberator.NEW_BACKGROUND_TAB);
getBrowser().removeTab(tab);
}
else
liberator.beep();
}
}
}[config.hostApplication] || function () {};
if (typeof count != "number" || count < 1)
count = 1;
if (quitOnLastTab >= 1 && getBrowser().mTabs.length <= count)
{
if (liberator.windows.length > 1)
window.close();
else
liberator.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;
}
getBrowser().mTabContainer.selectedIndex = lastRemovedTab - 1;
}
else
{
let i = index + count - 1;
if (i >= this.count)
i = this.count - 1;
for (; i >= index; i--)
removeOrBlankTab(this.getTab(i));
getBrowser().mTabContainer.selectedIndex = index;
}
},
keepOnly: function (tab)
{
getBrowser().removeAllTabsBut(tab);
},
select: function (spec, wrap)
{
let index = indexFromSpec(spec, wrap);
// FIXME:
if (index == -1)
{
liberator.beep(); // XXX: move to ex-handling?
return;
}
getBrowser().mTabContainer.selectedIndex = index;
},
reload: function (tab, bypassCache)
{
if (bypassCache)
{
const flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
getBrowser().getBrowserForTab(tab).reloadWithFlags(flags);
}
else
{
getBrowser().reloadTab(tab);
}
},
reloadAll: function (bypassCache)
{
if (bypassCache)
{
for (let i = 0; i < getBrowser().mTabs.length; i++)
{
try
{
this.reload(getBrowser().mTabs[i], bypassCache);
}
catch (e)
{
// FIXME: can we do anything useful here without stopping the
// other tabs from reloading?
}
}
}
else
{
getBrowser().reloadAllTabs();
}
},
// "buffer" is a string which matches the URL or title of a buffer, if it
// is null, the last used string is used again
switchTo: function (buffer, allowNonUnique, count, reverse)
{
if (buffer == "")
return;
if (buffer != null)
{
// store this command, so it can be repeated with "B"
lastBufferSwitchArgs = buffer;
lastBufferSwitchSpecial = allowNonUnique;
}
else
{
buffer = lastBufferSwitchArgs;
if (allowNonUnique === undefined || allowNonUnique == null) // XXX
allowNonUnique = lastBufferSwitchSpecial;
}
if (buffer == "#")
{
tabs.selectAlternateTab();
return;
}
if (!count || count < 1)
count = 1;
if (typeof reverse != "boolean")
reverse = false;
let matches = buffer.match(/^(\d+):?/);
if (matches)
{
tabs.select(parseInt(matches[1], 10) - 1, false); // make it zero-based
return;
}
matches = [];
let lowerBuffer = buffer.toLowerCase();
let first = tabs.index() + (reverse ? 0 : 1);
let nbrowsers = getBrowser().browsers.length;
for (let [i,] in tabs.browsers)
{
let index = (i + first) % nbrowsers;
let url = getBrowser().getBrowserAtIndex(index).contentDocument.location.href;
let title = getBrowser().getBrowserAtIndex(index).contentDocument.title.toLowerCase();
if (url == buffer)
{
tabs.select(index, false);
return;
}
if (url.indexOf(buffer) >= 0 || title.indexOf(lowerBuffer) >= 0)
matches.push(index);
}
if (matches.length == 0)
liberator.echoerr("E94: No matching buffer for " + buffer);
else if (matches.length > 1 && !allowNonUnique)
liberator.echoerr("E93: More than one match for " + buffer);
else
{
if (reverse)
{
index = matches.length - count;
while (index < 0)
index += matches.length;
}
else
index = (count - 1) % matches.length;
tabs.select(matches[index], false);
}
},
cloneTab: function (tab, activate)
{
let newTab = getBrowser().addTab();
copyTab(newTab, tab);
if (activate)
getBrowser().mTabContainer.selectedItem = newTab;
return newTab;
},
detachTab: function (tab)
{
if (!tab)
tab = getBrowser().mTabContainer.selectedItem;
window.open();
let win = services.get("windowMediator").getMostRecentWindow("navigator:browser");
copyTab(win.getBrowser().mCurrentTab, tab);
this.remove(tab, 1, false, 1);
},
selectAlternateTab: function ()
{
if (tabs.alternate == null || tabs.getTab() == tabs.alternate)
{
liberator.echoerr("E23: No alternate page");
return;
}
// 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
if (index == -1)
liberator.echoerr("E86: Buffer does not exist"); // TODO: This should read "Buffer N does not exist"
else
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
// shouldn't be one yet.
updateSelectionHistory: function ()
{
alternates = [this.getTab(), alternates[0]];
}
};
//}}}
}; //}}}
// vim: set fdm=marker sw=4 ts=4 et: