browser/base/content/newtab/transformations.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/base/content/newtab/transformations.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,271 @@
     1.4 +#ifdef 0
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +#endif
     1.9 +
    1.10 +/**
    1.11 + * This singleton allows to transform the grid by repositioning a site's node
    1.12 + * in the DOM and by showing or hiding the node. It additionally provides
    1.13 + * convenience methods to work with a site's DOM node.
    1.14 + */
    1.15 +let gTransformation = {
    1.16 +  /**
    1.17 +   * Returns the width of the left and top border of a cell. We need to take it
    1.18 +   * into account when measuring and comparing site and cell positions.
    1.19 +   */
    1.20 +  get _cellBorderWidths() {
    1.21 +    let cstyle = window.getComputedStyle(gGrid.cells[0].node, null);
    1.22 +    let widths = {
    1.23 +      left: parseInt(cstyle.getPropertyValue("border-left-width")),
    1.24 +      top: parseInt(cstyle.getPropertyValue("border-top-width"))
    1.25 +    };
    1.26 +
    1.27 +    // Cache this value, overwrite the getter.
    1.28 +    Object.defineProperty(this, "_cellBorderWidths",
    1.29 +                          {value: widths, enumerable: true});
    1.30 +
    1.31 +    return widths;
    1.32 +  },
    1.33 +
    1.34 +  /**
    1.35 +   * Gets a DOM node's position.
    1.36 +   * @param aNode The DOM node.
    1.37 +   * @return A Rect instance with the position.
    1.38 +   */
    1.39 +  getNodePosition: function Transformation_getNodePosition(aNode) {
    1.40 +    let {left, top, width, height} = aNode.getBoundingClientRect();
    1.41 +    return new Rect(left + scrollX, top + scrollY, width, height);
    1.42 +  },
    1.43 +
    1.44 +  /**
    1.45 +   * Fades a given node from zero to full opacity.
    1.46 +   * @param aNode The node to fade.
    1.47 +   * @param aCallback The callback to call when finished.
    1.48 +   */
    1.49 +  fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) {
    1.50 +    this._setNodeOpacity(aNode, 1, function () {
    1.51 +      // Clear the style property.
    1.52 +      aNode.style.opacity = "";
    1.53 +
    1.54 +      if (aCallback)
    1.55 +        aCallback();
    1.56 +    });
    1.57 +  },
    1.58 +
    1.59 +  /**
    1.60 +   * Fades a given node from full to zero opacity.
    1.61 +   * @param aNode The node to fade.
    1.62 +   * @param aCallback The callback to call when finished.
    1.63 +   */
    1.64 +  fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) {
    1.65 +    this._setNodeOpacity(aNode, 0, aCallback);
    1.66 +  },
    1.67 +
    1.68 +  /**
    1.69 +   * Fades a given site from zero to full opacity.
    1.70 +   * @param aSite The site to fade.
    1.71 +   * @param aCallback The callback to call when finished.
    1.72 +   */
    1.73 +  showSite: function Transformation_showSite(aSite, aCallback) {
    1.74 +    this.fadeNodeIn(aSite.node, aCallback);
    1.75 +  },
    1.76 +
    1.77 +  /**
    1.78 +   * Fades a given site from full to zero opacity.
    1.79 +   * @param aSite The site to fade.
    1.80 +   * @param aCallback The callback to call when finished.
    1.81 +   */
    1.82 +  hideSite: function Transformation_hideSite(aSite, aCallback) {
    1.83 +    this.fadeNodeOut(aSite.node, aCallback);
    1.84 +  },
    1.85 +
    1.86 +  /**
    1.87 +   * Allows to set a site's position.
    1.88 +   * @param aSite The site to re-position.
    1.89 +   * @param aPosition The desired position for the given site.
    1.90 +   */
    1.91 +  setSitePosition: function Transformation_setSitePosition(aSite, aPosition) {
    1.92 +    let style = aSite.node.style;
    1.93 +    let {top, left} = aPosition;
    1.94 +
    1.95 +    style.top = top + "px";
    1.96 +    style.left = left + "px";
    1.97 +  },
    1.98 +
    1.99 +  /**
   1.100 +   * Freezes a site in its current position by positioning it absolute.
   1.101 +   * @param aSite The site to freeze.
   1.102 +   */
   1.103 +  freezeSitePosition: function Transformation_freezeSitePosition(aSite) {
   1.104 +    if (this._isFrozen(aSite))
   1.105 +      return;
   1.106 +
   1.107 +    let style = aSite.node.style;
   1.108 +    let comp = getComputedStyle(aSite.node, null);
   1.109 +    style.width = comp.getPropertyValue("width")
   1.110 +    style.height = comp.getPropertyValue("height");
   1.111 +
   1.112 +    aSite.node.setAttribute("frozen", "true");
   1.113 +    this.setSitePosition(aSite, this.getNodePosition(aSite.node));
   1.114 +  },
   1.115 +
   1.116 +  /**
   1.117 +   * Unfreezes a site by removing its absolute positioning.
   1.118 +   * @param aSite The site to unfreeze.
   1.119 +   */
   1.120 +  unfreezeSitePosition: function Transformation_unfreezeSitePosition(aSite) {
   1.121 +    if (!this._isFrozen(aSite))
   1.122 +      return;
   1.123 +
   1.124 +    let style = aSite.node.style;
   1.125 +    style.left = style.top = style.width = style.height = "";
   1.126 +    aSite.node.removeAttribute("frozen");
   1.127 +  },
   1.128 +
   1.129 +  /**
   1.130 +   * Slides the given site to the target node's position.
   1.131 +   * @param aSite The site to move.
   1.132 +   * @param aTarget The slide target.
   1.133 +   * @param aOptions Set of options (see below).
   1.134 +   *        unfreeze - unfreeze the site after sliding
   1.135 +   *        callback - the callback to call when finished
   1.136 +   */
   1.137 +  slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) {
   1.138 +    let currentPosition = this.getNodePosition(aSite.node);
   1.139 +    let targetPosition = this.getNodePosition(aTarget.node)
   1.140 +    let callback = aOptions && aOptions.callback;
   1.141 +
   1.142 +    let self = this;
   1.143 +
   1.144 +    function finish() {
   1.145 +      if (aOptions && aOptions.unfreeze)
   1.146 +        self.unfreezeSitePosition(aSite);
   1.147 +
   1.148 +      if (callback)
   1.149 +        callback();
   1.150 +    }
   1.151 +
   1.152 +    // We need to take the width of a cell's border into account.
   1.153 +    targetPosition.left += this._cellBorderWidths.left;
   1.154 +    targetPosition.top += this._cellBorderWidths.top;
   1.155 +
   1.156 +    // Nothing to do here if the positions already match.
   1.157 +    if (currentPosition.left == targetPosition.left &&
   1.158 +        currentPosition.top == targetPosition.top) {
   1.159 +      finish();
   1.160 +    } else {
   1.161 +      this.setSitePosition(aSite, targetPosition);
   1.162 +      this._whenTransitionEnded(aSite.node, ["left", "top"], finish);
   1.163 +    }
   1.164 +  },
   1.165 +
   1.166 +  /**
   1.167 +   * Rearranges a given array of sites and moves them to their new positions or
   1.168 +   * fades in/out new/removed sites.
   1.169 +   * @param aSites An array of sites to rearrange.
   1.170 +   * @param aOptions Set of options (see below).
   1.171 +   *        unfreeze - unfreeze the site after rearranging
   1.172 +   *        callback - the callback to call when finished
   1.173 +   */
   1.174 +  rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) {
   1.175 +    let batch = [];
   1.176 +    let cells = gGrid.cells;
   1.177 +    let callback = aOptions && aOptions.callback;
   1.178 +    let unfreeze = aOptions && aOptions.unfreeze;
   1.179 +
   1.180 +    aSites.forEach(function (aSite, aIndex) {
   1.181 +      // Do not re-arrange empty cells or the dragged site.
   1.182 +      if (!aSite || aSite == gDrag.draggedSite)
   1.183 +        return;
   1.184 +
   1.185 +      let deferred = Promise.defer();
   1.186 +      batch.push(deferred.promise);
   1.187 +      let cb = deferred.resolve;
   1.188 +
   1.189 +      if (!cells[aIndex])
   1.190 +        // The site disappeared from the grid, hide it.
   1.191 +        this.hideSite(aSite, cb);
   1.192 +      else if (this._getNodeOpacity(aSite.node) != 1)
   1.193 +        // The site disappeared before but is now back, show it.
   1.194 +        this.showSite(aSite, cb);
   1.195 +      else
   1.196 +        // The site's position has changed, move it around.
   1.197 +        this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: cb});
   1.198 +    }, this);
   1.199 +
   1.200 +    if (callback) {
   1.201 +      Promise.all(batch).then(callback);
   1.202 +    }
   1.203 +  },
   1.204 +
   1.205 +  /**
   1.206 +   * Listens for the 'transitionend' event on a given node and calls the given
   1.207 +   * callback.
   1.208 +   * @param aNode The node that is transitioned.
   1.209 +   * @param aProperties The properties we'll wait to be transitioned.
   1.210 +   * @param aCallback The callback to call when finished.
   1.211 +   */
   1.212 +  _whenTransitionEnded:
   1.213 +    function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) {
   1.214 +
   1.215 +    let props = new Set(aProperties);
   1.216 +    aNode.addEventListener("transitionend", function onEnd(e) {
   1.217 +      if (props.has(e.propertyName)) {
   1.218 +        aNode.removeEventListener("transitionend", onEnd);
   1.219 +        aCallback();
   1.220 +      }
   1.221 +    });
   1.222 +  },
   1.223 +
   1.224 +  /**
   1.225 +   * Gets a given node's opacity value.
   1.226 +   * @param aNode The node to get the opacity value from.
   1.227 +   * @return The node's opacity value.
   1.228 +   */
   1.229 +  _getNodeOpacity: function Transformation_getNodeOpacity(aNode) {
   1.230 +    let cstyle = window.getComputedStyle(aNode, null);
   1.231 +    return cstyle.getPropertyValue("opacity");
   1.232 +  },
   1.233 +
   1.234 +  /**
   1.235 +   * Sets a given node's opacity.
   1.236 +   * @param aNode The node to set the opacity value for.
   1.237 +   * @param aOpacity The opacity value to set.
   1.238 +   * @param aCallback The callback to call when finished.
   1.239 +   */
   1.240 +  _setNodeOpacity:
   1.241 +    function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) {
   1.242 +
   1.243 +    if (this._getNodeOpacity(aNode) == aOpacity) {
   1.244 +      if (aCallback)
   1.245 +        aCallback();
   1.246 +    } else {
   1.247 +      if (aCallback) {
   1.248 +        this._whenTransitionEnded(aNode, ["opacity"], aCallback);
   1.249 +      }
   1.250 +
   1.251 +      aNode.style.opacity = aOpacity;
   1.252 +    }
   1.253 +  },
   1.254 +
   1.255 +  /**
   1.256 +   * Moves a site to the cell with the given index.
   1.257 +   * @param aSite The site to move.
   1.258 +   * @param aIndex The target cell's index.
   1.259 +   * @param aOptions Options that are directly passed to slideSiteTo().
   1.260 +   */
   1.261 +  _moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) {
   1.262 +    this.freezeSitePosition(aSite);
   1.263 +    this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
   1.264 +  },
   1.265 +
   1.266 +  /**
   1.267 +   * Checks whether a site is currently frozen.
   1.268 +   * @param aSite The site to check.
   1.269 +   * @return Whether the given site is frozen.
   1.270 +   */
   1.271 +  _isFrozen: function Transformation_isFrozen(aSite) {
   1.272 +    return aSite.node.hasAttribute("frozen");
   1.273 +  }
   1.274 +};

mercurial