Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dd-drag.js 43KB

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