aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content_scripts/link_hints.coffee16
-rw-r--r--lib/dom_utils.coffee36
2 files changed, 38 insertions, 14 deletions
diff --git a/content_scripts/link_hints.coffee b/content_scripts/link_hints.coffee
index 24bd7126..2ffe818f 100644
--- a/content_scripts/link_hints.coffee
+++ b/content_scripts/link_hints.coffee
@@ -36,17 +36,6 @@ LinkHints =
#
init: ->
- #
- # Generate an XPath describing what a clickable element is.
- # The final expression will be something like "//button | //xhtml:button | ..."
- # We use translate() instead of lower-case() because Chrome only supports XPath 1.0.
- #
- clickableElementsXPath: DomUtils.makeXPath(
- ["a", "area[@href]", "textarea", "button", "select",
- "input[not(@type='hidden' or @disabled or @readonly)]",
- "*[@onclick or @tabindex or @role='link' or @role='button' or contains(@class, 'button') or " +
- "@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_BG_TAB)
activateModeToOpenInNewForegroundTab: -> @activateMode(OPEN_IN_NEW_FG_TAB)
@@ -141,13 +130,12 @@ LinkHints =
# of digits needed to enumerate all of the links on screen.
#
getVisibleClickableElements: ->
- resultSet = DomUtils.evaluateXPath(@clickableElementsXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE)
+ resultSet = DomUtils.getClickableElements()
visibleElements = []
# Find all visible clickable elements.
- for i in [0...resultSet.snapshotLength] by 1
- element = resultSet.snapshotItem(i)
+ for element in resultSet
clientRect = DomUtils.getVisibleClientRect(element, clientRect)
if (clientRect != null)
visibleElements.push({element: element, rect: clientRect})
diff --git a/lib/dom_utils.coffee b/lib/dom_utils.coffee
index a0ac0bd3..26fa9b81 100644
--- a/lib/dom_utils.coffee
+++ b/lib/dom_utils.coffee
@@ -42,6 +42,42 @@ DomUtils =
document.evaluate(xpath, document.documentElement, namespaceResolver, resultType, null)
#
+ # Returns all the clickable element children of contextNode. This also can include contextNode itself.
+ #
+ getClickableElements: (contextNode = document.documentElement) ->
+ elements = Array::slice.call(contextNode?.getElementsByTagName "*")
+ elements.unshift contextNode # Check the contextNode as well.
+ clickableElements = []
+ for element in elements
+ isClickable = false
+ tagName = element.tagName.toLowerCase()
+ isClickable = (->
+ if element.hasAttribute "onclick"
+ true
+ else if element.hasAttribute "tabindex"
+ true
+ else if element.getAttribute "role" in ["button", "link"]
+ true
+ else if element.getAttribute("class")?.toLowerCase().indexOf("button") >= 0
+ true
+ else if element.getAttribute("contentEditable")?.toLowerCase() in ["", "contentEditable", "true"]
+ true
+ else if tagName == "a"
+ true
+ else if tagName == "area"
+ element.hasAttribute "href"
+ else if (tagName == "input" and DomUtils.isSelectable element) or tagName == "textarea"
+ not (element.disabled or element.hasAttribute "readonly")
+ else if (tagName == "input" and element.getAttribute("type")?.toLowerCase() != "hidden") or
+ tagName in ["button", "select"]
+ not element.disabled
+ else
+ false
+ )()
+ clickableElements.push element if isClickable
+ clickableElements
+
+ #
# Returns the first visible clientRect of an element if it exists. Otherwise it returns null.
#
getVisibleClientRect: (element) ->