diff options
Diffstat (limited to 'content_scripts')
| -rw-r--r-- | content_scripts/file_urls.css | 6 | ||||
| -rw-r--r-- | content_scripts/link_hints.coffee | 52 | ||||
| -rw-r--r-- | content_scripts/scroller.coffee | 26 | ||||
| -rw-r--r-- | content_scripts/vimium.css | 8 | ||||
| -rw-r--r-- | content_scripts/vimium_frontend.coffee | 29 | ||||
| -rw-r--r-- | content_scripts/vomnibar.coffee | 14 |
6 files changed, 88 insertions, 47 deletions
diff --git a/content_scripts/file_urls.css b/content_scripts/file_urls.css new file mode 100644 index 00000000..fd63c224 --- /dev/null +++ b/content_scripts/file_urls.css @@ -0,0 +1,6 @@ +/* Chrome file:// URLs set draggable=true for links to files (CSS selector .icon.file). This automatically + * sets -webkit-user-select: none, which disables selecting the file names and so prevents Vimium's search + * from working as expected. Here, we reset the value back to default. */ +.icon.file { + -webkit-user-select: auto !important; +} diff --git a/content_scripts/link_hints.coffee b/content_scripts/link_hints.coffee index f2479d0f..24314b26 100644 --- a/content_scripts/link_hints.coffee +++ b/content_scripts/link_hints.coffee @@ -9,7 +9,8 @@ # typing the text of the link itself. # OPEN_IN_CURRENT_TAB = {} -OPEN_IN_NEW_TAB = {} +OPEN_IN_NEW_BG_TAB = {} +OPEN_IN_NEW_FG_TAB = {} OPEN_WITH_QUEUE = {} COPY_LINK_URL = {} OPEN_INCOGNITO = {} @@ -24,7 +25,8 @@ LinkHints = delayMode: false # Handle the link hinting marker generation and matching. Must be initialized after settings have been # loaded, so that we can retrieve the option setting. - markerMatcher: undefined + getMarkerMatcher: -> + if settings.get("filterLinkHints") then filterHints else alphabetHints # lock to ensure only one instance runs at a time isActive: false @@ -32,7 +34,6 @@ LinkHints = # To be called after linkHints has been generated from linkHintsBase. # init: -> - @markerMatcher = if settings.get("filterLinkHints") then filterHints else alphabetHints # # Generate an XPath describing what a clickable element is. @@ -46,7 +47,8 @@ LinkHints = "@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']"]) # We need this as a top-level function because our command system doesn't yet support arguments. - activateModeToOpenInNewTab: -> @activateMode(OPEN_IN_NEW_TAB) + activateModeToOpenInNewTab: -> @activateMode(OPEN_IN_NEW_BG_TAB) + activateModeToOpenInNewForegroundTab: -> @activateMode(OPEN_IN_NEW_FG_TAB) activateModeToCopyLinkUrl: -> @activateMode(COPY_LINK_URL) activateModeWithQueue: -> @activateMode(OPEN_WITH_QUEUE) activateModeToOpenIncognito: -> @activateMode(OPEN_INCOGNITO) @@ -60,7 +62,8 @@ LinkHints = @isActive = true @setOpenLinkMode(mode) - hintMarkers = @markerMatcher.fillInMarkers(@createMarkerFor(el) for el in @getVisibleClickableElements()) + hintMarkers = (@createMarkerFor(el) for el in @getVisibleClickableElements()) + @getMarkerMatcher().fillInMarkers(hintMarkers) # Note(philc): Append these markers as top level children instead of as child nodes to the link itself, # because some clickable elements cannot contain children, e.g. submit buttons. This has the caveat @@ -77,15 +80,18 @@ LinkHints = }) setOpenLinkMode: (@mode) -> - if @mode is OPEN_IN_NEW_TAB or @mode is OPEN_WITH_QUEUE - if @mode is OPEN_IN_NEW_TAB + if @mode is OPEN_IN_NEW_BG_TAB or @mode is OPEN_IN_NEW_FG_TAB or @mode is OPEN_WITH_QUEUE + if @mode is OPEN_IN_NEW_BG_TAB HUD.show("Open link in new tab") + else if @mode is OPEN_IN_NEW_FG_TAB + HUD.show("Open link in new tab and switch to it") else HUD.show("Open multiple links in a new tab") @linkActivator = (link) -> # When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on # windows) to open it in a new tab if necessary. DomUtils.simulateClick(link, { + shiftKey: @mode is OPEN_IN_NEW_FG_TAB, metaKey: KeyboardUtils.platform == "Mac", ctrlKey: KeyboardUtils.platform != "Mac" }) else if @mode is COPY_LINK_URL @@ -101,9 +107,7 @@ LinkHints = url: link.href) else # OPEN_IN_CURRENT_TAB HUD.show("Open link in current tab") - # 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 time to notice which link has received focus. - @linkActivator = (link) -> setTimeout(DomUtils.simulateClick.bind(DomUtils, link), 400) + @linkActivator = (link) -> DomUtils.simulateClick.bind(DomUtils, link)() # # Creates a link marker for the given link. @@ -161,29 +165,29 @@ LinkHints = visibleElements # - # Handles shift and esc keys. The other keys are passed to markerMatcher.matchHintsByKey. + # Handles shift and esc keys. The other keys are passed to getMarkerMatcher().matchHintsByKey. # onKeyDownInMode: (hintMarkers, event) -> return if @delayMode - if (event.keyCode == keyCodes.shiftKey && @mode != COPY_LINK_URL) + if ((event.keyCode == keyCodes.shiftKey or event.keyCode == keyCodes.ctrlKey) and + (@mode == OPEN_IN_CURRENT_TAB or + @mode == OPEN_IN_NEW_BG_TAB or + @mode == OPEN_IN_NEW_FG_TAB)) # Toggle whether to open link in a new or current tab. prev_mode = @mode - @setOpenLinkMode(if @mode is OPEN_IN_CURRENT_TAB then OPEN_IN_NEW_TAB else OPEN_IN_CURRENT_TAB) + if event.keyCode == keyCodes.shiftKey + @setOpenLinkMode(if @mode is OPEN_IN_CURRENT_TAB then OPEN_IN_NEW_BG_TAB else OPEN_IN_CURRENT_TAB) - handlerStack.push({ - keyup: (event) => - return if (event.keyCode != keyCodes.shiftKey) - @setOpenLinkMode(prev_mode) if @isActive - @remove() - }) + else # event.keyCode == keyCodes.ctrlKey + @setOpenLinkMode(if @mode is OPEN_IN_NEW_FG_TAB then OPEN_IN_NEW_BG_TAB else OPEN_IN_NEW_FG_TAB) # TODO(philc): Ignore keys that have modifiers. if (KeyboardUtils.isEscape(event)) @deactivateMode() - else if (event.keyCode != keyCodes.shiftKey) - keyResult = @markerMatcher.matchHintsByKey(hintMarkers, event) + else + keyResult = @getMarkerMatcher().matchHintsByKey(hintMarkers, event) linksMatched = keyResult.linksMatched delay = keyResult.delay ? 0 if (linksMatched.length == 0) @@ -194,7 +198,7 @@ LinkHints = for marker in hintMarkers @hideMarker(marker) for matched in linksMatched - @showMarker(matched, @markerMatcher.hintKeystrokeQueue.length) + @showMarker(matched, @getMarkerMatcher().hintKeystrokeQueue.length) false # We've handled this key, so prevent propagation. # @@ -239,8 +243,8 @@ LinkHints = # deactivateMode: (delay, callback) -> deactivate = => - if (LinkHints.markerMatcher.deactivate) - LinkHints.markerMatcher.deactivate() + if (LinkHints.getMarkerMatcher().deactivate) + LinkHints.getMarkerMatcher().deactivate() if (LinkHints.hintMarkerContainingDiv) DomUtils.removeElement LinkHints.hintMarkerContainingDiv LinkHints.hintMarkerContainingDiv = null diff --git a/content_scripts/scroller.coffee b/content_scripts/scroller.coffee index 08ab14a1..f3c632b3 100644 --- a/content_scripts/scroller.coffee +++ b/content_scripts/scroller.coffee @@ -39,17 +39,17 @@ ensureScrollChange = (direction, changeFn) -> loop oldScrollValue = element[axisName] changeFn(element, axisName) + break unless (element[axisName] == oldScrollValue && element != document.body) lastElement = element # we may have an orphaned element. if so, just scroll the body element. element = element.parentElement || document.body - break unless (lastElement[axisName] == oldScrollValue && lastElement != document.body) # if the activated element has been scrolled completely offscreen, subsequent changes in its scroll # position will not provide any more visual feedback to the user. therefore we deactivate it so that # subsequent scrolls only move the parent element. rect = activatedElement.getBoundingClientRect() if (rect.bottom < 0 || rect.top > window.innerHeight || rect.right < 0 || rect.left > window.innerWidth) - activatedElement = lastElement + activatedElement = element # scroll the active element in :direction by :amount * :factor. # :factor is needed because :amount can take on string values, which scrollBy converts to element dimensions. @@ -65,12 +65,13 @@ root.scrollBy = (direction, amount, factor = 1) -> if (!activatedElement || !isRendered(activatedElement)) activatedElement = document.body - amount = getDimension activatedElement, direction, amount if Utils.isString amount - - amount *= factor - - if (amount != 0) - ensureScrollChange direction, (element, axisName) -> element[axisName] += amount + ensureScrollChange direction, (element, axisName) -> + if Utils.isString amount + elementAmount = getDimension element, direction, amount + else + elementAmount = amount + elementAmount *= factor + element[axisName] += elementAmount root.scrollTo = (direction, pos) -> return unless document.body @@ -78,9 +79,12 @@ root.scrollTo = (direction, pos) -> if (!activatedElement || !isRendered(activatedElement)) activatedElement = document.body - pos = getDimension activatedElement, direction, pos if Utils.isString pos - - ensureScrollChange direction, (element, axisName) -> element[axisName] = pos + ensureScrollChange direction, (element, axisName) -> + if Utils.isString pos + elementPos = getDimension element, direction, pos + else + elementPos = pos + element[axisName] = elementPos # TODO refactor and put this together with the code in getVisibleClientRect isRendered = (element) -> diff --git a/content_scripts/vimium.css b/content_scripts/vimium.css index ccbcb339..a63fc3a5 100644 --- a/content_scripts/vimium.css +++ b/content_scripts/vimium.css @@ -175,14 +175,14 @@ div#vimiumHelpDialog div.advanced { display: none; } div#vimiumHelpDialog div.advanced td:nth-of-type(3) { color: #555; } div#vimiumHelpDialog a.closeButton { position:absolute; - right:10px; + right:7px; top:5px; font-family:"courier new"; font-weight:bold; color:#555; text-decoration:none; padding-left:10px; - font-size:16px; + font-size:20px; } div#vimiumHelpDialog a { text-decoration: underline; @@ -296,6 +296,8 @@ body.vimiumFindMode ::selection { } #vomnibar input { + color: #000; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 20px; height: 34px; margin-bottom: 0; @@ -332,6 +334,8 @@ body.vimiumFindMode ::selection { font-size: 16px; color: black; position: relative; + display: list-item; + margin: auto; } #vomnibar li:last-of-type { diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index a2139df6..896253a6 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -8,7 +8,7 @@ window.handlerStack = new HandlerStack insertModeLock = null findMode = false -findModeQuery = { rawQuery: "" } +findModeQuery = { rawQuery: "", matchCount: 0 } findModeQueryHasResults = false findModeAnchorNode = null isShowingHelpDialog = false @@ -554,6 +554,18 @@ updateFindModeQuery = -> text = document.body.innerText findModeQuery.regexMatches = text.match(pattern) findModeQuery.activeRegexIndex = 0 + findModeQuery.matchCount = findModeQuery.regexMatches?.length + # if we are doing a basic plain string match, we still want to grep for matches of the string, so we can + # show a the number of results. We can grep on document.body.innerText, as it should be indistinguishable + # from the internal representation used by window.find. + else + # escape all special characters, so RegExp just parses the string 'as is'. + # Taken from http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex + escapeRegExp = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g + parsedNonRegexQuery = findModeQuery.parsedQuery.replace(escapeRegExp, (char) -> "\\" + char) + pattern = new RegExp(parsedNonRegexQuery, "g" + (if findModeQuery.ignoreCase then "i" else "")) + text = document.body.innerText + findModeQuery.matchCount = text.match(pattern)?.length handleKeyCharForFindMode = (keyChar) -> findModeQuery.rawQuery += keyChar @@ -799,7 +811,7 @@ findAndFollowRel = (value) -> for tag in relTags elements = document.getElementsByTagName(tag) for element in elements - if (element.hasAttribute("rel") && element.rel == value) + if (element.hasAttribute("rel") && element.rel.toLowerCase() == value) followLink(element) return true @@ -815,7 +827,7 @@ window.goNext = -> showFindModeHUDForQuery = -> if (findModeQueryHasResults || findModeQuery.parsedQuery.length == 0) - HUD.show("/" + findModeQuery.rawQuery) + HUD.show("/" + findModeQuery.rawQuery + " (" + findModeQuery.matchCount + " Matches)") else HUD.show("/" + findModeQuery.rawQuery + " (No Matches)") @@ -839,7 +851,7 @@ window.showHelpDialog = (html, fid) -> container.innerHTML = html container.getElementsByClassName("closeButton")[0].addEventListener("click", hideHelpDialog, false) - + VimiumHelpDialog = # This setting is pulled out of local storage. It's false by default. getShowAdvancedCommands: -> settings.get("helpDialog_showAdvancedCommands") @@ -869,8 +881,9 @@ window.showHelpDialog = (html, fid) -> VimiumHelpDialog.init() - container.getElementsByClassName("optionsPage")[0].addEventListener("click", - -> chrome.runtime.sendMessage({ handler: "openOptionsPageInNewTab" }) + container.getElementsByClassName("optionsPage")[0].addEventListener("click", (clickEvent) -> + clickEvent.preventDefault() + chrome.runtime.sendMessage({handler: "openOptionsPageInNewTab"}) false) @@ -908,7 +921,7 @@ HUD = show: (text) -> return unless HUD.enabled() clearTimeout(HUD._showForDurationTimerId) - HUD.displayElement().innerHTML = text + HUD.displayElement().innerText = text clearInterval(HUD._tweenId) HUD._tweenId = Tween.fade(HUD.displayElement(), 1.0, 150) HUD.displayElement().style.display = "" @@ -917,7 +930,7 @@ HUD = HUD.upgradeNotificationElement().innerHTML = "Vimium has been updated to <a class='vimiumReset' href='https://chrome.google.com/extensions/detail/dbepggeogbaibhgnhhndojpepiihcmeb'> - #{version}</a>.<a class='vimiumReset close-button' href='#'>x</a>" + #{version}</a>.<a class='vimiumReset close-button' href='#'>×</a>" links = HUD.upgradeNotificationElement().getElementsByTagName("a") links[0].addEventListener("click", HUD.onUpdateLinkClicked, false) links[1].addEventListener "click", (event) -> diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee index 6782ab28..6997d387 100644 --- a/content_scripts/vomnibar.coffee +++ b/content_scripts/vomnibar.coffee @@ -68,8 +68,18 @@ class VomnibarUI @update(true) updateSelection: -> + # We have taken the option to add some global state here (previousCompletionType) to tell if a search + # item has just appeared or disappeared, if that happens we either set the initialSelectionValue to 0 or 1 + # I feel that this approach is cleaner than bubbling the state up from the suggestion level + # so we just inspect it afterwards + if @completions[0] + if @previousCompletionType != "search" && @completions[0].type == "search" + @selection = 0 + else if @previousCompletionType == "search" && @completions[0].type != "search" + @selection = -1 for i in [0...@completionList.children.length] @completionList.children[i].className = (if i == @selection then "vomnibarSelected" else "") + @previousCompletionType = @completions[0].type if @completions[0] # # Returns the user's action ("up", "down", "enter", "dismiss" or null) based on their keypress. @@ -192,8 +202,8 @@ class BackgroundCompleter filter: (query, callback) -> id = Utils.createUniqueId() - @filterPort.onMessage.addListener (msg) -> - return if (msg.id != id) + @filterPort.onMessage.addListener (msg) => + @filterPort.onMessage.removeListener(arguments.callee) # The result objects coming from the background page will be of the form: # { html: "", type: "", url: "" } # type will be one of [tab, bookmark, history, domain]. |
