Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 implements site dragging functionality. |
michael@0 | 9 | */ |
michael@0 | 10 | let gDrag = { |
michael@0 | 11 | /** |
michael@0 | 12 | * The site offset to the drag start point. |
michael@0 | 13 | */ |
michael@0 | 14 | _offsetX: null, |
michael@0 | 15 | _offsetY: null, |
michael@0 | 16 | |
michael@0 | 17 | /** |
michael@0 | 18 | * The site that is dragged. |
michael@0 | 19 | */ |
michael@0 | 20 | _draggedSite: null, |
michael@0 | 21 | get draggedSite() this._draggedSite, |
michael@0 | 22 | |
michael@0 | 23 | /** |
michael@0 | 24 | * The cell width/height at the point the drag started. |
michael@0 | 25 | */ |
michael@0 | 26 | _cellWidth: null, |
michael@0 | 27 | _cellHeight: null, |
michael@0 | 28 | get cellWidth() this._cellWidth, |
michael@0 | 29 | get cellHeight() this._cellHeight, |
michael@0 | 30 | |
michael@0 | 31 | /** |
michael@0 | 32 | * Start a new drag operation. |
michael@0 | 33 | * @param aSite The site that's being dragged. |
michael@0 | 34 | * @param aEvent The 'dragstart' event. |
michael@0 | 35 | */ |
michael@0 | 36 | start: function Drag_start(aSite, aEvent) { |
michael@0 | 37 | this._draggedSite = aSite; |
michael@0 | 38 | |
michael@0 | 39 | // Mark nodes as being dragged. |
michael@0 | 40 | let selector = ".newtab-site, .newtab-control, .newtab-thumbnail"; |
michael@0 | 41 | let parentCell = aSite.node.parentNode; |
michael@0 | 42 | let nodes = parentCell.querySelectorAll(selector); |
michael@0 | 43 | for (let i = 0; i < nodes.length; i++) |
michael@0 | 44 | nodes[i].setAttribute("dragged", "true"); |
michael@0 | 45 | |
michael@0 | 46 | parentCell.setAttribute("dragged", "true"); |
michael@0 | 47 | |
michael@0 | 48 | this._setDragData(aSite, aEvent); |
michael@0 | 49 | |
michael@0 | 50 | // Store the cursor offset. |
michael@0 | 51 | let node = aSite.node; |
michael@0 | 52 | let rect = node.getBoundingClientRect(); |
michael@0 | 53 | this._offsetX = aEvent.clientX - rect.left; |
michael@0 | 54 | this._offsetY = aEvent.clientY - rect.top; |
michael@0 | 55 | |
michael@0 | 56 | // Store the cell dimensions. |
michael@0 | 57 | let cellNode = aSite.cell.node; |
michael@0 | 58 | this._cellWidth = cellNode.offsetWidth; |
michael@0 | 59 | this._cellHeight = cellNode.offsetHeight; |
michael@0 | 60 | |
michael@0 | 61 | gTransformation.freezeSitePosition(aSite); |
michael@0 | 62 | }, |
michael@0 | 63 | |
michael@0 | 64 | /** |
michael@0 | 65 | * Handles the 'drag' event. |
michael@0 | 66 | * @param aSite The site that's being dragged. |
michael@0 | 67 | * @param aEvent The 'drag' event. |
michael@0 | 68 | */ |
michael@0 | 69 | drag: function Drag_drag(aSite, aEvent) { |
michael@0 | 70 | // Get the viewport size. |
michael@0 | 71 | let {clientWidth, clientHeight} = document.documentElement; |
michael@0 | 72 | |
michael@0 | 73 | // We'll want a padding of 5px. |
michael@0 | 74 | let border = 5; |
michael@0 | 75 | |
michael@0 | 76 | // Enforce minimum constraints to keep the drag image inside the window. |
michael@0 | 77 | let left = Math.max(scrollX + aEvent.clientX - this._offsetX, border); |
michael@0 | 78 | let top = Math.max(scrollY + aEvent.clientY - this._offsetY, border); |
michael@0 | 79 | |
michael@0 | 80 | // Enforce maximum constraints to keep the drag image inside the window. |
michael@0 | 81 | left = Math.min(left, scrollX + clientWidth - this.cellWidth - border); |
michael@0 | 82 | top = Math.min(top, scrollY + clientHeight - this.cellHeight - border); |
michael@0 | 83 | |
michael@0 | 84 | // Update the drag image's position. |
michael@0 | 85 | gTransformation.setSitePosition(aSite, {left: left, top: top}); |
michael@0 | 86 | }, |
michael@0 | 87 | |
michael@0 | 88 | /** |
michael@0 | 89 | * Ends the current drag operation. |
michael@0 | 90 | * @param aSite The site that's being dragged. |
michael@0 | 91 | * @param aEvent The 'dragend' event. |
michael@0 | 92 | */ |
michael@0 | 93 | end: function Drag_end(aSite, aEvent) { |
michael@0 | 94 | let nodes = gGrid.node.querySelectorAll("[dragged]") |
michael@0 | 95 | for (let i = 0; i < nodes.length; i++) |
michael@0 | 96 | nodes[i].removeAttribute("dragged"); |
michael@0 | 97 | |
michael@0 | 98 | // Slide the dragged site back into its cell (may be the old or the new cell). |
michael@0 | 99 | gTransformation.slideSiteTo(aSite, aSite.cell, {unfreeze: true}); |
michael@0 | 100 | |
michael@0 | 101 | this._draggedSite = null; |
michael@0 | 102 | }, |
michael@0 | 103 | |
michael@0 | 104 | /** |
michael@0 | 105 | * Checks whether we're responsible for a given drag event. |
michael@0 | 106 | * @param aEvent The drag event to check. |
michael@0 | 107 | * @return Whether we should handle this drag and drop operation. |
michael@0 | 108 | */ |
michael@0 | 109 | isValid: function Drag_isValid(aEvent) { |
michael@0 | 110 | let link = gDragDataHelper.getLinkFromDragEvent(aEvent); |
michael@0 | 111 | |
michael@0 | 112 | // Check that the drag data is non-empty. |
michael@0 | 113 | // Can happen when dragging places folders. |
michael@0 | 114 | if (!link || !link.url) { |
michael@0 | 115 | return false; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | // Check that we're not accepting URLs which would inherit the caller's |
michael@0 | 119 | // principal (such as javascript: or data:). |
michael@0 | 120 | return gLinkChecker.checkLoadURI(link.url); |
michael@0 | 121 | }, |
michael@0 | 122 | |
michael@0 | 123 | /** |
michael@0 | 124 | * Initializes the drag data for the current drag operation. |
michael@0 | 125 | * @param aSite The site that's being dragged. |
michael@0 | 126 | * @param aEvent The 'dragstart' event. |
michael@0 | 127 | */ |
michael@0 | 128 | _setDragData: function Drag_setDragData(aSite, aEvent) { |
michael@0 | 129 | let {url, title} = aSite; |
michael@0 | 130 | |
michael@0 | 131 | let dt = aEvent.dataTransfer; |
michael@0 | 132 | dt.mozCursor = "default"; |
michael@0 | 133 | dt.effectAllowed = "move"; |
michael@0 | 134 | dt.setData("text/plain", url); |
michael@0 | 135 | dt.setData("text/uri-list", url); |
michael@0 | 136 | dt.setData("text/x-moz-url", url + "\n" + title); |
michael@0 | 137 | dt.setData("text/html", "<a href=\"" + url + "\">" + url + "</a>"); |
michael@0 | 138 | |
michael@0 | 139 | // Create and use an empty drag element. We don't want to use the default |
michael@0 | 140 | // drag image with its default opacity. |
michael@0 | 141 | let dragElement = document.createElementNS(HTML_NAMESPACE, "div"); |
michael@0 | 142 | dragElement.classList.add("newtab-drag"); |
michael@0 | 143 | let scrollbox = document.getElementById("newtab-scrollbox"); |
michael@0 | 144 | scrollbox.appendChild(dragElement); |
michael@0 | 145 | dt.setDragImage(dragElement, 0, 0); |
michael@0 | 146 | |
michael@0 | 147 | // After the 'dragstart' event has been processed we can remove the |
michael@0 | 148 | // temporary drag element from the DOM. |
michael@0 | 149 | setTimeout(function () scrollbox.removeChild(dragElement), 0); |
michael@0 | 150 | } |
michael@0 | 151 | }; |