diff --git a/content/commands.js b/content/commands.js index 2e4bbd38..66cdb8c9 100644 --- a/content/commands.js +++ b/content/commands.js @@ -145,15 +145,6 @@ vimperator.Commands = function () //{{{ var ex_commands = []; var last_run_command = ""; // updated whenever the users runs a command with :! - function addDefaultCommand(command) - { - ex_commands.push(command); - vimperator.Commands.prototype[command.name] = function (args, special, count, modifiers) - { - command.execute(args, special, count, modifiers); - }; - } - // in '-quoted strings, only ' and \ itself are escaped // in "-quoted strings, also ", \n and \t are translated // in non-quoted strings everything is taken literally apart from "\ " and "\\" @@ -452,87 +443,90 @@ vimperator.Commands = function () //{{{ ////////////////////// PUBLIC SECTION ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - this.__iterator__ = function () - { - return commandsIterator(); - }; + var commandManager = { - this.add = function (command) - { - if (!command) - return false; - - ex_commands.push(command); - - return true; - }; - - this.get = function (name) - { - for (var i = 0; i < ex_commands.length; i++) + __iterator__: function () { - if (ex_commands[i].hasName(name)) - return ex_commands[i]; - } + return commandsIterator(); + }, - return null; - }; - - // TODO: generalized 0 count handling -> "Zero count" - // FIXME: doesn't really belong here... - // return [null, null, null, null, heredoc_tag || false]; - // [count, cmd, special, args] = match; - this.parseCommand = function (str, tag) - { - // remove comments - str.replace(/\s*".*$/, ""); - - if (tag) // we already have a multiline heredoc construct + add: function (command) { - if (str == tag) - return [null, null, null, null, false]; + this[command.name] = function (args, special, count, modifiers) + { + command.execute(args, special, count, modifiers); + }; + ex_commands.push(command); + }, + + get: function (name) + { + for (var i = 0; i < ex_commands.length; i++) + { + if (ex_commands[i].hasName(name)) + return ex_commands[i]; + } + + return null; + }, + + // TODO: generalized 0 count handling -> "Zero count" + // FIXME: doesn't really belong here... + // return [null, null, null, null, heredoc_tag || false]; + // [count, cmd, special, args] = match; + parseCommand: function (str, tag) + { + // remove comments + str.replace(/\s*".*$/, ""); + + if (tag) // we already have a multiline heredoc construct + { + if (str == tag) + return [null, null, null, null, false]; + else + return [null, null, null, str, tag]; + } + + // 0 - count, 1 - cmd, 2 - special, 3 - args, 4 - heredoc tag + var matches = str.match(/^:*(\d+)?([a-zA-Z]+|!)(!)?(?:\s*(.*?)\s*)?$/); + if (!matches) + return [null, null, null, null, null]; + matches.shift(); + + // parse count + if (matches[0]) + matches[0] = parseInt(matches[0], 10); else - return [null, null, null, str, tag]; + matches[0] = -1; + + matches[2] = !!matches[2]; + matches.push(null); + if (matches[3]) + { + tag = matches[3].match(/<<\s*(\w+)\s*$/); + if (tag && tag[1]) + matches[4] = tag[1]; + } + else + matches[3] = ""; + + return matches; } - // 0 - count, 1 - cmd, 2 - special, 3 - args, 4 - heredoc tag - var matches = str.match(/^:*(\d+)?([a-zA-Z]+|!)(!)?(?:\s*(.*?)\s*)?$/); - if (!matches) - return [null, null, null, null, null]; - matches.shift(); - - // parse count - if (matches[0]) - matches[0] = parseInt(matches[0], 10); - else - matches[0] = -1; - - matches[2] = !!matches[2]; - matches.push(null); - if (matches[3]) - { - tag = matches[3].match(/<<\s*(\w+)\s*$/); - if (tag && tag[1]) - matches[4] = tag[1]; - } - else - matches[3] = ""; - - return matches; }; /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// DEFAULT COMMANDS //////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - addDefaultCommand(new vimperator.Command(["addo[ns]"], + commandManager.add(new vimperator.Command(["addo[ns]"], function () { vimperator.open("chrome://mozapps/content/extensions/extensions.xul", vimperator.NEW_TAB); }, { short_help: "Show available Browser Extensions and Themes", help: "You can add/remove/disable browser extensions from this dialog.
Be aware that not all Firefox extensions work, because Vimperator overrides some key bindings and changes Firefox's GUI." } )); - addDefaultCommand(new vimperator.Command(["ba[ck]"], + commandManager.add(new vimperator.Command(["ba[ck]"], function (args, special, count) { if (special) @@ -575,7 +569,7 @@ vimperator.Commands = function () //{{{ } } )); - addDefaultCommand(new vimperator.Command(["bd[elete]", "bw[ipeout]", "bun[load]", "tabc[lose]"], + commandManager.add(new vimperator.Command(["bd[elete]", "bw[ipeout]", "bun[load]", "tabc[lose]"], function (args, special, count) { vimperator.tabs.remove(getBrowser().mCurrentTab, count > 0 ? count : 1, special, 0); }, { usage: ["[count]bd[elete][!]"], @@ -584,13 +578,13 @@ vimperator.Commands = function () //{{{ "Do :bdelete! to select the tab to the left after removing the current tab." } )); - addDefaultCommand(new vimperator.Command(["beep"], + commandManager.add(new vimperator.Command(["beep"], function () { vimperator.beep(); }, { short_help: "Play a system beep" } )); - addDefaultCommand(new vimperator.Command(["bma[rk]"], + commandManager.add(new vimperator.Command(["bma[rk]"], function (args) { var res = parseArgs(args, this.args); @@ -628,7 +622,7 @@ vimperator.Commands = function () //{{{ [["-keyword", "-k"], OPTION_STRING, function (arg) { return /\w/.test(arg); }]] } )); - addDefaultCommand(new vimperator.Command(["bmarks"], + commandManager.add(new vimperator.Command(["bmarks"], function (args, special) { var res = parseArgs(args, this.args); @@ -649,7 +643,7 @@ vimperator.Commands = function () //{{{ args: [[["-tags", "-T"], OPTION_LIST]] } )); - addDefaultCommand(new vimperator.Command(["b[uffer]"], + commandManager.add(new vimperator.Command(["b[uffer]"], function (args, special) { vimperator.buffer.switchTo(args, special); }, { usage: ["b[uffer][!] {url|index}"], @@ -662,7 +656,7 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.get_buffer_completions(filter); } } )); - addDefaultCommand(new vimperator.Command(["buffers", "files", "ls", "tabs"], + commandManager.add(new vimperator.Command(["buffers", "files", "ls", "tabs"], function (args, special) { if (args) @@ -680,7 +674,7 @@ vimperator.Commands = function () //{{{ "Call the special version of this command again to close the window." } )); - addDefaultCommand(new vimperator.Command(["delbm[arks]"], + commandManager.add(new vimperator.Command(["delbm[arks]"], function (args, special) { var url = args; @@ -701,7 +695,7 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.bookmarks.get(filter); } } )); - addDefaultCommand(new vimperator.Command(["com[mand]"], + commandManager.add(new vimperator.Command(["com[mand]"], function (args) { var res = parseArgs(args, this.args); @@ -719,7 +713,7 @@ vimperator.Commands = function () //{{{ [["-bar"], OPTION_NOARG]] } )); - addDefaultCommand(new vimperator.Command(["delm[arks]"], + commandManager.add(new vimperator.Command(["delm[arks]"], function (args, special) { if (!special && !args) @@ -772,7 +766,7 @@ vimperator.Commands = function () //{{{ } )); - addDefaultCommand(new vimperator.Command(["delqm[arks]"], + commandManager.add(new vimperator.Command(["delqm[arks]"], function (args, special) { // TODO: finish arg parsing - we really need a proper way to do this. :) @@ -801,7 +795,7 @@ vimperator.Commands = function () //{{{ ":delqmarks! deletes all QuickMarks" } )); - addDefaultCommand(new vimperator.Command(["downl[oads]", "dl"], + commandManager.add(new vimperator.Command(["downl[oads]", "dl"], function () { vimperator.open("chrome://mozapps/content/downloads/downloads.xul", vimperator.NEW_TAB); }, { short_help: "Show progress of current downloads", @@ -838,7 +832,7 @@ vimperator.Commands = function () //{{{ return arg; } - addDefaultCommand(new vimperator.Command(["ec[ho]"], + commandManager.add(new vimperator.Command(["ec[ho]"], function (args) { var res = argToString(args, true); @@ -854,7 +848,7 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.javascript(filter); } } )); - addDefaultCommand(new vimperator.Command(["echoe[rr]"], + commandManager.add(new vimperator.Command(["echoe[rr]"], function (args) { var res = argToString(args, false); @@ -868,7 +862,7 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.javascript(filter); } } )); - addDefaultCommand(new vimperator.Command(["exe[cute]"], + commandManager.add(new vimperator.Command(["exe[cute]"], function (args) { // TODO: :exec has some difficult semantics -> later @@ -886,13 +880,13 @@ vimperator.Commands = function () //{{{ help: "Example: :execute echo test shows a message with the text "test".
" } )); - addDefaultCommand(new vimperator.Command(["exu[sage]"], + commandManager.add(new vimperator.Command(["exu[sage]"], function (args, special, count, modifiers) { vimperator.help("commands", special, null, modifiers); }, { short_help: "Show help for Ex commands" } )); - addDefaultCommand(new vimperator.Command(["fo[rward]", "fw"], + commandManager.add(new vimperator.Command(["fo[rward]", "fw"], function (args, special, count) { if (special) @@ -935,14 +929,14 @@ vimperator.Commands = function () //{{{ } } )); - addDefaultCommand(new vimperator.Command(["ha[rdcopy]"], + commandManager.add(new vimperator.Command(["ha[rdcopy]"], function () { getBrowser().contentWindow.print(); }, { short_help: "Print current document", help: "Open a GUI dialog where you can select the printer, number of copies, orientation, etc." } )); - addDefaultCommand(new vimperator.Command(["h[elp]"], + commandManager.add(new vimperator.Command(["h[elp]"], function (args, special, count, modifiers) { vimperator.help(args, special, null, modifiers); }, { usage: ["h[elp] {subject}"], @@ -958,7 +952,7 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.get_help_completions(filter); } } )); - addDefaultCommand(new vimperator.Command(["hist[ory]", "hs"], + commandManager.add(new vimperator.Command(["hist[ory]", "hs"], function (args, special) { vimperator.history.list(args, special); }, { usage: ["hist[ory] [filter]", "history!"], @@ -968,7 +962,7 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.history.get(filter); } } )); - addDefaultCommand(new vimperator.Command(["javas[cript]", "js"], + commandManager.add(new vimperator.Command(["javas[cript]", "js"], function (args, special) { if (special) // open javascript console @@ -1018,7 +1012,7 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.javascript(filter); } } )); - addDefaultCommand(new vimperator.Command(["let"], + commandManager.add(new vimperator.Command(["let"], function (args) { if (!args) @@ -1116,7 +1110,7 @@ vimperator.Commands = function () //{{{ } )); // code for abbreviations - addDefaultCommand(new vimperator.Command(["ab[breviate]"], + commandManager.add(new vimperator.Command(["ab[breviate]"], function (args) { if (!args) @@ -1140,7 +1134,7 @@ vimperator.Commands = function () //{{{ "List all abbreviations, if no arguments to are given.
" } )); - addDefaultCommand(new vimperator.Command(["ca[bbrev]"], + commandManager.add(new vimperator.Command(["ca[bbrev]"], function (args) { if (!args) @@ -1162,7 +1156,7 @@ vimperator.Commands = function () //{{{ help: "Same as :ab[reviate], but for Command-line mode only." } )); - addDefaultCommand(new vimperator.Command(["ia[bbrev]"], + commandManager.add(new vimperator.Command(["ia[bbrev]"], function (args) { if (!args) @@ -1184,14 +1178,14 @@ vimperator.Commands = function () //{{{ help: "Same as :ab[breviate], but for Insert mode only." } )); - addDefaultCommand(new vimperator.Command(["una[bbreviate]"], + commandManager.add(new vimperator.Command(["una[bbreviate]"], function (args) { vimperator.editor.removeAbbreviation("!", args); }, { usage: ["una[bbreviate] {lhs}"], short_help: "Remove an abbreviation" } )); - addDefaultCommand(new vimperator.Command(["cuna[bbrev]"], + commandManager.add(new vimperator.Command(["cuna[bbrev]"], function (args) { vimperator.editor.removeAbbreviation("c", args); }, { usage: ["cuna[bbrev] {lhs}"], @@ -1199,7 +1193,7 @@ vimperator.Commands = function () //{{{ help: "Same as :una[bbreviate], but for Command-line mode only." } )); - addDefaultCommand(new vimperator.Command(["iuna[bbrev]"], + commandManager.add(new vimperator.Command(["iuna[bbrev]"], function (args) { vimperator.editor.removeAbbreviation("i", args); }, { usage: ["iuna[bbrev] {lhs}"], @@ -1207,15 +1201,15 @@ vimperator.Commands = function () //{{{ help: "Same as :una[bbreviate], but for Insert mode only." } )); - addDefaultCommand(new vimperator.Command(["abc[lear]"], + commandManager.add(new vimperator.Command(["abc[lear]"], function (args) { vimperator.editor.removeAllAbbreviations("!"); }, { short_help: "Remove all abbreviations" } )); - addDefaultCommand(new vimperator.Command(["cabc[lear]"], + commandManager.add(new vimperator.Command(["cabc[lear]"], function (args) { vimperator.editor.removeAllAbbreviations("c"); }, { short_help: "Remove all abbreviations for Command-line mode" } )); - addDefaultCommand(new vimperator.Command(["iabc[lear]"], + commandManager.add(new vimperator.Command(["iabc[lear]"], function (args) { vimperator.editor.removeAllAbbreviations("i"); }, { short_help: "Remove all abbreviations for Insert mode" } )); @@ -1255,7 +1249,7 @@ vimperator.Commands = function () //{{{ vimperator.mappings.list(vimperator.modes.NORMAL, lhs); } } - addDefaultCommand(new vimperator.Command(["map"], + commandManager.add(new vimperator.Command(["map"], function (args) { map(args, false); }, { usage: ["map {lhs} {rhs}", "map {lhs}", "map"], @@ -1264,7 +1258,7 @@ vimperator.Commands = function () //{{{ "Mappings are NOT saved during sessions, make sure you put them in your vimperatorrc file!" } )); - addDefaultCommand(new vimperator.Command(["mapc[lear]"], + commandManager.add(new vimperator.Command(["mapc[lear]"], function (args) { if (args) @@ -1281,7 +1275,7 @@ vimperator.Commands = function () //{{{ ":map or :noremap are cleared." } )); - addDefaultCommand(new vimperator.Command(["ma[rk]"], + commandManager.add(new vimperator.Command(["ma[rk]"], function (args) { if (!args) @@ -1307,7 +1301,7 @@ vimperator.Commands = function () //{{{ short_help: "Mark current location within the web page" } )); - addDefaultCommand(new vimperator.Command(["marks"], + commandManager.add(new vimperator.Command(["marks"], function (args) { // ignore invalid mark characters unless there are no valid mark chars @@ -1326,7 +1320,7 @@ vimperator.Commands = function () //{{{ help: "If [arg] is specified then limit the list to those marks mentioned." } )); - addDefaultCommand(new vimperator.Command(["mkv[imperatorrc]"], + commandManager.add(new vimperator.Command(["mkv[imperatorrc]"], function (args, special) { // TODO: "E172: Only one file name allowed" @@ -1385,7 +1379,7 @@ vimperator.Commands = function () //{{{ "WARNING: this differs from Vim's behavior which defaults to writing the file in the current directory." } )); - addDefaultCommand(new vimperator.Command(["noh[lsearch]"], + commandManager.add(new vimperator.Command(["noh[lsearch]"], function (args) { vimperator.search.clear(); @@ -1396,7 +1390,7 @@ vimperator.Commands = function () //{{{ "'hlsearch' option is set." } )); - addDefaultCommand(new vimperator.Command(["norm[al]"], + commandManager.add(new vimperator.Command(["norm[al]"], function (args, special) { if (!args) @@ -1415,7 +1409,7 @@ vimperator.Commands = function () //{{{ } )); // TODO: remove duplication in :map - addDefaultCommand(new vimperator.Command(["no[remap]"], + commandManager.add(new vimperator.Command(["no[remap]"], function (args) { map(args, true); }, { usage: ["no[remap] {lhs} {rhs}", "no[remap] {lhs}", "no[remap]"], @@ -1423,7 +1417,7 @@ vimperator.Commands = function () //{{{ help: "No remapping of the {rhs} is performed." } )); - addDefaultCommand(new vimperator.Command(["o[pen]", "e[dit]"], + commandManager.add(new vimperator.Command(["o[pen]", "e[dit]"], function (args, special) { if (args) @@ -1466,20 +1460,20 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.get_url_completions(filter); } } )); - addDefaultCommand(new vimperator.Command(["pa[geinfo]"], + commandManager.add(new vimperator.Command(["pa[geinfo]"], function () { vimperator.buffer.pageInfo(true); }, { short_help: "Show various page information", help: "See :help 'pageinfo' for available options", } )); - addDefaultCommand(new vimperator.Command(["pc[lose]"], + commandManager.add(new vimperator.Command(["pc[lose]"], function () { vimperator.previewwindow.hide(); }, { short_help: "Close preview window on bottom of screen" } )); - addDefaultCommand(new vimperator.Command(["pref[erences]", "prefs"], + commandManager.add(new vimperator.Command(["pref[erences]", "prefs"], function (args, special, count, modifiers) { if (!args) @@ -1511,7 +1505,7 @@ vimperator.Commands = function () //{{{ ":prefs! opens about:config in the current tab where you can change advanced Firefox preferences." } )); - addDefaultCommand(new vimperator.Command(["qma[rk]"], + commandManager.add(new vimperator.Command(["qma[rk]"], function (args) { if (!args) @@ -1535,7 +1529,7 @@ vimperator.Commands = function () //{{{ ":qmark f http://forum1.com, http://forum2.com, imdb some artist" } )); - addDefaultCommand(new vimperator.Command(["qmarks"], + commandManager.add(new vimperator.Command(["qmarks"], function (args) { // ignore invalid mark characters unless there are no valid mark chars @@ -1554,7 +1548,7 @@ vimperator.Commands = function () //{{{ help: "If [arg] is specified then limit the list to those QuickMarks mentioned." } )); - addDefaultCommand(new vimperator.Command(["q[uit]"], + commandManager.add(new vimperator.Command(["q[uit]"], function () { vimperator.tabs.remove(getBrowser().mCurrentTab, 1, false, 1); }, { short_help: "Quit current tab", @@ -1562,14 +1556,14 @@ vimperator.Commands = function () //{{{ "last window, close Vimperator. When quitting Vimperator, the session is not stored." } )); - addDefaultCommand(new vimperator.Command(["quita[ll]", "qa[ll]"], + commandManager.add(new vimperator.Command(["quita[ll]", "qa[ll]"], function () { vimperator.quit(false); }, { short_help: "Quit Vimperator", help: "Quit Vimperator, no matter how many tabs/windows are open. The session is not stored." } )); - addDefaultCommand(new vimperator.Command(["redr[aw]"], + commandManager.add(new vimperator.Command(["redr[aw]"], function () { var wu = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor). @@ -1581,7 +1575,7 @@ vimperator.Commands = function () //{{{ help: "Useful to update the screen halfway executing a script or function." } )); - addDefaultCommand(new vimperator.Command(["re[load]"], + commandManager.add(new vimperator.Command(["re[load]"], function (args, special) { vimperator.tabs.reload(getBrowser().mCurrentTab, special); }, { usage: ["re[load][!]"], @@ -1589,7 +1583,7 @@ vimperator.Commands = function () //{{{ help: "Forces reloading of the current page. If ! is given, skip the cache." } )); - addDefaultCommand(new vimperator.Command(["reloada[ll]"], + commandManager.add(new vimperator.Command(["reloada[ll]"], function (args, special) { vimperator.tabs.reloadAll(special); }, { usage: ["reloada[ll][!]"], @@ -1597,14 +1591,14 @@ vimperator.Commands = function () //{{{ help: "Forces reloading of all pages. If ! is given, skip the cache." } )); - addDefaultCommand(new vimperator.Command(["res[tart]"], + commandManager.add(new vimperator.Command(["res[tart]"], function () { vimperator.restart(); }, { short_help: "Force the browser to restart", help: "Useful when installing extensions." } )); - addDefaultCommand(new vimperator.Command(["sav[eas]", "w[rite]"], + commandManager.add(new vimperator.Command(["sav[eas]", "w[rite]"], function () { saveDocument(window.content.document); }, { short_help: "Save current web page to disk", @@ -1612,7 +1606,7 @@ vimperator.Commands = function () //{{{ "There, you can save the current web page to disk with various options." } )); - addDefaultCommand(new vimperator.Command(["se[t]"], + commandManager.add(new vimperator.Command(["se[t]"], // TODO: support setting multiple options at once function (args, special, count, modifiers) { @@ -1827,7 +1821,7 @@ vimperator.Commands = function () //{{{ } )); // TODO: sclose instead? - addDefaultCommand(new vimperator.Command(["sbcl[ose]"], + commandManager.add(new vimperator.Command(["sbcl[ose]"], function (args) { if (args) @@ -1845,7 +1839,7 @@ vimperator.Commands = function () //{{{ )); // TODO: sopen instead? Separate :sidebar from :sbopen and make them behave // more like :cw, :cope etc - addDefaultCommand(new vimperator.Command(["sideb[ar]", "sb[ar]", "sbope[n]"], + commandManager.add(new vimperator.Command(["sideb[ar]", "sb[ar]", "sbope[n]"], function (args) { if (!args) @@ -1880,7 +1874,7 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.get_sidebar_completions(filter); } } )); - addDefaultCommand(new vimperator.Command(["so[urce]"], + commandManager.add(new vimperator.Command(["so[urce]"], function (args, special) { // FIXME: implement proper filename quoting @@ -1907,14 +1901,14 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.get_file_completions(filter); } } )); - addDefaultCommand(new vimperator.Command(["st[op]"], + commandManager.add(new vimperator.Command(["st[op]"], BrowserStop, { short_help: "Stop loading", help: "Stop loading current web page." } )); - addDefaultCommand(new vimperator.Command(["tab"], + commandManager.add(new vimperator.Command(["tab"], function (args) { vimperator.execute(args, { inTab: true }); }, { usage: ["tab {cmd}"], @@ -1925,13 +1919,13 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.get_command_completions(filter); } } )); - addDefaultCommand(new vimperator.Command(["tabl[ast]"], + commandManager.add(new vimperator.Command(["tabl[ast]"], function () { vimperator.tabs.select("$", false); }, { short_help: "Switch to the last tab" } )); - addDefaultCommand(new vimperator.Command(["tabm[ove]"], + commandManager.add(new vimperator.Command(["tabm[ove]"], function (args, special) { vimperator.tabs.move(getBrowser().mCurrentTab, args, special); }, { usage: ["tabm[ove] [N]", "tabm[ove][!] +N | -N"], @@ -1940,7 +1934,7 @@ vimperator.Commands = function () //{{{ "N can also be prefixed with '+' or '-' to indicate a relative movement. If ! is specified the movement wraps around the start or end of the tab list." } )); - addDefaultCommand(new vimperator.Command(["tabn[ext]", "tn[ext]"], + commandManager.add(new vimperator.Command(["tabn[ext]", "tn[ext]"], // TODO: count support function (args) { @@ -1967,13 +1961,13 @@ vimperator.Commands = function () //{{{ help: "Cycles to the first tab when the last is selected and {count} is not specified." } )); - addDefaultCommand(new vimperator.Command(["tabo[nly]"], + commandManager.add(new vimperator.Command(["tabo[nly]"], function () { vimperator.tabs.keepOnly(getBrowser().mCurrentTab); }, { short_help: "Close all other tabs" } )); - addDefaultCommand(new vimperator.Command(["tabopen", "t[open]", "tabnew", "tabe[dit]"], + commandManager.add(new vimperator.Command(["tabopen", "t[open]", "tabnew", "tabe[dit]"], function (args, special) { var where = special ? vimperator.NEW_TAB : vimperator.NEW_BACKGROUND_TAB; @@ -1993,7 +1987,7 @@ vimperator.Commands = function () //{{{ completer: function (filter) { return vimperator.completion.get_url_completions(filter); } } )); - addDefaultCommand(new vimperator.Command(["tabp[revious]", "tp[revious]", "tabN[ext]", "tN[ext]"], + commandManager.add(new vimperator.Command(["tabp[revious]", "tp[revious]", "tabN[ext]", "tN[ext]"], // TODO: count support function (args) { @@ -2010,14 +2004,14 @@ vimperator.Commands = function () //{{{ help: "Wraps around from the first tab to the last tab." } )); - addDefaultCommand(new vimperator.Command(["tabr[ewind]", "tabfir[st]"], + commandManager.add(new vimperator.Command(["tabr[ewind]", "tabfir[st]"], function () { vimperator.tabs.select(0, false); }, { usage: ["tabr[ewind]", "tabfir[st]"], short_help: "Switch to the first tab" } )); - addDefaultCommand(new vimperator.Command(["time"], + commandManager.add(new vimperator.Command(["time"], function (args, special, count) { try @@ -2108,7 +2102,7 @@ vimperator.Commands = function () //{{{ "Use the special version with [!] if you just want to run any command multiple times without showing profiling statistics." } )); - addDefaultCommand(new vimperator.Command(["u[ndo]"], + commandManager.add(new vimperator.Command(["u[ndo]"], function (args, special, count) { if (count < 1) @@ -2152,7 +2146,7 @@ vimperator.Commands = function () //{{{ } } )); - addDefaultCommand(new vimperator.Command(["undoa[ll]"], + commandManager.add(new vimperator.Command(["undoa[ll]"], function (args, special, count) { if (count > -1) @@ -2176,7 +2170,7 @@ vimperator.Commands = function () //{{{ help: "Firefox stores up to 10 closed tabs, even after a browser restart." } )); - addDefaultCommand(new vimperator.Command(["unl[et]"], + commandManager.add(new vimperator.Command(["unl[et]"], function (args, special) { if (!args) @@ -2208,7 +2202,7 @@ vimperator.Commands = function () //{{{ "Several variable names can be given." } )); - addDefaultCommand(new vimperator.Command(["unm[ap]"], + commandManager.add(new vimperator.Command(["unm[ap]"], function (args) { if (!args) @@ -2230,7 +2224,7 @@ vimperator.Commands = function () //{{{ help: "" } )); - addDefaultCommand(new vimperator.Command(["ve[rsion]"], + commandManager.add(new vimperator.Command(["ve[rsion]"], function (args, special) { if (special) @@ -2245,13 +2239,13 @@ vimperator.Commands = function () //{{{ help: "You can show the Firefox version page with :version!." } )); - addDefaultCommand(new vimperator.Command(["viu[sage]"], + commandManager.add(new vimperator.Command(["viu[sage]"], function (args, special, count, modifiers) { vimperator.help("mappings", special, null, modifiers); }, { short_help: "Show help for normal mode commands" } )); - addDefaultCommand(new vimperator.Command(["winc[lose]", "wc[lose]"], + commandManager.add(new vimperator.Command(["winc[lose]", "wc[lose]"], function (args) { window.close(); @@ -2261,7 +2255,7 @@ vimperator.Commands = function () //{{{ short_help: "Close window" } )); - addDefaultCommand(new vimperator.Command(["wino[pen]", "wo[pen]", "wine[dit]"], + commandManager.add(new vimperator.Command(["wino[pen]", "wo[pen]", "wine[dit]"], function (args) { if (args) @@ -2275,7 +2269,7 @@ vimperator.Commands = function () //{{{ help: "Like :open but open URLs in a new window.
" } )); - addDefaultCommand(new vimperator.Command(["wqa[ll]", "wq", "xa[ll]"], + commandManager.add(new vimperator.Command(["wqa[ll]", "wq", "xa[ll]"], function () { vimperator.quit(true); }, { usage: ["wqa[ll]", "xa[ll]"], @@ -2284,7 +2278,7 @@ vimperator.Commands = function () //{{{ ":wq is different as in Vim, as it closes the window instead of just one tab by popular demand. Complain on the mailing list, if you want to change that." } )); - addDefaultCommand(new vimperator.Command(["zo[om]"], + commandManager.add(new vimperator.Command(["zo[om]"], function (args, special) { var level; @@ -2329,7 +2323,7 @@ vimperator.Commands = function () //{{{ "Normally this command operates on the text zoom, if used with [!] it operates on full zoom." } )); - addDefaultCommand(new vimperator.Command(["!", "run"], + commandManager.add(new vimperator.Command(["!", "run"], function (args, special) { // :!! needs to be treated specially as the command parser sets the special flag but removes the ! from args @@ -2354,6 +2348,8 @@ vimperator.Commands = function () //{{{ } )); //}}} + + return commandManager; }; //}}} // vim: set fdm=marker sw=4 ts=4 et: diff --git a/content/events.js b/content/events.js index 7551bcdc..2cd3cf22 100644 --- a/content/events.js +++ b/content/events.js @@ -244,637 +244,642 @@ vimperator.Events = function () //{{{ /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// PUBLIC SECTION ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - this.wantsModeReset = true; // used in onFocusChange since Firefox is so buggy here - this.destroy = function () - { - // removeEventListeners() to avoid mem leaks - window.dump("TODO: remove all eventlisteners\n"); + var eventManager = { - getBrowser().removeProgressListener(this.progressListener); + wantsModeReset: true, // used in onFocusChange since Firefox is so buggy here - window.removeEventListener("popupshown", enterPopupMode, true); - window.removeEventListener("popuphidden", exitPopupMode, true); - window.removeEventListener("DOMMenuBarActive", enterMenuMode, true); - window.removeEventListener("DOMMenuBarInactive", exitMenuMode, true); - - window.removeEventListener("keypress", this.onKeyPress, true); - window.removeEventListener("keydown", this.onKeyDown, true); - }; - - // This method pushes keys into the event queue from vimperator - // it is similar to vim's feedkeys() method, but cannot cope with - // 2 partially feeded strings, you have to feed one parsable string - // - // @param keys: a string like "2" to pass - // if you want < to be taken literally, prepend it with a \\ - this.feedkeys = function (keys, noremap) - { - var doc = window.document; - var view = window.document.defaultView; - var escapeKey = false; // \ to escape some special keys - - noremap = !!noremap; - - for (var i = 0; i < keys.length; i++) + destroy: function () { - var charCode = keys.charCodeAt(i); - var keyCode = 0; - var shift = false, ctrl = false, alt = false, meta = false; - //if (charCode == 92) // the '\' key FIXME: support the escape key - if (charCode == 60 && !escapeKey) // the '<' key starts a complex key - { - var matches = keys.substr(i + 1).match(/([CSMAcsma]-)*([^>]+)/); - if (matches && matches[2]) - { - if (matches[1]) // check for modifiers - { - ctrl = /[cC]-/.test(matches[1]); - alt = /[aA]-/.test(matches[1]); - shift = /[sS]-/.test(matches[1]); - meta = /[mM]-/.test(matches[1]); - } - if (matches[2].length == 1) - { - if (!ctrl && !alt && !shift && !meta) - return; // an invalid key like - charCode = matches[2].charCodeAt(0); - } - else if (matches[2].toLowerCase() == "space") - { - charCode = 32; - } - else if (keyCode = getKeyCode(matches[2])) - { - charCode = 0; - } - else //an invalid key like was found, stop propagation here (like Vim) - { - return; - } + // removeEventListeners() to avoid mem leaks + window.dump("TODO: remove all eventlisteners\n"); - i += matches[0].length + 1; + getBrowser().removeProgressListener(this.progressListener); + + window.removeEventListener("popupshown", enterPopupMode, true); + window.removeEventListener("popuphidden", exitPopupMode, true); + window.removeEventListener("DOMMenuBarActive", enterMenuMode, true); + window.removeEventListener("DOMMenuBarInactive", exitMenuMode, true); + + window.removeEventListener("keypress", this.onKeyPress, true); + window.removeEventListener("keydown", this.onKeyDown, true); + }, + + // This method pushes keys into the event queue from vimperator + // it is similar to vim's feedkeys() method, but cannot cope with + // 2 partially feeded strings, you have to feed one parsable string + // + // @param keys: a string like "2" to pass + // if you want < to be taken literally, prepend it with a \\ + feedkeys: function (keys, noremap) + { + var doc = window.document; + var view = window.document.defaultView; + var escapeKey = false; // \ to escape some special keys + + noremap = !!noremap; + + for (var i = 0; i < keys.length; i++) + { + var charCode = keys.charCodeAt(i); + var keyCode = 0; + var shift = false, ctrl = false, alt = false, meta = false; + //if (charCode == 92) // the '\' key FIXME: support the escape key + if (charCode == 60 && !escapeKey) // the '<' key starts a complex key + { + var matches = keys.substr(i + 1).match(/([CSMAcsma]-)*([^>]+)/); + if (matches && matches[2]) + { + if (matches[1]) // check for modifiers + { + ctrl = /[cC]-/.test(matches[1]); + alt = /[aA]-/.test(matches[1]); + shift = /[sS]-/.test(matches[1]); + meta = /[mM]-/.test(matches[1]); + } + if (matches[2].length == 1) + { + if (!ctrl && !alt && !shift && !meta) + return; // an invalid key like + charCode = matches[2].charCodeAt(0); + } + else if (matches[2].toLowerCase() == "space") + { + charCode = 32; + } + else if (keyCode = getKeyCode(matches[2])) + { + charCode = 0; + } + else //an invalid key like was found, stop propagation here (like Vim) + { + return; + } + + i += matches[0].length + 1; + } + } + + var elem = window.document.commandDispatcher.focusedElement; + if (!elem) + elem = window.content; + + var evt = doc.createEvent("KeyEvents"); + evt.initKeyEvent("keypress", true, true, view, ctrl, alt, shift, meta, keyCode, charCode); + evt.noremap = noremap; + elem.dispatchEvent(evt); + } + }, + + // this function converts the given event to + // a keycode which can be used in mappings + // e.g. pressing ctrl+n would result in the string "" + // null if unknown key + toString: function (event) //{{{ + { + if (!event) + return; + + var key = null; + var modifier = ""; + + if (event.ctrlKey) + modifier += "C-"; + if (event.altKey) + modifier += "A-"; + if (event.metaKey) + modifier += "M-"; + + if (event.type == "keypress") + { + if (event.charCode == 0) + { + if (event.shiftKey) + modifier += "S-"; + + for (var i in keyTable) + { + if (keyTable[i][0] == event.keyCode) + { + key = keyTable[i][1][0]; + break; + } + } + } + // special handling of the Space key + else if (event.charCode == 32) + { + if (event.shiftKey) + modifier += "S-"; + key = "Space"; + } + // a normal key like a, b, c, 0, etc. + else if (event.charCode > 0) + { + key = String.fromCharCode(event.charCode); + if (modifier.length == 0) + return key; } } + else if (event.type == "click" || event.type == "dblclick") + { + if (event.shiftKey) + modifier += "S-"; + if (event.type == "dblclick") + modifier += "2-"; + // TODO: triple and quadruple click + + switch (event.button) + { + case 0: + key = "LeftMouse"; + break; + case 1: + key = "MiddleMouse"; + break; + case 2: + key = "RightMouse"; + break; + } + } + + if (key == null) + return null; + + // a key like F1 is always enclosed in < and > + return "<" + modifier + key + ">"; + + }, //}}} + + isAcceptKey: function (key) + { + return (key == "" || key == "" || key == ""); + }, + + isCancelKey: function (key) + { + return (key == "" || key == "" || key == ""); + }, + + // argument "event" is delibarately not used, as i don't seem to have + // access to the real focus target + // + // the ugly wantsModeReset is needed, because firefox generates a massive + // amount of focus changes for things like (focusing the search field) + onFocusChange: function (event) + { + // command line has it's own focus change handler + if (vimperator.mode == vimperator.modes.COMMAND_LINE) + return; var elem = window.document.commandDispatcher.focusedElement; - if (!elem) - elem = window.content; + if (elem && elem.readOnly) + return; - var evt = doc.createEvent("KeyEvents"); - evt.initKeyEvent("keypress", true, true, view, ctrl, alt, shift, meta, keyCode, charCode); - evt.noremap = noremap; - elem.dispatchEvent(evt); - } - }; - - // this function converts the given event to - // a keycode which can be used in mappings - // e.g. pressing ctrl+n would result in the string "" - // null if unknown key - this.toString = function (event) //{{{ - { - if (!event) - return; - - var key = null; - var modifier = ""; - - if (event.ctrlKey) - modifier += "C-"; - if (event.altKey) - modifier += "A-"; - if (event.metaKey) - modifier += "M-"; - - if (event.type == "keypress") - { - if (event.charCode == 0) + if (elem && elem instanceof HTMLInputElement && + (elem.type.toLowerCase() == "text" || elem.type.toLowerCase() == "password")) { - if (event.shiftKey) - modifier += "S-"; + this.wantsModeReset = false; + vimperator.mode = vimperator.modes.INSERT; + vimperator.buffer.lastInputField = elem; + } + else if (elem && elem instanceof HTMLTextAreaElement) + { + this.wantsModeReset = false; + if (vimperator.options["insertmode"]) + vimperator.modes.set(vimperator.modes.INSERT, vimperator.modes.TEXTAREA); + else if (elem.selectionEnd - elem.selectionStart > 0) + vimperator.modes.set(vimperator.modes.VISUAL, vimperator.modes.TEXTAREA); + else + vimperator.modes.main = vimperator.modes.TEXTAREA; + vimperator.buffer.lastInputField = elem; + } + else if (vimperator.mode == vimperator.modes.INSERT || + vimperator.mode == vimperator.modes.TEXTAREA || + vimperator.mode == vimperator.modes.VISUAL) + { + this.wantsModeReset = true; + setTimeout(function () { + if (vimperator.events.wantsModeReset) + vimperator.modes.reset(); + }, 10); + } + }, - for (var i in keyTable) + onSelectionChange: function (event) + { + var could_copy = false; + var controller = document.commandDispatcher.getControllerForCommand("cmd_copy"); + if (controller && controller.isCommandEnabled("cmd_copy")) + could_copy = true; + + if (vimperator.mode != vimperator.modes.VISUAL) + { + if (could_copy) { - if (keyTable[i][0] == event.keyCode) - { - key = keyTable[i][1][0]; - break; - } + if ((vimperator.mode == vimperator.modes.TEXTAREA || (vimperator.modes.extended & vimperator.modes.TEXTAREA)) + && !vimperator.options["insertmode"]) + vimperator.modes.set(vimperator.modes.VISUAL, vimperator.modes.TEXTAREA); + else if (vimperator.mode == vimperator.modes.CARET) + vimperator.modes.set(vimperator.modes.VISUAL, vimperator.modes.CARET); } } - // special handling of the Space key - else if (event.charCode == 32) + //else + //{ + // if (!could_copy && vimperator.modes.extended & vimperator.modes.CARET) + // vimperator.mode = vimperator.modes.CARET; + //} + }, + + // global escape handler, is called in ALL modes + onEscape: function () + { + if (!vimperator.modes.passNextKey) { - if (event.shiftKey) - modifier += "S-"; - key = "Space"; - } - // a normal key like a, b, c, 0, etc. - else if (event.charCode > 0) - { - key = String.fromCharCode(event.charCode); - if (modifier.length == 0) - return key; - } - } - else if (event.type == "click" || event.type == "dblclick") - { - if (event.shiftKey) - modifier += "S-"; - if (event.type == "dblclick") - modifier += "2-"; - // TODO: triple and quadruple click + if (vimperator.modes.passAllKeys) + { + vimperator.modes.passAllKeys = false; + return; + } - switch (event.button) - { - case 0: - key = "LeftMouse"; - break; - case 1: - key = "MiddleMouse"; - break; - case 2: - key = "RightMouse"; - break; - } - } - - if (key == null) - return null; - - // a key like F1 is always enclosed in < and > - return "<" + modifier + key + ">"; - - }; //}}} - - this.isAcceptKey = function (key) - { - return (key == "" || key == "" || key == ""); - }; - this.isCancelKey = function (key) - { - return (key == "" || key == "" || key == ""); - }; - - // argument "event" is delibarately not used, as i don't seem to have - // access to the real focus target - // - // the ugly wantsModeReset is needed, because firefox generates a massive - // amount of focus changes for things like (focusing the search field) - this.onFocusChange = function (event) - { - // command line has it's own focus change handler - if (vimperator.mode == vimperator.modes.COMMAND_LINE) - return; - - var elem = window.document.commandDispatcher.focusedElement; - if (elem && elem.readOnly) - return; - - if (elem && elem instanceof HTMLInputElement && - (elem.type.toLowerCase() == "text" || elem.type.toLowerCase() == "password")) - { - this.wantsModeReset = false; - vimperator.mode = vimperator.modes.INSERT; - vimperator.buffer.lastInputField = elem; - } - else if (elem && elem instanceof HTMLTextAreaElement) - { - this.wantsModeReset = false; - if (vimperator.options["insertmode"]) - vimperator.modes.set(vimperator.modes.INSERT, vimperator.modes.TEXTAREA); - else if (elem.selectionEnd - elem.selectionStart > 0) - vimperator.modes.set(vimperator.modes.VISUAL, vimperator.modes.TEXTAREA); - else - vimperator.modes.main = vimperator.modes.TEXTAREA; - vimperator.buffer.lastInputField = elem; - } - else if (vimperator.mode == vimperator.modes.INSERT || - vimperator.mode == vimperator.modes.TEXTAREA || - vimperator.mode == vimperator.modes.VISUAL) - { - this.wantsModeReset = true; - setTimeout(function () { - if (vimperator.events.wantsModeReset) + switch (vimperator.mode) + { + case vimperator.modes.HINTS: + case vimperator.modes.COMMAND_LINE: vimperator.modes.reset(); - }, 10); - } - }; + break; - this.onSelectionChange = function (event) - { - var could_copy = false; - var controller = document.commandDispatcher.getControllerForCommand("cmd_copy"); - if (controller && controller.isCommandEnabled("cmd_copy")) - could_copy = true; + case vimperator.modes.VISUAL: + if (vimperator.modes.extended & vimperator.modes.TEXTAREA) + vimperator.mode = vimperator.modes.TEXTAREA; + else if (vimperator.modes.extended & vimperator.modes.CARET) + vimperator.mode = vimperator.modes.CARET; + break; - if (vimperator.mode != vimperator.modes.VISUAL) - { - if (could_copy) - { - if ((vimperator.mode == vimperator.modes.TEXTAREA || (vimperator.modes.extended & vimperator.modes.TEXTAREA)) - && !vimperator.options["insertmode"]) - vimperator.modes.set(vimperator.modes.VISUAL, vimperator.modes.TEXTAREA); - else if (vimperator.mode == vimperator.modes.CARET) - vimperator.modes.set(vimperator.modes.VISUAL, vimperator.modes.CARET); - } - } - //else - //{ - // if (!could_copy && vimperator.modes.extended & vimperator.modes.CARET) - // vimperator.mode = vimperator.modes.CARET; - //} - }; + case vimperator.modes.CARET: + // setting this option will trigger an observer which will + // care about all other details like setting the NORMAL mode + vimperator.options.setFirefoxPref("accessibility.browsewithcaret", false); + break; - // global escape handler, is called in ALL modes - this.onEscape = function () - { - if (!vimperator.modes.passNextKey) - { - if (vimperator.modes.passAllKeys) - { - vimperator.modes.passAllKeys = false; - return; - } + case vimperator.modes.INSERT: + if ((vimperator.modes.extended & vimperator.modes.TEXTAREA) && !vimperator.options["insertmode"]) + { + vimperator.mode = vimperator.modes.TEXTAREA; + } + else + { + vimperator.modes.reset(); + vimperator.focusContent(true); + } + break; - switch (vimperator.mode) - { - case vimperator.modes.HINTS: - case vimperator.modes.COMMAND_LINE: - vimperator.modes.reset(); - break; - case vimperator.modes.VISUAL: - if (vimperator.modes.extended & vimperator.modes.TEXTAREA) - vimperator.mode = vimperator.modes.TEXTAREA; - else if (vimperator.modes.extended & vimperator.modes.CARET) - vimperator.mode = vimperator.modes.CARET; - break; + default: + // clear any selection made + var selection = window.content.getSelection(); + try + { // a simple if (selection) does not seem to work + selection.collapseToStart(); + } + catch (e) { } + vimperator.commandline.clear(); - case vimperator.modes.CARET: - // setting this option will trigger an observer which will - // care about all other details like setting the NORMAL mode - vimperator.options.setFirefoxPref("accessibility.browsewithcaret", false); - break; - - case vimperator.modes.INSERT: - if ((vimperator.modes.extended & vimperator.modes.TEXTAREA) && !vimperator.options["insertmode"]) - { - vimperator.mode = vimperator.modes.TEXTAREA; - } - else - { vimperator.modes.reset(); vimperator.focusContent(true); - } - break; - - - default: - // clear any selection made - var selection = window.content.getSelection(); - try - { // a simple if (selection) does not seem to work - selection.collapseToStart(); - } - catch (e) { } - vimperator.commandline.clear(); - - vimperator.modes.reset(); - vimperator.focusContent(true); + } } - } - }; + }, - // this keypress handler gets always called first, even if e.g. - // the commandline has focus - this.onKeyPress = function (event) - { - var key = vimperator.events.toString(event); - if (!key) - return true; - - var stop = true; // set to false if we should NOT consume this event but let also firefox handle it - - var win = document.commandDispatcher.focusedWindow; - if (win && win.document.designMode == "on") - return true; - - // menus have their own command handlers - if (vimperator.modes.extended & vimperator.modes.MENU) - return true; - - // handle Escape-one-key mode (Ctrl-v) - if (vimperator.modes.passNextKey && !vimperator.modes.passAllKeys) + // this keypress handler gets always called first, even if e.g. + // the commandline has focus + onKeyPress: function (event) { - vimperator.modes.passNextKey = false; - return true; - } - // handle Escape-all-keys mode (Ctrl-q) - if (vimperator.modes.passAllKeys) - { - if (vimperator.modes.passNextKey) - vimperator.modes.passNextKey = false; // and then let flow continue - else if (key == "" || key == "" || key == "") - ; // let flow continue to handle these keys to cancel escape-all-keys mode - else - return true; - } + var key = vimperator.events.toString(event); + if (!key) + return true; - // FIXME: proper way is to have a better onFocus handler which also handles events for the XUL - if (!vimperator.mode == vimperator.modes.TEXTAREA && - !vimperator.mode == vimperator.modes.INSERT && - !vimperator.mode == vimperator.modes.COMMAND_LINE && - isFormElemFocused()) // non insert mode, but e.g. the location bar has focus + var stop = true; // set to false if we should NOT consume this event but let also firefox handle it + + var win = document.commandDispatcher.focusedWindow; + if (win && win.document.designMode == "on") return true; - if (vimperator.mode == vimperator.modes.COMMAND_LINE && - (vimperator.modes.extended & vimperator.modes.OUTPUT_MULTILINE)) - { - vimperator.commandline.onMultilineOutputEvent(event); - return false; - } + // menus have their own command handlers + if (vimperator.modes.extended & vimperator.modes.MENU) + return true; - // XXX: ugly hack for now pass certain keys to firefox as they are without beeping - // also fixes key navigation in combo boxes, etc. - if (vimperator.mode == vimperator.modes.NORMAL) - { - if (key == "" || key == "" || key == "" || key == "" || key == "" || key == "") + // handle Escape-one-key mode (Ctrl-v) + if (vimperator.modes.passNextKey && !vimperator.modes.passAllKeys) + { + vimperator.modes.passNextKey = false; + return true; + } + // handle Escape-all-keys mode (Ctrl-q) + if (vimperator.modes.passAllKeys) + { + if (vimperator.modes.passNextKey) + vimperator.modes.passNextKey = false; // and then let flow continue + else if (key == "" || key == "" || key == "") + ; // let flow continue to handle these keys to cancel escape-all-keys mode + else + return true; + } + + // FIXME: proper way is to have a better onFocus handler which also handles events for the XUL + if (!vimperator.mode == vimperator.modes.TEXTAREA && + !vimperator.mode == vimperator.modes.INSERT && + !vimperator.mode == vimperator.modes.COMMAND_LINE && + isFormElemFocused()) // non insert mode, but e.g. the location bar has focus + return true; + + if (vimperator.mode == vimperator.modes.COMMAND_LINE && + (vimperator.modes.extended & vimperator.modes.OUTPUT_MULTILINE)) + { + vimperator.commandline.onMultilineOutputEvent(event); 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 - if (key != "" && key != "") - { - if (vimperator.mode == vimperator.modes.HINTS) - { - vimperator.hints.onEvent(event); - event.preventDefault(); - event.stopPropagation(); - return true; } - } - var count_str = vimperator.input.buffer.match(/^[0-9]*/)[0]; - var candidate_command = (vimperator.input.buffer + key).replace(count_str, ""); - var map; - if (event.noremap) - map = vimperator.mappings.getDefaultMap(vimperator.mode, candidate_command); - else - map = vimperator.mappings.get(vimperator.mode, candidate_command); + // XXX: ugly hack for now pass certain keys to firefox as they are without beeping + // also fixes key navigation in combo boxes, etc. + if (vimperator.mode == vimperator.modes.NORMAL) + { + if (key == "" || key == "" || key == "" || key == "" || key == "" || key == "") + return false; + } - // counts must be at the start of a complete mapping (10j -> go 10 lines down) - if (/^[1-9][0-9]*$/.test(vimperator.input.buffer + key)) - { - // no count for insert mode mappings - if (vimperator.mode == vimperator.modes.INSERT || vimperator.mode == vimperator.modes.COMMAND_LINE) - stop = false; - else - vimperator.input.buffer += key; - } - else if (vimperator.input.pendingArgMap) - { - vimperator.input.buffer = ""; + // // 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 if (key != "" && key != "") - vimperator.input.pendingArgMap.execute(null, vimperator.input.count, key); - - vimperator.input.pendingArgMap = null; - } - else if (map) - { - vimperator.input.count = parseInt(count_str, 10); - if (isNaN(vimperator.input.count)) - vimperator.input.count = -1; - if (map.flags & vimperator.Mappings.flags.ARGUMENT) { - vimperator.input.pendingArgMap = map; + if (vimperator.mode == vimperator.modes.HINTS) + { + vimperator.hints.onEvent(event); + event.preventDefault(); + event.stopPropagation(); + return true; + } + } + + var count_str = vimperator.input.buffer.match(/^[0-9]*/)[0]; + var candidate_command = (vimperator.input.buffer + key).replace(count_str, ""); + var map; + if (event.noremap) + map = vimperator.mappings.getDefaultMap(vimperator.mode, candidate_command); + else + map = vimperator.mappings.get(vimperator.mode, candidate_command); + + // counts must be at the start of a complete mapping (10j -> go 10 lines down) + if (/^[1-9][0-9]*$/.test(vimperator.input.buffer + key)) + { + // no count for insert mode mappings + if (vimperator.mode == vimperator.modes.INSERT || vimperator.mode == vimperator.modes.COMMAND_LINE) + stop = false; + else + vimperator.input.buffer += key; + } + else if (vimperator.input.pendingArgMap) + { + vimperator.input.buffer = ""; + + if (key != "" && key != "") + vimperator.input.pendingArgMap.execute(null, vimperator.input.count, key); + + vimperator.input.pendingArgMap = null; + } + else if (map) + { + vimperator.input.count = parseInt(count_str, 10); + if (isNaN(vimperator.input.count)) + vimperator.input.count = -1; + if (map.flags & vimperator.Mappings.flags.ARGUMENT) + { + vimperator.input.pendingArgMap = map; + vimperator.input.buffer += key; + } + else if (vimperator.input.pendingMotionMap) + { + if (key != "" && key != "") + { + vimperator.input.pendingMotionMap.execute(candidate_command, vimperator.input.count, null); + } + vimperator.input.pendingMotionMap = null; + vimperator.input.buffer = ""; + } + // no count support for these commands yet + else if (map.flags & vimperator.Mappings.flags.MOTION) + { + vimperator.input.pendingMotionMap = map; + vimperator.input.buffer = ""; + } + else + { + vimperator.input.buffer = ""; + var ret = map.execute(null, vimperator.input.count); + if (map.flags & vimperator.Mappings.flags.ALLOW_EVENT_ROUTING && ret) + stop = false; + } + } + else if (vimperator.mappings.getCandidates(vimperator.mode, candidate_command).length > 0) + { vimperator.input.buffer += key; } - else if (vimperator.input.pendingMotionMap) + else { + vimperator.input.buffer = ""; + vimperator.input.pendingArgMap = null; + vimperator.input.pendingMotionMap = null; + if (key != "" && key != "") { - vimperator.input.pendingMotionMap.execute(candidate_command, vimperator.input.count, null); - } - vimperator.input.pendingMotionMap = null; - vimperator.input.buffer = ""; - } - // no count support for these commands yet - else if (map.flags & vimperator.Mappings.flags.MOTION) - { - vimperator.input.pendingMotionMap = map; - vimperator.input.buffer = ""; - } - else - { - vimperator.input.buffer = ""; - var ret = map.execute(null, vimperator.input.count); - if (map.flags & vimperator.Mappings.flags.ALLOW_EVENT_ROUTING && ret) + // allow key to be passed to firefox if we can't handle it stop = false; - } - } - else if (vimperator.mappings.getCandidates(vimperator.mode, candidate_command).length > 0) - { - vimperator.input.buffer += key; - } - else - { - vimperator.input.buffer = ""; - vimperator.input.pendingArgMap = null; - vimperator.input.pendingMotionMap = null; - if (key != "" && key != "") - { - // allow key to be passed to firefox if we can't handle it - stop = false; - - // TODO: see if this check is needed or are all motion commands already mapped in these modes? - if (vimperator.mode != vimperator.modes.INSERT && - vimperator.mode != vimperator.modes.COMMAND_LINE) - vimperator.beep(); - } - } - - if (stop) - { - event.preventDefault(); - event.stopPropagation(); - } - - var motion_map = (vimperator.input.pendingMotionMap && vimperator.input.pendingMotionMap.names[0]) || ""; - vimperator.statusline.updateInputBuffer(motion_map + vimperator.input.buffer); - return false; - }; - window.addEventListener("keypress", this.onKeyPress, true); - - // this is need for sites like msn.com which focus the input field on keydown - this.onKeyUpOrDown = function (event) - { - if (vimperator.modes.passNextKey ^ vimperator.modes.passAllKeys || isFormElemFocused()) - return true; - - event.stopPropagation(); - return false; - }; - window.addEventListener("keydown", this.onKeyUpOrDown, true); - window.addEventListener("keyup", this.onKeyUpOrDown, true); - - this.progressListener = - { - QueryInterface: function (aIID) - { - if (aIID.equals(Components.interfaces.nsIWebProgressListener) || - aIID.equals(Components.interfaces.nsIXULBrowserWindow) || // for setOverLink(); - aIID.equals(Components.interfaces.nsISupportsWeakReference) || - aIID.equals(Components.interfaces.nsISupports)) - return this; - throw Components.results.NS_NOINTERFACE; - }, - - // XXX: function may later be needed to detect a canceled synchronous openURL() - onStateChange: function (webProgress, aRequest, flags, aStatus) - { - // STATE_IS_DOCUMENT | STATE_IS_WINDOW is important, because we also - // receive statechange events for loading images and other parts of the web page - if (flags & (Components.interfaces.nsIWebProgressListener.STATE_IS_DOCUMENT | - Components.interfaces.nsIWebProgressListener.STATE_IS_WINDOW)) - { - // This fires when the load event is initiated - if (flags & Components.interfaces.nsIWebProgressListener.STATE_START) - { - vimperator.statusline.updateProgress(0); + // TODO: see if this check is needed or are all motion commands already mapped in these modes? + if (vimperator.mode != vimperator.modes.INSERT && + vimperator.mode != vimperator.modes.COMMAND_LINE) + vimperator.beep(); } - else if (flags & Components.interfaces.nsIWebProgressListener.STATE_STOP) - ;// vimperator.statusline.updateUrl(); } - }, - // for notifying the user about secure web pages - onSecurityChange: function (webProgress, aRequest, aState) - { - const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; - if (aState & nsIWebProgressListener.STATE_IS_INSECURE) - vimperator.statusline.setClass("insecure"); - else if (aState & nsIWebProgressListener.STATE_IS_BROKEN) - vimperator.statusline.setClass("broken"); - else if (aState & nsIWebProgressListener.STATE_IS_SECURE) - vimperator.statusline.setClass("secure"); - }, - onStatusChange: function (webProgress, request, status, message) - { - vimperator.statusline.updateUrl(message); - }, - onProgressChange: function (webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) - { - vimperator.statusline.updateProgress(curTotalProgress/maxTotalProgress); - }, - // happens when the users switches tabs - onLocationChange: function () - { - vimperator.statusline.updateUrl(); - vimperator.statusline.updateProgress(); - // if this is not delayed we get the position of the old buffer - setTimeout(function () { vimperator.statusline.updateBufferPosition(); }, 100); - }, - // called at the very end of a page load - asyncUpdateUI: function () - { - setTimeout(vimperator.statusline.updateUrl, 100); - }, - setOverLink : function (link, b) - { - var ssli = vimperator.options["showstatuslinks"]; - if (link && ssli) + if (stop) { - if (ssli == 1) - vimperator.statusline.updateUrl("Link: " + link); - else if (ssli == 2) - vimperator.echo("Link: " + link, vimperator.commandline.DISALLOW_MULTILINE); + event.preventDefault(); + event.stopPropagation(); } - if (link == "") - { - if (ssli == 1) - vimperator.statusline.updateUrl(); - else if (ssli == 2) - vimperator.modes.show(); - } + var motion_map = (vimperator.input.pendingMotionMap && vimperator.input.pendingMotionMap.names[0]) || ""; + vimperator.statusline.updateInputBuffer(motion_map + vimperator.input.buffer); + return false; }, - // stub functions for the interfaces - setJSStatus: function (status) { ; }, - setJSDefaultStatus: function (status) { ; }, - setDefaultStatus: function (status) { ; }, - onLinkIconAvailable: function () { ; } + // this is need for sites like msn.com which focus the input field on keydown + onKeyUpOrDown: function (event) + { + if (vimperator.modes.passNextKey ^ vimperator.modes.passAllKeys || isFormElemFocused()) + return true; + + event.stopPropagation(); + return false; + }, + + progressListener: { + QueryInterface: function (aIID) + { + if (aIID.equals(Components.interfaces.nsIWebProgressListener) || + aIID.equals(Components.interfaces.nsIXULBrowserWindow) || // for setOverLink(); + aIID.equals(Components.interfaces.nsISupportsWeakReference) || + aIID.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_NOINTERFACE; + }, + + // XXX: function may later be needed to detect a canceled synchronous openURL() + onStateChange: function (webProgress, aRequest, flags, aStatus) + { + // STATE_IS_DOCUMENT | STATE_IS_WINDOW is important, because we also + // receive statechange events for loading images and other parts of the web page + if (flags & (Components.interfaces.nsIWebProgressListener.STATE_IS_DOCUMENT | + Components.interfaces.nsIWebProgressListener.STATE_IS_WINDOW)) + { + // This fires when the load event is initiated + if (flags & Components.interfaces.nsIWebProgressListener.STATE_START) + { + vimperator.statusline.updateProgress(0); + } + else if (flags & Components.interfaces.nsIWebProgressListener.STATE_STOP) + ;// vimperator.statusline.updateUrl(); + } + }, + // for notifying the user about secure web pages + onSecurityChange: function (webProgress, aRequest, aState) + { + const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; + if (aState & nsIWebProgressListener.STATE_IS_INSECURE) + vimperator.statusline.setClass("insecure"); + else if (aState & nsIWebProgressListener.STATE_IS_BROKEN) + vimperator.statusline.setClass("broken"); + else if (aState & nsIWebProgressListener.STATE_IS_SECURE) + vimperator.statusline.setClass("secure"); + }, + onStatusChange: function (webProgress, request, status, message) + { + vimperator.statusline.updateUrl(message); + }, + onProgressChange: function (webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) + { + vimperator.statusline.updateProgress(curTotalProgress/maxTotalProgress); + }, + // happens when the users switches tabs + onLocationChange: function () + { + vimperator.statusline.updateUrl(); + vimperator.statusline.updateProgress(); + + // if this is not delayed we get the position of the old buffer + setTimeout(function () { vimperator.statusline.updateBufferPosition(); }, 100); + }, + // called at the very end of a page load + asyncUpdateUI: function () + { + setTimeout(vimperator.statusline.updateUrl, 100); + }, + setOverLink : function (link, b) + { + var ssli = vimperator.options["showstatuslinks"]; + if (link && ssli) + { + if (ssli == 1) + vimperator.statusline.updateUrl("Link: " + link); + else if (ssli == 2) + vimperator.echo("Link: " + link, vimperator.commandline.DISALLOW_MULTILINE); + } + + if (link == "") + { + if (ssli == 1) + vimperator.statusline.updateUrl(); + else if (ssli == 2) + vimperator.modes.show(); + } + }, + + // stub functions for the interfaces + setJSStatus: function (status) { ; }, + setJSDefaultStatus: function (status) { ; }, + setDefaultStatus: function (status) { ; }, + onLinkIconAvailable: function () { ; } + }, + + prefObserver: { + register: function () + { + var prefService = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService); + this._branch = prefService.getBranch(""); // better way to monitor all changes? + this._branch.QueryInterface(Components.interfaces.nsIPrefBranch2); + this._branch.addObserver("", this, false); + }, + + unregister: function () + { + if (!this._branch) return; + this._branch.removeObserver("", this); + }, + + observe: function (aSubject, aTopic, aData) + { + if (aTopic != "nsPref:changed") return; + // aSubject is the nsIPrefBranch we're observing (after appropriate QI) + // aData is the name of the pref that's been changed (relative to aSubject) + switch (aData) + { + case "accessibility.browsewithcaret": + var value = vimperator.options.getFirefoxPref("accessibility.browsewithcaret", false); + vimperator.mode = value ? vimperator.modes.CARET : vimperator.modes.NORMAL; + break; + } + } + } }; - window.XULBrowserWindow = this.progressListener; + window.XULBrowserWindow = eventManager.progressListener; window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIWebNavigation) .QueryInterface(Components.interfaces.nsIDocShellTreeItem).treeOwner .QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIXULWindow) .XULBrowserWindow = window.XULBrowserWindow; - getBrowser().addProgressListener(this.progressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL); + getBrowser().addProgressListener(eventManager.progressListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL); + eventManager.prefObserver.register(); - this.prefObserver = - { - register: function () - { - var prefService = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefService); - this._branch = prefService.getBranch(""); // better way to monitor all changes? - this._branch.QueryInterface(Components.interfaces.nsIPrefBranch2); - this._branch.addObserver("", this, false); - }, - - unregister: function () - { - if (!this._branch) return; - this._branch.removeObserver("", this); - }, - - observe: function (aSubject, aTopic, aData) - { - if (aTopic != "nsPref:changed") return; - // aSubject is the nsIPrefBranch we're observing (after appropriate QI) - // aData is the name of the pref that's been changed (relative to aSubject) - switch (aData) - { - case "accessibility.browsewithcaret": - var value = vimperator.options.getFirefoxPref("accessibility.browsewithcaret", false); - vimperator.mode = value ? vimperator.modes.CARET : vimperator.modes.NORMAL; - break; - } - } - }; - this.prefObserver.register(); + window.addEventListener("keypress", eventManager.onKeyPress, true); + window.addEventListener("keydown", eventManager.onKeyUpOrDown, true); + window.addEventListener("keyup", eventManager.onKeyUpOrDown, true); + return eventManager; //}}} }; //}}} diff --git a/content/mappings.js b/content/mappings.js index 723ecc28..9fa59b9d 100644 --- a/content/mappings.js +++ b/content/mappings.js @@ -176,6 +176,7 @@ vimperator.Mappings = function () //{{{ ////////////////////// PUBLIC SECTION ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ + // FIXME: vimperator.Mappings.flags = { ALLOW_EVENT_ROUTING: 1 << 0, // if set, return true inside the map command to pass the event further to firefox MOTION: 1 << 1, @@ -183,127 +184,131 @@ vimperator.Mappings = function () //{{{ ARGUMENT: 1 << 3 }; - // NOTE: just normal mode for now - this.__iterator__ = function () - { - return mappingsIterator(vimperator.modes.NORMAL, main); - }; + var mappingManager = { - // FIXME - this.getIterator = function (mode) - { - return mappingsIterator(mode, main); - }; - - // FIXME - this.getUserIterator = function (mode) - { - return mappingsIterator(mode, user); - }; - - this.hasMap = function (mode, cmd) - { - var user_maps = user[mode]; - - for (var i = 0; i < user_maps.length; i++) + // NOTE: just normal mode for now + __iterator__: function () { - if (user_maps[i].names.indexOf(cmd) != -1) - return true; - } + return mappingsIterator(vimperator.modes.NORMAL, main); + }, - return false; - }; - - this.add = function (map) - { - for (var i = 0; i < map.names.length; i++) + // FIXME + getIterator: function (mode) { - // only store keysyms with uppercase modifier strings - map.names[i] = map.names[i].replace(/[casm]-/g, function ($0) { return $0.toUpperCase(); }); - for (var j = 0; j < map.modes.length; j++) - removeMap(map.modes[j], map.names[i]); - } + return mappingsIterator(mode, main); + }, - for (var k = 0; k < map.modes.length; k++) - user[map.modes[k]].push(map); - }; - - this.remove = function (mode, cmd) - { - removeMap(mode, cmd); - }; - - this.removeAll = function (mode) - { - user[mode] = []; - }; - - this.get = function (mode, cmd) - { - var map = getMap(mode, cmd, user); - - if (!map) - map = getMap(mode, cmd, main); - - return map; - }; - - // TODO: move default maps to their own v.normal namespace - this.getDefaultMap = function (mode, cmd) - { - return getMap(mode, cmd, main); - }; - - // returns an array of mappings with names which start with "cmd" - this.getCandidates = function (mode, cmd) - { - var mappings = []; - var matches = []; - - mappings = user[mode].concat(main[mode]); - - for (var i = 0; i < mappings.length; i++) + // FIXME + getUserIterator: function (mode) { - var map = mappings[i]; - for (var j = 0; j < map.names.length; j++) + return mappingsIterator(mode, user); + }, + + hasMap: function (mode, cmd) + { + var user_maps = user[mode]; + + for (var i = 0; i < user_maps.length; i++) { - if (map.names[j].indexOf(cmd) == 0) + if (user_maps[i].names.indexOf(cmd) != -1) + return true; + } + + return false; + }, + + add: function (map) + { + for (var i = 0; i < map.names.length; i++) + { + // only store keysyms with uppercase modifier strings + map.names[i] = map.names[i].replace(/[casm]-/g, function ($0) { return $0.toUpperCase(); }); + for (var j = 0; j < map.modes.length; j++) + removeMap(map.modes[j], map.names[i]); + } + + for (var k = 0; k < map.modes.length; k++) + user[map.modes[k]].push(map); + }, + + remove: function (mode, cmd) + { + removeMap(mode, cmd); + }, + + removeAll: function (mode) + { + user[mode] = []; + }, + + get: function (mode, cmd) + { + var map = getMap(mode, cmd, user); + + if (!map) + map = getMap(mode, cmd, main); + + return map; + }, + + // TODO: move default maps to their own v.normal namespace + getDefaultMap: function (mode, cmd) + { + return getMap(mode, cmd, main); + }, + + // returns an array of mappings with names which start with "cmd" + getCandidates: function (mode, cmd) + { + var mappings = []; + var matches = []; + + mappings = user[mode].concat(main[mode]); + + for (var i = 0; i < mappings.length; i++) + { + var map = mappings[i]; + for (var j = 0; j < map.names.length; j++) { - // for < only return a candidate if it doesn't seem like a mapping - if (cmd != "<" || !/^<.+>/.test(map.names[j])) - matches.push(map); + if (map.names[j].indexOf(cmd) == 0) + { + // for < only return a candidate if it doesn't seem like a mapping + if (cmd != "<" || !/^<.+>/.test(map.names[j])) + matches.push(map); + } } } - } - return matches; - }; + return matches; + }, - this.list = function (mode, filter) - { - var maps = user[mode]; - - if (!maps || maps.length == 0) + list: function (mode, filter) { - vimperator.echo("No mappings found"); - return; - } + var maps = user[mode]; - var list = ""; - for (var i = 0; i < maps.length; i++) - { - for (var j = 0; j < maps[i].names.length; j++) + if (!maps || maps.length == 0) { - list += ""; - list += ""; - if (maps[i].rhs) - list += ""; - list += ""; + vimperator.echo("No mappings found"); + return; } - } - list += "
" + vimperator.util.escapeHTML(maps[i].names[j]) + " " + vimperator.util.escapeHTML(maps[i].rhs) + "
"; - vimperator.commandline.echo(list, vimperator.commandline.HL_NORMAL, vimperator.commandline.FORCE_MULTILINE); + var list = ""; + for (var i = 0; i < maps.length; i++) + { + for (var j = 0; j < maps[i].names.length; j++) + { + list += ""; + list += ""; + if (maps[i].rhs) + list += ""; + list += ""; + } + } + list += "
" + vimperator.util.escapeHTML(maps[i].names[j]) + " " + vimperator.util.escapeHTML(maps[i].rhs) + "
"; + + vimperator.commandline.echo(list, vimperator.commandline.HL_NORMAL, vimperator.commandline.FORCE_MULTILINE); + } + }; /////////////////////////////////////////////////////////////////////////////}}} @@ -324,7 +329,7 @@ vimperator.Mappings = function () //{{{ vimperator.modes.TEXTAREA]; // - // Normal mode + // NORMAL mode // {{{ // vimperator management @@ -1187,9 +1192,8 @@ vimperator.Mappings = function () //{{{ } )); - // }}} - // Hints mode + // HINTS mode // {{{ // // action keys @@ -1418,7 +1422,7 @@ vimperator.Mappings = function () //{{{ // )); // }}} - // Caret mode + // CARET mode // {{{ function getSelectionController() @@ -1766,9 +1770,8 @@ vimperator.Mappings = function () //{{{ { } )); - // }}} - // Textarea mode + // TEXTAREA mode // {{{ addDefaultMap(new vimperator.Map([vimperator.modes.TEXTAREA], ["i", ""], @@ -1977,6 +1980,7 @@ vimperator.Mappings = function () //{{{ // }}} // INSERT mode // {{{ + addDefaultMap(new vimperator.Map([vimperator.modes.INSERT, vimperator.modes.COMMAND_LINE], [""], function () { vimperator.editor.executeCommand("cmd_deleteWordBackward", 1); }, { } @@ -2038,6 +2042,7 @@ vimperator.Mappings = function () //{{{ //}}} // COMMAND_LINE mode //{{{ + addDefaultMap(new vimperator.Map([vimperator.modes.COMMAND_LINE], [""], function () { return vimperator.editor.expandAbbreviation("c"); }, { flags: vimperator.Mappings.flags.ALLOW_EVENT_ROUTING } @@ -2046,8 +2051,9 @@ vimperator.Mappings = function () //{{{ ["", ""], function () { vimperator.editor.expandAbbreviation("c"); }, { } )); - //}}} + //}}} }}} + return mappingManager; }; //}}} // vim: set fdm=marker sw=4 ts=4 et: diff --git a/content/options.js b/content/options.js index 8b753f9e..21bc1c48 100644 --- a/content/options.js +++ b/content/options.js @@ -125,6 +125,7 @@ vimperator.Options = function () //{{{ //////////////////////////////////////////////////////////////////////////////// ////////////////////// PRIVATE SECTION ///////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ + var firefox_prefs = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefBranch); var vimperator_prefs = firefox_prefs.getBranch("extensions.vimperator."); @@ -312,109 +313,113 @@ vimperator.Options = function () //{{{ ////////////////////// PUBLIC SECTION ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - this.__iterator__ = function () - { - return optionsIterator(); - }; + var optionManager = { - this.get = function (name) - { - for (var i = 0; i < options.length; i++) + __iterator__: function () { - if (options[i].hasName(name)) - return options[i]; - } - return null; - }; + return optionsIterator(); + }, - this.add = function (option) - { - this.__defineGetter__(option.name, function () { return option.value; }); - this.__defineSetter__(option.name, function (value) { option.value = value; }); - options.push(option); - }; - - this.destroy = function () - { - // reset some modified firefox prefs - if (loadPreference("dom.popup_allowed_events", "change click dblclick mouseup reset submit") - == popup_allowed_events + " keypress") - storePreference("dom.popup_allowed_events", popup_allowed_events); - }; - - this.list = function (only_non_default) - { - // TODO: columns like Vim? - var list = ":" + vimperator.util.escapeHTML(vimperator.commandline.getCommand()) + "
" + - ""; - var name, value, def; - - for (var i = 0; i < options.length; i++) + get: function (name) { - name = options[i].name; - value = options[i].value; - def = options[i].default_value; - - if (only_non_default && value == def) - continue; - - if (options[i].type == "boolean") + for (var i = 0; i < options.length; i++) { - name = value ? " " + name : "no" + name; - if (value != def) - name = "" + name + " (default: " + (def ? "" : "no") + options[i].name + ")"; - list += ""; + if (options[i].hasName(name)) + return options[i]; } - else + return null; + }, + + add: function (option) + { + this.__defineGetter__(option.name, function () { return option.value; }); + this.__defineSetter__(option.name, function (value) { option.value = value; }); + options.push(option); + }, + + destroy: function () + { + // reset some modified firefox prefs + if (loadPreference("dom.popup_allowed_events", "change click dblclick mouseup reset submit") + == popup_allowed_events + " keypress") + storePreference("dom.popup_allowed_events", popup_allowed_events); + }, + + list: function (only_non_default) + { + // TODO: columns like Vim? + var list = ":" + vimperator.util.escapeHTML(vimperator.commandline.getCommand()) + "
" + + "
--- Options ---
" + name + "
"; + var name, value, def; + + for (var i = 0; i < options.length; i++) { - if (value != def) + name = options[i].name; + value = options[i].value; + def = options[i].default_value; + + if (only_non_default && value == def) + continue; + + if (options[i].type == "boolean") { - name = "" + name + ""; - value = vimperator.util.colorize(value, false) + " (default: " + def + ")"; + name = value ? " " + name : "no" + name; + if (value != def) + name = "" + name + " (default: " + (def ? "" : "no") + options[i].name + ")"; + list += ""; } else - value = vimperator.util.colorize(value, false); + { + if (value != def) + { + name = "" + name + ""; + value = vimperator.util.colorize(value, false) + " (default: " + def + ")"; + } + else + value = vimperator.util.colorize(value, false); - list += ""; + list += ""; + } } + + list += "
--- Options ---
" + name + "
" + " " + name + "=" + value + "
" + " " + name + "=" + value + "
"; + + vimperator.commandline.echo(list, vimperator.commandline.HL_NORMAL, vimperator.commandline.FORCE_MULTILINE); + }, + + // this hack is only needed, because we need to do asynchronous loading of the .vimperatorrc + setInitialGUI: function () + { + if (!guioptions_done) + this.get("guioptions").reset(); + if (!laststatus_done) + this.get("laststatus").reset(); + if (!showtabline_done) + this.get("showtabline").reset(); + }, + + // TODO: separate Preferences from Options? Would these utility functions + // be better placed in the 'core' vimperator namespace somewhere? + setPref: function (name, value) + { + return storePreference(name, value, true); + }, + + getPref: function (name, forced_default) + { + return loadPreference(name, forced_default, true); + }, + + setFirefoxPref: function (name, value) + { + return storePreference(name, value); + }, + + getFirefoxPref: function (name, forced_default) + { + return loadPreference(name, forced_default); } - list += ""; - - vimperator.commandline.echo(list, vimperator.commandline.HL_NORMAL, vimperator.commandline.FORCE_MULTILINE); - }; - - // this hack is only needed, because we need to do asynchronous loading of the .vimperatorrc - this.setInitialGUI = function () - { - if (!guioptions_done) - this.get("guioptions").reset(); - if (!laststatus_done) - this.get("laststatus").reset(); - if (!showtabline_done) - this.get("showtabline").reset(); - }; - - // TODO: separate Preferences from Options? Would these utility functions - // be better placed in the 'core' vimperator namespace somewhere? - this.setPref = function (name, value) - { - return storePreference(name, value, true); - }; - - this.getPref = function (name, forced_default) - { - return loadPreference(name, forced_default, true); - }; - - this.setFirefoxPref = function (name, value) - { - return storePreference(name, value); - }; - - this.getFirefoxPref = function (name, forced_default) - { - return loadPreference(name, forced_default); }; /////////////////////////////////////////////////////////////////////////////}}} @@ -426,7 +431,7 @@ vimperator.Options = function () //{{{ "//xhtml:*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @class='s'] | " + "//xhtml:input[not(@type='hidden')] | //xhtml:a | //xhtml:area | //xhtml:iframe | //xhtml:textarea | //xhtml:button | //xhtml:select"; - this.add(new vimperator.Option(["activate", "act"], "stringlist", + optionManager.add(new vimperator.Option(["activate", "act"], "stringlist", { short_help: "Define when tabs are automatically activated", help: "Available items:
" + @@ -443,7 +448,7 @@ vimperator.Options = function () //{{{ } } )); - this.add(new vimperator.Option(["complete", "cpt"], "charlist", + optionManager.add(new vimperator.Option(["complete", "cpt"], "charlist", { short_help: "Items which are completed at the :[tab]open prompt", help: "Available items:
" + @@ -459,7 +464,7 @@ vimperator.Options = function () //{{{ validator: function (value) { return !/[^sfbh]/.test(value); } } )); - this.add(new vimperator.Option(["defsearch", "ds"], "string", + optionManager.add(new vimperator.Option(["defsearch", "ds"], "string", { short_help: "Set the default search engine", help: "The default search engine is used in the :[tab]open [arg] command " + @@ -467,7 +472,7 @@ vimperator.Options = function () //{{{ default_value: "google" } )); - this.add(new vimperator.Option(["editor"], "string", + optionManager.add(new vimperator.Option(["editor"], "string", { short_help: "Set the external text editor", help: "Sets the editor to run when <C-i> " + @@ -477,20 +482,20 @@ vimperator.Options = function () //{{{ default_value: "gvim -f" } )); - this.add(new vimperator.Option(["extendedhinttags", "eht"], "string", + optionManager.add(new vimperator.Option(["extendedhinttags", "eht"], "string", { short_help: "XPath string of hintable elements activated by ';'", default_value: DEFAULT_HINTTAGS } )); - this.add(new vimperator.Option(["focusedhintstyle", "fhs"], "string", + optionManager.add(new vimperator.Option(["focusedhintstyle", "fhs"], "string", { short_help: "CSS specification of focused hints", default_value: "z-index:5000; font-family:monospace; font-size:12px; color:ButtonText; background-color:ButtonShadow; " + "border-color:ButtonShadow; border-width:1px; border-style:solid; padding:0px 1px 0px 1px; position:absolute;" } )); - this.add(new vimperator.Option(["fullscreen", "fs"], "boolean", + optionManager.add(new vimperator.Option(["fullscreen", "fs"], "boolean", { short_help: "Show the current window fullscreen", setter: function (value) { window.fullScreen = value; }, @@ -498,7 +503,7 @@ vimperator.Options = function () //{{{ default_value: false } )); - this.add(new vimperator.Option(["guioptions", "go"], "charlist", + optionManager.add(new vimperator.Option(["guioptions", "go"], "charlist", { short_help: "Show or hide the menu, toolbar and scrollbars", help: "Supported characters:
" + @@ -512,7 +517,7 @@ vimperator.Options = function () //{{{ validator: function (value) { return !/[^mTb]/.test(value); } } )); - this.add(new vimperator.Option(["hinttimeout", "hto"], "number", + optionManager.add(new vimperator.Option(["hinttimeout", "hto"], "number", { short_help: "Automatically follow non unique numerical hint after {arg} ms", help: "Set to 0 (the default) to only follow numeric hints after pressing <Return> or when the hint is unique.", @@ -520,46 +525,46 @@ vimperator.Options = function () //{{{ validator: function (value) { return value >= 0; } } )); - this.add(new vimperator.Option(["hintstyle", "hs"], "string", + optionManager.add(new vimperator.Option(["hintstyle", "hs"], "string", { short_help: "CSS specification of unfocused hints", default_value: "z-index:5000; font-family:monospace; font-size:12px; color:white; background-color:red; " + "border-color:ButtonShadow; border-width:0px; border-style:solid; padding:0px 1px 0px 1px; position:absolute;" } )); - this.add(new vimperator.Option(["hinttags", "ht"], "string", + optionManager.add(new vimperator.Option(["hinttags", "ht"], "string", { short_help: "XPath string of hintable elements activated by 'f' and 'F'", default_value: DEFAULT_HINTTAGS } )); - this.add(new vimperator.Option(["hlsearch", "hls"], "boolean", + optionManager.add(new vimperator.Option(["hlsearch", "hls"], "boolean", { short_help: "Highlight previous search pattern matches", setter: function (value) { if (value) vimperator.search.highlight(); else vimperator.search.clear(); }, default_value: false } )); - this.add(new vimperator.Option(["hlsearchstyle", "hlss"], "string", + optionManager.add(new vimperator.Option(["hlsearchstyle", "hlss"], "string", { short_help: "CSS specification of highlighted search items", default_value: "color: black; background-color: yellow; padding: 0; display: inline;" } )); - this.add(new vimperator.Option(["ignorecase", "ic"], "boolean", + optionManager.add(new vimperator.Option(["ignorecase", "ic"], "boolean", { short_help: "Ignore case in search patterns", default_value: true } )); - this.add(new vimperator.Option(["incsearch", "is"], "boolean", + optionManager.add(new vimperator.Option(["incsearch", "is"], "boolean", { short_help: "Show where the search pattern matches as it is typed", help: "NOTE: Incremental searching currently only works in the forward direction.", default_value: true } )); - this.add(new vimperator.Option(["insertmode", "im"], "boolean", + optionManager.add(new vimperator.Option(["insertmode", "im"], "boolean", { short_help: "Use Insert mode as the default for text areas", help: "Makes Vimperator work in a way that Insert mode is the default mode for text areas. " + @@ -567,7 +572,7 @@ vimperator.Options = function () //{{{ default_value: true } )); - this.add(new vimperator.Option(["laststatus", "ls"], "number", + optionManager.add(new vimperator.Option(["laststatus", "ls"], "number", { short_help: "Show the status line", help: "Determines when the last window will have a status line. " + @@ -583,20 +588,20 @@ vimperator.Options = function () //{{{ validator: function (value) { return (value >= 0 && value <= 2); } } )); - this.add(new vimperator.Option(["linksearch", "lks"], "boolean", + optionManager.add(new vimperator.Option(["linksearch", "lks"], "boolean", { short_help: "Limit the search to hyperlink text", help: "This includes (X)HTML elements with an \"href\" atrribute and XLink \"simple\" links.", default_value: false } )); - this.add(new vimperator.Option(["more"], "boolean", + optionManager.add(new vimperator.Option(["more"], "boolean", { short_help: "Pause the message list window when more than one screen of listings is displayed", default_value: true } )); - this.add(new vimperator.Option(["pageinfo", "pa"], "charlist", + optionManager.add(new vimperator.Option(["pageinfo", "pa"], "charlist", { short_help: "Desired info on :pa[geinfo]", help: "Available items:
" + @@ -610,7 +615,7 @@ vimperator.Options = function () //{{{ validator: function (value) { return !(/[^gfm]/.test(value) || value.length > 3 || value.length < 1); } } )); - this.add(new vimperator.Option(["popups", "pps"], "number", + optionManager.add(new vimperator.Option(["popups", "pps"], "number", { short_help: "Where to show requested popup windows", help: "Define where to show requested popup windows. Does not apply to windows which are opened by middle clicking a link, they always open in a new tab. " + @@ -627,7 +632,7 @@ vimperator.Options = function () //{{{ validator: function (value) { return (value >= 0 && value <= 3); } } )); - this.add(new vimperator.Option(["preload"], "boolean", + optionManager.add(new vimperator.Option(["preload"], "boolean", { short_help: "Speed up first time history/bookmark completion", help: "History access can be quite slow for a large history. Vimperator maintains a cache to speed it up significantly on subsequent access.
" + @@ -635,7 +640,7 @@ vimperator.Options = function () //{{{ default_value: true } )); - this.add(new vimperator.Option(["previewheight", "pvh"], "number", + optionManager.add(new vimperator.Option(["previewheight", "pvh"], "number", { short_help: "Default height for preview window", help: "Value must be between 1 and 50. If the value is too high, completions may cover the command-line. " + @@ -645,7 +650,7 @@ vimperator.Options = function () //{{{ validator: function (value) { return (value >= 1 && value <= 50); } } )); - this.add(new vimperator.Option(["scroll", "scr"], "number", + optionManager.add(new vimperator.Option(["scroll", "scr"], "number", { short_help: "Number of lines to scroll with C-u and C-d commands", help: "The number of lines scrolled defaults to half the window size. " + @@ -655,13 +660,13 @@ vimperator.Options = function () //{{{ validator: function (value) { return value >= 0; } } )); - this.add(new vimperator.Option(["showmode", "smd"], "boolean", + optionManager.add(new vimperator.Option(["showmode", "smd"], "boolean", { short_help: "Show the current mode in the command line", default_value: true } )); - this.add(new vimperator.Option(["showstatuslinks", "ssli"], "number", + optionManager.add(new vimperator.Option(["showstatuslinks", "ssli"], "number", { short_help: "Show the destination of the link under the cursor in the status bar", help: "Also links which are focused by keyboard commands like <Tab> are shown. " + @@ -675,7 +680,7 @@ vimperator.Options = function () //{{{ validator: function (value) { return (value >= 0 && value <= 2); } } )); - this.add(new vimperator.Option(["showtabline", "stal"], "number", + optionManager.add(new vimperator.Option(["showtabline", "stal"], "number", { short_help: "Control when to show the tab bar of opened web pages", help: "Possible values:
" + @@ -689,14 +694,14 @@ vimperator.Options = function () //{{{ validator: function (value) { return (value >= 0 && value <= 2); } } )); - this.add(new vimperator.Option(["smartcase", "scs"], "boolean", + optionManager.add(new vimperator.Option(["smartcase", "scs"], "boolean", { short_help: "Override the 'ignorecase' option if the pattern contains uppercase characters", help: "This is only used if the 'ignorecase' option is set.", default_value: true } )); - this.add(new vimperator.Option(["titlestring"], "string", + optionManager.add(new vimperator.Option(["titlestring"], "string", { short_help: "Change the title of the browser window", help: "Vimperator changes the browser title from \"Title of web page - Mozilla Firefox\" to " + @@ -706,7 +711,7 @@ vimperator.Options = function () //{{{ default_value: "Vimperator" } )); - this.add(new vimperator.Option(["usermode", "um"], "boolean", + optionManager.add(new vimperator.Option(["usermode", "um"], "boolean", { short_help: "Show current website with a minimal style sheet to make it easily accessible", help: "Note that this is a local option for now, later it may be split into a global and :setlocal part", @@ -715,7 +720,7 @@ vimperator.Options = function () //{{{ default_value: false } )); - this.add(new vimperator.Option(["verbose", "vbs"], "number", + optionManager.add(new vimperator.Option(["verbose", "vbs"], "number", { short_help: "Define which type of messages are logged", help: "When bigger than zero, Vimperator will give messages about what it is doing. They are printed to the error console which can be shown with :javascript!.
" + @@ -724,14 +729,14 @@ vimperator.Options = function () //{{{ validator: function (value) { return (value >= 0 && value <= 9); } } )); - this.add(new vimperator.Option(["visualbell", "vb"], "boolean", + optionManager.add(new vimperator.Option(["visualbell", "vb"], "boolean", { short_help: "Use visual bell instead of beeping on errors", setter: function (value) { vimperator.options.setFirefoxPref("accessibility.typeaheadfind.enablesound", !value); }, default_value: false } )); - this.add(new vimperator.Option(["wildmode", "wim"], "stringlist", + optionManager.add(new vimperator.Option(["wildmode", "wim"], "stringlist", { short_help: "Define how command line completion works", help: "It is a comma-separated list of parts, where each part specifies " + @@ -755,7 +760,7 @@ vimperator.Options = function () //{{{ } } )); - this.add(new vimperator.Option(["wildoptions", "wop"], "stringlist", + optionManager.add(new vimperator.Option(["wildoptions", "wop"], "stringlist", { short_help: "Change how command line completion is done", help: "A list of words that change how command line completion is done.
" + @@ -767,7 +772,7 @@ vimperator.Options = function () //{{{ validator: function (value) { return /^(sort|)$/.test(value); } } )); - this.add(new vimperator.Option(["nextpattern"], "stringlist", + optionManager.add(new vimperator.Option(["nextpattern"], "stringlist", { short_help: "String to search when looking for 'next' page in document relation", help: "Change it to make it look for another string in links when pressing ]n
" + @@ -775,7 +780,7 @@ vimperator.Options = function () //{{{ default_value: "\\bnext,^>$" } )); - this.add(new vimperator.Option(["previouspattern"], "stringlist", + optionManager.add(new vimperator.Option(["previouspattern"], "stringlist", { short_help: "String to search when looking for 'prev' page in document relation", help: "Change it to make it look for another string in links when pressing ]p
" + @@ -792,8 +797,10 @@ vimperator.Options = function () //{{{ setLastStatus(0); guioptions_done = showtabline_done = laststatus_done = false; - setTitleString(this.titlestring); - setPopups(this.popups); + setTitleString(optionManager.titlestring); + setPopups(optionManager.popups); + + return optionManager; }; //}}} // vim: set fdm=marker sw=4 ts=4 et: diff --git a/content/vimperator.js b/content/vimperator.js index 58d9b281..7d36c349 100644 --- a/content/vimperator.js +++ b/content/vimperator.js @@ -47,6 +47,7 @@ const vimperator = (function () //{{{ /////////////////////////////////////////////////////////////////////////////{{{ return { + get mode() { return vimperator.modes.main; }, set mode(value) { vimperator.modes.main = value; }, @@ -581,11 +582,11 @@ const vimperator = (function () //{{{ // these objects are created here only after the chrome is ready vimperator.log("Loading module options...", 3); - vimperator.options = new vimperator.Options(); + vimperator.options = vimperator.Options(); vimperator.log("Loading module events...", 3); - vimperator.events = new vimperator.Events(); + vimperator.events = vimperator.Events(); vimperator.log("Loading module commands...", 3); - vimperator.commands = new vimperator.Commands(); + vimperator.commands = vimperator.Commands(); vimperator.log("Loading module bookmarks...", 3); vimperator.bookmarks = vimperator.Bookmarks(); vimperator.log("Loading module history...", 3); @@ -599,7 +600,7 @@ const vimperator = (function () //{{{ vimperator.log("Loading module buffer window...", 3); vimperator.bufferwindow = vimperator.InformationList("vimperator-bufferwindow", { incremental_fill: false, max_items: 10 }); vimperator.log("Loading module mappings...", 3); - vimperator.mappings = new vimperator.Mappings(); + vimperator.mappings = vimperator.Mappings(); vimperator.log("Loading module statusline...", 3); vimperator.statusline = vimperator.StatusLine(); vimperator.log("Loading module buffer...", 3);