|
1 #ifdef 0 |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 #endif |
|
6 |
|
7 /** |
|
8 * This singleton allows to transform the grid by repositioning a site's node |
|
9 * in the DOM and by showing or hiding the node. It additionally provides |
|
10 * convenience methods to work with a site's DOM node. |
|
11 */ |
|
12 let gTransformation = { |
|
13 /** |
|
14 * Returns the width of the left and top border of a cell. We need to take it |
|
15 * into account when measuring and comparing site and cell positions. |
|
16 */ |
|
17 get _cellBorderWidths() { |
|
18 let cstyle = window.getComputedStyle(gGrid.cells[0].node, null); |
|
19 let widths = { |
|
20 left: parseInt(cstyle.getPropertyValue("border-left-width")), |
|
21 top: parseInt(cstyle.getPropertyValue("border-top-width")) |
|
22 }; |
|
23 |
|
24 // Cache this value, overwrite the getter. |
|
25 Object.defineProperty(this, "_cellBorderWidths", |
|
26 {value: widths, enumerable: true}); |
|
27 |
|
28 return widths; |
|
29 }, |
|
30 |
|
31 /** |
|
32 * Gets a DOM node's position. |
|
33 * @param aNode The DOM node. |
|
34 * @return A Rect instance with the position. |
|
35 */ |
|
36 getNodePosition: function Transformation_getNodePosition(aNode) { |
|
37 let {left, top, width, height} = aNode.getBoundingClientRect(); |
|
38 return new Rect(left + scrollX, top + scrollY, width, height); |
|
39 }, |
|
40 |
|
41 /** |
|
42 * Fades a given node from zero to full opacity. |
|
43 * @param aNode The node to fade. |
|
44 * @param aCallback The callback to call when finished. |
|
45 */ |
|
46 fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) { |
|
47 this._setNodeOpacity(aNode, 1, function () { |
|
48 // Clear the style property. |
|
49 aNode.style.opacity = ""; |
|
50 |
|
51 if (aCallback) |
|
52 aCallback(); |
|
53 }); |
|
54 }, |
|
55 |
|
56 /** |
|
57 * Fades a given node from full to zero opacity. |
|
58 * @param aNode The node to fade. |
|
59 * @param aCallback The callback to call when finished. |
|
60 */ |
|
61 fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) { |
|
62 this._setNodeOpacity(aNode, 0, aCallback); |
|
63 }, |
|
64 |
|
65 /** |
|
66 * Fades a given site from zero to full opacity. |
|
67 * @param aSite The site to fade. |
|
68 * @param aCallback The callback to call when finished. |
|
69 */ |
|
70 showSite: function Transformation_showSite(aSite, aCallback) { |
|
71 this.fadeNodeIn(aSite.node, aCallback); |
|
72 }, |
|
73 |
|
74 /** |
|
75 * Fades a given site from full to zero opacity. |
|
76 * @param aSite The site to fade. |
|
77 * @param aCallback The callback to call when finished. |
|
78 */ |
|
79 hideSite: function Transformation_hideSite(aSite, aCallback) { |
|
80 this.fadeNodeOut(aSite.node, aCallback); |
|
81 }, |
|
82 |
|
83 /** |
|
84 * Allows to set a site's position. |
|
85 * @param aSite The site to re-position. |
|
86 * @param aPosition The desired position for the given site. |
|
87 */ |
|
88 setSitePosition: function Transformation_setSitePosition(aSite, aPosition) { |
|
89 let style = aSite.node.style; |
|
90 let {top, left} = aPosition; |
|
91 |
|
92 style.top = top + "px"; |
|
93 style.left = left + "px"; |
|
94 }, |
|
95 |
|
96 /** |
|
97 * Freezes a site in its current position by positioning it absolute. |
|
98 * @param aSite The site to freeze. |
|
99 */ |
|
100 freezeSitePosition: function Transformation_freezeSitePosition(aSite) { |
|
101 if (this._isFrozen(aSite)) |
|
102 return; |
|
103 |
|
104 let style = aSite.node.style; |
|
105 let comp = getComputedStyle(aSite.node, null); |
|
106 style.width = comp.getPropertyValue("width") |
|
107 style.height = comp.getPropertyValue("height"); |
|
108 |
|
109 aSite.node.setAttribute("frozen", "true"); |
|
110 this.setSitePosition(aSite, this.getNodePosition(aSite.node)); |
|
111 }, |
|
112 |
|
113 /** |
|
114 * Unfreezes a site by removing its absolute positioning. |
|
115 * @param aSite The site to unfreeze. |
|
116 */ |
|
117 unfreezeSitePosition: function Transformation_unfreezeSitePosition(aSite) { |
|
118 if (!this._isFrozen(aSite)) |
|
119 return; |
|
120 |
|
121 let style = aSite.node.style; |
|
122 style.left = style.top = style.width = style.height = ""; |
|
123 aSite.node.removeAttribute("frozen"); |
|
124 }, |
|
125 |
|
126 /** |
|
127 * Slides the given site to the target node's position. |
|
128 * @param aSite The site to move. |
|
129 * @param aTarget The slide target. |
|
130 * @param aOptions Set of options (see below). |
|
131 * unfreeze - unfreeze the site after sliding |
|
132 * callback - the callback to call when finished |
|
133 */ |
|
134 slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) { |
|
135 let currentPosition = this.getNodePosition(aSite.node); |
|
136 let targetPosition = this.getNodePosition(aTarget.node) |
|
137 let callback = aOptions && aOptions.callback; |
|
138 |
|
139 let self = this; |
|
140 |
|
141 function finish() { |
|
142 if (aOptions && aOptions.unfreeze) |
|
143 self.unfreezeSitePosition(aSite); |
|
144 |
|
145 if (callback) |
|
146 callback(); |
|
147 } |
|
148 |
|
149 // We need to take the width of a cell's border into account. |
|
150 targetPosition.left += this._cellBorderWidths.left; |
|
151 targetPosition.top += this._cellBorderWidths.top; |
|
152 |
|
153 // Nothing to do here if the positions already match. |
|
154 if (currentPosition.left == targetPosition.left && |
|
155 currentPosition.top == targetPosition.top) { |
|
156 finish(); |
|
157 } else { |
|
158 this.setSitePosition(aSite, targetPosition); |
|
159 this._whenTransitionEnded(aSite.node, ["left", "top"], finish); |
|
160 } |
|
161 }, |
|
162 |
|
163 /** |
|
164 * Rearranges a given array of sites and moves them to their new positions or |
|
165 * fades in/out new/removed sites. |
|
166 * @param aSites An array of sites to rearrange. |
|
167 * @param aOptions Set of options (see below). |
|
168 * unfreeze - unfreeze the site after rearranging |
|
169 * callback - the callback to call when finished |
|
170 */ |
|
171 rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) { |
|
172 let batch = []; |
|
173 let cells = gGrid.cells; |
|
174 let callback = aOptions && aOptions.callback; |
|
175 let unfreeze = aOptions && aOptions.unfreeze; |
|
176 |
|
177 aSites.forEach(function (aSite, aIndex) { |
|
178 // Do not re-arrange empty cells or the dragged site. |
|
179 if (!aSite || aSite == gDrag.draggedSite) |
|
180 return; |
|
181 |
|
182 let deferred = Promise.defer(); |
|
183 batch.push(deferred.promise); |
|
184 let cb = deferred.resolve; |
|
185 |
|
186 if (!cells[aIndex]) |
|
187 // The site disappeared from the grid, hide it. |
|
188 this.hideSite(aSite, cb); |
|
189 else if (this._getNodeOpacity(aSite.node) != 1) |
|
190 // The site disappeared before but is now back, show it. |
|
191 this.showSite(aSite, cb); |
|
192 else |
|
193 // The site's position has changed, move it around. |
|
194 this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: cb}); |
|
195 }, this); |
|
196 |
|
197 if (callback) { |
|
198 Promise.all(batch).then(callback); |
|
199 } |
|
200 }, |
|
201 |
|
202 /** |
|
203 * Listens for the 'transitionend' event on a given node and calls the given |
|
204 * callback. |
|
205 * @param aNode The node that is transitioned. |
|
206 * @param aProperties The properties we'll wait to be transitioned. |
|
207 * @param aCallback The callback to call when finished. |
|
208 */ |
|
209 _whenTransitionEnded: |
|
210 function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) { |
|
211 |
|
212 let props = new Set(aProperties); |
|
213 aNode.addEventListener("transitionend", function onEnd(e) { |
|
214 if (props.has(e.propertyName)) { |
|
215 aNode.removeEventListener("transitionend", onEnd); |
|
216 aCallback(); |
|
217 } |
|
218 }); |
|
219 }, |
|
220 |
|
221 /** |
|
222 * Gets a given node's opacity value. |
|
223 * @param aNode The node to get the opacity value from. |
|
224 * @return The node's opacity value. |
|
225 */ |
|
226 _getNodeOpacity: function Transformation_getNodeOpacity(aNode) { |
|
227 let cstyle = window.getComputedStyle(aNode, null); |
|
228 return cstyle.getPropertyValue("opacity"); |
|
229 }, |
|
230 |
|
231 /** |
|
232 * Sets a given node's opacity. |
|
233 * @param aNode The node to set the opacity value for. |
|
234 * @param aOpacity The opacity value to set. |
|
235 * @param aCallback The callback to call when finished. |
|
236 */ |
|
237 _setNodeOpacity: |
|
238 function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) { |
|
239 |
|
240 if (this._getNodeOpacity(aNode) == aOpacity) { |
|
241 if (aCallback) |
|
242 aCallback(); |
|
243 } else { |
|
244 if (aCallback) { |
|
245 this._whenTransitionEnded(aNode, ["opacity"], aCallback); |
|
246 } |
|
247 |
|
248 aNode.style.opacity = aOpacity; |
|
249 } |
|
250 }, |
|
251 |
|
252 /** |
|
253 * Moves a site to the cell with the given index. |
|
254 * @param aSite The site to move. |
|
255 * @param aIndex The target cell's index. |
|
256 * @param aOptions Options that are directly passed to slideSiteTo(). |
|
257 */ |
|
258 _moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) { |
|
259 this.freezeSitePosition(aSite); |
|
260 this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions); |
|
261 }, |
|
262 |
|
263 /** |
|
264 * Checks whether a site is currently frozen. |
|
265 * @param aSite The site to check. |
|
266 * @return Whether the given site is frozen. |
|
267 */ |
|
268 _isFrozen: function Transformation_isFrozen(aSite) { |
|
269 return aSite.node.hasAttribute("frozen"); |
|
270 } |
|
271 }; |