| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // The delegate interface: | |
| 6 // dragContainer --> | |
| 7 // element containing the draggable items | |
| 8 // | |
| 9 // transitionsDuration --> | |
| 10 // length of time of transitions in ms | |
| 11 // | |
| 12 // dragItem --> | |
| 13 // get / set property containing the item being dragged | |
| 14 // | |
| 15 // getItem(e) --> | |
| 16 // get's the item that is under the mouse event |e| | |
| 17 // | |
| 18 // canDropOn(coordinates) --> | |
| 19 // returns true if the coordinates (relative to the drag container) | |
| 20 // point to a valid place to drop an item | |
| 21 // | |
| 22 // setDragPlaceholder(coordinates) --> | |
| 23 // tells the delegate that the dragged item is currently above | |
| 24 // the specified coordinates. | |
| 25 // | |
| 26 // saveDrag(draggedItem) --> | |
| 27 // tells the delegate that the drag is done. move the item to the | |
| 28 // position last specified by setDragPlaceholder (e.g., commit changes). | |
| 29 // draggedItem was the item being dragged. | |
| 30 // | |
| 31 | |
| 32 // The distance, in px, that the mouse must move before initiating a drag. | |
| 33 var DRAG_THRESHOLD = 35; | |
| 34 | |
| 35 function DragAndDropController(delegate) { | |
| 36 this.delegate_ = delegate; | |
| 37 | |
| 38 // Install the 'mousedown' handler, the entry point to drag and drop. | |
| 39 var el = this.delegate_.dragContainer; | |
| 40 el.addEventListener('mousedown', this.handleMouseDown_.bind(this)); | |
| 41 } | |
| 42 | |
| 43 DragAndDropController.prototype = { | |
| 44 isDragging_: false, | |
| 45 startItem_: null, | |
| 46 startItemXY_: null, | |
| 47 startMouseXY_: null, | |
| 48 mouseXY_: null, | |
| 49 | |
| 50 // Enables the handlers that are only active during a drag. | |
| 51 enableHandlers_: function() { | |
| 52 // Record references to the generated functions so we can | |
| 53 // remove the listeners later. | |
| 54 this.mouseMoveListener_ = this.handleMouseMove_.bind(this); | |
| 55 this.mouseUpListener_ = this.handleMouseUp_.bind(this); | |
| 56 this.scrollListener_ = this.handleScroll_.bind(this); | |
| 57 | |
| 58 document.addEventListener('mousemove', this.mouseMoveListener_, true); | |
| 59 document.addEventListener('mouseup', this.mouseUpListener_, true); | |
| 60 document.addEventListener('scroll', this.scrollListener_, true); | |
| 61 }, | |
| 62 | |
| 63 disableHandlers_: function() { | |
| 64 document.removeEventListener('mousemove', this.mouseMoveListener_, true); | |
| 65 document.removeEventListener('mouseup', this.mouseUpListener_, true); | |
| 66 document.removeEventListener('scroll', this.scrollListener_, true); | |
| 67 }, | |
| 68 | |
| 69 isDragging: function() { | |
| 70 return this.isDragging_; | |
| 71 }, | |
| 72 | |
| 73 distance_: function(p1, p2) { | |
| 74 var x2 = Math.pow(p1.x - p2.x, 2); | |
| 75 var y2 = Math.pow(p1.y - p2.y, 2); | |
| 76 return Math.sqrt(x2 + y2); | |
| 77 }, | |
| 78 | |
| 79 // Shifts the client coordinates, |xy|, so they are relative to the top left | |
| 80 // of the drag container. | |
| 81 getCoordinates_: function(xy) { | |
| 82 var rect = this.delegate_.dragContainer.getBoundingClientRect(); | |
| 83 var coordinates = { | |
| 84 x: xy.x - rect.left, | |
| 85 y: xy.y - rect.top | |
| 86 }; | |
| 87 | |
| 88 // If we're in an RTL language, reflect the coordinates so the delegate | |
| 89 // doesn't need to worry about it. | |
| 90 if (isRtl()) | |
| 91 coordinates.x = this.delegate_.dragContainer.offsetWidth - coordinates.x; | |
| 92 | |
| 93 return coordinates; | |
| 94 }, | |
| 95 | |
| 96 // Listen to mousedown to get the relative position of the cursor when | |
| 97 // starting drag and drop. | |
| 98 handleMouseDown_: function(e) { | |
| 99 var item = this.delegate_.getItem(e); | |
| 100 | |
| 101 // This can't be a drag & drop event if it's not the left mouse button | |
| 102 // or if the mouse is not above an item. We also bail out if the dragging | |
| 103 // flag is still set (the flag remains around for a bit so that 'click' | |
| 104 // event handlers can distinguish between a click and drag). | |
| 105 if (!item || e.button != 0 || this.isDragging()) | |
| 106 return; | |
| 107 | |
| 108 this.startItem_ = item; | |
| 109 this.startItemXY_ = {x: item.offsetLeft, y: item.offsetTop}; | |
| 110 this.startMouseXY_ = {x: e.clientX, y: e.clientY}; | |
| 111 this.startScrollXY_ = {x: window.scrollX, y: window.scrollY}; | |
| 112 | |
| 113 this.enableHandlers_(); | |
| 114 }, | |
| 115 | |
| 116 handleMouseMove_: function(e) { | |
| 117 this.mouseXY_ = {x: e.clientX, y: e.clientY}; | |
| 118 | |
| 119 if (this.isDragging()) { | |
| 120 this.handleDrag_(); | |
| 121 return; | |
| 122 } | |
| 123 | |
| 124 // Initiate the drag if the mouse has moved far enough. | |
| 125 if (this.distance_(this.startMouseXY_, this.mouseXY_) >= DRAG_THRESHOLD) | |
| 126 this.handleDragStart_(); | |
| 127 }, | |
| 128 | |
| 129 handleMouseUp_: function() { | |
| 130 this.handleDrop_(); | |
| 131 }, | |
| 132 | |
| 133 handleScroll_: function(e) { | |
| 134 if (this.isDragging()) | |
| 135 this.handleDrag_(); | |
| 136 }, | |
| 137 | |
| 138 handleDragStart_: function() { | |
| 139 // Use the item that the mouse was above when 'mousedown' fired. | |
| 140 var item = this.startItem_; | |
| 141 if (!item) | |
| 142 return; | |
| 143 | |
| 144 this.isDragging_ = true; | |
| 145 this.delegate_.dragItem = item; | |
| 146 item.classList.add('dragging'); | |
| 147 item.style.zIndex = 2; | |
| 148 }, | |
| 149 | |
| 150 handleDragOver_: function() { | |
| 151 var coordinates = this.getCoordinates_(this.mouseXY_); | |
| 152 if (!this.delegate_.canDropOn(coordinates)) | |
| 153 return; | |
| 154 | |
| 155 this.delegate_.setDragPlaceholder(coordinates); | |
| 156 }, | |
| 157 | |
| 158 handleDrop_: function() { | |
| 159 this.disableHandlers_(); | |
| 160 | |
| 161 var dragItem = this.delegate_.dragItem; | |
| 162 if (!dragItem) | |
| 163 return; | |
| 164 | |
| 165 this.delegate_.dragItem = this.startItem_ = null; | |
| 166 this.delegate_.saveDrag(dragItem); | |
| 167 dragItem.classList.remove('dragging'); | |
| 168 | |
| 169 setTimeout(function() { | |
| 170 // Keep the flag around a little so other 'mouseup' and 'click' | |
| 171 // listeners know the event is from a drag operation. | |
| 172 this.isDragging_ = false; | |
| 173 dragItem.style.zIndex = 0; | |
| 174 }.bind(this), this.delegate_.transitionsDuration); | |
| 175 }, | |
| 176 | |
| 177 handleDrag_: function() { | |
| 178 // Moves the drag item making sure that it is not displayed outside the | |
| 179 // drag container. | |
| 180 var dragItem = this.delegate_.dragItem; | |
| 181 var dragContainer = this.delegate_.dragContainer; | |
| 182 var rect = dragContainer.getBoundingClientRect(); | |
| 183 | |
| 184 // First, move the item the same distance the mouse has moved. | |
| 185 var x = this.startItemXY_.x + this.mouseXY_.x - this.startMouseXY_.x + | |
| 186 window.scrollX - this.startScrollXY_.x; | |
| 187 var y = this.startItemXY_.y + this.mouseXY_.y - this.startMouseXY_.y + | |
| 188 window.scrollY - this.startScrollXY_.y; | |
| 189 | |
| 190 var w = this.delegate_.dimensions.width; | |
| 191 var h = this.delegate_.dimensions.height; | |
| 192 | |
| 193 var offset = parseInt(getComputedStyle(dragContainer).marginLeft); | |
| 194 | |
| 195 // The position of the item is relative to the drag container. We | |
| 196 // want to make sure that half of the item's width or height is within | |
| 197 // the container. | |
| 198 x = Math.max(x, - w / 2 - offset); | |
| 199 x = Math.min(x, rect.width + w / 2 - offset); | |
| 200 | |
| 201 y = Math.max(- h / 2, y); | |
| 202 y = Math.min(y, rect.height - h / 2); | |
| 203 | |
| 204 dragItem.style.left = x + 'px'; | |
| 205 dragItem.style.top = y + 'px'; | |
| 206 | |
| 207 // Update the layouts and positions based on the new drag location. | |
| 208 this.handleDragOver_(); | |
| 209 | |
| 210 this.delegate_.scrollPage(this.mouseXY_); | |
| 211 } | |
| 212 }; | |
| OLD | NEW |