1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
  | 
#
# A heads-up-display (HUD) for showing Vimium page operations.
# Note: you cannot interact with the HUD until document.body is available.
#
HUD =
  tween: null
  hudUI: null
  _displayElement: null
  # This HUD is styled to precisely mimick the chrome HUD on Mac. Use the "has_popup_and_link_hud.html"
  # test harness to tweak these styles to match Chrome's. One limitation of our HUD display is that
  # it doesn't sit on top of horizontal scrollbars like Chrome's HUD does.
  init: ->
    @hudUI = new UIComponent "pages/hud.html", "vimiumHUDFrame", ({data}) =>
      this[data.name]? data
    @tween = new Tween "iframe.vimiumHUDFrame.vimiumUIComponentVisible", @hudUI.shadowDOM
  showForDuration: (text, duration) ->
    @show(text)
    @_showForDurationTimerId = setTimeout((=> @hide()), duration)
  show: (text) ->
    return unless @enabled()
    clearTimeout(@_showForDurationTimerId)
    @hudUI.show {name: "show", text}
    @tween.fade 1.0, 150
  showFindMode: (text = "") ->
    return unless @enabled()
    @hudUI.show {name: "showFindMode", text}
    @tween.fade 1.0, 150
  search: (data) ->
    window.scrollTo findMode.scrollX, findMode.scrollY if findMode.options.returnToViewport
    findModeQuery.rawQuery = data.query
    updateFindModeQuery()
    findMode.findInPlace()
    # Show the number of matches in the HUD UI.
    matchCount = if findModeQuery.parsedQuery.length > 0 then findModeQuery.matchCount else 0
    showMatchText = findModeQuery.rawQuery.length > 0
    @hudUI.postMessage {name: "updateMatchesCount", matchCount, showMatchText}
  # Hide the HUD.
  # If :immediate is falsy, then the HUD is faded out smoothly (otherwise it is hidden immediately).
  # If :updateIndicator is truthy, then we also refresh the mode indicator.  The only time we don't update the
  # mode indicator, is when hide() is called for the mode indicator itself.
  hide: (immediate = false, updateIndicator = true) ->
    return unless @tween?
    clearTimeout(@_showForDurationTimerId)
    @tween.stop()
    if immediate
      unless updateIndicator
        @hudUI.hide()
        @hudUI.postMessage {name: "hide"}
      Mode.setIndicator() if updateIndicator
    else
      @tween.fade 0, 150, => @hide true, updateIndicator
  hideFindMode: (data) ->
    # An element element won't receive a focus event if the search landed on it while we were in the HUD
    # iframe. To end up with the correct modes active, we create a focus/blur event manually after refocusing
    # this window.
    window.focus()
    focusNode = DomUtils.getSelectionFocusElement()
    document.activeElement?.blur()
    focusNode?.focus()
    findModeQuery.rawQuery = data.query
    @findModeKeydown data.event
  findModeKeydown: (event) ->
    window.scrollTo findMode.scrollX, findMode.scrollY if findMode.options.returnToViewport
    if event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey
      findMode.exit()
      new PostFindMode if findModeQueryHasResults
    else if event.keyCode == keyCodes.enter
      handleEnterForFindMode()
      findMode.exit()
      new PostFindMode if findModeQueryHasResults
    else if KeyboardUtils.isEscape event
      findMode.exit()
      handleEscapeForFindMode()
      new PostFindMode if findModeQueryHasResults
  isReady: do ->
    ready = false
    DomUtils.documentReady -> ready = true
    -> ready and document.body != null
  # A preference which can be toggled in the Options page. */
  enabled: -> !Settings.get("hideHud")
class Tween
  opacity: 0
  intervalId: -1
  styleElement: null
  constructor: (@cssSelector, insertionPoint = document.documentElement) ->
    @styleElement = document.createElement "style"
    unless @styleElement.style
      # We're in an XML document, so we shouldn't inject any elements. See the comment in UIComponent.
      Tween::fade = Tween::stop = Tween::updateStyle = ->
      return
    @styleElement.type = "text/css"
    @styleElement.innerHTML = ""
    insertionPoint.appendChild @styleElement
  fade: (toAlpha, duration, onComplete) ->
    clearInterval @intervalId
    startTime = (new Date()).getTime()
    fromAlpha = @opacity
    alphaStep = toAlpha - fromAlpha
    performStep = =>
      elapsed = (new Date()).getTime() - startTime
      if (elapsed >= duration)
        clearInterval @intervalId
        @updateStyle toAlpha
        onComplete?()
      else
        value = (elapsed / duration) * alphaStep + fromAlpha
        @updateStyle value
    @updateStyle @opacity
    @intervalId = setInterval performStep, 50
  stop: -> clearInterval @intervalId
  updateStyle: (@opacity) ->
    @styleElement.innerHTML = """
      #{@cssSelector} {
        opacity: #{@opacity};
      }
    """
root = exports ? window
root.HUD = HUD
  |