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() --> |
| 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 // |
| 30 |
| 31 function DragAndDropController(delegate) { |
| 32 this.delegate_ = delegate; |
| 33 |
| 34 this.installHandlers_(); |
| 35 } |
| 36 |
| 37 DragAndDropController.prototype = { |
| 38 startX_: 0, |
| 39 startY_: 0, |
| 40 startScreenX_: 0, |
| 41 startScreenY_: 0, |
| 42 |
| 43 installHandlers_: function() { |
| 44 var el = this.delegate_.dragContainer; |
| 45 el.addEventListener('dragstart', this.handleDragStart_.bind(this)); |
| 46 el.addEventListener('dragenter', this.handleDragEnter_.bind(this)); |
| 47 el.addEventListener('dragover', this.handleDragOver_.bind(this)); |
| 48 el.addEventListener('dragleave', this.handleDragLeave_.bind(this)); |
| 49 el.addEventListener('drop', this.handleDrop_.bind(this)); |
| 50 el.addEventListener('dragend', this.handleDragEnd_.bind(this)); |
| 51 el.addEventListener('drag', this.handleDrag_.bind(this)); |
| 52 el.addEventListener('mousedown', this.handleMouseDown_.bind(this)); |
| 53 }, |
| 54 |
| 55 getCoordinates_: function(e) { |
| 56 var rect = this.delegate_.dragContainer.getBoundingClientRect(); |
| 57 var coordinates = { |
| 58 x: e.clientX + window.scrollX - rect.left, |
| 59 y: e.clientY + window.scrollY - rect.top |
| 60 }; |
| 61 |
| 62 // If we're in an RTL language, reflect the coordinates so the delegate |
| 63 // doesn't need to worry about it. |
| 64 if (isRtl()) |
| 65 coordinates.x = this.delegate_.dragContainer.offsetWidth - coordinates.x; |
| 66 |
| 67 return coordinates; |
| 68 }, |
| 69 |
| 70 // Listen to mousedown to get the relative position of the cursor when |
| 71 // starting drag and drop. |
| 72 handleMouseDown_: function(e) { |
| 73 var item = this.delegate_.getItem(e); |
| 74 if (!item) |
| 75 return; |
| 76 |
| 77 this.startX_ = item.offsetLeft; |
| 78 this.startY_ = item.offsetTop; |
| 79 this.startScreenX_ = e.screenX; |
| 80 this.startScreenY_ = e.screenY; |
| 81 |
| 82 // We don't want to focus the item on mousedown. However, to prevent |
| 83 // focus one has to call preventDefault but this also prevents the drag |
| 84 // and drop (sigh) so we only prevent it when the user is not doing a |
| 85 // left mouse button drag. |
| 86 if (e.button != 0) // LEFT |
| 87 e.preventDefault(); |
| 88 }, |
| 89 |
| 90 handleDragStart_: function(e) { |
| 91 var item = this.delegate_.getItem(e); |
| 92 if (!item) |
| 93 return; |
| 94 |
| 95 // Don't set data since HTML5 does not allow setting the name for |
| 96 // url-list. Instead, we just rely on the dragging of link behavior. |
| 97 this.delegate_.dragItem = item; |
| 98 item.classList.add('dragging'); |
| 99 |
| 100 e.dataTransfer.effectAllowed = 'copyLinkMove'; |
| 101 }, |
| 102 |
| 103 handleDragEnter_: function(e) { |
| 104 if (this.delegate_.canDropOn(this.getCoordinates_(e))) |
| 105 e.preventDefault(); |
| 106 }, |
| 107 |
| 108 handleDragOver_: function(e) { |
| 109 var coordinates = this.getCoordinates_(e); |
| 110 if (!this.delegate_.canDropOn(coordinates)) |
| 111 return; |
| 112 |
| 113 this.delegate_.setDragPlaceholder(coordinates); |
| 114 e.preventDefault(); |
| 115 e.dataTransfer.dropEffect = 'move'; |
| 116 }, |
| 117 |
| 118 handleDragLeave_: function(e) { |
| 119 if (this.delegate_.canDropOn(this.getCoordinates_(e))) |
| 120 e.preventDefault(); |
| 121 }, |
| 122 |
| 123 handleDrop_: function(e) { |
| 124 var dragItem = this.delegate_.dragItem; |
| 125 if (!dragItem) |
| 126 return; |
| 127 |
| 128 this.delegate_.dragItem = null; |
| 129 this.delegate_.saveDrag(); |
| 130 |
| 131 setTimeout(function() { |
| 132 dragItem.classList.remove('dragging'); |
| 133 }, this.delegate_.transitionsDuration + 10); |
| 134 }, |
| 135 |
| 136 handleDragEnd_: function(e) { |
| 137 return this.handleDrop_(e); |
| 138 }, |
| 139 |
| 140 handleDrag_: function(e) { |
| 141 // Moves the drag item making sure that it is not displayed outside the |
| 142 // browser viewport. |
| 143 var dragItem = this.delegate_.dragItem; |
| 144 var rect = this.delegate_.dragContainer.getBoundingClientRect(); |
| 145 |
| 146 var x = this.startX_ + e.screenX - this.startScreenX_; |
| 147 var y = this.startY_ + e.screenY - this.startScreenY_; |
| 148 |
| 149 // The position of the item is relative to #apps so we need to |
| 150 // subtract that when calculating the allowed position. |
| 151 x = Math.max(x, -rect.left); |
| 152 x = Math.min(x, document.body.clientWidth - rect.left - |
| 153 dragItem.offsetWidth - 2); |
| 154 |
| 155 // The shadow is 2px |
| 156 y = Math.max(-rect.top, y); |
| 157 y = Math.min(y, document.body.clientHeight - rect.top - |
| 158 dragItem.offsetHeight - 2); |
| 159 |
| 160 // Override right in case of RTL. |
| 161 dragItem.style.left = x + 'px'; |
| 162 dragItem.style.top = y + 'px'; |
| 163 } |
| 164 }; |
OLD | NEW |