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