1 utils = require 'utils'
3 # All the following elements qualify as a link
10 # All elements that have one or more of the following properties
12 LINK_ELEMENT_PROPERTIES = [
19 "contains(@class, 'button')"
22 # Find a link that match with the patterns
23 findLinkMatchPattern = (document, patterns) ->
24 links = getLinkElements(document)
27 # filter visible links that match patterns and put in candidateLinks
28 for i in [0...links.snapshotLength] by 1
29 link = links.snapshotItem(i)
31 if isVisibleElement(link) and isElementMatchPattern(link, patterns)
32 candidateLinks.push(link)
34 return if candidateLinks.length == 0
36 for link in candidateLinks
37 link.wordCount = link.textContent.trim().split(/\s+/).length
39 # favor shorter links, links near the end of a page
40 # and ignore those that are more than one word longer than the shortest link
42 candidateLinks.sort((a, b) ->
43 if a.wordCount == b.wordCount then 1 else a.wordCount - b.wordCount
44 ).filter((a) -> a.wordCount <= candidateLinks[0].wordCount + 1)
47 for pattern in patterns
49 if /^\b|\b$/.test(pattern)
50 new RegExp('\\b' + pattern + '\\b', 'i')
52 new RegExp(pattern, 'i')
54 for candidateLink in candidateLinks
55 if exactWordRegex.test(candidateLink.textContent)
60 # Returns elements that qualify as links
61 getLinkElements = do ->
64 "*[#{ LINK_ELEMENT_PROPERTIES.join(' or ') }]"
67 return utils.getDomElements(elements)
70 # Determine if the link is visible
71 isVisibleElement = (element) ->
72 document = element.ownerDocument
73 window = document.defaultView
75 # element that isn't visible on the page
76 computedStyle = window.getComputedStyle(element, null)
77 if computedStyle.getPropertyValue('visibility') != 'visible' or
78 computedStyle.getPropertyValue('display') == 'none' or
79 computedStyle.getPropertyValue('opacity') == '0'
82 # element that has zero dimension
83 clientRect = element.getBoundingClientRect()
84 if clientRect.width == 0 or clientRect.height == 0
89 # Determine if the link has a pattern matched
90 isElementMatchPattern = (element, patterns) ->
91 for pattern in patterns
92 if element.textContent.toLowerCase().contains(pattern)
97 exports.find = findLinkMatchPattern