updater.js 5.6 KB

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