diff --git a/chrome/content/vimperator/commands.js b/chrome/content/vimperator/commands.js
index 85e1c359..4cd7326d 100644
--- a/chrome/content/vimperator/commands.js
+++ b/chrome/content/vimperator/commands.js
@@ -76,7 +76,7 @@ var g_commands = [/*{{{*/
["beep"],
"Play a system beep",
null,
- beep,
+ function() { /*vimperator.*/beep(); },
null
],
[
@@ -144,7 +144,7 @@ var g_commands = [/*{{{*/
["ec[ho]"],
"Display a string at the bottom of the window",
"Echo all arguments of this command. Useful for showing informational messages.
Multiple lines WILL be seperated by \\n.",
- echo,
+ function(args) { vimperator.echo(args); } ,
null
],
[
@@ -152,7 +152,7 @@ var g_commands = [/*{{{*/
["echoe[rr]"],
"Display an error string at the bottom of the window",
"Echo all arguments of this command highlighted in red. Useful for showing important messages.
Multiple lines WILL be seperated by \\n.",
- echoerr,
+ function(args) { vimperator.echoerr(args); } ,
null
],
[
@@ -526,7 +526,7 @@ var g_mappings = [/*{{{*/
["b {number}"],
"Open a prompt to switch buffers",
"Typing the corresponding number opens switches to this buffer",
- function (args) { /*bufshow("", true); */vimperator.commandline.open(":", "buffer ", MODE_EX); }
+ function (args) { vimperator.commandline.open(":", "buffer ", vimperator.modes.EX); }
],
[
["B"],
@@ -599,14 +599,14 @@ var g_mappings = [/*{{{*/
["o"],
"Open one or more URLs in the current tab",
"See :open for more details",
- function(count) { vimperator.commandline.open(":", "open ", MODE_EX); }
+ function(count) { vimperator.commandline.open(":", "open ", vimperator.modes.EX); }
],
[
["O"],
["O"],
"Open one ore more URLs in the current tab, based on current location",
"Works like o, but preselects current URL in the :open query.",
- function(count) { vimperator.commandline.open(":", "open " + getCurrentLocation(), MODE_EX); }
+ function(count) { vimperator.commandline.open(":", "open " + getCurrentLocation(), vimperator.modes.EX); }
],
[
["p", ""],
@@ -643,14 +643,14 @@ var g_mappings = [/*{{{*/
"Open one or more URLs in a new tab",
"Like o but open URLs in a new tab.
"+
"See :tabopen for more details",
- function(count) { vimperator.commandline.open(":", "tabopen ", MODE_EX); }
+ function(count) { vimperator.commandline.open(":", "tabopen ", vimperator.modes.EX); }
],
[
["T"],
["T"],
"Open one ore more URLs in a new tab, based on current location",
"Works like t, but preselects current URL in the :tabopen query.",
- function(count) { vimperator.commandline.open(":", "tabopen " + getCurrentLocation(), MODE_EX); }
+ function(count) { vimperator.commandline.open(":", "tabopen " + getCurrentLocation(), vimperator.modes.EX); }
],
[
["u"],
@@ -846,7 +846,7 @@ var g_mappings = [/*{{{*/
"In QuickHint mode, every hintable item (according to the 'hinttags' XPath query) is assigned a label.
"+
"If you then press the keys for a label, it is followed as soon as it can be uniquely identified and this mode is stopped. Or press <Esc> to stop this mode.
"+
"If you write the hint in ALLCAPS, the hint is followed in a background tab.",
- function(count) { hah.enableHahMode(HINT_MODE_QUICK); }
+ function(count) { hah.enableHahMode(vimperator.modes.QUICK_HINT); }
],
[
["F"],
@@ -856,7 +856,7 @@ var g_mappings = [/*{{{*/
"If you then press the keys for a label, it is followed as soon as it can be uniquely identified. Labels stay active after following a hint in this mode, press <Esc> to stop this mode.
"+
"This hint mode is especially useful for browsing large sites like Forums as hints are automatically regenerated when switching to a new document.
"+
"Also, most Ctrl-prefixed shortcut keys are available in this mode for navigation.",
- function(count) { hah.enableHahMode(HINT_MODE_ALWAYS); }
+ function(count) { hah.enableHahMode(vimperator.modes.ALWAYS_HINT); }
],
[
[";"],
@@ -877,7 +877,7 @@ var g_mappings = [/*{{{*/
""+
"Multiple hints can be seperated by commas where it makes sense. ;ab,ac,adt opens AB, AC and AD in a new tab.
"+
"Hintable elements for this mode can be set in the 'extendedhinttags' XPath string.",
- function(count) { hah.enableHahMode(HINT_MODE_EXTENDED); }
+ function(count) { hah.enableHahMode(vimperator.modes.EXTENDED_HINT); }
],
/* search managment */
@@ -916,7 +916,7 @@ var g_mappings = [/*{{{*/
[":"],
"Start command line mode",
"In command line mode, you can perform extended commands, which may require arguments.",
- function(count) { vimperator.commandline.open(":", "", MODE_EX); }
+ function(count) { vimperator.commandline.open(":", "", vimperator.modes.EX); }
],
[
["I"],
@@ -926,7 +926,7 @@ var g_mappings = [/*{{{*/
"This is especially useful, if JavaScript controlled forms like the RichEdit form fields of GMail don't work anymore.
" +
"To exit this mode, press <Esc>. If you also need to pass <Esc>"+
"in this mode to the webpage, prepend it with <C-v>.",
- function(count) { addMode(MODE_ESCAPE_ALL_KEYS);}
+ function(count) { vimperator.addMode(null, vimperator.modes.ESCAPE_ALL_KEYS);}
],
[
[""], // if you ever add/remove keys here, also check them in the onVimperatorKeypress() function
@@ -935,7 +935,7 @@ var g_mappings = [/*{{{*/
"If you need to pass a certain key to a javascript form field or another extension prefix the key with <C-v>.
"+
"Also works to unshadow Firefox shortcuts like <C-o> which are otherwise hidden in Vimperator.
"+
"When in 'ignorekeys' mode (activated by <I>), <C-v> will pass the next key to Vimperator instead of the webpage.",
- function(count) { addMode(MODE_ESCAPE_ONE_KEY); }
+ function(count) { vimperator.addMode(null, vimperator.modes.ESCAPE_ONE_KEY); }
],
[
[""],
@@ -1045,7 +1045,7 @@ var g_hint_mappings = [ /*{{{*/
["y", "hah.yankUrlHints();", true, false],
["Y", "hah.yankTextHints();", true, false],
[",", "g_inputbuffer+=','; hah.setCurrentState(0);", false, true],
- [":", "vimperator.commandline.open(':', '', MODE_EX);", false, true],
+ [":", "vimperator.commandline.open(':', '', vimperator.modes.EX);", false, true],
/* movement keys */
["", "scrollBufferRelative(0, 1);", false, true],
["", "scrollBufferRelative(0, -1);", false, true],
@@ -1075,31 +1075,6 @@ var g_hint_mappings = [ /*{{{*/
["", "", true, true]
]; /*}}}*/
-//var g_searchengines = [ /*{{{*/
-// ["google", "http://www.google.com/search?num=100&q=%s"],
-// ["lucky", "http://www.google.com/search?num=100&q=%s&btnI=I'm%20Feeling%20Lucky"],
-// ["chefkoch", "http://www.chefkoch.de/rezept-suche.php?Suchbegriff=%s"],
-// ["dewiki", "http://de.wikipedia.org/wiki/%s"],
-// ["discogs", "http://www.discogs.com/search?type=all&q=%s&btn=Search"],
-// ["geizhals", "http://geizhals.at/?fs=%s"],
-// ["imdb", "http://www.imdb.com/find?s=all&q=%s"],
-// ["leo", "http://dict.leo.org/ende?search=%s"],
-// ["wien", "http://members.aon.at/flole/vienna.html?UserQuery=%s&ResUser=1024&WidthUser=2000"],
-// ["wiki", "http://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go"],
-// ["vim", "http://www.google.com/custom?q=%s&sa=Google+Search&cof=LW%3A125%3BL%3Ahttp%3A%2F%2Fvim.sf.net%2Fimages%2Fvim.gif%3BLH%3A60%3BAH%3Acenter%3BGL%3A0%3BS%3Ahttp%3A%2F%2Fwww.vim.org%3BAWFID%3A057fa53529d52655%3B&domains=vim.sourceforge.net%3Bwww.vim.org%3Bvimdoc.sourceforge.net&sitesearch=vim.sourceforge.net"]
-//];/*}}}*/
-
-var g_modemessages = {};
-g_modemessages[MODE_NORMAL | MODE_ESCAPE_ALL_KEYS] = "ESCAPE ALL KEYS";
-g_modemessages[MODE_NORMAL | MODE_ESCAPE_ONE_KEY] = "ESCAPE ONE KEY";
-g_modemessages[MODE_NORMAL | MODE_ESCAPE_ALL_KEYS | MODE_ESCAPE_ONE_KEY] = "PASS ONE KEY";
-g_modemessages[HINT_MODE_QUICK] = "QUICK HINT";
-g_modemessages[HINT_MODE_ALWAYS] = "ALWAYS HINT";
-g_modemessages[HINT_MODE_EXTENDED] = "EXTENDED HINT";
-g_modemessages[MODE_NORMAL] = false;
-g_modemessages[MODE_INSERT] = "INSERT";
-g_modemessages[MODE_VISUAL] = "VISUAL";
-
// returns null, if the cmd cannot be found in our g_commands array, or
// otherwise a reference to our command
function get_command(cmd) // {{{
@@ -1142,7 +1117,7 @@ function execute_command(count, cmd, special, args, modifiers) // {{{
if (command === null)
{
echoerr("E492: Not an editor command: " + cmd);
- focusContent(false, false);
+ vimperator.focusContent();
return;
}
@@ -1295,7 +1270,6 @@ function openURLs(str)
if (urls.length == 0)
return false;
- //getWebNavigation().loadURI(urls[0], nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
getBrowser().loadURI(urls[0]);
for (var url=1; url < urls.length; url++)
@@ -1423,9 +1397,10 @@ function isDirectory(url)
////////////////////////////////////////////////////////////////////////
// function stolen from Conkeror
-function focusNextFrame()
+function focusNextFrame(count)
{
- try {
+ try
+ {
var frames = window.content.frames;
if (frames.length == 0)
{
@@ -2204,45 +2179,4 @@ function toggle_images() {
message ("imageBehavior set to " + pref);
}
-////////////////////////////////////////////////////////////////////////
-// mode related functions ///////////////////////////////////////// {{{1
-////////////////////////////////////////////////////////////////////////
-
-// set current mode
-function setCurrentMode(mode)
-{
- g_current_mode = mode;
- showMode();
-}
-// get current mode
-function hasMode(mode)
-{
- return g_current_mode & mode;
-}
-// add to current mode
-function addMode(mode)
-{
- g_current_mode |= mode;
- showMode();
- return g_current_mode;
-}
-// get current mode
-function removeMode(mode)
-{
- g_current_mode = (g_current_mode | mode) ^ mode;
- showMode();
- return g_current_mode;
-}
-
-function showMode()
-{
- // XXX: remove
- // showStatusbarMessage(g_current_mode, STATUSFIELD_INPUTBUFFER);
-
- if (!get_pref("showmode") || !g_modemessages[g_current_mode])
- return;
-
- echo("-- " + g_modemessages[g_current_mode] + " --");
-}
-
// vim: set fdm=marker sw=4 ts=4 et:
diff --git a/chrome/content/vimperator/find.js b/chrome/content/vimperator/find.js
index e4bd5e96..f467a406 100644
--- a/chrome/content/vimperator/find.js
+++ b/chrome/content/vimperator/find.js
@@ -247,9 +247,9 @@ function Search()
this.gFindState = [];
// Event handlers for search - closure is needed
- vimperator.registerCallback("change", MODE_SEARCH, function(command){ self.searchKeyPressed(command); });
- vimperator.registerCallback("submit", MODE_SEARCH, function(command){ self.searchSubmitted(command); });
- vimperator.registerCallback("cancel", MODE_SEARCH, function(){ self.searchCancelled(); });
+ vimperator.registerCallback("change", vimperator.modes.SEARCH_FORWARD, function(command){ self.searchKeyPressed(command); });
+ vimperator.registerCallback("submit", vimperator.modes.SEARCH_FORWARD, function(command){ self.searchSubmitted(command); });
+ vimperator.registerCallback("cancel", vimperator.modes.SEARCH_FORWARD, function(){ self.searchCancelled(); });
// Called when the search dialog is asked for. Sets up everything necessary
@@ -276,7 +276,7 @@ function Search()
this.gFindState.push(state);
this.resumeFindState(state);
- vimperator.commandline.open('/', '', MODE_SEARCH);
+ vimperator.commandline.open('/', '', vimperator.modes.SEARCH_FORWARD);
}
// Called when the current search needs to be repeated in the forward
@@ -330,7 +330,8 @@ function Search()
// Called when the enter key is pressed to trigger a search
this.searchSubmitted = function(command) {
- removeMode(MODE_SEARCH);
+ //removeMode(MODE_SEARCH);
+ vimperator.setMode(vimperator.modes.NORMAL);
if (this.lastFindState()["range"] == null) {
vimperator.echoerr("E492: Pattern not found: " + this.lastFindState()["search-str"]);
}
@@ -339,9 +340,10 @@ function Search()
// Called when the search is cancelled - for example if someone presses
// escape while typing a search
this.searchCancelled = function() {
- removeMode(MODE_SEARCH);
+ //removeMode(MODE_SEARCH);
+ vimperator.setMode(vimperator.modes.NORMAL);
clearSelection();
- focusContent(true, true);
+ vimperator.focusContent();
}
diff --git a/chrome/content/vimperator/hints.js b/chrome/content/vimperator/hints.js
index 5769af4f..bcbc15ab 100644
--- a/chrome/content/vimperator/hints.js
+++ b/chrome/content/vimperator/hints.js
@@ -27,14 +27,14 @@ function hit_a_hint()
const HINT_PREFIX = 'hah_hint_'; // prefix for the hint id
// public accessors
- this.hintsVisible = function() { return isHahModeEnabled; };
+ //this.hintsVisible = function() { return isHahModeEnabled; };
this.hintedElements = function() { return hintedElems; };
this.currentState = function() { return state;};
this.setCurrentState = function(s) { state = s;};
- this.currentMode = function() { return hintmode;};
+ //this.currentMode = function() { return hintmode;};
var isHahModeEnabled = false; // is typing mode on
- var hintmode = HINT_MODE_QUICK;
+ //var hintmode = HINT_MODE_QUICK;
var hintedElems = [];
var linkNumString = ""; // the typed link number is in this string
var linkCount = 0;
@@ -164,7 +164,8 @@ function hit_a_hint()
genElemCoords(elem);
// for extended hint mode, show all - even currently hidden - hints
- if (hintmode == HINT_MODE_QUICK && (elem.absoTop < area[1] || elem.absoTop > area[3] ||
+ //if (hintmode == HINT_MODE_QUICK && (elem.absoTop < area[1] || elem.absoTop > area[3] ||
+ if (vimperator.hasMode(vimperator.modes.QUICK_HINT) && (elem.absoTop < area[1] || elem.absoTop > area[3] ||
elem.absoLeft > area[2] || elem.absoLeft < area[0]))
continue;
@@ -209,7 +210,8 @@ function hit_a_hint()
if (!win)
win = window.content;
- if (linkCount == 0 && hintmode != HINT_MODE_ALWAYS)
+ //if (linkCount == 0 && hintmode != HINT_MODE_ALWAYS)
+ if (linkCount == 0 && !vimperator.hasMode(vimperator.modes.ALWAYS_HINT))
{
beep();
//alert('h');
@@ -395,9 +397,9 @@ function hit_a_hint()
//function enableHahMode(event, mode)
this.enableHahMode = function(mode)
{
- setCurrentMode(mode);
- showMode();
- hintmode = mode;
+ //setCurrentMode(mode);
+ vimperator.setMode(vimperator.modes.HINTS, mode);
+ //hintmode = mode;
state = 0;
linkCount = 0;
linkNumString = '';
@@ -424,13 +426,14 @@ function hit_a_hint()
if(!isHahModeEnabled)
return;
- setCurrentMode(MODE_NORMAL);
+ //setCurrentMode(MODE_NORMAL);
+ vimperator.setMode(vimperator.modes.NORMAL);
isHahModeEnabled = false;
- hintmode = HINT_MODE_QUICK;
+ //hintmode = HINT_MODE_QUICK;
linkNumString = '';
hintedElems = [];
- if (!silent && get_pref("showmode"))
- vimperator.echo('');
+// if (!silent && get_pref("showmode"))
+// vimperator.echo('');
removeHints(win);
return 0;
@@ -662,7 +665,8 @@ function hit_a_hint()
startCoordLoader(doc);
- if (hintmode == HINT_MODE_ALWAYS)
+ //if (hintmode == HINT_MODE_ALWAYS)
+ if (vimperator.hasMode(vimperator.modes.ALWAYS_HINT))
{
state = 0;
linkCount = 0;
@@ -677,7 +681,8 @@ function hit_a_hint()
window.document.addEventListener("pageshow", initDoc, null);
window.addEventListener("resize", onResize, null);
- logMessage("HAH initialized.");
+
+ logMessage("Hints initialized");
}
var hah = new hit_a_hint();
diff --git a/chrome/content/vimperator/settings.js b/chrome/content/vimperator/settings.js
index 2dd1f84a..57a75bb2 100644
--- a/chrome/content/vimperator/settings.js
+++ b/chrome/content/vimperator/settings.js
@@ -271,8 +271,8 @@ var g_settings = [/*{{{*/
":set titlestring=Mozilla Firefox.",
"string",
null,
- function(value) { set_pref("title", value); set_title(value); },
- function() { return get_pref("title"); },
+ function(value) { set_pref("titlestring", value); set_titlestring(value); },
+ function() { return get_pref("titlestring"); },
"Vimperator",
null
],
@@ -500,7 +500,7 @@ function set_showtabline(value)
}
}
-function set_title(value)
+function set_titlestring(value)
{
if (!value || typeof(value) != "string")
value = get_pref("titlestring");
diff --git a/chrome/content/vimperator/ui.js b/chrome/content/vimperator/ui.js
index 39a26673..2ca09fed 100644
--- a/chrome/content/vimperator/ui.js
+++ b/chrome/content/vimperator/ui.js
@@ -1,14 +1,4 @@
// XXX: move somehere else!
-// function save_history()
-// {
-// set_pref("comp_history", comp_history.join("\n"));
-// }
-//
-// function load_history()
-// {
-// var hist = get_pref("comp_history", "");
-// comp_history = hist.split("\n");
-// }
function multiliner(line, prev_match, heredoc)
{
@@ -17,7 +7,7 @@ function multiliner(line, prev_match, heredoc)
if (prev_match[3] === undefined) prev_match[3] = '';
if (match[4] === null)
{
- focusContent(false, true); // also sets tab_index to -1
+ vimperator.focusContent();
execute_command.apply(this, match);
}
else
@@ -25,7 +15,7 @@ function multiliner(line, prev_match, heredoc)
if (match[4] === false)
{
prev_match[3] = prev_match[3].replace(new RegExp('<<\s*' + prev_match[4]), heredoc.replace(/\n$/, ''));
- focusContent(false, true); // also sets comp_tab_index to -1
+ vimperator.focusContent(); // also sets comp_tab_index to -1
execute_command.apply(this, prev_match);
prev_match = new Array(5);
prev_match[3] = '';
@@ -155,8 +145,8 @@ function CommandLine ()
prompt = "";
if (!cmd)
cmd = "";
- if (minor_mode)
- setCurrentMode(minor_mode);
+ //if (minor_mode)
+ vimperator.setMode(vimperator.modes.COMMAND_LINE, minor_mode);
setNormalStyle();
setPrompt(prompt);
@@ -228,8 +218,9 @@ function CommandLine ()
// command_line.value = "";
// NOTE: the command is saved to the history in the blur() handler
- focusContent();
+ vimperator.focusContent();
var res = vimperator.triggerCallback("submit", command);
+ vimperator.setMode(vimperator.modes.NORMAL, null, true);
return res;
}
/* user pressed ESCAPE to cancel this prompt */
@@ -238,7 +229,7 @@ function CommandLine ()
var res = vimperator.triggerCallback("cancel");
addToHistory(command);
this.clear();
- focusContent(true, true);
+ vimperator.focusContent();
return res;
}
@@ -412,7 +403,7 @@ function CommandLine ()
if(command.length == 0)
{
this.clear();
- focusContent();
+ vimperator.focusContent();
}
}
else // any other key
@@ -712,9 +703,7 @@ function StatusLine()
if(!total_tabs || typeof(cur_index != "number"))
total_tabs = vimperator.tabs.count();
- //var tabcount_str = "[" + cur_index.toString() + "/" + total_tabs.toString() + "]";
tabcount_widget.value = "[" + cur_index.toString() + "/" + total_tabs.toString() + "]";
- //tabcount_widget.value = tabcount_str;
};
// percent is given between 0 and 1
diff --git a/chrome/content/vimperator/vimperator.js b/chrome/content/vimperator/vimperator.js
index f0672000..58975438 100644
--- a/chrome/content/vimperator/vimperator.js
+++ b/chrome/content/vimperator/vimperator.js
@@ -29,25 +29,6 @@ the terms of any one of the MPL, the GPL or the LGPL.
// The only global object, a handler to the main Vimperator object
var vimperator = null;
-// major modes - FIXME: major cleanup needed
-const MODE_NORMAL = 1;
-const MODE_INSERT = 2;
-const MODE_VISUAL = 4;
-const MODE_ESCAPE_ONE_KEY = 8;
-const MODE_ESCAPE_ALL_KEYS = 16;
-const MODE_HINTS = 2048;
- const HINT_MODE_QUICK = 32;
- const HINT_MODE_ALWAYS = 64;
- const HINT_MODE_EXTENDED = 128;
-const MODE_COMMAND_LINE = 4096;
- const MODE_EX = 256;
- const MODE_SEARCH = 512;
- const MODE_SEARCH_BACKWARD = 1024;
-// need later?
-//const MODE_BROWSER
-//const MODE_CARET
-
-var g_current_mode = MODE_NORMAL;
var popup_allowed_events; // need to change and reset this firefox pref
var g_inputbuffer = ""; // here we store partial commands (e.g. 'g' if you want to type 'gg')
@@ -95,8 +76,13 @@ nsBrowserStatusHandler.prototype =
if (link == "")
{
- vimperator.statusline.updateUrl();
- showMode();
+ if (ssli == 1)
+ vimperator.statusline.updateUrl();
+ else if (ssli == 2)
+ {
+ //vimperator.echo("");
+ vimperator.setMode(); // trick to reshow the mode in the command line
+ }
}
},
setJSStatus : function(status) { },
@@ -136,7 +122,8 @@ nsBrowserStatusHandler.prototype =
gURLBar.value = url;
// onLocationChange is also called when switching/deleting tabs
- if (hah.currentMode() != HINT_MODE_ALWAYS)
+ //if (hah.currentMode() != HINT_MODE_ALWAYS)
+ if (vimperator.hasMode(vimperator.modes.HINTS) && !vimperator.hasMode(vimperator.modes.ALWAYS_HINT))
hah.disableHahMode();
vimperator.statusline.updateUrl(url);
@@ -188,15 +175,19 @@ function init()
// these inner classes are only created here, because outside the init()
// function, the chrome:// is not ready
Vimperator.prototype.qm = new QM;
+// alert("ini3");
+// Vimperator.prototype.commandline = new CommandLine;
Vimperator.prototype.search = new Search;
+// alert("ini4");
Vimperator.prototype.previewwindow = new InformationList("vimperator-preview-window", { incremental_fill: false, max_items: 10 });
+// alert("ini5");
Vimperator.prototype.bufferwindow = new InformationList("vimperator-buffer-window", { incremental_fill: false, max_items: 10 });
Vimperator.prototype.statusline = new StatusLine();
Vimperator.prototype.tabs = new Tabs();
// XXX: move elsewhere
- vimperator.registerCallback("submit", MODE_EX, function(command) { /*vimperator.*/execute(command); } );
- vimperator.registerCallback("complete", MODE_EX, function(str) { return exTabCompletion(str); } );
+ vimperator.registerCallback("submit", vimperator.modes.EX, function(command) { /*vimperator.*/execute(command); } );
+ vimperator.registerCallback("complete", vimperator.modes.EX, function(str) { return exTabCompletion(str); } );
//status_line = document.getElementById("vim-statusbar");
command_line = document.getElementById("vim-commandbar");
@@ -316,24 +307,13 @@ function init()
-
-
-
-
-
// this function adds all our required listeners to react on events
// also stuff like window.onScroll is handled there.
addEventListeners();
- // we always start in normal mode
- setCurrentMode(MODE_NORMAL);
-
- /*** load our preferences ***/
- // load_history(); FIXME
-
set_showtabline(get_pref("showtabline"));
set_guioptions(get_pref("guioptions"));
- set_title();
+ set_titlestring();
// work around firefox popup blocker
popup_allowed_events = get_firefox_pref('dom.popup_allowed_events', 'change click dblclick mouseup reset submit');
@@ -348,17 +328,14 @@ function init()
if (get_pref("firsttime", true))
{
setTimeout(function() {
- //var tab = openURLsInNewTab("about:blank", true);
- //BrowserStop();
- help(null, null, null, {inTab: true});
+ help(null, null, null, { inTab: true });
set_pref("firsttime", false);
}, 1000);
}
-
gURLBar.blur();
- focusContent(true, true);
+ vimperator.focusContent();
// everything important is done, register a preload handler to speed up first time history cache
if(get_pref("preload"))
@@ -367,11 +344,8 @@ function init()
// firefox preferences which we need to be changed to work well with vimperator
set_firefox_pref("browser.startup.page", 3); // start with saved session
-
- /*
- * Finally, read a ~/.vimperatorrc
- * Make sourcing asynchronous, otherwise commands that open new tabs won't work
- */
+ // Finally, read a ~/.vimperatorrc
+ // Make sourcing asynchronous, otherwise commands that open new tabs won't work
setTimeout(function() {
source("~/.vimperatorrc", true);
logMessage("~/.vimperatorrc sourced");
@@ -385,6 +359,8 @@ function unload()
/*** save our preferences ***/
vimperator.commandline.saveHistory();
+ // TODO: removeEventListeners();
+
// reset some modified firefox prefs
if (get_firefox_pref('dom.popup_allowed_events', 'change click dblclick mouseup reset submit')
== popup_allowed_events + " keypress")
@@ -392,283 +368,17 @@ function unload()
}
-////////////////////////////////////////////////////////////////////////
-// keyboard input handling //////////////////////////////////////// {{{1
-////////////////////////////////////////////////////////////////////////
-function onVimperatorKeypress(event)/*{{{*/
-{
- // change the event to a usable string representation
- var key = keyToString(event);
- //alert(key);
- if (key == null)
- return false;
-
- if(event.type == "keydown")
- {
- logObject(event);
- logObject(event.target);
- return;//alert(event.target.id);
- }
- // sometimes the non-content area has focus, making our keys not work
-// if (event.target.id == "main-window")
-// alert("focusContent();");
-
-
- // XXX: ugly hack for now pass certain keys to firefox as they are without beeping
- // also fixes key navigation in menus, etc.
- if (key == "" || key == "" || key == "" || key == "" || key == "")
- return false;
-
- // XXX: for now only, later: input mappings if form element focused
- if (isFormElemFocused())
- return false;
-
- // handle Escape-one-key mode (Ctrl-v)
- if (hasMode(MODE_ESCAPE_ONE_KEY) && !hasMode(MODE_ESCAPE_ALL_KEYS))
- {
- removeMode(MODE_ESCAPE_ONE_KEY);
- showMode();
- return false;
- }
- // handle Escape-all-keys mode (I)
- if (hasMode(MODE_ESCAPE_ALL_KEYS))
- {
- if(hasMode(MODE_ESCAPE_ONE_KEY))
- removeMode(MODE_ESCAPE_ONE_KEY); // and then let flow continue
- else if (key == "" || key == "" || key == "")
- ; // let flow continue to handle these keys
- else
- return false;
- }
-
-// // FIXME: handle middle click in content area {{{
-// // alert(event.target.id);
-// if (/*event.type == 'mousedown' && */event.button == 1 && event.target.id == 'content')
-// {
-// //echo("foo " + event.target.id);
-// //if (document.commandDispatcher.focusedElement == command_line.inputField)
-// {
-// //alert(command_line.value.substring(0, command_line.selectionStart));
-// command_line.value = command_line.value.substring(0, command_line.selectionStart) +
-// readFromClipboard() +
-// command_line.value.substring(command_line.selectionEnd, command_line.value.length);
-// alert(command_line.value);
-// }
-// //else
-// // {
-// // openURLs(readFromClipboard());
-// // }
-// return true;
-// } }}}
-
-
-
- // if Hit-a-hint mode is on, special handling of keys is required
- // g_hint_mappings is used
- // FIXME: total mess
- if (hah.hintsVisible())
- {
- // never propagate this key to firefox, when hints are visible
- event.preventDefault();
- event.stopPropagation();
-
- for (i = 0; i < g_hint_mappings.length; i++)
- {
- if(g_hint_mappings[i][0] == key)
- {
- if(g_hint_mappings[i][3] == true || hah.currentState() == 1)
- {
- //g_hint_mappings[i][1].call(this, event);
- eval(g_hint_mappings[i][1]);
- if (g_hint_mappings[i][2] == true) // stop processing this event
- {
- hah.disableHahMode();
- g_inputbuffer = "";
- vimperator.statusline.updateInputBuffer("");
- return false;
- }
- else
- {
- // FIXME: make sure that YOU update the statusbar message yourself
- // first in g_hint_mappings when in this mode!
- vimperator.statusline.updateInputBuffer(g_inputbuffer);
- return false;
- }
- }
- }
- }
-
- // no mapping found, beep()
- if (hah.currentState() == 1)
- {
- beep();
- hah.disableHahMode();
- g_inputbuffer = "";
- vimperator.statusline.updateInputBuffer(g_inputbuffer);
- return true;
- }
-
- // if we came here, let hit-a-hint process the key as it is part
- // of a partial link
- var res = hah.processEvent(event);
- if (res < 0) // error occured processing this key
- {
- beep();
- if(hah.currentMode() == HINT_MODE_QUICK)
- hah.disableHahMode();
- else // ALWAYS mode
- hah.resetHintedElements();
- g_inputbuffer = "";
- }
- else if (res == 0 || hah.currentMode() == HINT_MODE_EXTENDED) // key processed, part of a larger hint
- g_inputbuffer += key;
- else // this key completed a quick hint
- {
- // if the hint is all in UPPERCASE, open it in new tab
- g_inputbuffer += key;
- if (g_inputbuffer.toUpperCase() == g_inputbuffer)
- hah.openHints(true, false);
- else // open in current window
- hah.openHints(false, false);
-
- if(hah.currentMode() == HINT_MODE_QUICK)
- hah.disableHahMode();
- else // ALWAYS mode
- hah.resetHintedElements();
-
- g_inputbuffer = "";
- }
-
- vimperator.statusline.updateInputBuffer(g_inputbuffer);
- return true;
- }
-
- // set this variable to true, if we have the start of a mapping
- var couldBecomeCompleteMapping = false;
- var count_str = g_inputbuffer.match(/^[0-9]*/)[0];
-
- // counts must be at the start of a complete mapping (10j -> go 10 lines down)
- if (event.charCode >= 48 && event.charCode <= 57 && !(event.ctrlKey || event.altKey))
- {
- if (g_inputbuffer.search(/[^0-9]/) != -1)
- {
- g_inputbuffer = "";
- beep();
- vimperator.statusline.updateInputBuffer(g_inputbuffer);
- return true;
- }
- else
- {
- // handle '0' specially to allow binding of 0
- if (g_inputbuffer != "" || key != "0")
- {
- g_inputbuffer += key;
- vimperator.statusline.updateInputBuffer(g_inputbuffer);
- return true;
- }
- // else let the flow continue, and check if 0 is a mapping
- }
- }
-
- for (var i in g_mappings)
- {
- // each internal mapping can have multiple keys
- for (var j in g_mappings[i][COMMANDS])
- {
- var mapping = g_mappings[i][COMMANDS][j];
- // alert("key: " + key +" - mapping: "+ mapping + " - g_input: " + g_inputbuffer);
- if(count_str + mapping == g_inputbuffer + key)
- //if (count_str + mapping == vimperator.commandline.getCommand() + key)
- {
- g_count = parseInt(count_str, 10);
- if (isNaN(g_count))
- g_count = -1;
-
- // allow null (= no operation) mappings
- if(g_mappings[i][FUNCTION] != null)
- g_mappings[i][FUNCTION].call(this, g_count);
-
- // command executed, reset input buffer
- g_inputbuffer = "";
- vimperator.statusline.updateInputBuffer(g_inputbuffer);
- event.preventDefault();
- event.stopPropagation();
- return false;
- }
- else if ((count_str+mapping).indexOf(g_inputbuffer + key) == 0)
- //else if ((count_str+mapping).indexOf(vimperator.commandline.getCommand() + key) == 0)
- {
- couldBecomeCompleteMapping = true;
- }
- }
- }
-
- if (couldBecomeCompleteMapping)
- {
- g_inputbuffer += key;
- event.preventDefault();
- event.stopPropagation();
- }
- else
- {
- g_inputbuffer = "";
- beep();
- }
-
- vimperator.statusline.updateInputBuffer(g_inputbuffer);
- return false;
-}/*}}}*/
-
////////////////////////////////////////////////////////////////////////
// focus and mode handling //////////////////////////////////////// {{{1
////////////////////////////////////////////////////////////////////////
-/* After pressing Escape, put focus on a non-input field of the browser document */
-function focusContent(clear_command_line, clear_statusline)
-{
- try
- {
- g_count = -1; // clear count
-
-// if(clear_command_line)
-// {
-// command_line.value = "";
-// command_line.inputField.setAttribute("style","font-family: monospace;");
-//
-// var commandBarPrompt = document.getElementById('vim-commandbar-prompt');
-// commandBarPrompt.style.visibility = 'collapsed';
-// commandBarPrompt.value = '';
-//
-// //vimperator.commandline.clear();
-// }
-//
-// if(clear_statusline)
-// {
-// completion_list.hidden = true;
-// comp_tab_index = COMPLETION_UNINITIALIZED;
-// comp_history_index = -1;
-// }
-
- var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
- .getService(Components.interfaces.nsIWindowWatcher);
- if (window == ww.activeWindow && document.commandDispatcher.focusedElement)
- {
- document.commandDispatcher.focusedElement.blur();
- }
- content.focus();
-
- } catch(e)
- {
- vimperator.echoerr(e);
- }
-}
-
function onEscape()
{
- if (!hasMode(MODE_ESCAPE_ONE_KEY))
+ if (!vimperator.hasMode(vimperator.modes.ESCAPE_ONE_KEY))
{
- setCurrentMode(MODE_NORMAL);
+ vimperator.setMode(vimperator.modes.NORMAL);
+ vimperator.echo("");
hah.disableHahMode();
- focusContent(true, true);
+ vimperator.focusContent();
vimperator.statusline.updateUrl();
}
}
@@ -679,8 +389,9 @@ function onEscape()
function addEventListeners()
{
window.addEventListener("unload", unload, false);
- window.addEventListener("keypress", onVimperatorKeypress, true);
-// window.addEventListener("keydown", onVimperatorKeypress, true);
+
+ window.addEventListener("keypress", vimperator.onEvent, true);
+ //window.addEventListener("keypress", onVimperatorKeypress, true);
// this handler is for middle click only in the content
//window.addEventListener("mousedown", onVimperatorKeypress, true);
@@ -693,11 +404,12 @@ function addEventListeners()
window.addEventListener("TabClose", vimperator.statusline.updateTabCount, false);
window.addEventListener("TabSelect", function(event)
{
- if (hah.currentMode == HINT_MODE_ALWAYS)
- {
- hah.disableHahMode();
- hah.enableHahMode(HINT_MODE_ALWAYS);
- }
+ // FIXME:
+// if (hah.currentMode == HINT_MODE_ALWAYS)
+// {
+// hah.disableHahMode();
+// hah.enableHahMode(HINT_MODE_ALWAYS);
+// }
vimperator.statusline.updateTabCount();
}, false);
@@ -793,7 +505,7 @@ function isFormElemFocused()
tagname == "textarea" ||
// tagName == "SELECT" ||
// tagName == "BUTTON" ||
- tagname == "isindex") // isindex is deprecated one-line input box
+ tagname == "isindex") // isindex is a deprecated one-line input box
return true;
return false;
@@ -983,13 +695,70 @@ function getLinkNodes(doc)
return links;
}//}}}
-//vimperator = new function()
function Vimperator()
{
////////////////////////////////////////////////////////////////////////////////
////////////////////// PRIVATE SECTION /////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
+ this.modes = { // actually not private, but Firefox complains if this doesn't come first
+ // main modes
+ NONE: 0,
+ NORMAL: 1 << 0,
+ INSERT: 1 << 1,
+ VISUAL: 1 << 2,
+ HINTS: 1 << 3,
+ COMMAND_LINE: 1 << 4,
+ // extended modes
+ EX: 1 << 5,
+ SEARCH_FORWARD: 1 << 6,
+ SEARCH_BACKWARD: 1 << 7,
+ ESCAPE_ONE_KEY: 1 << 8,
+ ESCAPE_ALL_KEYS: 1 << 9,
+ QUICK_HINT: 1 << 10,
+ EXTENDED_HINT: 1 << 11,
+ ALWAYS_HINT: 1 << 12
+ }
+ var mode_messages = {};
+ mode_messages[this.modes.NORMAL] = "";
+ mode_messages[this.modes.INSERT] = "INSERT";
+ mode_messages[this.modes.VISUAL] = "VISUAL";
+ mode_messages[this.modes.HINTS] = "HINTS";
+ mode_messages[this.modes.ESCAPE_ONE_KEY] = "escape one key";
+ mode_messages[this.modes.ESCAPE_ALL_KEYS] = "escape all keys";
+ mode_messages[this.modes.ESCAPE_ONE_KEY | this.modes.ESCAPE_ALL_KEYS] = "pass one key";
+ mode_messages[this.modes.QUICK_HINT] = "quick";
+ mode_messages[this.modes.EXTENDED_HINT] = "extended";
+ mode_messages[this.modes.ALWAYS_HINT] = "always";
+
var callbacks = new Array();
+ var mode = this.modes.NORMAL;
+ var extended_mode = this.modes.NONE;
+ var count = -1;
+ var inputbuffer = "";
+
+ function showMode()
+ {
+ if (!get_pref("showmode"))
+ return;
+
+ var str_mode = mode_messages[mode];
+ var str_extended = mode_messages[extended_mode];
+ if(!str_mode && !str_extended)
+ {
+ vimperator.echo("");
+ return;
+ }
+
+ if(str_mode && str_extended)
+ str_extended = " (" + str_extended + ")";
+ else
+ {
+ str_extended = "(" + str_extended + ")";
+ str_mode = "";
+ }
+
+ vimperator.echo("-- " + str_mode + str_extended + " --");
+ }
////////////////////////////////////////////////////////////////////////////////
////////////////////// PUBLIC SECTION //////////////////////////////////////////
@@ -1009,18 +778,304 @@ function Vimperator()
{
for (i in callbacks)
{
- [typ, mode, func] = callbacks[i];
- if (hasMode(mode) && type == typ)
- return func.call(this, data);
+ [thistype, thismode, thisfunc] = callbacks[i];
+ if (vimperator.hasMode(thismode) && type == thistype)
+ return thisfunc.call(this, data);
}
return false;
}
- this.foo = function () {alert("foo");};
-
// just forward these echo commands
this.echo = this.commandline.echo;
this.echoerr = this.commandline.echoErr;
+
+ // set current mode
+ // use "null" if you only want to set one of those modes
+ this.setMode = function(main, extended, silent)
+ {
+ // if a main mode is set, the extended is always cleared
+ if (main)
+ {
+ mode = main;
+ extended_mode = this.modes.NONE;
+ }
+ if (typeof(extended) === "number")
+ extended_mode = extended;
+
+ if (typeof(silent) == "undefined" || !silent)
+ showMode();
+ }
+ // returns true if "whichmode" is found in either the main or
+ // extended mode
+ this.hasMode = function(whichmode)
+ {
+ return ((mode & whichmode) || (extended_mode & whichmode) > 0) ? true : false;
+ }
+ this.addMode = function(main, extended)
+ {
+ if (main)
+ mode |= main;
+ if (extended)
+ extended_mode |= extended;
+
+ showMode();
+ }
+ // always show the new mode in the statusline
+ this.removeMode = function(main, extended)
+ {
+ if (main)
+ mode = (mode | main) ^ main;
+ if (extended)
+ extended_mode = (extended_mode | extended) ^ extended;
+
+ showMode();
+ }
+
+ /* After pressing Escape, put focus on a non-input field of the browser document */
+ this.focusContent = function()
+ {
+// count = -1;
+// inputbuffer = "";
+
+ var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].getService(Components.interfaces.nsIWindowWatcher);
+ if (window == ww.activeWindow && document.commandDispatcher.focusedElement)
+ document.commandDispatcher.focusedElement.blur();
+
+ content.focus();
+ //this.setMode(vimperator.modes.NORMAL);
+ //showMode();
+ }
+
+ this.onEvent = function(event)/*{{{*/
+ {
+ if (event.type != "keypress")
+ return false;
+
+ // change the event to a usable string representation
+ var key = keyToString(event);
+ //alert(key);
+ if (key == null)
+ return false;
+
+ if(event.type == "keydown")
+ {
+ logObject(event);
+ logObject(event.target);
+ return;//alert(event.target.id);
+ }
+ // sometimes the non-content area has focus, making our keys not work
+ // if (event.target.id == "main-window")
+ // alert("focusContent();");
+
+
+ // XXX: ugly hack for now pass certain keys to firefox as they are without beeping
+ // also fixes key navigation in menus, etc.
+ if (key == "" || key == "" || key == "" || key == "" || key == "")
+ return false;
+
+ // XXX: for now only, later: input mappings if form element focused
+ if (isFormElemFocused())
+ return false;
+
+ // handle Escape-one-key mode (Ctrl-v)
+ if (vimperator.hasMode(vimperator.modes.ESCAPE_ONE_KEY) && !vimperator.hasMode(vimperator.modes.ESCAPE_ALL_KEYS))
+ {
+ vimperator.removeMode(null, vimperator.modes.ESCAPE_ONE_KEY);
+ return false;
+ }
+ // handle Escape-all-keys mode (I)
+ if (vimperator.hasMode(vimperator.modes.ESCAPE_ALL_KEYS))
+ {
+ if(vimperator.hasMode(vimperator.modes.ESCAPE_ONE_KEY))
+ vimperator.removeMode(null, vimperator.modes.ESCAPE_ONE_KEY); // and then let flow continue
+ else if (key == "" || key == "" || key == "")
+ ; // let flow continue to handle these keys
+ else
+ return false;
+ }
+
+ // // FIXME: handle middle click in content area {{{
+ // // alert(event.target.id);
+ // if (/*event.type == 'mousedown' && */event.button == 1 && event.target.id == 'content')
+ // {
+ // //echo("foo " + event.target.id);
+ // //if (document.commandDispatcher.focusedElement == command_line.inputField)
+ // {
+ // //alert(command_line.value.substring(0, command_line.selectionStart));
+ // command_line.value = command_line.value.substring(0, command_line.selectionStart) +
+ // readFromClipboard() +
+ // command_line.value.substring(command_line.selectionEnd, command_line.value.length);
+ // alert(command_line.value);
+ // }
+ // //else
+ // // {
+ // // openURLs(readFromClipboard());
+ // // }
+ // return true;
+ // } }}}
+
+
+
+ // if Hit-a-hint mode is on, special handling of keys is required
+ // g_hint_mappings is used
+ // FIXME: total mess
+ //if (hah.hintsVisible())
+ if (vimperator.hasMode(vimperator.modes.HINTS))
+ {
+ // never propagate this key to firefox, when hints are visible
+ event.preventDefault();
+ event.stopPropagation();
+
+ for (i = 0; i < g_hint_mappings.length; i++)
+ {
+ if(g_hint_mappings[i][0] == key)
+ {
+ if(g_hint_mappings[i][3] == true || hah.currentState() == 1)
+ {
+ //g_hint_mappings[i][1].call(this, event);
+ eval(g_hint_mappings[i][1]);
+ if (g_hint_mappings[i][2] == true) // stop processing this event
+ {
+ hah.disableHahMode();
+ g_inputbuffer = "";
+ vimperator.statusline.updateInputBuffer("");
+ return false;
+ }
+ else
+ {
+ // FIXME: make sure that YOU update the statusbar message yourself
+ // first in g_hint_mappings when in this mode!
+ vimperator.statusline.updateInputBuffer(g_inputbuffer);
+ return false;
+ }
+ }
+ }
+ }
+
+ // no mapping found, beep()
+ if (hah.currentState() == 1)
+ {
+ beep();
+ hah.disableHahMode();
+ g_inputbuffer = "";
+ vimperator.statusline.updateInputBuffer(g_inputbuffer);
+ return true;
+ }
+
+ // if we came here, let hit-a-hint process the key as it is part
+ // of a partial link
+ var res = hah.processEvent(event);
+ if (res < 0) // error occured processing this key
+ {
+ beep();
+ //if(hah.currentMode() == HINT_MODE_QUICK)
+ if(vimperator.hasMode(vimperator.modes.QUICK_HINT))
+ hah.disableHahMode();
+ else // ALWAYS mode
+ hah.resetHintedElements();
+ g_inputbuffer = "";
+ }
+ //else if (res == 0 || hah.currentMode() == HINT_MODE_EXTENDED) // key processed, part of a larger hint
+ else if (res == 0 || vimperator.hasMode(vimperator.modes.EXTENDED_HINT)) // key processed, part of a larger hint
+ g_inputbuffer += key;
+ else // this key completed a quick hint
+ {
+ // if the hint is all in UPPERCASE, open it in new tab
+ g_inputbuffer += key;
+ if (g_inputbuffer.toUpperCase() == g_inputbuffer)
+ hah.openHints(true, false);
+ else // open in current window
+ hah.openHints(false, false);
+
+ //if(hah.currentMode() == HINT_MODE_QUICK)
+ if(vimperator.hasMode(vimperator.modes.QUICK_HINT))
+ hah.disableHahMode();
+ else // ALWAYS mode
+ hah.resetHintedElements();
+
+ g_inputbuffer = "";
+ }
+
+ vimperator.statusline.updateInputBuffer(g_inputbuffer);
+ return true;
+ }
+
+ // set this variable to true, if we have the start of a mapping
+ var couldBecomeCompleteMapping = false;
+ var count_str = g_inputbuffer.match(/^[0-9]*/)[0];
+
+ // counts must be at the start of a complete mapping (10j -> go 10 lines down)
+ if (event.charCode >= 48 && event.charCode <= 57 && !(event.ctrlKey || event.altKey))
+ {
+ if (g_inputbuffer.search(/[^0-9]/) != -1)
+ {
+ g_inputbuffer = "";
+ beep();
+ vimperator.statusline.updateInputBuffer(g_inputbuffer);
+ return true;
+ }
+ else
+ {
+ // handle '0' specially to allow binding of 0
+ if (g_inputbuffer != "" || key != "0")
+ {
+ g_inputbuffer += key;
+ vimperator.statusline.updateInputBuffer(g_inputbuffer);
+ return true;
+ }
+ // else let the flow continue, and check if 0 is a mapping
+ }
+ }
+
+ for (var i in g_mappings)
+ {
+ // each internal mapping can have multiple keys
+ for (var j in g_mappings[i][COMMANDS])
+ {
+ var mapping = g_mappings[i][COMMANDS][j];
+ // alert("key: " + key +" - mapping: "+ mapping + " - g_input: " + g_inputbuffer);
+ if(count_str + mapping == g_inputbuffer + key)
+ //if (count_str + mapping == vimperator.commandline.getCommand() + key)
+ {
+ g_count = parseInt(count_str, 10);
+ if (isNaN(g_count))
+ g_count = -1;
+
+ // allow null (= no operation) mappings
+ if(g_mappings[i][FUNCTION] != null)
+ g_mappings[i][FUNCTION].call(this, g_count);
+
+ // command executed, reset input buffer
+ g_inputbuffer = "";
+ vimperator.statusline.updateInputBuffer(g_inputbuffer);
+ event.preventDefault();
+ event.stopPropagation();
+ return false;
+ }
+ else if ((count_str+mapping).indexOf(g_inputbuffer + key) == 0)
+ //else if ((count_str+mapping).indexOf(vimperator.commandline.getCommand() + key) == 0)
+ {
+ couldBecomeCompleteMapping = true;
+ }
+ }
+ }
+
+ if (couldBecomeCompleteMapping)
+ {
+ g_inputbuffer += key;
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ else
+ {
+ g_inputbuffer = "";
+ beep();
+ }
+
+ vimperator.statusline.updateInputBuffer(g_inputbuffer);
+ return false;
+ }/*}}}*/
+ // alert('end');
}
// provides functions for working with tabs