<commit_before>#
# This implements link hinting. Typing "F" will enter link-hinting mode, where all clickable items on the
# page have a hint marker displayed containing a sequence of letters. Typing those letters will select a link.
#
# In our 'default' mode, the characters we use to show link hints are a user-configurable option. By default
# they're the home row.  The CSS which is used on the link hints is also a configurable option.
#
# In 'filter' mode, our link hints are numbers, and the user can narrow down the range of possibilities by
# typing the text of the link itself.
#
# The "name" property below is a short-form name to appear in the link-hints mode's name.  It's for debug only.
#
isMac = KeyboardUtils.platform == "Mac"
OPEN_IN_CURRENT_TAB =
  name: "curr-tab"
  indicator: "Open link in current tab."
OPEN_IN_NEW_BG_TAB =
  name: "bg-tab"
  indicator: "Open link in new tab."
  clickModifiers: metaKey: isMac, ctrlKey: not isMac
OPEN_IN_NEW_FG_TAB =
  name: "fg-tab"
  indicator: "Open link in new tab and switch to it."
  clickModifiers: shiftKey: true, metaKey: isMac, ctrlKey: not isMac
OPEN_WITH_QUEUE =
  name: "queue"
  indicator: "Open multiple links in new tabs."
  clickModifiers: metaKey: isMac, ctrlKey: not isMac
COPY_LINK_URL =
  name: "link"
  indicator: "Copy link URL to Clipboard."
  linkActivator: (link) ->
    if link.href?
      chrome.runtime.sendMessage handler: "copyToClipboard", data: link.href
      url = link.href
      url = url[0..25] + "...." if 28 < url.length
      HUD.showForDuration "Yanked #{url}", 2000
    else
      HUD.showForDuration "No link to yank.", 2000
OPEN_INCOGNITO =
  name: "incognito"
  indicator: "Open link in incognito window."
  linkActivator: (link) -> chrome.runtime.sendMessage handler: 'openUrlInIncognito', url: link.href
DOWNLOAD_LINK_URL =
  name: "download"
  indicator: "Download link URL."
  clickModifiers: altKey: true, ctrlKey: false, metaKey: false

availableModes = [OPEN_IN_CURRENT_TAB, OPEN_IN_NEW_BG_TAB, OPEN_IN_NEW_FG_TAB, OPEN_WITH_QUEUE, COPY_LINK_URL,
  OPEN_INCOGNITO, DOWNLOAD_LINK_URL]

HintCoordinator =
  onExit: []

  sendMessage: (name, request = {}) ->
    request = extend request, {handler: "linkHintsMessage", name, frameId}
    chrome.runtime.sendMessage request

  activateMode: (@mode, onExit) ->
    @onExit = [onExit]
    @sendMessage "activateMode", modeIndex: availableModes.indexOf @mode

  getHints: ->
    @localHints = ClickableElements.getVisibleClickableElements()
    @sendMessage "postHints", hints: for hint, localIndex in @localHints
      {rect: hint.rect, linkText: hint.linkText, showLinkText: hint.showLinkText, localIndex, frameId}

  activateLinkHintsMode: ({hints, modeIndex, frameId: activateModeFrameId}) ->
    @onExit = [] unless frameId == activateModeFrameId
    mode = availableModes[modeIndex]
    @linkHintsMode = new LinkHintsMode hints, mode

  postKeyState: (request) ->
    @linkHintsMode.postKeyState request

  activateActiveHintMarker: ->
    @linkHintsMode.activateLink @linkHintsMode.markerMatcher.activeHintMarker

  exit: ->
    @onExit.pop()() while 0 < @onExit.length
    @linkHintsMode = null
    @localHints = null

  exitFailure: ->
    @onExit = [=> @linkHintsMode.deactivateMode()]
    @exit()

  getLocalHintMarker: (hint) ->
    if hint.frameId == frameId then @localHints[hint.localIndex] else null

