Wed, 31 Dec 2014 06:55:46 +0100
Added tag TORBROWSER_REPLICA for changeset 6474c204b198
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
7 /**
8 * This singleton provides the ability to re-arrange the current grid to
9 * indicate the transformation that results from dropping a cell at a certain
10 * position.
11 */
12 let gDropPreview = {
13 /**
14 * Rearranges the sites currently contained in the grid when a site would be
15 * dropped onto the given cell.
16 * @param aCell The drop target cell.
17 * @return The re-arranged array of sites.
18 */
19 rearrange: function DropPreview_rearrange(aCell) {
20 let sites = gGrid.sites;
22 // Insert the dragged site into the current grid.
23 this._insertDraggedSite(sites, aCell);
25 // After the new site has been inserted we need to correct the positions
26 // of all pinned tabs that have been moved around.
27 this._repositionPinnedSites(sites, aCell);
29 return sites;
30 },
32 /**
33 * Inserts the currently dragged site into the given array of sites.
34 * @param aSites The array of sites to insert into.
35 * @param aCell The drop target cell.
36 */
37 _insertDraggedSite: function DropPreview_insertDraggedSite(aSites, aCell) {
38 let dropIndex = aCell.index;
39 let draggedSite = gDrag.draggedSite;
41 // We're currently dragging a site.
42 if (draggedSite) {
43 let dragCell = draggedSite.cell;
44 let dragIndex = dragCell.index;
46 // Move the dragged site into its new position.
47 if (dragIndex != dropIndex) {
48 aSites.splice(dragIndex, 1);
49 aSites.splice(dropIndex, 0, draggedSite);
50 }
51 // We're handling an external drag item.
52 } else {
53 aSites.splice(dropIndex, 0, null);
54 }
55 },
57 /**
58 * Correct the position of all pinned sites that might have been moved to
59 * different positions after the dragged site has been inserted.
60 * @param aSites The array of sites containing the dragged site.
61 * @param aCell The drop target cell.
62 */
63 _repositionPinnedSites:
64 function DropPreview_repositionPinnedSites(aSites, aCell) {
66 // Collect all pinned sites.
67 let pinnedSites = this._filterPinnedSites(aSites, aCell);
69 // Correct pinned site positions.
70 pinnedSites.forEach(function (aSite) {
71 aSites[aSites.indexOf(aSite)] = aSites[aSite.cell.index];
72 aSites[aSite.cell.index] = aSite;
73 }, this);
75 // There might be a pinned cell that got pushed out of the grid, try to
76 // sneak it in by removing a lower-priority cell.
77 if (this._hasOverflowedPinnedSite(aSites, aCell))
78 this._repositionOverflowedPinnedSite(aSites, aCell);
79 },
81 /**
82 * Filter pinned sites out of the grid that are still on their old positions
83 * and have not moved.
84 * @param aSites The array of sites to filter.
85 * @param aCell The drop target cell.
86 * @return The filtered array of sites.
87 */
88 _filterPinnedSites: function DropPreview_filterPinnedSites(aSites, aCell) {
89 let draggedSite = gDrag.draggedSite;
91 // When dropping on a cell that contains a pinned site make sure that all
92 // pinned cells surrounding the drop target are moved as well.
93 let range = this._getPinnedRange(aCell);
95 return aSites.filter(function (aSite, aIndex) {
96 // The site must be valid, pinned and not the dragged site.
97 if (!aSite || aSite == draggedSite || !aSite.isPinned())
98 return false;
100 let index = aSite.cell.index;
102 // If it's not in the 'pinned range' it's a valid pinned site.
103 return (index > range.end || index < range.start);
104 });
105 },
107 /**
108 * Determines the range of pinned sites surrounding the drop target cell.
109 * @param aCell The drop target cell.
110 * @return The range of pinned cells.
111 */
112 _getPinnedRange: function DropPreview_getPinnedRange(aCell) {
113 let dropIndex = aCell.index;
114 let range = {start: dropIndex, end: dropIndex};
116 // We need a pinned range only when dropping on a pinned site.
117 if (aCell.containsPinnedSite()) {
118 let links = gPinnedLinks.links;
120 // Find all previous siblings of the drop target that are pinned as well.
121 while (range.start && links[range.start - 1])
122 range.start--;
124 let maxEnd = links.length - 1;
126 // Find all next siblings of the drop target that are pinned as well.
127 while (range.end < maxEnd && links[range.end + 1])
128 range.end++;
129 }
131 return range;
132 },
134 /**
135 * Checks if the given array of sites contains a pinned site that has
136 * been pushed out of the grid.
137 * @param aSites The array of sites to check.
138 * @param aCell The drop target cell.
139 * @return Whether there is an overflowed pinned cell.
140 */
141 _hasOverflowedPinnedSite:
142 function DropPreview_hasOverflowedPinnedSite(aSites, aCell) {
144 // If the drop target isn't pinned there's no way a pinned site has been
145 // pushed out of the grid so we can just exit here.
146 if (!aCell.containsPinnedSite())
147 return false;
149 let cells = gGrid.cells;
151 // No cells have been pushed out of the grid, nothing to do here.
152 if (aSites.length <= cells.length)
153 return false;
155 let overflowedSite = aSites[cells.length];
157 // Nothing to do if the site that got pushed out of the grid is not pinned.
158 return (overflowedSite && overflowedSite.isPinned());
159 },
161 /**
162 * We have a overflowed pinned site that we need to re-position so that it's
163 * visible again. We try to find a lower-priority cell (empty or containing
164 * an unpinned site) that we can move it to.
165 * @param aSites The array of sites.
166 * @param aCell The drop target cell.
167 */
168 _repositionOverflowedPinnedSite:
169 function DropPreview_repositionOverflowedPinnedSite(aSites, aCell) {
171 // Try to find a lower-priority cell (empty or containing an unpinned site).
172 let index = this._indexOfLowerPrioritySite(aSites, aCell);
174 if (index > -1) {
175 let cells = gGrid.cells;
176 let dropIndex = aCell.index;
178 // Move all pinned cells to their new positions to let the overflowed
179 // site fit into the grid.
180 for (let i = index + 1, lastPosition = index; i < aSites.length; i++) {
181 if (i != dropIndex) {
182 aSites[lastPosition] = aSites[i];
183 lastPosition = i;
184 }
185 }
187 // Finally, remove the overflowed site from its previous position.
188 aSites.splice(cells.length, 1);
189 }
190 },
192 /**
193 * Finds the index of the last cell that is empty or contains an unpinned
194 * site. These are considered to be of a lower priority.
195 * @param aSites The array of sites.
196 * @param aCell The drop target cell.
197 * @return The cell's index.
198 */
199 _indexOfLowerPrioritySite:
200 function DropPreview_indexOfLowerPrioritySite(aSites, aCell) {
202 let cells = gGrid.cells;
203 let dropIndex = aCell.index;
205 // Search (beginning with the last site in the grid) for a site that is
206 // empty or unpinned (an thus lower-priority) and can be pushed out of the
207 // grid instead of the pinned site.
208 for (let i = cells.length - 1; i >= 0; i--) {
209 // The cell that is our drop target is not a good choice.
210 if (i == dropIndex)
211 continue;
213 let site = aSites[i];
215 // We can use the cell only if it's empty or the site is un-pinned.
216 if (!site || !site.isPinned())
217 return i;
218 }
220 return -1;
221 }
222 };