| // 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. |
| |
| /** |
| * @fileoverview DragWrapper |
| * A class for simplifying HTML5 drag and drop. Classes should use this to |
| * handle the nitty gritty of nested drag enters and leaves. |
| */ |
| cr.define('cr.ui', function() { |
| /** |
| * Creates a DragWrapper which listens for drag target events on |target| and |
| * delegates event handling to |handler|. The |handler| must implement: |
| * shouldAcceptDrag |
| * doDragEnter |
| * doDragLeave |
| * doDragOver |
| * doDrop |
| */ |
| function DragWrapper(target, handler) { |
| this.initialize(target, handler); |
| } |
| |
| DragWrapper.prototype = { |
| initialize: function(target, handler) { |
| target.addEventListener('dragenter', |
| this.onDragEnter_.bind(this)); |
| target.addEventListener('dragover', this.onDragOver_.bind(this)); |
| target.addEventListener('drop', this.onDrop_.bind(this)); |
| target.addEventListener('dragleave', this.onDragLeave_.bind(this)); |
| |
| this.target_ = target; |
| this.handler_ = handler; |
| }, |
| |
| /** |
| * The number of un-paired dragenter events that have fired on |this|. This |
| * is incremented by |onDragEnter_| and decremented by |onDragLeave_|. This |
| * is necessary because dragging over child widgets will fire additional |
| * enter and leave events on |this|. A non-zero value does not necessarily |
| * indicate that |isCurrentDragTarget()| is true. |
| * @type {number} |
| * @private |
| */ |
| dragEnters_: 0, |
| |
| /** |
| * Whether the tile page is currently being dragged over with data it can |
| * accept. |
| * @type {boolean} |
| */ |
| get isCurrentDragTarget() { |
| return this.target_.classList.contains('drag-target'); |
| }, |
| |
| /** |
| * Handler for dragenter events fired on |target_|. |
| * @param {Event} e A MouseEvent for the drag. |
| * @private |
| */ |
| onDragEnter_: function(e) { |
| if (++this.dragEnters_ == 1) { |
| if (this.handler_.shouldAcceptDrag(e)) { |
| this.target_.classList.add('drag-target'); |
| this.handler_.doDragEnter(e); |
| } |
| } else { |
| // Sometimes we'll get an enter event over a child element without an |
| // over event following it. In this case we have to still call the |
| // drag over handler so that we make the necessary updates (one visible |
| // symptom of not doing this is that the cursor's drag state will |
| // flicker during drags). |
| this.onDragOver_(e); |
| } |
| }, |
| |
| /** |
| * Thunk for dragover events fired on |target_|. |
| * @param {Event} e A MouseEvent for the drag. |
| * @private |
| */ |
| onDragOver_: function(e) { |
| if (!this.target_.classList.contains('drag-target')) |
| return; |
| this.handler_.doDragOver(e); |
| }, |
| |
| /** |
| * Thunk for drop events fired on |target_|. |
| * @param {Event} e A MouseEvent for the drag. |
| * @private |
| */ |
| onDrop_: function(e) { |
| this.dragEnters_ = 0; |
| if (!this.target_.classList.contains('drag-target')) |
| return; |
| this.target_.classList.remove('drag-target'); |
| this.handler_.doDrop(e); |
| }, |
| |
| /** |
| * Thunk for dragleave events fired on |target_|. |
| * @param {Event} e A MouseEvent for the drag. |
| * @private |
| */ |
| onDragLeave_: function(e) { |
| if (--this.dragEnters_ > 0) |
| return; |
| |
| this.target_.classList.remove('drag-target'); |
| this.handler_.doDragLeave(e); |
| }, |
| }; |
| |
| return { |
| DragWrapper: DragWrapper |
| }; |
| }); |