blob: 510c1f981936a4af0cefdd06458412578dff40ae [file] [log] [blame]
// 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
};
});