LinkHints =
  activateMode: (count = 1, mode = OPEN_IN_CURRENT_TAB) ->
    if 0 < count or mode is OPEN_WITH_QUEUE
      HintCoordinator.activateMode mode, ->
        # Wait for the next tick to allow the previous mode to exit.  It might yet generate a click event,
        # which would cause our new mode to exit immediately.
        Utils.nextTick -> LinkHints.activateMode count-1, mode

  activateModeToOpenInNewTab: (count) -> @activateMode count, OPEN_IN_NEW_BG_TAB
  activateModeToOpenInNewForegroundTab: (count) -> @activateMode count, OPEN_IN_NEW_FG_TAB
  activateModeToCopyLinkUrl: (count) -> @activateMode count, COPY_LINK_URL
  activateModeWithQueue: -> @activateMode 1, OPEN_WITH_QUEUE
  activateModeToOpenIncognito: (count) -> @activateMode count, OPEN_INCOGNITO
  activateModeToDownloadLink: (count) -> @activateMode count, DOWNLOAD_LINK_URL

class LinkHintsModeBase
  hintMarkerContainingDiv: null
  # One of the enums listed at the top of this file.
  mode: undefined
  # Function that does the appropriate action on the selected link.
  linkActivator: undefined
  # The link-hints "mode" (in the key-handler, indicator sense).
  hintMode: null
  # A count of the number of Tab presses since the last non-Tab keyboard event.
  tabCount: 0

  constructor: (elements, mode = OPEN_IN_CURRENT_TAB) ->
    # we need documentElement to be ready in order to append links
    return unless document.documentElement

    # For these modes, we filter out those elements which don't have an HREF (since there's nothing we can do
    # with them).
    elements = (el for el in elements when el.element.href?) if mode in [ COPY_LINK_URL, OPEN_INCOGNITO ]

    if elements.length == 0
      HUD.showForDuration "No links to select.", 2000
      return

    hintMarkers = (@createMarkerFor(el) for el in elements)
    @markerMatcher = new (if Settings.get "filterLinkHints" then FilterHints else AlphabetHints)
    @markerMatcher.fillInMarkers hintMarkers

    @hintMode = new Mode
      name: "hint/#{mode.name}"
      indicator: false
      passInitialKeyupEvents: true
      suppressAllKeyboardEvents: true
      suppressTrailingKeyEvents: true
      exitOnEscape: true
      exitOnClick: true
      keydown: @onKeyDownInMode.bind this, hintMarkers
      keypress: @onKeyPressInMode.bind this, hintMarkers

    @hintMode.onExit (event) =>
      if event?.type == "click" or (event?.type == "keydown" and KeyboardUtils.isEscape event) or
          (event?.type == "keydown" and event.keyCode in [ keyCodes.backspace, keyCodes.deleteKey ])
        HintCoordinator.sendMessage "exitFailure"

    @setOpenLinkMode mode

    # Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
    # because some clickable elements cannot contain children, e.g. submit buttons.
    @hintMarkerContainingDiv = DomUtils.addElementList hintMarkers,
      id: "vimiumHintMarkerContainer", className: "vimiumReset"
    # Hide markers from other frames.
    @hideMarker marker for marker in hintMarkers when marker.hint.frameId != frameId
    @postKeyState = @postKeyState.bind this, hintMarkers

  setOpenLinkMode: (@mode) ->
    @hintMode.setIndicator @mode.indicator if DomUtils.isTopFrame()

  #
  # Creates a link marker for the given link.
  #
  createMarkerFor: do ->
    # This count is used to rank equal-scoring hints when sorting, thereby making JavaScript's sort stable.
    stableSortCount = 0
    (link) ->
      marker = DomUtils.createElement "div"
      marker.className = "vimiumReset internalVimiumHintMarker vimiumHintMarker"
      marker.clickableItem = link.element
      marker.stableSortCount = ++stableSortCount
      # Keep track of the original hint descriptor.  We'll need this to decide which markers to display.
      marker.hint = link
      marker.linkText = link.linkText<commit_msg>Simplify diff (again).<commit_after>#
