Index: chrome/browser/resources/ntp/drag_drop_controller.js |
diff --git a/chrome/browser/resources/ntp/drag_drop_controller.js b/chrome/browser/resources/ntp/drag_drop_controller.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fe604a41ee9ee583829280cc8e9c4d5209c65a33 |
--- /dev/null |
+++ b/chrome/browser/resources/ntp/drag_drop_controller.js |
@@ -0,0 +1,164 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// The delegate interface: |
+// dragContainer --> |
+// element containing the draggable items |
+// |
+// transitionsDuration --> |
+// length of time of transitions in ms |
+// |
+// dragItem --> |
+// get / set property containing the item being dragged |
+// |
+// getItem(e) --> |
+// get's the item that is under the mouse event |e| |
+// |
+// canDropOn(coordinates) --> |
+// returns true if the coordinates (relative to the drag container) |
+// point to a valid place to drop an item |
+// |
+// setDragPlaceholder(coordinates) --> |
+// tells the delegate that the dragged item is currently above |
+// the specified coordinates. |
+// |
+// saveDrag() --> |
+// tells the delegate that the drag is done. move the item to the |
+// position last specified by setDragPlaceholder. (e.g., commit changes) |
+// |
+ |
+function DragAndDropController(delegate) { |
+ this.delegate_ = delegate; |
+ |
+ this.installHandlers_(); |
+} |
+ |
+DragAndDropController.prototype = { |
+ startX_: 0, |
+ startY_: 0, |
+ startScreenX_: 0, |
+ startScreenY_: 0, |
+ |
+ installHandlers_: function() { |
+ var el = this.delegate_.dragContainer; |
+ el.addEventListener('dragstart', this.handleDragStart_.bind(this)); |
+ el.addEventListener('dragenter', this.handleDragEnter_.bind(this)); |
+ el.addEventListener('dragover', this.handleDragOver_.bind(this)); |
+ el.addEventListener('dragleave', this.handleDragLeave_.bind(this)); |
+ el.addEventListener('drop', this.handleDrop_.bind(this)); |
+ el.addEventListener('dragend', this.handleDragEnd_.bind(this)); |
+ el.addEventListener('drag', this.handleDrag_.bind(this)); |
+ el.addEventListener('mousedown', this.handleMouseDown_.bind(this)); |
+ }, |
+ |
+ getCoordinates_: function(e) { |
+ var rect = this.delegate_.dragContainer.getBoundingClientRect(); |
+ var coordinates = { |
+ x: e.clientX + window.scrollX - rect.left, |
+ y: e.clientY + window.scrollY - rect.top |
+ }; |
+ |
+ // If we're in an RTL language, reflect the coordinates so the delegate |
+ // doesn't need to worry about it. |
+ if (isRtl()) |
+ coordinates.x = this.delegate_.dragContainer.offsetWidth - coordinates.x; |
+ |
+ return coordinates; |
+ }, |
+ |
+ // Listen to mousedown to get the relative position of the cursor when |
+ // starting drag and drop. |
+ handleMouseDown_: function(e) { |
+ var item = this.delegate_.getItem(e); |
+ if (!item) |
+ return; |
+ |
+ this.startX_ = item.offsetLeft; |
+ this.startY_ = item.offsetTop; |
+ this.startScreenX_ = e.screenX; |
+ this.startScreenY_ = e.screenY; |
+ |
+ // We don't want to focus the item on mousedown. However, to prevent |
+ // focus one has to call preventDefault but this also prevents the drag |
+ // and drop (sigh) so we only prevent it when the user is not doing a |
+ // left mouse button drag. |
+ if (e.button != 0) // LEFT |
+ e.preventDefault(); |
+ }, |
+ |
+ handleDragStart_: function(e) { |
+ var item = this.delegate_.getItem(e); |
+ if (!item) |
+ return; |
+ |
+ // Don't set data since HTML5 does not allow setting the name for |
+ // url-list. Instead, we just rely on the dragging of link behavior. |
+ this.delegate_.dragItem = item; |
+ item.classList.add('dragging'); |
+ |
+ e.dataTransfer.effectAllowed = 'copyLinkMove'; |
+ }, |
+ |
+ handleDragEnter_: function(e) { |
+ if (this.delegate_.canDropOn(this.getCoordinates_(e))) |
+ e.preventDefault(); |
+ }, |
+ |
+ handleDragOver_: function(e) { |
+ var coordinates = this.getCoordinates_(e); |
+ if (!this.delegate_.canDropOn(coordinates)) |
+ return; |
+ |
+ this.delegate_.setDragPlaceholder(coordinates); |
+ e.preventDefault(); |
+ e.dataTransfer.dropEffect = 'move'; |
+ }, |
+ |
+ handleDragLeave_: function(e) { |
+ if (this.delegate_.canDropOn(this.getCoordinates_(e))) |
+ e.preventDefault(); |
+ }, |
+ |
+ handleDrop_: function(e) { |
+ var dragItem = this.delegate_.dragItem; |
+ if (!dragItem) |
+ return; |
+ |
+ this.delegate_.dragItem = null; |
+ this.delegate_.saveDrag(); |
+ |
+ setTimeout(function() { |
+ dragItem.classList.remove('dragging'); |
+ }, this.delegate_.transitionsDuration + 10); |
+ }, |
+ |
+ handleDragEnd_: function(e) { |
+ return this.handleDrop_(e); |
+ }, |
+ |
+ handleDrag_: function(e) { |
+ // Moves the drag item making sure that it is not displayed outside the |
+ // browser viewport. |
+ var dragItem = this.delegate_.dragItem; |
+ var rect = this.delegate_.dragContainer.getBoundingClientRect(); |
+ |
+ var x = this.startX_ + e.screenX - this.startScreenX_; |
+ var y = this.startY_ + e.screenY - this.startScreenY_; |
+ |
+ // The position of the item is relative to #apps so we need to |
+ // subtract that when calculating the allowed position. |
+ x = Math.max(x, -rect.left); |
+ x = Math.min(x, document.body.clientWidth - rect.left - |
+ dragItem.offsetWidth - 2); |
+ |
+ // The shadow is 2px |
+ y = Math.max(-rect.top, y); |
+ y = Math.min(y, document.body.clientHeight - rect.top - |
+ dragItem.offsetHeight - 2); |
+ |
+ // Override right in case of RTL. |
+ dragItem.style.left = x + 'px'; |
+ dragItem.style.top = y + 'px'; |
+ } |
+}; |