browser/base/content/newtab/transformations.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial