draglistgroup.js 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326
  1. // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview A DragListGroup is a class representing a group of one or more
  16. * "drag lists" with items that can be dragged within them and between them.
  17. *
  18. * @see ../demos/draglistgroup.html
  19. */
  20. goog.provide('goog.fx.DragListDirection');
  21. goog.provide('goog.fx.DragListGroup');
  22. goog.provide('goog.fx.DragListGroup.EventType');
  23. goog.provide('goog.fx.DragListGroupEvent');
  24. goog.require('goog.array');
  25. goog.require('goog.asserts');
  26. goog.require('goog.dom');
  27. goog.require('goog.dom.classlist');
  28. goog.require('goog.events');
  29. goog.require('goog.events.Event');
  30. goog.require('goog.events.EventHandler');
  31. goog.require('goog.events.EventId');
  32. goog.require('goog.events.EventTarget');
  33. goog.require('goog.events.EventType');
  34. goog.require('goog.fx.Dragger');
  35. goog.require('goog.math.Coordinate');
  36. goog.require('goog.string');
  37. goog.require('goog.style');
  38. /**
  39. * A class representing a group of one or more "drag lists" with items that can
  40. * be dragged within them and between them.
  41. *
  42. * Example usage:
  43. * var dragListGroup = new goog.fx.DragListGroup();
  44. * dragListGroup.setDragItemHandleHoverClass(className1, className2);
  45. * dragListGroup.setDraggerElClass(className3);
  46. * dragListGroup.addDragList(vertList, goog.fx.DragListDirection.DOWN);
  47. * dragListGroup.addDragList(horizList, goog.fx.DragListDirection.RIGHT);
  48. * dragListGroup.init();
  49. *
  50. * @extends {goog.events.EventTarget}
  51. * @constructor
  52. * @struct
  53. */
  54. goog.fx.DragListGroup = function() {
  55. goog.fx.DragListGroup.base(this, 'constructor');
  56. /**
  57. * The user-supplied CSS classes to add to a drag item on hover (not during a
  58. * drag action).
  59. * @private {Array|undefined}
  60. */
  61. this.dragItemHoverClasses_;
  62. /**
  63. * The user-supplied CSS classes to add to a drag item handle on hover (not
  64. * during a drag action).
  65. * @private {Array|undefined}
  66. */
  67. this.dragItemHandleHoverClasses_;
  68. /**
  69. * The user-supplied CSS classes to add to the current drag item (during a
  70. * drag action).
  71. * @private {Array|undefined}
  72. */
  73. this.currDragItemClasses_;
  74. /**
  75. * The user-supplied CSS classes to add to the clone of the current drag item
  76. * that's actually being dragged around (during a drag action).
  77. * @private {Array<string>|undefined}
  78. */
  79. this.draggerElClasses_;
  80. /**
  81. * The current drag item being moved.
  82. * Note: This is only defined while a drag action is happening.
  83. * @private {Element}
  84. */
  85. this.currDragItem_;
  86. /**
  87. * The drag list that {@code this.currDragItem_} is currently hovering over,
  88. * or null if it is not hovering over a list.
  89. * @private {Element}
  90. */
  91. this.currHoverList_;
  92. /**
  93. * The original drag list that the current drag item came from. We need to
  94. * remember this in case the user drops the item outside of any lists, in
  95. * which case we return the item to its original location.
  96. * Note: This is only defined while a drag action is happening.
  97. * @private {Element}
  98. */
  99. this.origList_;
  100. /**
  101. * The original next item in the original list that the current drag item came
  102. * from. We need to remember this in case the user drops the item outside of
  103. * any lists, in which case we return the item to its original location.
  104. * Note: This is only defined while a drag action is happening.
  105. * @private {Element}
  106. */
  107. this.origNextItem_;
  108. /**
  109. * The current item in the list we are hovering over. We need to remember
  110. * this in case we do not update the position of the current drag item while
  111. * dragging (see {@code updateWhileDragging_}). In this case the current drag
  112. * item will be inserted into the list before this element when the drag ends.
  113. * @private {Element}
  114. */
  115. this.currHoverItem_;
  116. /**
  117. * The clone of the current drag item that's actually being dragged around.
  118. * Note: This is only defined while a drag action is happening.
  119. * @private {HTMLElement}
  120. */
  121. this.draggerEl_;
  122. /**
  123. * The dragger object.
  124. * Note: This is only defined while a drag action is happening.
  125. * @private {goog.fx.Dragger}
  126. */
  127. this.dragger_;
  128. /**
  129. * The amount of distance, in pixels, after which a mousedown or touchstart is
  130. * considered a drag.
  131. * @private {number}
  132. */
  133. this.hysteresisDistance_ = 0;
  134. /**
  135. * The drag lists.
  136. * @private {Array<Element>}
  137. */
  138. this.dragLists_ = [];
  139. /**
  140. * All the drag items. Set by init().
  141. * @private {Array<Element>}
  142. */
  143. this.dragItems_ = [];
  144. /**
  145. * Which drag item corresponds to a given handle. Set by init().
  146. * Specifically, this maps from the unique ID (as given by goog.getUid)
  147. * of the handle to the drag item.
  148. * @private {Object}
  149. */
  150. this.dragItemForHandle_ = {};
  151. /**
  152. * The event handler for this instance.
  153. * @private {goog.events.EventHandler<!goog.fx.DragListGroup>}
  154. */
  155. this.eventHandler_ = new goog.events.EventHandler(this);
  156. /**
  157. * Whether the setup has been done to make all items in all lists draggable.
  158. * @private {boolean}
  159. */
  160. this.isInitialized_ = false;
  161. /**
  162. * Whether the currDragItem is always displayed. By default the list
  163. * collapses, the currDragItem's display is set to none, when we do not
  164. * hover over a draglist.
  165. * @private {boolean}
  166. */
  167. this.isCurrDragItemAlwaysDisplayed_ = false;
  168. /**
  169. * Whether to update the position of the currDragItem as we drag, i.e.,
  170. * insert the currDragItem each time to the position where it would land if
  171. * we were to end the drag at that point. Defaults to true.
  172. * @private {boolean}
  173. */
  174. this.updateWhileDragging_ = true;
  175. };
  176. goog.inherits(goog.fx.DragListGroup, goog.events.EventTarget);
  177. /**
  178. * Enum to indicate the direction that a drag list grows.
  179. * @enum {number}
  180. */
  181. goog.fx.DragListDirection = {
  182. DOWN: 0, // common
  183. RIGHT: 2, // common
  184. LEFT: 3, // uncommon (except perhaps for right-to-left interfaces)
  185. RIGHT_2D: 4, // common + handles multiple lines if items are wrapped
  186. LEFT_2D: 5 // for rtl languages
  187. };
  188. /**
  189. * Events dispatched by this class.
  190. * @enum {!goog.events.EventId<!goog.fx.DragListGroupEvent>}
  191. */
  192. goog.fx.DragListGroup.EventType = {
  193. /**
  194. * Raised on mouse down, when the dragger is first created. Handle this event
  195. * to customize the dragger element even if the drag never actually starts (if
  196. * the mouse never moves beyond hysteresis).
  197. */
  198. DRAGGERCREATED:
  199. new goog.events.EventId(goog.events.getUniqueId('draggercreated')),
  200. BEFOREDRAGSTART: new goog.events.EventId('beforedragstart'),
  201. DRAGSTART: new goog.events.EventId('dragstart'),
  202. BEFOREDRAGMOVE: new goog.events.EventId('beforedragmove'),
  203. DRAGMOVE: new goog.events.EventId('dragmove'),
  204. BEFOREDRAGEND: new goog.events.EventId('beforedragend'),
  205. /** Raised after the dragged item is moved to the new spot. */
  206. DRAGEND: new goog.events.EventId('dragend'),
  207. /**
  208. * Raised whenever the dragger element is removed:
  209. * - When a drag completes successfully.
  210. * - If the drag never started due to mouseup within hysteresis.
  211. * - If the drag was cancelled by a BEFORE* event.
  212. * - If the drag was cancelled due to focus loss.
  213. */
  214. DRAGGERREMOVED:
  215. new goog.events.EventId(goog.events.getUniqueId('draggerremoved'))
  216. };
  217. /**
  218. * Sets the property of the currDragItem that it is always displayed in the
  219. * list.
  220. */
  221. goog.fx.DragListGroup.prototype.setIsCurrDragItemAlwaysDisplayed = function() {
  222. this.isCurrDragItemAlwaysDisplayed_ = true;
  223. };
  224. /**
  225. * Sets the private property updateWhileDragging_ to false. This disables the
  226. * update of the position of the currDragItem while dragging. It will only be
  227. * placed to its new location once the drag ends.
  228. */
  229. goog.fx.DragListGroup.prototype.setNoUpdateWhileDragging = function() {
  230. this.updateWhileDragging_ = false;
  231. };
  232. /**
  233. * Sets the distance the user has to drag the element before a drag operation
  234. * is started.
  235. * @param {number} distance The number of pixels after which a mousedown and
  236. * move is considered a drag.
  237. */
  238. goog.fx.DragListGroup.prototype.setHysteresis = function(distance) {
  239. this.hysteresisDistance_ = distance;
  240. };
  241. /**
  242. * @return {number} distance The number of pixels after which a mousedown and
  243. * move is considered a drag.
  244. */
  245. goog.fx.DragListGroup.prototype.getHysteresis = function() {
  246. return this.hysteresisDistance_;
  247. };
  248. /** @return {boolean} true if the user is currently dragging an element. */
  249. goog.fx.DragListGroup.prototype.isDragging = function() {
  250. return !!this.dragger_;
  251. };
  252. /**
  253. * Adds a drag list to this DragListGroup.
  254. * All calls to this method must happen before the call to init().
  255. * Remember that all child nodes (except text nodes) will be made draggable to
  256. * any other drag list in this group.
  257. *
  258. * @param {Element} dragListElement Must be a container for a list of items
  259. * that should all be made draggable.
  260. * @param {goog.fx.DragListDirection} growthDirection The direction that this
  261. * drag list grows in (i.e. if an item is appended to the DOM, the list's
  262. * bounding box expands in this direction).
  263. * @param {boolean=} opt_unused Unused argument.
  264. * @param {string=} opt_dragHoverClass CSS class to apply to this drag list when
  265. * the draggerEl hovers over it during a drag action. If present, must be a
  266. * single, valid classname (not a string of space-separated classnames).
  267. */
  268. goog.fx.DragListGroup.prototype.addDragList = function(
  269. dragListElement, growthDirection, opt_unused, opt_dragHoverClass) {
  270. goog.asserts.assert(!this.isInitialized_);
  271. dragListElement.dlgGrowthDirection_ = growthDirection;
  272. dragListElement.dlgDragHoverClass_ = opt_dragHoverClass;
  273. this.dragLists_.push(dragListElement);
  274. };
  275. /**
  276. * Sets a user-supplied function used to get the "handle" element for a drag
  277. * item. The function must accept exactly one argument. The argument may be
  278. * any drag item element.
  279. *
  280. * If not set, the default implementation uses the whole drag item as the
  281. * handle.
  282. *
  283. * @param {function(!Element): Element} getHandleForDragItemFn A function that,
  284. * given any drag item, returns a reference to its "handle" element
  285. * (which may be the drag item element itself).
  286. */
  287. goog.fx.DragListGroup.prototype.setFunctionToGetHandleForDragItem = function(
  288. getHandleForDragItemFn) {
  289. goog.asserts.assert(!this.isInitialized_);
  290. this.getHandleForDragItem_ = getHandleForDragItemFn;
  291. };
  292. /**
  293. * Sets a user-supplied CSS class to add to a drag item on hover (not during a
  294. * drag action).
  295. * @param {...string} var_args The CSS class or classes.
  296. */
  297. goog.fx.DragListGroup.prototype.setDragItemHoverClass = function(var_args) {
  298. goog.asserts.assert(!this.isInitialized_);
  299. this.dragItemHoverClasses_ = goog.array.slice(arguments, 0);
  300. };
  301. /**
  302. * Sets a user-supplied CSS class to add to a drag item handle on hover (not
  303. * during a drag action).
  304. * @param {...string} var_args The CSS class or classes.
  305. */
  306. goog.fx.DragListGroup.prototype.setDragItemHandleHoverClass = function(
  307. var_args) {
  308. goog.asserts.assert(!this.isInitialized_);
  309. this.dragItemHandleHoverClasses_ = goog.array.slice(arguments, 0);
  310. };
  311. /**
  312. * Sets a user-supplied CSS class to add to the current drag item (during a
  313. * drag action).
  314. *
  315. * If not set, the default behavior adds visibility:hidden to the current drag
  316. * item so that it is a block of empty space in the hover drag list (if any).
  317. * If this class is set by the user, then the default behavior does not happen
  318. * (unless, of course, the class also contains visibility:hidden).
  319. *
  320. * @param {...string} var_args The CSS class or classes.
  321. */
  322. goog.fx.DragListGroup.prototype.setCurrDragItemClass = function(var_args) {
  323. goog.asserts.assert(!this.isInitialized_);
  324. this.currDragItemClasses_ = goog.array.slice(arguments, 0);
  325. };
  326. /**
  327. * Sets a user-supplied CSS class to add to the clone of the current drag item
  328. * that's actually being dragged around (during a drag action).
  329. * @param {string} draggerElClass The CSS class.
  330. */
  331. goog.fx.DragListGroup.prototype.setDraggerElClass = function(draggerElClass) {
  332. goog.asserts.assert(!this.isInitialized_);
  333. // Split space-separated classes up into an array.
  334. this.draggerElClasses_ = goog.string.trim(draggerElClass).split(' ');
  335. };
  336. /**
  337. * Performs the initial setup to make all items in all lists draggable.
  338. */
  339. goog.fx.DragListGroup.prototype.init = function() {
  340. if (this.isInitialized_) {
  341. return;
  342. }
  343. for (var i = 0, numLists = this.dragLists_.length; i < numLists; i++) {
  344. var dragList = this.dragLists_[i];
  345. var dragItems = goog.dom.getChildren(dragList);
  346. for (var j = 0, numItems = dragItems.length; j < numItems; ++j) {
  347. this.listenForDragEvents(dragItems[j]);
  348. }
  349. }
  350. this.isInitialized_ = true;
  351. };
  352. /**
  353. * Adds a single item to the given drag list and sets up the drag listeners for
  354. * it.
  355. * If opt_index is specified the item is inserted at this index, otherwise the
  356. * item is added as the last child of the list.
  357. *
  358. * @param {!Element} list The drag list where to add item to.
  359. * @param {!Element} item The new element to add.
  360. * @param {number=} opt_index Index where to insert the item in the list. If not
  361. * specified item is inserted as the last child of list.
  362. */
  363. goog.fx.DragListGroup.prototype.addItemToDragList = function(
  364. list, item, opt_index) {
  365. if (goog.isDef(opt_index)) {
  366. goog.dom.insertChildAt(list, item, opt_index);
  367. } else {
  368. goog.dom.appendChild(list, item);
  369. }
  370. this.listenForDragEvents(item);
  371. };
  372. /** @override */
  373. goog.fx.DragListGroup.prototype.disposeInternal = function() {
  374. this.eventHandler_.dispose();
  375. for (var i = 0, n = this.dragLists_.length; i < n; i++) {
  376. var dragList = this.dragLists_[i];
  377. // Note: IE doesn't allow 'delete' for fields on HTML elements (because
  378. // they're not real JS objects in IE), so we just set them to undefined.
  379. dragList.dlgGrowthDirection_ = undefined;
  380. dragList.dlgDragHoverClass_ = undefined;
  381. }
  382. this.dragLists_.length = 0;
  383. this.dragItems_.length = 0;
  384. this.dragItemForHandle_ = null;
  385. // In the case where a drag event is currently in-progress and dispose is
  386. // called, this cleans up the extra state.
  387. this.cleanupDragDom_();
  388. goog.fx.DragListGroup.superClass_.disposeInternal.call(this);
  389. };
  390. /**
  391. * Caches the heights of each drag list and drag item, except for the current
  392. * drag item.
  393. *
  394. */
  395. goog.fx.DragListGroup.prototype.recacheListAndItemBounds = function() {
  396. this.recacheListAndItemBounds_(this.currDragItem_);
  397. };
  398. /**
  399. * Caches the heights of each drag list and drag item, except for the current
  400. * drag item.
  401. *
  402. * @param {Element} currDragItem The item currently being dragged.
  403. * @private
  404. */
  405. goog.fx.DragListGroup.prototype.recacheListAndItemBounds_ = function(
  406. currDragItem) {
  407. for (var i = 0, n = this.dragLists_.length; i < n; i++) {
  408. var dragList = this.dragLists_[i];
  409. dragList.dlgBounds_ = goog.style.getBounds(dragList);
  410. }
  411. for (var i = 0, n = this.dragItems_.length; i < n; i++) {
  412. var dragItem = this.dragItems_[i];
  413. if (dragItem != currDragItem) {
  414. dragItem.dlgBounds_ = goog.style.getBounds(dragItem);
  415. }
  416. }
  417. };
  418. /**
  419. * Listens for drag events on the given drag item. This method is currently used
  420. * to initialize drag items.
  421. *
  422. * @param {!Element} dragItem the element to initialize. This element has to be
  423. * in one of the drag lists.
  424. * @protected
  425. */
  426. goog.fx.DragListGroup.prototype.listenForDragEvents = function(dragItem) {
  427. var dragItemHandle = this.getHandleForDragItem_(dragItem);
  428. var uid = goog.getUid(dragItemHandle);
  429. this.dragItemForHandle_[uid] = dragItem;
  430. if (this.dragItemHoverClasses_) {
  431. this.eventHandler_.listen(
  432. dragItem, goog.events.EventType.MOUSEOVER,
  433. this.handleDragItemMouseover_);
  434. this.eventHandler_.listen(
  435. dragItem, goog.events.EventType.MOUSEOUT, this.handleDragItemMouseout_);
  436. }
  437. if (this.dragItemHandleHoverClasses_) {
  438. this.eventHandler_.listen(
  439. dragItemHandle, goog.events.EventType.MOUSEOVER,
  440. this.handleDragItemHandleMouseover_);
  441. this.eventHandler_.listen(
  442. dragItemHandle, goog.events.EventType.MOUSEOUT,
  443. this.handleDragItemHandleMouseout_);
  444. }
  445. this.dragItems_.push(dragItem);
  446. this.eventHandler_.listen(
  447. dragItemHandle,
  448. [goog.events.EventType.MOUSEDOWN, goog.events.EventType.TOUCHSTART],
  449. this.handlePotentialDragStart_);
  450. };
  451. /**
  452. * Handles mouse and touch events which may start a drag action.
  453. * @param {!goog.events.BrowserEvent} e MOUSEDOWN or TOUCHSTART event.
  454. * @private
  455. */
  456. goog.fx.DragListGroup.prototype.handlePotentialDragStart_ = function(e) {
  457. var uid = goog.getUid(/** @type {Node} */ (e.currentTarget));
  458. this.currDragItem_ = /** @type {Element} */ (this.dragItemForHandle_[uid]);
  459. this.draggerEl_ = /** @type {!HTMLElement} */ (
  460. this.createDragElementInternal(this.currDragItem_));
  461. if (this.draggerElClasses_) {
  462. // Add CSS class for the clone, if any.
  463. goog.dom.classlist.addAll(
  464. goog.asserts.assert(this.draggerEl_), this.draggerElClasses_ || []);
  465. }
  466. // Place the clone (i.e. draggerEl) at the same position as the actual
  467. // current drag item. This is a bit tricky since
  468. // goog.style.getPageOffset() gets the left-top pos of the border, but
  469. // goog.style.setPageOffset() sets the left-top pos of the margin.
  470. // It's difficult to adjust for the margins of the clone because it's
  471. // difficult to read it: goog.style.getComputedStyle() doesn't work for IE.
  472. // Instead, our workaround is simply to set the clone's margins to 0px.
  473. this.draggerEl_.style.margin = '0';
  474. this.draggerEl_.style.position = 'absolute';
  475. this.draggerEl_.style.visibility = 'hidden';
  476. var doc = goog.dom.getOwnerDocument(this.currDragItem_);
  477. doc.body.appendChild(this.draggerEl_);
  478. // Important: goog.style.setPageOffset() only works correctly for IE when the
  479. // element is already in the document.
  480. var currDragItemPos = goog.style.getPageOffset(this.currDragItem_);
  481. goog.style.setPageOffset(this.draggerEl_, currDragItemPos);
  482. this.dragger_ = new goog.fx.Dragger(this.draggerEl_);
  483. this.dragger_.setHysteresis(this.hysteresisDistance_);
  484. // Listen to events on the dragger. These handlers will be unregistered at
  485. // DRAGEND, when the dragger is disposed of. We can't use eventHandler_,
  486. // because it creates new references to the handler functions at each
  487. // dragging action, and keeps them until DragListGroup is disposed of.
  488. goog.events.listen(
  489. this.dragger_, goog.fx.Dragger.EventType.START, this.handleDragStart_,
  490. false, this);
  491. goog.events.listen(
  492. this.dragger_, goog.fx.Dragger.EventType.END, this.handleDragEnd_, false,
  493. this);
  494. goog.events.listen(
  495. this.dragger_, goog.fx.Dragger.EventType.EARLY_CANCEL, this.cleanup_,
  496. false, this);
  497. this.dispatchEvent(new goog.fx.DragListGroupEvent(
  498. goog.fx.DragListGroup.EventType.DRAGGERCREATED, this, e,
  499. this.currDragItem_, this.draggerEl_, this.dragger_));
  500. this.dragger_.startDrag(e);
  501. };
  502. /**
  503. * Creates copy of node being dragged.
  504. *
  505. * @param {Element} sourceEl Element to copy.
  506. * @return {!Element} The clone of {@code sourceEl}.
  507. * @deprecated Use goog.fx.Dragger.cloneNode().
  508. * @private
  509. */
  510. goog.fx.DragListGroup.prototype.cloneNode_ = function(sourceEl) {
  511. return goog.fx.Dragger.cloneNode(sourceEl);
  512. };
  513. /**
  514. * Generates an element to follow the cursor during dragging, given a drag
  515. * source element. The default behavior is simply to clone the source element,
  516. * but this may be overridden in subclasses. This method is called by
  517. * {@code createDragElement()} before the drag class is added.
  518. *
  519. * @param {Element} sourceEl Drag source element.
  520. * @return {!Element} The new drag element.
  521. * @protected
  522. * @suppress {deprecated}
  523. */
  524. goog.fx.DragListGroup.prototype.createDragElementInternal = function(sourceEl) {
  525. return this.cloneNode_(sourceEl);
  526. };
  527. /**
  528. * Handles the start of a drag action.
  529. * @param {!goog.fx.DragEvent} e goog.fx.Dragger.EventType.START event.
  530. * @private
  531. */
  532. goog.fx.DragListGroup.prototype.handleDragStart_ = function(e) {
  533. if (!this.dispatchEvent(
  534. new goog.fx.DragListGroupEvent(
  535. goog.fx.DragListGroup.EventType.BEFOREDRAGSTART, this,
  536. e.browserEvent, this.currDragItem_, null, null))) {
  537. e.preventDefault();
  538. this.cleanup_();
  539. return;
  540. }
  541. // Record the original location of the current drag item.
  542. // Note: this.origNextItem_ may be null.
  543. this.origList_ = /** @type {Element} */ (this.currDragItem_.parentNode);
  544. this.origNextItem_ = goog.dom.getNextElementSibling(this.currDragItem_);
  545. this.currHoverItem_ = this.origNextItem_;
  546. this.currHoverList_ = this.origList_;
  547. // If there's a CSS class specified for the current drag item, add it.
  548. // Otherwise, make the actual current drag item hidden (takes up space).
  549. if (this.currDragItemClasses_) {
  550. goog.dom.classlist.addAll(
  551. goog.asserts.assert(this.currDragItem_),
  552. this.currDragItemClasses_ || []);
  553. } else {
  554. this.currDragItem_.style.visibility = 'hidden';
  555. }
  556. // Precompute distances from top-left corner to center for efficiency.
  557. var draggerElSize = goog.style.getSize(this.draggerEl_);
  558. this.draggerEl_.halfWidth = draggerElSize.width / 2;
  559. this.draggerEl_.halfHeight = draggerElSize.height / 2;
  560. this.draggerEl_.style.visibility = '';
  561. // Record the bounds of all the drag lists and all the other drag items. This
  562. // caching is for efficiency, so that we don't have to recompute the bounds on
  563. // each drag move. Do this in the state where the current drag item is not in
  564. // any of the lists, except when update while dragging is disabled, as in this
  565. // case the current drag item does not get removed until drag ends.
  566. if (this.updateWhileDragging_) {
  567. this.currDragItem_.style.display = 'none';
  568. }
  569. this.recacheListAndItemBounds_(this.currDragItem_);
  570. this.currDragItem_.style.display = '';
  571. // Listen to events on the dragger.
  572. goog.events.listen(
  573. this.dragger_, goog.fx.Dragger.EventType.DRAG, this.handleDragMove_,
  574. false, this);
  575. this.dispatchEvent(
  576. new goog.fx.DragListGroupEvent(
  577. goog.fx.DragListGroup.EventType.DRAGSTART, this, e.browserEvent,
  578. this.currDragItem_, this.draggerEl_, this.dragger_));
  579. };
  580. /**
  581. * Handles a drag movement (i.e. DRAG event fired by the dragger).
  582. *
  583. * @param {goog.fx.DragEvent} dragEvent Event object fired by the dragger.
  584. * @return {boolean} The return value for the event.
  585. * @private
  586. */
  587. goog.fx.DragListGroup.prototype.handleDragMove_ = function(dragEvent) {
  588. // Compute the center of the dragger element (i.e. the cloned drag item).
  589. var draggerElPos = goog.style.getPageOffset(this.draggerEl_);
  590. var draggerElCenter = new goog.math.Coordinate(
  591. draggerElPos.x + this.draggerEl_.halfWidth,
  592. draggerElPos.y + this.draggerEl_.halfHeight);
  593. // Check whether the center is hovering over one of the drag lists.
  594. var hoverList = this.getHoverDragList_(draggerElCenter);
  595. // If hovering over a list, find the next item (if drag were to end now).
  596. var hoverNextItem =
  597. hoverList ? this.getHoverNextItem_(hoverList, draggerElCenter) : null;
  598. var rv = this.dispatchEvent(
  599. new goog.fx.DragListGroupEvent(
  600. goog.fx.DragListGroup.EventType.BEFOREDRAGMOVE, this, dragEvent,
  601. this.currDragItem_, this.draggerEl_, this.dragger_, draggerElCenter,
  602. hoverList, hoverNextItem));
  603. if (!rv) {
  604. return false;
  605. }
  606. if (hoverList) {
  607. if (this.updateWhileDragging_) {
  608. this.insertCurrDragItem_(hoverList, hoverNextItem);
  609. } else {
  610. // If update while dragging is disabled do not insert
  611. // the dragged item, but update the hovered item instead.
  612. this.updateCurrHoverItem(hoverNextItem, draggerElCenter);
  613. }
  614. this.currDragItem_.style.display = '';
  615. // Add drag list's hover class (if any).
  616. if (hoverList.dlgDragHoverClass_) {
  617. goog.dom.classlist.add(
  618. goog.asserts.assert(hoverList), hoverList.dlgDragHoverClass_);
  619. }
  620. } else {
  621. // Not hovering over a drag list, so remove the item altogether unless
  622. // specified otherwise by the user.
  623. if (!this.isCurrDragItemAlwaysDisplayed_) {
  624. this.currDragItem_.style.display = 'none';
  625. }
  626. // Remove hover classes (if any) from all drag lists.
  627. for (var i = 0, n = this.dragLists_.length; i < n; i++) {
  628. var dragList = this.dragLists_[i];
  629. if (dragList.dlgDragHoverClass_) {
  630. goog.dom.classlist.remove(
  631. goog.asserts.assert(dragList), dragList.dlgDragHoverClass_);
  632. }
  633. }
  634. }
  635. // If the current hover list is different than the last, the lists may have
  636. // shrunk, so we should recache the bounds.
  637. if (hoverList != this.currHoverList_) {
  638. this.currHoverList_ = hoverList;
  639. this.recacheListAndItemBounds_(this.currDragItem_);
  640. }
  641. this.dispatchEvent(
  642. new goog.fx.DragListGroupEvent(
  643. goog.fx.DragListGroup.EventType.DRAGMOVE, this, dragEvent,
  644. /** @type {Element} */ (this.currDragItem_), this.draggerEl_,
  645. this.dragger_, draggerElCenter, hoverList, hoverNextItem));
  646. // Return false to prevent selection due to mouse drag.
  647. return false;
  648. };
  649. /**
  650. * Clear all our temporary fields that are only defined while dragging, and
  651. * all the bounds info stored on the drag lists and drag elements.
  652. * @param {!goog.events.Event=} opt_e EARLY_CANCEL event from the dragger if
  653. * cleanup_ was called as an event handler.
  654. * @private
  655. */
  656. goog.fx.DragListGroup.prototype.cleanup_ = function(opt_e) {
  657. this.cleanupDragDom_();
  658. this.currDragItem_ = null;
  659. this.currHoverList_ = null;
  660. this.origList_ = null;
  661. this.origNextItem_ = null;
  662. this.draggerEl_ = null;
  663. this.dragger_ = null;
  664. // Note: IE doesn't allow 'delete' for fields on HTML elements (because
  665. // they're not real JS objects in IE), so we just set them to null.
  666. for (var i = 0, n = this.dragLists_.length; i < n; i++) {
  667. this.dragLists_[i].dlgBounds_ = null;
  668. }
  669. for (var i = 0, n = this.dragItems_.length; i < n; i++) {
  670. this.dragItems_[i].dlgBounds_ = null;
  671. }
  672. };
  673. /**
  674. * Handles the end or the cancellation of a drag action, i.e. END or CLEANUP
  675. * event fired by the dragger.
  676. *
  677. * @param {!goog.fx.DragEvent} dragEvent Event object fired by the dragger.
  678. * @return {boolean} Whether the event was handled.
  679. * @private
  680. */
  681. goog.fx.DragListGroup.prototype.handleDragEnd_ = function(dragEvent) {
  682. var rv = this.dispatchEvent(
  683. new goog.fx.DragListGroupEvent(
  684. goog.fx.DragListGroup.EventType.BEFOREDRAGEND, this, dragEvent,
  685. /** @type {Element} */ (this.currDragItem_), this.draggerEl_,
  686. this.dragger_));
  687. if (!rv) {
  688. return false;
  689. }
  690. // If update while dragging is disabled insert the current drag item into
  691. // its intended location.
  692. if (!this.updateWhileDragging_) {
  693. this.insertCurrHoverItem();
  694. }
  695. // The DRAGEND handler may need the new order of the list items. Clean up the
  696. // garbage.
  697. // TODO(user): Regression test.
  698. this.cleanupDragDom_();
  699. this.dispatchEvent(
  700. new goog.fx.DragListGroupEvent(
  701. goog.fx.DragListGroup.EventType.DRAGEND, this, dragEvent,
  702. this.currDragItem_, this.draggerEl_, this.dragger_));
  703. this.cleanup_();
  704. return true;
  705. };
  706. /**
  707. * Cleans up DOM changes that are made by the {@code handleDrag*} methods.
  708. * @private
  709. */
  710. goog.fx.DragListGroup.prototype.cleanupDragDom_ = function() {
  711. // Disposes of the dragger and remove the cloned drag item.
  712. goog.dispose(this.dragger_);
  713. var hadDragger = this.draggerEl_ && this.draggerEl_.parentElement;
  714. if (this.draggerEl_) {
  715. goog.dom.removeNode(this.draggerEl_);
  716. }
  717. // If the current drag item is not in any list, put it back in its original
  718. // location.
  719. if (this.currDragItem_ && this.currDragItem_.style.display == 'none') {
  720. // Note: this.origNextItem_ may be null, but insertBefore() still works.
  721. this.origList_.insertBefore(this.currDragItem_, this.origNextItem_);
  722. this.currDragItem_.style.display = '';
  723. }
  724. // If there's a CSS class specified for the current drag item, remove it.
  725. // Otherwise, make the current drag item visible (instead of empty space).
  726. if (this.currDragItemClasses_ && this.currDragItem_) {
  727. goog.dom.classlist.removeAll(
  728. goog.asserts.assert(this.currDragItem_),
  729. this.currDragItemClasses_ || []);
  730. } else if (this.currDragItem_) {
  731. this.currDragItem_.style.visibility = '';
  732. }
  733. // Remove hover classes (if any) from all drag lists.
  734. for (var i = 0, n = this.dragLists_.length; i < n; i++) {
  735. var dragList = this.dragLists_[i];
  736. if (dragList.dlgDragHoverClass_) {
  737. goog.dom.classlist.remove(
  738. goog.asserts.assert(dragList), dragList.dlgDragHoverClass_);
  739. }
  740. }
  741. if (hadDragger) {
  742. this.dispatchEvent(new goog.fx.DragListGroupEvent(
  743. goog.fx.DragListGroup.EventType.DRAGGERREMOVED, this, null,
  744. this.currDragItem_, this.draggerEl_, this.dragger_));
  745. }
  746. };
  747. /**
  748. * Default implementation of the function to get the "handle" element for a
  749. * drag item. By default, we use the whole drag item as the handle. Users can
  750. * change this by calling setFunctionToGetHandleForDragItem().
  751. *
  752. * @param {!Element} dragItem The drag item to get the handle for.
  753. * @return {Element} The dragItem element itself.
  754. * @private
  755. */
  756. goog.fx.DragListGroup.prototype.getHandleForDragItem_ = function(dragItem) {
  757. return dragItem;
  758. };
  759. /**
  760. * Handles a MOUSEOVER event fired on a drag item.
  761. * @param {goog.events.BrowserEvent} e The event.
  762. * @private
  763. */
  764. goog.fx.DragListGroup.prototype.handleDragItemMouseover_ = function(e) {
  765. var targetEl = goog.asserts.assertElement(e.currentTarget);
  766. goog.dom.classlist.addAll(targetEl, this.dragItemHoverClasses_ || []);
  767. };
  768. /**
  769. * Handles a MOUSEOUT event fired on a drag item.
  770. * @param {goog.events.BrowserEvent} e The event.
  771. * @private
  772. */
  773. goog.fx.DragListGroup.prototype.handleDragItemMouseout_ = function(e) {
  774. var targetEl = goog.asserts.assertElement(e.currentTarget);
  775. goog.dom.classlist.removeAll(targetEl, this.dragItemHoverClasses_ || []);
  776. };
  777. /**
  778. * Handles a MOUSEOVER event fired on the handle element of a drag item.
  779. * @param {goog.events.BrowserEvent} e The event.
  780. * @private
  781. */
  782. goog.fx.DragListGroup.prototype.handleDragItemHandleMouseover_ = function(e) {
  783. var targetEl = goog.asserts.assertElement(e.currentTarget);
  784. goog.dom.classlist.addAll(targetEl, this.dragItemHandleHoverClasses_ || []);
  785. };
  786. /**
  787. * Handles a MOUSEOUT event fired on the handle element of a drag item.
  788. * @param {goog.events.BrowserEvent} e The event.
  789. * @private
  790. */
  791. goog.fx.DragListGroup.prototype.handleDragItemHandleMouseout_ = function(e) {
  792. var targetEl = goog.asserts.assertElement(e.currentTarget);
  793. goog.dom.classlist.removeAll(
  794. targetEl, this.dragItemHandleHoverClasses_ || []);
  795. };
  796. /**
  797. * Helper for handleDragMove_().
  798. * Given the position of the center of the dragger element, figures out whether
  799. * it's currently hovering over any of the drag lists.
  800. *
  801. * @param {goog.math.Coordinate} draggerElCenter The center position of the
  802. * dragger element.
  803. * @return {Element} If currently hovering over a drag list, returns the drag
  804. * list element. Else returns null.
  805. * @private
  806. */
  807. goog.fx.DragListGroup.prototype.getHoverDragList_ = function(draggerElCenter) {
  808. // If the current drag item was in a list last time we did this, then check
  809. // that same list first.
  810. var prevHoverList = null;
  811. if (this.currDragItem_.style.display != 'none') {
  812. prevHoverList = /** @type {Element} */ (this.currDragItem_.parentNode);
  813. // Important: We can't use the cached bounds for this list because the
  814. // cached bounds are based on the case where the current drag item is not
  815. // in the list. Since the current drag item is known to be in this list, we
  816. // must recompute the list's bounds.
  817. var prevHoverListBounds = goog.style.getBounds(prevHoverList);
  818. if (this.isInRect_(draggerElCenter, prevHoverListBounds)) {
  819. return prevHoverList;
  820. }
  821. }
  822. for (var i = 0, n = this.dragLists_.length; i < n; i++) {
  823. var dragList = this.dragLists_[i];
  824. if (dragList == prevHoverList) {
  825. continue;
  826. }
  827. if (this.isInRect_(draggerElCenter, dragList.dlgBounds_)) {
  828. return dragList;
  829. }
  830. }
  831. return null;
  832. };
  833. /**
  834. * Checks whether a coordinate position resides inside a rectangle.
  835. * @param {goog.math.Coordinate} pos The coordinate position.
  836. * @param {goog.math.Rect} rect The rectangle.
  837. * @return {boolean} True if 'pos' is within the bounds of 'rect'.
  838. * @private
  839. */
  840. goog.fx.DragListGroup.prototype.isInRect_ = function(pos, rect) {
  841. return pos.x > rect.left && pos.x < rect.left + rect.width &&
  842. pos.y > rect.top && pos.y < rect.top + rect.height;
  843. };
  844. /**
  845. * Updates the value of currHoverItem_.
  846. *
  847. * This method is used for insertion only when updateWhileDragging_ is false.
  848. * The below implementation is the basic one. This method can be extended by
  849. * a subclass to support changes to hovered item (eg: highlighting). Parametr
  850. * opt_draggerElCenter can be used for more sophisticated effects.
  851. *
  852. * @param {Element} hoverNextItem element of the list that is hovered over.
  853. * @param {goog.math.Coordinate=} opt_draggerElCenter current position of
  854. * the dragged element.
  855. * @protected
  856. */
  857. goog.fx.DragListGroup.prototype.updateCurrHoverItem = function(
  858. hoverNextItem, opt_draggerElCenter) {
  859. if (hoverNextItem) {
  860. this.currHoverItem_ = hoverNextItem;
  861. }
  862. };
  863. /**
  864. * Inserts the currently dragged item in its new place.
  865. *
  866. * This method is used for insertion only when updateWhileDragging_ is false
  867. * (otherwise there is no need for that). In the basic implementation
  868. * the element is inserted before the currently hovered over item (this can
  869. * be changed by overriding the method in subclasses).
  870. *
  871. * @protected
  872. */
  873. goog.fx.DragListGroup.prototype.insertCurrHoverItem = function() {
  874. this.origList_.insertBefore(this.currDragItem_, this.currHoverItem_);
  875. };
  876. /**
  877. * Helper for handleDragMove_().
  878. * Given the position of the center of the dragger element, plus the drag list
  879. * that it's currently hovering over, figures out the next drag item in the
  880. * list that follows the current position of the dragger element. (I.e. if
  881. * the drag action ends right now, it would become the item after the current
  882. * drag item.)
  883. *
  884. * @param {Element} hoverList The drag list that we're hovering over.
  885. * @param {goog.math.Coordinate} draggerElCenter The center position of the
  886. * dragger element.
  887. * @return {Element} Returns the earliest item in the hover list that belongs
  888. * after the current position of the dragger element. If all items in the
  889. * list should come before the current drag item, then returns null.
  890. * @private
  891. */
  892. goog.fx.DragListGroup.prototype.getHoverNextItem_ = function(
  893. hoverList, draggerElCenter) {
  894. if (hoverList == null) {
  895. throw Error('getHoverNextItem_ called with null hoverList.');
  896. }
  897. // The definition of what it means for the draggerEl to be "before" a given
  898. // item in the hover drag list is not always the same. It changes based on
  899. // the growth direction of the hover drag list in question.
  900. /** @type {number} */
  901. var relevantCoord = 0;
  902. var getRelevantBoundFn;
  903. var isBeforeFn;
  904. var pickClosestRow = false;
  905. var distanceToClosestRow = undefined;
  906. switch (hoverList.dlgGrowthDirection_) {
  907. case goog.fx.DragListDirection.DOWN:
  908. // "Before" means draggerElCenter.y is less than item's bottom y-value.
  909. relevantCoord = draggerElCenter.y;
  910. getRelevantBoundFn = goog.fx.DragListGroup.getBottomBound_;
  911. isBeforeFn = goog.fx.DragListGroup.isLessThan_;
  912. break;
  913. case goog.fx.DragListDirection.RIGHT_2D:
  914. pickClosestRow = true;
  915. case goog.fx.DragListDirection.RIGHT:
  916. // "Before" means draggerElCenter.x is less than item's right x-value.
  917. relevantCoord = draggerElCenter.x;
  918. getRelevantBoundFn = goog.fx.DragListGroup.getRightBound_;
  919. isBeforeFn = goog.fx.DragListGroup.isLessThan_;
  920. break;
  921. case goog.fx.DragListDirection.LEFT_2D:
  922. pickClosestRow = true;
  923. case goog.fx.DragListDirection.LEFT:
  924. // "Before" means draggerElCenter.x is greater than item's left x-value.
  925. relevantCoord = draggerElCenter.x;
  926. getRelevantBoundFn = goog.fx.DragListGroup.getLeftBound_;
  927. isBeforeFn = goog.fx.DragListGroup.isGreaterThan_;
  928. break;
  929. }
  930. // This holds the earliest drag item found so far that should come after
  931. // this.currDragItem_ in the hover drag list (based on draggerElCenter).
  932. var earliestAfterItem = null;
  933. // This is the position of the relevant bound for the earliestAfterItem,
  934. // where "relevant" is determined by the growth direction of hoverList.
  935. var earliestAfterItemRelevantBound;
  936. var hoverListItems = goog.dom.getChildren(hoverList);
  937. for (var i = 0, n = hoverListItems.length; i < n; i++) {
  938. var item = hoverListItems[i];
  939. if (item == this.currDragItem_) {
  940. continue;
  941. }
  942. var relevantBound = getRelevantBoundFn(item.dlgBounds_);
  943. // When the hoverlist is broken into multiple rows (i.e., in the case of
  944. // LEFT_2D and RIGHT_2D) it is no longer enough to only look at the
  945. // x-coordinate alone in order to find the {@earliestAfterItem} in the
  946. // hoverlist. Make sure it is chosen from the row closest to the
  947. // {@code draggerElCenter}.
  948. if (pickClosestRow) {
  949. var distanceToRow = goog.fx.DragListGroup.verticalDistanceFromItem_(
  950. item, draggerElCenter);
  951. // Initialize the distance to the closest row to the current value if
  952. // undefined.
  953. if (!goog.isDef(distanceToClosestRow)) {
  954. distanceToClosestRow = distanceToRow;
  955. }
  956. if (isBeforeFn(relevantCoord, relevantBound) &&
  957. (earliestAfterItemRelevantBound == undefined ||
  958. (distanceToRow < distanceToClosestRow) ||
  959. ((distanceToRow == distanceToClosestRow) &&
  960. (isBeforeFn(relevantBound, earliestAfterItemRelevantBound) ||
  961. relevantBound == earliestAfterItemRelevantBound)))) {
  962. earliestAfterItem = item;
  963. earliestAfterItemRelevantBound = relevantBound;
  964. }
  965. // Update distance to closest row.
  966. if (distanceToRow < distanceToClosestRow) {
  967. distanceToClosestRow = distanceToRow;
  968. }
  969. } else if (
  970. isBeforeFn(relevantCoord, relevantBound) &&
  971. (earliestAfterItemRelevantBound == undefined ||
  972. isBeforeFn(relevantBound, earliestAfterItemRelevantBound))) {
  973. earliestAfterItem = item;
  974. earliestAfterItemRelevantBound = relevantBound;
  975. }
  976. }
  977. // If we ended up picking an element that is not in the closest row it can
  978. // only happen if we should have picked the last one in which case there is
  979. // no consecutive element.
  980. if (!goog.isNull(earliestAfterItem) &&
  981. goog.fx.DragListGroup.verticalDistanceFromItem_(
  982. earliestAfterItem, draggerElCenter) > distanceToClosestRow) {
  983. return null;
  984. } else {
  985. return earliestAfterItem;
  986. }
  987. };
  988. /**
  989. * Private helper for getHoverNextItem().
  990. * Given an item and a target determine the vertical distance from the item's
  991. * center to the target.
  992. * @param {Element} item The item to measure the distance from.
  993. * @param {goog.math.Coordinate} target The (x,y) coordinate of the target
  994. * to measure the distance to.
  995. * @return {number} The vertical distance between the center of the item and
  996. * the target.
  997. * @private
  998. */
  999. goog.fx.DragListGroup.verticalDistanceFromItem_ = function(item, target) {
  1000. var itemBounds = item.dlgBounds_;
  1001. var itemCenterY = itemBounds.top + (itemBounds.height - 1) / 2;
  1002. return Math.abs(target.y - itemCenterY);
  1003. };
  1004. /**
  1005. * Private helper for getHoverNextItem_().
  1006. * Given the bounds of an item, computes the item's bottom y-value.
  1007. * @param {goog.math.Rect} itemBounds The bounds of the item.
  1008. * @return {number} The item's bottom y-value.
  1009. * @private
  1010. */
  1011. goog.fx.DragListGroup.getBottomBound_ = function(itemBounds) {
  1012. return itemBounds.top + itemBounds.height - 1;
  1013. };
  1014. /**
  1015. * Private helper for getHoverNextItem_().
  1016. * Given the bounds of an item, computes the item's right x-value.
  1017. * @param {goog.math.Rect} itemBounds The bounds of the item.
  1018. * @return {number} The item's right x-value.
  1019. * @private
  1020. */
  1021. goog.fx.DragListGroup.getRightBound_ = function(itemBounds) {
  1022. return itemBounds.left + itemBounds.width - 1;
  1023. };
  1024. /**
  1025. * Private helper for getHoverNextItem_().
  1026. * Given the bounds of an item, computes the item's left x-value.
  1027. * @param {goog.math.Rect} itemBounds The bounds of the item.
  1028. * @return {number} The item's left x-value.
  1029. * @private
  1030. */
  1031. goog.fx.DragListGroup.getLeftBound_ = function(itemBounds) {
  1032. return itemBounds.left || 0;
  1033. };
  1034. /**
  1035. * Private helper for getHoverNextItem_().
  1036. * @param {number} a Number to compare.
  1037. * @param {number} b Number to compare.
  1038. * @return {boolean} Whether a is less than b.
  1039. * @private
  1040. */
  1041. goog.fx.DragListGroup.isLessThan_ = function(a, b) {
  1042. return a < b;
  1043. };
  1044. /**
  1045. * Private helper for getHoverNextItem_().
  1046. * @param {number} a Number to compare.
  1047. * @param {number} b Number to compare.
  1048. * @return {boolean} Whether a is greater than b.
  1049. * @private
  1050. */
  1051. goog.fx.DragListGroup.isGreaterThan_ = function(a, b) {
  1052. return a > b;
  1053. };
  1054. /**
  1055. * Inserts the current drag item to the appropriate location in the drag list
  1056. * that we're hovering over (if the current drag item is not already there).
  1057. *
  1058. * @param {Element} hoverList The drag list we're hovering over.
  1059. * @param {Element} hoverNextItem The next item in the hover drag list.
  1060. * @private
  1061. */
  1062. goog.fx.DragListGroup.prototype.insertCurrDragItem_ = function(
  1063. hoverList, hoverNextItem) {
  1064. if (this.currDragItem_.parentNode != hoverList ||
  1065. goog.dom.getNextElementSibling(this.currDragItem_) != hoverNextItem) {
  1066. // The current drag item is not in the correct location, so we move it.
  1067. // Note: hoverNextItem may be null, but insertBefore() still works.
  1068. hoverList.insertBefore(this.currDragItem_, hoverNextItem);
  1069. }
  1070. };
  1071. /**
  1072. * The event object dispatched by DragListGroup.
  1073. * The fields draggerElCenter, hoverList, and hoverNextItem are only available
  1074. * for the BEFOREDRAGMOVE and DRAGMOVE events.
  1075. *
  1076. * @param {!goog.fx.DragListGroup.EventType} type
  1077. * @param {goog.fx.DragListGroup} dragListGroup A reference to the associated
  1078. * DragListGroup object.
  1079. * @param {goog.events.BrowserEvent|goog.fx.DragEvent} event The event fired
  1080. * by the browser or fired by the dragger.
  1081. * @param {Element} currDragItem The current drag item being moved.
  1082. * @param {Element} draggerEl The clone of the current drag item that's actually
  1083. * being dragged around.
  1084. * @param {goog.fx.Dragger} dragger The dragger object.
  1085. * @param {goog.math.Coordinate=} opt_draggerElCenter The current center
  1086. * position of the draggerEl.
  1087. * @param {Element=} opt_hoverList The current drag list that's being hovered
  1088. * over, or null if the center of draggerEl is outside of any drag lists.
  1089. * If not null and the drag action ends right now, then currDragItem will
  1090. * end up in this list.
  1091. * @param {Element=} opt_hoverNextItem The current next item in the hoverList
  1092. * that the draggerEl is hovering over. (I.e. If the drag action ends
  1093. * right now, then this item would become the next item after the new
  1094. * location of currDragItem.) May be null if not applicable or if
  1095. * currDragItem would be added to the end of hoverList.
  1096. * @constructor
  1097. * @struct
  1098. * @extends {goog.events.Event}
  1099. */
  1100. goog.fx.DragListGroupEvent = function(
  1101. type, dragListGroup, event, currDragItem, draggerEl, dragger,
  1102. opt_draggerElCenter, opt_hoverList, opt_hoverNextItem) {
  1103. goog.events.Event.call(this, type);
  1104. /**
  1105. * A reference to the associated DragListGroup object.
  1106. * @type {goog.fx.DragListGroup}
  1107. */
  1108. this.dragListGroup = dragListGroup;
  1109. /**
  1110. * The event fired by the browser or fired by the dragger.
  1111. * @type {goog.events.BrowserEvent|goog.fx.DragEvent}
  1112. */
  1113. this.event = event;
  1114. /**
  1115. * The current drag item being move.
  1116. * @type {Element}
  1117. */
  1118. this.currDragItem = currDragItem;
  1119. /**
  1120. * The clone of the current drag item that's actually being dragged around.
  1121. * @type {Element}
  1122. */
  1123. this.draggerEl = draggerEl;
  1124. /**
  1125. * The dragger object.
  1126. * @type {goog.fx.Dragger}
  1127. */
  1128. this.dragger = dragger;
  1129. /**
  1130. * The current center position of the draggerEl.
  1131. * @type {goog.math.Coordinate|undefined}
  1132. */
  1133. this.draggerElCenter = opt_draggerElCenter;
  1134. /**
  1135. * The current drag list that's being hovered over, or null if the center of
  1136. * draggerEl is outside of any drag lists. (I.e. If not null and the drag
  1137. * action ends right now, then currDragItem will end up in this list.)
  1138. * @type {Element|undefined}
  1139. */
  1140. this.hoverList = opt_hoverList;
  1141. /**
  1142. * The current next item in the hoverList that the draggerEl is hovering over.
  1143. * (I.e. If the drag action ends right now, then this item would become the
  1144. * next item after the new location of currDragItem.) May be null if not
  1145. * applicable or if currDragItem would be added to the end of hoverList.
  1146. * @type {Element|undefined}
  1147. */
  1148. this.hoverNextItem = opt_hoverNextItem;
  1149. };
  1150. goog.inherits(goog.fx.DragListGroupEvent, goog.events.Event);