<commit_before>utils = require 'utils'

# All the following elements qualify as a link
LINK_ELEMENTS = [
  "a"
  "area[@href]"
  "button"
]

# All elements that have one or more of the following properties
# qualify as a link
LINK_ELEMENT_PROPERTIES = [
  "@onclick"
  "@onmousedown"
  "@onmouseup"
  "@oncommand"
  "@role='link'"
  "@role='button'"
  "contains(@class, 'button')"
]

# Find a link that match with the patterns
findLinkMatchPattern = (document, patterns) ->
  links = getLinkElements(document)
  candidateLinks = []

  # filter visible links that contain patterns and put in candidateLinks
  for i in [0...links.snapshotLength] by 1
    link = links.snapshotItem(i)

    if isVisibleElement(link) and isElementMatchPattern(link, patterns)
      candidateLinks.push(link)

  return if candidateLinks.length == 0

  for link, idx in candidateLinks
    link.firstMatch = -1
    link.wordCount = link.textContent.trim().split(/\s+/).length

  for pattern, idx in patterns
    # if the pattern is a word, wrapped it in word boundaries.
    # thus we won't match words like 'previously' to 'previous'
    exactWordRegex =
      if /^\b|\b$/.test(pattern)
        new RegExp("^#{pattern}\\b|\\b#{pattern}\\b$", 'i')
      else
        new RegExp(pattern, 'i')

    for link in candidateLinks
      if exactWordRegex.test(link.textContent)
        link.firstMatch = idx if link.firstMatch == -1

  # favor shorter links, then the pattern matched in order
  return candidateLinks.filter((a) -> a.firstMatch != -1).sort (a, b) ->
    if a.wordCount != b.wordCount
      a.wordCount - b.wordCount
    else
      a.firstMatch - b.firstMatch


# Returns elements that qualify as links
getLinkElements = do ->
  elements = [
    LINK_ELEMENTS...
    "*[#{ LINK_ELEMENT_PROPERTIES.join(' or ') }]"
  ]

  return utils.getDomElements(elements)


# Determine if the link is visible
isVisibleElement = (element) ->
  document = element.ownerDocument
  window   = document.defaultView

  # element that isn't visible on the page
  computedStyle = window.getComputedStyle(element, null)
  if computedStyle.getPropertyValue('visibility') != 'visible' or
      computedStyle.getPropertyValue('display') == 'none' or
      computedStyle.getPropertyValue('opacity') == '0'
    return false

  # element that has zero dimension
  clientRect = element.getBoundingClientRect()
  if clientRect.width == 0 or clientRect.height == 0
    return false

  return true


# Determine if the link has a pattern matched
isElementMatchPattern = (element, patterns) ->
  for pattern in patterns
    if element.textContent.toLowerCase().contains(pattern)
      return true

  return false

exports.find = findLinkMatchPattern
<commit_msg>update comments and shorten code<commit_after>utils = require 'utils'

# All the following elements qualify as a link
LINK_ELEMENTS = [
  "a"
  "area[@href]"
  "button"
]

# All elements that have one or more of the following properties
# qualify as a link
LINK_ELEMENT_PROPERTIES = [
  "@onclick"
  "@onmousedown"
  "@onmouseup"
  "@oncommand"
  "@role='link'"
  "@role='button'"
  "contains(@class, 'button')"
]

# Find an array of links that match with patterns
findLinkMatchPattern = (document, patterns) ->
  links = getLinkElements(document)
  candidateLinks = []

  # filter visible links that contain patterns and put in candidateLinks
  for i in [0...links.snapshotLength] by 1
    link = links.snapshotItem(i)

    if isVisibleElement(link) and isElementMatchPattern(link, patterns)
      link.firstMatch = -1
      link.wordCount = link.textContent.trim().split(/\s+/).length

      candidateLinks.push(link)

  return [] if candidateLinks.length == 0

  for pattern, idx in patterns
    # if the pattern is a word, it needs to be the first or last word in string,
    # and wrapped in word boundaries.
    exactWordRegex =
      if /^\b|\b$/.test(pattern)
        /// ^#{ pattern }\b | \b#{ pattern }$ ///i
      else
        /// #{ pattern } ///i

    for link in candidateLinks when exactWordRegex.test(link.textContent)
      link.firstMatch = idx if link.firstMatch == -1

  # favor shorter links, then the pattern matched in order
  return candidateLinks
    .filter((a) -> a.firstMatch != -1)
    .sort((a, b) -> a.wordCount - b.wordCount or a.firstMatch - b.firstMatch)


# Returns elements that qualify as links
getLinkElements = do ->
  elements = [
    LINK_ELEMENTS...
    "*[#{ LINK_ELEMENT_PROPERTIES.join(' or ') }]"
  ]

  return utils.getDomElements(elements)


# Determine if the link is visible
isVisibleElement = (element) ->
  document = element.ownerDocument
  window   = document.defaultView

  # element that isn't visible on the page
  computedStyle = window.getComputedStyle(element, null)
  if computedStyle.getPropertyValue('visibility') != 'visible' or
      computedStyle.getPropertyValue('display') == 'none' or
      computedStyle.getPropertyValue('opacity') == '0'
    return false

  # element that has zero dimension
  clientRect = element.getBoundingClientRect()
  if clientRect.width == 0 or clientRect.height == 0
    return false

  return true


# Determine if the link has a pattern matched
isElementMatchPattern = (element, patterns) ->
  for pattern in patterns
    if element.textContent.toLowerCase().contains(pattern)
      return true

  return false

exports.find = findLinkMatchPattern

{'fraction_non_alphanumeric': 0.10820895522388059, 'fraction_numerical': 0.0033582089552238806, 'mean_word_length': 3.44896265560166, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2745823', 'n_tokens_mistral': 1604, 'n_tokens_neox': 1586, 'n_words': 493}