]> git.gir.st - VimFx.git/blob - extension/packages/events.coffee
Fixed default properties
[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
8 { interfaces: Ci } = Components
9
10 HTMLDocument = Ci.nsIDOMHTMLDocument
11
12 vimBucket = new utils.Bucket(utils.getWindowId, (w) -> new Vim(w))
13
14 keyStrFromEvent = (event) ->
15 { ctrlKey: ctrl, metaKey: meta, altKey: alt, shiftKey: shift } = event
16
17 if !meta and !alt
18 return unless keyChar = keyUtils.keyCharFromCode(event.keyCode, shift)
19 keyStr = keyUtils.applyModifiers(keyChar, ctrl, alt, meta)
20 return keyStr
21
22 return null
23
24 # When a menu or panel is shown VimFx should temporarily stop processing keyboard input, allowing
25 # accesskeys to be used.
26 popupPassthrough = false
27 checkPassthrough = (event) ->
28 if event.target.nodeName in ['menupopup', 'panel']
29 popupPassthrough = switch event.type
30 when 'popupshown' then true
31 when 'popuphidden' then false
32
33 suppress = false
34 suppressEvent = (event) ->
35 event.preventDefault()
36 event.stopPropagation()
37
38 removeVimFromTab = (tab, gBrowser) ->
39 return unless browser = gBrowser.getBrowserForTab(tab)
40 vimBucket.forget(browser.contentWindow)
41
42 updateButton = (vim) ->
43 return unless rootWindow = utils.getRootWindow(vim.window)
44 updateToolbarButton(rootWindow, {blacklisted: vim.blacklisted, insertMode: vim.mode == 'insert'})
45
46 # The following listeners are installed on every top level Chrome window
47 windowsListeners =
48 keydown: (event) ->
49 try
50 # No matter what, always reset the `suppress` flag, so we don't suppress more than intended.
51 suppress = false
52
53 return if getPref('disabled')
54
55 if popupPassthrough
56 # The `popupPassthrough` flag is set a bit unreliably. Sometimes it can be stuck as `true`
57 # even though no popup is shown, effectively disabling the extension. Therefore we check
58 # if there actually _are_ any open popups before stopping processing keyboard input. This is
59 # only done when popups (might) be open (not on every keystroke) of performance reasons.
60 return unless rootWindow = utils.getEventRootWindow(event)
61 popups = rootWindow.document.querySelectorAll('menupopup, panel')
62 for popup in popups
63 return if popup.state == 'open'
64 popupPassthrough = false # No popup was actually open: Reset the flag.
65
66 return unless window = utils.getEventCurrentTabWindow(event)
67 return unless vim = vimBucket.get(window)
68
69 return if vim.blacklisted
70
71 return unless keyStr = keyStrFromEvent(event)
72 suppress = vim.onInput(keyStr, event)
73
74 suppressEvent(event) if suppress
75
76 catch error
77 console.error("#{ error }\n#{ error.stack?.replace(/@.+-> /g, '@') }")
78
79 # Note that the below event listeners can suppress the event even in blacklisted sites. That's
80 # intentional. For example, if you press 'x' to close the current tab, it will close before keyup
81 # fires. So keyup (and perhaps keypress) will fire in another tab. Even if that particular tab is
82 # blacklisted, we must suppress the event, so that 'x' isn't sent to the page. The rule is simple:
83 # If the `suppress` flag is `true`, the event should be suppressed, no matter what. It has the
84 # highest priority.
85 keypress: (event) -> suppressEvent(event) if suppress
86 keyup: (event) -> suppressEvent(event) if suppress
87
88 popupshown: checkPassthrough
89 popuphidden: checkPassthrough
90
91 focus: (event) ->
92 return if getPref('disabled') or getPref('prevent_autofocus')
93 return unless target = event.originalTarget
94 return unless target.ownerDocument instanceof HTMLDocument
95 return unless window = utils.getEventCurrentTabWindow(event)
96 return unless vim = vimBucket.get(window)
97 return unless isEditable = utils.isElementEditable(target)
98 return unless lastLoad = vim.storage.lastLoad
99
100 if (new Date().getTime() - lastLoad.getTime()) < 1000
101 console.log("blur")
102 vim.storage.lastLoad = undefined
103 window.setTimeout((-> target.blur()), 0)
104
105 DOMContentLoaded: (event) ->
106 return if getPref('disabled') or getPref('prevent_autofocus')
107 return unless window = utils.getEventCurrentTabWindow(event)
108 return unless vim = vimBucket.get(window)
109 if vim.storage.location != window.location.href
110 vim.storage.location = window.location.href
111 vim.storage.lastLoad = new Date
112
113 if vim.storage.lastLoad && activeElement = window.document.activeElement
114 console.log("blur")
115 vim.storage.lastLoad = undefined
116 window.setTimeout((-> activeElement.blur()), 0)
117
118 # When the top level window closes we should release all Vims that were
119 # associated with tabs in this window
120 DOMWindowClose: (event) ->
121 { gBrowser } = event.originalTarget
122 return unless gBrowser
123 for tab in gBrowser.tabs
124 removeVimFromTab(tab, gBrowser)
125
126 TabClose: (event) ->
127 { gBrowser } = utils.getEventRootWindow(event) ? {}
128 return unless gBrowser
129 tab = event.originalTarget
130 removeVimFromTab(tab, gBrowser)
131
132 # Update the toolbar button icon to reflect the blacklisted state
133 TabSelect: (event) ->
134 return unless window = event.originalTarget?.linkedBrowser?.contentDocument?.defaultView
135 return unless vim = vimBucket.get(window)
136 updateButton(vim)
137
138
139 # This listener works on individual tabs within Chrome Window
140 tabsListener =
141 # Listenfor location changes and disable the extension on blacklisted urls
142 onLocationChange: (browser, webProgress, request, location) ->
143 return unless vim = vimBucket.get(browser.contentWindow)
144
145 # If the location changes when in hints mode (for example because the reload button has been
146 # clicked), we're going to end up in hints mode without any markers. So switch back to normal
147 # mode in that case.
148 if vim.mode == 'hints'
149 vim.enterMode('normal')
150
151 vim.blacklisted = utils.isBlacklisted(location.spec)
152 updateButton(vim)
153
154 addEventListeners = (window) ->
155 for name, listener of windowsListeners
156 window.addEventListener(name, listener, true)
157
158 window.gBrowser.addTabsProgressListener(tabsListener)
159
160 unload ->
161 for name, listener of windowsListeners
162 window.removeEventListener(name, listener, true)
163
164 window.gBrowser.removeTabsProgressListener(tabsListener)
165
166 exports.addEventListeners = addEventListeners
167 exports.vimBucket = vimBucket
Imprint / Impressum