aboutsummaryrefslogtreecommitdiffstats
path: root/pages/help_dialog.coffee
blob: f36155e415d0587a957baef73210bb09ed016bf2 (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
130
131
132
133
134
135
136
137
138
139
$ = (id) -> document.getElementById id
$$ = (element, selector) -> element.querySelector selector

# The ordering we show key bindings is alphanumerical, except that special keys sort to the end.
compareKeys = (a,b) ->
  a = a.replace "<","~"
  b = b.replace "<", "~"
  if a < b then -1 else if b < a then 1 else 0

# This overrides the HelpDialog implementation in vimium_frontend.coffee.  We provide aliases for the two
# HelpDialog methods required by normalMode (isShowing() and toggle()).
HelpDialog =
  dialogElement: null
  isShowing: -> true

  # This setting is pulled out of local storage. It's false by default.
  getShowAdvancedCommands: -> Settings.get("helpDialog_showAdvancedCommands")

  init: ->
    return if @dialogElement?
    @dialogElement = document.getElementById "vimiumHelpDialog"

    @dialogElement.getElementsByClassName("closeButton")[0].addEventListener("click", (clickEvent) =>
        clickEvent.preventDefault()
        @hide()
      false)
    document.getElementById("helpDialogOptionsPage").addEventListener("click", (clickEvent) ->
        clickEvent.preventDefault()
        chrome.runtime.sendMessage({handler: "openOptionsPageInNewTab"})
      false)
    document.getElementById("toggleAdvancedCommands").addEventListener("click",
      HelpDialog.toggleAdvancedCommands.bind(HelpDialog), false)

    document.documentElement.addEventListener "click", (event) =>
      @hide() unless @dialogElement.contains event.target
    , false

  instantiateHtmlTemplate: (parentNode, templateId, callback) ->
    templateContent = document.querySelector(templateId).content
    node = document.importNode templateContent, true
    parentNode.appendChild node
    callback parentNode.lastElementChild

  show: ({showAllCommandDetails}) ->
    $("help-dialog-title").textContent = if showAllCommandDetails then "Command Listing" else "Help"
    $("help-dialog-version").textContent = Utils.getCurrentVersion()

    chrome.storage.local.get "helpPageData", ({helpPageData}) =>
      for own group, commands of helpPageData
        container = @dialogElement.querySelector("#help-dialog-#{group}")
        container.innerHTML = ""
        for command in commands when showAllCommandDetails or 0 < command.keys.length
          keysElement = null
          descriptionElement = null

          useTwoRows = 12 <= command.keys.join(", ").length
          unless useTwoRows
            @instantiateHtmlTemplate container, "#helpDialogEntry", (element) ->
              element.classList.add "advanced" if command.advanced
              keysElement = descriptionElement = element
          else
            @instantiateHtmlTemplate container, "#helpDialogEntryBindingsOnly", (element) ->
              element.classList.add "advanced" if command.advanced
              keysElement = element
            @instantiateHtmlTemplate container, "#helpDialogEntry", (element) ->
              element.classList.add "advanced" if command.advanced
              descriptionElement = element

          $$(descriptionElement, ".vimiumHelpDescription").textContent = command.description

          keysElement = $$(keysElement, ".vimiumKeyBindings")
          lastElement = null
          for key in command.keys.sort compareKeys
            @instantiateHtmlTemplate keysElement, "#keysTemplate", (element) ->
              lastElement = element
              $$(element, ".vimiumHelpDialogKey").textContent = key
          # And strip off the trailing ", ", if necessary.
          lastElement.removeChild $$ lastElement, ".commaSeparator" if lastElement

          if showAllCommandDetails
            @instantiateHtmlTemplate $$(descriptionElement, ".vimiumHelpDescription"), "#commandNameTemplate", (element) ->
              commandNameElement = $$ element, ".vimiumCopyCommandNameName"
              commandNameElement.textContent = command.command
              commandNameElement.title = "Click to copy \"#{command.command}\" to clipboard."
              commandNameElement.addEventListener "click", ->
                chrome.runtime.sendMessage handler: "copyToClipboard", data: commandNameElement.textContent
                HUD.showForDuration("Yanked #{commandNameElement.textContent}.", 2000)

      @showAdvancedCommands(@getShowAdvancedCommands())

      # "Click" the dialog element (so that it becomes scrollable).
      DomUtils.simulateClick @dialogElement

  hide: -> UIComponentServer.hide()
  toggle: -> @hide()

  #
  # Advanced commands are hidden by default so they don't overwhelm new and casual users.
  #
  toggleAdvancedCommands: (event) ->
    vimiumHelpDialogContainer = $ "vimiumHelpDialogContainer"
    scrollHeightBefore = vimiumHelpDialogContainer.scrollHeight
    event.preventDefault()
    showAdvanced = HelpDialog.getShowAdvancedCommands()
    HelpDialog.showAdvancedCommands(!showAdvanced)
    Settings.set("helpDialog_showAdvancedCommands", !showAdvanced)
    # Try to keep the "show advanced commands" button in the same scroll position.
    scrollHeightDelta = vimiumHelpDialogContainer.scrollHeight - scrollHeightBefore
    vimiumHelpDialogContainer.scrollTop += scrollHeightDelta if 0 < scrollHeightDelta

  showAdvancedCommands: (visible) ->
    document.getElementById("toggleAdvancedCommands").textContent =
      if visible then "Hide advanced commands" else "Show advanced commands"

    # Add/remove the showAdvanced class to show/hide advanced commands.
    addOrRemove = if visible then "add" else "remove"
    HelpDialog.dialogElement.classList[addOrRemove] "showAdvanced"

UIComponentServer.registerHandler (event) ->
  switch event.data.name ? event.data
    when "hide" then HelpDialog.hide()
    when "activate"
      HelpDialog.init()
      HelpDialog.show event.data
      Frame.postMessage "registerFrame"
      # If we abandoned (see below) in a mode with a HUD indicator, then we have to reinstate it.
      Mode.setIndicator()
    when "hidden"
      # Unregister the frame, so that it's not available for `gf` or link hints.
      Frame.postMessage "unregisterFrame"
      # Abandon any HUD which might be showing within the help dialog.
      HUD.abandon()

document.addEventListener "DOMContentLoaded", ->
  DomUtils.injectUserCss() # Manually inject custom user styles.

root = exports ? window
root.HelpDialog = HelpDialog
root.isVimiumHelpDialog = true