2 # Copyright Anton Khodakivskiy 2012, 2013.
3 # Copyright Simon Lydell 2013, 2014.
5 # This file is part of VimFx.
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.
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.
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/>.
21 notation = require('vim-like-key-notation')
22 legacy = require('./legacy')
23 utils = require('./utils')
24 prefs = require('./prefs')
27 { classes: Cc, interfaces: Ci, utils: Cu } = Components
29 XULDocument = Ci.nsIDOMXULDocument
31 CONTAINER_ID = 'VimFxHelpDialogContainer'
33 # coffeelint: disable=max_line_length
35 removeHelp = (document) ->
36 document.getElementById(CONTAINER_ID)?.remove()
38 injectHelp = (document, commands) ->
39 if document.documentElement
42 type = if document instanceof XULDocument then 'box' else 'div'
43 container = utils.createElement(document, type, {id: CONTAINER_ID})
45 container.appendChild(utils.parseHTML(document, helpDialogHtml(commands)))
46 for element in container.getElementsByTagName('*')
47 element.classList.add('VimFxReset')
49 document.documentElement.appendChild(container)
51 container.addEventListener('click',
52 removeHandler.bind(undefined, document, commands), false)
53 container.addEventListener('click',
54 addHandler.bind(undefined, document, commands), false)
56 if button = document.getElementById('VimFxClose')
57 clickHandler = (event) ->
58 event.stopPropagation()
59 event.preventDefault()
61 button.addEventListener('click', clickHandler, false)
63 promptService = Cc['@mozilla.org/embedcomp/prompt-service;1']
64 .getService(Ci.nsIPromptService)
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))
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)
87 title = _('help_add_shortcut_title')
88 text = errorText + _('help_add_shortcut_text')
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)
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'
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))
110 getConflicts = (commands, value) ->
112 for command in commands
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})
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 }"
131 #{ _('help_add_shortcut_text_overwrite', key.join('')) }
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
141 for key in normalizedKeys
142 document.querySelector("a[data-key='#{ key }']").remove()
147 td = (text, klass = '') ->
148 """<td class="#{ klass }">#{ text }</td>"""
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>"""
158 <div class="VimFxKeySequence" data-command="#{ cmd.name }">
159 #{ (hint(cmd, key) for key in cmd.keys()).join('\n') }
162 dot = '<span class="VimFxDot">∙</span>'
163 a = """#{ cmd.help() }"""
165 <a href="#" data-command="#{ cmd.name }"
166 class="VimFxAddShortcutLink" title="#{ _('help_add_shortcut') }">⊞</a>
178 table = (commands) ->
181 #{ (tr(cmd) for cmd in commands).join('') }
185 section = (title, commands) ->
187 <div class="VimFxSectionTitle">#{ title }</div>
191 helpDialogHtml = (commands) ->
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>
199 <span class="VimFxVersion">#{ _('help_version') } #{ utils.getVersion() }</span>
200 <a class="VimFxClose" id="VimFxClose" href="#">✖</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 ⊞ to add new shortcut!</p>
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')) }
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')) }
217 <div class="VimFxClearFix"></div>
220 <div class="VimFxFooter">
221 <p>#{ _('help_overlapping_hints') }</p>
223 #{ _('help_found_bug') }
224 <a target="_blank" href="https://github.com/akhodakivskiy/VimFx/issues">
225 #{ _('help_report_bug') }
229 #{ _('help_enjoying') }
230 <a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/vimfx/">
231 #{ _('help_feedback') }
238 exports.injectHelp = injectHelp
239 exports.removeHelp = removeHelp