1 # This file constructs VimFx’s options UI in the Add-ons Manager.
3 defaults = require('./defaults')
4 prefs = require('./prefs')
5 prefsBulk = require('./prefs-bulk')
6 translate = require('./translate')
7 utils = require('./utils')
21 observe = (options) ->
22 observer = new Observer(options)
23 utils.observe('vimfx-options-displayed', observer)
24 utils.observe('vimfx-options-hidden', observer)
29 # Generalized observer.
31 constructor: (@options) ->
38 listen: (element, event, action) ->
39 element.addEventListener(event, action, @useCapture)
40 @listeners.push([element, event, action, @useCapture])
43 for [element, event, action, useCapture] in @listeners
44 element.removeEventListener(event, action, useCapture)
47 type: (value) -> TYPE_MAP[typeof value]
51 appendSetting: (attributes) ->
52 isControl = attributes.type == 'control'
53 setting = @document.createElement('div')
54 setting.classList.add('setting', attributes.class)
55 utils.setAttributes(setting, {
56 'data-pref': attributes.pref
57 'data-type': attributes.type
60 label = @document.createElement(if isControl then 'span' else 'label')
61 #^NOTE: can't use <label> when control has multiple buttons
63 title = @document.createElement('span')
64 title.className = 'title'
65 title.innerText = attributes.title
66 label.appendChild(title)
68 control = @document.createElement(if isControl then 'span' else 'input')
69 switch attributes.type
70 when 'bool', 'integer', 'string'
71 @setupInput(control, INPUT_MAP[attributes.type], attributes)
73 control.className = 'control'
74 label.appendChild(control)
76 setting.appendChild(label)
78 help = @document.createElement('div')
79 help.className = 'desc'
80 help.innerText = attributes.desc or ''
81 setting.appendChild(help)
83 @container.appendChild(setting)
86 setupInput: (control, type, attributes) ->
87 what = if type == 'checkbox' then 'checked' else 'value'
88 cast = if type == 'number' then (e) -> parseInt(e, 10) else (e) -> e
91 control[what] = prefs.root.get(attributes.pref)
93 control.addEventListener('input', =>
94 prefs.root.set(attributes.pref, cast(control[what]))
95 if attributes.class == 'is-shortcut'
96 @refreshShortcutErrors()
98 prefobserver = prefs.root.observe(attributes.pref, ->
99 control[what] = prefs.root.get(attributes.pref)
101 @document.defaultView.addEventListener('unload', ->
102 prefs?.root.unobserve(attributes.pref, prefobserver)
105 observe: (@document, topic, addonId) ->
107 when 'vimfx-options-displayed'
109 when 'vimfx-options-hidden'
113 @container = @document.getElementById('detail-rows')
119 # VimFx specific observer.
120 class Observer extends BaseObserver
121 constructor: (@vimfx) ->
122 super({id: @vimfx.id})
130 if @vimfx.goToCommand
131 utils.nextTick(@document.ownerGlobal, =>
132 {pref} = @vimfx.goToCommand
133 setting = @container.querySelector("[data-pref='#{pref}']")
134 setting.scrollIntoView()
135 setting.querySelector('input').select()
136 @vimfx.goToCommand = null
140 setting = @appendSetting({
142 title: translate('prefs.instructions.title')
144 'prefs.instructions.desc',
145 @vimfx.options['options.key.quote'],
146 @vimfx.options['options.key.insert_default'],
147 @vimfx.options['options.key.reset_default'],
152 setting.id = 'header'
154 href = "#{@vimfx.info.homepageURL}/tree/master/documentation#contents"
155 docsLink = @document.createElement('a')
156 docsLink.innerText = translate('prefs.documentation')
157 utils.setAttributes(docsLink, {
161 setting.querySelector('.control').appendChild(docsLink)
163 for key, fn of BUTTONS
164 button = @document.createElement('button')
165 button.innerText = translate("prefs.#{key}.label")
166 button.onclick = runWithVim.bind(null, @vimfx, fn)
167 setting.querySelector('.control').appendChild(button)
172 for key, value of defaults.options
174 pref: "#{defaults.BRANCH}#{key}"
176 title: translate("pref.#{key}.title")
177 desc: translate("pref.#{key}.desc")
182 for mode in @vimfx.getGroupedCommands()
189 for category in mode.categories
197 for {command} in category.commands
201 title: command.description
202 desc: @generateErrorMessage(command.pref)
208 generateErrorMessage: (pref) ->
209 commandErrors = @vimfx.errors[pref] ? []
210 return commandErrors.map(({id, context, subject}) ->
211 return translate("error.#{id}", context ? subject, subject)
215 # Note that `setting = event.originalTarget` does _not_ return the correct
216 # element in these listeners!
218 @listen(@container, 'keydown', (event) =>
220 isString = (input.type == 'text')
222 setting = input.closest('.setting')
223 pref = setting.getAttribute('data-pref')
224 keyString = @vimfx.stringifyKeyEvent(event)
226 # Some shortcuts only make sense for string settings. We still match
227 # those shortcuts and suppress the default behavior for _all_ types of
228 # settings for consistency. For example, pressing <c-d> in a number input
229 # (which looks like a text input) would otherwise bookmark the page, and
230 # <c-q> would close the window!
235 break unless isString
236 utils.insertText(input, keyString)
237 prefs.root.set(pref, input.value)
239 when keyString == @vimfx.options['options.key.quote']
240 break unless isString
242 # Override `<force>` commands (such as `<escape>` and `<tab>`).
243 return unless vim = @vimfx.getCurrentVim(utils.getCurrentWindow())
244 @vimfx.modes.normal.commands.quote.run({vim, count: 1})
245 when keyString == @vimfx.options['options.key.insert_default']
246 break unless isString
247 utils.insertText(input, prefs.root.default.get(pref))
248 prefs.root.set(pref, input.value)
249 when keyString == @vimfx.options['options.key.reset_default']
250 prefs.root.set(pref, null)
254 event.preventDefault()
255 @refreshShortcutErrors()
257 @listen(@container, 'blur', -> quote = false)
259 refreshShortcutErrors: ->
260 for setting in @container.getElementsByClassName('is-shortcut')
261 setting.querySelector('.desc').innerText =
262 @generateErrorMessage(setting.getAttribute('data-pref'))
265 resetAllPrefs = (vim) ->
266 vim._modal('confirm', [translate('prefs.reset.enter')], (ok) ->
269 vim.notify(translate('prefs.reset.success'))
272 exportAllPrefs = (vim) ->
273 exported = prefsBulk.exportAll()
274 if Object.keys(exported).length == 0
275 vim.notify(translate('prefs.export.none'))
277 utils.writeToClipboard(JSON.stringify(exported, null, 2))
278 vim.notify(translate('prefs.export.success'))
280 importExportedPrefs = (vim) ->
281 vim._modal('prompt', [translate('prefs.import.enter')], (input) ->
282 return if input == null or input.trim() == ''
283 result = prefsBulk.importExported(input.trim())
284 if result.errors.length == 0
285 vim.notify(translate('prefs.import.success'))
287 vim._modal('alert', [prefsBulk.createImportErrorReport(result)])
290 runWithVim = (vimfx, fn) ->
291 return unless vim = vimfx.getCurrentVim(utils.getCurrentWindow())
295 export: exportAllPrefs
296 import: importExportedPrefs