Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/customizableUI.js

Issue 5741004535627776: Fix toolbar icon customization in Australis (Closed)
Patch Set: Improved approach (custom button type), marked our custom API extensions Created Nov. 25, 2013, 9:34 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/appSupport.js ('k') | lib/ui.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 });
OLDNEW
« no previous file with comments | « lib/appSupport.js ('k') | lib/ui.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld