Skip to content

Commit

Permalink
Make scrollable elements selectable with hints.
Browse files Browse the repository at this point in the history
Fixes #425.

Conflicts:
	content_scripts/scroller.coffee
  • Loading branch information
smblott-github committed Mar 28, 2016
1 parent 2a62e48 commit f49d4b2
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 12 deletions.
25 changes: 17 additions & 8 deletions content_scripts/link_hints.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ LocalHints =
isClickable = false
onlyHasTabIndex = false
visibleElements = []
reason = null

# Insert area elements that provide click functionality to an img.
if tagName == "img"
Expand Down Expand Up @@ -254,8 +255,15 @@ LocalHints =
when "label"
isClickable ||= element.control? and (@getVisibleClickable element.control).length == 0
when "body"
isClickable ||= element == document.body and not document.hasFocus() and
window.innerWidth > 3 and window.innerHeight > 3
isClickable ||=
if element == document.body and not document.hasFocus() and
window.innerWidth > 3 and window.innerHeight > 3 and
document.body?.tagName.toLowerCase() != "frameset"
reason = "Frame."
when "div", "ol", "ul"
isClickable ||=
if Scroller.isScrollableElement element
reason = "Scroll."

# Elements with tabindex are sometimes useful, but usually not. We can treat them as second class
# citizens when it improves UX, so take special note of them.
Expand All @@ -267,7 +275,7 @@ LocalHints =
if isClickable
clientRect = DomUtils.getVisibleClientRect element, true
if clientRect != null
visibleElements.push {element: element, rect: clientRect, secondClassCitizen: onlyHasTabIndex}
visibleElements.push {element: element, rect: clientRect, secondClassCitizen: onlyHasTabIndex, reason}

visibleElements

Expand Down Expand Up @@ -313,7 +321,7 @@ LocalHints =
# Subtract negativeRect from every rect in rects, and concatenate the arrays of rects that result.
rects = [].concat (rects.map (rect) -> Rect.subtract rect, negativeRect)...
if rects.length > 0
nonOverlappingElements.push {element: visibleElement.element, rect: rects[0]}
nonOverlappingElements.push extend visibleElement, rect: rects[0]
else
# Every part of the element is covered by some other element, so just insert the whole element's
# rect. Except for elements with tabIndex set (second class citizens); these are often more trouble
Expand All @@ -325,7 +333,7 @@ LocalHints =
hint.hasHref = hint.element.href? for hint in localHints
if Settings.get "filterLinkHints"
@withLabelMap (labelMap) =>
extend hint, @generateLinkText labelMap, hint.element for hint in localHints
extend hint, @generateLinkText labelMap, hint for hint in localHints
localHints

# Generate a map of input element => label text, call a callback with it.
Expand All @@ -342,7 +350,8 @@ LocalHints =
labelMap[forElement] = labelText
callback labelMap

generateLinkText: (labelMap, element) ->
generateLinkText: (labelMap, hint) ->
element = hint.element
linkText = ""
showLinkText = false
# toLowerCase is necessary as html documents return "IMG" and xhtml documents return "img"
Expand All @@ -362,8 +371,8 @@ LocalHints =
element.firstElementChild.nodeName.toLowerCase() == "img"
linkText = element.firstElementChild.alt || element.firstElementChild.title
showLinkText = true if linkText
else if element == document.body
linkText = "Frame."
else if hint.reason?
linkText = hint.reason
showLinkText = true
else
linkText = (element.textContent.trim() || element.innerHTML.trim())[...512]
Expand Down
13 changes: 9 additions & 4 deletions content_scripts/scroller.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,14 @@ doesScroll = (element, direction, amount, factor) ->
delta = getSign delta # 1 or -1
performScroll(element, direction, delta) and performScroll(element, direction, -delta)

isScrollableElement = (element, direction = "y", amount = 1, factor = 1) ->
doesScroll(element, direction, amount, factor) and shouldScroll element, direction

# From element and its parents, find the first which we should scroll and which does scroll.
findScrollableElement = (element, direction, amount, factor) ->
while element != document.body and
not (doesScroll(element, direction, amount, factor) and shouldScroll(element, direction))
element = (DomUtils.getContainingElement element) ? document.body
if element == document.body then firstScrollableElement element else element
while element != document.body and not isScrollableElement element, direction, amount, factor
element = DomUtils.getContainingElement(element) ? document.body
element

# On some pages, document.body is not scrollable. Here, we search the document for the largest visible
# element which does scroll vertically. This is used to initialize activatedElement. See #1358.
Expand Down Expand Up @@ -257,6 +259,9 @@ Scroller =
amount = getDimension(element,direction,pos) - element[scrollProperties[direction].axisName]
CoreScroller.scroll element, direction, amount

isScrollableElement: (element) ->
isScrollableElement element

# Scroll the top, bottom, left and right of element into view. The is used by visual mode to ensure the
# focus remains visible.
scrollIntoView: (element) ->
Expand Down

0 comments on commit f49d4b2

Please sign in to comment.