browser/base/content/newtab/dropTargetShim.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 };

mercurial