diff options
| author | Alex Kovar | 2010-09-27 11:13:37 -0500 |
|---|---|---|
| committer | Alex Kovar | 2010-09-27 11:13:37 -0500 |
| commit | 60ffade9df3f46fca1bf31dc77e46ad9873a9cdf (patch) | |
| tree | 835c479c6fe501972e305070e902cb8bd9278f2c | |
| parent | b2f3db6fe2a3ff8887c5961b868bebf530c4cfaf (diff) | |
| parent | 9d9bd40ae050ae4b5b9665fcdde4c60e3486cc87 (diff) | |
| download | vimium-60ffade9df3f46fca1bf31dc77e46ad9873a9cdf.tar.bz2 | |
Merge branch 'master' of http://github.com/philc/vimium
* 'master' of http://github.com/philc/vimium:
Revert a rename - not sure why I committed that.
Oops, fixed a regression.
Code cleanup & README changes for the multiple link hints patch.
Fix some frame logic and disable focusing the largest one for now because it's buggy with iframes.
A bunch of changes having to do with the frame support patch:
Updated README
Added ab3 to Credits ;)
Clean up code
added resetLinkHintsMode
Added activeteLinkHintsModeWithQueue and resetLinkHintsMode to linkHints.js and added 'q cmd' and 'q help cmd' to commands.js
Update README with new/changed key mappings.
Command to go to next frame.
Only show the Help Dialog on the focused frame.
Fix view source toggle when inside a frame.
Basic Support for Frames
Conflicts:
commands.js
manifest.json
| -rw-r--r-- | CREDITS | 2 | ||||
| -rw-r--r-- | README.markdown | 6 | ||||
| -rw-r--r-- | background_page.html | 90 | ||||
| -rw-r--r-- | commands.js | 18 | ||||
| -rw-r--r-- | linkHints.js | 54 | ||||
| -rw-r--r-- | manifest.json | 8 | ||||
| -rw-r--r-- | vimiumFrontend.js | 65 |
7 files changed, 194 insertions, 49 deletions
@@ -14,6 +14,8 @@ Contributors: lack markstos rodimius + Tim Morgan <tim@timmorgan.org> (github: seven1m) tsigo + Werner Laurensse (github: ab3) Feel free to add real names in addition to GitHub usernames. diff --git a/README.markdown b/README.markdown index 2e144110..36e267bf 100644 --- a/README.markdown +++ b/README.markdown @@ -36,8 +36,9 @@ Navigating the current page: <c-b> scroll up a full page f activate link hints mode to open in current tab F activate link hints mode to open in new tab + <a-f> activate link hints mode to open multiple links in a new tab r reload - gf view source + gs view source zi zoom in zo zoom out / enter find mode -- type your search query and hit enter to search or esc to cancel @@ -46,6 +47,7 @@ Navigating the current page: i enter insert mode -- all commands will be ignored until you hit esc to exit yy copy the current url to the clipboard gu go up one level in the URL hierarchy + gf cycle forward to the next frame Navigating your history: H go back in history @@ -84,6 +86,8 @@ Release Notes - In link hints mode, holding down the shift key will now toggle between opening in the current tab and opening in a new tab. - Two new commands (`zH` and `zL`) to scroll to the left and right edges of the page. - A new command (`gi`) to focus the first (or n-th) text input box on the page. +- A new command (`<a-f>`) to open up multiple links at a time in new tabs. +- Frame support. - Bug fixes. 1.19 (06/29/2010) diff --git a/background_page.html b/background_page.html index 991992b8..e0c1f3ce 100644 --- a/background_page.html +++ b/background_page.html @@ -14,6 +14,8 @@ var keyQueue = ""; // Queue of keys typed var validFirstKeys = {}; var singleKeyCommands = []; + var focusedFrame = null; + var framesForTab = {}; // Keys are either literal characters, or "named" - for example <a-b> (alt+b), <left> (the left arrow) or <f12> // This regular expression captures two groups, the first is a named key, the second is the remainder of the string. @@ -67,7 +69,10 @@ var sendRequestHandlers = { getCompletionKeys: getCompletionKeys, getLinkHintCss: getLinkHintCss, + openUrlInCurrentTab: openUrlInCurrentTab, openOptionsPageInNewTab: openOptionsPageInNewTab, + registerFrame: registerFrame, + frameFocused: handleFrameFocused, upgradeNotificationClosed: upgradeNotificationClosed, updateScrollPosition: handleUpdateScrollPosition }; @@ -91,6 +96,7 @@ } // domReady is the appropriate time to show the "vimium has been upgraded" message. + // TODO: This might be broken on pages with frames. if (shouldShowUpgradeMessage()) chrome.tabs.sendRequest(senderTabId, { name: "showUpgradeNotification", version: currentVersion }); } @@ -153,9 +159,9 @@ returnPort.postMessage({ zoomLevel: zoomLevel }); } - function showHelp() { + function showHelp(callback, frameId) { chrome.tabs.getSelected(null, function(tab) { - chrome.tabs.sendRequest(tab.id, { name: "showHelpDialog", dialogHtml: helpDialogHtml() }); + chrome.tabs.sendRequest(tab.id, { name: "showHelpDialog", dialogHtml: helpDialogHtml(), frameId:frameId }); }); } @@ -220,6 +226,15 @@ return {completionKeys: generateCompletionKeys()}; } + /** + * Opens the url in the current tab. + */ + function openUrlInCurrentTab(request) { + chrome.tabs.getSelected(null, function(tab) { + chrome.tabs.update(tab.id, {url: request.url}); + }); + } + /* * Returns the core CSS used for link hints, along with any user-provided overrides. */ @@ -268,9 +283,9 @@ if (selectionChangedHandlers.length > 0) { selectionChangedHandlers.pop().call(); } }); - function repeatFunction(func, totalCount, currentCount) { + function repeatFunction(func, totalCount, currentCount, frameId) { if (currentCount < totalCount) - func(function() { repeatFunction(func, totalCount, currentCount + 1); }); + func(function() { repeatFunction(func, totalCount, currentCount + 1, frameId); }, frameId); } // Returns the scroll coordinates of the given tab. Pass in a callback of the form: @@ -369,6 +384,7 @@ tabQueue[openTabInfo.windowId] = [openTabInfo]; delete openTabs[tabId]; + delete framesForTab[tabId]; }); chrome.windows.onRemoved.addListener(function(windowId) { @@ -487,19 +503,20 @@ return {count: count, command: command}; } - function handleKeyDown(key, port) { + function handleKeyDown(request, port) { + var key = request.keyChar; if (key == "<ESC>") { console.log("clearing keyQueue"); keyQueue = "" } else { console.log("checking keyQueue: [", keyQueue + key, "]"); - keyQueue = checkKeyQueue(keyQueue + key, port.tab.id); + keyQueue = checkKeyQueue(keyQueue + key, port.tab.id, request.frameId); console.log("new KeyQueue: " + keyQueue); } } - function checkKeyQueue(keysToCheck, tabId) { + function checkKeyQueue(keysToCheck, tabId, frameId) { var refreshedCompletionKeys = false; var splitHash = splitKeyQueue(keysToCheck); command = splitHash.command; @@ -515,6 +532,7 @@ if (!registryEntry.isBackgroundCommand) { var port = chrome.tabs.connect(tabId, { name: "executePageCommand" }); port.postMessage({ command: registryEntry.command, + frameId: frameId, count: count, passCountToFunction: registryEntry.passCountToFunction, completionKeys: generateCompletionKeys("") @@ -522,7 +540,7 @@ refreshedCompletionKeys = true; } else { - repeatFunction(this[registryEntry.command], count, 0); + repeatFunction(this[registryEntry.command], count, 0, frameId); } newKeyQueue = ""; @@ -592,6 +610,62 @@ }); } + function registerFrame(request, sender) { + if (!framesForTab[sender.tab.id]) + framesForTab[sender.tab.id] = { frames: [] }; + + if (request.top) { + focusedFrame = request.frameId; + framesForTab[sender.tab.id].total = request.total; + } + + framesForTab[sender.tab.id].frames.push({ id: request.frameId, area: request.area }); + + // We've seen all the frames. Time to focus the largest one. + // NOTE: Disabled because it's buggy with iframes. + // if (framesForTab[sender.tab.id].frames.length >= framesForTab[sender.tab.id].total) + // focusLargestFrame(sender.tab.id); + } + + function focusLargestFrame(tabId) { + var mainFrameId = null; + var mainFrameArea = 0; + + for (var i = 0; i < framesForTab[tabId].frames.length; i++) { + var currentFrame = framesForTab[tabId].frames[i]; + + if (currentFrame.area > mainFrameArea) { + mainFrameId = currentFrame.id; + mainFrameArea = currentFrame.area; + } + } + + chrome.tabs.sendRequest(tabId, { name: "focusFrame", frameId: mainFrameId, highlight: false }); + } + + function handleFrameFocused(request, sender) { + focusedFrame = request.frameId; + } + + function nextFrame(callback, frameId) { + chrome.tabs.getSelected(null, function(tab) { + var index; + var frames = framesForTab[tab.id].frames; + + for (index=0; index < frames.length; index++) { + if (frames[index].id == focusedFrame) + break; + } + + if (index >= frames.length-1) + index = 0; + else + index++; + + chrome.tabs.sendRequest(tab.id, { name: "focusFrame", frameId: frames[index].id, highlight: true }); + }); + } + function init() { clearKeyMappingsAndSetDefaults(); diff --git a/commands.js b/commands.js index e9a0bd41..fb264510 100644 --- a/commands.js +++ b/commands.js @@ -97,7 +97,7 @@ function clearKeyMappingsAndSetDefaults() { mapKeyToCommand('<c-f>', 'scrollFullPageDown'); mapKeyToCommand('<c-b>', 'scrollFullPageUp'); mapKeyToCommand('r', 'reload'); - mapKeyToCommand('gf', 'toggleViewSource'); + mapKeyToCommand('gs', 'toggleViewSource'); mapKeyToCommand('i', 'enterInsertMode'); @@ -110,8 +110,9 @@ function clearKeyMappingsAndSetDefaults() { mapKeyToCommand('gi', 'focusInput'); - mapKeyToCommand('f', 'activateLinkHintsMode'); - mapKeyToCommand('F', 'activateLinkHintsModeToOpenInNewTab'); + mapKeyToCommand('f', 'activateLinkHintsMode'); + mapKeyToCommand('F', 'activateLinkHintsModeToOpenInNewTab'); + mapKeyToCommand('<a-f>', 'activateLinkHintsModeWithQueue'); mapKeyToCommand('/', 'enterFindMode'); mapKeyToCommand('n', 'performFind'); @@ -127,8 +128,12 @@ function clearKeyMappingsAndSetDefaults() { mapKeyToCommand('t', 'createTab'); mapKeyToCommand('d', 'removeTab'); mapKeyToCommand('u', 'restoreTab'); + mapKeyToCommand('b', 'activateBookmarkFindMode'); mapKeyToCommand('B', 'activateBookmarkFindModeToOpenInNewTab') + + mapKeyToCommand('gf', 'nextFrame'); + } // Navigating the current page: @@ -158,6 +163,7 @@ addCommand('focusInput', 'Focus the first (or n-th) text box on the pag addCommand('activateLinkHintsMode', 'Enter link hints mode to open links in current tab'); addCommand('activateLinkHintsModeToOpenInNewTab', 'Enter link hints mode to open links in new tab'); +addCommand('activateLinkHintsModeWithQueue', 'Enter link hints mode to open multiple links in a new tab'); addCommand('activateBookmarkFindMode', 'Find bookmarks to open in current tab'); addCommand('activateBookmarkFindModeToOpenInNewTab', 'Find bookmarks to open in new tab'); @@ -180,6 +186,8 @@ addCommand('createTab', 'Create new tab', true); addCommand('removeTab', 'Close current tab', true); addCommand('restoreTab', "Restore closed tab", true); +addCommand('nextFrame', "Cycle forward to the next frame on the page", true); + // An ordered listing of all available commands, grouped by type. This is the order they will // be shown in the help page. @@ -190,8 +198,8 @@ var commandGroups = { "scrollPageUp", "scrollFullPageDown", "reload", "toggleViewSource", "zoomIn", "zoomOut", "copyCurrentUrl", "goUp", "enterInsertMode", "focusInput", - "activateLinkHintsMode", "activateLinkHintsModeToOpenInNewTab", - "enterFindMode", "performFind", "performBackwardsFind"], + "activateLinkHintsMode", "activateLinkHintsModeToOpenInNewTab", "activateLinkHintsModeWithQueue", + "enterFindMode", "performFind", "performBackwardsFind", "nextFrame"], historyNavigation: ["goBack", "goForward"], tabManipulation: diff --git a/linkHints.js b/linkHints.js index fcaa832b..7593a630 100644 --- a/linkHints.js +++ b/linkHints.js @@ -13,6 +13,7 @@ var hintMarkerContainingDiv = null; var hintKeystrokeQueue = []; var linkHintsModeActivated = false; var shouldOpenLinkHintInNewTab = false; +var shouldOpenLinkHintWithQueue = false; // Whether link hint's "open in current/new tab" setting is currently toggled var openLinkModeToggle = false; // Whether we have added to the page the CSS needed to display link hints. @@ -32,25 +33,32 @@ var clickableElementsXPath = (function() { })(); // We need this as a top-level function because our command system doesn't yet support arguments. -function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true); } +function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true, false); } -function activateLinkHintsMode(openInNewTab) { +function activateLinkHintsModeWithQueue() { activateLinkHintsMode(true, true); } + +function activateLinkHintsMode(openInNewTab, withQueue) { if (!linkHintsCssAdded) addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js linkHintCssAdded = true; linkHintsModeActivated = true; - setOpenLinkMode(openInNewTab); + setOpenLinkMode(openInNewTab, withQueue); buildLinkHints(); document.addEventListener("keydown", onKeyDownInLinkHintsMode, true); document.addEventListener("keyup", onKeyUpInLinkHintsMode, true); } -function setOpenLinkMode(openInNewTab) { +function setOpenLinkMode(openInNewTab, withQueue) { shouldOpenLinkHintInNewTab = openInNewTab; - if (shouldOpenLinkHintInNewTab) - HUD.show("Open link in new tab"); - else - HUD.show("Open link in current tab"); + shouldOpenLinkHintWithQueue = withQueue; + if (shouldOpenLinkHintWithQueue) { + HUD.show("Open multiple links in a new tab"); + } else { + if (shouldOpenLinkHintInNewTab) + HUD.show("Open link in new tab"); + else + HUD.show("Open link in current tab"); + } } /* @@ -143,10 +151,11 @@ function isVisible(element, clientRect) { } function onKeyDownInLinkHintsMode(event) { + console.log("Key Down"); if (event.keyCode == keyCodes.shiftKey && !openLinkModeToggle) { // Toggle whether to open link in a new or current tab. - setOpenLinkMode(!shouldOpenLinkHintInNewTab); - openLinkModeToggle = true; + setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue); + openLinkModeToggle = true; } var keyChar = getKeyChar(event); @@ -177,8 +186,8 @@ function onKeyDownInLinkHintsMode(event) { function onKeyUpInLinkHintsMode(event) { if (event.keyCode == keyCodes.shiftKey && openLinkModeToggle) { // Revert toggle on whether to open link in new or current tab. - setOpenLinkMode(!shouldOpenLinkHintInNewTab); - openLinkModeToggle = false; + setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue); + openLinkModeToggle = false; } event.stopPropagation(); event.preventDefault(); @@ -202,13 +211,19 @@ function updateLinkHints() { } else { // When we're opening the link in the current tab, don't navigate to the selected link immediately; // we want to give the user some feedback depicting which link they've selected by focusing it. - if (!shouldOpenLinkHintInNewTab) - setTimeout(function() { simulateClick(matchedLink); }, 400); - else + if (shouldOpenLinkHintWithQueue) { simulateClick(matchedLink); - matchedLink.focus(); + resetLinkHintsMode(); + } else if (shouldOpenLinkHintInNewTab) { + simulateClick(matchedLink); + matchedLink.focus(); + deactivateLinkHintsMode(); + } else { + setTimeout(function() { simulateClick(matchedLink); }, 400); + matchedLink.focus(); + deactivateLinkHintsMode(); + } } - deactivateLinkHintsMode(); } } @@ -289,6 +304,11 @@ function deactivateLinkHintsMode() { HUD.hide(); } +function resetLinkHintsMode() { + deactivateLinkHintsMode(); + activateLinkHintsModeWithQueue(); +} + /* * Creates a link marker for the given link. */ diff --git a/manifest.json b/manifest.json index baba4e09..878d8b5c 100644 --- a/manifest.json +++ b/manifest.json @@ -8,7 +8,10 @@ "background_page": "background_page.html", "options_page": "options.html", "permissions": [ - "tabs", "bookmarks" + "tabs", + "bookmarks", + "http://*/*", + "https://*/*" ], "content_scripts": [ { @@ -19,7 +22,8 @@ "vimiumFrontend.js", "bookmarks.js" ], - "run_at": "document_start" + "run_at": "document_start", + "all_frames": true } ] } diff --git a/vimiumFrontend.js b/vimiumFrontend.js index da850909..9e9ec87a 100644 --- a/vimiumFrontend.js +++ b/vimiumFrontend.js @@ -26,6 +26,11 @@ var linkHintCss; // TODO(philc): This should be pulled from the extension's storage when the page loads. var currentZoomLevel = 100; +/* + * Give this frame a unique id. + */ +frameId = Math.floor(Math.random()*999999999) + var hasModifiersRegex = /^<([amc]-)+.>/; function getSetting(key) { @@ -66,7 +71,10 @@ function initializePreDomReady() { if (isShowingHelpDialog) hideHelpDialog(); else - showHelpDialog(request.dialogHtml); + showHelpDialog(request.dialogHtml, request.frameId); + else if (request.name == "focusFrame") + if(frameId == request.frameId) + focusThisFrame(request.highlight); else if (request.name == "refreshCompletionKeys") refreshCompletionKeys(request.completionKeys); sendResponse({}); // Free up the resources used by this open connection. @@ -75,7 +83,7 @@ function initializePreDomReady() { chrome.extension.onConnect.addListener(function(port, name) { if (port.name == "executePageCommand") { port.onMessage.addListener(function(args) { - if (this[args.command]) { + if (this[args.command] && frameId == args.frameId) { if (args.passCountToFunction) { this[args.command].call(null, args.count); } else { @@ -138,10 +146,32 @@ function initializeWhenEnabled() { enterInsertModeIfElementIsFocused(); } + +/* + * The backend needs to know which frame has focus. + */ +window.addEventListener("focus", function(e){ + chrome.extension.sendRequest({handler: "frameFocused", frameId: frameId}); +}); + +/* + * Called from the backend in order to change frame focus. + */ +function focusThisFrame(shouldHighlight) { + window.focus(); + if (document.body && shouldHighlight) { + var borderWas = document.body.style.border; + document.body.style.border = '5px solid yellow'; + setTimeout(function(){document.body.style.border = borderWas}, 200); + } +} + /* * Initialization tasks that must wait for the document to be ready. */ function initializeOnDomReady() { + registerFrameIfSizeAvailable(window.top == window.self); + if (isEnabledForUrl) enterInsertModeIfElementIsFocused(); @@ -149,6 +179,14 @@ function initializeOnDomReady() { chrome.extension.connect({ name: "domReady" }); }; +// This is a little hacky but sometimes the size wasn't available on domReady? +function registerFrameIfSizeAvailable (top) { + if (innerWidth != undefined && innerWidth != 0 && innerHeight != undefined && innerHeight != 0) + chrome.extension.sendRequest({ handler: "registerFrame", frameId: frameId, area: innerWidth * innerHeight, top: top, total: frames.length + 1 }); + else + setTimeout(function () { registerFrameIfSizeAvailable(top); }, 100); +} + /* * Checks the currently focused element of the document and will enter insert mode if that element is focusable. */ @@ -258,9 +296,10 @@ function copyCurrentUrl() { function toggleViewSourceCallback(url) { if (url.substr(0, 12) == "view-source:") { - window.location.href = url.substr(12, url.length - 12); + url = url.substr(12, url.length - 12); } - else { window.location.href = "view-source:" + url; } + else { url = "view-source:" + url; } + chrome.extension.sendRequest({handler: "openUrlInCurrentTab", url:url}); } /** @@ -349,10 +388,10 @@ function onKeydown(event) { event.stopPropagation(); } - keyPort.postMessage(keyChar); + keyPort.postMessage({keyChar:keyChar, frameId:frameId}); } else if (isEscape(event)) { - keyPort.postMessage("<ESC>"); + keyPort.postMessage({keyChar:"<ESC>", frameId:frameId}); } } } @@ -494,8 +533,8 @@ function exitFindMode() { HUD.hide(); } -function showHelpDialog(html) { - if (isShowingHelpDialog || !document.body) +function showHelpDialog(html, fid) { + if (isShowingHelpDialog || !document.body || fid != frameId) return; isShowingHelpDialog = true; var container = document.createElement("div"); @@ -716,14 +755,8 @@ function addCssToPage(css) { head.appendChild(style); } -// Prevent our content script from being run on iframes -- only allow it to run on the top level DOM "window". -// TODO(philc): We don't want to process multiple keyhandlers etc. when embedded on a page containing IFrames. -// This should be revisited, because sometimes we *do* want to listen inside of the currently focused iframe. -var isIframe = (window.self != window.parent); -if (!isIframe) { - initializePreDomReady(); - window.addEventListener("DOMContentLoaded", initializeOnDomReady); -} +initializePreDomReady(); +window.addEventListener("DOMContentLoaded", initializeOnDomReady); window.onbeforeunload = function() { chrome.extension.sendRequest({ handler: "updateScrollPosition", |
