From 51fa63a5e97167b015acf7b80d673d081c2b91da Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Tue, 24 Oct 2017 18:36:23 +0100 Subject: Move NormalMode to its own content script --- content_scripts/mode_normal.coffee | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 content_scripts/mode_normal.coffee (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee new file mode 100644 index 00000000..3e1b1ee3 --- /dev/null +++ b/content_scripts/mode_normal.coffee @@ -0,0 +1,38 @@ +class NormalMode extends KeyHandlerMode + constructor: (options = {}) -> + defaults = + name: "normal" + indicator: false # There is normally no mode indicator in normal mode. + commandHandler: @commandHandler.bind this + + super extend defaults, options + + chrome.storage.local.get "normalModeKeyStateMapping", (items) => + @setKeyMapping items.normalModeKeyStateMapping + + chrome.storage.onChanged.addListener (changes, area) => + if area == "local" and changes.normalModeKeyStateMapping?.newValue + @setKeyMapping changes.normalModeKeyStateMapping.newValue + + commandHandler: ({command: registryEntry, count}) -> + count *= registryEntry.options.count ? 1 + count = 1 if registryEntry.noRepeat + + if registryEntry.repeatLimit? and registryEntry.repeatLimit < count + return unless confirm """ + You have asked Vimium to perform #{count} repetitions of the command: #{registryEntry.description}.\n + Are you sure you want to continue?""" + + if registryEntry.topFrame + # We never return to a UI-component frame (e.g. the help dialog), it might have lost the focus. + sourceFrameId = if window.isVimiumUIComponent then 0 else frameId + chrome.runtime.sendMessage + handler: "sendMessageToFrames", message: {name: "runInTopFrame", sourceFrameId, registryEntry} + else if registryEntry.background + chrome.runtime.sendMessage {handler: "runBackgroundCommand", registryEntry, count} + else + Utils.invokeCommandString registryEntry.command, count, {registryEntry} + +root = exports ? (window.root ?= {}) +root.NormalMode = NormalMode +extend window, root unless exports? -- cgit v1.2.3 From 0b24e966c6a64864204c7f489063b29e05b2ca34 Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Tue, 24 Oct 2017 19:03:38 +0100 Subject: Move self-contained normal mode commands to normal mode file --- content_scripts/mode_normal.coffee | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 3e1b1ee3..a7a8a8b6 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -33,6 +33,75 @@ class NormalMode extends KeyHandlerMode else Utils.invokeCommandString registryEntry.command, count, {registryEntry} +NormalModeCommands = + # Scrolling. + scrollToBottom: -> + Marks.setPreviousPosition() + Scroller.scrollTo "y", "max" + scrollToTop: (count) -> + Marks.setPreviousPosition() + Scroller.scrollTo "y", (count - 1) * Settings.get("scrollStepSize") + scrollToLeft: -> Scroller.scrollTo "x", 0 + scrollToRight: -> Scroller.scrollTo "x", "max" + scrollUp: (count) -> Scroller.scrollBy "y", -1 * Settings.get("scrollStepSize") * count + scrollDown: (count) -> Scroller.scrollBy "y", Settings.get("scrollStepSize") * count + scrollPageUp: (count) -> Scroller.scrollBy "y", "viewSize", -1/2 * count + scrollPageDown: (count) -> Scroller.scrollBy "y", "viewSize", 1/2 * count + scrollFullPageUp: (count) -> Scroller.scrollBy "y", "viewSize", -1 * count + scrollFullPageDown: (count) -> Scroller.scrollBy "y", "viewSize", 1 * count + scrollLeft: (count) -> Scroller.scrollBy "x", -1 * Settings.get("scrollStepSize") * count + scrollRight: (count) -> Scroller.scrollBy "x", Settings.get("scrollStepSize") * count + + # Page state. + reload: (count, options) -> + hard = options.registryEntry.options.hard ? false + window.location.reload(hard) + goBack: (count) -> history.go(-count) + goForward: (count) -> history.go(count) + + # Url manipulation. + goUp: (count) -> + url = window.location.href + if (url[url.length - 1] == "/") + url = url.substring(0, url.length - 1) + + urlsplit = url.split("/") + # make sure we haven't hit the base domain yet + if (urlsplit.length > 3) + urlsplit = urlsplit.slice(0, Math.max(3, urlsplit.length - count)) + window.location.href = urlsplit.join('/') + + goToRoot: -> + window.location.href = window.location.origin + + toggleViewSource: -> + chrome.runtime.sendMessage { handler: "getCurrentTabUrl" }, (url) -> + if (url.substr(0, 12) == "view-source:") + url = url.substr(12, url.length - 12) + else + url = "view-source:" + url + chrome.runtime.sendMessage {handler: "openUrlInNewTab", url} + + copyCurrentUrl: -> + chrome.runtime.sendMessage { handler: "getCurrentTabUrl" }, (url) -> + chrome.runtime.sendMessage { handler: "copyToClipboard", data: url } + url = url[0..25] + "...." if 28 < url.length + HUD.showForDuration("Yanked #{url}", 2000) + + # Mode changes. + enterInsertMode: -> + # If a focusable element receives the focus, then we exit and leave the permanently-installed insert-mode + # instance to take over. + new InsertMode global: true, exitOnFocus: true + + enterVisualMode: -> + new VisualMode userLaunchedMode: true + + enterVisualLineMode: -> + new VisualLineMode userLaunchedMode: true + root = exports ? (window.root ?= {}) root.NormalMode = NormalMode +root.NormalModeCommands = NormalModeCommands +extend root, NormalModeCommands extend window, root unless exports? -- cgit v1.2.3 From 6fcfde0b561e43955ef35ea3a91ed483cedfff4f Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Tue, 24 Oct 2017 19:30:02 +0100 Subject: Move normal mode find commands into normal mode file --- content_scripts/mode_normal.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index a7a8a8b6..652adc3c 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -100,6 +100,14 @@ NormalModeCommands = enterVisualLineMode: -> new VisualLineMode userLaunchedMode: true + enterFindMode: -> + Marks.setPreviousPosition() + new FindMode() + + # Find. + performFind: (count) -> FindMode.findNext false for [0...count] by 1 + performBackwardsFind: (count) -> FindMode.findNext true for [0...count] by 1 + root = exports ? (window.root ?= {}) root.NormalMode = NormalMode root.NormalModeCommands = NormalModeCommands -- cgit v1.2.3 From ee95cb38bbd24fec444e0af3f593f470cce60d22 Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Tue, 24 Oct 2017 19:34:20 +0100 Subject: Move mainFrame and showHelp to normal mode file --- content_scripts/mode_normal.coffee | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 652adc3c..6ae82fe6 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -108,6 +108,10 @@ NormalModeCommands = performFind: (count) -> FindMode.findNext false for [0...count] by 1 performBackwardsFind: (count) -> FindMode.findNext true for [0...count] by 1 + # Misc. + mainFrame: -> focusThisFrame highlight: true, forceFocusThisFrame: true + showHelp: (sourceFrameId) -> HelpDialog.toggle {sourceFrameId, showAllCommandDetails: false} + root = exports ? (window.root ?= {}) root.NormalMode = NormalMode root.NormalModeCommands = NormalModeCommands -- cgit v1.2.3 From fe2255f4de5e17d6ab1a9168191bfbfaa488d62d Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Tue, 24 Oct 2017 19:51:25 +0100 Subject: Move passNextKey to normal mode file --- content_scripts/mode_normal.coffee | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 6ae82fe6..9a53ce76 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -33,6 +33,13 @@ class NormalMode extends KeyHandlerMode else Utils.invokeCommandString registryEntry.command, count, {registryEntry} +enterNormalMode = (count) -> + new NormalMode + indicator: "Normal mode (pass keys disabled)" + exitOnEscape: true + singleton: "enterNormalMode" + count: count + NormalModeCommands = # Scrolling. scrollToBottom: -> @@ -112,6 +119,12 @@ NormalModeCommands = mainFrame: -> focusThisFrame highlight: true, forceFocusThisFrame: true showHelp: (sourceFrameId) -> HelpDialog.toggle {sourceFrameId, showAllCommandDetails: false} + passNextKey: (count, options) -> + if options.registryEntry.options.normal + enterNormalMode count + else + new PassNextKeyMode count + root = exports ? (window.root ?= {}) root.NormalMode = NormalMode root.NormalModeCommands = NormalModeCommands -- cgit v1.2.3 From d8f8015f4cd71cf3681506a5ec16f00f8ab46f46 Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Wed, 25 Oct 2017 00:34:43 +0100 Subject: Split focusInput, move the main part into the normal mode file --- content_scripts/mode_normal.coffee | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 9a53ce76..2408e4aa 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -125,6 +125,63 @@ NormalModeCommands = else new PassNextKeyMode count + focusInput: (count) -> + # Focus the first input element on the page, and create overlays to highlight all the input elements, with + # the currently-focused element highlighted specially. Tabbing will shift focus to the next input element. + # Pressing any other key will remove the overlays and the special tab behavior. + resultSet = DomUtils.evaluateXPath textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE + visibleInputs = + for i in [0...resultSet.snapshotLength] by 1 + element = resultSet.snapshotItem i + continue unless DomUtils.getVisibleClientRect element, true + { element, rect: Rect.copy element.getBoundingClientRect() } + + if visibleInputs.length == 0 + HUD.showForDuration("There are no inputs to focus.", 1000) + return + + # This is a hack to improve usability on the Vimium options page. We prime the recently-focused input + # to be the key-mappings input. Arguably, this is the input that the user is most likely to use. + recentlyFocusedElement = lastFocusedInput() + recentlyFocusedElement ?= document.getElementById "keyMappings" if window.isVimiumOptionsPage + + selectedInputIndex = + if count == 1 + # As the starting index, we pick that of the most recently focused input element (or 0). + elements = visibleInputs.map (visibleInput) -> visibleInput.element + Math.max 0, elements.indexOf recentlyFocusedElement + else + Math.min(count, visibleInputs.length) - 1 + + hints = for tuple in visibleInputs + hint = DomUtils.createElement "div" + hint.className = "vimiumReset internalVimiumInputHint vimiumInputHint" + + # minus 1 for the border + hint.style.left = (tuple.rect.left - 1) + window.scrollX + "px" + hint.style.top = (tuple.rect.top - 1) + window.scrollY + "px" + hint.style.width = tuple.rect.width + "px" + hint.style.height = tuple.rect.height + "px" + + hint + + new FocusSelector hints, visibleInputs, selectedInputIndex + +# The types in that we consider for focusInput command. Right now this is recalculated in +# each content script. Alternatively we could calculate it once in the background page and use a request to +# fetch it each time. +# Should we include the HTML5 date pickers here? + +# The corresponding XPath for such elements. +textInputXPath = (-> + textInputTypes = [ "text", "search", "email", "url", "number", "password", "date", "tel" ] + inputElements = ["input[" + + "(" + textInputTypes.map((type) -> '@type="' + type + '"').join(" or ") + "or not(@type))" + + " and not(@disabled or @readonly)]", + "textarea", "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']"] + DomUtils.makeXPath(inputElements) +)() + root = exports ? (window.root ?= {}) root.NormalMode = NormalMode root.NormalModeCommands = NormalModeCommands -- cgit v1.2.3 From b06ed7bd5cc82ec3307aeee945bcf6bf8f9264ce Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Wed, 25 Oct 2017 01:00:18 +0100 Subject: Include LinkHints, Vomnibar and Marks commands in normal mode file --- content_scripts/mode_normal.coffee | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 2408e4aa..3ddbf294 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -167,6 +167,31 @@ NormalModeCommands = new FocusSelector hints, visibleInputs, selectedInputIndex +if LinkHints? + extend NormalModeCommands, + "LinkHints.activateMode": LinkHints.activateMode + "LinkHints.activateModeToOpenInNewTab": LinkHints.activateModeToOpenInNewTab + "LinkHints.activateModeToOpenInNewForegroundTab": LinkHints.activateModeToOpenInNewForegroundTab + "LinkHints.activateModeWithQueue": LinkHints.activateModeWithQueue + "LinkHints.activateModeToOpenIncognito": LinkHints.activateModeToOpenIncognito + "LinkHints.activateModeToDownloadLink": LinkHints.activateModeToDownloadLink + "LinkHints.activateModeToCopyLinkUrl": LinkHints.activateModeToCopyLinkUrl + +if Vomnibar? + extend NormalModeCommands, + "Vomnibar.activate": Vomnibar.activate + "Vomnibar.activateInNewTab": Vomnibar.activateInNewTab + "Vomnibar.activateTabSelection": Vomnibar.activateTabSelection + "Vomnibar.activateBookmarks": Vomnibar.activateBookmarks + "Vomnibar.activateBookmarksInNewTab": Vomnibar.activateBookmarksInNewTab + "Vomnibar.activateEditUrl": Vomnibar.activateEditUrl + "Vomnibar.activateEditUrlInNewTab": Vomnibar.activateEditUrlInNewTab + +if Marks? + extend NormalModeCommands, + "Marks.activateCreateMode": Marks.activateCreateMode + "Marks.activateGotoMode": Marks.activateGotoMode + # The types in that we consider for focusInput command. Right now this is recalculated in # each content script. Alternatively we could calculate it once in the background page and use a request to # fetch it each time. -- cgit v1.2.3 From 6cb8a82b7cf7d6930b669fae74e1751a1e87567d Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Wed, 25 Oct 2017 01:04:58 +0100 Subject: Move goPrevious and goNext to normal mode file --- content_scripts/mode_normal.coffee | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 3ddbf294..8547c9e5 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -125,6 +125,16 @@ NormalModeCommands = else new PassNextKeyMode count + goPrevious: -> + previousPatterns = Settings.get("previousPatterns") || "" + previousStrings = previousPatterns.split(",").filter( (s) -> s.trim().length ) + findAndFollowRel("prev") || findAndFollowLink(previousStrings) + + goNext: -> + nextPatterns = Settings.get("nextPatterns") || "" + nextStrings = nextPatterns.split(",").filter( (s) -> s.trim().length ) + findAndFollowRel("next") || findAndFollowLink(nextStrings) + focusInput: (count) -> # Focus the first input element on the page, and create overlays to highlight all the input elements, with # the currently-focused element highlighted specially. Tabbing will shift focus to the next input element. -- cgit v1.2.3 From 5c4aab3d4488e5d8d64aba122791f86fc9766b99 Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Sat, 28 Oct 2017 17:02:12 +0100 Subject: Bind LinkHints, Vomnibar and Marks commands appropriately --- content_scripts/mode_normal.coffee | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 8547c9e5..6db42948 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -179,28 +179,28 @@ NormalModeCommands = if LinkHints? extend NormalModeCommands, - "LinkHints.activateMode": LinkHints.activateMode - "LinkHints.activateModeToOpenInNewTab": LinkHints.activateModeToOpenInNewTab - "LinkHints.activateModeToOpenInNewForegroundTab": LinkHints.activateModeToOpenInNewForegroundTab - "LinkHints.activateModeWithQueue": LinkHints.activateModeWithQueue - "LinkHints.activateModeToOpenIncognito": LinkHints.activateModeToOpenIncognito - "LinkHints.activateModeToDownloadLink": LinkHints.activateModeToDownloadLink - "LinkHints.activateModeToCopyLinkUrl": LinkHints.activateModeToCopyLinkUrl + "LinkHints.activateMode": LinkHints.activateMode.bind LinkHints + "LinkHints.activateModeToOpenInNewTab": LinkHints.activateModeToOpenInNewTab.bind LinkHints + "LinkHints.activateModeToOpenInNewForegroundTab": LinkHints.activateModeToOpenInNewForegroundTab.bind LinkHints + "LinkHints.activateModeWithQueue": LinkHints.activateModeWithQueue.bind LinkHints + "LinkHints.activateModeToOpenIncognito": LinkHints.activateModeToOpenIncognito.bind LinkHints + "LinkHints.activateModeToDownloadLink": LinkHints.activateModeToDownloadLink.bind LinkHints + "LinkHints.activateModeToCopyLinkUrl": LinkHints.activateModeToCopyLinkUrl.bind LinkHints if Vomnibar? extend NormalModeCommands, - "Vomnibar.activate": Vomnibar.activate - "Vomnibar.activateInNewTab": Vomnibar.activateInNewTab - "Vomnibar.activateTabSelection": Vomnibar.activateTabSelection - "Vomnibar.activateBookmarks": Vomnibar.activateBookmarks - "Vomnibar.activateBookmarksInNewTab": Vomnibar.activateBookmarksInNewTab - "Vomnibar.activateEditUrl": Vomnibar.activateEditUrl - "Vomnibar.activateEditUrlInNewTab": Vomnibar.activateEditUrlInNewTab + "Vomnibar.activate": Vomnibar.activate.bind Vomnibar + "Vomnibar.activateInNewTab": Vomnibar.activateInNewTab.bind Vomnibar + "Vomnibar.activateTabSelection": Vomnibar.activateTabSelection.bind Vomnibar + "Vomnibar.activateBookmarks": Vomnibar.activateBookmarks.bind Vomnibar + "Vomnibar.activateBookmarksInNewTab": Vomnibar.activateBookmarksInNewTab.bind Vomnibar + "Vomnibar.activateEditUrl": Vomnibar.activateEditUrl.bind Vomnibar + "Vomnibar.activateEditUrlInNewTab": Vomnibar.activateEditUrlInNewTab.bind Vomnibar if Marks? extend NormalModeCommands, - "Marks.activateCreateMode": Marks.activateCreateMode - "Marks.activateGotoMode": Marks.activateGotoMode + "Marks.activateCreateMode": Marks.activateCreateMode.bind Marks + "Marks.activateGotoMode": Marks.activateGotoMode.bind Marks # The types in that we consider for focusInput command. Right now this is recalculated in # each content script. Alternatively we could calculate it once in the background page and use a request to -- cgit v1.2.3 From f65720e58e31a0218e0c176a9140520451e5dd7d Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Sat, 28 Oct 2017 17:04:37 +0100 Subject: Remove invokeCommandString, call NormalModeCommands directly --- content_scripts/mode_normal.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 6db42948..d6f2934e 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -31,7 +31,7 @@ class NormalMode extends KeyHandlerMode else if registryEntry.background chrome.runtime.sendMessage {handler: "runBackgroundCommand", registryEntry, count} else - Utils.invokeCommandString registryEntry.command, count, {registryEntry} + NormalModeCommands[registryEntry.command] count, {registryEntry} enterNormalMode = (count) -> new NormalMode -- cgit v1.2.3 From 9847144f3493547af7eb419f11552ac18d7e9872 Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Sat, 28 Oct 2017 17:09:24 +0100 Subject: Remove normal mode commands from global scope --- content_scripts/mode_normal.coffee | 1 - 1 file changed, 1 deletion(-) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index d6f2934e..02681475 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -220,5 +220,4 @@ textInputXPath = (-> root = exports ? (window.root ?= {}) root.NormalMode = NormalMode root.NormalModeCommands = NormalModeCommands -extend root, NormalModeCommands extend window, root unless exports? -- cgit v1.2.3 From ad8fa8e850eaa6ca3ef4d45e1903d11aa564cafe Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Sun, 29 Oct 2017 12:03:43 +0000 Subject: Move FocusSelector to mode_normal.coffee --- content_scripts/mode_normal.coffee | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 02681475..9a03e9d0 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -217,6 +217,44 @@ textInputXPath = (-> DomUtils.makeXPath(inputElements) )() +class FocusSelector extends Mode + constructor: (hints, visibleInputs, selectedInputIndex) -> + super + name: "focus-selector" + exitOnClick: true + keydown: (event) => + if event.key == "Tab" + hints[selectedInputIndex].classList.remove 'internalVimiumSelectedInputHint' + selectedInputIndex += hints.length + (if event.shiftKey then -1 else 1) + selectedInputIndex %= hints.length + hints[selectedInputIndex].classList.add 'internalVimiumSelectedInputHint' + DomUtils.simulateSelect visibleInputs[selectedInputIndex].element + @suppressEvent + else unless event.key == "Shift" + @exit() + # Give the new mode the opportunity to handle the event. + @restartBubbling + + @hintContainingDiv = DomUtils.addElementList hints, + id: "vimiumInputMarkerContainer" + className: "vimiumReset" + + DomUtils.simulateSelect visibleInputs[selectedInputIndex].element + if visibleInputs.length == 1 + @exit() + return + else + hints[selectedInputIndex].classList.add 'internalVimiumSelectedInputHint' + + exit: -> + super() + DomUtils.removeElement @hintContainingDiv + if document.activeElement and DomUtils.isEditable document.activeElement + new InsertMode + singleton: "post-find-mode/focus-input" + targetElement: document.activeElement + indicator: false + root = exports ? (window.root ?= {}) root.NormalMode = NormalMode root.NormalModeCommands = NormalModeCommands -- cgit v1.2.3 From 722ba99720ed946f11e5672c7fbf0f1f34337576 Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Sun, 29 Oct 2017 12:05:40 +0000 Subject: Move findAndFollow* to mode_normal.coffee --- content_scripts/mode_normal.coffee | 85 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 9a03e9d0..8dad4b63 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -217,6 +217,91 @@ textInputXPath = (-> DomUtils.makeXPath(inputElements) )() +# used by the findAndFollow* functions. +followLink = (linkElement) -> + if (linkElement.nodeName.toLowerCase() == "link") + window.location.href = linkElement.href + else + # if we can click on it, don't simply set location.href: some next/prev links are meant to trigger AJAX + # calls, like the 'more' button on GitHub's newsfeed. + linkElement.scrollIntoView() + DomUtils.simulateClick(linkElement) + +# +# Find and follow a link which matches any one of a list of strings. If there are multiple such links, they +# are prioritized for shortness, by their position in :linkStrings, how far down the page they are located, +# and finally by whether the match is exact. Practically speaking, this means we favor 'next page' over 'the +# next big thing', and 'more' over 'nextcompany', even if 'next' occurs before 'more' in :linkStrings. +# +findAndFollowLink = (linkStrings) -> + linksXPath = DomUtils.makeXPath(["a", "*[@onclick or @role='link' or contains(@class, 'button')]"]) + links = DomUtils.evaluateXPath(linksXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE) + candidateLinks = [] + + # at the end of this loop, candidateLinks will contain all visible links that match our patterns + # links lower in the page are more likely to be the ones we want, so we loop through the snapshot backwards + for i in [(links.snapshotLength - 1)..0] by -1 + link = links.snapshotItem(i) + + # ensure link is visible (we don't mind if it is scrolled offscreen) + boundingClientRect = link.getBoundingClientRect() + if (boundingClientRect.width == 0 || boundingClientRect.height == 0) + continue + computedStyle = window.getComputedStyle(link, null) + if (computedStyle.getPropertyValue("visibility") != "visible" || + computedStyle.getPropertyValue("display") == "none") + continue + + linkMatches = false + for linkString in linkStrings + if link.innerText.toLowerCase().indexOf(linkString) != -1 || + 0 <= link.value?.indexOf? linkString + linkMatches = true + break + continue unless linkMatches + + candidateLinks.push(link) + + return if (candidateLinks.length == 0) + + for link in candidateLinks + link.wordCount = link.innerText.trim().split(/\s+/).length + + # We can use this trick to ensure that Array.sort is stable. We need this property to retain the reverse + # in-page order of the links. + + candidateLinks.forEach((a,i) -> a.originalIndex = i) + + # favor shorter links, and ignore those that are more than one word longer than the shortest link + candidateLinks = + candidateLinks + .sort((a, b) -> + if (a.wordCount == b.wordCount) then a.originalIndex - b.originalIndex else a.wordCount - b.wordCount + ) + .filter((a) -> a.wordCount <= candidateLinks[0].wordCount + 1) + + for linkString in linkStrings + exactWordRegex = + if /\b/.test(linkString[0]) or /\b/.test(linkString[linkString.length - 1]) + new RegExp "\\b" + linkString + "\\b", "i" + else + new RegExp linkString, "i" + for candidateLink in candidateLinks + if exactWordRegex.test(candidateLink.innerText) || + (candidateLink.value && exactWordRegex.test(candidateLink.value)) + followLink(candidateLink) + return true + false + +findAndFollowRel = (value) -> + relTags = ["link", "a", "area"] + for tag in relTags + elements = document.getElementsByTagName(tag) + for element in elements + if (element.hasAttribute("rel") && element.rel.toLowerCase() == value) + followLink(element) + return true + class FocusSelector extends Mode constructor: (hints, visibleInputs, selectedInputIndex) -> super -- cgit v1.2.3 From 24148c709806d1631f35cd8efd3fdec4f2376f92 Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Sun, 29 Oct 2017 12:51:53 +0000 Subject: Guard against undefined DomUtils, instead of stubbing in tests --- content_scripts/mode_normal.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'content_scripts/mode_normal.coffee') diff --git a/content_scripts/mode_normal.coffee b/content_scripts/mode_normal.coffee index 8dad4b63..ee05f4b0 100644 --- a/content_scripts/mode_normal.coffee +++ b/content_scripts/mode_normal.coffee @@ -214,7 +214,7 @@ textInputXPath = (-> "(" + textInputTypes.map((type) -> '@type="' + type + '"').join(" or ") + "or not(@type))" + " and not(@disabled or @readonly)]", "textarea", "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']"] - DomUtils.makeXPath(inputElements) + DomUtils?.makeXPath(inputElements) )() # used by the findAndFollow* functions. -- cgit v1.2.3