aboutsummaryrefslogtreecommitdiffstats
path: root/content_scripts/link_hints.coffee
diff options
context:
space:
mode:
Diffstat (limited to 'content_scripts/link_hints.coffee')
-rw-r--r--content_scripts/link_hints.coffee101
1 files changed, 82 insertions, 19 deletions
diff --git a/content_scripts/link_hints.coffee b/content_scripts/link_hints.coffee
index 2548abb3..751aa12c 100644
--- a/content_scripts/link_hints.coffee
+++ b/content_scripts/link_hints.coffee
@@ -110,6 +110,7 @@ HintCoordinator =
# The following messages are exchanged between frames while link-hints mode is active.
updateKeyState: (request) -> @linkHintsMode.updateKeyState request
+ rotateHints: -> @linkHintsMode.rotateHints()
setOpenLinkMode: ({modeIndex}) -> @linkHintsMode.setOpenLinkMode availableModes[modeIndex], false
activateActiveHintMarker: -> @linkHintsMode.activateLink @linkHintsMode.markerMatcher.activeHintMarker
getLocalHintMarker: (hint) -> if hint.frameId == frameId then @localHints[hint.localIndex] else null
@@ -199,26 +200,33 @@ class LinkHintsMode
#
# Creates a link marker for the given link.
#
- createMarkerFor: (desc) ->
- marker =
- if desc.frameId == frameId
- localHintDescriptor = HintCoordinator.getLocalHintMarker desc
- el = DomUtils.createElement "div"
- el.rect = localHintDescriptor.rect
- el.style.left = el.rect.left + "px"
- el.style.top = el.rect.top + "px"
- extend el,
- className: "vimiumReset internalVimiumHintMarker vimiumHintMarker"
- showLinkText: localHintDescriptor.showLinkText
- localHintDescriptor: localHintDescriptor
- else
- {}
+ createMarkerFor: do ->
+ # This is the starting z-index value; it produces z-index values which are greater than all of the other
+ # z-index values used by Vimium.
+ baseZIndex = 2140000000
+
+ (desc) ->
+ marker =
+ if desc.frameId == frameId
+ localHintDescriptor = HintCoordinator.getLocalHintMarker desc
+ el = DomUtils.createElement "div"
+ el.rect = localHintDescriptor.rect
+ el.style.left = el.rect.left + "px"
+ el.style.top = el.rect.top + "px"
+ # Each hint marker is assigned a different z-index.
+ el.style.zIndex = baseZIndex += 1
+ extend el,
+ className: "vimiumReset internalVimiumHintMarker vimiumHintMarker"
+ showLinkText: localHintDescriptor.showLinkText
+ localHintDescriptor: localHintDescriptor
+ else
+ {}
- extend marker,
- hintDescriptor: desc
- isLocalMarker: desc.frameId == frameId
- linkText: desc.linkText
- stableSortCount: ++@stableSortCount
+ extend marker,
+ hintDescriptor: desc
+ isLocalMarker: desc.frameId == frameId
+ linkText: desc.linkText
+ stableSortCount: ++@stableSortCount
# Handles <Shift> and <Ctrl>.
onKeyDownInMode: (event) ->
@@ -274,6 +282,9 @@ class LinkHintsMode
@tabCount = previousTabCount + (if event.shiftKey then -1 else 1)
@updateVisibleMarkers @tabCount
+ else if event.keyCode == keyCodes.space and @markerMatcher.shouldRotateHints event
+ HintCoordinator.sendMessage "rotateHints"
+
else
return
@@ -310,6 +321,51 @@ class LinkHintsMode
@setIndicator()
+ # Rotate the hints' z-index values so that hidden hints become visible.
+ rotateHints: do ->
+ markerOverlapsStack = (marker, stack) ->
+ for otherMarker in stack
+ return true if Rect.rectsOverlap marker.markerRect, otherMarker.markerRect
+ false
+
+ ->
+ # Get local, visible hint markers.
+ localHintMarkers = @hintMarkers.filter (marker) ->
+ marker.isLocalMarker and marker.style.display != "none"
+
+ # Fill in the markers' rects, if necessary.
+ marker.markerRect ?= marker.getClientRects()[0] for marker in localHintMarkers
+
+ # Calculate the overlapping groups of hints. This is O(n^2) in the best case and O(n^3) in the worst
+ # case.
+ stacks = []
+ for marker in localHintMarkers
+ stackForThisMarker = null
+ stacks =
+ for stack in stacks
+ markerOverlapsThisStack = markerOverlapsStack marker, stack
+ if markerOverlapsThisStack and not stackForThisMarker?
+ # We've found an existing stack for this marker.
+ stack.push marker
+ stackForThisMarker = stack
+ else if markerOverlapsThisStack and stackForThisMarker?
+ # This marker overlaps a second (or subsequent) stack; merge that stack into stackForThisMarker
+ # and discard it.
+ stackForThisMarker.push stack...
+ continue # Discard this stack.
+ else
+ stack # Keep this stack.
+ stacks.push [marker] unless stackForThisMarker?
+
+ # Rotate the z-indexes within each stack.
+ for stack in stacks
+ if 1 < stack.length
+ zIndexes = (marker.style.zIndex for marker in stack)
+ zIndexes.push zIndexes[0]
+ marker.style.zIndex = zIndexes[index + 1] for marker, index in stack
+
+ null # Prevent Coffeescript from building an unnecessary array.
+
# When only one hint remains, activate it in the appropriate way. The current frame may or may not contain
# the matched link, and may or may not have the focus. The resulting four cases are accounted for here by
# selectively pushing the appropriate HintCoordinator.onExit handlers.
@@ -420,6 +476,9 @@ class AlphabetHints
@hintKeystrokeQueue.push (if @useKeydown then keydownKeyChar else keyChar)
popKeyChar: -> @hintKeystrokeQueue.pop()
+ # For alphabet hints, <Space> always rotates the hints, regardless of modifiers.
+ shouldRotateHints: -> true
+
# Use numbers (usually) for hints, and also filter links by their text.
class FilterHints
constructor: ->
@@ -540,6 +599,10 @@ class FilterHints
# them as if their length was 100 (so, quite long).
score / Math.log 1 + (linkMarker.linkText.length || 100)
+ # For filtered hints, we require a modifier (because <Space> on its own is a token separator).
+ shouldRotateHints: (event) ->
+ event.ctrlKey or event.altKey or event.metaKey
+
#
# Make each hint character a span, so that we can highlight the typed characters as you type them.
#