Files
tubestation/browser/base/content/newtab/transformations.js

232 lines
7.1 KiB
JavaScript

#ifdef 0
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#endif
/**
* This singleton allows to transform the grid by repositioning a site's node
* in the DOM and by showing or hiding the node. It additionally provides
* convenience methods to work with a site's DOM node.
*/
let gTransformation = {
/**
* Returns the width of the left and top border of a cell. We need to take it
* into account when measuring and comparing site and cell positions.
*/
get _cellBorderWidths() {
let cstyle = window.getComputedStyle(gGrid.cells[0].node, null);
let widths = {
left: parseInt(cstyle.getPropertyValue("border-left-width")),
top: parseInt(cstyle.getPropertyValue("border-top-width"))
};
// Cache this value, overwrite the getter.
Object.defineProperty(this, "_cellBorderWidths",
{value: widths, enumerable: true});
return widths;
},
/**
* Gets a DOM node's position.
* @param aNode The DOM node.
* @return A Rect instance with the position.
*/
getNodePosition: function Transformation_getNodePosition(aNode) {
let {left, top, width, height} = aNode.getBoundingClientRect();
return new Rect(left + scrollX, top + scrollY, width, height);
},
/**
* Fades a given site from zero to full opacity.
* @param aSite The site to fade.
*/
showSite: function (aSite) {
let node = aSite.node;
return this._setNodeOpacity(node, 1).then(() => {
// Clear the style property.
node.style.opacity = "";
});
},
/**
* Fades a given site from full to zero opacity.
* @param aSite The site to fade.
*/
hideSite: function (aSite) {
return this._setNodeOpacity(aSite.node, 0);
},
/**
* Allows to set a site's position.
* @param aSite The site to re-position.
* @param aPosition The desired position for the given site.
*/
setSitePosition: function Transformation_setSitePosition(aSite, aPosition) {
let style = aSite.node.style;
let {top, left} = aPosition;
style.top = top + "px";
style.left = left + "px";
},
/**
* Freezes a site in its current position by positioning it absolute.
* @param aSite The site to freeze.
*/
freezeSitePosition: function Transformation_freezeSitePosition(aSite) {
if (this._isFrozen(aSite))
return;
let style = aSite.node.style;
let comp = getComputedStyle(aSite.node, null);
style.width = comp.getPropertyValue("width")
style.height = comp.getPropertyValue("height");
aSite.node.setAttribute("frozen", "true");
this.setSitePosition(aSite, this.getNodePosition(aSite.node));
},
/**
* Unfreezes a site by removing its absolute positioning.
* @param aSite The site to unfreeze.
*/
unfreezeSitePosition: function Transformation_unfreezeSitePosition(aSite) {
if (!this._isFrozen(aSite))
return;
let style = aSite.node.style;
style.left = style.top = style.width = style.height = "";
aSite.node.removeAttribute("frozen");
},
/**
* Slides the given site to the target node's position.
* @param aSite The site to move.
* @param aTarget The slide target.
* @param aOptions Set of options (see below).
* unfreeze - unfreeze the site after sliding
*/
slideSiteTo: function (aSite, aTarget, aOptions) {
let currentPosition = this.getNodePosition(aSite.node);
let targetPosition = this.getNodePosition(aTarget.node)
let promise;
// We need to take the width of a cell's border into account.
targetPosition.left += this._cellBorderWidths.left;
targetPosition.top += this._cellBorderWidths.top;
// Nothing to do here if the positions already match.
if (currentPosition.left == targetPosition.left &&
currentPosition.top == targetPosition.top) {
promise = Promise.resolve();
} else {
this.setSitePosition(aSite, targetPosition);
promise = this._whenTransitionEnded(aSite.node, ["left", "top"]);
}
if (aOptions && aOptions.unfreeze) {
promise = promise.then(() => this.unfreezeSitePosition(aSite));
}
return promise;
},
/**
* Rearranges a given array of sites and moves them to their new positions or
* fades in/out new/removed sites.
* @param aSites An array of sites to rearrange.
* @param aOptions Set of options (see below).
* unfreeze - unfreeze the site after rearranging
*/
rearrangeSites: function (aSites, aOptions) {
let self = this;
let cells = gGrid.cells;
let unfreeze = aOptions && aOptions.unfreeze;
function promises() {
let index = 0;
for (let site of aSites) {
if (site && site !== gDrag.draggedSite) {
if (!cells[index]) {
// The site disappeared from the grid, hide it.
yield self.hideSite(site);
} else if (self._getNodeOpacity(site.node) != 1) {
// The site disappeared before but is now back, show it.
yield self.showSite(site);
} else {
// The site's position has changed, move it around.
yield self._moveSite(site, index, {unfreeze: unfreeze});
}
}
index++;
}
}
return Promise.every([p for (p of promises())]);
},
/**
* Listens for the 'transitionend' event on a given node.
* @param aNode The node that is transitioned.
* @param aProperties The properties we'll wait to be transitioned.
*/
_whenTransitionEnded: function (aNode, aProperties) {
let deferred = Promise.defer();
let props = new Set(aProperties);
aNode.addEventListener("transitionend", function onEnd(e) {
if (props.has(e.propertyName)) {
aNode.removeEventListener("transitionend", onEnd);
deferred.resolve();
}
});
return deferred.promise;
},
/**
* Gets a given node's opacity value.
* @param aNode The node to get the opacity value from.
* @return The node's opacity value.
*/
_getNodeOpacity: function Transformation_getNodeOpacity(aNode) {
let cstyle = window.getComputedStyle(aNode, null);
return cstyle.getPropertyValue("opacity");
},
/**
* Sets a given node's opacity.
* @param aNode The node to set the opacity value for.
* @param aOpacity The opacity value to set.
*/
_setNodeOpacity: function (aNode, aOpacity) {
if (this._getNodeOpacity(aNode) == aOpacity) {
return Promise.resolve();
}
aNode.style.opacity = aOpacity;
return this._whenTransitionEnded(aNode, ["opacity"]);
},
/**
* Moves a site to the cell with the given index.
* @param aSite The site to move.
* @param aIndex The target cell's index.
* @param aOptions Options that are directly passed to slideSiteTo().
*/
_moveSite: function (aSite, aIndex, aOptions) {
this.freezeSitePosition(aSite);
return this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
},
/**
* Checks whether a site is currently frozen.
* @param aSite The site to check.
* @return Whether the given site is frozen.
*/
_isFrozen: function Transformation_isFrozen(aSite) {
return aSite.node.hasAttribute("frozen");
}
};