Thu, 22 Jan 2015 13:21:57 +0100
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 provides functionality to update the current grid to a new |
michael@0 | 9 | * set of pinned and blocked sites. It adds, moves and removes sites. |
michael@0 | 10 | */ |
michael@0 | 11 | let gUpdater = { |
michael@0 | 12 | /** |
michael@0 | 13 | * Updates the current grid according to its pinned and blocked sites. |
michael@0 | 14 | * This removes old, moves existing and creates new sites to fill gaps. |
michael@0 | 15 | * @param aCallback The callback to call when finished. |
michael@0 | 16 | */ |
michael@0 | 17 | updateGrid: function Updater_updateGrid(aCallback) { |
michael@0 | 18 | let links = gLinks.getLinks().slice(0, gGrid.cells.length); |
michael@0 | 19 | |
michael@0 | 20 | // Find all sites that remain in the grid. |
michael@0 | 21 | let sites = this._findRemainingSites(links); |
michael@0 | 22 | |
michael@0 | 23 | let self = this; |
michael@0 | 24 | |
michael@0 | 25 | // Remove sites that are no longer in the grid. |
michael@0 | 26 | this._removeLegacySites(sites, function () { |
michael@0 | 27 | // Freeze all site positions so that we can move their DOM nodes around |
michael@0 | 28 | // without any visual impact. |
michael@0 | 29 | self._freezeSitePositions(sites); |
michael@0 | 30 | |
michael@0 | 31 | // Move the sites' DOM nodes to their new position in the DOM. This will |
michael@0 | 32 | // have no visual effect as all the sites have been frozen and will |
michael@0 | 33 | // remain in their current position. |
michael@0 | 34 | self._moveSiteNodes(sites); |
michael@0 | 35 | |
michael@0 | 36 | // Now it's time to animate the sites actually moving to their new |
michael@0 | 37 | // positions. |
michael@0 | 38 | self._rearrangeSites(sites, function () { |
michael@0 | 39 | // Try to fill empty cells and finish. |
michael@0 | 40 | self._fillEmptyCells(links, aCallback); |
michael@0 | 41 | |
michael@0 | 42 | // Update other pages that might be open to keep them synced. |
michael@0 | 43 | gAllPages.update(gPage); |
michael@0 | 44 | }); |
michael@0 | 45 | }); |
michael@0 | 46 | }, |
michael@0 | 47 | |
michael@0 | 48 | /** |
michael@0 | 49 | * Takes an array of links and tries to correlate them to sites contained in |
michael@0 | 50 | * the current grid. If no corresponding site can be found (i.e. the link is |
michael@0 | 51 | * new and a site will be created) then just set it to null. |
michael@0 | 52 | * @param aLinks The array of links to find sites for. |
michael@0 | 53 | * @return Array of sites mapped to the given links (can contain null values). |
michael@0 | 54 | */ |
michael@0 | 55 | _findRemainingSites: function Updater_findRemainingSites(aLinks) { |
michael@0 | 56 | let map = {}; |
michael@0 | 57 | |
michael@0 | 58 | // Create a map to easily retrieve the site for a given URL. |
michael@0 | 59 | gGrid.sites.forEach(function (aSite) { |
michael@0 | 60 | if (aSite) |
michael@0 | 61 | map[aSite.url] = aSite; |
michael@0 | 62 | }); |
michael@0 | 63 | |
michael@0 | 64 | // Map each link to its corresponding site, if any. |
michael@0 | 65 | return aLinks.map(function (aLink) { |
michael@0 | 66 | return aLink && (aLink.url in map) && map[aLink.url]; |
michael@0 | 67 | }); |
michael@0 | 68 | }, |
michael@0 | 69 | |
michael@0 | 70 | /** |
michael@0 | 71 | * Freezes the given sites' positions. |
michael@0 | 72 | * @param aSites The array of sites to freeze. |
michael@0 | 73 | */ |
michael@0 | 74 | _freezeSitePositions: function Updater_freezeSitePositions(aSites) { |
michael@0 | 75 | aSites.forEach(function (aSite) { |
michael@0 | 76 | if (aSite) |
michael@0 | 77 | gTransformation.freezeSitePosition(aSite); |
michael@0 | 78 | }); |
michael@0 | 79 | }, |
michael@0 | 80 | |
michael@0 | 81 | /** |
michael@0 | 82 | * Moves the given sites' DOM nodes to their new positions. |
michael@0 | 83 | * @param aSites The array of sites to move. |
michael@0 | 84 | */ |
michael@0 | 85 | _moveSiteNodes: function Updater_moveSiteNodes(aSites) { |
michael@0 | 86 | let cells = gGrid.cells; |
michael@0 | 87 | |
michael@0 | 88 | // Truncate the given array of sites to not have more sites than cells. |
michael@0 | 89 | // This can happen when the user drags a bookmark (or any other new kind |
michael@0 | 90 | // of link) onto the grid. |
michael@0 | 91 | let sites = aSites.slice(0, cells.length); |
michael@0 | 92 | |
michael@0 | 93 | sites.forEach(function (aSite, aIndex) { |
michael@0 | 94 | let cell = cells[aIndex]; |
michael@0 | 95 | let cellSite = cell.site; |
michael@0 | 96 | |
michael@0 | 97 | // The site's position didn't change. |
michael@0 | 98 | if (!aSite || cellSite != aSite) { |
michael@0 | 99 | let cellNode = cell.node; |
michael@0 | 100 | |
michael@0 | 101 | // Empty the cell if necessary. |
michael@0 | 102 | if (cellSite) |
michael@0 | 103 | cellNode.removeChild(cellSite.node); |
michael@0 | 104 | |
michael@0 | 105 | // Put the new site in place, if any. |
michael@0 | 106 | if (aSite) |
michael@0 | 107 | cellNode.appendChild(aSite.node); |
michael@0 | 108 | } |
michael@0 | 109 | }, this); |
michael@0 | 110 | }, |
michael@0 | 111 | |
michael@0 | 112 | /** |
michael@0 | 113 | * Rearranges the given sites and slides them to their new positions. |
michael@0 | 114 | * @param aSites The array of sites to re-arrange. |
michael@0 | 115 | * @param aCallback The callback to call when finished. |
michael@0 | 116 | */ |
michael@0 | 117 | _rearrangeSites: function Updater_rearrangeSites(aSites, aCallback) { |
michael@0 | 118 | let options = {callback: aCallback, unfreeze: true}; |
michael@0 | 119 | gTransformation.rearrangeSites(aSites, options); |
michael@0 | 120 | }, |
michael@0 | 121 | |
michael@0 | 122 | /** |
michael@0 | 123 | * Removes all sites from the grid that are not in the given links array or |
michael@0 | 124 | * exceed the grid. |
michael@0 | 125 | * @param aSites The array of sites remaining in the grid. |
michael@0 | 126 | * @param aCallback The callback to call when finished. |
michael@0 | 127 | */ |
michael@0 | 128 | _removeLegacySites: function Updater_removeLegacySites(aSites, aCallback) { |
michael@0 | 129 | let batch = []; |
michael@0 | 130 | |
michael@0 | 131 | // Delete sites that were removed from the grid. |
michael@0 | 132 | gGrid.sites.forEach(function (aSite) { |
michael@0 | 133 | // The site must be valid and not in the current grid. |
michael@0 | 134 | if (!aSite || aSites.indexOf(aSite) != -1) |
michael@0 | 135 | return; |
michael@0 | 136 | |
michael@0 | 137 | let deferred = Promise.defer(); |
michael@0 | 138 | batch.push(deferred.promise); |
michael@0 | 139 | |
michael@0 | 140 | // Fade out the to-be-removed site. |
michael@0 | 141 | gTransformation.hideSite(aSite, function () { |
michael@0 | 142 | let node = aSite.node; |
michael@0 | 143 | |
michael@0 | 144 | // Remove the site from the DOM. |
michael@0 | 145 | node.parentNode.removeChild(node); |
michael@0 | 146 | deferred.resolve(); |
michael@0 | 147 | }); |
michael@0 | 148 | }); |
michael@0 | 149 | |
michael@0 | 150 | Promise.all(batch).then(aCallback); |
michael@0 | 151 | }, |
michael@0 | 152 | |
michael@0 | 153 | /** |
michael@0 | 154 | * Tries to fill empty cells with new links if available. |
michael@0 | 155 | * @param aLinks The array of links. |
michael@0 | 156 | * @param aCallback The callback to call when finished. |
michael@0 | 157 | */ |
michael@0 | 158 | _fillEmptyCells: function Updater_fillEmptyCells(aLinks, aCallback) { |
michael@0 | 159 | let {cells, sites} = gGrid; |
michael@0 | 160 | let batch = []; |
michael@0 | 161 | |
michael@0 | 162 | // Find empty cells and fill them. |
michael@0 | 163 | sites.forEach(function (aSite, aIndex) { |
michael@0 | 164 | if (aSite || !aLinks[aIndex]) |
michael@0 | 165 | return; |
michael@0 | 166 | |
michael@0 | 167 | let deferred = Promise.defer(); |
michael@0 | 168 | batch.push(deferred.promise); |
michael@0 | 169 | |
michael@0 | 170 | // Create the new site and fade it in. |
michael@0 | 171 | let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]); |
michael@0 | 172 | |
michael@0 | 173 | // Set the site's initial opacity to zero. |
michael@0 | 174 | site.node.style.opacity = 0; |
michael@0 | 175 | |
michael@0 | 176 | // Flush all style changes for the dynamically inserted site to make |
michael@0 | 177 | // the fade-in transition work. |
michael@0 | 178 | window.getComputedStyle(site.node).opacity; |
michael@0 | 179 | gTransformation.showSite(site, function () deferred.resolve()); |
michael@0 | 180 | }); |
michael@0 | 181 | |
michael@0 | 182 | Promise.all(batch).then(aCallback); |
michael@0 | 183 | } |
michael@0 | 184 | }; |