]> git.gir.st - VimFx.git/blob - extension/lib/utils.coffee
Add pt-BR translator to PEOPLE.md
[VimFx.git] / extension / lib / utils.coffee
1 ###
2 # Copyright Anton Khodakivskiy 2012, 2013, 2014.
3 # Copyright Simon Lydell 2013, 2014.
4 # Copyright Wang Zhuochun 2013.
5 #
6 # This file is part of VimFx.
7 #
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.
12 #
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.
17 #
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/>.
20 ###
21
22 { classes: Cc, interfaces: Ci, utils: Cu } = Components
23
24 Window = Ci.nsIDOMWindow
25 ChromeWindow = Ci.nsIDOMChromeWindow
26 Element = Ci.nsIDOMElement
27 HTMLDocument = Ci.nsIDOMHTMLDocument
28 HTMLAnchorElement = Ci.nsIDOMHTMLAnchorElement
29 HTMLButtonElement = Ci.nsIDOMHTMLButtonElement
30 HTMLInputElement = Ci.nsIDOMHTMLInputElement
31 HTMLTextAreaElement = Ci.nsIDOMHTMLTextAreaElement
32 HTMLSelectElement = Ci.nsIDOMHTMLSelectElement
33 XULDocument = Ci.nsIDOMXULDocument
34 XULButtonElement = Ci.nsIDOMXULButtonElement
35 XULControlElement = Ci.nsIDOMXULControlElement
36 XULMenuListElement = Ci.nsIDOMXULMenuListElement
37 XULTextBoxElement = Ci.nsIDOMXULTextBoxElement
38
39 class Bucket
40 constructor: (@newFunc, @observer = null) ->
41 @bucket = new WeakMap()
42
43 get: (obj) ->
44 if @bucket.has(obj)
45 value = @bucket.get(obj)
46 else
47 value = @newFunc(obj)
48 @bucket.set(obj, value)
49 @observer.emit('bucket.get', value) if @observer
50 return value
51
52 forget: (obj) ->
53 @bucket.delete(obj)
54
55 class EventEmitter
56 constructor: ->
57 @listeners = {}
58
59 on: (event, listener) ->
60 (@listeners[event] ?= []).push(listener)
61
62 emit: (event, data) ->
63 for listener in @listeners[event] ? []
64 listener(data)
65 return
66
67 getEventWindow = (event) ->
68 if event.originalTarget instanceof Window
69 return event.originalTarget
70 else
71 doc = event.originalTarget.ownerDocument or event.originalTarget
72 if doc instanceof HTMLDocument or doc instanceof XULDocument
73 return doc.defaultView
74
75 getEventRootWindow = (event) ->
76 return unless window = getEventWindow(event)
77 return getRootWindow(window)
78
79 getEventCurrentTabWindow = (event) ->
80 return unless rootWindow = getEventRootWindow(event)
81 return getCurrentTabWindow(rootWindow)
82
83 getRootWindow = (window) ->
84 return window
85 .QueryInterface(Ci.nsIInterfaceRequestor)
86 .getInterface(Ci.nsIWebNavigation)
87 .QueryInterface(Ci.nsIDocShellTreeItem)
88 .rootTreeItem
89 .QueryInterface(Ci.nsIInterfaceRequestor)
90 .getInterface(Window)
91
92 getCurrentTabWindow = (window) ->
93 return window.gBrowser.selectedTab.linkedBrowser.contentWindow
94
95 blurActiveElement = (window) ->
96 # Only blur focusable elements, in order to interfere with the browser as
97 # little as possible.
98 { activeElement } = window.document
99 if activeElement and activeElement.tabIndex > -1
100 activeElement.blur()
101
102 isProperLink = (element) ->
103 # `.getAttribute` is used below instead of `.hasAttribute` to exclude `<a
104 # href="">`s used as buttons on some sites.
105 return element.getAttribute('href') and
106 (element instanceof HTMLAnchorElement or
107 element.ownerDocument instanceof XULDocument) and
108 not element.href.endsWith('#') and
109 not element.href.startsWith('javascript:')
110
111 isTextInputElement = (element) ->
112 return (element instanceof HTMLInputElement and element.type in [
113 'text', 'search', 'tel', 'url', 'email', 'password', 'number'
114 ]) or
115 element instanceof HTMLTextAreaElement or
116 # `<select>` elements can also receive text input: You may type the
117 # text of an item to select it.
118 element instanceof HTMLSelectElement or
119 element instanceof XULMenuListElement or
120 element instanceof XULTextBoxElement
121
122 isContentEditable = (element) ->
123 return element.isContentEditable or
124 isGoogleEditable(element)
125
126 isGoogleEditable = (element) ->
127 # `g_editable` is a non-standard attribute commonly used by Google.
128 return element.getAttribute?('g_editable') == 'true' or
129 element.ownerDocument.body?.getAttribute('g_editable') == 'true'
130
131 isActivatable = (element) ->
132 return element instanceof HTMLAnchorElement or
133 element instanceof HTMLButtonElement or
134 (element instanceof HTMLInputElement and element.type in [
135 'button', 'submit', 'reset', 'image'
136 ]) or
137 element instanceof XULButtonElement
138
139 isAdjustable = (element) ->
140 return element instanceof HTMLInputElement and element.type in [
141 'checkbox', 'radio', 'file', 'color'
142 'date', 'time', 'datetime', 'datetime-local', 'month', 'week'
143 ] or
144 element instanceof XULControlElement or
145 # Youtube special case.
146 element.classList?.contains('html5-video-player') or
147 element.classList?.contains('ytp-button')
148
149 area = (element) ->
150 return element.clientWidth * element.clientHeight
151
152 getSessionStore = ->
153 Cc['@mozilla.org/browser/sessionstore;1'].getService(Ci.nsISessionStore)
154
155 loadCss = (name) ->
156 sss = Cc['@mozilla.org/content/style-sheet-service;1']
157 .getService(Ci.nsIStyleSheetService)
158 uri = Services.io.newURI("chrome://vimfx/skin/#{ name }.css", null, null)
159 method = sss.AUTHOR_SHEET
160 unless sss.sheetRegistered(uri, method)
161 sss.loadAndRegisterSheet(uri, method)
162
163 module.onShutdown(->
164 sss.unregisterSheet(uri, method)
165 )
166
167 # Store events that we’ve simulated. A `WeakMap` is used in order not to leak
168 # memory. This approach is better than for example setting `event.simulated =
169 # true`, since that tells the sites that the click was simulated, and allows
170 # sites to spoof it.
171 simulated_events = new WeakMap()
172
173 # Simulate mouse click with a full chain of events. ('command' is for XUL
174 # elements.)
175 eventSequence = ['mouseover', 'mousedown', 'mouseup', 'click', 'command']
176 simulateClick = (element) ->
177 window = element.ownerDocument.defaultView
178 for type in eventSequence
179 mouseEvent = new window.MouseEvent(type, {
180 # Let the event bubble in order to trigger delegated event listeners.
181 bubbles: true
182 # Make the event cancelable so that `<a href="#">` can be used as a
183 # JavaScript-powered button without scrolling to the top of the page.
184 cancelable: true
185 })
186 element.dispatchEvent(mouseEvent)
187
188 isEventSimulated = (event) ->
189 return simulated_events.has(event)
190
191 # Write a string to the system clipboard.
192 writeToClipboard = (text) ->
193 clipboardHelper = Cc['@mozilla.org/widget/clipboardhelper;1']
194 .getService(Ci.nsIClipboardHelper)
195 clipboardHelper.copyString(text)
196
197 # Executes function `func` and measures how much time it took.
198 timeIt = (func, name) ->
199 console.time(name)
200 result = func()
201 console.timeEnd(name)
202 return result
203
204 createBox = (document, className, parent = null, text = null) ->
205 box = document.createElement('box')
206 box.className = className
207 box.textContent = text if text?
208 parent.appendChild(box) if parent?
209 return box
210
211 setAttributes = (element, attributes) ->
212 for attribute, value of attributes
213 element.setAttribute(attribute, value)
214 return
215
216 insertText = (input, value) ->
217 { selectionStart, selectionEnd } = input
218 input.value =
219 input.value[0...selectionStart] + value + input.value[selectionEnd..]
220 input.selectionStart = input.selectionEnd = selectionStart + value.length
221
222 isURL = (str) ->
223 try
224 url = Cc['@mozilla.org/network/io-service;1']
225 .getService(Ci.nsIIOService)
226 .newURI(str, null, null)
227 .QueryInterface(Ci.nsIURL)
228 return true
229 catch err
230 return false
231
232 # Use Firefox services to search for a given string.
233 browserSearchSubmission = (str) ->
234 ss = Cc['@mozilla.org/browser/search-service;1']
235 .getService(Ci.nsIBrowserSearchService)
236
237 engine = ss.currentEngine or ss.defaultEngine
238 return engine.getSubmission(str, null)
239
240 openTab = (rootWindow, url, options) ->
241 { gBrowser } = rootWindow
242 rootWindow.TreeStyleTabService?.readyToOpenChildTab(gBrowser.selectedTab)
243 gBrowser.loadOneTab(url, options)
244
245 # Remove duplicate characters from string (case insensitive).
246 removeDuplicateCharacters = (str) ->
247 return removeDuplicates( str.toLowerCase().split('') ).join('')
248
249 # Escape a string to render it usable in regular expressions.
250 regexpEscape = (s) -> s and s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
251
252 removeDuplicates = (array) ->
253 # coffeelint: disable=no_backticks
254 return `[...new Set(array)]`
255 # coffeelint: enable=no_backticks
256
257 formatError = (error) ->
258 stack = String(error.stack?.formattedStack ? error.stack ? '')
259 .split('\n')
260 .filter((line) -> line.contains('.xpi!'))
261 .map((line) -> ' ' + line.replace(/(?:\/<)*@.+\.xpi!/g, '@'))
262 .join('\n')
263 return "#{ error }\n#{ stack }"
264
265 observe = (topic, observer) ->
266 observer = {observe: observer} if typeof observer == 'function'
267 Services.obs.addObserver(observer, topic, false)
268 module.onShutdown(->
269 Services.obs.removeObserver(observer, topic, false)
270 )
271
272 has = Function::call.bind(Object::hasOwnProperty)
273
274 class Counter
275 constructor: ({start, step}) ->
276 @value = start ? 0
277 @step = step ? 1
278 tick: -> @value += @step
279
280 module.exports = {
281 Bucket
282 EventEmitter
283 getEventWindow
284 getEventRootWindow
285 getEventCurrentTabWindow
286 getRootWindow
287 getCurrentTabWindow
288
289 blurActiveElement
290 isProperLink
291 isTextInputElement
292 isContentEditable
293 isActivatable
294 isAdjustable
295 area
296 getSessionStore
297
298 loadCss
299
300 simulateClick
301 isEventSimulated
302 writeToClipboard
303 timeIt
304
305 createBox
306 setAttributes
307 insertText
308 isURL
309 browserSearchSubmission
310 openTab
311 regexpEscape
312 removeDuplicates
313 removeDuplicateCharacters
314 formatError
315 observe
316 has
317 Counter
318 }
Imprint / Impressum