]> git.gir.st - VimFx.git/blob - extension/packages/events.coffee
Merge branch 'proper-modes' of git://github.com/lydell/VimFx into lydell-proper-modes
[VimFx.git] / extension / packages / events.coffee
1 utils = require 'utils'
2 keyUtils = require 'key-utils'
3 { Vim } = require 'vim'
4 { getPref } = require 'prefs'
5 { updateToolbarButton } = require 'button'
6 { unload } = require 'unload'
7 { commands
8 , escapeCommand } = require 'commands'
9 { modes } = require 'modes'
10
11 { interfaces: Ci } = Components
12
13 # Not suppressing Esc allows for stopping the loading of the page as well as closing many custom
14 # dialogs (and perhaps other things -- Esc is a very commonly used key). There are two reasons we
15 # might suppress it in other modes. If some custom dialog of a website is open, we should be able to
16 # cancel hint markers on it without closing it. Secondly, otherwise cancelling hint markers on
17 # google causes its search bar to be focused.
18 NEVER_SUPPRESS_IN_NORMAL_MODE = ['Esc']
19
20 newFunc = (window) -> new Vim({window, commands, modes, escapeCommand})
21 vimBucket = new utils.Bucket(utils.getWindowId, newFunc)
22
23 keyStrFromEvent = (event) ->
24 { ctrlKey: ctrl, metaKey: meta, altKey: alt, shiftKey: shift } = event
25
26 if !meta and !alt
27 return unless keyChar = keyUtils.keyCharFromCode(event.keyCode, shift)
28 keyStr = keyUtils.applyModifiers(keyChar, ctrl, alt, meta)
29 return keyStr
30
31 return null
32
33 # Passthrough mode is activated when VimFx should temporarily stop processing keyboard input, for
34 # example when a menu is shown.
35 passthrough = false
36 checkPassthrough = (event) ->
37 if event.target.nodeName in ['menupopup', 'panel']
38 passthrough = switch event.type
39 when 'popupshown' then true
40 when 'popuphidden' then false
41
42 suppress = false
43 keyListener = (event) ->
44 try
45 return if passthrough or getPref('disabled')
46 return unless window = utils.getEventCurrentTabWindow(event)
47 return unless vim = vimBucket.get(window)
48 return if vim.blacklisted
49
50 if event.type == 'keydown'
51 suppress = false
52
53 return unless keyStr = keyStrFromEvent(event)
54
55 # This check must be done before `vim.onInput()` below, since that call might change the mode.
56 # We are interested in the mode at the beginning of the events, not whatever it might be
57 # afterwards.
58 suppressException = (vim.mode == Vim.MODE_NORMAL and keyStr in NEVER_SUPPRESS_IN_NORMAL_MODE)
59 isEditable = utils.isElementEditable(event.originalTarget)
60
61 suppress = vim.onInput(keyStr, event, {autoInsertMode: isEditable})
62 if suppressException
63 suppress = false
64
65 if suppress
66 event.preventDefault()
67 event.stopPropagation()
68
69 catch error
70 console.log("#{ error } (in #{ event.type })\n#{ error.stack.replace(/@.+-> /g, '@') }")
71
72 removeVimFromTab = (tab, gBrowser) ->
73 return unless browser = gBrowser.getBrowserForTab(tab)
74 vimBucket.forget(browser.contentWindow)
75
76 # The following listeners are installed on every top level Chrome window
77 windowsListeners =
78 keydown: keyListener
79 keypress: keyListener
80 keyup: keyListener
81 popupshown: checkPassthrough
82 popuphidden: checkPassthrough
83
84 # When the top level window closes we should release all Vims that were
85 # associated with tabs in this window
86 DOMWindowClose: (event) ->
87 return unless { gBrowser } = event.originalTarget
88 for tab in gBrowser.tabs
89 removeVimFromTab(tab, gBrowser)
90
91 TabClose: (event) ->
92 return unless { gBrowser } = utils.getEventRootWindow(event) ? {}
93 tab = event.originalTarget
94 removeVimFromTab(tab, gBrowser)
95
96 # Update the toolbar button icon to reflect the blacklisted state
97 TabSelect: (event) ->
98 return unless window = event.originalTarget?.linkedBrowser?.contentDocument?.defaultView
99 return unless vim = vimBucket.get(window)
100 return unless rootWindow = utils.getRootWindow(window)
101 updateToolbarButton(rootWindow, {blacklisted: vim.blacklisted})
102
103 # This listener works on individual tabs within Chrome Window
104 tabsListener =
105 # Listenfor location changes and disable the extension on blacklisted urls
106 onLocationChange: (browser, webProgress, request, location) ->
107 return unless vim = vimBucket.get(browser.contentWindow)
108
109 # If the location changes when in hints mode (for example because the reload button has been
110 # clicked), we're going to end up in hints mode without any markers. So switch back to normal
111 # mode in that case.
112 if vim.mode == 'hints'
113 vim.enterNormalMode()
114
115 return unless rootWindow = utils.getRootWindow(vim.window)
116 vim.blacklisted = utils.isBlacklisted(location.spec)
117 updateToolbarButton(rootWindow, {blacklisted: vim.blacklisted})
118
119 addEventListeners = (window) ->
120 for name, listener of windowsListeners
121 window.addEventListener(name, listener, true)
122
123 window.gBrowser.addTabsProgressListener(tabsListener)
124
125 unload ->
126 for name, listener of windowsListeners
127 window.removeEventListener(name, listener, true)
128
129 window.gBrowser.removeTabsProgressListener(tabsListener)
130
131 exports.addEventListeners = addEventListeners
Imprint / Impressum