]> git.gir.st - VimFx.git/blob - extension/lib/help.coffee
Fix #249: Support other keyboard layouts than en-US
[VimFx.git] / extension / lib / help.coffee
1 ###
2 # Copyright Anton Khodakivskiy 2012, 2013.
3 # Copyright Simon Lydell 2013, 2014.
4 #
5 # This file is part of VimFx.
6 #
7 # VimFx is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # VimFx is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with VimFx. If not, see <http://www.gnu.org/licenses/>.
19 ###
20
21 notation = require('vim-like-key-notation')
22 legacy = require('./legacy')
23 utils = require('./utils')
24 prefs = require('./prefs')
25 _ = require('./l10n')
26
27 { classes: Cc, interfaces: Ci, utils: Cu } = Components
28
29 XULDocument = Ci.nsIDOMXULDocument
30
31 CONTAINER_ID = 'VimFxHelpDialogContainer'
32
33 # coffeelint: disable=max_line_length
34
35 removeHelp = (document) ->
36 document.getElementById(CONTAINER_ID)?.remove()
37
38 injectHelp = (document, commands) ->
39 if document.documentElement
40 removeHelp(document)
41
42 type = if document instanceof XULDocument then 'box' else 'div'
43 container = utils.createElement(document, type, {id: CONTAINER_ID})
44
45 container.appendChild(utils.parseHTML(document, helpDialogHtml(commands)))
46 for element in container.getElementsByTagName('*')
47 element.classList.add('VimFxReset')
48
49 document.documentElement.appendChild(container)
50
51 container.addEventListener('click',
52 removeHandler.bind(undefined, document, commands), false)
53 container.addEventListener('click',
54 addHandler.bind(undefined, document, commands), false)
55
56 if button = document.getElementById('VimFxClose')
57 clickHandler = (event) ->
58 event.stopPropagation()
59 event.preventDefault()
60 removeHelp(document)
61 button.addEventListener('click', clickHandler, false)
62
63 promptService = Cc['@mozilla.org/embedcomp/prompt-service;1']
64 .getService(Ci.nsIPromptService)
65
66 removeHandler = (document, commands, event) ->
67 return unless event.target.classList.contains('VimFxKeyLink')
68 event.preventDefault()
69 event.stopPropagation()
70 key = event.target.getAttribute('data-key')
71 name = event.target.getAttribute('data-command')
72 if cmd = commands.reduce(((m, v) -> if (v.name == name) then v else m), null)
73 title = _('help_remove_shortcut_title')
74 text = _('help_remove_shortcut_text')
75 if promptService.confirm(document.defaultView, title, text)
76 cmd.keys(cmd.keys().filter((a) -> utils.normalizedKey(a) != key))
77 event.target.remove()
78
79 addHandler = (document, commands, event) ->
80 return unless event.target.classList.contains('VimFxAddShortcutLink')
81 event.preventDefault()
82 event.stopPropagation()
83 name = event.target.getAttribute('data-command')
84 if cmd = commands.reduce(((m, v) -> if (v.name == name) then v else m), null)
85 errorText = ''
86 loop
87 title = _('help_add_shortcut_title')
88 text = errorText + _('help_add_shortcut_text')
89 value = {value: null}
90 check = {value: null}
91 if promptService.prompt(document.defaultView, title, text, value, null, check)
92 input = value.value.trim()
93 return if input.length == 0
94 key = notation.parseSequence(input)
95 try
96 normalizedKey = utils.normalizedKey(key)
97 catch {id, context, subject}
98 if /^\s$/.test(subject) then id = 'invalid_whitespace'
99 errorText = _("error_#{ id }", context ? subject, subject) + '\n'
100 continue
101 conflicts = getConflicts(commands, normalizedKey)
102 if conflicts.length == 0 or overwriteCmd(document, conflicts, key)
103 cmd.keys(cmd.keys().concat([key]))
104 for div in document.getElementsByClassName('VimFxKeySequence')
105 if div.getAttribute('data-command') == cmd.name
106 div.insertAdjacentHTML('beforeend', hint(cmd, key))
107 break
108 return
109
110 getConflicts = (commands, value) ->
111 conflicts = []
112 for command in commands
113 conflictingKeys = []
114 for key in command.keys()
115 normalizedKey = utils.normalizedKey(key)
116 shortest = Math.min(value.length, normalizedKey.length)
117 if value[...shortest] == normalizedKey[...shortest]
118 conflictingKeys.push(key)
119 if conflictingKeys.length > 0
120 conflicts.push({command, conflictingKeys})
121 return conflicts
122
123 overwriteCmd = (document, conflicts, key) ->
124 title = _('help_add_shortcut_title')
125 conflictSummary = conflicts.map((conflict) ->
126 conflictingKeys = conflict.conflictingKeys
127 .map((key) -> key.join('')).join(' ')
128 return "#{ conflict.command.help() }: #{ conflictingKeys }"
129 ).join('\n')
130 text = """
131 #{ _('help_add_shortcut_text_overwrite', key.join('')) }
132
133 #{ conflictSummary }
134 """
135 if promptService.confirm(document.defaultView, title, text)
136 for { command, conflictingKeys } in conflicts
137 normalizedKeys = conflictingKeys.map((key) -> utils.normalizedKey(key))
138 command.keys(command.keys().filter((key) ->
139 return utils.normalizedKey(key) not in normalizedKeys
140 ))
141 for key in normalizedKeys
142 document.querySelector("a[data-key='#{ key }']").remove()
143 return true
144 else
145 return false
146
147 td = (text, klass = '') ->
148 """<td class="#{ klass }">#{ text }</td>"""
149
150 hint = (cmd, key) ->
151 normalizedKey = utils.escapeHTML(utils.normalizedKey(key))
152 displayKey = utils.escapeHTML(key.join(''))
153 """<a href="#" class="VimFxReset VimFxKeyLink" title="#{ _('help_remove_shortcut') }" \
154 data-command="#{ cmd.name }" data-key="#{ normalizedKey }">#{ displayKey }</a>"""
155
156 tr = (cmd) ->
157 hints = """
158 <div class="VimFxKeySequence" data-command="#{ cmd.name }">
159 #{ (hint(cmd, key) for key in cmd.keys()).join('\n') }
160 </div>
161 """
162 dot = '<span class="VimFxDot">&#8729;</span>'
163 a = """#{ cmd.help() }"""
164 add = """
165 <a href="#" data-command="#{ cmd.name }"
166 class="VimFxAddShortcutLink" title="#{ _('help_add_shortcut') }">&#8862;</a>
167 """
168
169 return """
170 <tr>
171 #{ td(hints) }
172 #{ td(add) }
173 #{ td(dot) }
174 #{ td(a) }
175 </tr>
176 """
177
178 table = (commands) ->
179 """
180 <table>
181 #{ (tr(cmd) for cmd in commands).join('') }
182 </table>
183 """
184
185 section = (title, commands) ->
186 """
187 <div class="VimFxSectionTitle">#{ title }</div>
188 #{ table(commands) }
189 """
190
191 helpDialogHtml = (commands) ->
192 return """
193 <div id="VimFxHelpDialog">
194 <div class="VimFxHeader">
195 <div class="VimFxTitle">
196 <span class="VimFxTitleVim">Vim</span><span class="VimFxTitleFx">Fx</span>
197 <span>#{ _('help_title') }</span>
198 </div>
199 <span class="VimFxVersion">#{ _('help_version') } #{ utils.getVersion() }</span>
200 <a class="VimFxClose" id="VimFxClose" href="#">&#10006;</a>
201 <div class="VimFxClearFix"></div>
202 <p>Did you know that you can add/remove shortucts in this dialog?</p>
203 <div class="VimFxClearFix"></div>
204 <p>Click the shortcut to remove it, and click &#8862; to add new shortcut!</p>
205 </div>
206
207 <div class="VimFxBody">
208 <div class="VimFxColumn">
209 #{ section(_('help_section_urls'), commands.filter((a) -> a.group == 'urls')) }
210 #{ section(_('help_section_nav'), commands.filter((a) -> a.group == 'nav')) }
211 </div>
212 <div class="VimFxColumn">
213 #{ section(_('help_section_tabs'), commands.filter((a) -> a.group == 'tabs')) }
214 #{ section(_('help_section_browse'), commands.filter((a) -> a.group == 'browse')) }
215 #{ section(_('help_section_misc'), commands.filter((a) -> a.group == 'misc')) }
216 </div>
217 <div class="VimFxClearFix"></div>
218 </div>
219
220 <div class="VimFxFooter">
221 <p>#{ _('help_overlapping_hints') }</p>
222 <p>
223 #{ _('help_found_bug') }
224 <a target="_blank" href="https://github.com/akhodakivskiy/VimFx/issues">
225 #{ _('help_report_bug') }
226 </a>
227 </p>
228 <p>
229 #{ _('help_enjoying') }
230 <a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/vimfx/">
231 #{ _('help_feedback') }
232 </a>
233 </p>
234 </div>
235 </div>
236 """
237
238 exports.injectHelp = injectHelp
239 exports.removeHelp = removeHelp
Imprint / Impressum