aboutsummaryrefslogtreecommitdiffstats
path: root/content_scripts/hud.coffee
blob: b278049152e2f7553c62305d7e1c095701954e69 (plain)
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
#
# 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
  findMode: null
  abandon: -> @hudUI?.hide false

  # 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) ->
    DomUtils.documentComplete =>
      @init()
      clearTimeout(@_showForDurationTimerId)
      @hudUI.activate {name: "show", text}
      @tween.fade 1.0, 150

  showFindMode: (@findMode = null) ->
    DomUtils.documentComplete =>
      @init()
      @hudUI.activate name: "showFindMode"
      @tween.fade 1.0, 150

  search: (data) ->
    @findMode.findInPlace data.query

    # Show the number of matches in the HUD UI.
    matchCount = if FindMode.query.parsedQuery.length > 0 then FindMode.query.matchCount else 0
    showMatchText = FindMode.query.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) ->
    if @hudUI? and @tween?
      clearTimeout @_showForDurationTimerId
      @tween.stop()
      if immediate
        if updateIndicator then Mode.setIndicator() else @hudUI.hide()
      else
        @tween.fade 0, 150, => @hide true, updateIndicator

  # These parameters describe the reason find mode is exiting, and come from the HUD UI component.
  hideFindMode: ({exitEventIsEnter, exitEventIsEscape}) ->
    @findMode.checkReturnToViewPort()

    # An 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()

    if exitEventIsEnter
      handleEnterForFindMode()
      if FindMode.query.hasResults
        postExit = -> new PostFindMode
    else if exitEventIsEscape
      # We don't want FindMode to handle the click events that handleEscapeForFindMode can generate, so we
      # wait until the mode is closed before running it.
      postExit = handleEscapeForFindMode

    @findMode.exit()
    postExit?()

class Tween
  opacity: 0
  intervalId: -1
  styleElement: null

  constructor: (@cssSelector, insertionPoint = document.documentElement) ->
    @styleElement = DomUtils.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