2 # Copyright Anton Khodakivskiy 2013, 2014.
3 # Copyright Simon Lydell 2013, 2014.
4 # Copyright Wang Zhuochun 2014.
6 # This file is part of VimFx.
8 # VimFx is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # VimFx is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with VimFx. If not, see <http://www.gnu.org/licenses/>.
22 utils = require('./utils')
23 { injectHints } = require('./hints')
24 { rotateOverlappingMarkers } = require('./marker')
25 { updateToolbarButton } = require('./button')
26 Command = require('./command')
29 , findStorage } = require('./commands')
31 { interfaces: Ci } = Components
33 XULDocument = Ci.nsIDOMXULDocument
36 onEnter: (vim, storage) ->
38 storage.commands ?= {}
40 onLeave: (vim, storage) ->
41 storage.keys.length = 0
43 onInput: (vim, storage, keyStr, event) ->
44 target = event.originalTarget
45 document = target.ownerDocument
48 utils.isTextInputElement(target) or
49 utils.isContentEditable(target) or
50 (utils.isActivatable(target) and keyStr == '<enter>') or
51 (utils.isAdjustable(target) and keyStr in [
52 '<arrowup>', '<arrowdown>', '<arrowleft>', '<arrowright>'
55 vim.rootWindow.TabView.isVisible() or
56 document.fullscreenElement or document.mozFullScreenElement
58 storage.keys.push(keyStr)
60 { match, exact, command, count } =
61 Command.searchForMatchingCommand(@commands, storage.keys)
63 if vim.state.blacklistedKeys and
64 storage.keys.join('') in vim.state.blacklistedKeys
69 if autoInsertMode and command != escapeCommand
74 command.func(vim, event, count)
75 storage.keys.length = 0
77 # Esc key is not suppressed, and passed to the browser in normal mode.
79 # - It allows for stopping the loading of the page.
80 # - It allows for closing many custom dialogs (and perhaps other things
81 # -- Esc is a very commonly used key).
82 # - It is not passed if Esc is used for `command_Esc` and we’re blurring
83 # an element. That allows for blurring an input in a custom dialog
84 # without closing the dialog too.
85 # - There are two reasons we might suppress it in other modes. If some
86 # custom dialog of a website is open, we should be able to cancel hint
87 # markers on it without closing it. Secondly, otherwise cancelling hint
88 # markers on Google causes its search bar to be focused.
89 # - It may only be suppressed in web pages, not in browser chrome. That
90 # allows for reseting the location bar when blurring it, and closing
91 # dialogs such as the “bookmark this page” dialog (<c-d>).
92 inBrowserChrome = (document instanceof XULDocument)
93 if keyStr == '<escape>' and (not autoInsertMode or inBrowserChrome)
99 storage.keys.length = 0 unless /^\d$/.test(keyStr)
106 onEnter: (vim, storage, count = null) ->
107 storage.count = count
108 updateToolbarButton(vim.rootWindow, {insertMode: true})
110 updateToolbarButton(vim.rootWindow, {insertMode: false})
111 utils.blurActiveElement(vim.window)
112 onInput: (vim, storage, keyStr) ->
115 if @commands['exit'].match(keyStr)
116 vim.enterMode('normal')
119 vim.enterMode('normal')
127 exports['text-input'] =
128 onEnter: (vim, storage, inputs = []) ->
129 storage.inputs = inputs
130 onLeave: (vim, storage) ->
131 storage.inputs = null
132 onInput: (vim, storage, keyStr) ->
134 index = inputs.indexOf(vim.window.document.activeElement)
137 vim.enterMode('normal')
139 when escapeCommand.match(keyStr)
140 utils.blurActiveElement(vim.window)
141 vim.enterMode('normal')
143 # Override the built-in shortcuts <tab> and <s-tab> to switch between
145 when keyStr == '<tab>'
147 when keyStr == '<s-tab>'
151 inputs[index %% inputs.length].select()
158 findBar = vim.rootWindow.gBrowser.getFindBar()
159 findStorage.lastSearchString = findBar._findField.value
161 onInput: (vim, storage, keyStr) ->
162 findBar = vim.rootWindow.gBrowser.getFindBar()
163 if @commands['exit'].match(keyStr)
173 onEnter: (vim, storage, filter, callback) ->
174 [ markers, container ] = injectHints(vim.rootWindow, vim.window, filter)
175 if markers.length > 0
176 storage.markers = markers
177 storage.container = container
178 storage.callback = callback
179 storage.numEnteredChars = 0
181 vim.enterMode('normal')
183 onLeave: (vim, storage) ->
184 { container } = storage
185 vim.rootWindow.setTimeout((->
191 onInput: (vim, storage, keyStr, event) ->
192 { markers, callback } = storage
195 when @commands['exit'].match(keyStr)
196 # Remove the hints immediately.
197 storage.container?.remove()
198 vim.enterMode('normal')
201 when @commands['rotate_markers_forward'].match(keyStr)
202 rotateOverlappingMarkers(markers, true)
203 when @commands['rotate_markers_backward'].match(keyStr)
204 rotateOverlappingMarkers(markers, false)
206 when @commands['delete_hint_char'].match(keyStr)
207 for marker in markers
208 switch marker.hintIndex - storage.numEnteredChars
209 when 0 then marker.deleteHintChar()
210 when -1 then marker.show()
211 storage.numEnteredChars-- unless storage.numEnteredChars == 0
214 if keyStr not in utils.getHintChars()
217 for marker in markers when marker.hintIndex == storage.numEnteredChars
218 match = marker.matchHintChar(keyStr)
219 marker.hide() unless match
220 if marker.isMatched()
221 marker.markMatched(true)
222 matchedMarkers.push(marker)
223 if matchedMarkers.length > 0
224 again = callback(matchedMarkers[0])
226 vim.rootWindow.setTimeout((->
227 marker.markMatched(false) for marker in matchedMarkers
229 marker.reset() for marker in markers
230 storage.numEnteredChars = 0
232 vim.enterMode('normal')
234 storage.numEnteredChars++
242 'rotate_markers_forward'
243 'rotate_markers_backward'
247 for modeName, mode of exports
248 commandNames = mode.commands
249 continue if not commandNames or commandNames == commands
251 for commandName in commandNames
252 name = "mode_#{ modeName }_#{ commandName }"
253 mode.commands[commandName] = new Command(null, name, null)