gecko-dev/browser/base/content/newtab/transformations.js
2013-07-24 16:50:55 +02:00

271 lines
8.5 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 node from zero to full opacity.
* @param aNode The node to fade.
* @param aCallback The callback to call when finished.
*/
fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) {
this._setNodeOpacity(aNode, 1, function () {
// Clear the style property.
aNode.style.opacity = "";
if (aCallback)
aCallback();
});
},
/**
* Fades a given node from full to zero opacity.
* @param aNode The node to fade.
* @param aCallback The callback to call when finished.
*/
fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) {
this._setNodeOpacity(aNode, 0, aCallback);
},
/**
* Fades a given site from zero to full opacity.
* @param aSite The site to fade.
* @param aCallback The callback to call when finished.
*/
showSite: function Transformation_showSite(aSite, aCallback) {
this.fadeNodeIn(aSite.node, aCallback);
},
/**
* Fades a given site from full to zero opacity.
* @param aSite The site to fade.
* @param aCallback The callback to call when finished.
*/
hideSite: function Transformation_hideSite(aSite, aCallback) {
this.fadeNodeOut(aSite.node, aCallback);
},
/**
* 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
* callback - the callback to call when finished
*/
slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) {
let currentPosition = this.getNodePosition(aSite.node);
let targetPosition = this.getNodePosition(aTarget.node)
let callback = aOptions && aOptions.callback;
let self = this;
function finish() {
if (aOptions && aOptions.unfreeze)
self.unfreezeSitePosition(aSite);
if (callback)
callback();
}
// 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) {
finish();
} else {
this.setSitePosition(aSite, targetPosition);
this._whenTransitionEnded(aSite.node, ["left", "top"], finish);
}
},
/**
* 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
* callback - the callback to call when finished
*/
rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) {
let batch = [];
let cells = gGrid.cells;
let callback = aOptions && aOptions.callback;
let unfreeze = aOptions && aOptions.unfreeze;
aSites.forEach(function (aSite, aIndex) {
// Do not re-arrange empty cells or the dragged site.
if (!aSite || aSite == gDrag.draggedSite)
return;
let deferred = Promise.defer();
batch.push(deferred.promise);
let cb = function () deferred.resolve();
if (!cells[aIndex])
// The site disappeared from the grid, hide it.
this.hideSite(aSite, cb);
else if (this._getNodeOpacity(aSite.node) != 1)
// The site disappeared before but is now back, show it.
this.showSite(aSite, cb);
else
// The site's position has changed, move it around.
this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: cb});
}, this);
let wait = Promise.promised(function () callback && callback());
wait.apply(null, batch);
},
/**
* Listens for the 'transitionend' event on a given node and calls the given
* callback.
* @param aNode The node that is transitioned.
* @param aProperties The properties we'll wait to be transitioned.
* @param aCallback The callback to call when finished.
*/
_whenTransitionEnded:
function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) {
let props = new Set(aProperties);
aNode.addEventListener("transitionend", function onEnd(e) {
if (props.has(e.propertyName)) {
aNode.removeEventListener("transitionend", onEnd);
aCallback();
}
});
},
/**
* 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.
* @param aCallback The callback to call when finished.
*/
_setNodeOpacity:
function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) {
if (this._getNodeOpacity(aNode) == aOpacity) {
if (aCallback)
aCallback();
} else {
if (aCallback) {
this._whenTransitionEnded(aNode, ["opacity"], aCallback);
}
aNode.style.opacity = aOpacity;
}
},
/**
* 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 Transformation_moveSite(aSite, aIndex, aOptions) {
this.freezeSitePosition(aSite);
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");
}
};