1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-22 21:37:58 +01:00
Files
pentadactyl-pm/content/tabs.js
2008-02-29 23:47:27 +00:00

708 lines
26 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.
(c) 2006-2008: Martin Stubenschrott <stubenschrott@gmx.net>
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 *****/
vimperator.Tabs = function () //{{{
{
////////////////////////////////////////////////////////////////////////////////
////////////////////// PRIVATE SECTION /////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
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)
{
var position = getBrowser().mTabContainer.selectedIndex;
var length = getBrowser().mTabs.length;
var 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;
}
// hide tabs initially
getBrowser().mStrip.getElementsByClassName("tabbrowser-tabs")[0].collapsed = true;
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// OPTIONS /////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
vimperator.options.add(["activate", "act"],
"Define when tabs are automatically activated",
"stringlist", "homepage,quickmark,tabopen,paste",
{
validator: function (value)
{
return value.split(",").every(function (item) { return /^(homepage|quickmark|tabopen|paste|)$/.test(item); });
}
});
vimperator.options.add(["popups", "pps"],
"Where to show requested popup windows",
"number", 1,
{
setter: function (value)
{
var 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
vimperator.options.setPref("browser.link.open_newwindow.restriction", values[value][0]);
vimperator.options.setPref("browser.link.open_newwindow", values[value][1]);
},
validator: function (value) { return (value >= 0 && value <= 3); }
});
vimperator.options.add(["showtabline", "stal"],
"Control when to show the tab bar of opened web pages",
"number", 2,
{
setter: function (value)
{
var tabs = getBrowser().mStrip.getElementsByClassName("tabbrowser-tabs")[0];
if (!tabs)
return;
if (value == 0)
{
tabs.collapsed = true;
}
else if (value == 1)
{
vimperator.options.setPref("browser.tabs.autoHide", true);
tabs.collapsed = false;
}
else
{
vimperator.options.setPref("browser.tabs.autoHide", false);
tabs.collapsed = false;
}
},
validator: function (value) { return (value >= 0 && value <= 2); }
});
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// MAPPINGS ////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
vimperator.mappings.add([vimperator.modes.NORMAL], ["b"],
"Open a prompt to switch buffers",
function () { vimperator.commandline.open(":", "buffer! ", vimperator.modes.EX); });
vimperator.mappings.add([vimperator.modes.NORMAL], ["B"],
"Show buffer list",
function () { vimperator.tabs.list(false); });
vimperator.mappings.add([vimperator.modes.NORMAL], ["d"],
"Delete current buffer",
function (count) { vimperator.tabs.remove(getBrowser().mCurrentTab, count, false, 0); },
{ flags: vimperator.Mappings.flags.COUNT });
vimperator.mappings.add([vimperator.modes.NORMAL], ["D"],
"Delete current buffer, focus tab to the left",
function (count) { vimperator.tabs.remove(getBrowser().mCurrentTab, count, true, 0); },
{ flags: vimperator.Mappings.flags.COUNT });
vimperator.mappings.add([vimperator.modes.NORMAL], ["gb"],
"Repeat last :buffer[!] command",
function (count) { vimperator.tabs.switchTo(null, null, count, false); },
{ flags: vimperator.Mappings.flags.COUNT });
vimperator.mappings.add([vimperator.modes.NORMAL], ["gB"],
"Repeat last :buffer[!] command in reverse direction",
function (count) { vimperator.tabs.switchTo(null, null, count, true); },
{ flags: vimperator.Mappings.flags.COUNT });
vimperator.mappings.add([vimperator.modes.NORMAL], ["g0", "g^"],
"Go to the first tab",
function (count) { vimperator.tabs.select(0); });
vimperator.mappings.add([vimperator.modes.NORMAL], ["g$"],
"Go to the last tab",
function (count) { vimperator.tabs.select("$"); });
vimperator.mappings.add([vimperator.modes.NORMAL], ["gt", "<C-n>", "<C-Tab>", "<C-PageDown>"],
"Go to the next tab",
function (count) { vimperator.tabs.select(count > 0 ? count - 1: "+1", count > 0 ? false : true); },
{ flags: vimperator.Mappings.flags.COUNT });
vimperator.mappings.add([vimperator.modes.NORMAL], ["gT", "<C-p>", "<C-S-Tab>", "<C-PageUp>"],
"Go to previous tab",
function (count) { vimperator.tabs.select("-" + (count < 1 ? 1 : count), true); },
{ flags: vimperator.Mappings.flags.COUNT });
vimperator.mappings.add([vimperator.modes.NORMAL], ["u"],
"Undo closing of a tab",
function (count) { vimperator.commands.get("undo").execute("", false, count); },
{ flags: vimperator.Mappings.flags.COUNT });
vimperator.mappings.add([vimperator.modes.NORMAL], ["<C-^>", "<C-6>"],
"Select the alternate tab",
function ()
{
if (vimperator.tabs.alternate == null || vimperator.tabs.getTab() == vimperator.tabs.alternate)
{
vimperator.echoerr("E23: No alternate page");
return;
}
// NOTE: this currently relies on v.tabs.index() returning the
// currently selected tab index when passed null
var index = vimperator.tabs.index(vimperator.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)
vimperator.echoerr("E86: Buffer does not exist"); // TODO: This should read "Buffer N does not exist"
else
vimperator.tabs.select(index);
});
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// COMMANDS ////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
vimperator.commands.add(["bd[elete]", "bw[ipeout]", "bun[load]", "tabc[lose]"],
"Delete current buffer",
function (args, special, count)
{
vimperator.tabs.remove(getBrowser().mCurrentTab, count > 0 ? count : 1, special, 0);
});
vimperator.commands.add(["b[uffer]"],
"Switch to a buffer",
function (args, special) { vimperator.tabs.switchTo(args, special); },
{ completer: function (filter) { return vimperator.completion.buffer(filter); } });
vimperator.commands.add(["buffers", "files", "ls", "tabs"],
"Show a list of all buffers",
function (args, special)
{
if (args)
{
vimperator.echoerr("E488: Trailing characters");
return;
}
vimperator.tabs.list(special);
});
vimperator.commands.add(["quita[ll]", "qa[ll]"],
"Quit " + vimperator.config.appName,
function () { vimperator.quit(false); });
vimperator.commands.add(["reloada[ll]"],
"Reload all tab pages",
function (args, special) { vimperator.tabs.reloadAll(special); });
vimperator.commands.add(["tab"],
"Execute a command and tell it to output in a new tab",
function (args) { vimperator.execute(args, { inTab: true }); },
{ completer: function (filter) { return vimperator.completion.command(filter); } });
vimperator.commands.add(["tabl[ast]"],
"Switch to the last tab",
function () { vimperator.tabs.select("$", false); });
vimperator.commands.add(["tabm[ove]"],
"Move the current tab after tab N",
function (args, special)
{
// FIXME: tabmove! N should probably produce an error
if (!/^([+-]?\d+|)$/.test(args))
{
vimperator.echoerr("E488: Trailing characters");
return;
}
if (!args)
args = "$"; // if not specified, move to the last tab
vimperator.tabs.move(getBrowser().mCurrentTab, args, special);
});
// TODO: count support
vimperator.commands.add(["tabn[ext]", "tn[ext]"],
"Switch to the next or [count]th tab",
function (args)
{
if (!args)
{
vimperator.tabs.select("+1", true);
}
else if (/^\d+$/.test(args))
{
var index = parseInt(args, 10) - 1;
if (index < vimperator.tabs.count)
vimperator.tabs.select(index, true);
else
vimperator.beep();
}
else
{
vimperator.echoerr("E488: Trailing characters");
}
});
vimperator.commands.add(["tabo[nly]"],
"Close all other tabs",
function () { vimperator.tabs.keepOnly(getBrowser().mCurrentTab); });
vimperator.commands.add(["tabopen", "t[open]", "tabnew", "tabe[dit]"],
"Open one or more URLs in a new tab",
function (args, special)
{
var where = special ? vimperator.NEW_TAB : vimperator.NEW_BACKGROUND_TAB;
if (/\btabopen\b/.test(vimperator.options["activate"]))
where = special ? vimperator.NEW_BACKGROUND_TAB : vimperator.NEW_TAB;
if (args)
vimperator.open(args, where);
else
vimperator.open("about:blank", where);
},
{ completer: function (filter) { return vimperator.completion.url(filter); } });
// TODO: count support
vimperator.commands.add(["tabp[revious]", "tp[revious]", "tabN[ext]", "tN[ext]"],
"Switch to the previous tab or go [count] tabs back",
function (args)
{
if (!args)
vimperator.tabs.select("-1", true);
else if (/^\d+$/.test(args))
vimperator.tabs.select("-" + args, true); // FIXME: urgh!
else
vimperator.echoerr("E488: Trailing characters");
});
vimperator.commands.add(["tabr[ewind]", "tabfir[st]"],
"Switch to the first tab",
function () { vimperator.tabs.select(0, false); });
// TODO: extract common functionality of "undoall"
vimperator.commands.add(["u[ndo]"],
"Undo closing of a tab",
function (args, special, count)
{
if (count < 1)
count = 1;
if (args)
{
var ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
getService(Components.interfaces.nsISessionStore);
var undoItems = eval("(" + ss.getClosedTabData(window) + ")");
for (var i = 0; i < undoItems.length; i++)
{
if (undoItems[i].state.entries[0].url == args)
{
count = i + 1;
break;
}
}
}
undoCloseTab(count - 1);
},
{
completer: function (filter)
{
// get closed-tabs from nsSessionStore
var ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
getService(Components.interfaces.nsISessionStore);
var undoItems = eval("(" + ss.getClosedTabData(window) + ")");
var completions = [];
for (var i = 0; i < undoItems.length; i++)
{
// undoItems[i].image is also available if needed for favicons
var url = undoItems[i].state.entries[0].url;
var title = undoItems[i].title;
if (vimperator.completion.match([url, title], filter, false))
completions.push([url, title]);
}
return [0, completions];
}
});
vimperator.commands.add(["undoa[ll]"],
"Undo closing of all closed tabs",
function (args, special, count)
{
if (count > -1)
{
vimperator.echoerr("E481: No range allowed");
return;
}
if (special)
{
vimperator.echoerr("E477: No ! allowed");
return;
}
var ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
getService(Components.interfaces.nsISessionStore);
var undoItems = eval("(" + ss.getClosedTabData(window) + ")");
for (var i = 0; i < undoItems.length; i++)
undoCloseTab(); // doesn't work with i as the index to undoCloseTab
});
if (vimperator.has("session"))
{
vimperator.commands.add(["wqa[ll]", "wq", "xa[ll]"],
"Save the session and quit",
function () { vimperator.quit(true); });
}
/////////////////////////////////////////////////////////////////////////////}}}
////////////////////// PUBLIC SECTION //////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////{{{
return {
get alternate() { return alternates[1]; },
get count() { return getBrowser().mTabs.length; },
// @returns the index of the currently selected tab starting with 0
index: function (tab)
{
if (tab)
{
var length = getBrowser().mTabs.length;
for (var i = 0; i < length; i++)
{
if (getBrowser().mTabs[i] == tab)
return i;
}
return -1;
}
return getBrowser().mTabContainer.selectedIndex;
},
// TODO: implement filter
// @returns an array of tabs which match filter
get: function (filter)
{
var buffers = [];
var browsers = getBrowser().browsers;
for (var i in browsers)
{
var title = browsers[i].contentTitle || "(Untitled)";
var uri = browsers[i].currentURI.spec;
var number = i + 1;
buffers.push([number, title, uri]);
}
return buffers;
},
getTab: function (index)
{
if (index)
return getBrowser().mTabs[index];
return getBrowser().mTabContainer.selectedItem;
},
list: function (fullmode)
{
if (fullmode)
{
// toggle the special buffer preview window
if (vimperator.bufferwindow.visible())
{
vimperator.bufferwindow.hide();
}
else
{
var items = vimperator.completion.buffer("")[1];
vimperator.bufferwindow.show(items);
vimperator.bufferwindow.selectItem(getBrowser().mTabContainer.selectedIndex);
}
}
else
{
// TODO: move this to vimperator.buffers.get()
var items = vimperator.completion.buffer("")[1];
var number, indicator, title, url;
var list = ":" + (vimperator.util.escapeHTML(vimperator.commandline.getCommand()) || "buffers") + "<br/>" + "<table>";
for (var i = 0; i < items.length; i++)
{
if (i == vimperator.tabs.index())
indicator = " <span style=\"color: blue\">%</span> ";
else if (i == vimperator.tabs.index(vimperator.tabs.alternate))
indicator = " <span style=\"color: blue\">#</span> ";
else
indicator = " ";
[number, title] = items[i][0].split(/:\s+/, 2);
url = items[i][1];
url = vimperator.util.escapeHTML(url);
title = vimperator.util.escapeHTML(title);
list += "<tr><td align=\"right\"> " + number + "</td><td>" + indicator +
"</td><td style=\"width: 250px; max-width: 500px; overflow: hidden;\">" + title +
"</td><td><a href=\"#\" class=\"hl-URL buffer-list\">" + url + "</a></td></tr>";
}
list += "</table>";
vimperator.commandline.echo(list, vimperator.commandline.HL_NORMAL, vimperator.commandline.FORCE_MULTILINE);
}
},
// 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)
{
var 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)
{
function removeOrBlankTab (tab)
{
if (getBrowser().mTabs.length > 1)
getBrowser().removeTab(tab);
else
{
if (vimperator.buffer.URL != "about:blank" ||
getWebNavigation().sessionHistory.count > 0)
{
vimperator.open("about:blank", vimperator.NEW_BACKGROUND_TAB);
getBrowser().removeTab(tab);
}
else
vimperator.beep();
}
}
if (typeof count != "number" || count < 1)
count = 1;
if (quitOnLastTab >= 1 && getBrowser().mTabs.length <= count)
{
if (vimperator.windows.length > 1)
window.close();
else
vimperator.quit(quitOnLastTab == 2);
return;
}
var index = this.index(tab);
if (focusLeftTab)
{
var lastRemovedTab = 0;
for (var i = index; i > index - count && i >= 0; i--)
{
removeOrBlankTab(this.getTab(i));
lastRemovedTab = i > 0 ? i : 1;
}
getBrowser().mTabContainer.selectedIndex = lastRemovedTab - 1;
}
else
{
var i = index + count - 1;
if (i >= this.count)
i = this.count - 1;
for (; i >= index; i--)
removeOrBlankTab(this.getTab(i));
}
},
keepOnly: function (tab)
{
getBrowser().removeAllTabsBut(tab);
},
select: function (spec, wrap)
{
var index = indexFromSpec(spec, wrap);
// FIXME:
if (index === -1)
{
vimperator.beep(); // XXX: move to ex-handling?
return;
}
getBrowser().mTabContainer.selectedIndex = index;
},
reload: function (tab, bypassCache)
{
if (bypassCache)
{
const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
const flags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
getBrowser().getBrowserForTab(tab).reloadWithFlags(flags);
}
else
{
getBrowser().reloadTab(tab);
}
},
reloadAll: function (bypassCache)
{
if (bypassCache)
{
for (var 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;
}
else if (buffer != null)
{
// store this command, so it can be repeated with "B"
lastBufferSwitchArgs = buffer;
lastBufferSwitchSpecial = allowNonUnique;
}
else
{
buffer = lastBufferSwitchArgs;
if (typeof allowNonUnique == "undefined" || allowNonUnique == null)
allowNonUnique = lastBufferSwitchSpecial;
}
if (!count || count < 1)
count = 1;
if (typeof reverse != "boolean")
reverse = false;
var match;
if (match = buffer.match(/^(\d+):?/))
{
vimperator.tabs.select(parseInt(match[1], 10) - 1, false); // make it zero-based
return;
}
var matches = [];
var lowerBuffer = buffer.toLowerCase();
var first = vimperator.tabs.index() + (reverse ? 0 : 1);
for (var i = 0; i < getBrowser().browsers.length; i++)
{
var index = (i + first) % getBrowser().browsers.length;
var url = getBrowser().getBrowserAtIndex(index).contentDocument.location.href;
var title = getBrowser().getBrowserAtIndex(index).contentDocument.title.toLowerCase();
if (url == buffer)
{
vimperator.tabs.select(index, false);
return;
}
if (url.indexOf(buffer) >= 0 || title.indexOf(lowerBuffer) >= 0)
matches.push(index);
}
if (matches.length == 0)
vimperator.echoerr("E94: No matching buffer for " + buffer);
else if (matches.length > 1 && !allowNonUnique)
vimperator.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;
vimperator.tabs.select(matches[index], false);
}
},
// TODO: 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: