2 # Copyright Simon Lydell 2015, 2016.
4 # This file is part of VimFx.
6 # VimFx is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # VimFx is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with VimFx. If not, see <http://www.gnu.org/licenses/>.
20 # This file constructs VimFx’s options UI in the Add-ons Manager.
22 defaults = require('./defaults')
23 translate = require('./l10n')
24 prefs = require('./prefs')
25 utils = require('./utils')
32 observe = (options) ->
33 observer = new Observer(options)
34 utils.observe('addon-options-displayed', observer)
35 utils.observe('addon-options-hidden', observer)
40 # Generalized observer.
42 constructor: (@options) ->
49 listen: (element, event, action) ->
50 element.addEventListener(event, action, @useCapture)
51 @listeners.push([element, event, action, @useCapture])
54 for [element, event, action, useCapture] in @listeners
55 element.removeEventListener(event, action, useCapture)
58 type: (value) -> TYPE_MAP[typeof value]
62 appendSetting: (attributes) ->
63 setting = @document.createElement('setting')
64 utils.setAttributes(setting, attributes)
65 @container.appendChild(setting)
68 observe: (@document, topic, addonId) ->
69 return unless addonId == @options.id
71 when 'addon-options-displayed'
73 when 'addon-options-hidden'
77 @container = @document.getElementById('detail-rows')
83 # VimFx specific observer.
84 class Observer extends BaseObserver
85 constructor: (@vimfx) ->
86 super({id: @vimfx.id})
96 utils.nextTick(@document.ownerGlobal, =>
97 {pref} = @vimfx.goToCommand
98 setting = @container.querySelector("setting[pref='#{pref}']")
99 setting.scrollIntoView()
100 setting.input.select()
101 @vimfx.goToCommand = null
104 injectInstructions: ->
105 setting = @appendSetting({
107 title: translate('prefs.instructions.title')
108 desc: translate('prefs.instructions.desc',
109 @vimfx.options['options.key.quote'],
110 @vimfx.options['options.key.insert_default'],
111 @vimfx.options['options.key.reset_default'],
115 href = "#{@vimfx.info.homepageURL}/tree/master/documentation"
116 docsLink = @document.createElement('label')
117 utils.setAttributes(docsLink, {
118 value: translate('prefs.documentation')
123 setting.appendChild(docsLink)
126 for key, value of defaults.options
127 setting = @appendSetting({
128 pref: "#{defaults.BRANCH}#{key}"
130 title: translate("pref.#{key}.title")
131 desc: translate("pref.#{key}.desc")
136 for mode in @vimfx.getGroupedCommands()
143 for category in mode.categories
151 for {command} in category.commands
155 title: command.description
156 desc: @generateErrorMessage(command.pref)
162 generateErrorMessage: (pref) ->
163 commandErrors = @vimfx.errors[pref] ? []
164 return commandErrors.map(({id, context, subject}) ->
165 return translate("error.#{id}", context ? subject, subject)
169 # Note that `setting = event.originalTarget` does _not_ return the correct
170 # element in these listeners!
172 @listen(@container, 'keydown', (event) =>
173 setting = event.target
174 isString = (setting.type == 'string')
176 {input, pref} = setting
177 keyString = @vimfx.stringifyKeyEvent(event)
179 # Some shortcuts only make sense for string settings. We still match
180 # those shortcuts and suppress the default behavior for _all_ types of
181 # settings for consistency. For example, pressing <c-d> in a number input
182 # (which looks like a text input) would otherwise bookmark the page, and
183 # <c-q> would close the window!
188 break unless isString
189 utils.insertText(input, keyString)
191 when keyString == @vimfx.options['options.key.quote']
192 break unless isString
194 # Override `<force>` commands (such as `<escape>` and `<tab>`).
195 return unless vim = @vimfx.getCurrentVim(utils.getCurrentWindow())
196 @vimfx.modes.normal.commands.quote.run({vim, count: 1})
197 when keyString == @vimfx.options['options.key.insert_default']
198 break unless isString
199 utils.insertText(input, prefs.root.default.get(pref))
200 when keyString == @vimfx.options['options.key.reset_default']
201 prefs.root.set(pref, null)
205 event.preventDefault()
206 setting.valueToPreference()
207 @refreshShortcutErrors()
209 @listen(@container, 'blur', -> quote = false)
212 @listen(@container, 'input', (event) =>
213 setting = event.target
214 # Disable default behavior of updating the pref of the setting on each
215 # input. Do it on the 'change' event instead (see below), because all
216 # settings are validated and auto-adjusted as soon as the pref changes.
217 event.stopPropagation()
218 if setting.classList.contains('is-shortcut')
219 # However, for the shortcuts we _do_ want live validation, because they
220 # cannot be auto-adjusted. Instead an error message is shown.
221 setting.valueToPreference()
222 @refreshShortcutErrors()
225 @listen(@container, 'change', (event) ->
226 setting = event.target
227 unless setting.classList.contains('is-shortcut')
228 setting.valueToPreference()
231 refreshShortcutErrors: ->
232 for setting in @container.getElementsByClassName('is-shortcut')
233 setting.setAttribute('desc', @generateErrorMessage(setting.pref))