summaryrefslogtreecommitdiffstats
path: root/application/palemoon/base/content/newtab/dropTargetShim.js
diff options
context:
space:
mode:
Diffstat (limited to 'application/palemoon/base/content/newtab/dropTargetShim.js')
-rw-r--r--application/palemoon/base/content/newtab/dropTargetShim.js188
1 files changed, 188 insertions, 0 deletions
diff --git a/application/palemoon/base/content/newtab/dropTargetShim.js b/application/palemoon/base/content/newtab/dropTargetShim.js
new file mode 100644
index 000000000..a85a6ccd6
--- /dev/null
+++ b/application/palemoon/base/content/newtab/dropTargetShim.js
@@ -0,0 +1,188 @@
+#ifdef 0
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#endif
+
+/**
+ * This singleton provides a custom drop target detection. We need this because
+ * the default DnD target detection relies on the cursor's position. We want
+ * to pick a drop target based on the dragged site's position.
+ */
+let gDropTargetShim = {
+ /**
+ * Cache for the position of all cells, cleaned after drag finished.
+ */
+ _cellPositions: null,
+
+ /**
+ * The last drop target that was hovered.
+ */
+ _lastDropTarget: null,
+
+ /**
+ * Initializes the drop target shim.
+ */
+ init: function DropTargetShim_init() {
+ let node = gGrid.node;
+
+ // Add drag event handlers.
+ node.addEventListener("dragstart", this, true);
+ node.addEventListener("dragend", this, true);
+ },
+
+ /**
+ * Handles all shim events.
+ */
+ handleEvent: function DropTargetShim_handleEvent(aEvent) {
+ switch (aEvent.type) {
+ case "dragstart":
+ this._start(aEvent);
+ break;
+ case "dragover":
+ this._dragover(aEvent);
+ break;
+ case "dragend":
+ this._end(aEvent);
+ break;
+ }
+ },
+
+ /**
+ * Handles the 'dragstart' event.
+ * @param aEvent The 'dragstart' event.
+ */
+ _start: function DropTargetShim_start(aEvent) {
+ if (aEvent.target.classList.contains("newtab-link")) {
+ gGrid.lock();
+
+ // XXX bug 505521 - Listen for dragover on the document.
+ document.documentElement.addEventListener("dragover", this, false);
+ }
+ },
+
+ /**
+ * Handles the 'drag' event and determines the current drop target.
+ * @param aEvent The 'drag' event.
+ */
+ _drag: function DropTargetShim_drag(aEvent) {
+ // Let's see if we find a drop target.
+ let target = this._findDropTarget(aEvent);
+
+ if (target != this._lastDropTarget) {
+ if (this._lastDropTarget)
+ // We left the last drop target.
+ this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
+
+ if (target)
+ // We're now hovering a (new) drop target.
+ this._dispatchEvent(aEvent, "dragenter", target);
+
+ if (this._lastDropTarget)
+ // We left the last drop target.
+ this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget);
+
+ this._lastDropTarget = target;
+ }
+ },
+
+ /**
+ * Handles the 'dragover' event as long as bug 505521 isn't fixed to get
+ * current mouse cursor coordinates while dragging.
+ * @param aEvent The 'dragover' event.
+ */
+ _dragover: function DropTargetShim_dragover(aEvent) {
+ let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode;
+ gDrag.drag(sourceNode._newtabSite, aEvent);
+
+ this._drag(aEvent);
+ },
+
+ /**
+ * Handles the 'dragend' event.
+ * @param aEvent The 'dragend' event.
+ */
+ _end: function DropTargetShim_end(aEvent) {
+ // Make sure to determine the current drop target in case the dragenter
+ // event hasn't been fired.
+ this._drag(aEvent);
+
+ if (this._lastDropTarget) {
+ if (aEvent.dataTransfer.mozUserCancelled) {
+ // The drag operation was cancelled.
+ this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
+ this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget);
+ } else {
+ // A site was successfully dropped.
+ this._dispatchEvent(aEvent, "drop", this._lastDropTarget);
+ }
+
+ // Clean up.
+ this._lastDropTarget = null;
+ this._cellPositions = null;
+ }
+
+ gGrid.unlock();
+
+ // XXX bug 505521 - Remove the document's dragover listener.
+ document.documentElement.removeEventListener("dragover", this, false);
+ },
+
+ /**
+ * Determines the current drop target by matching the dragged site's position
+ * against all cells in the grid.
+ * @return The currently hovered drop target or null.
+ */
+ _findDropTarget: function DropTargetShim_findDropTarget() {
+ // These are the minimum intersection values - we want to use the cell if
+ // the site is >= 50% hovering its position.
+ let minWidth = gDrag.cellWidth / 2;
+ let minHeight = gDrag.cellHeight / 2;
+
+ let cellPositions = this._getCellPositions();
+ let rect = gTransformation.getNodePosition(gDrag.draggedSite.node);
+
+ // Compare each cell's position to the dragged site's position.
+ for (let i = 0; i < cellPositions.length; i++) {
+ let inter = rect.intersect(cellPositions[i].rect);
+
+ // If the intersection is big enough we found a drop target.
+ if (inter.width >= minWidth && inter.height >= minHeight)
+ return cellPositions[i].cell;
+ }
+
+ // No drop target found.
+ return null;
+ },
+
+ /**
+ * Gets the positions of all cell nodes.
+ * @return The (cached) cell positions.
+ */
+ _getCellPositions: function DropTargetShim_getCellPositions() {
+ if (this._cellPositions)
+ return this._cellPositions;
+
+ return this._cellPositions = gGrid.cells.map(function (cell) {
+ return {cell: cell, rect: gTransformation.getNodePosition(cell.node)};
+ });
+ },
+
+ /**
+ * Dispatches a custom DragEvent on the given target node.
+ * @param aEvent The source event.
+ * @param aType The event type.
+ * @param aTarget The target node that receives the event.
+ */
+ _dispatchEvent:
+ function DropTargetShim_dispatchEvent(aEvent, aType, aTarget) {
+
+ let node = aTarget.node;
+ let event = document.createEvent("DragEvents");
+
+ event.initDragEvent(aType, true, true, window, 0, 0, 0, 0, 0, false, false,
+ false, false, 0, node, aEvent.dataTransfer);
+
+ node.dispatchEvent(event);
+ }
+};