# This implements link hinting. Typing "F" will enter link-hinting mode, where all clickable items on the
# page have a hint marker displayed containing a sequence of letters. Typing those letters will select a link.
#
# In our 'default' mode, the characters we use to show link hints are a user-configurable option. By default
# they're the home row.  The CSS which is used on the link hints is also a configurable option.
#
# In 'filter' mode, our link hints are numbers, and the user can narrow down the range of possibilities by
# typing the text of the link itself.
#
# The "name" property below is a short-form name to appear in the link-hints mode's name.  It's for debug only.
#
isMac = KeyboardUtils.platform == "Mac"
OPEN_IN_CURRENT_TAB =
  name: "curr-tab"
  indicator: "Open link in current tab."
OPEN_IN_NEW_BG_TAB =
  name: "bg-tab"
  indicator: "Open link in new tab."
  clickModifiers: metaKey: isMac, ctrlKey: not isMac
OPEN_IN_NEW_FG_TAB =
  name: "fg-tab"
  indicator: "Open link in new tab and switch to it."
  clickModifiers: shiftKey: true, metaKey: isMac, ctrlKey: not isMac
OPEN_WITH_QUEUE =
  name: "queue"
  indicator: "Open multiple links in new tabs."
  clickModifiers: metaKey: isMac, ctrlKey: not isMac
COPY_LINK_URL =
  name: "link"
  indicator: "Copy link URL to Clipboard."
  linkActivator: (link) ->
    if link.href?
      chrome.runtime.sendMessage handler: "copyToClipboard", data: link.href
      url = link.href
      url = url[0..25] + "...." if 28 < url.length
      HUD.showForDuration "Yanked #{url}", 2000
    else
      HUD.showForDuration "No link to yank.", 2000
OPEN_INCOGNITO =
  name: "incognito"
  indicator: "Open link in incognito window."
  linkActivator: (link) -> chrome.runtime.sendMessage handler: 'openUrlInIncognito', url: link.href
DOWNLOAD_LINK_URL =
  name: "download"
  indicator: "Download link URL."
  clickModifiers: altKey: true, ctrlKey: false, metaKey: false

availableModes = [OPEN_IN_CURRENT_TAB, OPEN_IN_NEW_BG_TAB, OPEN_IN_NEW_FG_TAB, OPEN_WITH_QUEUE, COPY_LINK_URL,
  OPEN_INCOGNITO, DOWNLOAD_LINK_URL]

HintCoordinator =
  onExit: []

  sendMessage: (name, request = {}) ->
    request = extend request, {handler: "linkHintsMessage", name, frameId}
    chrome.runtime.sendMessage request

  activateMode: (@mode, onExit) ->
    @onExit = [onExit]
    @sendMessage "activateMode", modeIndex: availableModes.indexOf @mode

  getHints: ->
    @localHints = ClickableElements.getVisibleClickableElements()
    @sendMessage "postHints", hints: for hint, localIndex in @localHints
      {rect: hint.rect, linkText: hint.linkText, showLinkText: hint.showLinkText, localIndex, frameId}

  activateLinkHintsMode: ({hints, modeIndex, frameId: activateModeFrameId}) ->
    @onExit = [] unless frameId == activateModeFrameId
    @linkHintsMode = new LinkHintsMode hints, availableModes[modeIndex]

  postKeyState: (request) ->
    @linkHintsMode.postKeyState request

  activateActiveHintMarker: ->
    @linkHintsMode.activateLink @linkHintsMode.markerMatcher.activeHintMarker

  exit: ->
    @onExit.pop()() while 0 < @onExit.length
    @linkHintsMode = @localHints = null

  exitFailure: ->
    @onExit = [=> @linkHintsMode.deactivateMode()]
    @exit()

  getLocalHintMarker: (hint) ->
    if hint.frameId == frameId then @localHints[hint.localIndex] else null

