123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575 |
- // Copyright 2006 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 Abstract Base Class for Drag and Drop.
- *
- * Provides functionality for implementing drag and drop classes. Also provides
- * support classes and events.
- *
- * @author eae@google.com (Emil A Eklund)
- */
- goog.provide('goog.fx.AbstractDragDrop');
- goog.provide('goog.fx.AbstractDragDrop.EventType');
- goog.provide('goog.fx.DragDropEvent');
- goog.provide('goog.fx.DragDropItem');
- goog.require('goog.array');
- goog.require('goog.asserts');
- goog.require('goog.dom');
- goog.require('goog.dom.classlist');
- goog.require('goog.events');
- 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.math.Box');
- goog.require('goog.math.Coordinate');
- goog.require('goog.style');
- /**
- * Abstract class that provides reusable functionality for implementing drag
- * and drop functionality.
- *
- * This class also allows clients to define their own subtargeting function
- * so that drop areas can have finer granularity then a singe element. This is
- * accomplished by using a client provided function to map from element and
- * coordinates to a subregion id.
- *
- * This class can also be made aware of scrollable containers that contain
- * drop targets by calling addScrollableContainer. This will cause dnd to
- * take changing scroll positions into account while a drag is occurring.
- *
- * @extends {goog.events.EventTarget}
- * @constructor
- * @struct
- */
- goog.fx.AbstractDragDrop = function() {
- goog.fx.AbstractDragDrop.base(this, 'constructor');
- /**
- * List of items that makes up the drag source or drop target.
- * @protected {Array<goog.fx.DragDropItem>}
- * @suppress {underscore|visibility}
- */
- this.items_ = [];
- /**
- * List of associated drop targets.
- * @private {Array<goog.fx.AbstractDragDrop>}
- */
- this.targets_ = [];
- /**
- * Scrollable containers to account for during drag
- * @private {Array<goog.fx.ScrollableContainer_>}
- */
- this.scrollableContainers_ = [];
- /**
- * Flag indicating if it's a drag source, set by addTarget.
- * @private {boolean}
- */
- this.isSource_ = false;
- /**
- * Flag indicating if it's a drop target, set when added as target to another
- * DragDrop object.
- * @private {boolean}
- */
- this.isTarget_ = false;
- /**
- * Subtargeting function accepting args:
- * (goog.fx.DragDropItem, goog.math.Box, number, number)
- * @private {?Function}
- */
- this.subtargetFunction_;
- /**
- * Last active subtarget.
- * @private {?Object}
- */
- this.activeSubtarget_;
- /**
- * Class name to add to source elements being dragged. Set by setDragClass.
- * @private {?string}
- */
- this.dragClass_;
- /**
- * Class name to add to source elements. Set by setSourceClass.
- * @private {?string}
- */
- this.sourceClass_;
- /**
- * Class name to add to target elements. Set by setTargetClass.
- * @private {?string}
- */
- this.targetClass_;
- /**
- * The SCROLL event target used to make drag element follow scrolling.
- * @private {?EventTarget}
- */
- this.scrollTarget_;
- /**
- * Dummy target, {@see maybeCreateDummyTargetForPosition_}.
- * @private {?goog.fx.ActiveDropTarget_}
- */
- this.dummyTarget_;
- /**
- * Whether the object has been initialized.
- * @private {boolean}
- */
- this.initialized_ = false;
- /** @private {?Element} */
- this.dragEl_;
- /** @private {?Array<!goog.fx.ActiveDropTarget_>} */
- this.targetList_;
- /** @private {?goog.math.Box} */
- this.targetBox_;
- /** @private {?goog.fx.ActiveDropTarget_} */
- this.activeTarget_;
- /** @private {?goog.fx.DragDropItem} */
- this.dragItem_;
- /** @private {?goog.fx.Dragger} */
- this.dragger_;
- };
- goog.inherits(goog.fx.AbstractDragDrop, goog.events.EventTarget);
- /**
- * Minimum size (in pixels) for a dummy target. If the box for the target is
- * less than the specified size it's not created.
- * @type {number}
- * @private
- */
- goog.fx.AbstractDragDrop.DUMMY_TARGET_MIN_SIZE_ = 10;
- /**
- * Constants for event names
- * @const
- */
- goog.fx.AbstractDragDrop.EventType = {
- DRAGOVER: 'dragover',
- DRAGOUT: 'dragout',
- DRAG: 'drag',
- DROP: 'drop',
- DRAGSTART: 'dragstart',
- DRAGEND: 'dragend'
- };
- /**
- * Constant for distance threshold, in pixels, an element has to be moved to
- * initiate a drag operation.
- * @type {number}
- */
- goog.fx.AbstractDragDrop.initDragDistanceThreshold = 5;
- /**
- * Set class to add to source elements being dragged.
- *
- * @param {string} className Class to be added. Must be a single, valid
- * classname.
- */
- goog.fx.AbstractDragDrop.prototype.setDragClass = function(className) {
- this.dragClass_ = className;
- };
- /**
- * Set class to add to source elements.
- *
- * @param {string} className Class to be added. Must be a single, valid
- * classname.
- */
- goog.fx.AbstractDragDrop.prototype.setSourceClass = function(className) {
- this.sourceClass_ = className;
- };
- /**
- * Set class to add to target elements.
- *
- * @param {string} className Class to be added. Must be a single, valid
- * classname.
- */
- goog.fx.AbstractDragDrop.prototype.setTargetClass = function(className) {
- this.targetClass_ = className;
- };
- /**
- * Whether the control has been initialized.
- *
- * @return {boolean} True if it's been initialized.
- */
- goog.fx.AbstractDragDrop.prototype.isInitialized = function() {
- return this.initialized_;
- };
- /**
- * Add item to drag object.
- *
- * @param {Element|string} element Dom Node, or string representation of node
- * id, to be used as drag source/drop target.
- * @throws Error Thrown if called on instance of abstract class
- */
- goog.fx.AbstractDragDrop.prototype.addItem = goog.abstractMethod;
- /**
- * Associate drop target with drag element.
- *
- * @param {goog.fx.AbstractDragDrop} target Target to add.
- */
- goog.fx.AbstractDragDrop.prototype.addTarget = function(target) {
- this.targets_.push(target);
- target.isTarget_ = true;
- this.isSource_ = true;
- };
- /**
- * Removes the specified target from the list of drop targets.
- *
- * @param {!goog.fx.AbstractDragDrop} target Target to remove.
- */
- goog.fx.AbstractDragDrop.prototype.removeTarget = function(target) {
- goog.array.remove(this.targets_, target);
- if (this.activeTarget_ && this.activeTarget_.target_ == target) {
- this.activeTarget_ = null;
- }
- this.recalculateDragTargets();
- };
- /**
- * Sets the SCROLL event target to make drag element follow scrolling.
- *
- * @param {EventTarget} scrollTarget The element that dispatches SCROLL events.
- */
- goog.fx.AbstractDragDrop.prototype.setScrollTarget = function(scrollTarget) {
- this.scrollTarget_ = scrollTarget;
- };
- /**
- * Initialize drag and drop functionality for sources/targets already added.
- * Sources/targets added after init has been called will initialize themselves
- * one by one.
- */
- goog.fx.AbstractDragDrop.prototype.init = function() {
- if (this.initialized_) {
- return;
- }
- for (var item, i = 0; item = this.items_[i]; i++) {
- this.initItem(item);
- }
- this.initialized_ = true;
- };
- /**
- * Initializes a single item.
- *
- * @param {goog.fx.DragDropItem} item Item to initialize.
- * @protected
- */
- goog.fx.AbstractDragDrop.prototype.initItem = function(item) {
- if (this.isSource_) {
- goog.events.listen(
- item.element, goog.events.EventType.MOUSEDOWN, item.mouseDown_, false,
- item);
- if (this.sourceClass_) {
- goog.dom.classlist.add(
- goog.asserts.assert(item.element), this.sourceClass_);
- }
- }
- if (this.isTarget_ && this.targetClass_) {
- goog.dom.classlist.add(
- goog.asserts.assert(item.element), this.targetClass_);
- }
- };
- /**
- * Called when removing an item. Removes event listeners and classes.
- *
- * @param {goog.fx.DragDropItem} item Item to dispose.
- * @protected
- */
- goog.fx.AbstractDragDrop.prototype.disposeItem = function(item) {
- if (this.isSource_) {
- goog.events.unlisten(
- item.element, goog.events.EventType.MOUSEDOWN, item.mouseDown_, false,
- item);
- if (this.sourceClass_) {
- goog.dom.classlist.remove(
- goog.asserts.assert(item.element), this.sourceClass_);
- }
- }
- if (this.isTarget_ && this.targetClass_) {
- goog.dom.classlist.remove(
- goog.asserts.assert(item.element), this.targetClass_);
- }
- item.dispose();
- };
- /**
- * Removes all items.
- */
- goog.fx.AbstractDragDrop.prototype.removeItems = function() {
- for (var item, i = 0; item = this.items_[i]; i++) {
- this.disposeItem(item);
- }
- this.items_.length = 0;
- };
- /**
- * Starts a drag event for an item if the mouse button stays pressed and the
- * cursor moves a few pixels. Allows dragging of items without first having to
- * register them with addItem.
- *
- * @param {goog.events.BrowserEvent} event Mouse down event.
- * @param {goog.fx.DragDropItem} item Item that's being dragged.
- */
- goog.fx.AbstractDragDrop.prototype.maybeStartDrag = function(event, item) {
- item.maybeStartDrag_(event, item.element);
- };
- /**
- * Event handler that's used to start drag.
- *
- * @param {goog.events.BrowserEvent} event Mouse move event.
- * @param {goog.fx.DragDropItem} item Item that's being dragged.
- */
- goog.fx.AbstractDragDrop.prototype.startDrag = function(event, item) {
- // Prevent a new drag operation from being started if another one is already
- // in progress (could happen if the mouse was released outside of the
- // document).
- if (this.dragItem_) {
- return;
- }
- this.dragItem_ = item;
- // Dispatch DRAGSTART event
- var dragStartEvent = new goog.fx.DragDropEvent(
- goog.fx.AbstractDragDrop.EventType.DRAGSTART, this, this.dragItem_,
- undefined, // opt_target
- undefined, // opt_targetItem
- undefined, // opt_targetElement
- undefined, // opt_clientX
- undefined, // opt_clientY
- undefined, // opt_x
- undefined, // opt_y
- undefined, // opt_subtarget
- event);
- if (this.dispatchEvent(dragStartEvent) == false) {
- this.dragItem_ = null;
- return;
- }
- // Get the source element and create a drag element for it.
- var el = item.getCurrentDragElement();
- this.dragEl_ = this.createDragElement(el);
- var doc = goog.dom.getOwnerDocument(el);
- doc.body.appendChild(this.dragEl_);
- this.dragger_ = this.createDraggerFor(el, this.dragEl_, event);
- this.dragger_.setScrollTarget(this.scrollTarget_);
- goog.events.listen(
- this.dragger_, goog.fx.Dragger.EventType.DRAG, this.moveDrag_, false,
- this);
- goog.events.listen(
- this.dragger_, goog.fx.Dragger.EventType.END, this.endDrag, false, this);
- // IE may issue a 'selectstart' event when dragging over an iframe even when
- // default mousemove behavior is suppressed. If the default selectstart
- // behavior is not suppressed, elements dragged over will show as selected.
- goog.events.listen(
- doc.body, goog.events.EventType.SELECTSTART, this.suppressSelect_);
- this.recalculateDragTargets();
- this.recalculateScrollableContainers();
- this.activeTarget_ = null;
- this.initScrollableContainerListeners_();
- this.dragger_.startDrag(event);
- event.preventDefault();
- };
- /**
- * Recalculates the geometry of this source's drag targets. Call this
- * if the position or visibility of a drag target has changed during
- * a drag, or if targets are added or removed.
- *
- * TODO(user): this is an expensive operation; more efficient APIs
- * may be necessary.
- */
- goog.fx.AbstractDragDrop.prototype.recalculateDragTargets = function() {
- this.targetList_ = [];
- for (var target, i = 0; target = this.targets_[i]; i++) {
- for (var itm, j = 0; itm = target.items_[j]; j++) {
- this.addDragTarget_(target, itm);
- }
- }
- if (!this.targetBox_) {
- this.targetBox_ = new goog.math.Box(0, 0, 0, 0);
- }
- };
- /**
- * Recalculates the current scroll positions of scrollable containers and
- * allocates targets. Call this if the position of a container changed or if
- * targets are added or removed.
- */
- goog.fx.AbstractDragDrop.prototype.recalculateScrollableContainers =
- function() {
- var container, i, j, target;
- for (i = 0; container = this.scrollableContainers_[i]; i++) {
- container.containedTargets_ = [];
- container.savedScrollLeft_ = container.element_.scrollLeft;
- container.savedScrollTop_ = container.element_.scrollTop;
- var pos = goog.style.getPageOffset(container.element_);
- var size = goog.style.getSize(container.element_);
- container.box_ = new goog.math.Box(
- pos.y, pos.x + size.width, pos.y + size.height, pos.x);
- }
- for (i = 0; target = this.targetList_[i]; i++) {
- for (j = 0; container = this.scrollableContainers_[j]; j++) {
- if (goog.dom.contains(container.element_, target.element_)) {
- container.containedTargets_.push(target);
- target.scrollableContainer_ = container;
- }
- }
- }
- };
- /**
- * Creates the Dragger for the drag element.
- * @param {Element} sourceEl Drag source element.
- * @param {Element} el the element created by createDragElement().
- * @param {goog.events.BrowserEvent} event Mouse down event for start of drag.
- * @return {!goog.fx.Dragger} The new Dragger.
- * @protected
- */
- goog.fx.AbstractDragDrop.prototype.createDraggerFor = function(
- sourceEl, el, event) {
- // Position the drag element.
- var pos = this.getDragElementPosition(sourceEl, el, event);
- el.style.position = 'absolute';
- el.style.left = pos.x + 'px';
- el.style.top = pos.y + 'px';
- return new goog.fx.Dragger(el);
- };
- /**
- * Event handler that's used to stop drag. Fires a drop event if over a valid
- * target.
- *
- * @param {goog.fx.DragEvent} event Drag event.
- */
- goog.fx.AbstractDragDrop.prototype.endDrag = function(event) {
- var activeTarget = event.dragCanceled ? null : this.activeTarget_;
- if (activeTarget && activeTarget.target_) {
- var clientX = event.clientX;
- var clientY = event.clientY;
- var scroll = this.getScrollPos();
- var x = clientX + scroll.x;
- var y = clientY + scroll.y;
- var subtarget;
- // If a subtargeting function is enabled get the current subtarget
- if (this.subtargetFunction_) {
- subtarget =
- this.subtargetFunction_(activeTarget.item_, activeTarget.box_, x, y);
- }
- var dragEvent = new goog.fx.DragDropEvent(
- goog.fx.AbstractDragDrop.EventType.DRAG, this, this.dragItem_,
- activeTarget.target_, activeTarget.item_, activeTarget.element_,
- clientX, clientY, x, y);
- this.dispatchEvent(dragEvent);
- var dropEvent = new goog.fx.DragDropEvent(
- goog.fx.AbstractDragDrop.EventType.DROP, this, this.dragItem_,
- activeTarget.target_, activeTarget.item_, activeTarget.element_,
- clientX, clientY, x, y, subtarget, event.browserEvent);
- activeTarget.target_.dispatchEvent(dropEvent);
- }
- var dragEndEvent = new goog.fx.DragDropEvent(
- goog.fx.AbstractDragDrop.EventType.DRAGEND, this, this.dragItem_,
- activeTarget ? activeTarget.target_ : undefined,
- activeTarget ? activeTarget.item_ : undefined,
- activeTarget ? activeTarget.element_ : undefined);
- this.dispatchEvent(dragEndEvent);
- goog.events.unlisten(
- this.dragger_, goog.fx.Dragger.EventType.DRAG, this.moveDrag_, false,
- this);
- goog.events.unlisten(
- this.dragger_, goog.fx.Dragger.EventType.END, this.endDrag, false, this);
- var doc = goog.dom.getOwnerDocument(this.dragItem_.getCurrentDragElement());
- goog.events.unlisten(
- doc.body, goog.events.EventType.SELECTSTART, this.suppressSelect_);
- this.afterEndDrag(this.activeTarget_ ? this.activeTarget_.item_ : null);
- };
- /**
- * Called after a drag operation has finished.
- *
- * @param {goog.fx.DragDropItem=} opt_dropTarget Target for successful drop.
- * @protected
- */
- goog.fx.AbstractDragDrop.prototype.afterEndDrag = function(opt_dropTarget) {
- this.disposeDrag();
- };
- /**
- * Called once a drag operation has finished. Removes event listeners and
- * elements.
- *
- * @protected
- */
- goog.fx.AbstractDragDrop.prototype.disposeDrag = function() {
- this.disposeScrollableContainerListeners_();
- this.dragger_.dispose();
- goog.dom.removeNode(this.dragEl_);
- delete this.dragItem_;
- delete this.dragEl_;
- delete this.dragger_;
- delete this.targetList_;
- delete this.activeTarget_;
- };
- /**
- * Event handler for drag events. Determines the active drop target, if any, and
- * fires dragover and dragout events appropriately.
- *
- * @param {goog.fx.DragEvent} event Drag event.
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.moveDrag_ = function(event) {
- var position = this.getEventPosition(event);
- var x = position.x;
- var y = position.y;
- var activeTarget = this.activeTarget_;
- this.dispatchEvent(
- new goog.fx.DragDropEvent(
- goog.fx.AbstractDragDrop.EventType.DRAG, this, this.dragItem_,
- activeTarget ? activeTarget.target_ : undefined,
- activeTarget ? activeTarget.item_ : undefined,
- activeTarget ? activeTarget.element_ : undefined, event.clientX,
- event.clientY, x, y));
- // Check if we're still inside the bounds of the active target, if not fire
- // a dragout event and proceed to find a new target.
- var subtarget;
- if (activeTarget) {
- // If a subtargeting function is enabled get the current subtarget
- if (this.subtargetFunction_ && activeTarget.target_) {
- subtarget =
- this.subtargetFunction_(activeTarget.item_, activeTarget.box_, x, y);
- }
- if (activeTarget.box_.contains(position) &&
- subtarget == this.activeSubtarget_) {
- return;
- }
- if (activeTarget.target_) {
- var sourceDragOutEvent = new goog.fx.DragDropEvent(
- goog.fx.AbstractDragDrop.EventType.DRAGOUT, this, this.dragItem_,
- activeTarget.target_, activeTarget.item_, activeTarget.element_);
- this.dispatchEvent(sourceDragOutEvent);
- // The event should be dispatched the by target DragDrop so that the
- // target DragDrop can manage these events without having to know what
- // sources this is a target for.
- var targetDragOutEvent = new goog.fx.DragDropEvent(
- goog.fx.AbstractDragDrop.EventType.DRAGOUT, this, this.dragItem_,
- activeTarget.target_, activeTarget.item_, activeTarget.element_,
- undefined, undefined, undefined, undefined, this.activeSubtarget_);
- activeTarget.target_.dispatchEvent(targetDragOutEvent);
- }
- this.activeSubtarget_ = subtarget;
- this.activeTarget_ = null;
- }
- // Check if inside target box
- if (this.targetBox_.contains(position)) {
- // Search for target and fire a dragover event if found
- activeTarget = this.activeTarget_ = this.getTargetFromPosition_(position);
- if (activeTarget && activeTarget.target_) {
- // If a subtargeting function is enabled get the current subtarget
- if (this.subtargetFunction_) {
- subtarget = this.subtargetFunction_(
- activeTarget.item_, activeTarget.box_, x, y);
- }
- var sourceDragOverEvent = new goog.fx.DragDropEvent(
- goog.fx.AbstractDragDrop.EventType.DRAGOVER, this, this.dragItem_,
- activeTarget.target_, activeTarget.item_, activeTarget.element_);
- sourceDragOverEvent.subtarget = subtarget;
- this.dispatchEvent(sourceDragOverEvent);
- // The event should be dispatched by the target DragDrop so that the
- // target DragDrop can manage these events without having to know what
- // sources this is a target for.
- var targetDragOverEvent = new goog.fx.DragDropEvent(
- goog.fx.AbstractDragDrop.EventType.DRAGOVER, this, this.dragItem_,
- activeTarget.target_, activeTarget.item_, activeTarget.element_,
- event.clientX, event.clientY, undefined, undefined, subtarget);
- activeTarget.target_.dispatchEvent(targetDragOverEvent);
- } else if (!activeTarget) {
- // If no target was found create a dummy one so we won't have to iterate
- // over all possible targets for every move event.
- this.activeTarget_ = this.maybeCreateDummyTargetForPosition_(x, y);
- }
- }
- };
- /**
- * Event handler for suppressing selectstart events. Selecting should be
- * disabled while dragging.
- *
- * @param {goog.events.Event} event The selectstart event to suppress.
- * @return {boolean} Whether to perform default behavior.
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.suppressSelect_ = function(event) {
- return false;
- };
- /**
- * Sets up listeners for the scrollable containers that keep track of their
- * scroll positions.
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.initScrollableContainerListeners_ =
- function() {
- var container, i;
- for (i = 0; container = this.scrollableContainers_[i]; i++) {
- goog.events.listen(
- container.element_, goog.events.EventType.SCROLL,
- this.containerScrollHandler_, false, this);
- }
- };
- /**
- * Cleans up the scrollable container listeners.
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.disposeScrollableContainerListeners_ =
- function() {
- for (var i = 0, container; container = this.scrollableContainers_[i]; i++) {
- goog.events.unlisten(
- container.element_, 'scroll', this.containerScrollHandler_, false,
- this);
- container.containedTargets_ = [];
- }
- };
- /**
- * Makes drag and drop aware of a target container that could scroll mid drag.
- * @param {Element} element The scroll container.
- */
- goog.fx.AbstractDragDrop.prototype.addScrollableContainer = function(element) {
- this.scrollableContainers_.push(new goog.fx.ScrollableContainer_(element));
- };
- /**
- * Removes all scrollable containers.
- */
- goog.fx.AbstractDragDrop.prototype.removeAllScrollableContainers = function() {
- this.disposeScrollableContainerListeners_();
- this.scrollableContainers_ = [];
- };
- /**
- * Event handler for containers scrolling.
- * @param {goog.events.BrowserEvent} e The event.
- * @suppress {visibility} TODO(martone): update dependent projects.
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.containerScrollHandler_ = function(e) {
- for (var i = 0, container; container = this.scrollableContainers_[i]; i++) {
- if (e.target == container.element_) {
- var deltaTop = container.savedScrollTop_ - container.element_.scrollTop;
- var deltaLeft =
- container.savedScrollLeft_ - container.element_.scrollLeft;
- container.savedScrollTop_ = container.element_.scrollTop;
- container.savedScrollLeft_ = container.element_.scrollLeft;
- // When the container scrolls, it's possible that one of the targets will
- // move to the region contained by the dummy target. Since we don't know
- // which sides (if any) of the dummy target are defined by targets
- // contained by this container, we are conservative and just shrink it.
- if (this.dummyTarget_ && this.activeTarget_ == this.dummyTarget_) {
- if (deltaTop > 0) {
- this.dummyTarget_.box_.top += deltaTop;
- } else {
- this.dummyTarget_.box_.bottom += deltaTop;
- }
- if (deltaLeft > 0) {
- this.dummyTarget_.box_.left += deltaLeft;
- } else {
- this.dummyTarget_.box_.right += deltaLeft;
- }
- }
- for (var j = 0, target; target = container.containedTargets_[j]; j++) {
- var box = target.box_;
- box.top += deltaTop;
- box.left += deltaLeft;
- box.bottom += deltaTop;
- box.right += deltaLeft;
- this.calculateTargetBox_(box);
- }
- }
- }
- this.dragger_.onScroll_(e);
- };
- /**
- * Set a function that provides subtargets. A subtargeting function
- * returns an arbitrary identifier for each subtarget of an element.
- * DnD code will generate additional drag over / out events when
- * switching from subtarget to subtarget. This is useful for instance
- * if you are interested if you are on the top half or the bottom half
- * of the element.
- * The provided function will be given the DragDropItem, box, x, y
- * box is the current window coordinates occupied by element
- * x, y is the mouse position in window coordinates
- *
- * @param {Function} f The new subtarget function.
- */
- goog.fx.AbstractDragDrop.prototype.setSubtargetFunction = function(f) {
- this.subtargetFunction_ = f;
- };
- /**
- * Creates an element for the item being dragged.
- *
- * @param {Element} sourceEl Drag source element.
- * @return {Element} The new drag element.
- */
- goog.fx.AbstractDragDrop.prototype.createDragElement = function(sourceEl) {
- var dragEl = this.createDragElementInternal(sourceEl);
- goog.asserts.assert(dragEl);
- if (this.dragClass_) {
- goog.dom.classlist.add(dragEl, this.dragClass_);
- }
- return dragEl;
- };
- /**
- * Returns the position for the drag element.
- *
- * @param {Element} el Drag source element.
- * @param {Element} dragEl The dragged element created by createDragElement().
- * @param {goog.events.BrowserEvent} event Mouse down event for start of drag.
- * @return {!goog.math.Coordinate} The position for the drag element.
- */
- goog.fx.AbstractDragDrop.prototype.getDragElementPosition = function(
- el, dragEl, event) {
- var pos = goog.style.getPageOffset(el);
- // Subtract margin from drag element position twice, once to adjust the
- // position given by the original node and once for the drag node.
- var marginBox = goog.style.getMarginBox(el);
- pos.x -= (marginBox.left || 0) * 2;
- pos.y -= (marginBox.top || 0) * 2;
- return pos;
- };
- /**
- * Returns the dragger object.
- *
- * @return {goog.fx.Dragger} The dragger object used by this drag and drop
- * instance.
- */
- goog.fx.AbstractDragDrop.prototype.getDragger = function() {
- return this.dragger_;
- };
- /**
- * Creates copy of node being dragged.
- *
- * @param {Element} sourceEl Element to copy.
- * @return {!Element} The clone of {@code sourceEl}.
- * @deprecated Use goog.fx.Dragger.cloneNode().
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.cloneNode_ = function(sourceEl) {
- return goog.fx.Dragger.cloneNode(sourceEl);
- };
- /**
- * Generates an element to follow the cursor during dragging, given a drag
- * source element. The default behavior is simply to clone the source element,
- * but this may be overridden in subclasses. This method is called by
- * {@code createDragElement()} before the drag class is added.
- *
- * @param {Element} sourceEl Drag source element.
- * @return {!Element} The new drag element.
- * @protected
- * @suppress {deprecated}
- */
- goog.fx.AbstractDragDrop.prototype.createDragElementInternal = function(
- sourceEl) {
- return this.cloneNode_(sourceEl);
- };
- /**
- * Add possible drop target for current drag operation.
- *
- * @param {goog.fx.AbstractDragDrop} target Drag handler.
- * @param {goog.fx.DragDropItem} item Item that's being dragged.
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.addDragTarget_ = function(target, item) {
- // Get all the draggable elements and add each one.
- var draggableElements = item.getDraggableElements();
- for (var i = 0; i < draggableElements.length; i++) {
- var draggableElement = draggableElements[i];
- // Determine target position and dimension
- var box = this.getElementBox(item, draggableElement);
- this.targetList_.push(
- new goog.fx.ActiveDropTarget_(box, target, item, draggableElement));
- this.calculateTargetBox_(box);
- }
- };
- /**
- * Calculates the position and dimension of a draggable element.
- *
- * @param {goog.fx.DragDropItem} item Item that's being dragged.
- * @param {Element} element The element to calculate the box.
- *
- * @return {!goog.math.Box} Box describing the position and dimension
- * of element.
- * @protected
- */
- goog.fx.AbstractDragDrop.prototype.getElementBox = function(item, element) {
- var pos = goog.style.getPageOffset(element);
- var size = goog.style.getSize(element);
- return new goog.math.Box(
- pos.y, pos.x + size.width, pos.y + size.height, pos.x);
- };
- /**
- * Calculate the outer bounds (the region all targets are inside).
- *
- * @param {goog.math.Box} box Box describing the position and dimension
- * of a drag target.
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.calculateTargetBox_ = function(box) {
- if (this.targetList_.length == 1) {
- this.targetBox_ =
- new goog.math.Box(box.top, box.right, box.bottom, box.left);
- } else {
- var tb = this.targetBox_;
- tb.left = Math.min(box.left, tb.left);
- tb.right = Math.max(box.right, tb.right);
- tb.top = Math.min(box.top, tb.top);
- tb.bottom = Math.max(box.bottom, tb.bottom);
- }
- };
- /**
- * Creates a dummy target for the given cursor position. The assumption is to
- * create as big dummy target box as possible, the only constraints are:
- * - The dummy target box cannot overlap any of real target boxes.
- * - The dummy target has to contain a point with current mouse coordinates.
- *
- * NOTE: For performance reasons the box construction algorithm is kept simple
- * and it is not optimal (see example below). Currently it is O(n) in regard to
- * the number of real drop target boxes, but its result depends on the order
- * of those boxes being processed (the order in which they're added to the
- * targetList_ collection).
- *
- * The algorithm.
- * a) Assumptions
- * - Mouse pointer is in the bounding box of real target boxes.
- * - None of the boxes have negative coordinate values.
- * - Mouse pointer is not contained by any of "real target" boxes.
- * - For targets inside a scrollable container, the box used is the
- * intersection of the scrollable container's box and the target's box.
- * This is because the part of the target that extends outside the scrollable
- * container should not be used in the clipping calculations.
- *
- * b) Outline
- * - Initialize the fake target to the bounding box of real targets.
- * - For each real target box - clip the fake target box so it does not contain
- * that target box, but does contain the mouse pointer.
- * -- Project the real target box, mouse pointer and fake target box onto
- * both axes and calculate the clipping coordinates.
- * -- Only one coordinate is used to clip the fake target box to keep the
- * fake target as big as possible.
- * -- If the projection of the real target box contains the mouse pointer,
- * clipping for a given axis is not possible.
- * -- If both clippings are possible, the clipping more distant from the
- * mouse pointer is selected to keep bigger fake target area.
- * - Save the created fake target only if it has a big enough area.
- *
- *
- * c) Example
- * <pre>
- * Input: Algorithm created box: Maximum box:
- * +---------------------+ +---------------------+ +---------------------+
- * | B1 | B2 | | B1 B2 | | B1 B2 |
- * | | | | +-------------+ | |+-------------------+|
- * |---------x-----------| | | | | || ||
- * | | | | | | | || ||
- * | | | | | | | || ||
- * | | | | | | | || ||
- * | | | | | | | || ||
- * | | | | +-------------+ | |+-------------------+|
- * | B4 | B3 | | B4 B3 | | B4 B3 |
- * +---------------------+ +---------------------+ +---------------------+
- * </pre>
- *
- * @param {number} x Cursor position on the x-axis.
- * @param {number} y Cursor position on the y-axis.
- * @return {goog.fx.ActiveDropTarget_} Dummy drop target.
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.maybeCreateDummyTargetForPosition_ =
- function(x, y) {
- if (!this.dummyTarget_) {
- this.dummyTarget_ = new goog.fx.ActiveDropTarget_(this.targetBox_.clone());
- }
- var fakeTargetBox = this.dummyTarget_.box_;
- // Initialize the fake target box to the bounding box of DnD targets.
- fakeTargetBox.top = this.targetBox_.top;
- fakeTargetBox.right = this.targetBox_.right;
- fakeTargetBox.bottom = this.targetBox_.bottom;
- fakeTargetBox.left = this.targetBox_.left;
- // Clip the fake target based on mouse position and DnD target boxes.
- for (var i = 0, target; target = this.targetList_[i]; i++) {
- var box = target.box_;
- if (target.scrollableContainer_) {
- // If the target has a scrollable container, use the intersection of that
- // container's box and the target's box.
- var scrollBox = target.scrollableContainer_.box_;
- box = new goog.math.Box(
- Math.max(box.top, scrollBox.top),
- Math.min(box.right, scrollBox.right),
- Math.min(box.bottom, scrollBox.bottom),
- Math.max(box.left, scrollBox.left));
- }
- // Calculate clipping coordinates for horizontal and vertical axis.
- // The clipping coordinate is calculated by projecting fake target box,
- // the mouse pointer and DnD target box onto an axis and checking how
- // box projections overlap and if the projected DnD target box contains
- // mouse pointer. The clipping coordinate cannot be computed and is set to
- // a negative value if the projected DnD target contains the mouse pointer.
- var horizontalClip = null; // Assume mouse is above or below the DnD box.
- if (x >= box.right) { // Mouse is to the right of the DnD box.
- // Clip the fake box only if the DnD box overlaps it.
- horizontalClip =
- box.right > fakeTargetBox.left ? box.right : fakeTargetBox.left;
- } else if (x < box.left) { // Mouse is to the left of the DnD box.
- // Clip the fake box only if the DnD box overlaps it.
- horizontalClip =
- box.left < fakeTargetBox.right ? box.left : fakeTargetBox.right;
- }
- var verticalClip = null;
- if (y >= box.bottom) {
- verticalClip =
- box.bottom > fakeTargetBox.top ? box.bottom : fakeTargetBox.top;
- } else if (y < box.top) {
- verticalClip =
- box.top < fakeTargetBox.bottom ? box.top : fakeTargetBox.bottom;
- }
- // If both clippings are possible, choose one that gives us larger distance
- // to mouse pointer (mark the shorter clipping as impossible, by setting it
- // to null).
- if (!goog.isNull(horizontalClip) && !goog.isNull(verticalClip)) {
- if (Math.abs(horizontalClip - x) > Math.abs(verticalClip - y)) {
- verticalClip = null;
- } else {
- horizontalClip = null;
- }
- }
- // Clip none or one of fake target box sides (at most one clipping
- // coordinate can be active).
- if (!goog.isNull(horizontalClip)) {
- if (horizontalClip <= x) {
- fakeTargetBox.left = horizontalClip;
- } else {
- fakeTargetBox.right = horizontalClip;
- }
- } else if (!goog.isNull(verticalClip)) {
- if (verticalClip <= y) {
- fakeTargetBox.top = verticalClip;
- } else {
- fakeTargetBox.bottom = verticalClip;
- }
- }
- }
- // Only return the new fake target if it is big enough.
- return (fakeTargetBox.right - fakeTargetBox.left) *
- (fakeTargetBox.bottom - fakeTargetBox.top) >=
- goog.fx.AbstractDragDrop.DUMMY_TARGET_MIN_SIZE_ ?
- this.dummyTarget_ :
- null;
- };
- /**
- * Returns the target for a given cursor position.
- *
- * @param {goog.math.Coordinate} position Cursor position.
- * @return {goog.fx.ActiveDropTarget_} Target for position or null if no target
- * was defined for the given position.
- * @private
- */
- goog.fx.AbstractDragDrop.prototype.getTargetFromPosition_ = function(position) {
- for (var target, i = 0; target = this.targetList_[i]; i++) {
- if (target.box_.contains(position)) {
- if (target.scrollableContainer_) {
- // If we have a scrollable container we will need to make sure
- // we account for clipping of the scroll area
- var box = target.scrollableContainer_.box_;
- if (box.contains(position)) {
- return target;
- }
- } else {
- return target;
- }
- }
- }
- return null;
- };
- /**
- * Checks whatever a given point is inside a given box.
- *
- * @param {number} x Cursor position on the x-axis.
- * @param {number} y Cursor position on the y-axis.
- * @param {goog.math.Box} box Box to check position against.
- * @return {boolean} Whether the given point is inside {@code box}.
- * @protected
- * @deprecated Use goog.math.Box.contains.
- */
- goog.fx.AbstractDragDrop.prototype.isInside = function(x, y, box) {
- return x >= box.left && x < box.right && y >= box.top && y < box.bottom;
- };
- /**
- * Gets the scroll distance as a coordinate object, using
- * the window of the current drag element's dom.
- * @return {!goog.math.Coordinate} Object with scroll offsets 'x' and 'y'.
- * @protected
- */
- goog.fx.AbstractDragDrop.prototype.getScrollPos = function() {
- return goog.dom.getDomHelper(this.dragEl_).getDocumentScroll();
- };
- /**
- * Get the position of a drag event.
- * @param {goog.fx.DragEvent} event Drag event.
- * @return {!goog.math.Coordinate} Position of the event.
- * @protected
- */
- goog.fx.AbstractDragDrop.prototype.getEventPosition = function(event) {
- var scroll = this.getScrollPos();
- return new goog.math.Coordinate(
- event.clientX + scroll.x, event.clientY + scroll.y);
- };
- /** @override */
- goog.fx.AbstractDragDrop.prototype.disposeInternal = function() {
- goog.fx.AbstractDragDrop.base(this, 'disposeInternal');
- this.removeItems();
- };
- /**
- * Object representing a drag and drop event.
- *
- * @param {string} type Event type.
- * @param {goog.fx.AbstractDragDrop} source Source drag drop object.
- * @param {goog.fx.DragDropItem} sourceItem Source item.
- * @param {goog.fx.AbstractDragDrop=} opt_target Target drag drop object.
- * @param {goog.fx.DragDropItem=} opt_targetItem Target item.
- * @param {Element=} opt_targetElement Target element.
- * @param {number=} opt_clientX X-Position relative to the screen.
- * @param {number=} opt_clientY Y-Position relative to the screen.
- * @param {number=} opt_x X-Position relative to the viewport.
- * @param {number=} opt_y Y-Position relative to the viewport.
- * @param {Object=} opt_subtarget The currently active subtarget.
- * @param {goog.events.BrowserEvent=} opt_browserEvent The browser event
- * that caused this dragdrop event.
- * @extends {goog.events.Event}
- * @constructor
- * @struct
- */
- goog.fx.DragDropEvent = function(
- type, source, sourceItem, opt_target, opt_targetItem, opt_targetElement,
- opt_clientX, opt_clientY, opt_x, opt_y, opt_subtarget, opt_browserEvent) {
- // TODO(eae): Get rid of all the optional parameters and have the caller set
- // the fields directly instead.
- goog.fx.DragDropEvent.base(this, 'constructor', type);
- /**
- * Reference to the source goog.fx.AbstractDragDrop object.
- * @type {goog.fx.AbstractDragDrop}
- */
- this.dragSource = source;
- /**
- * Reference to the source goog.fx.DragDropItem object.
- * @type {goog.fx.DragDropItem}
- */
- this.dragSourceItem = sourceItem;
- /**
- * Reference to the target goog.fx.AbstractDragDrop object.
- * @type {goog.fx.AbstractDragDrop|undefined}
- */
- this.dropTarget = opt_target;
- /**
- * Reference to the target goog.fx.DragDropItem object.
- * @type {goog.fx.DragDropItem|undefined}
- */
- this.dropTargetItem = opt_targetItem;
- /**
- * The actual element of the drop target that is the target for this event.
- * @type {Element|undefined}
- */
- this.dropTargetElement = opt_targetElement;
- /**
- * X-Position relative to the screen.
- * @type {number|undefined}
- */
- this.clientX = opt_clientX;
- /**
- * Y-Position relative to the screen.
- * @type {number|undefined}
- */
- this.clientY = opt_clientY;
- /**
- * X-Position relative to the viewport.
- * @type {number|undefined}
- */
- this.viewportX = opt_x;
- /**
- * Y-Position relative to the viewport.
- * @type {number|undefined}
- */
- this.viewportY = opt_y;
- /**
- * The subtarget that is currently active if a subtargeting function
- * is supplied.
- * @type {Object|undefined}
- */
- this.subtarget = opt_subtarget;
- /**
- * The browser event that caused this dragdrop event.
- * @const
- */
- this.browserEvent = opt_browserEvent;
- };
- goog.inherits(goog.fx.DragDropEvent, goog.events.Event);
- /**
- * Class representing a source or target element for drag and drop operations.
- *
- * @param {Element|string} element Dom Node, or string representation of node
- * id, to be used as drag source/drop target.
- * @param {Object=} opt_data Data associated with the source/target.
- * @throws Error If no element argument is provided or if the type is invalid
- * @extends {goog.events.EventTarget}
- * @constructor
- * @struct
- */
- goog.fx.DragDropItem = function(element, opt_data) {
- goog.fx.DragDropItem.base(this, 'constructor');
- /**
- * Reference to drag source/target element
- * @type {Element}
- */
- this.element = goog.dom.getElement(element);
- /**
- * Data associated with element.
- * @type {Object|undefined}
- */
- this.data = opt_data;
- /**
- * Drag object the item belongs to.
- * @type {goog.fx.AbstractDragDrop?}
- * @private
- */
- this.parent_ = null;
- /**
- * Event handler for listeners on events that can initiate a drag.
- * @type {!goog.events.EventHandler<!goog.fx.DragDropItem>}
- * @private
- */
- this.eventHandler_ = new goog.events.EventHandler(this);
- this.registerDisposable(this.eventHandler_);
- /**
- * The current element being dragged. This is needed because a DragDropItem
- * can have multiple elements that can be dragged.
- * @private {?Element}
- */
- this.currentDragElement_ = null;
- /** @private {?goog.math.Coordinate} */
- this.startPosition_;
- if (!this.element) {
- throw Error('Invalid argument');
- }
- };
- goog.inherits(goog.fx.DragDropItem, goog.events.EventTarget);
- /**
- * Get the data associated with the source/target.
- * @return {Object|null|undefined} Data associated with the source/target.
- */
- goog.fx.DragDropItem.prototype.getData = function() {
- return this.data;
- };
- /**
- * Gets the element that is actually draggable given that the given target was
- * attempted to be dragged. This should be overriden when the element that was
- * given actually contains many items that can be dragged. From the target, you
- * can determine what element should actually be dragged.
- *
- * @param {Element} target The target that was attempted to be dragged.
- * @return {Element} The element that is draggable given the target. If
- * none are draggable, this will return null.
- */
- goog.fx.DragDropItem.prototype.getDraggableElement = function(target) {
- return target;
- };
- /**
- * Gets the element that is currently being dragged.
- *
- * @return {Element} The element that is currently being dragged.
- */
- goog.fx.DragDropItem.prototype.getCurrentDragElement = function() {
- return this.currentDragElement_;
- };
- /**
- * Gets all the elements of this item that are potentially draggable/
- *
- * @return {!Array<Element>} The draggable elements.
- */
- goog.fx.DragDropItem.prototype.getDraggableElements = function() {
- return [this.element];
- };
- /**
- * Event handler for mouse down.
- *
- * @param {goog.events.BrowserEvent} event Mouse down event.
- * @private
- */
- goog.fx.DragDropItem.prototype.mouseDown_ = function(event) {
- if (!event.isMouseActionButton()) {
- return;
- }
- // Get the draggable element for the target.
- var element = this.getDraggableElement(/** @type {Element} */ (event.target));
- if (element) {
- this.maybeStartDrag_(event, element);
- }
- };
- /**
- * Sets the dragdrop to which this item belongs.
- * @param {goog.fx.AbstractDragDrop} parent The parent dragdrop.
- */
- goog.fx.DragDropItem.prototype.setParent = function(parent) {
- this.parent_ = parent;
- };
- /**
- * Adds mouse move, mouse out and mouse up handlers.
- *
- * @param {goog.events.BrowserEvent} event Mouse down event.
- * @param {Element} element Element.
- * @private
- */
- goog.fx.DragDropItem.prototype.maybeStartDrag_ = function(event, element) {
- var eventType = goog.events.EventType;
- this.eventHandler_
- .listen(element, eventType.MOUSEMOVE, this.mouseMove_, false)
- .listen(element, eventType.MOUSEOUT, this.mouseMove_, false);
- // Capture the MOUSEUP on the document to ensure that we cancel the start
- // drag handlers even if the mouse up occurs on some other element. This can
- // happen for instance when the mouse down changes the geometry of the element
- // clicked on (e.g. through changes in activation styling) such that the mouse
- // up occurs outside the original element.
- var doc = goog.dom.getOwnerDocument(element);
- this.eventHandler_.listen(doc, eventType.MOUSEUP, this.mouseUp_, true);
- this.currentDragElement_ = element;
- this.startPosition_ = new goog.math.Coordinate(event.clientX, event.clientY);
- };
- /**
- * Event handler for mouse move. Starts drag operation if moved more than the
- * threshold value.
- *
- * @param {goog.events.BrowserEvent} event Mouse move or mouse out event.
- * @private
- */
- goog.fx.DragDropItem.prototype.mouseMove_ = function(event) {
- var distance = Math.abs(event.clientX - this.startPosition_.x) +
- Math.abs(event.clientY - this.startPosition_.y);
- // Fire dragStart event if the drag distance exceeds the threshold or if the
- // mouse leave the dragged element.
- // TODO(user): Consider using the goog.fx.Dragger to track the distance
- // even after the mouse leaves the dragged element.
- var currentDragElement = this.currentDragElement_;
- var distanceAboveThreshold =
- distance > goog.fx.AbstractDragDrop.initDragDistanceThreshold;
- var mouseOutOnDragElement = event.type == goog.events.EventType.MOUSEOUT &&
- event.target == currentDragElement;
- if (distanceAboveThreshold || mouseOutOnDragElement) {
- this.eventHandler_.removeAll();
- this.parent_.startDrag(event, this);
- }
- // Prevent text selection while dragging an element.
- event.preventDefault();
- };
- /**
- * Event handler for mouse up. Removes mouse move, mouse out and mouse up event
- * handlers.
- *
- * @param {goog.events.BrowserEvent} event Mouse up event.
- * @private
- */
- goog.fx.DragDropItem.prototype.mouseUp_ = function(event) {
- this.eventHandler_.removeAll();
- delete this.startPosition_;
- this.currentDragElement_ = null;
- };
- /**
- * Class representing an active drop target
- *
- * @param {goog.math.Box} box Box describing the position and dimension of the
- * target item.
- * @param {goog.fx.AbstractDragDrop=} opt_target Target that contains the item
- associated with position.
- * @param {goog.fx.DragDropItem=} opt_item Item associated with position.
- * @param {Element=} opt_element Element of item associated with position.
- * @constructor
- * @struct
- * @private
- */
- goog.fx.ActiveDropTarget_ = function(box, opt_target, opt_item, opt_element) {
- /**
- * Box describing the position and dimension of the target item
- * @type {goog.math.Box}
- * @private
- */
- this.box_ = box;
- /**
- * Target that contains the item associated with position
- * @type {goog.fx.AbstractDragDrop|undefined}
- * @private
- */
- this.target_ = opt_target;
- /**
- * Item associated with position
- * @type {goog.fx.DragDropItem|undefined}
- * @private
- */
- this.item_ = opt_item;
- /**
- * The draggable element of the item associated with position.
- * @type {Element}
- * @private
- */
- this.element_ = opt_element || null;
- /**
- * If this target is in a scrollable container this is it.
- * @private {?goog.fx.ScrollableContainer_}
- */
- this.scrollableContainer_ = null;
- };
- /**
- * Class for representing a scrollable container
- * @param {Element} element the scrollable element.
- * @constructor
- * @private
- */
- goog.fx.ScrollableContainer_ = function(element) {
- /**
- * The targets that lie within this container.
- * @type {Array<goog.fx.ActiveDropTarget_>}
- * @private
- */
- this.containedTargets_ = [];
- /**
- * The element that is this container
- * @type {Element}
- * @private
- */
- this.element_ = element;
- /**
- * The saved scroll left location for calculating deltas.
- * @type {number}
- * @private
- */
- this.savedScrollLeft_ = 0;
- /**
- * The saved scroll top location for calculating deltas.
- * @type {number}
- * @private
- */
- this.savedScrollTop_ = 0;
- /**
- * The space occupied by the container.
- * @type {goog.math.Box}
- * @private
- */
- this.box_ = null;
- };
|