From d0fe3cc4262e13d18977132d1232df00f67576c9 Mon Sep 17 00:00:00 2001 From: Martin Stubenschrott Date: Tue, 10 Apr 2007 19:43:19 +0000 Subject: [PATCH] Initial import --- chrome/content/vimperator/bookmarks.js | 153 ++ chrome/content/vimperator/commands.js | 1682 ++++++++++++++++++++++ chrome/content/vimperator/completion.js | 486 +++++++ chrome/content/vimperator/contents.rdf | 24 + chrome/content/vimperator/hints.js | 640 ++++++++ chrome/content/vimperator/settings.js | 358 +++++ chrome/content/vimperator/tags | 110 ++ chrome/content/vimperator/vimperator.js | 970 +++++++++++++ chrome/content/vimperator/vimperator.xul | 96 ++ 9 files changed, 4519 insertions(+) create mode 100644 chrome/content/vimperator/bookmarks.js create mode 100644 chrome/content/vimperator/commands.js create mode 100644 chrome/content/vimperator/completion.js create mode 100644 chrome/content/vimperator/contents.rdf create mode 100644 chrome/content/vimperator/hints.js create mode 100644 chrome/content/vimperator/settings.js create mode 100644 chrome/content/vimperator/tags create mode 100644 chrome/content/vimperator/vimperator.js create mode 100644 chrome/content/vimperator/vimperator.xul diff --git a/chrome/content/vimperator/bookmarks.js b/chrome/content/vimperator/bookmarks.js new file mode 100644 index 00000000..22f72c32 --- /dev/null +++ b/chrome/content/vimperator/bookmarks.js @@ -0,0 +1,153 @@ +/* + * low-level BOOKMARK and HISTORY handling + * + * these commands try to be generic and don't use any GUI handling code + * for higher-level functions look into commands.js + */ + +function getProperty( aInput, aArc, DS ) +{ + var node; + node = DS.GetTarget( aInput, aArc, true ); + if( node instanceof Components.interfaces.nsIRDFResource ) { + return node.Value; + } + if( node instanceof Components.interfaces.nsIRDFLiteral ) { + return node.Value; + } + return ""; +} + +function addBookmark(title, uri) +{ + folder = RDF.GetResource("NC:BookmarksRoot"); + var rSource = BookmarksUtils.createBookmark(title, uri, null, title); + var selection = BookmarksUtils.getSelectionFromResource(rSource); + var target = BookmarksUtils.getTargetFromFolder(folder); + BookmarksUtils.insertAndCheckSelection("newbookmark", selection, target); + + //also update bookmark cache + g_bookmarks.unshift([uri, title]); +} + +/* no idea what it does, it Just Works (TM) + * + * returns number of deleted bookmarks + */ +function deleteBookmark(url) +{ + var deleted = 0; + + // gNC_NS for trunk, NC_NS for 1.X + try {var pNC_NS; pNC_NS = gNC_NS;} catch (err) { pNC_NS = NC_NS;} + if(! BMSVC || ! BMDS || ! RDF || ! pNC_NS ) return null; + if ( !url) return null; // just in case + + var curfolder = RDF.GetResource("NC:BookmarksRoot"); + var urlArc = RDF.GetResource(pNC_NS+"URL"); + var urlLiteral = RDF.GetLiteral(url); + if (BMDS.hasArcIn(urlLiteral, urlArc)) { + var bmResources, bmResource, title, uri, type, ptype; + bmResources = BMSVC.GetSources(urlArc, urlLiteral, true); + while (bmResources.hasMoreElements()) { + bmResource = bmResources.getNext(); + type = BookmarksUtils.resolveType(bmResource); + if (type != "ImmutableBookmark") { + ptype = BookmarksUtils.resolveType(BMSVC.getParent(bmResource)); +// alert(type); +// if ( type == "Folder") // store the current folder +// curfolder = bmResource; + if ( (type == "Bookmark" || type == "IEFavorite") && ptype != "Livemark") { + title = BookmarksUtils.getProperty(bmResource, pNC_NS+"Name"); + uri = BookmarksUtils.getProperty(bmResource, pNC_NS+"URL"); + + if (uri == url) + { + RDFC.Init(BMDS, BMSVC.getParent(bmResource)); + RDFC.RemoveElement(bmResource, true); + deleted++; + } + } + } + } + } + + // also update bookmark cache, if we removed at least one bookmark + if(deleted > 0) + bookmarks_loaded = false; + + return deleted; +} + +/* call the function like this: + var res = new Object(); + parseBookmarkString("-t tag1,tag2 -T title http://www.orf.at", res); + res.tags is an array of tags + res.title is the title or "" if no one was given + res.url is the url as a string + + returns false, if parsing failed +*/ +function parseBookmarkString(str, res) +{ + res.tags = []; + res.title = null; + res.url = null; + + var re_title = /^\s*((-t|--title)\s+(\w+|\".*\"))(.*)/; + var re_tags = /^\s*((-T|--tags)\s+((\w+)(,\w+)*))(.*)/; + var re_url = /^\s*(\".+\"|\S+)(.*)/; + + var match_tags = null; + var match_title = null; + var match_url = null; + + while(!str.match(/^\s*$/)) + { + /* first check for --tags */ + match_tags = str.match(re_tags); + if(match_tags != null) + { + str = match_tags[match_tags.length-1]; // the last captured parenthesis is the rest of the string + tags = match_tags[3].split(","); + res.tags = res.tags.concat(tags); + } + else /* then for --titles */ + { + + match_title = str.match(re_title); + if(match_title != null) + { + // only one title allowed + if (res.title != null) + return false; + + str = match_title[match_title.length-1]; // the last captured parenthesis is the rest of the string + title = match_title[3]; + if(title.charAt(0) == '"') + title = title.substring(1,title.length-1); + res.title = title; + } + else /* at last check for an url */ + { + match_url = str.match(re_url); + if (match_url != null) + { + // only one url allowed + if (res.url != null) + return false; + + str = match_url[match_url.length-1]; // the last captured parenthesis is the rest of the string + url = match_url[1]; + if(url.charAt(0) == '"') + url = url.substring(1,url.length-1); + res.url = url; + } + else return false; // no url, tag or title found but still text left, abort + } + } + } + + return true; +} + diff --git a/chrome/content/vimperator/commands.js b/chrome/content/vimperator/commands.js new file mode 100644 index 00000000..3b405dbc --- /dev/null +++ b/chrome/content/vimperator/commands.js @@ -0,0 +1,1682 @@ +/***** BEGIN LICENSE BLOCK ***** {{{ +Version: MPL 1.1/GPL 2.0/LGPL 2.1 + +The contents of this file are subject to the Mozilla Public License Version +1.1 (the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +for the specific language governing rights and limitations under the +License. + +(c) 2006-2007: Martin Stubenschrott + +Alternatively, the contents of this file may be used under the terms of +either the GNU General Public License Version 2 or later (the "GPL"), or +the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +in which case the provisions of the GPL or the LGPL are applicable instead +of those above. If you wish to allow use of your version of this file only +under the terms of either the GPL or the LGPL, and not to allow others to +use your version of this file under the terms of the MPL, indicate your +decision by deleting the provisions above and replace them with the notice +and other provisions required by the GPL or the LGPL. If you do not delete +the provisions above, a recipient may use your version of this file under +the terms of any one of the MPL, the GPL or the LGPL. +}}} ***** END LICENSE BLOCK *****/ + +/* all built-in :ex-commands of Vimperator + * format: + * [ + * 0: [all names of this command], + * 1: description, + * 2: function (arguments in this order: args, special, count) + * 3: helptext + * 4: completefunc + * ] + */ +var g_commands = [/*{{{*/ + [ + ["addons"], + "Show available Browser Extensions and Themes", + "You can add/remove/disable browser extensions from this dialog.
Be aware that not all Firefox extensions work, because Vimperator overrides some keybindings and changes Firefox's GUI.", + function() { openURLsInNewTab("chrome://mozapps/content/extensions/extensions.xul", true); }, + null + ], + [ + ["back", "ba"], + "Go back in the browser history", + "Count is supported, :3back goes back 3 pages in the browser history.", + function(args, special, count) { stepInHistory(count > 0 ? -1 * count : -1); }, + null + ], + [ + ["bdelete", "bd", "bwipeout", "bw", "bunload", "bun", "tabclose", "tabc"], + "Delete current buffer (=tab)", + "Count WILL be supported in future releases, then :2bd removes two tabs and the one the right is selected.
Do :bdelete! to select the tab to the left after removing the current tab.", + function (args, special, count) { tab_remove (count, special, 0); }, + null + ], + [ + ["beep"], + "Play a system beep", + null, + function() { beep(); }, + null + ], + [ + ["bmadd"], + "Add a bookmark", + "Usage: :bmadd [-t \"my custom title\"] [-T \"comma,separated,tags\"] [url]
" + + "If you don't add a custom title, either the title of the webpage or the URL will be taken as the title.
"+ + "Tags WILL be some mechanism to classify bookmarks. Assume, you tag a url with the tags \"linux\" and \"computer\" you'll be able to search for bookmarks containing these tags.
" + + "You can omit the optional [url] field, so just do :bmadd to bookmark the currently loaded web page with a default title and without any tags.", + function(args) { bmadd(args); }, + null + ], + [ + ["bmdel"], + "Delete a bookmark", + "Usage: :bmdel [-T \"comma,separated,tags\"] <url>
" + + "Deletes all bookmarks which matches the url AND the specified tags. Use <Tab> key on a regular expression to complete the url which you want to delete.", + function(args) { bmdel(args); }, + function(filter) { return get_bookmark_completions(filter); } + ], + [ + ["bookmarks", "bm"], + "Show bookmarks", + "Usage: :bm [-T \"comma,separated,tags\"] <regexp>
" + + "Open the preview window at the bottom of the screen for all bookmarks which match the regexp either in the title or URL.
" + + "Close this window with :pclose or open entries with double click in the current tab or middle click in a new tab.", + function(args, special) { bmshow(args, special); }, + function(filter) { return get_bookmark_completions(filter); } + ], + [ + ["downloads", "dl"], + "Show progress of current downloads", + "Open the original Firefox download dialog in a new tab.
" + + "Here, downloads can be paused, canceled and resumed.", + function() { openURLsInNewTab("chrome://mozapps/content/downloads/downloads.xul", true); }, + null + ], + [ + ["echo"], + "Display a string at the bottom of the window", + "Echo all arguments of this command. Useful for showing informational messages.
Multiple lines WILL be seperated by \\n.", + function(args) { echo(args); }, + null + ], + [ + ["echoerr"], + "Display an error string at the bottom of the window", + "Echo all arguments of this command highlighted in red. Useful for showing important messages.
Multiple lines WILL be seperated by \\n.", + function(args) { echoerr(args); }, + null + ], + [ + ["execute", "exec"], + "Run any javascript command through eval()", + "Acts as a javascript interpreter by passing the argument to eval().
"+ + ":exec alert('Hello world') would show a dialog box with the text \"Hello world\".
"+ + "The special version :execute! will open the javascript console of Firefox.", + function(args, special) { + if (special) // open javascript console + openURLsInNewTab("chrome://global/content/console.xul", true); + else + eval(args); + }, + null + ], + [ + ["forward", "fw"], + "Go forward in the browser history", + "Count is supported, :3forward goes forward 3 pages in the browser history.", + function(count) { stepInHistory(count > 0 ? count : 1); }, + null + ], + [ + ["hardcopy", "ha"], + "Print current document", + "Open a GUI dialog where you can select the printer, number of copies, orientation, etc.", + function() { goDoCommand('cmd_print'); }, + null + ], + [ + ["help", "h"], + "Open help window", + "Open the help window in the current tab. You WILL be able to show a specific section with :help commands.", + function() { help(null); }, + null + ], + [ + ["history", "hs"], + "Show recently visited URLs", + "Usage: :hs <regexp>
" + + "Open the preview window at the bottom of the screen for all history items which match the regexp either in the title or URL.
" + + "Close this window with :pclose or open entries with double click in the current tab or middle click in a new tab.", + function(args, special) { hsshow(args, special); }, + function(filter) { return get_history_completions(filter); } + ], + [ + ["mark"], + "Mark current location within the webpage", + "Not implemented yet", + function (args) { set_location_mark(args); }, + null + ], + [ + ["marks"], + "Show all location marks of current webpage", + "Not implemented yet", + function (args) { set_location_mark(args); }, + null + ], + [ + ["open", "o"], + "Open one ore more URLs", + "Usage: :open <url> [| <url>]
" + + "Opens one ore more URLs in the current buffer.
"+ + "Multiple URLs can be separated with the | character.
" + + "Each |-separated token is analazed and in this order:
"+ + "
  1. Opened with the specified search engine if the token looks like a search string and the first word of the token is the name of a search engine (:open wiki linus torvalds will open the wikipedia entry for linux torvalds).
  2. "+ + "
  3. Opened with the default search engine if the first word is no search engine (:open linus torvalds will open a google search for linux torvalds).
  4. "+ + "
  5. Passed directly to Firefox in all other cases (:open www.osnews.com | www.slashdot.org will open OSNews in the current, and Slashdot in a new background tab).
"+ + "You WILL be able to use :open [-T \"linux\"] torvalds<Tab> to complete bookmarks with tag \"linux\" and which contain \"torvalds\". Note that -T support is only available for tab completion, not for the actual command.
"+ + "The items which are completed on <Tab> are specified in the 'complete' option.", + function(args) { if(args) openURLs(args); else reload(false); }, + function(filter) { return get_url_completions(filter); } + ], + [ + ["pclose", "pc"], + "Close preview window on bottom of screen", + null, + function() { preview_window.hidden = true; }, + null + ], + [ + ["quit", "q"], + "Quit current tab or quit Vimperator if this was the last tab", + "When quitting Vimperator, the session is not stored.", + function (args) { tab_remove(1, false, 1); }, + null + ], + [ + ["quitall", "qall", "qa"], + "Quit Vimperator", + "Quit Vimperator, no matter how many tabs/windows are open. The session is not stored.", + function (args) { quit(false); }, + null + ], + [ + ["reload", "re"], + "Reload current page", + "Forces reloading of the current page, or of all open pages, if ! is given.", + function(args, special) { reload(special); }, + null + ], + [ + ["save", "saveas", "sav"], + "Save current web page to disk", + "Open the original Firefox \"Save page as...\" dialog in a new tab.
" + + "There, you can save the current web page to disk with various options.", + function() { goDoCommand('Browser:SavePage'); }, + null + ], + [ + ["set", "se"], + "Set an option", + "Permanently change an option. In contrast to Vim options are stored throughout sessions.
"+ + "Boolean options must be set with :set option and :set nooption.
"+ + ":set without an argument opens about:config in a new tab to change advanced Firefox options.
"+ + ":set! opens the GUI preference panel from Firefox in a new tab.
"+ + ":set option? shows the current value of the option.
"+ + ":set option+=foo and :set option-=foo WILL add/remove foo from list options.
", + function(args, special) { set(args, special); }, + function(filter) { return get_settings_completions(filter); } + ], + [ + ["source", "so"], + "Load a local javascript file and execute it", + "Not implemented yet", + function(args) { source(args); }, + null + ], + [ + ["tabnext", "tn", "tnext"], + "Switch to the next tab", + "Cycles to the first tab, when the last is selected.", + function(args, special, count) { go_tab(0); }, + null + ], + [ + ["tabopen", "t", "to", "topen"], + "Open one or more URLs in a new tab", + "Like :open but open URLs in a new tab. If used with !, the 'tabopen' value of the 'activate' setting is negated.", + function (args, special) { openURLsInNewTab(args, !special); }, + function (filter) { return get_url_completions(filter); } + ], + [ + ["tabprevious", "tp", "tprev", "tprevious"], + "Switch to the previous tab", + "Cycles to the last tab, when the first is selected.", + function(args, count) { go_tab(-1); }, + null + ], + [ + ["undo", "u"], + "Undo closing of a tab", + "If a count is given, don't close the last but the n'th last tab", + function(args, special, count) { if(count < 1) count = 1; undoCloseTab(count-1); }, + null + ], + [ + ["qmarkadd", "qmadd"], + "Mark a URL with a letter for quick access", + "Not implemented yet", + function(args) { set_url_mark("mark", "url"); }, // FIXME + function(filter) { return [["a", ""], ["b", ""]]; } + ], + [ + ["qmarkdel", "qmdel"], + "Mark a URL with a letter for quick access", + "Not implemented yet", + function(args) { set_url_mark("mark", "url"); }, // FIXME + function(filter) { return [["a", ""], ["b", ""]]; } + ], + [ + ["qmarks", "qms"], + "Shows marked URLs", + "Not implemented yet", + function(args) { show_url_marks(args); }, // FIXME + null + ], + [ + ["version", "ve"], + "Show version information", + null, + function () { echo("Vimperator version: 0.2.0.1"); }, + null + ], + [ + ["winopen", "w", "wo", "wopen"], + "Open an URL in a new window", + "Not implemented yet", + function () { echo("winopen not yet implemented"); }, + null + ], + [ + ["xall", "xa", "wqall", "wqa"], + "Save the session and quit", + "Quit Vimperator, no matter how many tabs/windows are open. The session is stored.", + function (args) { quit(true); }, + null + ], + [ + ["zoom", "zo"], + "Set zoom value of the webpage", + "Usage: :zoom 150 zooms to 150% text size.
"+ + "Zoom value can be between 25 and 500%. If it is omitted, zoom is reset to 100%.", + function (args) { zoom_to(args); }, + null + ] +];/*}}}*/ + +/* all built-in normal mode commands of Vimperator + * format: + * [ + * 0: [all shortcuts of this command], + * 1: description, + * 2: helptext + * 3: function (arguments in this order: args, special, count) + * ] + */ +var g_mappings = [/*{{{*/ + [ + ["]f"], + "Focus next frame", + "Flashes the next frame in order with a red color, to quickly show where keyboard focus is.
"+ + "This may not work correctly for frames with lots of CSS code.", + function(count) { focusNextFrame(); } + ], + [ + ["d"], + "Delete current buffer (=tab)", + "Count WILL be supported in future releases, then 2d removes two tabs and the one the right is selected.", + function(count) { tab_remove(count, false, 0); } + ], + [ + ["D"], + "Delete current buffer (=tab)", + "Count WILL be supported in future releases, then 2D removes two tabs and the one the left is selected.", + function(count) { tab_remove(count, true, 0); } + ], + [ + ["ge"], + "Execute a JavaScript code", + "Go Execute works like :execute.
"+ + "This mapping is for debugging purposes, and may be removed in future.", + function(count) { openVimperatorBar('execute '); } + ], + [ + ["gP"], + "Open (put) an URL based on the current Clipboard contents in a new buffer", + "Works like P, but inverts the 'activate' setting.", + function(count) { openURLsInNewTab(readFromClipboard(), false); } + ], + [ + ["gt", ""], + "Go to next tab", + "Cycles to the first tab, when the last is selected.
"+ + "Count is supported, 3gt goes to the third tab.", + function(count) { tab_go(count > 0 ? count : 0); } + ], + [ + ["gT", ""], + "Go to previous tab", + "Cycles to the last tab, when the first is selected.
"+ + "Count is supported, 3gt goes to the third tab.", + function(count) { tab_go(count > 0 ? count :-1); } + ], + [ + ["o"], + "Open one or more URLs in the current tab", + "See :open for more details", + function(count) { openVimperatorBar('open '); } + ], + [ + ["O"], + "Open one ore more URLs in the current tab, based on current location", + "Works like o, but preselects current URL in the :open query.", + function(count) { openVimperatorBar('open ' + getCurrentLocation()); } + ], + [ + ["p", ""], + "Open (put) an URL based on the current Clipboard contents in the current buffer", + "You can also just select some non-URL text, and search for it with the default search engine with p", + function(count) { openURLs(readFromClipboard()); } + ], + [ + ["P"], + "Open (put) an URL based on the current Clipboard contents in a new buffer", + "If the new buffer is activated, depends on the 'activate' setting."+ + "You can also just select some non-URL text, and search for it with the default search engine with p", + function(count) { openURLsInNewTab(readFromClipboard(), true); } + ], + [ + ["r"], + "Reload current page", + "Forces reloading of the current page.", + function(count) { reload(false); } + ], + [ + ["R"], + "Reload all pages", + "Forces reloading of all open pages.", + function(count) { reload(true); } + ], + [ + ["t"], + "Open one or more URLs in a new tab", + "Like o but open URLs in a new tab."+ + "See :tabopen for more details", + function(count) { openVimperatorBar('tabopen '); } + ], + [ + ["T"], + "Open one ore more URLs in a new tab, based on current location", + "Works like t, but preselects current URL in the :tabopen query.", + function(count) { openVimperatorBar('tabopen ' + getCurrentLocation()); } + ], + [ + ["u"], + "Undo closing of a tab", + "If a count is given, don't close the last but the n'th last tab", + function(count) { execute_command(count, 'undo', false, ''); } + ], + [ + ["y"], + "Yank current location to the Clipboard", + "Under UNIX the location is also put into the selection, which can be pasted with the middle mouse button.", + function(count) { yankCurrentLocation(); } + ], + [ + ["zi"], + "Zoom in", + "Zoom in current web page by 25%.
"+ + "Currently no count supported.", + function(count) { zoom_in(1); } + ], + [ + ["zI"], + "Zoom in more", + "Zoom in current web page by 100%.
"+ + "Currently no count supported.", + function(count) { zoom_in(4); } + ], + [ + ["zo"], + "Zoom out", + "Zoom out current web page by 25%.
"+ + "Currently no count supported.", + function(count) { zoom_in(-1); } + ], + [ + ["zO"], + "Zoom out more", + "Zoom out current web page by 100%.
"+ + "Currently no count supported.", + function(count) { zoom_in(-4); } + ], + [ + ["zz"], + "Set zoom value of the webpage", + "150zz zooms to 150% text size.
"+ + "Zoom value can be between 25 and 500%. If it is omitted, zoom is reset to 100%.", + function(count) { zoom_to(count); } + ], + [ + ["ZQ"], + "Quit Vimperator", + "Quit Vimperator, no matter how many tabs/windows are open. The session is not stored.
" + + "Works like :qall.", + function(count) { quit(false); } + ], + [ + ["ZZ"], + "Save the session and quit", + "Quit Vimperator, no matter how many tabs/windows are open. The session is stored.
" + + "Works like :xall.", + function(count) { quit(true); } + ], + + /* scrolling commands */ + [ + ["0", "^"], + "Scroll to the absolute left of the document", + "Unlike in vim, 0 and ^ work exactly the same way.", + function(count) { scrollBufferAbsolute(0, -1); } + ], + [ + ["$"], + "Scroll to the absolute right of the document", + null, + function(count) { scrollBufferAbsolute(100, -1); } + ], + [ + ["gg", ""], + "Goto the top of the document", + "Count is supported, 35gg vertically goes to 35% of the document", + function(count) { scrollBufferAbsolute(-1, count > 0 ? count : 0); } + ], + [ + ["G", ""], + "Goto the end of the document", + "Count is supported, 35G vertically goes to 35% of the document", + function(count) { scrollBufferAbsolute(-1, count >= 0 ? count : 100); } + ], + [ + ["h", ""], + "Scroll document to the left", + "Count is supported: 10h will move 10 times as much to the left.
"+ + "If the document cannot scroll more, a beep is emmited (unless 'beep' is turned off).", + function(count) { scrollBufferRelative(-1, 0); } + ], + [ + ["j", "", ""], + "Scroll document down", + "Count is supported: 10j will move 10 times as much down.
"+ + "If the document cannot scroll more, a beep is emmited (unless 'beep' is turned off).", + function(count) { scrollBufferRelative(0, 1); } + ], + [ + ["k", "", ""], + "Scroll document up", + "Count is supported: 10k will move 10 times as much up.
"+ + "If the document cannot scroll more, a beep is emmited (unless 'beep' is turned off).", + function(count) { scrollBufferRelative(0, -1); } + ], + [ + ["l", ""], + "Scroll document to the right", + "Count is supported: 10l will move 10 times as much to the right.
"+ + "If the document cannot scroll more, a beep is emmited (unless 'beep' is turned off).", + function(count) { scrollBufferRelative(1, 0); } + ], + [ + ["", ""], + "Scroll up a page", + "Scroll up a full page of the current document. No count support for now.", + function(count) { goDoCommand('cmd_scrollPageUp'); } + ], + [ + ["", ""], + "Scroll down a page", + "Scroll dow a full page of the current document. No count support for now.", + function(count) { goDoCommand('cmd_scrollPageDown'); } + ], + + /* history manipulation and jumplist */ + [ + [""], + "Go to an older position in the jump list", + "The jump list is just the browser history for now", + function(count) { stepInHistory(count > 0 ? -1 * count : -1); } + ], + [ + [""], + "Go to a newer position in the jump list", + "The jump list is just the browser history for now", + function(count) { stepInHistory(count > 0 ? count : 1); } + ], + [ + ["H"], + "Go back in the browser history", + "Count is supported, 3H goes back 3 steps.", + function(count) { stepInHistory(count > 0 ? -1 * count : -1); } + ], + [ + ["L"], + "Go forward in the browser history", + "Count is supported, 3L goes forward 3 steps.", + function(count) { stepInHistory(count > 0 ? count : 1); } + ], + + /* hint managment */ + [ + ["f"], + "Start QuickHint mode", + "In QuickHint mode, every hintable item (according to the 'hinttags' XPath query) is assigned a label.
"+ + "If you then press the keys for a label, it is followed as soon as it can be uniquely identified and this mode is stopped. Or press <Esc> to stop this mode.
"+ + "If you write the hint in ALLCAPS, the hint is followed in a background tab.", + function(count) { hah.enableHahMode(HINT_MODE_QUICK); } + ], + [ + ["F"], + "Start AlwaysHint mode", + "In AlwaysHint mode, every hintable item (according to the 'hinttags' XPath query) is assigned a label.
"+ + "If you then press the keys for a label, it is followed as soon as it can be uniquely identified. Labels stay active after following a hint in this mode, press <Esc> to stop this mode.
"+ + "This hint mode is especially useful for browsing large sites like Forums as hints are automatically regenerated when switching to a new document.", + function(count) { hah.enableHahMode(HINT_MODE_ALWAYS); } + ], + [ + [";"], + "Start ExtendedHint mode", + "ExtendedHint mode is useful, since in this mode you can yank link locations, or open them in a new window.
"+ + "If you want to yank the location of hint AB, press ; to start this hint mode.
"+ + "Then press AB to select the hint. Now press y to yank its location.
"+ + "Actions for selected hints in ExtendedHint mode are:
"+ + "
  • y to yank its location
  • "+ + "
  • o to open its location in the current tab
  • "+ + "
  • t to open its location in a new tab
  • "+ + "
  • O to open its location in an :open query (not implemented yet)
  • "+ + "
  • T to open its location in an :tabopen query (not implemented yet)
  • "+ + "
  • s to save its destination (not implemented yet)
  • "+ + "
  • <C-w> to open its destination in a new window
  • "+ + "
"+ + "Multiple hints can be seperated by commas where it makes sense. ;AB,AC,ADt opens AB, AC and AD in a new tab.
"+ + "Hintable elements for this mode can be set in the 'extendedhinttags' XPath string.", + function(count) { hah.enableHahMode(HINT_MODE_EXTENDED); } + ], + + + /* vimperator managment */ + [ + [""], + "Open help window", + "The default section is shown, if you need help for a specific topic, try :help <F1> (jumping to a specific section not implemented yet).", + function(count) { execute_command(0, 'help', false, ''); } + ], + [ + [":"], + "Start command line mode", + "In command line mode, you can perform extended commands, which may require arguments.", + function(count) { openVimperatorBar(null); } + ], + [ + [""], + "Cancel any operation", + "Stops loading the current webpage and exits any command line or hint mode.
"+ + "Also focuses the web page, in case a form field has focus, and eats our key presses.", + function(count) { onEscape(); } + ], + + /* quick bookmark access - will be customizable in future*/ + [ + ["'b"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLs('www.bwin.com'); } + ], + [ + ["'o"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLs('www.osnews.com'); } + ], + [ + ["'s"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLs('www.derstandard.at'); } + ], + [ + ["'w"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLs('wetter.orf.at'); } + ], + [ + ["'t"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLs('www.tvinfo.de'); } + ], + [ + ["\"b"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLsInNewTab('www.bwin.com'); } + ], + [ + ["\"o"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLsInNewTab('www.osnews.com'); } + ], + [ + ["\"s"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLsInNewTab('www.derstandard.at'); } + ], + [ + ["\"w"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLsInNewTab('wetter.orf.at'); } + ], + [ + ["\"t"], + "These quick bookmarks will be customizable in future releases, ignore for now", + null, + function(count) { openURLsInNewTab('www.tvinfo.de'); } + ] +];/*}}}*/ + +var g_insert_mappings = [ /*{{{*/ + ["xxx", "todo"], + ["", "delete word"], + ["", "delete beginning"], + ["", "go beginning"], + ["", "go end"], + ["", "cancel"] +]; /*}}}*/ + +/* [command, action, cancel_hint_mode, always_active] */ +var g_hint_mappings = [ /*{{{*/ + /* hint action keys */ + ["o", "hah.openHints(false, false);", true, false], + ["t", "hah.openHints(true, false);", true, false], + ["", "hah.openHints(false, true );", true, false], + ["s", "echoerr('Saving of links not yet implemented');", true, false], + ["y", "hah.yankHints();", true, false], + [",", "g_inputbuffer+=','; hah.setCurrentState(0);", false, true], + [":", "openVimperatorBar(null);", false, true], + /* movement keys */ + ["", "scrollBufferRelative(0, 1);", false, true], + ["", "scrollBufferRelative(0, -1);", false, true], + ["", "scrollBufferAbsolute(-1, 0);", false, true], + ["", "scrollBufferAbsolute(-1, 100);", false, true], + ["", "goDoCommand('cmd_scrollPageUp');", false, true], + ["", "goDoCommand('cmd_scrollPageUp');", false, true], + ["", "goDoCommand('cmd_scrollPageDown');", false, true], + ["", "goDoCommand('cmd_scrollPageDown');", false, true], + ["", "scrollBufferRelative(-1, 0);", false, true], + ["", "scrollBufferRelative(0, 1);", false, true], + ["", "scrollBufferRelative(0, -1);", false, true], + ["", "scrollBufferRelative(1, 0);", false, true], + /* tab managment */ + ["", "tab_go(0)", true, true], // same as gt, but no count supported + ["", "tab_go(-1)", true, true], + /* navigation */ + ["", "stepInHistory(g_count > 0 ? -1 * g_count : -1);", false, true], + ["", "stepInHistory(g_count > 0 ? g_count : 1);", false, true], + ["", "tab_remove(g_count, false, 0);", true, true], + /* cancel hint mode keys */ + ["", "", true, true], + ["", "", true, true], + ["", "", true, true] +]; /*}}}*/ + +var g_searchengines = [ /*{{{*/ + ["google", "http://www.google.com/search?num=100&q=%s"], + ["chefkoch", "http://www.chefkoch.de/rezept-suche.php?Suchbegriff=%s"], + ["dewiki", "http://de.wikipedia.org/wiki/%s"], + ["discogs", "http://www.discogs.com/search?type=all&q=%s&btn=Search"], + ["geizhals", "http://geizhals.at/?fs=%s"], + ["imdb", "http://www.imdb.com/find?s=all&q=%s"], + ["leo", "http://dict.leo.org/ende?search=%s"], + ["wien", "http://members.aon.at/flole/vienna.html?UserQuery=%s&ResUser=1024&WidthUser=2000"], + ["wiki", "http://en.wikipedia.org/wiki/%s"] +];/*}}}*/ + + +// returns null, if the cmd cannot be found in our g_commands array, or +// otherwise a refernce to our command +function get_command(cmd) // {{{ +{ + for (var i=0; i < g_commands.length; i++) + { + for (var j=0; j < g_commands[i][0].length; j++) + { + if (g_commands[i][0][j] == cmd) + { + return g_commands[i]; + } + } + } + return null; +} // }}} + +function execute_command(count, cmd, special, args) // {{{ +{ + var command = get_command(cmd); + if (command == null) + { + echoerr("E492: Not an editor command: " + cmd); + focusContent(false, false); + return; + } + + if (command[3] == null) + { + echoerr("E666: Internal error: command[2] == null"); + return; + } + + // valid command, call it: + command[3].call(this, args, special, count); + +} // }}} + +//////////////////////////////////////////////////////////////////////// +// statusbar/commandbar handling ////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +function echo(msg) +{ + /* In Mozilla, the XUL textbox is implemented as a wrapper around an HTML + * input element. The read only property '.inputField' holds a reference to this inner + * input element. */ + var bar = command_line.inputField; + var focused = document.commandDispatcher.focusedElement; + if (focused && focused == bar) + return; + + bar.setAttribute("style","font-family: monospace;"); + bar.value = msg; +} + +function echoerr(msg) +{ + /* In Mozilla, the XUL textbox is implemented as a wrapper around an HTML + * input element. The read only property '.inputField' holds a reference to this inner + * input element. */ + var bar = command_line.inputField; + var focused = document.commandDispatcher.focusedElement; + if (focused && focused == bar) + return; + + bar.setAttribute("style", "font-family: monospace; color:white; background-color:red; font-weight: bold"); + bar.value = msg; +} + +//////////////////////////////////////////////////////////////////////// +// navigation functions /////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +function stepInHistory(steps) +{ + var index = getWebNavigation().sessionHistory.index + steps; + if (index >= 0 && index < getWebNavigation().sessionHistory.count) { + getWebNavigation().gotoIndex(index); + } + else + { + beep(); + if(index<0) + echo("Already at beginning of history"); + else + echo("Already at end of history"); + } +} + +function goUp() // FIXME +{ + +} + + +//////////////////////////////////////////////////////////////////////// +// url functions ////////////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +function openURLs(str) +{ + urls = stringToURLs(str); + if (urls.length == 0) + return; + + getWebNavigation().loadURI(urls[0], nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null); + for (var url=1; url < urls.length; url++) + gBrowser.addTab(urls[url]); +} + +function openURLsInNewTab(str, activate) +{ + urls = stringToURLs(str); + if (urls.length == 0) + return null; + + var firsttab = getBrowser().addTab(urls[0]); + if (activate) + getBrowser().selectedTab = firsttab; + for (url=1; url < urls.length; url++) + gBrowser.addTab(urls[url]); + + return firsttab; +} + +/* takes a string like 'google bla| www.osnews.com' + * and returns an array ['www.google.com/search?q=bla', 'www.osnews.com'] + */ +function stringToURLs(str) +{ + var urls = str.split(/\s*\|\s*/); + begin: for(var url=0; url < urls.length; url++) + { + for(var i=0; i < g_searchengines.length; i++) + { + var regex = new RegExp("^" + g_searchengines[i][0] + "\\s+" + "(.+)"); + matches = urls[url].match(regex); + if(matches != null) + { + urls[url] = g_searchengines[i][1].replace(/%s/, matches[1]); + break begin; + } + } + + /* if the string contains a space or does not conatain any of: .:/ + * open it with default searchengine */ + if (urls[url].match(/\s+/) || urls[url].match(/\.|:|\//) == null) + urls[url] = g_searchengines[0][1].replace(/%s/, urls[url]); + } + return urls; +} + +//////////////////////////////////////////////////////////////////////// +// frame related functions //////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// + +// function stolen from Conkeror +function focusNextFrame() +{ + try { + var frames = window.content.frames; + if (frames.length == 0) + { + echo("No frames found"); + beep(); + return; + } + + var w = document.commandDispatcher.focusedWindow; + var next = 0; + + // Find the next frame to focus + for (var i=0; i= frames.length) + next = 0; + frames[next].focus(); + var oldbg = frames[next].document.bgColor; + var oldstyle = frames[next].document.body.getAttribute("style"); + frames[next].document.bgColor = "red"; + frames[next].document.body.setAttribute("style", "background-color: #FF0000;"); + + setTimeout(function(doc, bgcolor, style) { + doc.bgColor = bgcolor; + if (oldstyle == null) + doc.body.removeAttribute("style"); + else + doc.body.setAttribute("style", style); + }, 150, frames[next].document, oldbg, oldstyle); + + } catch(e) {alert(e);} +} + + + +//////////////////////////////////////////////////////////////////////// +// location handling ////////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +function getCurrentLocation() +{ + return content.document.location.href; +} + +function yankCurrentLocation() +{ + var loc = getCurrentLocation(); + copyToClipboard(loc); + echo("Yanked " + loc); +} + +// return null, if no link with a href focused +function getCurrentLinkLocation() +{ + var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] + .getService(Components.interfaces.nsIWindowWatcher); + if (window == ww.activeWindow && document.commandDispatcher.focusedElement) + { + var url = document.commandDispatcher.focusedElement.href; + if (url) + return url; + } + return null; +} + +//////////////////////////////////////////////////////////////////////// +// high level bookmark/history related functions ////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +// takes: -t "foo" -T "tag1,tag2", myurl +// converts that string to a useful url and title, and calls addBookmark +function bmadd(str) +{ + var res = new Object(); + if (parseBookmarkString(str, res)) + { + if(res.url == null) + res.url = getCurrentLocation(); + if(res.title == null) // XXX: maybe use current title of webpage + res.title = res.url; + + addBookmark(res.title, res.url); + echo("Bookmark `" + res.url + "' added"); + } + else + echo("Usage: :bmadd [-t \"My Title\"] [-T tag1,tag2] "); +} + +function bmdel(str) +{ + var res = new Object(); + if (parseBookmarkString(str, res)) + { + if(res.url == null) + res.url = getCurrentLocation(); + + var del = deleteBookmark(res.url); + echo(del + " bookmark(s) with url `" + res.url + "' deleted"); + } + else + echo("Usage: :bmdel "); +} + +function bmshow(filter, fullmode) +{ + if (fullmode) + openURLsInNewTab("chrome://browser/content/bookmarks/bookmarksPanel.xul", true); + else + { + items = get_bookmark_completions(filter); + preview_window_fill(items); + preview_window.hidden = false; + } +} +function hsshow(filter, fullmode) +{ + if (fullmode) + openURLsInNewTab("chrome://browser/content/history/history-panel.xul", true); + else + { + items = get_history_completions(filter); + preview_window_fill(items); + preview_window.hidden = false; + } +} + +//////////////////////////////////////////////////////////////////////// +// url marks functions //////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +/* vimperator has a concept of URL marks + * these provide quick access to URLs with a single character + * + * mark urls with e.g. Ma and you can go there with 'a or open a + * new tab with the url with "a + * valid characters for url marks are [a-zA-Z0-9] + */ +function set_url_mark(mark, url) +{ + +} + +function get_url_mark(mark) +{ + +} + +function del_url_mark(mark) +{ + +} + +function show_url_marks(mark) +{ + +} + +//////////////////////////////////////////////////////////////////////// +// location marks functions /////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +/* vimperator has a concept of location marks + * these provide quick access to a location within a webpage + */ +function set_location_mark(mark) +{ + +} + +function show_location_marks(mark) +{ + +} + +//////////////////////////////////////////////////////////////////////// +// tab related functions ////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +/* if index = 0, advance on tab + * if index < 0, go one tab to the left + * otherwise, jump directly to tab + */ +function tab_go(index) +{ + if (index < 0) + getBrowser().mTabContainer.advanceSelectedTab(-1, true); + else if (index == 0) + getBrowser().mTabContainer.advanceSelectedTab(1, true); + else + { + if (getBrowser().mTabContainer.childNodes.length < index) + beep(); + else + getBrowser().mTabContainer.selectedIndex = index-1; + } + + updateStatusbar(); +} + +/* quit_on_last_tab = 1: quit without saving session + quit_on_last_tab = 2: quit and save session + */ +function tab_remove(count, focus_left_tab, quit_on_last_tab) +{ + if (count < 1) count = 1; + + if (quit_on_last_tab >= 1 && getBrowser().mTabContainer.childNodes.length <= count) + quit(quit_on_last_tab == 2); + + var tab = getBrowser().mCurrentTab; + if(focus_left_tab && tab.previousSibling) + gBrowser.mTabContainer.selectedIndex--; + getBrowser().removeTab(tab); +} + +//////////////////////////////////////////////////////////////////////// +// scrolling ////////////////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +function scrollBufferRelative(right, down) +{ + var win = document.commandDispatcher.focusedWindow; + if (g_count < 1) + g_count = 1; + + // beep if we can't go there + if (down > 0) + { + if (win.scrollY == win.scrollMaxY) beep(); + } + else if (down < 0) + { + if (win.scrollY == 0) beep(); + } + + if (right > 0) + { + if (win.scrollX == win.scrollMaxX) beep(); + } + else if (right < 0) + { + if (win.scrollX == 0) beep(); + } + + win.scrollBy(g_count * right * 20, g_count * down * 20); +} + +/* both values are given in percent, -1 means no change */ +function scrollBufferAbsolute(horizontal, vertical) +{ + var win = document.commandDispatcher.focusedWindow; + var horiz, vert; + + if (horizontal < 0) + horiz = win.scrollX; + else + horiz = win.scrollMaxX/100 * horizontal; + + if (vertical < 0) + vert = win.scrollY; + else + vert = win.scrollMaxY/100 * vertical; + + win.scrollTo(horiz, vert); +} + +//////////////////////////////////////////////////////////////////////// +// zooming //////////////////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// + +/* also used to zoom out, when factor is negative */ +function zoom_in(factor) +{ + if (g_count < 1) + g_count = 1; + + //ZoomManager.prototype.getInstance().enlarge(); + var zoomMgr = ZoomManager.prototype.getInstance(); + if (zoomMgr.textZoom == 25 && factor < 0) + { + echoerr("Minimum zoom level of 25% reached"); + beep(); + } + else if (zoomMgr.textZoom == 500 && factor > 0) + { + echoerr("Maximum zoom level of 500% reached"); + beep(); + } + else + { + var value = zoomMgr.textZoom + factor*g_count*25; + if (value < 25) value = 25; + if (value > 500) value = 500; + + zoomMgr.textZoom = value; + echo("Zoom value: " + value + "%"); + } +} + +function zoom_to(value) +{ + var zoomMgr = ZoomManager.prototype.getInstance(); + if (!value || value <= 0) + value = 100; + + // convert to int, if string was given + if (typeof(value) != "number") + { + oldval = value; + value = parseInt(oldval, 10); + if (isNaN(value)) + { + echoerr("Cannot convert " + oldval + " to a number"); + return; + } + } + + if (value < 25 || value > 500) + { + echoerr("Zoom value must be between 25% and 500%"); + beep(); + return; + } + + zoomMgr.textZoom = value; + echo("Zoom value: " + value + "%"); +} + + +//////////////////////////////////////////////////////////////////////// +// misc helper funcstions ///////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// + +function copyToClipboard(str) +{ + var clipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"] + .getService(Components.interfaces.nsIClipboardHelper); + clipboardHelper.copyString(str); +} + +function beep() +{ + if (get_pref("beep") == false) + return; + + var gBeepService = Components.classes['@mozilla.org/sound;1'] + .getService(Components.interfaces.nsISound); + + if (gBeepService) + gBeepService.beep(); + else + echoerr('no beep service found'); +} + +// quit vimperator, no matter how many tabs/windows are open +function quit(save_session) +{ + if (save_history) + set_firefox_pref("browser.sessionstore.resume_session_once", true); + + goQuitApplication(); +} + +function reload(all_tabs) +{ + if (all_tabs) + getBrowser().reloadAllTabs(); + else + BrowserReload(); +} + +// sets an vimperator option +function set(args, special) +{ + if (args == "") + { + if (special) // open firefox settings gui dialog + openURLsInNewTab("chrome://browser/content/preferences/preferences.xul", true); + else + openURLsInNewTab("about:config", true); + } + else + { + var matches = args.match(/^\s*(no)?([a-z]+)(\?)?(=(.*))?/); + if (!matches) + { + echoerr("E518: Unknown option: " + args); + return; + } + + var no = true; if (matches[1] == undefined) no = false; + var opt = matches[2]; + var get = false; if (matches[3] != undefined) get = true; + var val = matches[5]; if (val == undefined) val = ""; + + var setting = get_setting(opt); + if (!setting) + { + echoerr("E518: Unknown option: " + opt); + return; + } + + // read access + if (get) + { + var cur_val = setting[4].call(this); + echo(" " + setting[0][0] + "=" + cur_val); + } + // write access + else + { + var type = setting[5]; + if (type == "boolean") + { + setting[3].call(this, !no); + } + else if (type == "number") + { + var num = parseInt(val, 10); + if (isNaN(num)) + echoerr("Invalid argument type to option " + setting[0][0] + ": Expects number"); + else + { + if (setting[7] != null && setting[7].call(this, num) == false) + echoerr("Invalid argument to option " + setting[0][0] + ": Check help for more details"); + else // all checks passed, execute option handler + setting[3].call(this, num); + } + } + else if (type == "charlist" || type == "stringlist" || type == "string") + { + if (setting[7] != null && setting[7].call(this, num) == false) + echoerr("Invalid argument to option " + setting[0][0] + ": Check help for more details"); + else // all checks passed, execute option handler + setting[3].call(this, val); + } + else + echoerr("Internal error, option format `" + type + "' not supported"); + } + } +} + +function source(filename) +{ + echoerr("Soucing not yet implemented"); +} + + +function help(section) +{ + var doc = window.content.document; + + var style = ""; + + + var header = '

Vimperator

' + + '

First there was a Navigator, then there was an Explorer. Later it was time for a Konqueror. Now it\'s time for an Imperator, the VIMperator :)

' + + var introduction = '

Introduction

' + + '

Vimperator is a free browser add-on for Firefox, which makes it look and behave like the Vim text editor. ' + + 'It has similar key bindings, and you could call it a modal webbrowser, as key bindings differ according to which mode you are in.

' + + + '

Warning: To provide the most authentic Vim experience, the Firefox menubar and toolbar were hidden. If you really need them, type: :set guioptions=mT to get it back. ' + + 'If you don\'t like Vimperator at all, you can uninstall it by typing :addons and remove/disable it. ' + + 'If you like it, but can\'t remember the shortcuts, press F1 or :help to get this help window back.

' + + + '

Since Vimperator\'s GUI is embedded into a toolbar, it may look too 3D-like with the default theme. For best experience, I therefore recommend the Whitehart theme.

' + + + '

Vimperator was written by Martin Stubenschrott. If you appreciate my work on Vimperator, you can either send me greetings, patches ' + + 'or make a donation: ' + + + '

' + + '' + + '' + + '' + + '' + + '
' + + + 'Of course as a believer in free open source software, only make a donation if you really like Vimperator, and the money doesn\'t hurt - otherwise just use it, recommend it and like it :)' + +// xxx: for firebug: :exec Firebug.toggleBar(true) + + /* commands = array where help information is located + * color = used for background of the table + * beg = string which is printed before the commmand/setting/mapping name + * end = string which is printed after the commmand/setting/mapping name + * func = called with 'commands' array and result which is a sring is prepended to the help text + */ + function makeHelpString(commands, color, beg, end, func) + { + var ret = ""; + for (var i=0; i < commands.length; i++) + { + ret += ''; + var command = ""; + for (var j=0; j < commands[i][0].length; j++) + { + var cmd_name = commands[i][0][j]; + cmd_name = cmd_name.replace(//g, ">"); + if (j==0) // color the first item differently + command += "" +beg+ cmd_name +end+ '
'; + else + command += "" +beg+ cmd_name +end+ '
'; + } + ret += command + ''; + if (commands[i][1]) + ret += '' + commands[i][1] + '
'; // the short description + if (func) + ret += func.call(this, commands[i]); + if (commands[i][2]) + { + if(func) + ret += "
" + ret += commands[i][2]; // the help description + } + ret += ''; + } + return ret; + } + function makeSettingsHelpString(command) + { + var ret = ""; + ret = command[5] + " (default: "; + if (command[5] == "boolean") + { + if(command[6] == true) + ret += "on"; + else + ret += "off"; + } + else + ret += command[6]; + + ret += ")"; + return ret; + } + + var mappings = '

Mappings

' + mappings += makeHelpString(g_mappings, "#102663", "", "", null); + mappings += '
'; + + var commands = '

Commands

' + commands += makeHelpString(g_commands, "#632610", ":", "", null); + commands += '
'; + + var settings = '

Settings

' + settings += makeHelpString(g_settings, "#106326", "'", "'", makeSettingsHelpString); + settings += '
'; + + var fulldoc = 'Vimperator help' + + style + + '' + + header + + introduction + + mappings + + commands + + settings + + '' + + doc.open(); + doc.write(fulldoc); + doc.close(); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// list all installed themes and extensions +function outputAddonsList(aTarget) +{ + var RDFService = Components.classes["@mozilla.org/rdf/rdf-service;1"] + .getService(Components.interfaces.nsIRDFService); + var Container = Components.classes["@mozilla.org/rdf/container;1"] + .getService(Components.interfaces.nsIRDFContainer); + var stream = Components.classes['@mozilla.org/network/file-output-stream;1'] + .createInstance(Components.interfaces.nsIFileOutputStream); + var fp = Components.classes["@mozilla.org/filepicker;1"] + .createInstance(Components.interfaces.nsIFilePicker); + + fp.init(window, aTarget+'s List', fp.modeSave); + fp.defaultString=aTarget+"sList.txt"; + fp.appendFilters(fp.filterText); + fp.appendFilters(fp.filterAll); + if (fp.show() == fp.returnCancel) + return; + + var extensionDS= Components.classes["@mozilla.org/extensions/manager;1"] + .getService(Components.interfaces.nsIExtensionManager).datasource; + var root = RDFService + .GetResource("urn:mozilla:"+aTarget.toLowerCase()+":root"); + var nameArc = RDFService + .GetResource("http://www.mozilla.org/2004/em-rdf#name"); + var versionArc = RDFService + .GetResource("http://www.mozilla.org/2004/em-rdf#version"); + var disabledArc = RDFService + .GetResource("http://www.mozilla.org/2004/em-rdf#disabled"); + + var list=""; + var disabledlist=""; + + Container.Init(extensionDS,root); + var elements=Container.GetElements(); + + while(elements.hasMoreElements()) + { + var element=elements.getNext(); + var name=""; + var version=""; + var disabled=""; + element.QueryInterface(Components.interfaces.nsIRDFResource); + var target=extensionDS.GetTarget(element, nameArc ,true); + if(target) + name=target + .QueryInterface(Components.interfaces.nsIRDFLiteral).Value; + target=extensionDS.GetTarget(element, versionArc ,true); + if(target) + version=target + .QueryInterface(Components.interfaces.nsIRDFLiteral).Value; + target=extensionDS.GetTarget(element, disabledArc ,true); + if(target) + disabled=target + .QueryInterface(Components.interfaces.nsIRDFLiteral).Value; + if( disabled && disabled=="true") + disabledlist += name + " " + version +"\n"; + else if(name) + list += name + " " + version +"\n" + } + + if(disabledlist) + list += "\n#Disabled Extensions\n" + disabledlist; + + stream.init(fp.file, 0x20|0x02|0x08, 0666, 0); + stream.write(list, list.length); + stream.close(); +} + +/* selects the first input box */ +function selectInput() +{ +// if (! (ev.charCode == 47 /* ord('/') */ && ev.ctrlKey)) +// return; + + var texts = document.evaluate("//input[@type='text']", document, + null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); + + texts.snapshotItem(0).focus(); +} + + +function toggle_images() { + if (!gPrefService) { + message("EEP: no gPrefService"); + return 0; + } + + + var pref; + if (!gPrefService.prefHasUserValue("network.image.imageBehavior")) { + pref = 0; + } else { + pref = gPrefService.getIntPref("network.image.imageBehavior"); + } + + + set_pref("network.image.imageBehavior", pref ? 0 : 2); + pref = gPrefService.getIntPref("network.image.imageBehavior"); +// redraw(); + message ("imageBehavior set to " + pref); +} + + +// function keycodeToName(keyCode) { +// for (keyName in KeyboardEvent.prototype) { +// if (keyName.substr(0,7) == 'DOM_VK_' && KeyboardEvent.prototype[keyName] == keyCode) { +// // turn 'DOM_VK_KEY_NAME' into 'Key Name' +// parts = keyName.substr(7).split('_'); // strip off DOM_VK_ and split into words +// for (var i = 0; i < parts.length; i++) +// parts[i] = parts[i][0] + parts[i].substr(1).toLowerCase(); +// return parts.join(' '); +// } +// } +// return 0; +// } + +// vim: set fdm=marker : diff --git a/chrome/content/vimperator/completion.js b/chrome/content/vimperator/completion.js new file mode 100644 index 00000000..8be02cdf --- /dev/null +++ b/chrome/content/vimperator/completion.js @@ -0,0 +1,486 @@ +/* + * COMPLETION HANDLING + * + * also responsible for command history, url history and bookmark handling + */ + +// array of all our bookmarks +var g_bookmarks = []; +var bookmarks_loaded = false; + +// array of all our history items +var g_history = []; +var history_loaded = false; + +// variables for the tab completion and command history: +// -1: filled, but no selection made +// >= 0: index of current item in the g_completions array +const COMPLETION_UNINITIALIZED = -2; // if we need to build the completion array first +const COMPLETION_MAXITEMS = 10; +const COMPLETION_CONTEXTLINES = 3; +const COMMAND_LINE_HISTORY_SIZE = 500; + +// 2 dimensional: 1st element: what to complete +// 2nd element: help description +var g_completions = new Array(); + +var comp_tab_index = COMPLETION_UNINITIALIZED; // the index in the internal g_completions array +var comp_tab_list_offset = 0; // how many items is the displayed list shifted from the internal tab index +var comp_tab_startstring = ""; +var comp_history = new Array(); +var comp_history_index = -1; + + + +/* uses the entries in g_completions to fill the listbox + * starts at index 'startindex', and shows COMPLETION_MAXITEMS + * returns the number of items */ +function completion_fill_list(startindex)/*{{{*/ +{ + // remove all old items first + var items = completion_list.getElementsByTagName("listitem"); + while (items.length > 0) { completion_list.removeChild(items[0]);} + + // find start index + //var i = index - 3; // 3 lines of context + if (startindex + COMPLETION_MAXITEMS > g_completions.length) + startindex = g_completions.length - COMPLETION_MAXITEMS; + if (startindex < 0) + startindex = 0; + + for(i=startindex; i 0) // XXX: respect completetopt setting +// completion_list.hidden = false; +// else +// completion_list.hidden = true; +// completion_list.setAttribute("rows", (i-startindex).toString()); + + return (i-startindex); +}/*}}}*/ + +function completion_show_list()/*{{{*/ +{ + var items = g_completions.length; + if (items > COMPLETION_MAXITEMS) + items = COMPLETION_MAXITEMS; + if (items > 1) // FIXME + { + //alert(completion_list.getRowCount().toString()); + completion_list.setAttribute("rows", items.toString()); + //completion_list.rowCountChanged(0, items); + completion_list.hidden = false; + //completion_list.selectedIndex = selected_index; + } + else + completion_list.hidden = true; +}/*}}}*/ + +/* add a single completion item to the list */ +function completion_add_to_list(completion_item, at_beginning)/*{{{*/ +{ + var item = document.createElement("listitem"); + var cell1 = document.createElement("listcell"); + var cell2 = document.createElement("listcell"); + + cell1.setAttribute("label", completion_item[0]); + cell1.setAttribute("width", "200"); + cell2.setAttribute("label", completion_item[1]); + cell2.setAttribute("style", "color:green; font-family: sans"); + + item.appendChild(cell1); + item.appendChild(cell2); + if (at_beginning == true) + { + var items = completion_list.getElementsByTagName("listitem"); + if (items.length > 0) + completion_list.insertBefore(item, items[0]); + else + completion_list.appendChild(item); + } + else + completion_list.appendChild(item); +}/*}}}*/ + +/* select the next index, refill list if necessary + * + * changes 'comp_tab_index' */ +function completion_select_next_item()/*{{{*/ +{ + comp_tab_index++; + if (comp_tab_index >= g_completions.length) /* wrap around */ + { + comp_tab_index = -1; + completion_list.selectedIndex = -1; + return; + } + + if (comp_tab_index == 0) // at the top of the list + { + completion_fill_list(0); + comp_tab_list_offset = 0; + } + + var listindex = comp_tab_index - comp_tab_list_offset; + // only move the list, if there are still items we can move + if (listindex >= COMPLETION_MAXITEMS - COMPLETION_CONTEXTLINES && + comp_tab_list_offset < g_completions.length - COMPLETION_MAXITEMS) + { + // for speed reason: just remove old item, and add new at the end of the list + var items = completion_list.getElementsByTagName("listitem"); + completion_list.removeChild(items[0]); + completion_add_to_list(g_completions[comp_tab_index + COMPLETION_CONTEXTLINES], false); + comp_tab_list_offset++; + } + + listindex = comp_tab_index - comp_tab_list_offset; + completion_list.selectedIndex = listindex; + + showStatusbarMessage(" match " + (comp_tab_index+1).toString() + " of " + g_completions.length.toString() + " ", STATUSFIELD_PROGRESS); +}/*}}}*/ + +/* select the previous index, refill list if necessary + * + * changes 'comp_tab_index' */ +function completion_select_previous_item()/*{{{*/ +{ + comp_tab_index--; + if (comp_tab_index == -1) + { + completion_list.selectedIndex = -1; + return; + } + + if (comp_tab_index < -1) // go to the end of the list + { + comp_tab_index = g_completions.length -1; + completion_fill_list(g_completions.length - COMPLETION_MAXITEMS); + comp_tab_list_offset = g_completions.length - COMPLETION_MAXITEMS;//COMPLETION_MAXITEMS - 1; + if (comp_tab_list_offset < 0) + comp_tab_list_offset = 0; + } + + var listindex = comp_tab_index - comp_tab_list_offset; + // only move the list, if there are still items we can move + if (listindex < COMPLETION_CONTEXTLINES && comp_tab_list_offset > 0) + { + // for speed reason: just remove old item, and add new at the end of the list + var items = completion_list.getElementsByTagName("listitem"); + completion_list.removeChild(items[items.length-1]); + completion_add_to_list(g_completions[comp_tab_index - COMPLETION_CONTEXTLINES], true); + comp_tab_list_offset--; + } + + listindex = comp_tab_index - comp_tab_list_offset; + completion_list.selectedIndex = listindex; + + showStatusbarMessage("item " + (comp_tab_index+1).toString() + " of " + g_completions.length.toString(), STATUSFIELD_PROGRESS); +}/*}}}*/ + + + +/* + * filter a list of urls + * + * may consist of searchengines, boorkmarks and history, + * depending on the 'complete' option + */ +function get_url_completions(filter)/*{{{*/ +{ + g_completions = []; + + var cpt = get_pref("complete"); + // join all completion arrays together + for (var i = 0; i < cpt.length; i++) + { + if (cpt[i] == 's') + g_completions = g_completions.concat(get_search_completions(filter)); + else if (cpt[i] == 'b') + g_completions = g_completions.concat(get_bookmark_completions(filter)); + else if (cpt[i] == 'h') + g_completions = g_completions.concat(get_history_completions(filter)); + } + + return g_completions; +}/*}}}*/ + +/* discard all entries in the 'urls' array, which don't match 'filter */ +function filter_url_array(urls, filter, use_regex)/*{{{*/ +{ + var filtered = []; + // completions which don't match the url but just the description + // list them add the end of the array + var additional_completions = []; + + var reg, reg2; + if (use_regex) + { + reg = new RegExp("^" + filter, "i"); + reg2 = new RegExp(filter, "i"); + } + +outer: + for (var i = 0; i < urls.length; ++i) + { + if (filter != null && filter.length > 0) + { + // first check if the filter matches in the main part of the URL + var bm = urls[i][0].replace(/^.*:\/\/(www)?/, ""); + bm = bm.replace(/\/.*$/, ""); + var tokens = bm.split("."); + for (var k = 0; k < tokens.length-1; k++) // -1 because we don't search the domain (like .com) + { + if (use_regex) + { + if(tokens[k].search(reg) != -1) + { + filtered.push([urls[i][0], urls[i][1] ]); + continue outer; + } + } + else + { + if (tokens[k].indexOf(filter) == 0) + { + filtered.push([urls[i][0], urls[i][1] ]); + continue outer; + } + } + } + + // otherwise check if the filter matches in the description of the bookmark + if (use_regex) + { + if(urls[i][0].search(reg2) == -1 && urls[i][1].search(reg2) == -1) + continue; + } + else + { + if (urls[i][0].indexOf(filter) == -1 && urls[i][1].indexOf(filter) == -1) + continue; + } + } + additional_completions.push([urls[i][0], urls[i][1] ]); + } + return filtered.concat(additional_completions); +}/*}}}*/ + +function get_search_completions(filter)/*{{{*/ +{ + var search_completions = []; + + var reg = new RegExp("^" + filter, "i"); + for (var i = 0; i < g_searchengines.length; i++) + { + if (g_searchengines[i][0].search(reg) != -1) + search_completions.push([g_searchengines[i][0] + " ", g_searchengines[i][1] ]); + } + + return search_completions; +}/*}}}*/ + +function get_bookmark_completions(filter)/*{{{*/ +{ + if (!bookmarks_loaded) + { + // update our bookmark cache + var RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService( Components.interfaces.nsIRDFService ); + var root = RDF.GetResource( "NC:BookmarksRoot" ); + bookmarks = []; // here getAllChildren will store the bookmarks + g_bookmarks = []; // also clear our bookmark cache + BookmarksUtils.getAllChildren(root, bookmarks); + for(var i = 0; i < bookmarks.length; i++) + { + if (bookmarks[i][0] && bookmarks[i][1]) + g_bookmarks.push([bookmarks[i][1].Value, bookmarks[i][0].Value ]); + } + bookmarks_loaded = true; + } + + return filter_url_array(g_bookmarks, filter, true); +}/*}}}*/ + +function get_history_completions(filter)/*{{{*/ +{ + var history = document.getElementById("hiddenHistoryTree"); + + // build our history cache + if (history_loaded == false) + { + if (history.hidden) + { + history.hidden = false; + var globalHistory = Components.classes["@mozilla.org/browser/global-history;2"].getService(Components.interfaces.nsIRDFDataSource); + history.database.AddDataSource(globalHistory); + history_completions = []; + } + + if (!history.ref) + history.ref = "NC:HistoryRoot"; + + const NC_NS = "http://home.netscape.com/NC-rdf#"; + if (!gRDF) + gRDF = Components.classes["@mozilla.org/rdf/rdf-service;1"] + .getService(Components.interfaces.nsIRDFService); + + var nameResource = gRDF.GetResource(NC_NS + "Name"); + var builder = history.builder.QueryInterface(Components.interfaces.nsIXULTreeBuilder); + + var count = history.view.rowCount; + for (var i = count-1; i >= 0; i--) + { + var res = builder.getResourceAtIndex(i); + var url = res.Value; + // var col = history.columns["Name"]; + //var title = history.view.getCellText(i, col); + var title; + var titleRes = history.database.GetTarget(res, nameResource, true); + if (!titleRes) + continue; + + var titleLiteral = titleRes.QueryInterface(Components.interfaces.nsIRDFLiteral); + if(titleLiteral) + title = titleLiteral.Value; + else + title = ""; + + g_history.push([url, title]); + } + // if(url.toLowerCase().indexOf(filter.toLowerCase()) > -1) + history_loaded = true; + } + + return filter_url_array(g_history, filter, true); +}/*}}}*/ + +function get_command_completions(filter)/*{{{*/ +{ + g_completions = []; + for(var i=0; i it DOES hurt :( + //g_completions.sort(); +}/*}}}*/ + +function get_settings_completions(filter)/*{{{*/ +{ + settings_completions = []; + var no_mode = false; + + if (filter.indexOf("no") == 0) // boolean option + { + no_mode = true; + filter = filter.substr(2); + } + // check if filter ends with =, then complete current value + else if(filter.length > 0 && filter.lastIndexOf("=") == filter.length -1) + { + filter = filter.substr(0, filter.length-1); + for(var i=0; i -1) + { + // for 'no'-mode only return bool options + if (no_mode) + { + if (g_settings[i][5] == "bool") + settings_completions.push(["no" + g_settings[i][0][0], g_settings[i][1]]); + } + else + settings_completions.push([g_settings[i][0][0], g_settings[i][1]]); + } + } + + return settings_completions; +}/*}}}*/ + +////////// COMMAND HISTORY HANDLING //////////// + +function add_to_command_history(str) +{ + /* add string to the command line history */ + if (str.length >= 2 && comp_history.push(str) > COMMAND_LINE_HISTORY_SIZE) + comp_history.shift(); +} + +function save_history() +{ + set_pref("comp_history", comp_history.join("\n")); +} + +function load_history() +{ + var hist = get_pref("comp_history", ""); + comp_history = hist.split("\n"); +} + + +///////// PREVIEW WINDOW ////////////////////// + +/* uses the entries in completions to fill the listbox */ +function preview_window_fill(completions) +{ + // remove all old items first + var items = preview_window.getElementsByTagName("listitem"); + while (items.length > 0) { preview_window.removeChild(items[0]);} + + for(i=0; i + + + + + + + + + + + + chrome://vimperator/content/vimperator.xul + + + + + diff --git a/chrome/content/vimperator/hints.js b/chrome/content/vimperator/hints.js new file mode 100644 index 00000000..39c9485b --- /dev/null +++ b/chrome/content/vimperator/hints.js @@ -0,0 +1,640 @@ +/** + * + * Mozilla Public License Notice + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * The Original Code and Idea is the Hit-a-Hint Mozilla extension. + * The Initial Developer of the Original Code and the Idea is Pekka + * P. Sillanpaa. Portions created by Initial Developer are Copyright + * (C) 2004. All Rights Reserved. + * + * Contributor(s): Pekka Sillanpaa, Paul Stone + * adapted for vimperator use by: Martin Stubenschrott + * + */ + +const HINT_MODE_QUICK = 0; +const HINT_MODE_ALWAYS = 1; +const HINT_MODE_EXTENDED = 2; + +function hit_a_hint() +{ + const HINT_PREFIX = 'hah_hint_'; // prefix for the hint id + + // public accessors + this.hintsVisible = function() { return isHahModeEnabled; }; + this.hintedElements = function() { return hintedElems; }; + this.currentState = function() { return state;}; + this.setCurrentState = function(s) { state = s;}; + this.currentMode = function() { return hintmode;}; + + var isHahModeEnabled = false; // is typing mode on + var hintmode = HINT_MODE_QUICK; + var hintedElems = []; + var linkNumString = ""; // the typed link number is in this string + var linkCount = 0; + var state = 0; // 0: empty or processing, 1: a full hint was parsed + + var wins; // frame array + + // each hint element is a clone of this element + var hintElemSpan; + + //////////////////////////////////////////////////////////////////////////////// + // configuration and initialization related functions + //////////////////////////////////////////////////////////////////////////////// + +// function load() +// { +// isHahModeEnabled = false; +// hintedElem = null; +// } + + //////////////////////////////////////////////////////////////////////////////// + // hint activating and loading related functions + //////////////////////////////////////////////////////////////////////////////// + + function startCoordLoader(doc) + { + win = doc.defaultView; + if (!win) + return; + + if (win.winId != null) + { + window.clearTimeout(win.coordLoaderId); + } + else + { + if (!wins) + wins = new Array(); + + win.winId = wins.length; + wins.push(win); + } + // logMessage("winId:"+win.winId); + win.res = doc.evaluate(get_pref("hinttags"), doc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); + win.coordLoaderId = window.setTimeout("hah.loadCoord(" + win.winId + ", 0);", 1); + } + + this.loadCoord = function(winId, i) + { + win = wins[winId]; + var elem = win.res.snapshotItem(i); + + if (elem) + genElemCoords(elem); + + i++; + + if (i < win.res.snapshotLength && !isHahModeEnabled) + window.setTimeout("hah.loadCoord(" + winId + ", "+ i +");", 1); + else + win.coordLoaderId = null; + }; + + function genElemCoords(elem) + { + if (typeof(elem.validCoord) != "undefined") + { + if (elem.validCoord == elem.ownerDocument.validCoords) + return; + } + + if (elem.offsetParent) + { + genElemCoords(elem.offsetParent); + elem.absoLeft = elem.offsetParent.absoLeft + elem.offsetLeft; + elem.absoTop = elem.offsetParent.absoTop + elem.offsetTop; + } + else + { + elem.absoLeft = elem.offsetLeft - 5; + elem.absoTop = elem.offsetTop; + } + elem.validCoord = elem.ownerDocument.validCoords; + } + + function createHints(win) + { + if (!win) + { + win = window._content; + linkCount = 0; + } + + var area = new Array(4); + area[0] = win.pageXOffset - 5; + area[1] = win.pageYOffset - 5; + area[2] = area[0] + win.innerWidth; + area[3] = area[1] + win.innerHeight; + + var doc = win.document; + var res = doc.evaluate(get_pref("hinttags"), doc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); + + var elem, i; + + hintElemSpan = doc.createElement('SPAN'); + hintElemSpan.style.cssText = get_pref("hintstyle"); + hintElemSpan.setAttribute('name', 'hah_hint'); + + var hintContainer = doc.getElementById('hah_hints'); + + if (hintContainer == null) + { + genHintContainer(doc); + hintContainer = doc.getElementById('hah_hints'); + } + hintContainer.valid_hint_count = 0; // none of these hints should be visible initially + + var hints = hintContainer.childNodes; + var maxhints = get_pref("maxhints"); + for (i = 0; i < res.snapshotLength; i++) + { + // this saves from script timeouts on pages with some thousand links + if (linkCount >= maxhints) + break; + + elem = res.snapshotItem(i); + genElemCoords(elem); + + // for extended hint mode, show all - even currently hidden - hints + if (hintmode == HINT_MODE_QUICK && (elem.absoTop < area[1] || elem.absoTop > area[3] || + elem.absoLeft > area[2] || elem.absoLeft < area[0])) + continue; + + // XXX: what does that do + // if (elem.offsetWidth == 0 && elem.offsetHeight == 0) + // continue; + + var cs = doc.defaultView.getComputedStyle(elem, null); + + if (cs.getPropertyValue("visibility") == "hidden") + continue; + + if (linkCount < hints.length) + hintElem = hints[linkCount]; + else // need to attach this new hintElem to the hint container + { + hintElem = hintElemSpan.cloneNode(false); + hintContainer.appendChild(hintElem); + } + + hintElem.style.display = 'none'; + hintElem.style.top = elem.absoTop + "px"; + hintElem.style.left = elem.absoLeft + "px"; + hintElem.refElem = elem; + + hintContainer.valid_hint_count++; // one more visible hint in this frame + linkCount++; // and one more total hint + } + + doc.coordsInvalidated = false; + + // recursively create hints + for (i = 0; i < win.frames.length; i++) + createHints(win.frames[i]); + + } + + function showHints(win, off) + { + offset = off; // must be global without 'var' for recursion + + if (!win) + win = window._content; + + if (linkCount == 0 && hintmode != HINT_MODE_ALWAYS) + { + beep(); + linkNumString = ''; + hintedElems = []; + isHahModeEnabled = false; + return; + } + + var doc = win.document; + var hintElem = null; + var hintContainer = doc.getElementById('hah_hints'); + var hints = hintContainer.childNodes; + var i, j; + + for (i = 0; i < hintContainer.valid_hint_count; i++) + { + hintText = formatHint(offset+i); + hintElem = hints[i]; + hintElem.style.display = 'inline'; + //hintElem.style.position = 'absolute'; + hintElem.innerHTML = hintText; + hintElem.id = HINT_PREFIX + hintText; + } + offset += hintContainer.valid_hint_count; + + // recursively show hints + for (j = 0; j < win.frames.length; j++) + showHints(win.frames[j], offset); + } + + /* removes all visible hints from doc + * or from current document, if doc == null + */ + function removeHints(win) + { + if (!win) + win = window._content; + + var doc = win.document; + var res = doc.evaluate("//HINTS/SPAN", doc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE , null); + var elem, i; + + for (i = 0; i < res.snapshotLength; i++) + { + elem = res.snapshotItem(i); + setHintStyle(elem, get_pref("hintstyle")); + elem.style.display = 'none'; + } + + for (i = 0; i < win.frames.length; i++) + removeHints(win.frames[i]); + } + + function onResize(event) + { + doc = event.originalTarget; + invalidateCoords(doc); + startCoordLoader(doc); + } + + function invalidateCoords(doc) + { + if (!doc.coordsInvalidated) + { + // every element has .validCoord + // if it is the same as doc:s .validCoords, + // the coordinates have not been regenerated, otherwise they + // have. This way we can also use recursive generation + // so that the coordinates are generated for every + // element just once + doc.validCoords = !doc.validCoords; + // this is because window can be resized many times + // and the coords should be invalidated just once. + doc.coordsInvalidated = true; + // logMessage(doc.validCoords); + } + } + + function getHintById(id, win) + { + if (!win) + win = window._content; + + var doc = win.document; + var elem, i; + + //var hintId = parseInt(id, nums.length); + //elem = doc.getElementById(prefix + hintId); + elem = doc.getElementById(HINT_PREFIX + id); + + if (elem) + { + return elem; + } + else + { + for (i = 0; i < win.frames.length; i++) + { + elem = getHintById(id, win.frames[i]); + if (elem) + return elem; + } + } + return null; + } + + function formatHint(hintNum) + { + var hintCharacters = get_pref("hintchars"); + var str = hintNum.toString(hintCharacters.length); // turn hintNum into a base(length) number + + // map the number onto the chars in the numbers string + var result = ''; + // make all links the same length + var hintLength = 1; + var tmp = linkCount; + while((tmp /= hintCharacters.length) > 1.0) + hintLength++; + while(str.length < hintLength) + { + result += hintCharacters.charAt(0).toUpperCase(); + hintLength--; + } + + for (var i = 0; i < str.length; i++) + result += (hintCharacters.charAt(parseInt(str[i], hintCharacters.length))).toUpperCase(); + + return result; + } + + function setHintStyle(hintElem, styleString) + { + if (hintElem && hintElem.style) + { + xTemp = hintElem.style.left; + yTemp = hintElem.style.top; + hintElem.style.cssText = styleString; + hintElem.style.left = xTemp; + hintElem.style.top = yTemp; + } + } + + function changeHintFocus(linkNumString, oldLinkNumString) + { + var styleString = get_pref("hintstyle"); + var styleStringFocus = get_pref("focusedhintstyle"); + var hintElem; + + if (oldLinkNumString.length > 0) + { + hintElem = getHintById(oldLinkNumString); + setHintStyle(hintElem, styleString); + } + if (linkNumString.length > 0) + { + hintElem = getHintById(linkNumString); + setHintStyle(hintElem, styleStringFocus); + if(hintElem) + setMouseOverElement(hintElem.refElem); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // basic functionality + //////////////////////////////////////////////////////////////////////////////// + + /** + * Enables the HaH-mode by showing the hints and prepare to input the + * hint numbers + * + * @author Pekka Sillanpaa + * @param event that caused the mode to change + * @return -1 if already enabled + */ + //function enableHahMode(event, mode) + this.enableHahMode = function(mode) + { +// if (isHahModeEnabled) +// return false; + + hintmode = mode; + state = 0; + linkCount = 0; + linkNumString = ''; + isHahModeEnabled = true; + + createHints(); + showHints(null, 0); + + return true; + }; + + /** + * Disables the HaH-mode by hiding the hints and disabling the input mode + * + * @author Pekka Sillanpaa + * @param event that caused the mode to change + * @param action = true if something is to be clicked + * false if cancel + * @return -1 if already disabled + */ + //function disableHahMode(event) + this.disableHahMode = function(win) + { + isHahModeEnabled = false; + hintmode = HINT_MODE_QUICK; + linkNumString = ''; + hintedElems = []; + + removeHints(win); + return 0; + }; + + this.resetHintedElements = function() + { + linkNumString = ''; + state = 0; + + while(hintedElems.length > 0) + { + var elem = hintedElems.pop(); + if (!elem) + return 0; + // reset style attribute + setHintStyle(elem, get_pref("hintstyle")); + } + }; + + + + // this function 'click' an element, which also works + // for javascript links + this.openHints = function(new_tab, new_window) + { + var x = 0, y = 0; + + while(hintedElems.length > 0) + { + var elem = hintedElems.pop(); + if (!elem) + return 0; + + setHintStyle(elem, get_pref("hintstyle")); + elem = elem.refElem; + var elemTagName = elem.tagName; + elem.focus(); + + if (elemTagName == 'FRAME' || elemTagName == 'IFRAME') + return 0; + + // for imagemap + if (elemTagName == 'AREA') + { + var coords = elem.getAttribute("coords").split(","); + x = Number(coords[0]); + y = Number(coords[1]); + } + doc = window._content.document; + view = window.document.defaultView; + + + var evt = doc.createEvent('MouseEvents'); + evt.initMouseEvent('mousedown', true, true, view, 1, x+1, y+1, 0, 0, /*ctrl*/ new_tab, /*event.altKey*/0, /*event.shiftKey*/ new_window, /*event.metaKey*/0, 0, null); + elem.dispatchEvent(evt); + + var evt = doc.createEvent('MouseEvents'); + evt.initMouseEvent('click', true, true, view, 1, x+1, y+1, 0, 0, /*ctrl*/ new_tab, /*event.altKey*/0, /*event.shiftKey*/ new_window, /*event.metaKey*/0, 0, null); + elem.dispatchEvent(evt); + + + // for 'pure' open calls without a new tab or window it doesn't + // make sense to open more hints in the current tab, open new tabs + // for it + if (!new_tab && !new_window) + new_tab = true; + } + + return 0; + }; + + this.yankHints = function() + { + var loc = ""; + var elems = hah.hintedElements(); + var tmp = ""; + for(i=0; i 0) + loc += tmp + "\n"; + } + + copyToClipboard(loc); + echo("Yanked " + loc); + }; + + function setMouseOverElement(elem) + { + var doc = window.document; + + if (elem.tagName == 'FRAME' || elem.tagName == 'IFRAME') + { + elem.contentWindow.focus(); + return; + } + else + { + //elem.focus(); + } + + var evt = doc.createEvent('MouseEvents'); + var x = 0; + var y = 0; + // for imagemap + if (elem.tagName == 'AREA') + { + var coords = elem.getAttribute("coords").split(","); + x = Number(coords[0]); + y = Number(coords[1]); + } + + evt.initMouseEvent('mouseover', true, true, doc.defaultView, 1, x, y, 0, 0, 0, 0, 0, 0, 0, null); + elem.dispatchEvent(evt); + } + + //////////////////////////////////////////////////////////////////////////////// + // event handlers + //////////////////////////////////////////////////////////////////////////////// + + // returns nr. of fully parsed links when a new hint has been found, + // otherwise 0 if current state is part of a hint, or -1 if an error occured + // (like we have typed keys which never can become a hint + this.processEvent = function(event) + { + if (!isHahModeEnabled) + return -1; + + // reset state to show that we are in processing mode + state = 0; + + var num = String.fromCharCode(event.charCode).toUpperCase(); + var hintCharacters = get_pref("hintchars"); + if (num != null && hintCharacters.toUpperCase().indexOf(num) > -1) + { + var oldLinkNumString = linkNumString; + linkNumString += '' + num; + // update reference to currently selected node; + var elem = getHintById(linkNumString); + changeHintFocus(linkNumString, oldLinkNumString); + + // if we found the hint, fine just return it + if (elem) + { + hintedElems.push(elem); + linkNumString = ''; + state = 1; + return hintedElems.length; + } + + //calculate how many characters a hint must have + var hintLength = 1; + var tmp = linkCount; + while((tmp /= hintCharacters.length) > 1.0) + hintLength++; + + if (linkNumString.length >= hintLength) + return -1; + else + return 0; + } + // an unparseable or wrong key + return -1; + } + + function genHintContainer(doc) + { + if (doc.getElementsByTagName('HINTS').length > 0) + return; + + hints = doc.createElement('HINTS'); + hints.id = "hah_hints"; + hints.valid_hint_count = 0; // initially 0 elements are usable as hints + doc.body.appendChild(hints); + } + + function initDoc(event) + { + doc = event.originalTarget; + genHintContainer(doc); + isHahModeEnabled = false; + hintedElems = []; + + if (!doc.validCoords) + doc.validCoords = true; + else + doc.validCoords = false; + + // XXX: prepend a ! ? + if (doc.coordsInvalidated) + doc.coordsInvalidated = true; + else + doc.coordsInvalidated = false; + + startCoordLoader(doc); + + if (hintmode == HINT_MODE_ALWAYS) + { + state = 0; + linkCount = 0; + linkNumString = ''; + isHahModeEnabled = true; + + createHints(); + showHints(null, 0); + } + //window.setTimeout("this.enableHahMode(HINT_MODE_ALWAYS);", 0); + } + + window.document.addEventListener("pageshow", initDoc, null); + window.addEventListener("resize", onResize, null); + logMessage("HAH initialized."); +} + +var hah = new hit_a_hint(); diff --git a/chrome/content/vimperator/settings.js b/chrome/content/vimperator/settings.js new file mode 100644 index 00000000..11af006b --- /dev/null +++ b/chrome/content/vimperator/settings.js @@ -0,0 +1,358 @@ +// settings.js +// +// handles all persistent storage of information +// to and from the firefox registry + +// the global handle to the root of the firefox settings +var g_firefox_prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); +var g_vimperator_prefs = null; + +// non persistent options +var opt_usermode = false; +var opt_fullscreen = false; + +/* all user-setable vimperator settings + * format: + * [ + * 0: [all names of this setting], + * 1: description, + * 2: help text, + * 3: set_function, + * 4: get_function, + * 5: type, + * 6: default, + * 7: checkfunc, + * 8: completefunc + * ] + */ +var g_settings = [/*{{{*/ + [ + ["activate"], + "Define when tabs are automatically activated", + "Not implemented yet", + function(value) { set_pref("activate", value); }, + function() { return get_pref("activate"); }, + "stringlist", + "quickmark,tabopen,paste", + null, + null + ], + [ + ["beep"], + "Emit a pc speaker beep on certain errors", + null, + function(value) { set_pref("beep", value); }, + function() { return get_pref("beep"); }, + "boolean", + true, + null, + null + ], + [ + ["complete", "cpt"], + "Order and items which are completed at the :[tab]open prompt", + "Available items:
"+ + "
  • s: Search machines
  • "+ + " b: Bookmarks
  • "+ + " h: History
"+ + "The order is important, so :set complete=bs would list bookmarks first, and then any available quick searches.", + function(value) { set_pref("complete", value); }, + function() { return get_pref("complete"); }, + "charlist", + "sbh", + null, + null + ], + [ + ["completeopt", "cot"], + "Define how command line completion works", + "Not implemented yet.", + function(value) { set_pref("completeopt", value); }, + function() { return get_pref("completeopt"); }, + "stringlist", + "menu", + null, + null + ], + [ + ["extendedhinttags", "eht"], + "XPath string of hintable elements activated by ';'", + null, + function(value) { set_pref("extendedhinttags", value); }, + function() { return get_pref("extendedhinttags"); }, + "string", + "//*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @class='s'] | //input[@type!='hidden'] | //a | //area | //iframe | //textarea | //button | //select", + null, + null + ], + [ + ["focusedhintstyle", "fhs"], + "CSS specification of focused hints appearance", + null, + function(value) { set_pref("focusedhintstyle", value); }, + function() { return get_pref("focusedhintstyle"); }, + "string", + "z-index:500;font:normal normal normal x-small monospace;color:ButtonText;background-color:ButtonShadow;border-color:ButtonShadow;border-width:1px;border-style:solid;padding:0px 1px 0px 1px;position:absolute;", + null, + null + ], + [ + ["fullscreen", "fs"], + "Shows the current window fullscreen", + null, + function(value) { opt_fullscreen = value; BrowserFullScreen(); }, + function() { return opt_fullscreen; }, + "boolean", + false, + null, + null + ], + [ + ["guioptions", "go"], + "Shows or hides the menu, toolbar and scrollbars", + "Supported characters:
  • m: menubar
  • T: toolbar
", + function(value) { set_pref("guioptions", value); set_guioptions(value); }, + function() { return get_pref("guioptions"); }, + "charlist", + "", + null, + null + ], + [ + ["hintchars", "hc"], + "String of single characters which can be used to follow hints", + null, + function(value) { set_pref("hintchars", value); }, + function() { return get_pref("hintchars"); }, + "charlist", + "hjklasdfgyuiopqwertnmzxcvb", + null, + null + ], + [ + ["hintstyle", "hs"], + "CSS specification of unfocused hints appearance", + null, + function(value) { set_pref("hintstyle", value); }, + function() { return get_pref("hintstyle"); }, + "string", + "z-index:500;font:normal normal normal x-small monospace;color:black;background-color:yellow;border-color:ButtonShadow;border-width:0px;border-style:solid;padding:0px 0px 0px 0px;position:absolute;", + null, + null + ], + [ + ["hinttags"], + "XPath string of hintable elements activated by 'f'", + null, + function(value) { set_pref("hinttags", value); }, + function() { return get_pref("hinttags"); }, + "string", + "//*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @class='s'] | //input[@type!='hidden'] | //a | //area | //iframe | //textarea | //button | //select", + null, + null + ], + [ + ["maxhints", "mh"], + "Maximum of simultanously shown hints", + "If you want to speed up display of hints, choose a smaller value", + function(value) { set_pref("maxhints", value); }, + function() { return get_pref("maxhints"); }, + "number", + 250, + function (value) { if (value>=1 && value <=1000) return true; else return false; }, + null + ], + [ + ["preload"], + "Speed up first time history/bookmark completion", + "History access can be quite slow for a large history. Vimperator maintains a cache to speed it up significantly on subsequent access.
"+ + "In order to also speed up first time access, it is cached at startup, if this option is set (recommended).", + function(value) { set_pref("preload", value); }, + function() { return get_pref("preload"); }, + "boolean", + true, + null, + null + ], + [ + ["showtabline", "stal"], + "Control when to show the tab bar of opened web pages", + "Available items:
"+ + "
  • 0: Never show tab bar
  • "+ + " 1: Show tab bar only if more than one tab is open
  • "+ + " 2: Always show tab bar
"+ + "Not implemented yet.", + function(value) { set_pref("showtabline", value); set_showtabline(value); }, + function() { return get_pref("showtabline"); }, + "number", + 2, + function (value) { if (value>=0 && value <=2) return true; else return false; }, + null + ], + [ + ["usermode", "um"], + "Show current website with a minimal stylesheet to make it easily accessible", + "Note that this is a local setting for now, later it may be split into a global and :setlocal part", + function(value) { opt_usermode = value; setStyleDisabled(value); }, + function() { return opt_usermode; }, + "boolean", + false, + null, + null + ] +]/*}}}*/ + +// return null, if the cmd cannot be found in our g_settings array, or +// otherwise a refernce to our command +function get_setting(cmd) +{ + for (var i=0; i < g_settings.length; i++) + { + for (var j=0; j < g_settings[i][0].length; j++) + { + if (g_settings[i][0][j] == cmd) + { + return g_settings[i]; + } + } + } + return null; +} + +///////////////////////////////////////////////// +// preference getter functions ///////////// {{{1 +///////////////////////////////////////////////// +function get_pref(name, forced_default) +{ + var pref = null; + var default_value = ""; + + // sometimes this var is not yet inititialized, make sure, it is + if (!g_vimperator_prefs) + g_vimperator_prefs = g_firefox_prefs.getBranch("extensions.vimperator."); + + if (forced_default) // this argument sets defaults for non-user settable options (like comp_history) + default_value = forced_default; + else + { + for (var i=0; i -1 ? false : true; + document.getElementById("toolbar-menubar").hidden = value.indexOf("m") > -1 ? false : true; + // and main toolbar + document.getElementById("nav-bar").collapsed = value.indexOf("T") > -1 ? false : true; + document.getElementById("nav-bar").hidden = value.indexOf("T") > -1 ? false : true; + // and bookmarks toolbar + document.getElementById("PersonalToolbar").collapsed = value.indexOf("b") > -1 ? false : true; + document.getElementById("PersonalToolbar").hidden = value.indexOf("b") > -1 ? false : true; + // and original status bar (default), but show it, e.g. when needed for extensions + document.getElementById("status-bar").collapsed = value.indexOf("S") > -1 ? false : true; + document.getElementById("status-bar").hidden = value.indexOf("S") > -1 ? false : true; +} + +function set_showtabline(value) +{ + // hide tabbar + if(value == 0) + { + gBrowser.mStrip.collapsed = true; + gBrowser.mStrip.hidden = true; + } + else if(value == 1) + echo("show tabline only with > 1 page open not impl. yet"); + else + { + gBrowser.mStrip.collapsed = false; + gBrowser.mStrip.hidden = false; + } +} + +// vim: set fdm=marker : diff --git a/chrome/content/vimperator/tags b/chrome/content/vimperator/tags new file mode 100644 index 00000000..8effa363 --- /dev/null +++ b/chrome/content/vimperator/tags @@ -0,0 +1,110 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 2 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.6 // +addBookmark bookmarks.js /^function addBookmark(title, uri)$/;" f +add_to_command_history completion.js /^function add_to_command_history(str)$/;" f +beep commands.js /^function beep()$/;" f +bmadd commands.js /^function bmadd(str)$/;" f +bmdel commands.js /^function bmdel(str)$/;" f +bmshow commands.js /^function bmshow(filter, fullmode)$/;" f +changeHintFocus hints.js /^ function changeHintFocus(linkNumString, oldLinkNumString)$/;" f +completion_add_to_list completion.js /^function completion_add_to_list(completion_item, at_beginning)\/*{{{*\/$/;" f +completion_fill_list completion.js /^function completion_fill_list(startindex)\/*{{{*\/$/;" f +completion_select_next_item completion.js /^function completion_select_next_item()\/*{{{*\/$/;" f +completion_select_previous_item completion.js /^function completion_select_previous_item()\/*{{{*\/$/;" f +completion_show_list completion.js /^function completion_show_list()\/*{{{*\/$/;" f +copyToClipboard commands.js /^function copyToClipboard(str)$/;" f +createCursorPositionString vimperator.js /^function createCursorPositionString()$/;" f +createHints hints.js /^ function createHints(win)$/;" f +createProgressBar vimperator.js /^function createProgressBar(aProgress)$/;" f +deleteBookmark bookmarks.js /^function deleteBookmark(url)$/;" f +del_url_mark commands.js /^function del_url_mark(mark)$/;" f +echo commands.js /^function echo(msg)$/;" f +echoerr commands.js /^function echoerr(msg)$/;" f +execute_command commands.js /^function execute_command(count, cmd, special, args) \/\/ {{{$/;" f +filter_url_array completion.js /^function filter_url_array(urls, filter, use_regex)\/*{{{*\/$/;" f +focusContent vimperator.js /^function focusContent(clear_command_line, clear_statusline)$/;" f +focusNextFrame commands.js /^function focusNextFrame()$/;" f +formatHint hints.js /^ function formatHint(hintNum)$/;" f +genElemCoords hints.js /^ function genElemCoords(elem)$/;" f +genHintContainer hints.js /^ function genHintContainer(doc)$/;" f +getCurrentLinkLocation commands.js /^function getCurrentLinkLocation()$/;" f +getCurrentLocation commands.js /^function getCurrentLocation()$/;" f +getHintById hints.js /^ function getHintById(id, win)$/;" f +getLinkNodes vimperator.js /^function getLinkNodes(doc)$/;" f +getPageLinkNodes vimperator.js /^function getPageLinkNodes()$/;" f +getProperty bookmarks.js /^function getProperty( aInput, aArc, DS )$/;" f +get_bookmark_completions completion.js /^function get_bookmark_completions(filter)\/*{{{*\/$/;" f +get_command commands.js /^function get_command(cmd) \/\/ {{{$/;" f +get_command_completions completion.js /^function get_command_completions(filter)\/*{{{*\/$/;" f +get_firefox_pref settings.js /^function get_firefox_pref(name, default_value)$/;" f +get_history_completions completion.js /^function get_history_completions(filter)\/*{{{*\/$/;" f +get_pref settings.js /^function get_pref(name, forced_default)$/;" f +get_search_completions completion.js /^function get_search_completions(filter)\/*{{{*\/$/;" f +get_setting settings.js /^function get_setting(cmd)$/;" f +get_settings_completions completion.js /^function get_settings_completions(filter)\/*{{{*\/$/;" f +get_url_completions completion.js /^function get_url_completions(filter)\/*{{{*\/$/;" f +get_url_mark commands.js /^function get_url_mark(mark)$/;" f +goUp commands.js /^function goUp() \/\/ FIXME$/;" f +help commands.js /^function help(section)$/;" f +hit_a_hint hints.js /^function hit_a_hint()$/;" f +hsshow commands.js /^function hsshow(filter, fullmode)$/;" f +init vimperator.js /^function init()$/;" f +initDoc hints.js /^ function initDoc(event)$/;" f +invalidateCoords hints.js /^ function invalidateCoords(doc)$/;" f +isFormElemFocused vimperator.js /^function isFormElemFocused()$/;" f +keyToString vimperator.js /^function keyToString(event)$/;" f +load_history completion.js /^function load_history()$/;" f +logMessage vimperator.js /^function logMessage(msg)$/;" f +makeHelpString commands.js /^ function makeHelpString(commands, color, beg, end, func)$/;" f +makeSettingsHelpString commands.js /^ function makeSettingsHelpString(command)$/;" f +nsBrowserStatusHandler vimperator.js /^function nsBrowserStatusHandler() \/*{{{*\/$/;" f +onBlur vimperator.js /^function onBlur() \/\/ FIXME: needed?$/;" f +onCommandBarInput vimperator.js /^function onCommandBarInput(event)$/;" f +onCommandBarKeypress vimperator.js /^function onCommandBarKeypress(evt)\/*{{{*\/$/;" f +onEscape vimperator.js /^function onEscape()$/;" f +onResize hints.js /^ function onResize(event)$/;" f +onVimperatorKeypress vimperator.js /^function onVimperatorKeypress(event)\/*{{{*\/$/;" f +openURLs commands.js /^function openURLs(str)$/;" f +openURLsInNewTab commands.js /^function openURLsInNewTab(str, activate)$/;" f +openVimperatorBar vimperator.js /^function openVimperatorBar(str)$/;" f +outputAddonsList commands.js /^function outputAddonsList(aTarget)$/;" f +parseBookmarkString bookmarks.js /^function parseBookmarkString(str, res)$/;" f +preview_window_fill completion.js /^function preview_window_fill(completions)$/;" f +preview_window_select completion.js /^function preview_window_select(event)$/;" f +quit commands.js /^function quit(save_session)$/;" f +reload commands.js /^function reload(all_tabs)$/;" f +removeHints hints.js /^ function removeHints(win)$/;" f +save_history completion.js /^function save_history()$/;" f +scrollBufferAbsolute commands.js /^function scrollBufferAbsolute(horizontal, vertical)$/;" f +scrollBufferRelative commands.js /^function scrollBufferRelative(right, down)$/;" f +selectInput commands.js /^function selectInput()$/;" f +set commands.js /^function set(args, special)$/;" f +setHintStyle hints.js /^ function setHintStyle(hintElem, styleString)$/;" f +setMouseOverElement hints.js /^ function setMouseOverElement(elem)$/;" f +setStatusbarColor vimperator.js /^function setStatusbarColor(color)$/;" f +set_firefox_pref settings.js /^function set_firefox_pref(name, value)$/;" f +set_guioptions settings.js /^function set_guioptions(value)$/;" f +set_location_mark commands.js /^function set_location_mark(mark)$/;" f +set_pref settings.js /^function set_pref(name, value)$/;" f +set_showtabline settings.js /^function set_showtabline(value)$/;" f +set_url_mark commands.js /^function set_url_mark(mark, url)$/;" f +showHints hints.js /^ function showHints(win, off)$/;" f +showStatusbarMessage vimperator.js /^function showStatusbarMessage(msg, field)$/;" f +show_location_marks commands.js /^function show_location_marks(mark)$/;" f +show_url_marks commands.js /^function show_url_marks(mark)$/;" f +source commands.js /^function source(filename)$/;" f +startCoordLoader hints.js /^ function startCoordLoader(doc)$/;" f +stepInHistory commands.js /^function stepInHistory(steps)$/;" f +stringToURLs commands.js /^function stringToURLs(str)$/;" f +tab_go commands.js /^function tab_go(index)$/;" f +tab_remove commands.js /^function tab_remove(count, focus_left_tab, quit_on_last_tab)$/;" f +toggle_images commands.js /^function toggle_images() {$/;" f +unload vimperator.js /^function unload()$/;" f +updateStatusbar vimperator.js /^function updateStatusbar()$/;" f +yankCurrentLocation commands.js /^function yankCurrentLocation()$/;" f +zoom_in commands.js /^function zoom_in(factor)$/;" f +zoom_to commands.js /^function zoom_to(value)$/;" f diff --git a/chrome/content/vimperator/vimperator.js b/chrome/content/vimperator/vimperator.js new file mode 100644 index 00000000..d01b51ff --- /dev/null +++ b/chrome/content/vimperator/vimperator.js @@ -0,0 +1,970 @@ +/***** BEGIN LICENSE BLOCK ***** {{{ +Version: MPL 1.1/GPL 2.0/LGPL 2.1 + +The contents of this file are subject to the Mozilla Public License Version +1.1 (the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +for the specific language governing rights and limitations under the +License. + +(c) 2006-2007: Martin Stubenschrott + +Alternatively, the contents of this file may be used under the terms of +either the GNU General Public License Version 2 or later (the "GPL"), or +the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +in which case the provisions of the GPL or the LGPL are applicable instead +of those above. If you wish to allow use of your version of this file only +under the terms of either the GPL or the LGPL, and not to allow others to +use your version of this file under the terms of the MPL, indicate your +decision by deleting the provisions above and replace them with the notice +and other provisions required by the GPL or the LGPL. If you do not delete +the provisions above, a recipient may use your version of this file under +the terms of any one of the MPL, the GPL or the LGPL. +}}} ***** END LICENSE BLOCK *****/ + +var Modes = { // XXX: not yet used + MODE_NORMAL: 1, + MODE_INSERT: 2, + MODE_HAH_MAGIC: 3 // when we are holding space for hah input +}; +var current_mode = Modes.MODE_NORMAL; +var popup_allowed_events; // need to change and reset this firefox pref + +var g_inputbuffer = ""; // here we store partial commands (e.g. 'g' if you want to type 'gg') +var g_count = -1; // the parsed integer of g_inputbuffer, or -1 if no count was given + +// handles to our gui elements +var preview_window = null; +var status_line = null; +var completion_list = null; +var command_line = null; + +// our status bar fields +const STATUSFIELD_URL = 1; +const STATUSFIELD_INPUTBUFFER = 2; +const STATUSFIELD_PROGRESS = 3; +const STATUSFIELD_BUFFERS = 4; +const STATUSFIELD_CURSOR_POSITION = 5; + + +/* this function reacts to status bar and url changes which are sent from + the mozilla core */ +function nsBrowserStatusHandler() /*{{{*/ +{ + this.init(); +} +nsBrowserStatusHandler.prototype = +{ + QueryInterface : function(aIID) + { + if (aIID.equals(Components.interfaces.nsIWebProgressListener) || + aIID.equals(Components.interfaces.nsISupportsWeakReference) || + aIID.equals(Components.interfaces.nsIXULBrowserWindow) || + aIID.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_NOINTERFACE; + }, + + init : function() + { + }, + + setOverLink : function(link, b) + { + echo(link); + }, + setJSStatus : function(status) + { + // echo("setJSStatus"); + // this.updateStatusField(status); + }, + setJSDefaultStatus : function(status) + { + // echo("setJSDefaultStatus"); + // this.updateStatusField(status); + }, + setDefaultStatus : function(status) + { + // echo("setDefaultStatus"); + // this.updateStatusField(status); + }, + + + onStateChange:function(aProgress,aRequest,aFlag,aStatus) + { + //alert("state change"); + const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; + const nsIChannel = Components.interfaces.nsIChannel; + if (aFlag & nsIWebProgressListener.STATE_START && aRequest && aRequest.URI) + { + var toLoadUrl = aRequest.URI.spec; + } + else if (aFlag & nsIWebProgressListener.STATE_STOP) + { + updateStatusbar(); + } + return 0; + }, + onLocationChange:function (aWebProgress, aRequest, aLocation) + { + UpdateBackForwardButtons(); + var url = aLocation.spec; + gURLBar.value = url; // also update the original firefox location bar + + // onLocationChange is also called when switching/deleting tabs + if (hah.currentMode() != HINT_MODE_ALWAYS) + hah.disableHahMode(); + + // updating history cache is not done here but in + // the 'pageshow' event handler, because at this point I don't + // have access to the url title + }, + onProgressChange:function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) + { + showStatusbarMessage(createProgressBar(aCurTotalProgress/aMaxTotalProgress), STATUSFIELD_PROGRESS); + return 0; + }, + onStatusChange:function (aWebProgress, aRequest, aStatus, aMessage) + { + showStatusbarMessage(aMessage, STATUSFIELD_URL); + return 0; + }, + onSecurityChange:function (aWebProgress, aRequest, aState) + { + const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; + if(aState & nsIWebProgressListener.STATE_IS_INSECURE) + setStatusbarColor("transparent"); + else if(aState & nsIWebProgressListener.STATE_IS_BROKEN) + setStatusbarColor("orange"); + else if(aState & nsIWebProgressListener.STATE_IS_SECURE) + setStatusbarColor("yellow"); + + return 0; + } + //onLinkIconAvailable:function(a){} + +};/*}}}*/ + +window.addEventListener("load", init, false); + +//////////////////////////////////////////////////////////////////////// +// init/uninit //////////////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +function init() +{ + preview_window = document.getElementById("vim-preview_window"); + status_line = document.getElementById("vim-statusbar"); + completion_list = document.getElementById("vim-completion"); + command_line = document.getElementById("vim-commandbar"); + if (!completion_list || !command_line) + alert("GUI not correctly created! Strange things will happen (until I find out, how to exit this script by code)"); + + // Setup our status handler - from browser.js + window.XULBrowserWindow = new nsBrowserStatusHandler(); + 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; + + + window.addEventListener("unload", unload, false); + window.addEventListener("keypress", onVimperatorKeypress, true); + // window.addEventListener("keyup", onVimperatorKeyup, true); + // window.addEventListener("keydown", onVimperatorKeydown, true); + + // this handler is for middle click only in the content + //window.addEventListener("mousedown", onVimperatorKeypress, true); + //content.mPanelContainer.addEventListener("mousedown", onVimperatorKeypress, true); + //document.getElementById("content").onclick = function(event) { alert("foo"); }; + + // these 4 events require >=firefox-2.0 beta1 + window.addEventListener("TabMove", updateStatusbar, false); + window.addEventListener("TabOpen", updateStatusbar, false); + window.addEventListener("TabClose", updateStatusbar, false); + //window.addEventListener("TabSelect", updateStatusbar, false); + window.addEventListener("TabSelect", function(event) + { + //alert("select"); + if (hah.currentMode == HINT_MODE_ALWAYS) + { + hah.disableHahMode(); + hah.enableHahMode(HINT_MODE_ALWAYS); + } + updateStatusbar(); + }, false); + + /*** load our preferences ***/ + load_history(); + + set_showtabline(get_pref("showtabline")); + set_guioptions(get_pref("guioptions")); + + // work around firefox popup blocker + popup_allowed_events = get_firefox_pref('dom.popup_allowed_events', 'change click dblclick mouseup reset submit'); + if (!popup_allowed_events.match("keypress")) + set_firefox_pref('dom.popup_allowed_events', popup_allowed_events + " keypress"); + + // we have our own typeahead find implementation + set_firefox_pref('accessibility.typeaheadfind.autostart', false); + set_firefox_pref('accessibility.typeaheadfind', false); // actually the above setting should do it, but has no effect in firefox + + // first time intro message + if (get_pref("firsttime", true)) + { + setTimeout(function() { + var tab = openURLsInNewTab("about:blank", true); + BrowserStop(); + help(); + set_pref("firsttime", false); + },1000); + } + + + // update our history cache when a new page is shown + // XXX: there should be a cleaner way with onload() handler, but it just + // does not work out well for me :( + window.document.addEventListener("pageshow", function(event) + { + if (!event.persisted) // only if not bypassing cache + { + var url = getCurrentLocation(); + var title = document.title; + for(var i=0; i go 10 lines down) + if (event.charCode >= 48 && event.charCode <= 57 && !(event.ctrlKey || event.altKey)) + { + if (g_inputbuffer.search(/[^0-9]/) != -1) + { + g_inputbuffer = ""; + beep(); + updateStatusbar(); + return true; + } + else + { + // handle '0' specially to allow binding of 0 + if (g_inputbuffer != "" || key != "0") + { + g_inputbuffer += key; + updateStatusbar(); + return true; + } + // else let the flow continue, and check if 0 is a mapping + } + } + + for (var i in g_mappings) + { + // each internal mapping can have multiple keys + for (var j in g_mappings[i][0]) + { + var mapping = g_mappings[i][0][j]; + // alert("key: " + key +" - mapping: "+ mapping + " - g_input: " + g_inputbuffer); + if(count_str + mapping == g_inputbuffer + key) + { + g_count = parseInt(count_str, 10); + if (isNaN(g_count)) + g_count = -1; + + // allow null (= no operation) mappings + if(g_mappings[i][3] != null) + g_mappings[i][3].call(this, g_count); + + // command executed, reset input buffer + g_inputbuffer = ""; + updateStatusbar(); + event.preventDefault(); + event.preventBubble(); + event.stopPropagation(); + return false; + } + else if ((count_str+mapping).indexOf(g_inputbuffer + key) == 0) + { + couldBecomeCompleteMapping = true; + } + } + } + + if (couldBecomeCompleteMapping) + { + g_inputbuffer += key; + event.preventDefault(); + event.preventBubble(); + event.stopPropagation(); + } + else + { + g_inputbuffer = ""; + beep(); + } + + updateStatusbar(); + return false; +}/*}}}*/ +function onCommandBarKeypress(evt)/*{{{*/ +{ + try + { + /* parse our command string into tokens */ + var command = command_line.value; + + var matches1 = command.match(/^:(\d+)/); + var matches2 = command.match(/^:(\d+)?([a-zA-Z!]+)/); + var matches3 = command.match(/^:(\d+)?([a-zA-Z!]+)\s*(.*?)\s*$/); + var count = 0; + var cmd = ""; + var args= ""; + var special = false; + + if (matches1 != null && matches1.length == 2) + count = parseInt(matches1[1], 10); + if (matches2 != null && matches2.length >= 3) + cmd = matches2[2]; + if (matches3 != null && matches3.length >= 4) + args = matches3[3]; + + /* if the user executes a command with ! at the end */ + if (cmd.length - 1 == cmd.lastIndexOf("!")) + { + special = true; + cmd = cmd.replace(/!$/, ""); + } + else + special = false; + + + /* user pressed ENTER to carry out a command */ + if (evt.keyCode == KeyEvent.DOM_VK_RETURN) + { + // unfocus command line first + focusContent(false, true); // also sets comp_tab_index to -1 + add_to_command_history(command); + + execute_command(count, cmd, special, args); + } + + else if (evt.keyCode == KeyEvent.DOM_VK_ESCAPE) + { + add_to_command_history(command); + focusContent(true, true); + } + + /* user pressed UP or DOWN arrow to cycle completion */ + else if (evt.keyCode == KeyEvent.DOM_VK_UP || evt.keyCode == KeyEvent.DOM_VK_DOWN) + { + /* save 'start' position for iterating through the history */ + if (comp_history_index == -1) + { + comp_history_index = comp_history.length; + comp_history_start = command_line.value; + } + + while (comp_history_index >= -1 && comp_history_index <= comp_history.length) + { + evt.keyCode == KeyEvent.DOM_VK_UP ? comp_history_index-- : comp_history_index++; + if (comp_history_index == comp_history.length) // user pressed DOWN when there is no newer history item + { + command_line.value = comp_history_start; + return; + } + + /* if we are at either end of the list, reset the counter, break the loop and beep */ + if((evt.keyCode == KeyEvent.DOM_VK_UP && comp_history_index <= -1) || + (evt.keyCode == KeyEvent.DOM_VK_DOWN && comp_history_index >= comp_history.length)) + { + evt.keyCode == KeyEvent.DOM_VK_UP ? comp_history_index++ : comp_history_index--; + break; + } + + if (comp_history[comp_history_index].indexOf(comp_history_start) == 0) + { + command_line.value = comp_history[comp_history_index]; + return; + } + + } + beep(); + } + + /* user pressed TAB to get completions of a command */ + else if (evt.keyCode == KeyEvent.DOM_VK_TAB) + { + //always reset our completion history so up/down keys will start with new values + comp_history_index = -1; + + // we need to build our completion list first + if (comp_tab_index == COMPLETION_UNINITIALIZED) + { + g_completions = []; + comp_tab_index = -1; + comp_tab_list_offset = 0; + comp_tab_startstring = command; + + /* if there is no space between the command name and the cursor + * then get completions of the command name + */ + if(command_line.value.substring(0, command_line.selectionStart).search(/[ \t]/) == -1) + { + get_command_completions(cmd); + } + else // dynamically get completions as specified in the g_commands array + { + var command = get_command(cmd); + if (command && command[4]) + { + g_completions = command[4].call(this, args); + } + } + } + + /* now we have the g_completions, so lets show them */ + if (comp_tab_index >= -1) + { + // we could also return when no completion is found + // but we fall through to the cleanup anyway + if (g_completions.length == 0) + beep(); + + // show the list + completion_show_list(); + + if (evt.shiftKey) + completion_select_previous_item(); + else + completion_select_next_item(); + //command_line.focus(); // workaraound only need for RICHlistbox + + + if (comp_tab_index == -1) // wrapped around matches, reset command line + { + command_line.value = comp_tab_startstring; + completion_list.selectedIndex = -1; + } + else + { + var compl = g_completions[comp_tab_index][0]; + if (compl) + { + /* if there is no space between the command name and the cursor + * the completions are for the command name + */ + if(command_line.value.substring(0, command_line.selectionStart).search(/[ \t]/) == -1) + { + command_line.value = ":" + (count ? count.toString() : "") + compl; + } + else // completions are for an argument + { + command_line.value = ":" + (count ? count.toString() : "") + + cmd + (special ? "!" : "") + " " + compl; + } + } + } + } + + // prevent tab from moving to the next field + evt.preventDefault(); + evt.preventBubble(); + evt.stopPropagation(); + + } + else if (evt.keyCode == KeyEvent.DOM_VK_BACK_SPACE) + { + if (command_line.value == ":") + { + evt.preventDefault(); + focusContent(true, true); + } + comp_tab_index = COMPLETION_UNINITIALIZED; + comp_history_index = -1; + } + else + { + // some key hit, check if the cursor is before the : + if (command_line.selectionStart == 0) + command_line.selectionStart = 1; + + // and reset the tab completion + comp_tab_index = COMPLETION_UNINITIALIZED; + comp_history_index = -1; + + } + } catch(e) { alert(e); } +}/*}}}*/ + +function onCommandBarInput(event) +{ + if (command_line.value == "") + command_line.value = ":"; +} + +//////////////////////////////////////////////////////////////////////// +// focus and mode handling //////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +/* After pressing Escape, put focus on a non-input field of the browser document */ +function focusContent(clear_command_line, clear_statusline) +{ + try + { + g_count = -1; // clear count + + if(clear_command_line) + { + command_line.value = ""; + command_line.inputField.setAttribute("style","font-family: monospace;"); + } + + if(clear_statusline) + { + completion_list.hidden = true; + comp_tab_index = COMPLETION_UNINITIALIZED; + comp_history_index = -1; + updateStatusbar(); + } + + var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] + .getService(Components.interfaces.nsIWindowWatcher); + if (window == ww.activeWindow && document.commandDispatcher.focusedElement) + { + document.commandDispatcher.focusedElement.blur(); + } + content.focus(); + + } catch(e) + { + echoerr(e); + } +} + +function openVimperatorBar(str) +{ + // make sure the input field is not red anymore if we had an echoerr() first + command_line.inputField.setAttribute("style","font-family: monospace;"); + + if(str == null) + str = ""; + + if (g_count > 1) + command_line.value = ":" + g_count.toString() + str; + else + command_line.value = ":" + str; + + try { + command_line.focus(); + } catch(e) { + echo(e); + } +} + + +function onEscape() +{ + BrowserStop(); + focusContent(true, true); + hah.disableHahMode(); +} + +function onBlur() // FIXME: needed? +{ + //alert('blur'); + //focusContent(false, false); +} + +//////////////////////////////////////////////////////////////////////// +// statusbar/progressbar ////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +/* the statusbar is currently divided into 5 fields, you can set + * each one independently */ +function showStatusbarMessage(msg, field) +{ + var bar = document.getElementById("vim-sb-field-" + field); + if (bar) + bar.value = msg; +} + +function setStatusbarColor(color) +{ + var bar = document.getElementById("vim-statusbar"); + bar.setAttribute("style", "background-color: " + color); +} + +function updateStatusbar() +{ + var buffers = "[" + (gBrowser.tabContainer.selectedIndex + 1).toString() + "/" + + gBrowser.tabContainer.childNodes.length.toString() + "]"; + + showStatusbarMessage(getCurrentLocation(), STATUSFIELD_URL); + showStatusbarMessage(" " + g_inputbuffer + " ", STATUSFIELD_INPUTBUFFER); + showStatusbarMessage("", STATUSFIELD_PROGRESS); + showStatusbarMessage(buffers, STATUSFIELD_BUFFERS); + + // required to postpone it a little, otherwise we could get the wrong cursor + // position when switching tabs + setTimeout(function() { + showStatusbarMessage(createCursorPositionString(), STATUSFIELD_CURSOR_POSITION); + } , 10); +} + +/* aProgress is a float between 0 and 1 */ +function createProgressBar(aProgress) +{ + /* the progress field */ + var progress; + if (aProgress <= 0) + progress = "[ Loading... ]"; + else if (aProgress >= 1) + progress = "[====================]"; + else + { + progress = /*(aProgress*100).round().toString() + "% */"["; + done = Math.floor(aProgress * 20); + for (i=0; i < done; i++) + progress = progress + "="; + progress = progress + ">"; + for (i=19; i > done; i--) + progress = progress + " "; + progress = progress + "]"; + } + return progress; +} + +function createCursorPositionString() +{ + var win = document.commandDispatcher.focusedWindow; + //var x = win.scrollMaxX == 0 ? 100 : Math.round(win.scrollX / win.scrollMaxX * 100); + var y = win.scrollMaxY == 0 ? -1 : Math.round(win.scrollY / win.scrollMaxY * 100); + + var percent; + if (y < 0) percent = "All"; + else if (y == 0) percent = "Top"; + else if (y < 10) percent = " " + y.toString() + "%"; + else if (y >= 100) percent = "Bot"; + else percent = y.toString() + "%"; + + return(" " + percent); +} + +//////////////////////////////////////////////////////////////////////// +// text input functions /////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +function isFormElemFocused() +{ + var elt = document.commandDispatcher.focusedElement; + if (elt == null) return false; + + var tagName = elt.localName.toUpperCase(); + + if (tagName == "INPUT" || + tagName == "TEXTAREA" || + tagName == "SELECT" || + tagName == "BUTTON" || + tagName == "ISINDEX") + return true; + + return false; +} + + +//////////////////////////////////////////////////////////////////////// +// logging //////////////////////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// + +var gConsoleService = Components.classes['@mozilla.org/consoleservice;1'] + .getService(Components.interfaces.nsIConsoleService); + +function logMessage(msg) +{ + gConsoleService.logStringMessage('vimperator: ' + msg); +} + +//////////////////////////////////////////////////////////////////////// +// misc helper funcstions ///////////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +// this function gets an event as the input and converts it to +// a keycode which can be used in mappings +// e.g. pressing ctrl+n would result in the string "" +function keyToString(event) +{ + var key = String.fromCharCode(event.charCode); + var modifier = ""; + if (event.charCode > 0) + { + if (event.ctrlKey) + modifier += "C-"; + if (event.altKey) + modifier += "A-"; + if (event.metaKey) + modifier += "M-"; + + if (modifier.length > 0) + return "<" + modifier + key + ">"; + else + return key; + } + else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_RETURN) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_TAB) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_DELETE) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_BACK_SPACE) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_HOME) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_END) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_LEFT) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_RIGHT) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_UP) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_DOWN) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_PAGE_UP) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_PAGE_DOWN) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F1) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F2) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F3) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F4) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F5) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F6) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F7) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F8) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F9) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F10) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F11) + return ""; + else if (event.keyCode == KeyEvent.DOM_VK_F12) + return ""; + + // if nothing matches + return null; +} + +//////////////////////////////////////////////////////////////////////// +// DOM related helper functsion /////////////////////////////////// {{{1 +//////////////////////////////////////////////////////////////////////// +// Handle frames if they're present +function getPageLinkNodes() +{ + var frames = window._content.frames; + + // The main content may have link nodes as well as it's frames. + var nodes = getLinkNodes(_content.content.document); + var tmp; + for (var i=0; i + + + + + + + +