Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 a custom drop target detection. We need this because
9 * the default DnD target detection relies on the cursor's position. We want
10 * to pick a drop target based on the dragged site's position.
11 */
12 let gDropTargetShim = {
13 /**
14 * Cache for the position of all cells, cleaned after drag finished.
15 */
16 _cellPositions: null,
18 /**
19 * The last drop target that was hovered.
20 */
21 _lastDropTarget: null,
23 /**
24 * Initializes the drop target shim.
25 */
26 init: function () {
27 gGrid.node.addEventListener("dragstart", this, true);
28 },
30 /**
31 * Add all event listeners needed during a drag operation.
32 */
33 _addEventListeners: function () {
34 gGrid.node.addEventListener("dragend", this);
36 let docElement = document.documentElement;
37 docElement.addEventListener("dragover", this);
38 docElement.addEventListener("dragenter", this);
39 docElement.addEventListener("drop", this);
40 },
42 /**
43 * Remove all event listeners that were needed during a drag operation.
44 */
45 _removeEventListeners: function () {
46 gGrid.node.removeEventListener("dragend", this);
48 let docElement = document.documentElement;
49 docElement.removeEventListener("dragover", this);
50 docElement.removeEventListener("dragenter", this);
51 docElement.removeEventListener("drop", this);
52 },
54 /**
55 * Handles all shim events.
56 */
57 handleEvent: function (aEvent) {
58 switch (aEvent.type) {
59 case "dragstart":
60 this._dragstart(aEvent);
61 break;
62 case "dragenter":
63 aEvent.preventDefault();
64 break;
65 case "dragover":
66 this._dragover(aEvent);
67 break;
68 case "drop":
69 this._drop(aEvent);
70 break;
71 case "dragend":
72 this._dragend(aEvent);
73 break;
74 }
75 },
77 /**
78 * Handles the 'dragstart' event.
79 * @param aEvent The 'dragstart' event.
80 */
81 _dragstart: function (aEvent) {
82 if (aEvent.target.classList.contains("newtab-link")) {
83 gGrid.lock();
84 this._addEventListeners();
85 }
86 },
88 /**
89 * Handles the 'dragover' event.
90 * @param aEvent The 'dragover' event.
91 */
92 _dragover: function (aEvent) {
93 // XXX bug 505521 - Use the dragover event to retrieve the
94 // current mouse coordinates while dragging.
95 let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode;
96 gDrag.drag(sourceNode._newtabSite, aEvent);
98 // Find the current drop target, if there's one.
99 this._updateDropTarget(aEvent);
101 // If we have a valid drop target,
102 // let the drag-and-drop service know.
103 if (this._lastDropTarget) {
104 aEvent.preventDefault();
105 }
106 },
108 /**
109 * Handles the 'drop' event.
110 * @param aEvent The 'drop' event.
111 */
112 _drop: function (aEvent) {
113 // We're accepting all drops.
114 aEvent.preventDefault();
116 // Make sure to determine the current drop target
117 // in case the dragover event hasn't been fired.
118 this._updateDropTarget(aEvent);
120 // A site was successfully dropped.
121 this._dispatchEvent(aEvent, "drop", this._lastDropTarget);
122 },
124 /**
125 * Handles the 'dragend' event.
126 * @param aEvent The 'dragend' event.
127 */
128 _dragend: function (aEvent) {
129 if (this._lastDropTarget) {
130 if (aEvent.dataTransfer.mozUserCancelled) {
131 // The drag operation was cancelled.
132 this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
133 this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget);
134 }
136 // Clean up.
137 this._lastDropTarget = null;
138 this._cellPositions = null;
139 }
141 gGrid.unlock();
142 this._removeEventListeners();
143 },
145 /**
146 * Tries to find the current drop target and will fire
147 * appropriate dragenter, dragexit, and dragleave events.
148 * @param aEvent The current drag event.
149 */
150 _updateDropTarget: function (aEvent) {
151 // Let's see if we find a drop target.
152 let target = this._findDropTarget(aEvent);
154 if (target != this._lastDropTarget) {
155 if (this._lastDropTarget)
156 // We left the last drop target.
157 this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
159 if (target)
160 // We're now hovering a (new) drop target.
161 this._dispatchEvent(aEvent, "dragenter", target);
163 if (this._lastDropTarget)
164 // We left the last drop target.
165 this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget);
167 this._lastDropTarget = target;
168 }
169 },
171 /**
172 * Determines the current drop target by matching the dragged site's position
173 * against all cells in the grid.
174 * @return The currently hovered drop target or null.
175 */
176 _findDropTarget: function () {
177 // These are the minimum intersection values - we want to use the cell if
178 // the site is >= 50% hovering its position.
179 let minWidth = gDrag.cellWidth / 2;
180 let minHeight = gDrag.cellHeight / 2;
182 let cellPositions = this._getCellPositions();
183 let rect = gTransformation.getNodePosition(gDrag.draggedSite.node);
185 // Compare each cell's position to the dragged site's position.
186 for (let i = 0; i < cellPositions.length; i++) {
187 let inter = rect.intersect(cellPositions[i].rect);
189 // If the intersection is big enough we found a drop target.
190 if (inter.width >= minWidth && inter.height >= minHeight)
191 return cellPositions[i].cell;
192 }
194 // No drop target found.
195 return null;
196 },
198 /**
199 * Gets the positions of all cell nodes.
200 * @return The (cached) cell positions.
201 */
202 _getCellPositions: function DropTargetShim_getCellPositions() {
203 if (this._cellPositions)
204 return this._cellPositions;
206 return this._cellPositions = gGrid.cells.map(function (cell) {
207 return {cell: cell, rect: gTransformation.getNodePosition(cell.node)};
208 });
209 },
211 /**
212 * Dispatches a custom DragEvent on the given target node.
213 * @param aEvent The source event.
214 * @param aType The event type.
215 * @param aTarget The target node that receives the event.
216 */
217 _dispatchEvent: function (aEvent, aType, aTarget) {
218 let node = aTarget.node;
219 let event = document.createEvent("DragEvents");
221 // The event should not bubble to prevent recursion.
222 event.initDragEvent(aType, false, true, window, 0, 0, 0, 0, 0, false, false,
223 false, false, 0, node, aEvent.dataTransfer);
225 node.dispatchEvent(event);
226 }
227 };