]> git.gir.st - VimFx.git/blob - extension/packages/help.coffee
Suppress non-hintchars key presses in hints mode
[VimFx.git] / extension / packages / help.coffee
1 utils = require 'utils'
2 prefs = require 'prefs'
3 { _ } = require 'l10n'
4
5 { classes: Cc, interfaces: Ci, utils: Cu } = Components
6
7 XULDocument = Ci.nsIDOMXULDocument
8
9 CONTAINER_ID = 'VimFxHelpDialogContainer'
10
11 removeHelp = (document) ->
12 document.getElementById(CONTAINER_ID)?.remove()
13
14 injectHelp = (document, commands) ->
15 if document.documentElement
16 removeHelp(document)
17
18 type = if document instanceof XULDocument then 'box' else 'div'
19 container = utils.createElement(document, type, {id: CONTAINER_ID})
20
21 container.appendChild(utils.parseHTML(document, helpDialogHtml(commands)))
22 for element in container.getElementsByTagName('*')
23 element.classList.add('VimFxReset')
24
25 document.documentElement.appendChild(container)
26
27 container.addEventListener('click', removeHandler.bind(undefined, document, commands), false)
28 container.addEventListener('click', addHandler.bind(undefined, document, commands), false)
29
30 if button = document.getElementById('VimFxClose')
31 clickHandler = (event) ->
32 event.stopPropagation()
33 event.preventDefault()
34 removeHelp(document)
35 button.addEventListener('click', clickHandler, false)
36
37 promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService)
38 removeHandler = (document, commands, event) ->
39 return unless event.target.classList.contains('VimFxKeyLink')
40 event.preventDefault()
41 event.stopPropagation()
42 key = event.target.getAttribute('data-key')
43 name = event.target.getAttribute('data-command')
44 if cmd = commands.reduce(((m, v) -> if (v.name == name) then v else m), null)
45 title = _('help_remove_shortcut_title')
46 text = _('help_remove_shortcut_text')
47 if promptService.confirm(document.defaultView, title, text)
48 cmd.keys(cmd.keys().filter((a) -> a != key))
49 event.target.remove()
50
51 addHandler = (document, commands, event) ->
52 return unless event.target.classList.contains('VimFxAddShortcutLink')
53 event.preventDefault()
54 event.stopPropagation()
55 name = event.target.getAttribute('data-command')
56 if cmd = commands.reduce(((m, v) -> if (v.name == name) then v else m), null)
57 title = _('help_add_shortcut_title')
58 text = _('help_add_shortcut_text')
59 value = { value: null }
60 check = { value: null }
61 if promptService.prompt(document.defaultView, title, text, value, null, check)
62 return if value.value.length == 0
63 conflicts = getConflicts(commands, value.value)
64 if conflicts.length == 0 or overwriteCmd(document, conflicts, value.value)
65 cmd.keys(cmd.keys().concat(value.value))
66 for div in document.getElementsByClassName('VimFxKeySequence')
67 if div.getAttribute('data-command') == cmd.name
68 div.insertAdjacentHTML('beforeend', hint(cmd, value.value))
69 return
70
71 getConflicts = (commands, value) ->
72 conflicts = []
73 for command in commands
74 conflictingKeys = []
75 for key in command.keys()
76 shortest = Math.min(value.length, key.length)
77 if "#{value},"[..shortest] == "#{key},"[..shortest]
78 conflictingKeys.push(key)
79 if conflictingKeys.length > 0
80 conflicts.push({ command, conflictingKeys })
81 return conflicts
82
83 overwriteCmd = (document, conflicts, key) ->
84 title = _('help_add_shortcut_title')
85 conflictSummary = conflicts.map((conflict) ->
86 return "#{ conflict.command.help() }: #{ conflict.conflictingKeys.join(' ') }"
87 ).join("\n")
88 text = """
89 #{ _('help_add_shortcut_text_overwrite', key) }
90
91 #{ conflictSummary }
92 """
93 if promptService.confirm(document.defaultView, title, text)
94 for { command, conflictingKeys } in conflicts
95 command.keys(command.keys().filter((key) -> key not in conflictingKeys))
96 for key in conflictingKeys
97 document.querySelector("a[data-key='#{key}']").remove()
98 return true
99 else
100 return false
101
102 td = (text, klass='') ->
103 """<td class="#{ klass }">#{ text }</td>"""
104
105 hint = (cmd, key) ->
106 keyDisplay = key.replace(/,/g, '')
107 """<a href="#" class="VimFxReset VimFxKeyLink" title="#{ _('help_remove_shortcut') }"
108 data-command="#{ cmd.name }" data-key="#{ key }">#{ keyDisplay }</a>"""
109
110 tr = (cmd) ->
111 hints = """
112 <div class="VimFxKeySequence" data-command="#{ cmd.name }">
113 #{ (hint(cmd, key) for key in cmd.keys()).join('\n') }
114 </div>
115 """
116 dot = """<span class="VimFxDot">&#8729;</span>"""
117 a = """#{ cmd.help() }"""
118 add = """
119 <a href="#" data-command="#{ cmd.name }"
120 class="VimFxAddShortcutLink" title="#{ _('help_add_shortcut') }">&#8862;</a>
121 """
122
123 return """
124 <tr>
125 #{ td(hints) }
126 #{ td(add) }
127 #{ td(dot) }
128 #{ td(a) }
129 </tr>
130 """
131
132 table = (commands) ->
133 """
134 <table>
135 #{ (tr(cmd) for cmd in commands).join('') }
136 </table>
137 """
138
139 section = (title, commands) ->
140 """
141 <div class="VimFxSectionTitle">#{ title }</div>
142 #{ table(commands) }
143 """
144
145 helpDialogHtml = (commands) ->
146 return """
147 <div id="VimFxHelpDialog">
148 <div class="VimFxHeader">
149 <div class="VimFxTitle">
150 <span class="VimFxTitleVim">Vim</span><span class="VimFxTitleFx">Fx</span>
151 <span>#{ _('help_title') }</span>
152 </div>
153 <span class="VimFxVersion">#{ _('help_version') } #{ utils.getVersion() }</span>
154 <a class="VimFxClose" id="VimFxClose" href="#">&#10006;</a>
155 <div class="VimFxClearFix"></div>
156 <p>Did you know that you can add/remove shortucts in this dialog?</p>
157 <div class="VimFxClearFix"></div>
158 <p>Click the shortcut to remove it, and click &#8862; to add new shortcut!</p>
159 </div>
160
161 <div class="VimFxBody">
162 <div class="VimFxColumn">
163 #{ section(_('help_section_urls'), commands.filter((a) -> a.group == 'urls')) }
164 #{ section(_('help_section_nav'), commands.filter((a) -> a.group == 'nav')) }
165 </div>
166 <div class="VimFxColumn">
167 #{ section(_('help_section_tabs'), commands.filter((a) -> a.group == 'tabs')) }
168 #{ section(_('help_section_browse'), commands.filter((a) -> a.group == 'browse')) }
169 #{ section(_('help_section_misc'), commands.filter((a) -> a.group == 'misc')) }
170 </div>
171 <div class="VimFxClearFix"></div>
172 </div>
173
174 <div class="VimFxFooter">
175 <p>#{ _('help_overlapping_hints') }</p>
176 <p>
177 #{ _('help_found_bug') }
178 <a target="_blank" href="https://github.com/akhodakivskiy/VimFx/issues">
179 #{ _('help_report_bug') }
180 </a>
181 </p>
182 <p>
183 #{ _('help_enjoying') }
184 <a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/vimfx/">
185 #{ _('help_feedback') }
186 </a>
187 </p>
188 </div>
189 </div>
190 """
191
192 exports.injectHelp = injectHelp
193 exports.removeHelp = removeHelp
Imprint / Impressum