LinkHints =
  activateMode: (count = 1, mode = OPEN_IN_CURRENT_TAB) ->
    if 0 < count or mode is OPEN_WITH_QUEUE
      HintCoordinator.activateMode mode, ->
        # Wait for the next tick to allow the previous mode to exit.  It might yet generate a click event,
        # which would cause our new mode to exit immediately.
        Utils.nextTick -> LinkHints.activateMode count-1, mode

  activateModeToOpenInNewTab: (count) -> @activateMode count, OPEN_IN_NEW_BG_TAB
  activateModeToOpenInNewForegroundTab: (count) -> @activateMode count, OPEN_IN_NEW_FG_TAB
  activateModeToCopyLinkUrl: (count) -> @activateMode count, COPY_LINK_URL
  activateModeWithQueue: -> @activateMode 1, OPEN_WITH_QUEUE
  activateModeToOpenIncognito: (count) -> @activateMode count, OPEN_INCOGNITO
  activateModeToDownloadLink: (count) -> @activateMode count, DOWNLOAD_LINK_URL

class LinkHintsModeBase
  hintMarkerContainingDiv: null
  # One of the enums listed at the top of this file.
  mode: undefined
  # Function that does the appropriate action on the selected link.
  linkActivator: undefined
  # The link-hints "mode" (in the key-handler, indicator sense).
  hintMode: null
  # A count of the number of Tab presses since the last non-Tab keyboard event.
  tabCount: 0

  constructor: (elements, mode = OPEN_IN_CURRENT_TAB) ->
    # we need documentElement to be ready in order to append links
    return unless document.documentElement

    # For these modes, we filter out those elements which don't have an HREF (since there's nothing we can do
    # with them).
    elements = (el for el in elements when el.element.href?) if mode in [ COPY_LINK_URL, OPEN_INCOGNITO ]

    if elements.length == 0
      HUD.showForDuration "No links to select.", 2000
      return

    hintMarkers = (@createMarkerFor(el) for el in elements)
    @markerMatcher = new (if Settings.get "filterLinkHints" then FilterHints else AlphabetHints)
    @markerMatcher.fillInMarkers hintMarkers

    @hintMode = new Mode
      name: "hint/#{mode.name}"
      indicator: false
      passInitialKeyupEvents: true
      suppressAllKeyboardEvents: true
      suppressTrailingKeyEvents: true
      exitOnEscape: true
      exitOnClick: true
      keydown: @onKeyDownInMode.bind this, hintMarkers
      keypress: @onKeyPressInMode.bind this, hintMarkers

    @hintMode.onExit (event) =>
      if event?.type == "click" or (event?.type == "keydown" and KeyboardUtils.isEscape event) or
          (event?.type == "keydown" and event.keyCode in [ keyCodes.backspace, keyCodes.deleteKey ])
        HintCoordinator.sendMessage "exitFailure"

    @setOpenLinkMode mode

    # Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
    # because some clickable elements cannot contain children, e.g. submit buttons.
    @hintMarkerContainingDiv = DomUtils.addElementList hintMarkers,
      id: "vimiumHintMarkerContainer", className: "vimiumReset"
    # Hide markers from other frames.
    @hideMarker marker for marker in hintMarkers when marker.hint.frameId != frameId
    @postKeyState = @postKeyState.bind this, hintMarkers

  setOpenLinkMode: (@mode) ->
    @hintMode.setIndicator @mode.indicator if DomUtils.isTopFrame()

  #
  # Creates a link marker for the given link.
  #
  createMarkerFor: do ->
    # This count is used to rank equal-scoring hints when sorting, thereby making JavaScript's sort stable.
    stableSortCount = 0
    (link) ->
      marker = DomUtils.createElement "div"
      marker.className = "vimiumReset internalVimiumHintMarker vimiumHintMarker"
      marker.clickableItem = link.element
      marker.stableSortCount = ++stableSortCount
      # Keep track of the original hint descriptor, we'll need it to decide which markers to display.
      marker.hint = link
      marker.linkText = link.linkText
{'fraction_non_alphanumeric': 0.08727020464793618, 'fraction_numerical': 0.00346860908775581, 'mean_word_length': 3.90006798096533, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4718219', 'n_tokens_mistral': 4512, 'n_tokens_neox': 4343, 'n_words': 1434}