mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-13 23:17:57 +00:00
271 lines
8.5 KiB
JavaScript
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");
|
|
}
|
|
};
|