API Docs for: 3.13.0
Show:

File: dd/js/drag.js

  1.  
  2. /**
  3. * Provides the ability to drag a Node.
  4. * @module dd
  5. * @submodule dd-drag
  6. */
  7. /**
  8. * Provides the ability to drag a Node.
  9. * @class Drag
  10. * @extends Base
  11. * @constructor
  12. * @namespace DD
  13. */
  14.  
  15. var DDM = Y.DD.DDM,
  16. NODE = 'node',
  17. DRAGGING = 'dragging',
  18. DRAG_NODE = 'dragNode',
  19. OFFSET_HEIGHT = 'offsetHeight',
  20. OFFSET_WIDTH = 'offsetWidth',
  21. /**
  22. * Handles the mouseup DOM event, does nothing internally just fires.
  23. * @event drag:mouseup
  24. * @bubbles DDM
  25. * @type {CustomEvent}
  26. */
  27. /**
  28. * Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
  29. * @event drag:mouseDown
  30. * @preventable _defMouseDownFn
  31. * @param {EventFacade} event An Event Facade object with the following specific property added:
  32. * <dl><dt>ev</dt><dd>The original mousedown event.</dd></dl>
  33. * @bubbles DDM
  34. * @type {CustomEvent}
  35. */
  36. EV_MOUSE_DOWN = 'drag:mouseDown',
  37. /**
  38. * Fires after the mousedown event has been cleared.
  39. * @event drag:afterMouseDown
  40. * @param {EventFacade} event An Event Facade object with the following specific property added:
  41. * <dl><dt>ev</dt><dd>The original mousedown event.</dd></dl>
  42. * @bubbles DDM
  43. * @type {CustomEvent}
  44. */
  45. EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
  46. /**
  47. * Fires after a handle is removed.
  48. * @event drag:removeHandle
  49. * @param {EventFacade} event An Event Facade object with the following specific property added:
  50. * <dl><dt>handle</dt><dd>The handle that was removed.</dd></dl>
  51. * @bubbles DDM
  52. * @type {CustomEvent}
  53. */
  54. EV_REMOVE_HANDLE = 'drag:removeHandle',
  55. /**
  56. * Fires after a handle is added.
  57. * @event drag:addHandle
  58. * @param {EventFacade} event An Event Facade object with the following specific property added:
  59. * <dl><dt>handle</dt><dd>The handle that was added.</dd></dl>
  60. * @bubbles DDM
  61. * @type {CustomEvent}
  62. */
  63. EV_ADD_HANDLE = 'drag:addHandle',
  64. /**
  65. * Fires after an invalid selector is removed.
  66. * @event drag:removeInvalid
  67. * @param {EventFacade} event An Event Facade object with the following specific property added:
  68. * <dl><dt>handle</dt><dd>The handle that was removed.</dd></dl>
  69. * @bubbles DDM
  70. * @type {CustomEvent}
  71. */
  72. EV_REMOVE_INVALID = 'drag:removeInvalid',
  73. /**
  74. * Fires after an invalid selector is added.
  75. * @event drag:addInvalid
  76. * @param {EventFacade} event An Event Facade object with the following specific property added:
  77. * <dl><dt>handle</dt><dd>The handle that was added.</dd></dl>
  78. * @bubbles DDM
  79. * @type {CustomEvent}
  80. */
  81. EV_ADD_INVALID = 'drag:addInvalid',
  82. /**
  83. * Fires at the start of a drag operation.
  84. * @event drag:start
  85. * @param {EventFacade} event An Event Facade object with the following specific property added:
  86. * <dl>
  87. * <dt>pageX</dt><dd>The original node position X.</dd>
  88. * <dt>pageY</dt><dd>The original node position Y.</dd>
  89. * <dt>startTime</dt><dd>The startTime of the event. getTime on the current Date object.</dd>
  90. * </dl>
  91. * @bubbles DDM
  92. * @type {CustomEvent}
  93. */
  94. EV_START = 'drag:start',
  95. /**
  96. * Fires at the end of a drag operation.
  97. * @event drag:end
  98. * @param {EventFacade} event An Event Facade object with the following specific property added:
  99. * <dl>
  100. * <dt>pageX</dt><dd>The current node position X.</dd>
  101. * <dt>pageY</dt><dd>The current node position Y.</dd>
  102. * <dt>startTime</dt><dd>The startTime of the event, from the start event.</dd>
  103. * <dt>endTime</dt><dd>The endTime of the event. getTime on the current Date object.</dd>
  104. * </dl>
  105. * @bubbles DDM
  106. * @type {CustomEvent}
  107. */
  108. EV_END = 'drag:end',
  109. /**
  110. * Fires every mousemove during a drag operation.
  111. * @event drag:drag
  112. * @param {EventFacade} event An Event Facade object with the following specific property added:
  113. * <dl>
  114. * <dt>pageX</dt><dd>The current node position X.</dd>
  115. * <dt>pageY</dt><dd>The current node position Y.</dd>
  116. * <dt>scroll</dt><dd>Should a scroll action occur.</dd>
  117. * <dt>info</dt><dd>Object hash containing calculated XY arrays: start, xy, delta, offset</dd>
  118. * </dl>
  119. * @bubbles DDM
  120. * @type {CustomEvent}
  121. */
  122. EV_DRAG = 'drag:drag',
  123. /**
  124. * Fires when this node is aligned.
  125. * @event drag:align
  126. * @preventable _defAlignFn
  127. * @param {EventFacade} event An Event Facade object with the following specific property added:
  128. * <dl>
  129. * <dt>pageX</dt><dd>The current node position X.</dd>
  130. * <dt>pageY</dt><dd>The current node position Y.</dd>
  131. * </dl>
  132. * @bubbles DDM
  133. * @type {CustomEvent}
  134. */
  135. EV_ALIGN = 'drag:align',
  136. /**
  137. * Fires when this node is over a Drop Target. (Fired from dd-drop)
  138. * @event drag:over
  139. * @param {EventFacade} event An Event Facade object with the following specific property added:
  140. * <dl>
  141. * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
  142. * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
  143. * </dl>
  144. * @bubbles DDM
  145. * @type {CustomEvent}
  146. */
  147. /**
  148. * Fires when this node enters a Drop Target. (Fired from dd-drop)
  149. * @event drag:enter
  150. * @param {EventFacade} event An Event Facade object with the following specific property added:
  151. * <dl>
  152. * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
  153. * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
  154. * </dl>
  155. * @bubbles DDM
  156. * @type {CustomEvent}
  157. */
  158. /**
  159. * Fires when this node exits a Drop Target. (Fired from dd-drop)
  160. * @event drag:exit
  161. * @param {EventFacade} event An Event Facade object with the following specific property added:
  162. * <dl>
  163. * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
  164. * </dl>
  165. * @bubbles DDM
  166. * @type {CustomEvent}
  167. */
  168. /**
  169. * Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
  170. * @event drag:drophit
  171. * @param {EventFacade} event An Event Facade object with the following specific property added:
  172. * <dl>
  173. * <dt>drop</dt><dd>The best guess on what was dropped on.</dd>
  174. * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
  175. * <dt>others</dt><dd>An array of all the other drop targets that was dropped on.</dd>
  176. * </dl>
  177. * @bubbles DDM
  178. * @type {CustomEvent}
  179. */
  180. /**
  181. * Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
  182. * @event drag:dropmiss
  183. * @param {EventFacade} event An Event Facade object with the following specific property added:
  184. * <dl>
  185. * <dt>pageX</dt><dd>The current node position X.</dd>
  186. * <dt>pageY</dt><dd>The current node position Y.</dd>
  187. * </dl>
  188. * @bubbles DDM
  189. * @type {CustomEvent}
  190. */
  191.  
  192. Drag = function(o) {
  193. this._lazyAddAttrs = false;
  194. Drag.superclass.constructor.apply(this, arguments);
  195.  
  196. var valid = DDM._regDrag(this);
  197. if (!valid) {
  198. Y.error('Failed to register node, already in use: ' + o.node);
  199. }
  200. };
  201.  
  202. Drag.NAME = 'drag';
  203.  
  204. /**
  205. * This property defaults to "mousedown", but when drag-gestures is loaded, it is changed to "gesturemovestart"
  206. * @static
  207. * @property START_EVENT
  208. */
  209. Drag.START_EVENT = 'mousedown';
  210.  
  211. Drag.ATTRS = {
  212. /**
  213. * Y.Node instance to use as the element to initiate a drag operation
  214. * @attribute node
  215. * @type Node
  216. */
  217. node: {
  218. setter: function(node) {
  219. if (this._canDrag(node)) {
  220. return node;
  221. }
  222. var n = Y.one(node);
  223. if (!n) {
  224. Y.error('DD.Drag: Invalid Node Given: ' + node);
  225. }
  226. return n;
  227. }
  228. },
  229. /**
  230. * Y.Node instance to use as the draggable element, defaults to node
  231. * @attribute dragNode
  232. * @type Node
  233. */
  234. dragNode: {
  235. setter: function(node) {
  236. if (this._canDrag(node)) {
  237. return node;
  238. }
  239. var n = Y.one(node);
  240. if (!n) {
  241. Y.error('DD.Drag: Invalid dragNode Given: ' + node);
  242. }
  243. return n;
  244. }
  245. },
  246. /**
  247. * Offset the drag element by the difference in cursor position: default true
  248. * @attribute offsetNode
  249. * @type Boolean
  250. */
  251. offsetNode: {
  252. value: true
  253. },
  254. /**
  255. * Center the dragNode to the mouse position on drag:start: default false
  256. * @attribute startCentered
  257. * @type Boolean
  258. */
  259. startCentered: {
  260. value: false
  261. },
  262. /**
  263. * The number of pixels to move to start a drag operation, default is 3.
  264. * @attribute clickPixelThresh
  265. * @type Number
  266. */
  267. clickPixelThresh: {
  268. value: DDM.get('clickPixelThresh')
  269. },
  270. /**
  271. * The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
  272. * @attribute clickTimeThresh
  273. * @type Number
  274. */
  275. clickTimeThresh: {
  276. value: DDM.get('clickTimeThresh')
  277. },
  278. /**
  279. * Set to lock this drag element so that it can't be dragged: default false.
  280. * @attribute lock
  281. * @type Boolean
  282. */
  283. lock: {
  284. value: false,
  285. setter: function(lock) {
  286. if (lock) {
  287. this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
  288. } else {
  289. this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
  290. }
  291. return lock;
  292. }
  293. },
  294. /**
  295. * A payload holder to store arbitrary data about this drag object, can be used to store any value.
  296. * @attribute data
  297. * @type Mixed
  298. */
  299. data: {
  300. value: false
  301. },
  302. /**
  303. * If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
  304. * @attribute move
  305. * @type Boolean
  306. */
  307. move: {
  308. value: true
  309. },
  310. /**
  311. * Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
  312. * @attribute useShim
  313. * @type Boolean
  314. */
  315. useShim: {
  316. value: true
  317. },
  318. /**
  319. * Config option is set by Drag to inform you of which handle fired the drag event (in the case that there are several handles): default false.
  320. * @attribute activeHandle
  321. * @type Node
  322. */
  323. activeHandle: {
  324. value: false
  325. },
  326. /**
  327. * By default a drag operation will only begin if the mousedown occurred with the primary mouse button.
  328. * Setting this to false will allow for all mousedown events to trigger a drag.
  329. * @attribute primaryButtonOnly
  330. * @type Boolean
  331. */
  332. primaryButtonOnly: {
  333. value: true
  334. },
  335. /**
  336. * This attribute is not meant to be used by the implementor, it is meant to be used as an Event tracker so you can listen for it to change.
  337. * @attribute dragging
  338. * @type Boolean
  339. */
  340. dragging: {
  341. value: false
  342. },
  343. parent: {
  344. value: false
  345. },
  346. /**
  347. * This attribute only works if the dd-drop module has been loaded. It will make this node a drop target as well as draggable.
  348. * @attribute target
  349. * @type Boolean
  350. */
  351. target: {
  352. value: false,
  353. setter: function(config) {
  354. this._handleTarget(config);
  355. return config;
  356. }
  357. },
  358. /**
  359. * This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
  360. * @attribute dragMode
  361. * @type String
  362. */
  363. dragMode: {
  364. value: null,
  365. setter: function(mode) {
  366. return DDM._setDragMode(mode);
  367. }
  368. },
  369. /**
  370. * Array of groups to add this drag into.
  371. * @attribute groups
  372. * @type Array
  373. */
  374. groups: {
  375. value: ['default'],
  376. getter: function() {
  377. if (!this._groups) {
  378. this._groups = {};
  379. return [];
  380. }
  381.  
  382. return Y.Object.keys(this._groups);
  383. },
  384. setter: function(g) {
  385. this._groups = Y.Array.hash(g);
  386. return g;
  387. }
  388. },
  389. /**
  390. * Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
  391. * @attribute handles
  392. * @type Array
  393. */
  394. handles: {
  395. value: null,
  396. setter: function(g) {
  397. if (g) {
  398. this._handles = {};
  399. Y.Array.each(g, function(v) {
  400. var key = v;
  401. if (v instanceof Y.Node || v instanceof Y.NodeList) {
  402. key = v._yuid;
  403. }
  404. this._handles[key] = v;
  405. }, this);
  406. } else {
  407. this._handles = null;
  408. }
  409. return g;
  410. }
  411. },
  412. /**
  413. * Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling. Use bubbleTargets in config
  414. * @deprecated
  415. * @attribute bubbles
  416. * @type Object
  417. */
  418. bubbles: {
  419. setter: function(t) {
  420. Y.log('bubbles is deprecated use bubbleTargets: HOST', 'warn', 'dd');
  421. this.addTarget(t);
  422. return t;
  423. }
  424. },
  425. /**
  426. * Should the mousedown event be halted. Default: true
  427. * @attribute haltDown
  428. * @type Boolean
  429. */
  430. haltDown: {
  431. value: true
  432. }
  433. };
  434.  
  435. Y.extend(Drag, Y.Base, {
  436. /**
  437. * Checks the object for the methods needed to drag the object around.
  438. * Normally this would be a node instance, but in the case of Graphics, it
  439. * may be an SVG node or something similar.
  440. * @method _canDrag
  441. * @private
  442. * @param {Object} n The object to check
  443. * @return {Boolean} True or false if the Object contains the methods needed to Drag
  444. */
  445. _canDrag: function(n) {
  446. if (n && n.setXY && n.getXY && n.test && n.contains) {
  447. return true;
  448. }
  449. return false;
  450. },
  451. /**
  452. * The default bubbleTarget for this object. Default: Y.DD.DDM
  453. * @private
  454. * @property _bubbleTargets
  455. */
  456. _bubbleTargets: Y.DD.DDM,
  457. /**
  458. * Add this Drag instance to a group, this should be used for on-the-fly group additions.
  459. * @method addToGroup
  460. * @param {String} g The group to add this Drag Instance to.
  461. * @return {Self}
  462. * @chainable
  463. */
  464. addToGroup: function(g) {
  465. this._groups[g] = true;
  466. DDM._activateTargets();
  467. return this;
  468. },
  469. /**
  470. * Remove this Drag instance from a group, this should be used for on-the-fly group removals.
  471. * @method removeFromGroup
  472. * @param {String} g The group to remove this Drag Instance from.
  473. * @return {Self}
  474. * @chainable
  475. */
  476. removeFromGroup: function(g) {
  477. delete this._groups[g];
  478. DDM._activateTargets();
  479. return this;
  480. },
  481. /**
  482. * This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
  483. * @property target
  484. * @type {Object}
  485. */
  486. target: null,
  487. /**
  488. * Attribute handler for the target config attribute.
  489. * @private
  490. * @method _handleTarget
  491. * @param {Boolean/Object} config The Config
  492. */
  493. _handleTarget: function(config) {
  494. if (Y.DD.Drop) {
  495. if (config === false) {
  496. if (this.target) {
  497. DDM._unregTarget(this.target);
  498. this.target = null;
  499. }
  500. } else {
  501. if (!Y.Lang.isObject(config)) {
  502. config = {};
  503. }
  504. config.bubbleTargets = config.bubbleTargets || this.getTargets();
  505. config.node = this.get(NODE);
  506. config.groups = config.groups || this.get('groups');
  507. this.target = new Y.DD.Drop(config);
  508. }
  509. }
  510. },
  511. /**
  512. * Storage Array for the groups this drag belongs to.
  513. * @private
  514. * @property _groups
  515. * @type {Array}
  516. */
  517. _groups: null,
  518. /**
  519. * This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
  520. * @private
  521. * @method _createEvents
  522. */
  523. _createEvents: function() {
  524.  
  525. this.publish(EV_MOUSE_DOWN, {
  526. defaultFn: this._defMouseDownFn,
  527. queuable: false,
  528. emitFacade: true,
  529. bubbles: true,
  530. prefix: 'drag'
  531. });
  532.  
  533. this.publish(EV_ALIGN, {
  534. defaultFn: this._defAlignFn,
  535. queuable: false,
  536. emitFacade: true,
  537. bubbles: true,
  538. prefix: 'drag'
  539. });
  540.  
  541. this.publish(EV_DRAG, {
  542. defaultFn: this._defDragFn,
  543. queuable: false,
  544. emitFacade: true,
  545. bubbles: true,
  546. prefix: 'drag'
  547. });
  548.  
  549. this.publish(EV_END, {
  550. defaultFn: this._defEndFn,
  551. preventedFn: this._prevEndFn,
  552. queuable: false,
  553. emitFacade: true,
  554. bubbles: true,
  555. prefix: 'drag'
  556. });
  557.  
  558. var ev = [
  559. EV_AFTER_MOUSE_DOWN,
  560. EV_REMOVE_HANDLE,
  561. EV_ADD_HANDLE,
  562. EV_REMOVE_INVALID,
  563. EV_ADD_INVALID,
  564. EV_START,
  565. 'drag:drophit',
  566. 'drag:dropmiss',
  567. 'drag:over',
  568. 'drag:enter',
  569. 'drag:exit'
  570. ];
  571.  
  572. Y.Array.each(ev, function(v) {
  573. this.publish(v, {
  574. type: v,
  575. emitFacade: true,
  576. bubbles: true,
  577. preventable: false,
  578. queuable: false,
  579. prefix: 'drag'
  580. });
  581. }, this);
  582. },
  583. /**
  584. * A private reference to the mousedown DOM event
  585. * @private
  586. * @property _ev_md
  587. * @type {EventFacade}
  588. */
  589. _ev_md: null,
  590. /**
  591. * The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
  592. * @private
  593. * @property _startTime
  594. * @type Date
  595. */
  596. _startTime: null,
  597. /**
  598. * The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
  599. * @private
  600. * @property _endTime
  601. * @type Date
  602. */
  603. _endTime: null,
  604. /**
  605. * A private hash of the valid drag handles
  606. * @private
  607. * @property _handles
  608. * @type {Object}
  609. */
  610. _handles: null,
  611. /**
  612. * A private hash of the invalid selector strings
  613. * @private
  614. * @property _invalids
  615. * @type {Object}
  616. */
  617. _invalids: null,
  618. /**
  619. * A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
  620. * @private
  621. * @property _invalidsDefault
  622. * @type {Object}
  623. */
  624. _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true },
  625. /**
  626. * Private flag to see if the drag threshhold was met
  627. * @private
  628. * @property _dragThreshMet
  629. * @type {Boolean}
  630. */
  631. _dragThreshMet: null,
  632. /**
  633. * Flag to determine if the drag operation came from a timeout
  634. * @private
  635. * @property _fromTimeout
  636. * @type {Boolean}
  637. */
  638. _fromTimeout: null,
  639. /**
  640. * Holder for the setTimeout call
  641. * @private
  642. * @property _clickTimeout
  643. * @type {Boolean}
  644. */
  645. _clickTimeout: null,
  646. /**
  647. * The offset of the mouse position to the element's position
  648. * @property deltaXY
  649. * @type {Array}
  650. */
  651. deltaXY: null,
  652. /**
  653. * The initial mouse position
  654. * @property startXY
  655. * @type {Array}
  656. */
  657. startXY: null,
  658. /**
  659. * The initial element position
  660. * @property nodeXY
  661. * @type {Array}
  662. */
  663. nodeXY: null,
  664. /**
  665. * The position of the element as it's moving (for offset calculations)
  666. * @property lastXY
  667. * @type {Array}
  668. */
  669. lastXY: null,
  670. /**
  671. * The xy that the node will be set to. Changing this will alter the position as it's dragged.
  672. * @property actXY
  673. * @type {Array}
  674. */
  675. actXY: null,
  676. /**
  677. * The real xy position of the node.
  678. * @property realXY
  679. * @type {Array}
  680. */
  681. realXY: null,
  682. /**
  683. * The XY coords of the mousemove
  684. * @property mouseXY
  685. * @type {Array}
  686. */
  687. mouseXY: null,
  688. /**
  689. * A region object associated with this drag, used for checking regions while dragging.
  690. * @property region
  691. * @type Object
  692. */
  693. region: null,
  694. /**
  695. * Handler for the mouseup DOM event
  696. * @private
  697. * @method _handleMouseUp
  698. * @param {EventFacade} ev The Event
  699. */
  700. _handleMouseUp: function() {
  701. this.fire('drag:mouseup');
  702. this._fixIEMouseUp();
  703. if (DDM.activeDrag) {
  704. DDM._end();
  705. }
  706. },
  707. /**
  708. * The function we use as the ondragstart handler when we start a drag
  709. * in Internet Explorer. This keeps IE from blowing up on images as drag handles.
  710. * @private
  711. * @method _fixDragStart
  712. * @param {Event} e The Event
  713. */
  714. _fixDragStart: function(e) {
  715. if (this.validClick(e)) {
  716. e.preventDefault();
  717. }
  718. },
  719. /**
  720. * The function we use as the onselectstart handler when we start a drag in Internet Explorer
  721. * @private
  722. * @method _ieSelectFix
  723. */
  724. _ieSelectFix: function() {
  725. return false;
  726. },
  727. /**
  728. * We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
  729. * @private
  730. * @property _ieSelectBack
  731. */
  732. _ieSelectBack: null,
  733. /**
  734. * This method copies the onselectstart listner on the document to the _ieSelectFix property
  735. * @private
  736. * @method _fixIEMouseDown
  737. */
  738. _fixIEMouseDown: function() {
  739. if (Y.UA.ie) {
  740. this._ieSelectBack = Y.config.doc.body.onselectstart;
  741. Y.config.doc.body.onselectstart = this._ieSelectFix;
  742. }
  743. },
  744. /**
  745. * This method copies the _ieSelectFix property back to the onselectstart listner on the document.
  746. * @private
  747. * @method _fixIEMouseUp
  748. */
  749. _fixIEMouseUp: function() {
  750. if (Y.UA.ie) {
  751. Y.config.doc.body.onselectstart = this._ieSelectBack;
  752. }
  753. },
  754. /**
  755. * Handler for the mousedown DOM event
  756. * @private
  757. * @method _handleMouseDownEvent
  758. * @param {EventFacade} ev The Event
  759. */
  760. _handleMouseDownEvent: function(ev) {
  761. this.fire(EV_MOUSE_DOWN, { ev: ev });
  762. },
  763. /**
  764. * Handler for the mousedown DOM event
  765. * @private
  766. * @method _defMouseDownFn
  767. * @param {EventFacade} e The Event
  768. */
  769. _defMouseDownFn: function(e) {
  770. var ev = e.ev;
  771.  
  772. this._dragThreshMet = false;
  773. this._ev_md = ev;
  774.  
  775. if (this.get('primaryButtonOnly') && ev.button > 1) {
  776. return false;
  777. }
  778. if (this.validClick(ev)) {
  779. this._fixIEMouseDown(ev);
  780. if (Drag.START_EVENT.indexOf('gesture') !== 0) {
  781. //Only do these if it's not a gesture
  782. if (this.get('haltDown')) {
  783. Y.log('Halting MouseDown', 'info', 'drag');
  784. ev.halt();
  785. } else {
  786. Y.log('Preventing Default on MouseDown', 'info', 'drag');
  787. ev.preventDefault();
  788. }
  789. }
  790.  
  791. this._setStartPosition([ev.pageX, ev.pageY]);
  792.  
  793. DDM.activeDrag = this;
  794.  
  795. this._clickTimeout = Y.later(this.get('clickTimeThresh'), this, this._timeoutCheck);
  796. }
  797. this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
  798. },
  799. /**
  800. * Method first checks to see if we have handles, if so it validates the click
  801. * against the handle. Then if it finds a valid handle, it checks it against
  802. * the invalid handles list. Returns true if a good handle was used, false otherwise.
  803. * @method validClick
  804. * @param {EventFacade} ev The Event
  805. * @return {Boolean}
  806. */
  807. validClick: function(ev) {
  808. var r = false, n = false,
  809. tar = ev.target,
  810. hTest = null,
  811. els = null,
  812. nlist = null,
  813. set = false;
  814. if (this._handles) {
  815. Y.Object.each(this._handles, function(i, n) {
  816. if (i instanceof Y.Node || i instanceof Y.NodeList) {
  817. if (!r) {
  818. nlist = i;
  819. if (nlist instanceof Y.Node) {
  820. nlist = new Y.NodeList(i._node);
  821. }
  822. nlist.each(function(nl) {
  823. if (nl.contains(tar)) {
  824. r = true;
  825. }
  826. });
  827. }
  828. } else if (Y.Lang.isString(n)) {
  829. //Am I this or am I inside this
  830. if (tar.test(n + ', ' + n + ' *') && !hTest) {
  831. hTest = n;
  832. r = true;
  833. }
  834. }
  835. });
  836. } else {
  837. n = this.get(NODE);
  838. if (n.contains(tar) || n.compareTo(tar)) {
  839. r = true;
  840. }
  841. }
  842. if (r) {
  843. if (this._invalids) {
  844. Y.Object.each(this._invalids, function(i, n) {
  845. if (Y.Lang.isString(n)) {
  846. //Am I this or am I inside this
  847. if (tar.test(n + ', ' + n + ' *')) {
  848. r = false;
  849. }
  850. }
  851. });
  852. }
  853. }
  854. if (r) {
  855. if (hTest) {
  856. els = ev.currentTarget.all(hTest);
  857. set = false;
  858. els.each(function(n) {
  859. if ((n.contains(tar) || n.compareTo(tar)) && !set) {
  860. set = true;
  861. this.set('activeHandle', n);
  862. }
  863. }, this);
  864. } else {
  865. this.set('activeHandle', this.get(NODE));
  866. }
  867. }
  868. return r;
  869. },
  870. /**
  871. * Sets the current position of the Element and calculates the offset
  872. * @private
  873. * @method _setStartPosition
  874. * @param {Array} xy The XY coords to set the position to.
  875. */
  876. _setStartPosition: function(xy) {
  877. this.startXY = xy;
  878.  
  879. this.nodeXY = this.lastXY = this.realXY = this.get(NODE).getXY();
  880.  
  881. if (this.get('offsetNode')) {
  882. this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
  883. } else {
  884. this.deltaXY = [0, 0];
  885. }
  886. },
  887. /**
  888. * The method passed to setTimeout to determine if the clickTimeThreshold was met.
  889. * @private
  890. * @method _timeoutCheck
  891. */
  892. _timeoutCheck: function() {
  893. if (!this.get('lock') && !this._dragThreshMet && this._ev_md) {
  894. this._fromTimeout = this._dragThreshMet = true;
  895. this.start();
  896. this._alignNode([this._ev_md.pageX, this._ev_md.pageY], true);
  897. }
  898. },
  899. /**
  900. * Remove a Selector added by addHandle
  901. * @method removeHandle
  902. * @param {String} str The selector for the handle to be removed.
  903. * @return {Self}
  904. * @chainable
  905. */
  906. removeHandle: function(str) {
  907. var key = str;
  908. if (str instanceof Y.Node || str instanceof Y.NodeList) {
  909. key = str._yuid;
  910. }
  911. if (this._handles[key]) {
  912. delete this._handles[key];
  913. this.fire(EV_REMOVE_HANDLE, { handle: str });
  914. }
  915. return this;
  916. },
  917. /**
  918. * Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
  919. * @method addHandle
  920. * @param {String} str The selector to test for a valid handle. Must be a child of the element.
  921. * @return {Self}
  922. * @chainable
  923. */
  924. addHandle: function(str) {
  925. if (!this._handles) {
  926. this._handles = {};
  927. }
  928. var key = str;
  929. if (str instanceof Y.Node || str instanceof Y.NodeList) {
  930. key = str._yuid;
  931. }
  932. this._handles[key] = str;
  933. this.fire(EV_ADD_HANDLE, { handle: str });
  934. return this;
  935. },
  936. /**
  937. * Remove an invalid handle added by addInvalid
  938. * @method removeInvalid
  939. * @param {String} str The invalid handle to remove from the internal list.
  940. * @return {Self}
  941. * @chainable
  942. */
  943. removeInvalid: function(str) {
  944. if (this._invalids[str]) {
  945. this._invalids[str] = null;
  946. delete this._invalids[str];
  947. this.fire(EV_REMOVE_INVALID, { handle: str });
  948. }
  949. return this;
  950. },
  951. /**
  952. * Add a selector string to test the handle against. If the test passes the drag operation will not continue.
  953. * @method addInvalid
  954. * @param {String} str The selector to test against to determine if this is an invalid drag handle.
  955. * @return {Self}
  956. * @chainable
  957. */
  958. addInvalid: function(str) {
  959. if (Y.Lang.isString(str)) {
  960. this._invalids[str] = true;
  961. this.fire(EV_ADD_INVALID, { handle: str });
  962. }
  963. return this;
  964. },
  965. /**
  966. * Internal init handler
  967. * @private
  968. * @method initializer
  969. */
  970. initializer: function() {
  971.  
  972. this.get(NODE).dd = this;
  973.  
  974. if (!this.get(NODE).get('id')) {
  975. var id = Y.stamp(this.get(NODE));
  976. this.get(NODE).set('id', id);
  977. }
  978.  
  979. this.actXY = [];
  980.  
  981. this._invalids = Y.clone(this._invalidsDefault, true);
  982.  
  983. this._createEvents();
  984.  
  985. if (!this.get(DRAG_NODE)) {
  986. this.set(DRAG_NODE, this.get(NODE));
  987. }
  988.  
  989. //Fix for #2528096
  990. //Don't prep the DD instance until all plugins are loaded.
  991. this.on('initializedChange', Y.bind(this._prep, this));
  992.  
  993. //Shouldn't have to do this..
  994. this.set('groups', this.get('groups'));
  995. },
  996. /**
  997. * Attach event listners and add classname
  998. * @private
  999. * @method _prep
  1000. */
  1001. _prep: function() {
  1002. this._dragThreshMet = false;
  1003. var node = this.get(NODE);
  1004. node.addClass(DDM.CSS_PREFIX + '-draggable');
  1005. node.on(Drag.START_EVENT, Y.bind(this._handleMouseDownEvent, this));
  1006. node.on('mouseup', Y.bind(this._handleMouseUp, this));
  1007. node.on('dragstart', Y.bind(this._fixDragStart, this));
  1008. },
  1009. /**
  1010. * Detach event listeners and remove classname
  1011. * @private
  1012. * @method _unprep
  1013. */
  1014. _unprep: function() {
  1015. var node = this.get(NODE);
  1016. node.removeClass(DDM.CSS_PREFIX + '-draggable');
  1017. node.detachAll('mouseup');
  1018. node.detachAll('dragstart');
  1019. node.detachAll(Drag.START_EVENT);
  1020. this.mouseXY = [];
  1021. this.deltaXY = [0,0];
  1022. this.startXY = [];
  1023. this.nodeXY = [];
  1024. this.lastXY = [];
  1025. this.actXY = [];
  1026. this.realXY = [];
  1027. },
  1028. /**
  1029. * Starts the drag operation
  1030. * @method start
  1031. * @return {Self}
  1032. * @chainable
  1033. */
  1034. start: function() {
  1035. if (!this.get('lock') && !this.get(DRAGGING)) {
  1036. var node = this.get(NODE), ow, oh, xy;
  1037. this._startTime = (new Date()).getTime();
  1038.  
  1039. DDM._start();
  1040. node.addClass(DDM.CSS_PREFIX + '-dragging');
  1041. this.fire(EV_START, {
  1042. pageX: this.nodeXY[0],
  1043. pageY: this.nodeXY[1],
  1044. startTime: this._startTime
  1045. });
  1046. node = this.get(DRAG_NODE);
  1047. xy = this.nodeXY;
  1048.  
  1049. ow = node.get(OFFSET_WIDTH);
  1050. oh = node.get(OFFSET_HEIGHT);
  1051.  
  1052. if (this.get('startCentered')) {
  1053. this._setStartPosition([xy[0] + (ow / 2), xy[1] + (oh / 2)]);
  1054. }
  1055.  
  1056.  
  1057. this.region = {
  1058. '0': xy[0],
  1059. '1': xy[1],
  1060. area: 0,
  1061. top: xy[1],
  1062. right: xy[0] + ow,
  1063. bottom: xy[1] + oh,
  1064. left: xy[0]
  1065. };
  1066. this.set(DRAGGING, true);
  1067. }
  1068. return this;
  1069. },
  1070. /**
  1071. * Ends the drag operation
  1072. * @method end
  1073. * @return {Self}
  1074. * @chainable
  1075. */
  1076. end: function() {
  1077. this._endTime = (new Date()).getTime();
  1078. if (this._clickTimeout) {
  1079. this._clickTimeout.cancel();
  1080. }
  1081. this._dragThreshMet = this._fromTimeout = false;
  1082.  
  1083. if (!this.get('lock') && this.get(DRAGGING)) {
  1084. this.fire(EV_END, {
  1085. pageX: this.lastXY[0],
  1086. pageY: this.lastXY[1],
  1087. startTime: this._startTime,
  1088. endTime: this._endTime
  1089. });
  1090. }
  1091. this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
  1092. this.set(DRAGGING, false);
  1093. this.deltaXY = [0, 0];
  1094.  
  1095. return this;
  1096. },
  1097. /**
  1098. * Handler for fixing the selection in IE
  1099. * @private
  1100. * @method _defEndFn
  1101. */
  1102. _defEndFn: function() {
  1103. this._fixIEMouseUp();
  1104. this._ev_md = null;
  1105. },
  1106. /**
  1107. * Handler for preventing the drag:end event. It will reset the node back to it's start position
  1108. * @private
  1109. * @method _prevEndFn
  1110. */
  1111. _prevEndFn: function() {
  1112. this._fixIEMouseUp();
  1113. //Bug #1852577
  1114. this.get(DRAG_NODE).setXY(this.nodeXY);
  1115. this._ev_md = null;
  1116. this.region = null;
  1117. },
  1118. /**
  1119. * Calculates the offsets and set's the XY that the element will move to.
  1120. * @private
  1121. * @method _align
  1122. * @param {Array} xy The xy coords to align with.
  1123. */
  1124. _align: function(xy) {
  1125. this.fire(EV_ALIGN, {pageX: xy[0], pageY: xy[1] });
  1126. },
  1127. /**
  1128. * Calculates the offsets and set's the XY that the element will move to.
  1129. * @private
  1130. * @method _defAlignFn
  1131. * @param {EventFacade} e The drag:align event.
  1132. */
  1133. _defAlignFn: function(e) {
  1134. this.actXY = [e.pageX - this.deltaXY[0], e.pageY - this.deltaXY[1]];
  1135. },
  1136. /**
  1137. * This method performs the alignment before the element move.
  1138. * @private
  1139. * @method _alignNode
  1140. * @param {Array} eXY The XY to move the element to, usually comes from the mousemove DOM event.
  1141. */
  1142. _alignNode: function(eXY, scroll) {
  1143. this._align(eXY);
  1144. if (!scroll) {
  1145. this._moveNode();
  1146. }
  1147. },
  1148. /**
  1149. * This method performs the actual element move.
  1150. * @private
  1151. * @method _moveNode
  1152. */
  1153. _moveNode: function(scroll) {
  1154. //if (!this.get(DRAGGING)) {
  1155. // return;
  1156. //}
  1157. var diffXY = [], diffXY2 = [], startXY = this.nodeXY, xy = this.actXY;
  1158.  
  1159. diffXY[0] = (xy[0] - this.lastXY[0]);
  1160. diffXY[1] = (xy[1] - this.lastXY[1]);
  1161.  
  1162. diffXY2[0] = (xy[0] - this.nodeXY[0]);
  1163. diffXY2[1] = (xy[1] - this.nodeXY[1]);
  1164.  
  1165.  
  1166. this.region = {
  1167. '0': xy[0],
  1168. '1': xy[1],
  1169. area: 0,
  1170. top: xy[1],
  1171. right: xy[0] + this.get(DRAG_NODE).get(OFFSET_WIDTH),
  1172. bottom: xy[1] + this.get(DRAG_NODE).get(OFFSET_HEIGHT),
  1173. left: xy[0]
  1174. };
  1175.  
  1176. this.fire(EV_DRAG, {
  1177. pageX: xy[0],
  1178. pageY: xy[1],
  1179. scroll: scroll,
  1180. info: {
  1181. start: startXY,
  1182. xy: xy,
  1183. delta: diffXY,
  1184. offset: diffXY2
  1185. }
  1186. });
  1187.  
  1188. this.lastXY = xy;
  1189. },
  1190. /**
  1191. * Default function for drag:drag. Fired from _moveNode.
  1192. * @private
  1193. * @method _defDragFn
  1194. * @param {EventFacade} ev The drag:drag event
  1195. */
  1196. _defDragFn: function(e) {
  1197. if (this.get('move')) {
  1198. if (e.scroll && e.scroll.node) {
  1199. var domNode = e.scroll.node.getDOMNode();
  1200. //If it's the window
  1201. if (domNode === Y.config.win) {
  1202. domNode.scrollTo(e.scroll.left, e.scroll.top);
  1203. } else {
  1204. e.scroll.node.set('scrollTop', e.scroll.top);
  1205. e.scroll.node.set('scrollLeft', e.scroll.left);
  1206. }
  1207. }
  1208. this.get(DRAG_NODE).setXY([e.pageX, e.pageY]);
  1209. this.realXY = [e.pageX, e.pageY];
  1210. }
  1211. },
  1212. /**
  1213. * Fired from DragDropMgr (DDM) on mousemove.
  1214. * @private
  1215. * @method _move
  1216. * @param {EventFacade} ev The mousemove DOM event
  1217. */
  1218. _move: function(ev) {
  1219. if (this.get('lock')) {
  1220. return false;
  1221. }
  1222.  
  1223. this.mouseXY = [ev.pageX, ev.pageY];
  1224. if (!this._dragThreshMet) {
  1225. var diffX = Math.abs(this.startXY[0] - ev.pageX),
  1226. diffY = Math.abs(this.startXY[1] - ev.pageY);
  1227. if (diffX > this.get('clickPixelThresh') || diffY > this.get('clickPixelThresh')) {
  1228. this._dragThreshMet = true;
  1229. this.start();
  1230. //This only happens on gestures to stop the page from scrolling
  1231. if (ev && ev.preventDefault) {
  1232. ev.preventDefault();
  1233. }
  1234. this._alignNode([ev.pageX, ev.pageY]);
  1235. }
  1236. } else {
  1237. if (this._clickTimeout) {
  1238. this._clickTimeout.cancel();
  1239. }
  1240. this._alignNode([ev.pageX, ev.pageY]);
  1241. }
  1242. },
  1243. /**
  1244. * Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
  1245. * @method stopDrag
  1246. * @return {Self}
  1247. * @chainable
  1248. */
  1249. stopDrag: function() {
  1250. if (this.get(DRAGGING)) {
  1251. DDM._end();
  1252. }
  1253. return this;
  1254. },
  1255. /**
  1256. * Lifecycle destructor, unreg the drag from the DDM and remove listeners
  1257. * @private
  1258. * @method destructor
  1259. */
  1260. destructor: function() {
  1261. this._unprep();
  1262. if (this.target) {
  1263. this.target.destroy();
  1264. }
  1265. DDM._unregDrag(this);
  1266. }
  1267. });
  1268. Y.namespace('DD');
  1269. Y.DD.Drag = Drag;
  1270.  
  1271.  
  1272.