diff --git a/chrome/content/vimperator/commands.js b/chrome/content/vimperator/commands.js index 5e55d5e9..4e7bc333 100644 --- a/chrome/content/vimperator/commands.js +++ b/chrome/content/vimperator/commands.js @@ -1031,34 +1031,78 @@ function isDirectory(url) // frame related functions //////////////////////////////////////// {{{1 //////////////////////////////////////////////////////////////////////// -function focusNextFrame(count) +// TODO: allow callback for filtering out unwanted frames? User defined? +function focusNextFrame(count, forward) { try { - var frames = window.content.frames; - if (frames.length == 0) - { - vimperator.echo("No frames found"); - beep(); + 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; - } - var w = document.commandDispatcher.focusedWindow; - var next = 0; + // remove all unfocusable frames + var start = document.commandDispatcher.focusedWindow; + frames = frames.filter(function(frame) { + frame.focus(); + if (document.commandDispatcher.focusedWindow == frame) + return frame; + }); + start.focus(); - // Find the next frame to focus - for (var i=0; i= frames.length) - next = 0; + // 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"; @@ -1068,12 +1112,16 @@ function focusNextFrame(count) indicator.setAttribute("style", style); doc.body.appendChild(indicator); - setTimeout(function() { doc.body.removeChild(indicator); }, 300); - } catch(e) { alert(e); } + // remove the frame indicator + setTimeout(function() { doc.body.removeChild(indicator); }, 500); + } + catch (e) + { + //vimperator.echoerr(e); + // FIXME: fail silently here for now + } } - - //////////////////////////////////////////////////////////////////////// // location handling ////////////////////////////////////////////// {{{1 //////////////////////////////////////////////////////////////////////// diff --git a/chrome/content/vimperator/mappings.js b/chrome/content/vimperator/mappings.js index 910e7133..18636001 100644 --- a/chrome/content/vimperator/mappings.js +++ b/chrome/content/vimperator/mappings.js @@ -191,12 +191,18 @@ function Mappings()//{{{ flags: Mappings.flags.ARGUMENT } )); - addDefaultMap(new Map(vimperator.modes.NORMAL, ["]f"], - focusNextFrame, + addDefaultMap(new Map(vimperator.modes.NORMAL, ["]f"], function(count) { focusNextFrame(count > 1 ? count : 1, true); }, { short_help: "Focus next frame", - help: "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." + help: "Transfers keyboard focus to the [count]th next frame in order. The newly focused frame is briefly colored red.", + flags: Mappings.flags.COUNT + } + )); + addDefaultMap(new Map(vimperator.modes.NORMAL, ["[f"], function(count) { focusNextFrame(count > 1 ? count : 1, false); }, + { + short_help: "Focus previous frame", + help: "Transfers keyboard focus to the [count]th previous frame in order. The newly focused frame is briefly colored red.", + flags: Mappings.flags.COUNT } )); addDefaultMap(new Map(vimperator.modes.NORMAL, ["b"],