diff --git a/chrome/content/vimperator/commands.js b/chrome/content/vimperator/commands.js index 96554440..63534461 100644 --- a/chrome/content/vimperator/commands.js +++ b/chrome/content/vimperator/commands.js @@ -1305,102 +1305,6 @@ function isDirectory(url) return false; } -/////////////////////////////////////////////////////////////////////}}} -// frame related functions ///////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////{{{ - -// TODO: allow callback for filtering out unwanted frames? User defined? -Vimperator.prototype.shiftFrameFocus = function(count, forward) -{ - try - { - var frames = []; - - // find all frames - depth-first search - (function(frame) - { - if (frame.document.body.localName.toLowerCase() == "body") - frames.push(frame); - for (var i = 0; i < frame.frames.length; i++) - arguments.callee(frame.frames[i]) - })(window.content); - - if (frames.length == 0) // currently top is always included - return; - - // remove all unfocusable frames - // TODO: find a better way to do this - var start = document.commandDispatcher.focusedWindow; - frames = frames.filter(function(frame) { - frame.focus(); - if (document.commandDispatcher.focusedWindow == frame) - return frame; - }); - start.focus(); - - // find the currently focused frame index - // TODO: If the window is a frameset then the first _frame_ should be - // focused. Since this is not the current FF behaviour, - // we initalise current to -1 so the first call takes us to the - // first frame. - var current = -1; - for (var i = 0; i < frames.length; i++) - { - if (frames[i] == document.commandDispatcher.focusedWindow) - { - var current = i; - break; - } - } - - // calculate the next frame to focus - var next = current; - if (forward) - { - if (count > 1) - next = current + count; - else - next++; - - if (next > frames.length - 1) - next = frames.length - 1; - } - else - { - if (count > 1) - next = current - count; - else - next--; - - if (next < 0) - next = 0; - } - - // focus next frame and scroll into view - frames[next].focus(); - if (frames[next] != window.content) - frames[next].frameElement.scrollIntoView(false); - - // add the frame indicator - var doc = frames[next].document; - var indicator = doc.createElement("div"); - indicator.id = "vimperator-frame-indicator"; - // NOTE: need to set a high z-index - it's a crapshoot! - var style = "background-color: red; opacity: 0.5; z-index: 999;" + - "position: fixed; top: 0; bottom: 0; left: 0; right: 0;"; - indicator.setAttribute("style", style); - doc.body.appendChild(indicator); - - // remove the frame indicator - setTimeout(function() { doc.body.removeChild(indicator); }, 500); - } - catch (e) - { - //vimperator.echoerr(e); - // FIXME: fail silently here for now - } -} - /////////////////////////////////////////////////////////////////////}}} // location handling /////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////{{{ @@ -1416,7 +1320,6 @@ function getCurrentTitle() return window.content.document.title; } - /////////////////////////////////////////////////////////////////////}}} // scrolling /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////{{{ @@ -1506,7 +1409,7 @@ function zoom_in(factor) } } -//Vimperator.prototype.zoom_to = function(value) +//vimperator.zoom_to = function(value) function zoom_to(value) { var zoomMgr = ZoomManager.prototype.getInstance(); @@ -1539,11 +1442,39 @@ function zoom_to(value) vimperator.echo("Zoom value: " + value + "%"); } -//}}} -//////////////////////////////////////////////////////////////////////// -// misc helper functions ////////////////////////////////////////////{{{ -//////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////}}} +// DOM related helper functions //////////////////////////////////////// +/////////////////////////////////////////////////////////////////////{{{ +function isFormElemFocused() +{ + var elt = window.document.commandDispatcher.focusedElement; + if (elt == null) + return false; + + try + { // sometimes the elt doesn't have .localName + var tagname = elt.localName.toLowerCase(); + var type = elt.type.toLowerCase(); + + if ( (tagname == "input" && (type != "image")) || + tagname == "textarea" || + // tagName == "SELECT" || + // tagName == "BUTTON" || + tagname == "isindex") // isindex is a deprecated one-line input box + return true; + } + catch (e) + { + // FIXME: do nothing? + } + + return false; +} + +/////////////////////////////////////////////////////////////////////}}} +// misc helper functions /////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////{{{ function copyToClipboard(str) { @@ -1572,119 +1503,6 @@ function evaluateXPath(expression, doc, ordered) ); return res; } - -Vimperator.prototype.beep = function() -{ - if (!vimperator.options["beep"]) - return; - - var gBeepService = Components.classes['@mozilla.org/sound;1'] - .getService(Components.interfaces.nsISound); - - if (gBeepService) - gBeepService.beep(); - else - vimperator.echoerr('no beep service found'); -} - -// quit vimperator, no matter how many tabs/windows are open -Vimperator.prototype.quit = function(save_session) -{ - if (save_session) - Options.setFirefoxPref("browser.startup.page", 3); // start with saved session - else - Options.setFirefoxPref("browser.startup.page", 1); // start with default homepage session - - goQuitApplication(); -} - -Vimperator.prototype.restart = function() -{ - // if (!arguments[1]) return; - const nsIAppStartup = Components.interfaces.nsIAppStartup; - - // Notify all windows that an application quit has been requested. - var os = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"] - .createInstance(Components.interfaces.nsISupportsPRBool); - os.notifyObservers(cancelQuit, "quit-application-requested", null); - - // Something aborted the quit process. - if (cancelQuit.data) - return; - - // Notify all windows that an application quit has been granted. - os.notifyObservers(null, "quit-application-granted", null); - - // Enumerate all windows and call shutdown handlers - var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] - .getService(Components.interfaces.nsIWindowMediator); - var windows = wm.getEnumerator(null); - while (windows.hasMoreElements()) - { - var win = windows.getNext(); - if (("tryToClose" in win) && !win.tryToClose()) - return; - } - Components.classes["@mozilla.org/toolkit/app-startup;1"].getService(nsIAppStartup) - .quit(nsIAppStartup.eRestart | nsIAppStartup.eAttemptQuit); -} - -Vimperator.prototype.source = function(filename, silent) -{ - if (!filename) - return; - - function getEnv(variable) - { - var environment = Components.classes["@mozilla.org/process/environment;1"] - .getService(Components.interfaces.nsIEnvironment); - return environment.get(variable); - } - - // convert "~" to HOME on Windows - if (navigator.platform == "Win32") - { - // TODO: proper pathname separator translation like Vim - filename = filename.replace('/', '\\', 'g'); - var matches = filename.match(/^~(.*)/) - if (matches) - { - var home_dir = getEnv("HOME"); - if (!home_dir) - home_dir = getEnv("USERPROFILE"); - if (!home_dir) - { - // TODO: are these guaranteed to be set? - home_dir = getEnv("HOMEDRIVE") + getEnv("HOMEPATH"); - } - filename = home_dir + "\\" + matches[1]; - } - } - - try - { - var fd = fopen(filename, "<"); - if (!fd) - return; - - var s = fd.read(); - fd.close(); - - var prev_match = new Array(5); - var heredoc = ''; - var end = false; - s.split('\n').forEach(function(line) { - [prev_match, heredoc, end] = multiliner(line, prev_match, heredoc); - }); - } - catch (e) - { - if (!silent) - vimperator.echoerr(e); - } -} //}}} // vim: set fdm=marker sw=4 ts=4 et: diff --git a/chrome/content/vimperator/find.js b/chrome/content/vimperator/find.js index 66a8db72..19916551 100644 --- a/chrome/content/vimperator/find.js +++ b/chrome/content/vimperator/find.js @@ -240,7 +240,7 @@ function abs_point (node) { // Vimperator searcher // make sure you only create this object when the "vimperator" object is ready -//Vimperator.prototype.search = new function() +//vimperator.search = new function() function Search() //{{{ { var self = this; // needed for callbacks since "this" is the "vimperator" object in a callback diff --git a/chrome/content/vimperator/help.js b/chrome/content/vimperator/help.js index 9fd80474..df3cb78e 100644 --- a/chrome/content/vimperator/help.js +++ b/chrome/content/vimperator/help.js @@ -26,7 +26,7 @@ the provisions above, a recipient may use your version of this file under the terms of any one of the MPL, the GPL or the LGPL. }}} ***** END LICENSE BLOCK *****/ -Vimperator.prototype.help = function(section, easter) //{{{ +vimperator.help = function(section, easter) //{{{ { if (easter) { diff --git a/chrome/content/vimperator/mappings.js b/chrome/content/vimperator/mappings.js index 139d688f..4bb9da5e 100644 --- a/chrome/content/vimperator/mappings.js +++ b/chrome/content/vimperator/mappings.js @@ -848,7 +848,7 @@ function Mappings() //{{{ { short_help: "Escape next key", help: "If you need to pass a certain key to a javascript form field or another extension prefix the key with <C-v>.
" + - "Also works to unshadow Firefox shortcuts like <C-o> which are otherwise hidden in Vimperator.
" + + "Also works to unshadow Firefox shortcuts like <C-o> which are otherwise hidden in vimperator.
" + "When in 'ignorekeys' mode (activated by <I>), <C-v> will pass the next key to Vimperator instead of the web page." } )); diff --git a/chrome/content/vimperator/vimperator.js b/chrome/content/vimperator/vimperator.js index d1711a5c..f21ee0a6 100644 --- a/chrome/content/vimperator/vimperator.js +++ b/chrome/content/vimperator/vimperator.js @@ -26,128 +26,13 @@ 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 *****/ -// The only global object, a handler to the main Vimperator object -var vimperator = null; - -var popup_allowed_events; // need to change and reset this firefox pref XXX: move to options class - -// called when the chrome is fully loaded and before the main window is shown -window.addEventListener("load", init, false); - -//////////////////////////////////////////////////////////////////////// -// init/uninit ///////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////{{{ - -function init() //{{{ -{ - window.dump("Vimperator init\n"); - // init the main object - vimperator = new Vimperator(); - vimperator.log("Initializing vimperator object...", 1); - - // these inner classes are created here, because outside the init() - // function, the chrome:// is not ready - vimperator.log("Loading module options...", 3); - Vimperator.prototype.options = new Options(); - vimperator.log("Loading module events...", 3); - Vimperator.prototype.events = new Events(); - vimperator.log("Loading module commands...", 3); - Vimperator.prototype.commands = new Commands(); - vimperator.log("Loading module bookmarks...", 3); - Vimperator.prototype.bookmarks = new Bookmarks(); - vimperator.log("Loading module history...", 3); - Vimperator.prototype.history = new History(); - vimperator.log("Loading module commandline...", 3); - Vimperator.prototype.commandline = new CommandLine(); - vimperator.log("Loading module search...", 3); - Vimperator.prototype.search = new Search(); - vimperator.log("Loading module preview window...", 3); - Vimperator.prototype.previewwindow = new InformationList("vimperator-previewwindow", { incremental_fill: false, max_items: 10 }); - vimperator.log("Loading module buffer window...", 3); - Vimperator.prototype.bufferwindow = new InformationList("vimperator-bufferwindow", { incremental_fill: false, max_items: 10 }); - vimperator.log("Loading module mappings...", 3); - Vimperator.prototype.mappings = new Mappings(); - vimperator.log("Loading module statusline...", 3); - Vimperator.prototype.statusline = new StatusLine(); - vimperator.log("Loading module tabs...", 3); - Vimperator.prototype.tabs = new Tabs(); - vimperator.log("Loading module marks...", 3); - Vimperator.prototype.marks = new Marks(); - vimperator.log("Loading module quickmarks...", 3); - Vimperator.prototype.quickmarks = new QuickMarks(); - vimperator.log("Loading module hints...", 3); - Vimperator.prototype.hints = new Hints(); - vimperator.log("All modules loaded", 3); - - // DJK FIXME - Vimperator.prototype.echo = vimperator.commandline.echo; - Vimperator.prototype.echoerr = vimperator.commandline.echoErr; - - // XXX: move elsewhere - vimperator.registerCallback("submit", vimperator.modes.EX, function(command) { /*vimperator.*/execute(command); } ); - vimperator.registerCallback("complete", vimperator.modes.EX, function(str) { return exTabCompletion(str); } ); - - // this function adds all our required listeners to react on events - // also stuff like window.onScroll is handled there. - //addEventListeners(); - //vimperator.events(); - - // work around firefox popup blocker - popup_allowed_events = Options.getFirefoxPref('dom.popup_allowed_events', 'change click dblclick mouseup reset submit'); - if (!popup_allowed_events.match("keypress")) - Options.setFirefoxPref('dom.popup_allowed_events', popup_allowed_events + " keypress"); - - // we have our own typeahead find implementation - Options.setFirefoxPref('accessibility.typeaheadfind.autostart', false); - Options.setFirefoxPref('accessibility.typeaheadfind', false); // actually the above setting should do it, but has no effect in firefox - - // first time intro message - if (Options.getPref("firsttime", true)) - { - setTimeout(function() { - vimperator.help(null, null, null, { inTab: true }); - Options.setPref("firsttime", false); - }, 1000); - } - - gURLBar.blur(); - vimperator.focusContent(); - - // firefox preferences which we need to be changed to work well with vimperator - Options.setFirefoxPref("browser.startup.page", 3); // start with saved session - - // Finally, read a ~/.vimperatorrc - // Make sourcing asynchronous, otherwise commands that open new tabs won't work - setTimeout(function() { - vimperator.source("~/.vimperatorrc", true); - vimperator.log("~/.vimperatorrc sourced", 1); - }, 50); - - window.addEventListener("unload", unload, false); - vimperator.log("Vimperator fully initialized", 1); -} //}}} - -function unload() //{{{ -{ - /*** save our preferences ***/ - vimperator.commandline.destroy(); - vimperator.events.destroy(); - vimperator.quickmarks.destroy(); - - // reset some modified firefox prefs - if (Options.getFirefoxPref('dom.popup_allowed_events', 'change click dblclick mouseup reset submit') - == popup_allowed_events + " keypress") - Options.setFirefoxPref('dom.popup_allowed_events', popup_allowed_events); -} //}}} -//}}} - -function Vimperator() //{{{ +const vimperator = (function() //{{{ { //////////////////////////////////////////////////////////////////////////////// ////////////////////// PRIVATE SECTION ///////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - this.modes = { // actually not private, but Firefox complains if this doesn't come first + var modes = { // main modes NONE: 0, NORMAL: 1 << 0, @@ -166,25 +51,33 @@ function Vimperator() //{{{ EXTENDED_HINT: 1 << 17, ALWAYS_HINT: 1 << 18 } - var mode_messages = {}; - mode_messages[this.modes.NORMAL] = ""; - mode_messages[this.modes.INSERT] = "INSERT"; - mode_messages[this.modes.VISUAL] = "VISUAL"; - mode_messages[this.modes.HINTS] = "HINTS"; - mode_messages[this.modes.ESCAPE_ONE_KEY] = "escape one key"; - mode_messages[this.modes.ESCAPE_ALL_KEYS] = "escape all keys"; - mode_messages[this.modes.ESCAPE_ONE_KEY | this.modes.ESCAPE_ALL_KEYS] = "pass one key"; - mode_messages[this.modes.QUICK_HINT] = "quick"; - mode_messages[this.modes.EXTENDED_HINT] = "extended"; - mode_messages[this.modes.ALWAYS_HINT] = "always"; - var callbacks = new Array(); - var mode = this.modes.NORMAL; - var extended_mode = this.modes.NONE; + var mode_messages = {}; + mode_messages[modes.NORMAL] = ""; + mode_messages[modes.INSERT] = "INSERT"; + mode_messages[modes.VISUAL] = "VISUAL"; + mode_messages[modes.HINTS] = "HINTS"; + mode_messages[modes.ESCAPE_ONE_KEY] = "escape one key"; + mode_messages[modes.ESCAPE_ALL_KEYS] = "escape all keys"; + mode_messages[modes.ESCAPE_ONE_KEY | modes.ESCAPE_ALL_KEYS] = "pass one key"; + mode_messages[modes.QUICK_HINT] = "quick"; + mode_messages[modes.EXTENDED_HINT] = "extended"; + mode_messages[modes.ALWAYS_HINT] = "always"; + + var mode = modes.NORMAL; + var extended_mode = modes.NONE; + + var callbacks = []; + + var popup_allowed_events; // need to change and reset this firefox pref XXX: move to options class // our services + var sound_service = Components.classes['@mozilla.org/sound;1'] + .getService(Components.interfaces.nsISound); var console_service = Components.classes['@mozilla.org/consoleservice;1'] .getService(Components.interfaces.nsIConsoleService); + var environment_service = Components.classes["@mozilla.org/process/environment;1"] + .getService(Components.interfaces.nsIEnvironment); function showMode() { @@ -214,169 +107,428 @@ function Vimperator() //{{{ ////////////////////// PUBLIC SECTION ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ - this.version = "###VERSION### CVS (created: ###DATE###)"; + return { - this.input = { - buffer: "", // partial command storage - pendingMap: null, // pending map storage - count: -1 // parsed count from the input buffer - }; + modes: modes, - /////////////// callbacks //////////////////////////// - // XXX: shouldn't that callback be moved to commandline? --mst - /** - * @param type Can be: - * "submit": when the user pressed enter in the command line - * "change" - * "cancel" - * "complete" - */ - this.registerCallback = function(type, mode, func) - { - // TODO: check if callback is already registered - callbacks.push([type, mode, func]); - } + version: "###VERSION### CVS (created: ###DATE###)", - this.triggerCallback = function(type, data) - { - for (var i in callbacks) - { - var [thistype, thismode, thisfunc] = callbacks[i]; - if (vimperator.hasMode(thismode) && type == thistype) - return thisfunc.call(this, data); - } - return false; - } + input: { + buffer: "", // partial command storage + pendingMap: null, // pending map storage + count: -1 // parsed count from the input buffer + }, - // just forward these echo commands - // DJK FIXME: this.echo = this.commandline.echo; - // DJK FIXME: this.echoerr = this.commandline.echoErr; - - - this.getMode = function() - { - return [mode, extended_mode]; - } - - // set current mode - // use "null" if you only want to set one of those modes - this.setMode = function(main, extended, silent) - { - // if a main mode is set, the extended is always cleared - if (main) + /** + * @param type Can be: + * "submit": when the user pressed enter in the command line + * "change" + * "cancel" + * "complete" + */ + registerCallback: function(type, mode, func) { - mode = main; - extended_mode = this.modes.NONE; - } - if (typeof extended === "number") - extended_mode = extended; + // TODO: check if callback is already registered + callbacks.push([type, mode, func]); + }, - if (!silent) - showMode(); - } - - // returns true if "whichmode" is found in either the main or - // extended mode - this.hasMode = function(whichmode) - { - return ((mode & whichmode) || (extended_mode & whichmode) > 0) ? true : false; - } - - this.addMode = function(main, extended) - { - if (main) - mode |= main; - if (extended) - extended_mode |= extended; - - showMode(); - } - - // always show the new mode in the statusline - this.removeMode = function(main, extended) - { - if (main) - mode = (mode | main) ^ main; - if (extended) - extended_mode = (extended_mode | extended) ^ extended; - - showMode(); - } - - // After pressing Escape, put focus on a non-input field of the browser document - this.focusContent = function() - { - 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(); - } - - /** - * logs any object to the javascript error console - * also prints all properties of thie object - */ - this.log = function(msg, level) - { - // if (Options.getPref("verbose") >= level) // FIXME: hangs vimperator, probably timing issue --mst - console_service.logStringMessage('vimperator: ' + msg); - } - - /** - * logs any object to the javascript error console - * also prints all properties of the object - */ - this.logObject = function(object, level) - { - if (typeof object != 'object') + triggerCallback: function(type, data) + { + for (var i in callbacks) + { + var [thistype, thismode, thisfunc] = callbacks[i]; + if (vimperator.hasMode(thismode) && type == thistype) + return thisfunc.call(this, data); + } return false; + }, - var string = object + '::\n'; - for (var i in object) + getMode: function() { - var value; - try { - var value = object[i]; - } catch (e) { value = '' } + return [mode, extended_mode]; + }, - string += i + ': ' + value + '\n'; + // set current mode + // use "null" if you only want to set one of those modes + setMode: function(main, extended, silent) + { + // if a main mode is set, the extended is always cleared + if (main) + { + mode = main; + extended_mode = this.modes.NONE; + } + if (typeof extended === "number") + extended_mode = extended; + + if (!silent) + showMode(); + }, + + // returns true if "whichmode" is found in either the main or + // extended mode + hasMode: function(whichmode) + { + return ((mode & whichmode) || (extended_mode & whichmode) > 0) ? true : false; + }, + + addMode: function(main, extended) + { + if (main) + mode |= main; + if (extended) + extended_mode |= extended; + + showMode(); + }, + + // always show the new mode in the statusline + removeMode: function(main, extended) + { + if (main) + mode = (mode | main) ^ main; + if (extended) + extended_mode = (extended_mode | extended) ^ extended; + + showMode(); + }, + + beep: function() + { + if (vimperator.options["beep"]) + sound_service.beep(); + }, + + // After pressing Escape, put focus on a non-input field of the browser document + focusContent: function() + { + 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(); + }, + + /** + * logs a message to the javascript error console + */ + log: function(msg, level) + { + // if (Options.getPref("verbose") >= level) // FIXME: hangs vimperator, probably timing issue --mst + console_service.logStringMessage('vimperator: ' + msg); + }, + + /** + * logs an object to the javascript error console also prints all + * properties of the object + */ + logObject: function(object, level) + { + if (typeof object != 'object') + return false; + + var string = object + '::\n'; + for (var i in object) + { + var value; + try { + var value = object[i]; + } catch (e) { value = '' } + + string += i + ': ' + value + '\n'; + } + this.log(string, level); + }, + + // quit vimperator, no matter how many tabs/windows are open + quit: function(save_session) + { + if (save_session) + Options.setFirefoxPref("browser.startup.page", 3); // start with saved session + else + Options.setFirefoxPref("browser.startup.page", 1); // start with default homepage session + + goQuitApplication(); + }, + + restart: function() + { + // if (!arguments[1]) return; + const nsIAppStartup = Components.interfaces.nsIAppStartup; + + // Notify all windows that an application quit has been requested. + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"] + .createInstance(Components.interfaces.nsISupportsPRBool); + os.notifyObservers(cancelQuit, "quit-application-requested", null); + + // Something aborted the quit process. + if (cancelQuit.data) + return; + + // Notify all windows that an application quit has been granted. + os.notifyObservers(null, "quit-application-granted", null); + + // Enumerate all windows and call shutdown handlers + var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var windows = wm.getEnumerator(null); + while (windows.hasMoreElements()) + { + var win = windows.getNext(); + if (("tryToClose" in win) && !win.tryToClose()) + return; + } + Components.classes["@mozilla.org/toolkit/app-startup;1"].getService(nsIAppStartup) + .quit(nsIAppStartup.eRestart | nsIAppStartup.eAttemptQuit); + }, + + // TODO: allow callback for filtering out unwanted frames? User defined? + shiftFrameFocus: function(count, forward) + { + try + { + var frames = []; + + // find all frames - depth-first search + (function(frame) + { + if (frame.document.body.localName.toLowerCase() == "body") + frames.push(frame); + for (var i = 0; i < frame.frames.length; i++) + arguments.callee(frame.frames[i]) + })(window.content); + + if (frames.length == 0) // currently top is always included + return; + + // remove all unfocusable frames + // TODO: find a better way to do this + var start = document.commandDispatcher.focusedWindow; + frames = frames.filter(function(frame) { + frame.focus(); + if (document.commandDispatcher.focusedWindow == frame) + return frame; + }); + start.focus(); + + // find the currently focused frame index + // TODO: If the window is a frameset then the first _frame_ should be + // focused. Since this is not the current FF behaviour, + // we initalise current to -1 so the first call takes us to the + // first frame. + var current = -1; + for (var i = 0; i < frames.length; i++) + { + if (frames[i] == document.commandDispatcher.focusedWindow) + { + var current = i; + break; + } + } + + // calculate the next frame to focus + var next = current; + if (forward) + { + if (count > 1) + next = current + count; + else + next++; + + if (next > frames.length - 1) + next = frames.length - 1; + } + else + { + if (count > 1) + next = current - count; + else + next--; + + if (next < 0) + next = 0; + } + + // focus next frame and scroll into view + frames[next].focus(); + if (frames[next] != window.content) + frames[next].frameElement.scrollIntoView(false); + + // add the frame indicator + var doc = frames[next].document; + var indicator = doc.createElement("div"); + indicator.id = "vimperator-frame-indicator"; + // NOTE: need to set a high z-index - it's a crapshoot! + var style = "background-color: red; opacity: 0.5; z-index: 999;" + + "position: fixed; top: 0; bottom: 0; left: 0; right: 0;"; + indicator.setAttribute("style", style); + doc.body.appendChild(indicator); + + // remove the frame indicator + setTimeout(function() { doc.body.removeChild(indicator); }, 500); + } + catch (e) + { + //vimperator.echoerr(e); + // FIXME: fail silently here for now + } + }, + + source: function(filename, silent) + { + if (!filename) + return; + + // convert "~" to HOME on Windows + if (navigator.platform == "Win32") + { + // TODO: proper pathname separator translation like Vim + filename = filename.replace('/', '\\', 'g'); + var matches = filename.match(/^~(.*)/) + if (matches) + { + var home_dir = environment_service.get("HOME"); + if (!home_dir) + home_dir = environment_service.get("USERPROFILE"); + if (!home_dir) + { + // TODO: are these guaranteed to be set? + home_dir = environment_service.get("HOMEDRIVE") + environment_service.get("HOMEPATH"); + } + filename = home_dir + "\\" + matches[1]; + } + } + + try + { + var fd = fopen(filename, "<"); + if (!fd) + return; + + var s = fd.read(); + fd.close(); + + var prev_match = new Array(5); + var heredoc = ''; + var end = false; + s.split('\n').forEach(function(line) { + [prev_match, heredoc, end] = multiliner(line, prev_match, heredoc); + }); + } + catch (e) + { + if (!silent) + vimperator.echoerr(e); + } + }, + + startup: function() + { + window.dump("Vimperator startup\n"); + vimperator.log("Initializing vimperator object...", 1); + + // these objects are created here only after the chrome is ready + vimperator.log("Loading module options...", 3); + vimperator.options = new Options(); + vimperator.log("Loading module events...", 3); + vimperator.events = new Events(); + vimperator.log("Loading module commands...", 3); + vimperator.commands = new Commands(); + vimperator.log("Loading module bookmarks...", 3); + vimperator.bookmarks = new Bookmarks(); + vimperator.log("Loading module history...", 3); + vimperator.history = new History(); + vimperator.log("Loading module commandline...", 3); + vimperator.commandline = new CommandLine(); + vimperator.log("Loading module search...", 3); + vimperator.search = new Search(); + vimperator.log("Loading module preview window...", 3); + vimperator.previewwindow = new InformationList("vimperator-previewwindow", { incremental_fill: false, max_items: 10 }); + vimperator.log("Loading module buffer window...", 3); + vimperator.bufferwindow = new InformationList("vimperator-bufferwindow", { incremental_fill: false, max_items: 10 }); + vimperator.log("Loading module mappings...", 3); + vimperator.mappings = new Mappings(); + vimperator.log("Loading module statusline...", 3); + vimperator.statusline = new StatusLine(); + vimperator.log("Loading module tabs...", 3); + vimperator.tabs = new Tabs(); + vimperator.log("Loading module marks...", 3); + vimperator.marks = new Marks(); + vimperator.log("Loading module quickmarks...", 3); + vimperator.quickmarks = new QuickMarks(); + vimperator.log("Loading module hints...", 3); + vimperator.hints = new Hints(); + vimperator.log("All modules loaded", 3); + + vimperator.echo = vimperator.commandline.echo; + vimperator.echoerr = vimperator.commandline.echoErr; + + // XXX: move elsewhere + vimperator.registerCallback("submit", vimperator.modes.EX, function(command) { /*vimperator.*/execute(command); } ); + vimperator.registerCallback("complete", vimperator.modes.EX, function(str) { return exTabCompletion(str); } ); + + // this function adds all our required listeners to react on events + // also stuff like window.onScroll is handled there. + //addEventListeners(); + //vimperator.events(); + + // work around firefox popup blocker + popup_allowed_events = Options.getFirefoxPref('dom.popup_allowed_events', 'change click dblclick mouseup reset submit'); + if (!popup_allowed_events.match("keypress")) + Options.setFirefoxPref('dom.popup_allowed_events', popup_allowed_events + " keypress"); + + // we have our own typeahead find implementation + Options.setFirefoxPref('accessibility.typeaheadfind.autostart', false); + Options.setFirefoxPref('accessibility.typeaheadfind', false); // actually the above setting should do it, but has no effect in firefox + + // first time intro message + if (Options.getPref("firsttime", true)) + { + setTimeout(function() { + vimperator.help(null, null, null, { inTab: true }); + Options.setPref("firsttime", false); + }, 1000); + } + + //gURLBar.blur(); // TODO: needed anymore? + vimperator.focusContent(); + + // firefox preferences which we need to be changed to work well with vimperator + Options.setFirefoxPref("browser.startup.page", 3); // start with saved session + + // Finally, read a ~/.vimperatorrc + // Make sourcing asynchronous, otherwise commands that open new tabs won't work + setTimeout(function() { + vimperator.source("~/.vimperatorrc", true); + vimperator.log("~/.vimperatorrc sourced", 1); + }, 50); + + vimperator.log("Vimperator fully initialized", 1); + }, + + shutdown: function() + { + window.dump("Vimperator shutdown\n"); + + /*** save our preferences ***/ + vimperator.commandline.destroy(); + vimperator.events.destroy(); + vimperator.quickmarks.destroy(); + + // reset some modified firefox prefs + if (Options.getFirefoxPref('dom.popup_allowed_events', 'change click dblclick mouseup reset submit') + == popup_allowed_events + " keypress") + Options.setFirefoxPref('dom.popup_allowed_events', popup_allowed_events); } - this.log(string, level); - } - //}}} -} //}}} + } //}}} +})(); //}}} -//////////////////////////////////////////////////////////////////////// -// DOM related helper functions //////////////////////////////////////// -/////////////////////////////////////////////////////////////////////{{{ -function isFormElemFocused() -{ - var elt = window.document.commandDispatcher.focusedElement; - if (elt == null) - return false; - - try - { // sometimes the elt doesn't have .localName - var tagname = elt.localName.toLowerCase(); - var type = elt.type.toLowerCase(); - - if ( (tagname == "input" && (type != "image")) || - tagname == "textarea" || - // tagName == "SELECT" || - // tagName == "BUTTON" || - tagname == "isindex") // isindex is a deprecated one-line input box - return true; - } - catch (e) - { - // FIXME: do nothing? - } - - return false; -} -//}}} +// called when the chrome is fully loaded and before the main window is shown +window.addEventListener("load", vimperator.startup, false); +window.addEventListener("unload", vimperator.shutdown, false); // vim: set fdm=marker sw=4 ts=4 et: