Left: | ||
Right: |
OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * This file is part of Adblock Plus <http://adblockplus.org/>, | |
3 * Copyright (C) 2006-2013 Eyeo GmbH | |
4 * | |
5 * Adblock Plus is free software: you can redistribute it and/or modify | |
6 * it under the terms of the GNU General Public License version 3 as | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * Adblock Plus is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU General Public License | |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | |
16 */ | |
17 | |
18 /** | |
19 * @fileOverview This emulates a subset of the CustomizableUI API from Firefox 2 8. | |
20 */ | |
21 | |
22 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", null); | |
23 | |
24 let {Utils} = require("utils"); | |
25 | |
26 // UI module has to be referenced lazily to avoid circular references | |
27 XPCOMUtils.defineLazyGetter(this, "UI", function() require("ui").UI); | |
28 | |
29 let widgets = Map(); | |
30 | |
31 function getToolbox(/**Window*/ window, /**Widget*/ widget) /**Element*/ | |
32 { | |
33 if (!("defaultArea" in widget) || !widget.defaultArea) | |
34 return null; | |
35 | |
36 let toolbar = UI.findElement(window, widget.defaultArea); | |
37 if (!toolbar) | |
38 return null; | |
39 | |
40 let toolbox = toolbar.toolbox; | |
41 if (toolbox && ("palette" in toolbox) && toolbox.palette) | |
42 return toolbox; | |
43 else | |
44 return null; | |
45 } | |
46 | |
47 function getToolbar(/**Element*/ element) /**Element*/ | |
48 { | |
49 for (let parent = element.parentNode; parent; parent = parent.parentNode) | |
50 if (parent.localName == "toolbar") | |
51 return parent; | |
52 return null; | |
53 } | |
54 | |
55 function getPaletteItem(/**Element*/ toolbox, /**String*/ id) /**Element*/ | |
56 { | |
57 for (let child of toolbox.palette.children) | |
58 if (child.id == id) | |
59 return child; | |
60 | |
61 return null; | |
62 } | |
63 | |
64 function restoreWidget(/**Element*/ toolbox, /**Widget*/ widget) | |
65 { | |
66 // Create node | |
67 let node = widget.onBuild(toolbox.ownerDocument); | |
68 if (typeof widget.onClick == "function") | |
69 node.addEventListener("click", widget.onClick, false); | |
70 if (typeof widget.onCommand == "function") | |
71 node.addEventListener("command", widget.onCommand, false); | |
72 | |
73 // Insert into the palette first | |
74 toolbox.palette.insertBefore(node, toolbox.palette.firstChild); | |
75 | |
76 // Now find out where we should put it | |
77 let position = toolbox.getAttribute(widget.positionAttribute); | |
78 if (!/^\S*,\S*,\S*$/.test(position)) | |
79 position = null; | |
80 | |
81 if (position == null) | |
82 { | |
83 // No explicitly saved position but maybe we can find it in a currentset | |
84 // attribute somewhere. | |
85 let toolbars = toolbox.externalToolbars.slice(); | |
86 for (let child of toolbox.children) | |
87 if (child.localName == "toolbar") | |
88 toolbars.push(child); | |
89 for (let toolbar of toolbars) | |
90 { | |
91 let currentSet = toolbar.getAttribute("currentset"); | |
92 if (currentSet) | |
93 { | |
94 let items = currentSet.split(","); | |
95 let index = items.indexOf(widget.id); | |
96 if (index >= 0) | |
97 { | |
98 let before = (index + 1 < items.length ? items[index + 1] : ""); | |
99 position = "visible," + toolbar.id + "," + before; | |
100 toolbox.setAttribute(widget.positionAttribute, position); | |
101 toolbox.ownerDocument.persist(toolbox.id, widget.positionAttribute); | |
102 break; | |
103 } | |
104 } | |
105 } | |
106 } | |
107 | |
108 showWidget(toolbox, widget, position); | |
109 } | |
110 | |
111 function showWidget(/**Element*/ toolbox, /**Widget*/ widget, /**String*/ positi on) | |
112 { | |
113 let visible = "visible", parent = null, before = null; | |
114 if (position) | |
115 { | |
116 [visible, parent, before] = position.split(",", 3); | |
117 parent = toolbox.ownerDocument.getElementById(parent); | |
118 if (before == "") | |
119 before = null; | |
120 else | |
121 before = toolbox.ownerDocument.getElementById(before); | |
122 if (before && before.parentNode != parent) | |
123 before = null; | |
124 } | |
125 | |
126 if (visible == "visible" && !parent) | |
127 { | |
128 let insertionPoint = { | |
129 parent: widget.defaultArea | |
130 }; | |
131 if (typeof widget.defaultBefore != "undefined") | |
132 insertionPoint.before = widget.defaultBefore; | |
133 if (typeof widget.defaultAfter != "undefined") | |
134 insertionPoint.after = widget.defaultAfter; | |
135 | |
136 [parent, before] = UI.resolveInsertionPoint(toolbox.ownerDocument.defaultVie w, insertionPoint); | |
137 } | |
138 | |
139 if (parent && parent.localName != "toolbar") | |
140 parent = null; | |
141 | |
142 if (visible != "visible") | |
143 { | |
144 // Move to palette if the item is currently visible | |
145 let node = toolbox.ownerDocument.getElementById(widget.id); | |
146 if (node) | |
147 toolbox.palette.appendChild(node); | |
148 } | |
149 else if (parent) | |
150 { | |
151 // Add the item to the toolbar | |
152 let items = parent.currentSet.split(","); | |
153 let index = (before ? items.indexOf(before.id) : -1); | |
154 if (index < 0) | |
155 before = null; | |
156 parent.insertItem(widget.id, before, null, false); | |
157 } | |
158 | |
159 saveState(toolbox, widget); | |
160 } | |
161 | |
162 function removeWidget(/**Window*/ window, /**Widget*/ widget) | |
163 { | |
164 let element = window.document.getElementById(widget.id); | |
165 if (element) | |
166 element.parentNode.removeChild(element); | |
167 | |
168 let toolbox = getToolbox(window, widget); | |
169 if (toolbox) | |
170 { | |
171 let paletteItem = getPaletteItem(toolbox, widget.id); | |
172 if (paletteItem) | |
173 paletteItem.parentNode.removeChild(paletteItem); | |
174 } | |
175 } | |
176 | |
177 function onToolbarCustomization(/**Event*/ event) | |
178 { | |
179 let toolbox = event.currentTarget; | |
180 for (let [id, widget] of widgets) | |
181 saveState(toolbox, widget); | |
182 } | |
183 | |
184 function saveState(/**Element*/ toolbox, /**Widget*/ widget) | |
185 { | |
186 let node = toolbox.ownerDocument.getElementById(widget.id); | |
187 | |
188 let position = toolbox.getAttribute(widget.positionAttribute) || "hidden,,"; | |
189 if (node) | |
190 { | |
191 if (typeof widget.onAdded == "function") | |
192 widget.onAdded(node) | |
193 | |
194 let toolbar = getToolbar(node); | |
195 position = "visible," + toolbar.id + "," + (node.nextSibling ? node.nextSibl ing.id : ""); | |
196 } | |
197 else | |
198 position = position.replace(/^visible,/, "hidden,") | |
199 | |
200 toolbox.setAttribute(widget.positionAttribute, position); | |
201 toolbox.ownerDocument.persist(toolbox.id, widget.positionAttribute); | |
202 } | |
203 | |
204 let CustomizableUI = exports.CustomizableUI = | |
205 { | |
206 createWidget: function(widget) | |
207 { | |
208 if (typeof widget.id == "undefined" || | |
209 typeof widget.defaultArea == "undefined" || | |
210 typeof widget.positionAttribute == "undefined") | |
211 { | |
212 throw new Error("Unexpected: required property missing from the widget dat a"); | |
213 } | |
214 widgets.set(widget.id, widget); | |
215 | |
216 // Show widget in any existing windows | |
217 for (let window of UI.applicationWindows) | |
218 { | |
219 let toolbox = getToolbox(window, widget); | |
220 if (toolbox) | |
221 { | |
222 toolbox.addEventListener("aftercustomization", onToolbarCustomization, f alse); | |
223 restoreWidget(toolbox, widget); | |
224 } | |
225 } | |
226 }, | |
227 | |
228 destroyWidget: function(id) | |
229 { | |
230 // Don't do anything here. This function is called on shutdown, | |
231 // removeFromWindow will take care of cleaning up already. | |
232 }, | |
233 | |
234 getPlacementOfWidget: function(id) | |
235 { | |
236 let window = UI.currentWindow; | |
237 if (!window) | |
238 return null; | |
239 | |
240 let widget = window.document.getElementById(id); | |
241 if (!widget) | |
242 return null; | |
243 | |
244 let toolbar = getToolbar(widget); | |
245 if (!toolbar) | |
246 return null; | |
247 | |
248 let items = toolbar.currentSet.split(","); | |
249 let index = items.indexOf(id); | |
250 if (index < 0) | |
251 return null; | |
252 else | |
253 return {area: toolbar.id, placement: index}; | |
Wladimir Palant
2013/11/27 16:04:01
Given that we don't use the placement parameter in
| |
254 }, | |
255 | |
256 addWidgetToArea: function(id, area, position) | |
257 { | |
258 let widget = widgets.get(id); | |
259 for (let window of UI.applicationWindows) | |
260 { | |
261 let toolbox = getToolbox(window, widget); | |
262 if (!toolbox) | |
263 continue; | |
264 | |
265 let position = toolbox.getAttribute(widget.positionAttribute); | |
Wladimir Palant
2013/11/27 16:04:01
This masks a parameter. We should probably simply
| |
266 if (position) | |
267 position = position.replace(/^hidden,/, "visible,"); | |
268 showWidget(toolbox, widget, position); | |
269 } | |
270 }, | |
271 | |
272 removeWidgetFromArea: function(id) | |
273 { | |
274 let widget = widgets.get(id); | |
275 for (let window of UI.applicationWindows) | |
276 { | |
277 let toolbox = getToolbox(window, widget); | |
278 if (!toolbox) | |
279 continue; | |
280 | |
281 let position = toolbox.getAttribute(widget.positionAttribute); | |
282 if (position) | |
283 position = position.replace(/^visible,/, "hidden,"); | |
284 else | |
285 position = "hidden,,"; | |
286 showWidget(toolbox, widget, position); | |
287 } | |
288 } | |
289 }; | |
290 | |
291 let {WindowObserver} = require("windowObserver"); | |
292 new WindowObserver({ | |
293 applyToWindow: function(window) | |
294 { | |
295 let {isKnownWindow} = require("appSupport"); | |
296 if (!isKnownWindow(window)) | |
297 return; | |
298 | |
299 for (let [id, widget] of widgets) | |
300 { | |
301 let toolbox = getToolbox(window, widget); | |
302 if (toolbox) | |
303 { | |
304 toolbox.addEventListener("aftercustomization", onToolbarCustomization, f alse); | |
305 | |
306 // Restore widget asynchronously to allow the stylesheet to load | |
307 Utils.runAsync(restoreWidget.bind(null, toolbox, widget)); | |
308 } | |
309 } | |
310 }, | |
311 | |
312 removeFromWindow: function(window) | |
313 { | |
314 let {isKnownWindow} = require("appSupport"); | |
315 if (!isKnownWindow(window)) | |
316 return; | |
317 | |
318 for (let [id, widget] of widgets) | |
319 { | |
320 let toolbox = getToolbox(window, widget); | |
321 if (toolbox) | |
322 toolbox.removeEventListener("aftercustomization", onToolbarCustomization , false); | |
323 | |
324 removeWidget(window, widget); | |
325 } | |
326 } | |
327 }); | |
OLD | NEW |