1302 lines
43 KiB
JavaScript
1302 lines
43 KiB
JavaScript
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview A DragListGroup is a class representing a group of one or more
|
|
* "drag lists" with items that can be dragged within them and between them.
|
|
*
|
|
* @see ../demos/draglistgroup.html
|
|
*/
|
|
|
|
|
|
goog.provide('goog.fx.DragListDirection');
|
|
goog.provide('goog.fx.DragListGroup');
|
|
goog.provide('goog.fx.DragListGroup.EventType');
|
|
goog.provide('goog.fx.DragListGroupEvent');
|
|
|
|
goog.require('goog.asserts');
|
|
goog.require('goog.dom');
|
|
goog.require('goog.dom.NodeType');
|
|
goog.require('goog.dom.classes');
|
|
goog.require('goog.events.Event');
|
|
goog.require('goog.events.EventHandler');
|
|
goog.require('goog.events.EventTarget');
|
|
goog.require('goog.events.EventType');
|
|
goog.require('goog.fx.Dragger');
|
|
goog.require('goog.fx.Dragger.EventType');
|
|
goog.require('goog.math.Coordinate');
|
|
goog.require('goog.style');
|
|
|
|
|
|
|
|
/**
|
|
* A class representing a group of one or more "drag lists" with items that can
|
|
* be dragged within them and between them.
|
|
*
|
|
* Example usage:
|
|
* var dragListGroup = new goog.fx.DragListGroup();
|
|
* dragListGroup.setDragItemHandleHoverClass(className1, className2);
|
|
* dragListGroup.setDraggerElClass(className3);
|
|
* dragListGroup.addDragList(vertList, goog.fx.DragListDirection.DOWN);
|
|
* dragListGroup.addDragList(horizList, goog.fx.DragListDirection.RIGHT);
|
|
* dragListGroup.init();
|
|
*
|
|
* @extends {goog.events.EventTarget}
|
|
* @constructor
|
|
*/
|
|
goog.fx.DragListGroup = function() {
|
|
goog.events.EventTarget.call(this);
|
|
|
|
/**
|
|
* The drag lists.
|
|
* @type {Array.<Element>}
|
|
* @private
|
|
*/
|
|
this.dragLists_ = [];
|
|
|
|
/**
|
|
* All the drag items. Set by init().
|
|
* @type {Array.<Element>}
|
|
* @private
|
|
*/
|
|
this.dragItems_ = [];
|
|
|
|
/**
|
|
* Which drag item corresponds to a given handle. Set by init().
|
|
* Specifically, this maps from the unique ID (as given by goog.getUid)
|
|
* of the handle to the drag item.
|
|
* @type {Object}
|
|
* @private
|
|
*/
|
|
this.dragItemForHandle_ = {};
|
|
|
|
/**
|
|
* The event handler for this instance.
|
|
* @type {goog.events.EventHandler}
|
|
* @private
|
|
*/
|
|
this.eventHandler_ = new goog.events.EventHandler(this);
|
|
|
|
/**
|
|
* Whether the setup has been done to make all items in all lists draggable.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
this.isInitialized_ = false;
|
|
|
|
/**
|
|
* Whether the currDragItem is always displayed. By default the list
|
|
* collapses, the currDragItem's display is set to none, when we do not
|
|
* hover over a draglist.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
this.isCurrDragItemAlwaysDisplayed_ = false;
|
|
|
|
/**
|
|
* Whether to update the position of the currDragItem as we drag, i.e.,
|
|
* insert the currDragItem each time to the position where it would land if
|
|
* we were to end the drag at that point. Defaults to true.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
this.updateWhileDragging_ = true;
|
|
};
|
|
goog.inherits(goog.fx.DragListGroup, goog.events.EventTarget);
|
|
|
|
|
|
/**
|
|
* Enum to indicate the direction that a drag list grows.
|
|
* @enum {number}
|
|
*/
|
|
goog.fx.DragListDirection = {
|
|
DOWN: 0, // common
|
|
RIGHT: 2, // common
|
|
LEFT: 3, // uncommon (except perhaps for right-to-left interfaces)
|
|
RIGHT_2D: 4, // common + handles multiple lines if items are wrapped
|
|
LEFT_2D: 5 // for rtl languages
|
|
};
|
|
|
|
|
|
/**
|
|
* Events dispatched by this class.
|
|
* @type {Object}
|
|
*/
|
|
goog.fx.DragListGroup.EventType = {
|
|
BEFOREDRAGSTART: 'beforedragstart',
|
|
DRAGSTART: 'dragstart',
|
|
BEFOREDRAGMOVE: 'beforedragmove',
|
|
DRAGMOVE: 'dragmove',
|
|
BEFOREDRAGEND: 'beforedragend',
|
|
DRAGEND: 'dragend'
|
|
};
|
|
|
|
|
|
// The next 4 are user-supplied CSS classes.
|
|
|
|
|
|
/**
|
|
* The user-supplied CSS classes to add to a drag item on hover (not during a
|
|
* drag action).
|
|
* @type {Array|undefined}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.dragItemHoverClasses_;
|
|
|
|
|
|
/**
|
|
* The user-supplied CSS classes to add to a drag item handle on hover (not
|
|
* during a drag action).
|
|
* @type {Array|undefined}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.dragItemHandleHoverClasses_;
|
|
|
|
|
|
/**
|
|
* The user-supplied CSS classes to add to the current drag item (during a drag
|
|
* action).
|
|
* @type {Array|undefined}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.currDragItemClasses_;
|
|
|
|
|
|
/**
|
|
* The user-supplied CSS class to add to the clone of the current drag item
|
|
* that's actually being dragged around (during a drag action).
|
|
* @type {string|undefined}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.draggerElClass_;
|
|
|
|
|
|
// The next 5 are info applicable during a drag action.
|
|
|
|
|
|
/**
|
|
* The current drag item being moved.
|
|
* Note: This is only defined while a drag action is happening.
|
|
* @type {Element}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.currDragItem_;
|
|
|
|
|
|
/**
|
|
* The drag list that {@code this.currDragItem_} is currently hovering over, or
|
|
* null if it is not hovering over a list.
|
|
* @type {Element}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.currHoverList_;
|
|
|
|
|
|
/**
|
|
* The original drag list that the current drag item came from. We need to
|
|
* remember this in case the user drops the item outside of any lists, in which
|
|
* case we return the item to its original location.
|
|
* Note: This is only defined while a drag action is happening.
|
|
* @type {Element}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.origList_;
|
|
|
|
|
|
/**
|
|
* The original next item in the original list that the current drag item came
|
|
* from. We need to remember this in case the user drops the item outside of
|
|
* any lists, in which case we return the item to its original location.
|
|
* Note: This is only defined while a drag action is happening.
|
|
* @type {Element}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.origNextItem_;
|
|
|
|
|
|
/**
|
|
* The current item in the list we are hovering over. We need to remember
|
|
* this in case we do not update the position of the current drag item while
|
|
* dragging (see {@code updateWhileDragging_}). In this case the current drag
|
|
* item will be inserted into the list before this element when the drag ends.
|
|
* @type {Element}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.currHoverItem_;
|
|
|
|
|
|
/**
|
|
* The clone of the current drag item that's actually being dragged around.
|
|
* Note: This is only defined while a drag action is happening.
|
|
* @type {Element}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.draggerEl_;
|
|
|
|
|
|
/**
|
|
* The dragger object.
|
|
* Note: This is only defined while a drag action is happening.
|
|
* @type {goog.fx.Dragger}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.dragger_;
|
|
|
|
|
|
/**
|
|
* The amount of distance, in pixels, after which a mousedown or touchstart is
|
|
* considered a drag.
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.hysteresisDistance_ = 0;
|
|
|
|
|
|
/**
|
|
* Sets the property of the currDragItem that it is always displayed in the
|
|
* list.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.setIsCurrDragItemAlwaysDisplayed = function() {
|
|
this.isCurrDragItemAlwaysDisplayed_ = true;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the private property updateWhileDragging_ to false. This disables the
|
|
* update of the position of the currDragItem while dragging. It will only be
|
|
* placed to its new location once the drag ends.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.setNoUpdateWhileDragging = function() {
|
|
this.updateWhileDragging_ = false;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the distance the user has to drag the element before a drag operation
|
|
* is started.
|
|
* @param {number} distance The number of pixels after which a mousedown and
|
|
* move is considered a drag.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.setHysteresis = function(distance) {
|
|
this.hysteresisDistance_ = distance;
|
|
};
|
|
|
|
|
|
/**
|
|
* @return {number} distance The number of pixels after which a mousedown and
|
|
* move is considered a drag.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.getHysteresis = function() {
|
|
return this.hysteresisDistance_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Adds a drag list to this DragListGroup.
|
|
* All calls to this method must happen before the call to init().
|
|
* Remember that all child nodes (except text nodes) will be made draggable to
|
|
* any other drag list in this group.
|
|
*
|
|
* @param {Element} dragListElement Must be a container for a list of items
|
|
* that should all be made draggable.
|
|
* @param {goog.fx.DragListDirection} growthDirection The direction that this
|
|
* drag list grows in (i.e. if an item is appended to the DOM, the list's
|
|
* bounding box expands in this direction).
|
|
* @param {boolean=} opt_unused Unused argument.
|
|
* @param {string=} opt_dragHoverClass CSS class to apply to this drag list when
|
|
* the draggerEl hovers over it during a drag action.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.addDragList = function(
|
|
dragListElement, growthDirection, opt_unused, opt_dragHoverClass) {
|
|
goog.asserts.assert(!this.isInitialized_);
|
|
|
|
dragListElement.dlgGrowthDirection_ = growthDirection;
|
|
dragListElement.dlgDragHoverClass_ = opt_dragHoverClass;
|
|
this.dragLists_.push(dragListElement);
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets a user-supplied function used to get the "handle" element for a drag
|
|
* item. The function must accept exactly one argument. The argument may be
|
|
* any drag item element.
|
|
*
|
|
* If not set, the default implementation uses the whole drag item as the
|
|
* handle.
|
|
*
|
|
* @param {function(Element): Element} getHandleForDragItemFn A function that,
|
|
* given any drag item, returns a reference to its "handle" element
|
|
* (which may be the drag item element itself).
|
|
*/
|
|
goog.fx.DragListGroup.prototype.setFunctionToGetHandleForDragItem = function(
|
|
getHandleForDragItemFn) {
|
|
goog.asserts.assert(!this.isInitialized_);
|
|
this.getHandleForDragItem_ = getHandleForDragItemFn;
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets a user-supplied CSS class to add to a drag item on hover (not during a
|
|
* drag action).
|
|
* @param {...!string} var_args The CSS class or classes.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.setDragItemHoverClass = function(var_args) {
|
|
goog.asserts.assert(!this.isInitialized_);
|
|
this.dragItemHoverClasses_ = goog.array.slice(arguments, 0);
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets a user-supplied CSS class to add to a drag item handle on hover (not
|
|
* during a drag action).
|
|
* @param {...!string} var_args The CSS class or classes.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.setDragItemHandleHoverClass = function(
|
|
var_args) {
|
|
goog.asserts.assert(!this.isInitialized_);
|
|
this.dragItemHandleHoverClasses_ = goog.array.slice(arguments, 0);
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets a user-supplied CSS class to add to the current drag item (during a
|
|
* drag action).
|
|
*
|
|
* If not set, the default behavior adds visibility:hidden to the current drag
|
|
* item so that it is a block of empty space in the hover drag list (if any).
|
|
* If this class is set by the user, then the default behavior does not happen
|
|
* (unless, of course, the class also contains visibility:hidden).
|
|
*
|
|
* @param {...!string} var_args The CSS class or classes.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.setCurrDragItemClass = function(var_args) {
|
|
goog.asserts.assert(!this.isInitialized_);
|
|
this.currDragItemClasses_ = goog.array.slice(arguments, 0);
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets a user-supplied CSS class to add to the clone of the current drag item
|
|
* that's actually being dragged around (during a drag action).
|
|
* @param {string} draggerElClass The CSS class.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.setDraggerElClass = function(draggerElClass) {
|
|
goog.asserts.assert(!this.isInitialized_);
|
|
this.draggerElClass_ = draggerElClass;
|
|
};
|
|
|
|
|
|
/**
|
|
* Performs the initial setup to make all items in all lists draggable.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.init = function() {
|
|
if (this.isInitialized_) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0, numLists = this.dragLists_.length; i < numLists; i++) {
|
|
var dragList = this.dragLists_[i];
|
|
|
|
var dragItems = goog.dom.getChildren(dragList);
|
|
for (var j = 0, numItems = dragItems.length; j < numItems; ++j) {
|
|
this.listenForDragEvents(dragItems[j]);
|
|
}
|
|
}
|
|
|
|
this.isInitialized_ = true;
|
|
};
|
|
|
|
|
|
/**
|
|
* Adds a single item to the given drag list and sets up the drag listeners for
|
|
* it.
|
|
* If opt_index is specified the item is inserted at this index, otherwise the
|
|
* item is added as the last child of the list.
|
|
*
|
|
* @param {!Element} list The drag list where to add item to.
|
|
* @param {!Element} item The new element to add.
|
|
* @param {number=} opt_index Index where to insert the item in the list. If not
|
|
* specified item is inserted as the last child of list.
|
|
*/
|
|
goog.fx.DragListGroup.prototype.addItemToDragList = function(list, item,
|
|
opt_index) {
|
|
if (goog.isDef(opt_index)) {
|
|
goog.dom.insertChildAt(list, item, opt_index);
|
|
} else {
|
|
goog.dom.appendChild(list, item);
|
|
}
|
|
this.listenForDragEvents(item);
|
|
};
|
|
|
|
|
|
/** @override */
|
|
goog.fx.DragListGroup.prototype.disposeInternal = function() {
|
|
this.eventHandler_.dispose();
|
|
|
|
for (var i = 0, n = this.dragLists_.length; i < n; i++) {
|
|
var dragList = this.dragLists_[i];
|
|
// Note: IE doesn't allow 'delete' for fields on HTML elements (because
|
|
// they're not real JS objects in IE), so we just set them to undefined.
|
|
dragList.dlgGrowthDirection_ = undefined;
|
|
dragList.dlgDragHoverClass_ = undefined;
|
|
}
|
|
|
|
this.dragLists_.length = 0;
|
|
this.dragItems_.length = 0;
|
|
this.dragItemForHandle_ = null;
|
|
|
|
// In the case where a drag event is currently in-progress and dispose is
|
|
// called, this cleans up the extra state.
|
|
this.cleanupDragDom_();
|
|
|
|
goog.fx.DragListGroup.superClass_.disposeInternal.call(this);
|
|
};
|
|
|
|
|
|
/**
|
|
* Caches the heights of each drag list and drag item, except for the current
|
|
* drag item.
|
|
*
|
|
* @param {Element} currDragItem The item currently being dragged.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.recacheListAndItemBounds_ = function(
|
|
currDragItem) {
|
|
for (var i = 0, n = this.dragLists_.length; i < n; i++) {
|
|
var dragList = this.dragLists_[i];
|
|
dragList.dlgBounds_ = goog.style.getBounds(dragList);
|
|
}
|
|
|
|
for (var i = 0, n = this.dragItems_.length; i < n; i++) {
|
|
var dragItem = this.dragItems_[i];
|
|
if (dragItem != currDragItem) {
|
|
dragItem.dlgBounds_ = goog.style.getBounds(dragItem);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Listens for drag events on the given drag item. This method is currently used
|
|
* to initialize drag items.
|
|
*
|
|
* @param {Element} dragItem the element to initialize. This element has to be
|
|
* in one of the drag lists.
|
|
* @protected
|
|
*/
|
|
goog.fx.DragListGroup.prototype.listenForDragEvents = function(dragItem) {
|
|
var dragItemHandle = this.getHandleForDragItem_(dragItem);
|
|
var uid = goog.getUid(dragItemHandle);
|
|
this.dragItemForHandle_[uid] = dragItem;
|
|
|
|
if (this.dragItemHoverClasses_) {
|
|
this.eventHandler_.listen(
|
|
dragItem, goog.events.EventType.MOUSEOVER,
|
|
this.handleDragItemMouseover_);
|
|
this.eventHandler_.listen(
|
|
dragItem, goog.events.EventType.MOUSEOUT,
|
|
this.handleDragItemMouseout_);
|
|
}
|
|
if (this.dragItemHandleHoverClasses_) {
|
|
this.eventHandler_.listen(
|
|
dragItemHandle, goog.events.EventType.MOUSEOVER,
|
|
this.handleDragItemHandleMouseover_);
|
|
this.eventHandler_.listen(
|
|
dragItemHandle, goog.events.EventType.MOUSEOUT,
|
|
this.handleDragItemHandleMouseout_);
|
|
}
|
|
|
|
this.dragItems_.push(dragItem);
|
|
this.eventHandler_.listen(dragItemHandle,
|
|
[goog.events.EventType.MOUSEDOWN, goog.events.EventType.TOUCHSTART],
|
|
this.handlePotentialDragStart_);
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles mouse and touch events which may start a drag action.
|
|
* @param {!goog.events.BrowserEvent} e MOUSEDOWN or TOUCHSTART event.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.handlePotentialDragStart_ = function(e) {
|
|
var uid = goog.getUid(/** @type {Node} */ (e.currentTarget));
|
|
this.currDragItem_ = /** @type {Element} */ (this.dragItemForHandle_[uid]);
|
|
|
|
this.draggerEl_ = this.cloneNode_(this.currDragItem_);
|
|
if (this.draggerElClass_) {
|
|
// Add CSS class for the clone, if any.
|
|
goog.dom.classes.add(this.draggerEl_, this.draggerElClass_);
|
|
}
|
|
|
|
// Place the clone (i.e. draggerEl) at the same position as the actual
|
|
// current drag item. This is a bit tricky since
|
|
// goog.style.getPageOffset() gets the left-top pos of the border, but
|
|
// goog.style.setPageOffset() sets the left-top pos of the margin.
|
|
// It's difficult to adjust for the margins of the clone because it's
|
|
// difficult to read it: goog.style.getComputedStyle() doesn't work for IE.
|
|
// Instead, our workaround is simply to set the clone's margins to 0px.
|
|
this.draggerEl_.style.margin = '0';
|
|
this.draggerEl_.style.position = 'absolute';
|
|
this.draggerEl_.style.visibility = 'hidden';
|
|
var doc = goog.dom.getOwnerDocument(this.currDragItem_);
|
|
doc.body.appendChild(this.draggerEl_);
|
|
|
|
// Important: goog.style.setPageOffset() only works correctly for IE when the
|
|
// element is already in the document.
|
|
var currDragItemPos = goog.style.getPageOffset(this.currDragItem_);
|
|
goog.style.setPageOffset(this.draggerEl_, currDragItemPos);
|
|
|
|
this.dragger_ = new goog.fx.Dragger(this.draggerEl_);
|
|
this.dragger_.setHysteresis(this.hysteresisDistance_);
|
|
|
|
// Listen to events on the dragger. These handlers will be unregistered at
|
|
// DRAGEND, when the dragger is disposed of. We can't use eventHandler_,
|
|
// because it creates new references to the handler functions at each
|
|
// dragging action, and keeps them until DragListGroup is disposed of.
|
|
goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.START,
|
|
this.handleDragStart_, false, this);
|
|
goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.END,
|
|
this.handleDragEnd_, false, this);
|
|
goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.EARLY_CANCEL,
|
|
this.cleanup_, false, this);
|
|
this.dragger_.startDrag(e);
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles the start of a drag action.
|
|
* @param {!goog.fx.DragEvent} e goog.fx.Dragger.EventType.START event.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.handleDragStart_ = function(e) {
|
|
if (!this.dispatchEvent(new goog.fx.DragListGroupEvent(
|
|
goog.fx.DragListGroup.EventType.BEFOREDRAGSTART, this, e.browserEvent,
|
|
this.currDragItem_, null, null))) {
|
|
e.preventDefault();
|
|
this.cleanup_();
|
|
return;
|
|
}
|
|
|
|
// Record the original location of the current drag item.
|
|
// Note: this.origNextItem_ may be null.
|
|
this.origList_ = /** @type {Element} */ (this.currDragItem_.parentNode);
|
|
this.origNextItem_ = goog.dom.getNextElementSibling(this.currDragItem_);
|
|
this.currHoverItem_ = this.origNextItem_;
|
|
this.currHoverList_ = this.origList_;
|
|
|
|
// If there's a CSS class specified for the current drag item, add it.
|
|
// Otherwise, make the actual current drag item hidden (takes up space).
|
|
if (this.currDragItemClasses_) {
|
|
goog.dom.classes.add.apply(null,
|
|
goog.array.concat(this.currDragItem_, this.currDragItemClasses_));
|
|
} else {
|
|
this.currDragItem_.style.visibility = 'hidden';
|
|
}
|
|
|
|
// Precompute distances from top-left corner to center for efficiency.
|
|
var draggerElSize = goog.style.getSize(this.draggerEl_);
|
|
this.draggerEl_.halfWidth = draggerElSize.width / 2;
|
|
this.draggerEl_.halfHeight = draggerElSize.height / 2;
|
|
|
|
this.draggerEl_.style.visibility = '';
|
|
|
|
// Record the bounds of all the drag lists and all the other drag items. This
|
|
// caching is for efficiency, so that we don't have to recompute the bounds on
|
|
// each drag move. Do this in the state where the current drag item is not in
|
|
// any of the lists, except when update while dragging is disabled, as in this
|
|
// case the current drag item does not get removed until drag ends.
|
|
if (this.updateWhileDragging_) {
|
|
this.currDragItem_.style.display = 'none';
|
|
}
|
|
this.recacheListAndItemBounds_(this.currDragItem_);
|
|
this.currDragItem_.style.display = '';
|
|
|
|
// Listen to events on the dragger.
|
|
goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.DRAG,
|
|
this.handleDragMove_, false, this);
|
|
|
|
this.dispatchEvent(
|
|
new goog.fx.DragListGroupEvent(
|
|
goog.fx.DragListGroup.EventType.DRAGSTART, this, e.browserEvent,
|
|
this.currDragItem_, this.draggerEl_, this.dragger_));
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles a drag movement (i.e. DRAG event fired by the dragger).
|
|
*
|
|
* @param {goog.fx.DragEvent} dragEvent Event object fired by the dragger.
|
|
* @return {boolean} The return value for the event.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.handleDragMove_ = function(dragEvent) {
|
|
|
|
// Compute the center of the dragger element (i.e. the cloned drag item).
|
|
var draggerElPos = goog.style.getPageOffset(this.draggerEl_);
|
|
var draggerElCenter = new goog.math.Coordinate(
|
|
draggerElPos.x + this.draggerEl_.halfWidth,
|
|
draggerElPos.y + this.draggerEl_.halfHeight);
|
|
|
|
// Check whether the center is hovering over one of the drag lists.
|
|
var hoverList = this.getHoverDragList_(draggerElCenter);
|
|
|
|
// If hovering over a list, find the next item (if drag were to end now).
|
|
var hoverNextItem =
|
|
hoverList ? this.getHoverNextItem_(hoverList, draggerElCenter) : null;
|
|
|
|
var rv = this.dispatchEvent(
|
|
new goog.fx.DragListGroupEvent(
|
|
goog.fx.DragListGroup.EventType.BEFOREDRAGMOVE, this, dragEvent,
|
|
this.currDragItem_, this.draggerEl_, this.dragger_,
|
|
draggerElCenter, hoverList, hoverNextItem));
|
|
if (!rv) {
|
|
return false;
|
|
}
|
|
|
|
if (hoverList) {
|
|
if (this.updateWhileDragging_) {
|
|
this.insertCurrDragItem_(hoverList, hoverNextItem);
|
|
} else {
|
|
// If update while dragging is disabled do not insert
|
|
// the dragged item, but update the hovered item instead.
|
|
this.updateCurrHoverItem(hoverNextItem, draggerElCenter);
|
|
}
|
|
this.currDragItem_.style.display = '';
|
|
// Add drag list's hover class (if any).
|
|
if (hoverList.dlgDragHoverClass_) {
|
|
goog.dom.classes.add(hoverList, hoverList.dlgDragHoverClass_);
|
|
}
|
|
|
|
} else {
|
|
// Not hovering over a drag list, so remove the item altogether unless
|
|
// specified otherwise by the user.
|
|
if (!this.isCurrDragItemAlwaysDisplayed_) {
|
|
this.currDragItem_.style.display = 'none';
|
|
}
|
|
|
|
// Remove hover classes (if any) from all drag lists.
|
|
for (var i = 0, n = this.dragLists_.length; i < n; i++) {
|
|
var dragList = this.dragLists_[i];
|
|
if (dragList.dlgDragHoverClass_) {
|
|
goog.dom.classes.remove(dragList, dragList.dlgDragHoverClass_);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the current hover list is different than the last, the lists may have
|
|
// shrunk, so we should recache the bounds.
|
|
if (hoverList != this.currHoverList_) {
|
|
this.currHoverList_ = hoverList;
|
|
this.recacheListAndItemBounds_(this.currDragItem_);
|
|
}
|
|
|
|
this.dispatchEvent(
|
|
new goog.fx.DragListGroupEvent(
|
|
goog.fx.DragListGroup.EventType.DRAGMOVE, this, dragEvent,
|
|
/** @type {Element} */ (this.currDragItem_),
|
|
this.draggerEl_, this.dragger_,
|
|
draggerElCenter, hoverList, hoverNextItem));
|
|
|
|
// Return false to prevent selection due to mouse drag.
|
|
return false;
|
|
};
|
|
|
|
|
|
/**
|
|
* Clear all our temporary fields that are only defined while dragging, and
|
|
* all the bounds info stored on the drag lists and drag elements.
|
|
* @param {!goog.events.Event=} opt_e EARLY_CANCEL event from the dragger if
|
|
* cleanup_ was called as an event handler.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.cleanup_ = function(opt_e) {
|
|
this.cleanupDragDom_();
|
|
|
|
this.currDragItem_ = null;
|
|
this.currHoverList_ = null;
|
|
this.origList_ = null;
|
|
this.origNextItem_ = null;
|
|
this.draggerEl_ = null;
|
|
this.dragger_ = null;
|
|
|
|
// Note: IE doesn't allow 'delete' for fields on HTML elements (because
|
|
// they're not real JS objects in IE), so we just set them to null.
|
|
for (var i = 0, n = this.dragLists_.length; i < n; i++) {
|
|
this.dragLists_[i].dlgBounds_ = null;
|
|
}
|
|
for (var i = 0, n = this.dragItems_.length; i < n; i++) {
|
|
this.dragItems_[i].dlgBounds_ = null;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles the end or the cancellation of a drag action, i.e. END or CLEANUP
|
|
* event fired by the dragger.
|
|
*
|
|
* @param {!goog.fx.DragEvent} dragEvent Event object fired by the dragger.
|
|
* @return {boolean} Whether the event was handled.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.handleDragEnd_ = function(dragEvent) {
|
|
var rv = this.dispatchEvent(
|
|
new goog.fx.DragListGroupEvent(
|
|
goog.fx.DragListGroup.EventType.BEFOREDRAGEND, this, dragEvent,
|
|
/** @type {Element} */ (this.currDragItem_),
|
|
this.draggerEl_, this.dragger_));
|
|
if (!rv) {
|
|
return false;
|
|
}
|
|
|
|
// If update while dragging is disabled insert the current drag item into
|
|
// its intended location.
|
|
if (!this.updateWhileDragging_) {
|
|
this.insertCurrHoverItem();
|
|
}
|
|
|
|
// The DRAGEND handler may need the new order of the list items. Clean up the
|
|
// garbage.
|
|
// TODO(user): Regression test.
|
|
this.cleanupDragDom_();
|
|
|
|
this.dispatchEvent(
|
|
new goog.fx.DragListGroupEvent(
|
|
goog.fx.DragListGroup.EventType.DRAGEND, this, dragEvent,
|
|
this.currDragItem_, this.draggerEl_, this.dragger_));
|
|
|
|
this.cleanup_();
|
|
|
|
return true;
|
|
};
|
|
|
|
|
|
/**
|
|
* Cleans up DOM changes that are made by the {@code handleDrag*} methods.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.cleanupDragDom_ = function() {
|
|
// Disposes of the dragger and remove the cloned drag item.
|
|
goog.dispose(this.dragger_);
|
|
if (this.draggerEl_) {
|
|
goog.dom.removeNode(this.draggerEl_);
|
|
}
|
|
|
|
// If the current drag item is not in any list, put it back in its original
|
|
// location.
|
|
if (this.currDragItem_ && this.currDragItem_.style.display == 'none') {
|
|
// Note: this.origNextItem_ may be null, but insertBefore() still works.
|
|
this.origList_.insertBefore(this.currDragItem_, this.origNextItem_);
|
|
this.currDragItem_.style.display = '';
|
|
}
|
|
|
|
// If there's a CSS class specified for the current drag item, remove it.
|
|
// Otherwise, make the current drag item visible (instead of empty space).
|
|
if (this.currDragItemClasses_ && this.currDragItem_) {
|
|
goog.dom.classes.remove.apply(null,
|
|
goog.array.concat(this.currDragItem_, this.currDragItemClasses_));
|
|
} else if (this.currDragItem_) {
|
|
this.currDragItem_.style.visibility = 'visible';
|
|
}
|
|
|
|
// Remove hover classes (if any) from all drag lists.
|
|
for (var i = 0, n = this.dragLists_.length; i < n; i++) {
|
|
var dragList = this.dragLists_[i];
|
|
if (dragList.dlgDragHoverClass_) {
|
|
goog.dom.classes.remove(dragList, dragList.dlgDragHoverClass_);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Default implementation of the function to get the "handle" element for a
|
|
* drag item. By default, we use the whole drag item as the handle. Users can
|
|
* change this by calling setFunctionToGetHandleForDragItem().
|
|
*
|
|
* @param {Element} dragItem The drag item to get the handle for.
|
|
* @return {Element} The dragItem element itself.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.getHandleForDragItem_ = function(dragItem) {
|
|
return dragItem;
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles a MOUSEOVER event fired on a drag item.
|
|
* @param {goog.events.BrowserEvent} e The event.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.handleDragItemMouseover_ = function(e) {
|
|
goog.dom.classes.add.apply(null,
|
|
goog.array.concat(/** @type {Element} */ (e.currentTarget),
|
|
this.dragItemHoverClasses_));
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles a MOUSEOUT event fired on a drag item.
|
|
* @param {goog.events.BrowserEvent} e The event.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.handleDragItemMouseout_ = function(e) {
|
|
goog.dom.classes.remove.apply(null,
|
|
goog.array.concat(/** @type {Element} */ (e.currentTarget),
|
|
this.dragItemHoverClasses_));
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles a MOUSEOVER event fired on the handle element of a drag item.
|
|
* @param {goog.events.BrowserEvent} e The event.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.handleDragItemHandleMouseover_ = function(e) {
|
|
goog.dom.classes.add.apply(null,
|
|
goog.array.concat(/** @type {Element} */ (e.currentTarget),
|
|
this.dragItemHandleHoverClasses_));
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles a MOUSEOUT event fired on the handle element of a drag item.
|
|
* @param {goog.events.BrowserEvent} e The event.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.handleDragItemHandleMouseout_ = function(e) {
|
|
goog.dom.classes.remove.apply(null,
|
|
goog.array.concat(/** @type {Element} */ (e.currentTarget),
|
|
this.dragItemHandleHoverClasses_));
|
|
};
|
|
|
|
|
|
/**
|
|
* Helper for handleDragMove_().
|
|
* Given the position of the center of the dragger element, figures out whether
|
|
* it's currently hovering over any of the drag lists.
|
|
*
|
|
* @param {goog.math.Coordinate} draggerElCenter The center position of the
|
|
* dragger element.
|
|
* @return {Element} If currently hovering over a drag list, returns the drag
|
|
* list element. Else returns null.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.getHoverDragList_ = function(draggerElCenter) {
|
|
|
|
// If the current drag item was in a list last time we did this, then check
|
|
// that same list first.
|
|
var prevHoverList = null;
|
|
if (this.currDragItem_.style.display != 'none') {
|
|
prevHoverList = /** @type {Element} */ (this.currDragItem_.parentNode);
|
|
// Important: We can't use the cached bounds for this list because the
|
|
// cached bounds are based on the case where the current drag item is not
|
|
// in the list. Since the current drag item is known to be in this list, we
|
|
// must recompute the list's bounds.
|
|
var prevHoverListBounds = goog.style.getBounds(prevHoverList);
|
|
if (this.isInRect_(draggerElCenter, prevHoverListBounds)) {
|
|
return prevHoverList;
|
|
}
|
|
}
|
|
|
|
for (var i = 0, n = this.dragLists_.length; i < n; i++) {
|
|
var dragList = this.dragLists_[i];
|
|
if (dragList == prevHoverList) {
|
|
continue;
|
|
}
|
|
if (this.isInRect_(draggerElCenter, dragList.dlgBounds_)) {
|
|
return dragList;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
|
|
/**
|
|
* Checks whether a coordinate position resides inside a rectangle.
|
|
* @param {goog.math.Coordinate} pos The coordinate position.
|
|
* @param {goog.math.Rect} rect The rectangle.
|
|
* @return {boolean} True if 'pos' is within the bounds of 'rect'.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.isInRect_ = function(pos, rect) {
|
|
return pos.x > rect.left && pos.x < rect.left + rect.width &&
|
|
pos.y > rect.top && pos.y < rect.top + rect.height;
|
|
};
|
|
|
|
|
|
/**
|
|
* Updates the value of currHoverItem_.
|
|
*
|
|
* This method is used for insertion only when updateWhileDragging_ is false.
|
|
* The below implementation is the basic one. This method can be extended by
|
|
* a subclass to support changes to hovered item (eg: highlighting). Parametr
|
|
* opt_draggerElCenter can be used for more sophisticated effects.
|
|
*
|
|
* @param {Element} hoverNextItem element of the list that is hovered over.
|
|
* @param {goog.math.Coordinate=} opt_draggerElCenter current position of
|
|
* the dragged element.
|
|
* @protected
|
|
*/
|
|
goog.fx.DragListGroup.prototype.updateCurrHoverItem = function(
|
|
hoverNextItem, opt_draggerElCenter) {
|
|
if (hoverNextItem) {
|
|
this.currHoverItem_ = hoverNextItem;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Inserts the currently dragged item in its new place.
|
|
*
|
|
* This method is used for insertion only when updateWhileDragging_ is false
|
|
* (otherwise there is no need for that). In the basic implementation
|
|
* the element is inserted before the currently hovered over item (this can
|
|
* be changed by overriding the method in subclasses).
|
|
*
|
|
* @protected
|
|
*/
|
|
goog.fx.DragListGroup.prototype.insertCurrHoverItem = function() {
|
|
this.origList_.insertBefore(this.currDragItem_, this.currHoverItem_);
|
|
};
|
|
|
|
|
|
/**
|
|
* Helper for handleDragMove_().
|
|
* Given the position of the center of the dragger element, plus the drag list
|
|
* that it's currently hovering over, figures out the next drag item in the
|
|
* list that follows the current position of the dragger element. (I.e. if
|
|
* the drag action ends right now, it would become the item after the current
|
|
* drag item.)
|
|
*
|
|
* @param {Element} hoverList The drag list that we're hovering over.
|
|
* @param {goog.math.Coordinate} draggerElCenter The center position of the
|
|
* dragger element.
|
|
* @return {Element} Returns the earliest item in the hover list that belongs
|
|
* after the current position of the dragger element. If all items in the
|
|
* list should come before the current drag item, then returns null.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.getHoverNextItem_ = function(
|
|
hoverList, draggerElCenter) {
|
|
if (hoverList == null) {
|
|
throw Error('getHoverNextItem_ called with null hoverList.');
|
|
}
|
|
|
|
// The definition of what it means for the draggerEl to be "before" a given
|
|
// item in the hover drag list is not always the same. It changes based on
|
|
// the growth direction of the hover drag list in question.
|
|
/** @type {number} */
|
|
var relevantCoord;
|
|
var getRelevantBoundFn;
|
|
var isBeforeFn;
|
|
var pickClosestRow = false;
|
|
var distanceToClosestRow = undefined;
|
|
switch (hoverList.dlgGrowthDirection_) {
|
|
case goog.fx.DragListDirection.DOWN:
|
|
// "Before" means draggerElCenter.y is less than item's bottom y-value.
|
|
relevantCoord = draggerElCenter.y;
|
|
getRelevantBoundFn = goog.fx.DragListGroup.getBottomBound_;
|
|
isBeforeFn = goog.fx.DragListGroup.isLessThan_;
|
|
break;
|
|
case goog.fx.DragListDirection.RIGHT_2D:
|
|
pickClosestRow = true;
|
|
case goog.fx.DragListDirection.RIGHT:
|
|
// "Before" means draggerElCenter.x is less than item's right x-value.
|
|
relevantCoord = draggerElCenter.x;
|
|
getRelevantBoundFn = goog.fx.DragListGroup.getRightBound_;
|
|
isBeforeFn = goog.fx.DragListGroup.isLessThan_;
|
|
break;
|
|
case goog.fx.DragListDirection.LEFT_2D:
|
|
pickClosestRow = true;
|
|
case goog.fx.DragListDirection.LEFT:
|
|
// "Before" means draggerElCenter.x is greater than item's left x-value.
|
|
relevantCoord = draggerElCenter.x;
|
|
getRelevantBoundFn = goog.fx.DragListGroup.getLeftBound_;
|
|
isBeforeFn = goog.fx.DragListGroup.isGreaterThan_;
|
|
break;
|
|
}
|
|
|
|
// This holds the earliest drag item found so far that should come after
|
|
// this.currDragItem_ in the hover drag list (based on draggerElCenter).
|
|
var earliestAfterItem = null;
|
|
// This is the position of the relevant bound for the earliestAfterItem,
|
|
// where "relevant" is determined by the growth direction of hoverList.
|
|
var earliestAfterItemRelevantBound;
|
|
|
|
var hoverListItems = goog.dom.getChildren(hoverList);
|
|
for (var i = 0, n = hoverListItems.length; i < n; i++) {
|
|
var item = hoverListItems[i];
|
|
if (item == this.currDragItem_) {
|
|
continue;
|
|
}
|
|
|
|
var relevantBound = getRelevantBoundFn(item.dlgBounds_);
|
|
// When the hoverlist is broken into multiple rows (i.e., in the case of
|
|
// LEFT_2D and RIGHT_2D) it is no longer enough to only look at the
|
|
// x-coordinate alone in order to find the {@earliestAfterItem} in the
|
|
// hoverlist. Make sure it is chosen from the row closest to the
|
|
// {@code draggerElCenter}.
|
|
if (pickClosestRow) {
|
|
var distanceToRow = goog.fx.DragListGroup.verticalDistanceFromItem_(item,
|
|
draggerElCenter);
|
|
// Initialize the distance to the closest row to the current value if
|
|
// undefined.
|
|
if (!goog.isDef(distanceToClosestRow)) {
|
|
distanceToClosestRow = distanceToRow;
|
|
}
|
|
if (isBeforeFn(relevantCoord, relevantBound) &&
|
|
(earliestAfterItemRelevantBound == undefined ||
|
|
(distanceToRow < distanceToClosestRow) ||
|
|
((distanceToRow == distanceToClosestRow) &&
|
|
(isBeforeFn(relevantBound, earliestAfterItemRelevantBound) ||
|
|
relevantBound == earliestAfterItemRelevantBound)))) {
|
|
earliestAfterItem = item;
|
|
earliestAfterItemRelevantBound = relevantBound;
|
|
}
|
|
// Update distance to closest row.
|
|
if (distanceToRow < distanceToClosestRow) {
|
|
distanceToClosestRow = distanceToRow;
|
|
}
|
|
} else if (isBeforeFn(relevantCoord, relevantBound) &&
|
|
(earliestAfterItemRelevantBound == undefined ||
|
|
isBeforeFn(relevantBound, earliestAfterItemRelevantBound))) {
|
|
earliestAfterItem = item;
|
|
earliestAfterItemRelevantBound = relevantBound;
|
|
}
|
|
}
|
|
// If we ended up picking an element that is not in the closest row it can
|
|
// only happen if we should have picked the last one in which case there is
|
|
// no consecutive element.
|
|
if (!goog.isNull(earliestAfterItem) &&
|
|
goog.fx.DragListGroup.verticalDistanceFromItem_(
|
|
earliestAfterItem, draggerElCenter) > distanceToClosestRow) {
|
|
return null;
|
|
} else {
|
|
return earliestAfterItem;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Private helper for getHoverNextItem().
|
|
* Given an item and a target determine the vertical distance from the item's
|
|
* center to the target.
|
|
* @param {Element} item The item to measure the distance from.
|
|
* @param {goog.math.Coordinate} target The (x,y) coordinate of the target
|
|
* to measure the distance to.
|
|
* @return {number} The vertical distance between the center of the item and
|
|
* the target.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.verticalDistanceFromItem_ = function(item, target) {
|
|
var itemBounds = item.dlgBounds_;
|
|
var itemCenterY = itemBounds.top + (itemBounds.height - 1) / 2;
|
|
return Math.abs(target.y - itemCenterY);
|
|
};
|
|
|
|
|
|
/**
|
|
* Private helper for getHoverNextItem_().
|
|
* Given the bounds of an item, computes the item's bottom y-value.
|
|
* @param {goog.math.Rect} itemBounds The bounds of the item.
|
|
* @return {number} The item's bottom y-value.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.getBottomBound_ = function(itemBounds) {
|
|
return itemBounds.top + itemBounds.height - 1;
|
|
};
|
|
|
|
|
|
/**
|
|
* Private helper for getHoverNextItem_().
|
|
* Given the bounds of an item, computes the item's right x-value.
|
|
* @param {goog.math.Rect} itemBounds The bounds of the item.
|
|
* @return {number} The item's right x-value.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.getRightBound_ = function(itemBounds) {
|
|
return itemBounds.left + itemBounds.width - 1;
|
|
};
|
|
|
|
|
|
/**
|
|
* Private helper for getHoverNextItem_().
|
|
* Given the bounds of an item, computes the item's left x-value.
|
|
* @param {goog.math.Rect} itemBounds The bounds of the item.
|
|
* @return {number} The item's left x-value.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.getLeftBound_ = function(itemBounds) {
|
|
return itemBounds.left || 0;
|
|
};
|
|
|
|
|
|
/**
|
|
* Private helper for getHoverNextItem_().
|
|
* @param {number} a Number to compare.
|
|
* @param {number} b Number to compare.
|
|
* @return {boolean} Whether a is less than b.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.isLessThan_ = function(a, b) {
|
|
return a < b;
|
|
};
|
|
|
|
|
|
/**
|
|
* Private helper for getHoverNextItem_().
|
|
* @param {number} a Number to compare.
|
|
* @param {number} b Number to compare.
|
|
* @return {boolean} Whether a is greater than b.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.isGreaterThan_ = function(a, b) {
|
|
return a > b;
|
|
};
|
|
|
|
|
|
/**
|
|
* Inserts the current drag item to the appropriate location in the drag list
|
|
* that we're hovering over (if the current drag item is not already there).
|
|
*
|
|
* @param {Element} hoverList The drag list we're hovering over.
|
|
* @param {Element} hoverNextItem The next item in the hover drag list.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.insertCurrDragItem_ = function(
|
|
hoverList, hoverNextItem) {
|
|
if (this.currDragItem_.parentNode != hoverList ||
|
|
goog.dom.getNextElementSibling(this.currDragItem_) != hoverNextItem) {
|
|
// The current drag item is not in the correct location, so we move it.
|
|
// Note: hoverNextItem may be null, but insertBefore() still works.
|
|
hoverList.insertBefore(this.currDragItem_, hoverNextItem);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Note: Copied from abstractdragdrop.js. TODO(user): consolidate.
|
|
* Creates copy of node being dragged.
|
|
*
|
|
* @param {Element} sourceEl Element to copy.
|
|
* @return {Element} The clone of {@code sourceEl}.
|
|
* @private
|
|
*/
|
|
goog.fx.DragListGroup.prototype.cloneNode_ = function(sourceEl) {
|
|
var clonedEl = /** @type {Element} */ (sourceEl.cloneNode(true));
|
|
switch (sourceEl.tagName.toLowerCase()) {
|
|
case 'tr':
|
|
return goog.dom.createDom(
|
|
'table', null, goog.dom.createDom('tbody', null, clonedEl));
|
|
case 'td':
|
|
case 'th':
|
|
return goog.dom.createDom(
|
|
'table', null, goog.dom.createDom('tbody', null, goog.dom.createDom(
|
|
'tr', null, clonedEl)));
|
|
default:
|
|
return clonedEl;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* The event object dispatched by DragListGroup.
|
|
* The fields draggerElCenter, hoverList, and hoverNextItem are only available
|
|
* for the BEFOREDRAGMOVE and DRAGMOVE events.
|
|
*
|
|
* @param {string} type The event type string.
|
|
* @param {goog.fx.DragListGroup} dragListGroup A reference to the associated
|
|
* DragListGroup object.
|
|
* @param {goog.events.BrowserEvent|goog.fx.DragEvent} event The event fired
|
|
* by the browser or fired by the dragger.
|
|
* @param {Element} currDragItem The current drag item being moved.
|
|
* @param {Element} draggerEl The clone of the current drag item that's actually
|
|
* being dragged around.
|
|
* @param {goog.fx.Dragger} dragger The dragger object.
|
|
* @param {goog.math.Coordinate=} opt_draggerElCenter The current center
|
|
* position of the draggerEl.
|
|
* @param {Element=} opt_hoverList The current drag list that's being hovered
|
|
* over, or null if the center of draggerEl is outside of any drag lists.
|
|
* If not null and the drag action ends right now, then currDragItem will
|
|
* end up in this list.
|
|
* @param {Element=} opt_hoverNextItem The current next item in the hoverList
|
|
* that the draggerEl is hovering over. (I.e. If the drag action ends
|
|
* right now, then this item would become the next item after the new
|
|
* location of currDragItem.) May be null if not applicable or if
|
|
* currDragItem would be added to the end of hoverList.
|
|
* @constructor
|
|
* @extends {goog.events.Event}
|
|
*/
|
|
goog.fx.DragListGroupEvent = function(
|
|
type, dragListGroup, event, currDragItem, draggerEl, dragger,
|
|
opt_draggerElCenter, opt_hoverList, opt_hoverNextItem) {
|
|
goog.events.Event.call(this, type);
|
|
|
|
/**
|
|
* A reference to the associated DragListGroup object.
|
|
* @type {goog.fx.DragListGroup}
|
|
*/
|
|
this.dragListGroup = dragListGroup;
|
|
|
|
/**
|
|
* The event fired by the browser or fired by the dragger.
|
|
* @type {goog.events.BrowserEvent|goog.fx.DragEvent}
|
|
*/
|
|
this.event = event;
|
|
|
|
/**
|
|
* The current drag item being move.
|
|
* @type {Element}
|
|
*/
|
|
this.currDragItem = currDragItem;
|
|
|
|
/**
|
|
* The clone of the current drag item that's actually being dragged around.
|
|
* @type {Element}
|
|
*/
|
|
this.draggerEl = draggerEl;
|
|
|
|
/**
|
|
* The dragger object.
|
|
* @type {goog.fx.Dragger}
|
|
*/
|
|
this.dragger = dragger;
|
|
|
|
/**
|
|
* The current center position of the draggerEl.
|
|
* @type {goog.math.Coordinate|undefined}
|
|
*/
|
|
this.draggerElCenter = opt_draggerElCenter;
|
|
|
|
/**
|
|
* The current drag list that's being hovered over, or null if the center of
|
|
* draggerEl is outside of any drag lists. (I.e. If not null and the drag
|
|
* action ends right now, then currDragItem will end up in this list.)
|
|
* @type {Element|undefined}
|
|
*/
|
|
this.hoverList = opt_hoverList;
|
|
|
|
/**
|
|
* The current next item in the hoverList that the draggerEl is hovering over.
|
|
* (I.e. If the drag action ends right now, then this item would become the
|
|
* next item after the new location of currDragItem.) May be null if not
|
|
* applicable or if currDragItem would be added to the end of hoverList.
|
|
* @type {Element|undefined}
|
|
*/
|
|
this.hoverNextItem = opt_hoverNextItem;
|
|
};
|
|
goog.inherits(goog.fx.DragListGroupEvent, goog.events.Event);
|