API Docs for: 3.17.2
Show:

File: dd/js/drop.js

  1.  
  2. /**
  3. * Provides the ability to create a Drop Target.
  4. * @module dd
  5. * @submodule dd-drop
  6. */
  7. /**
  8. * Provides the ability to create a Drop Target.
  9. * @class Drop
  10. * @extends Base
  11. * @constructor
  12. * @namespace DD
  13. */
  14.  
  15. var NODE = 'node',
  16. DDM = Y.DD.DDM,
  17. OFFSET_HEIGHT = 'offsetHeight',
  18. OFFSET_WIDTH = 'offsetWidth',
  19. /**
  20. * Fires when a drag element is over this target.
  21. * @event drop:over
  22. * @param {EventFacade} event An Event Facade object with the following specific property added:
  23. * <dl>
  24. * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
  25. * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
  26. * </dl>
  27. * @bubbles DDM
  28. * @type {CustomEvent}
  29. */
  30. EV_DROP_OVER = 'drop:over',
  31. /**
  32. * Fires when a drag element enters this target.
  33. * @event drop:enter
  34. * @param {EventFacade} event An Event Facade object with the following specific property added:
  35. * <dl>
  36. * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
  37. * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
  38. * </dl>
  39. * @bubbles DDM
  40. * @type {CustomEvent}
  41. */
  42. EV_DROP_ENTER = 'drop:enter',
  43. /**
  44. * Fires when a drag element exits this target.
  45. * @event drop:exit
  46. * @param {EventFacade} event An Event Facade object
  47. * @bubbles DDM
  48. * @type {CustomEvent}
  49. */
  50. EV_DROP_EXIT = 'drop:exit',
  51.  
  52. /**
  53. * Fires when a draggable node is dropped on this Drop Target. (Fired from dd-ddm-drop)
  54. * @event drop:hit
  55. * @param {EventFacade} event An Event Facade object with the following specific property added:
  56. * <dl>
  57. * <dt>drop</dt><dd>The best guess on what was dropped on.</dd>
  58. * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
  59. * <dt>others</dt><dd>An array of all the other drop targets that was dropped on.</dd>
  60. * </dl>
  61. * @bubbles DDM
  62. * @type {CustomEvent}
  63. */
  64.  
  65.  
  66. Drop = function() {
  67. this._lazyAddAttrs = false;
  68. Drop.superclass.constructor.apply(this, arguments);
  69.  
  70.  
  71. //DD init speed up.
  72. Y.on('domready', Y.bind(function() {
  73. Y.later(100, this, this._createShim);
  74. }, this));
  75. DDM._regTarget(this);
  76.  
  77. /* TODO
  78. if (Dom.getStyle(this.el, 'position') == 'fixed') {
  79. Event.on(window, 'scroll', function() {
  80. this.activateShim();
  81. }, this, true);
  82. }
  83. */
  84. };
  85.  
  86. Drop.NAME = 'drop';
  87.  
  88. Drop.ATTRS = {
  89. /**
  90. * Y.Node instance to use as the element to make a Drop Target
  91. * @attribute node
  92. * @type Node
  93. */
  94. node: {
  95. setter: function(node) {
  96. var n = Y.one(node);
  97. if (!n) {
  98. Y.error('DD.Drop: Invalid Node Given: ' + node);
  99. }
  100. return n;
  101. }
  102. },
  103. /**
  104. * Array of groups to add this drop into.
  105. * @attribute groups
  106. * @type Array
  107. */
  108. groups: {
  109. value: ['default'],
  110. getter: function() {
  111. if (!this._groups) {
  112. this._groups = {};
  113. return [];
  114. }
  115.  
  116. return Y.Object.keys(this._groups);
  117. },
  118. setter: function(g) {
  119. this._groups = Y.Array.hash(g);
  120. return g;
  121. }
  122. },
  123. /**
  124. * CSS style padding to make the Drop Target bigger than the node.
  125. * @attribute padding
  126. * @type String
  127. */
  128. padding: {
  129. value: '0',
  130. setter: function(p) {
  131. return DDM.cssSizestoObject(p);
  132. }
  133. },
  134. /**
  135. * Set to lock this drop element.
  136. * @attribute lock
  137. * @type Boolean
  138. */
  139. lock: {
  140. value: false,
  141. setter: function(lock) {
  142. if (lock) {
  143. this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-locked');
  144. } else {
  145. this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-locked');
  146. }
  147. return lock;
  148. }
  149. },
  150. /**
  151. * Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling.
  152. * Use bubbleTargets in config.
  153. * @deprecated
  154. * @attribute bubbles
  155. * @type Object
  156. */
  157. bubbles: {
  158. setter: function(t) {
  159. Y.log('bubbles is deprecated use bubbleTargets: HOST', 'warn', 'dd');
  160. this.addTarget(t);
  161. return t;
  162. }
  163. },
  164. /**
  165. * Use the Drop shim. Default: true
  166. * @deprecated
  167. * @attribute useShim
  168. * @type Boolean
  169. */
  170. useShim: {
  171. value: true,
  172. setter: function(v) {
  173. Y.DD.DDM._noShim = !v;
  174. return v;
  175. }
  176. }
  177. };
  178.  
  179. Y.extend(Drop, Y.Base, {
  180. /**
  181. * The default bubbleTarget for this object. Default: Y.DD.DDM
  182. * @private
  183. * @property _bubbleTargets
  184. */
  185. _bubbleTargets: Y.DD.DDM,
  186. /**
  187. * Add this Drop instance to a group, this should be used for on-the-fly group additions.
  188. * @method addToGroup
  189. * @param {String} g The group to add this Drop Instance to.
  190. * @chainable
  191. */
  192. addToGroup: function(g) {
  193. this._groups[g] = true;
  194. return this;
  195. },
  196. /**
  197. * Remove this Drop instance from a group, this should be used for on-the-fly group removals.
  198. * @method removeFromGroup
  199. * @param {String} g The group to remove this Drop Instance from.
  200. * @chainable
  201. */
  202. removeFromGroup: function(g) {
  203. delete this._groups[g];
  204. return this;
  205. },
  206. /**
  207. * This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
  208. * @private
  209. * @method _createEvents
  210. */
  211. _createEvents: function() {
  212.  
  213. var ev = [
  214. EV_DROP_OVER,
  215. EV_DROP_ENTER,
  216. EV_DROP_EXIT,
  217. 'drop:hit'
  218. ];
  219.  
  220. Y.Array.each(ev, function(v) {
  221. this.publish(v, {
  222. type: v,
  223. emitFacade: true,
  224. preventable: false,
  225. bubbles: true,
  226. queuable: false,
  227. prefix: 'drop'
  228. });
  229. }, this);
  230. },
  231. /**
  232. * Flag for determining if the target is valid in this operation.
  233. * @private
  234. * @property _valid
  235. * @type Boolean
  236. */
  237. _valid: null,
  238. /**
  239. * The groups this target belongs to.
  240. * @private
  241. * @property _groups
  242. * @type Array
  243. */
  244. _groups: null,
  245. /**
  246. * Node reference to the targets shim
  247. * @property shim
  248. * @type {Object}
  249. */
  250. shim: null,
  251. /**
  252. * A region object associated with this target, used for checking regions while dragging.
  253. * @property region
  254. * @type Object
  255. */
  256. region: null,
  257. /**
  258. * This flag is tripped when a drag element is over this target.
  259. * @property overTarget
  260. * @type Boolean
  261. */
  262. overTarget: null,
  263. /**
  264. * Check if this target is in one of the supplied groups.
  265. * @method inGroup
  266. * @param {Array} groups The groups to check against
  267. * @return Boolean
  268. */
  269. inGroup: function(groups) {
  270. this._valid = false;
  271. var ret = false;
  272. Y.Array.each(groups, function(v) {
  273. if (this._groups[v]) {
  274. ret = true;
  275. this._valid = true;
  276. }
  277. }, this);
  278. return ret;
  279. },
  280. /**
  281. * Private lifecycle method
  282. * @private
  283. * @method initializer
  284. */
  285. initializer: function() {
  286. Y.later(100, this, this._createEvents);
  287.  
  288. var node = this.get(NODE), id;
  289. if (!node.get('id')) {
  290. id = Y.stamp(node);
  291. node.set('id', id);
  292. }
  293. node.addClass(DDM.CSS_PREFIX + '-drop');
  294. //Shouldn't have to do this..
  295. this.set('groups', this.get('groups'));
  296. },
  297. /**
  298. * Lifecycle destructor, unreg the drag from the DDM and remove listeners
  299. * @private
  300. * @method destructor
  301. */
  302. destructor: function() {
  303. DDM._unregTarget(this);
  304. if (this.shim && (this.shim !== this.get(NODE))) {
  305. this.shim.detachAll();
  306. this.shim.remove();
  307. this.shim = null;
  308. }
  309. this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop');
  310. this.detachAll();
  311. },
  312. /**
  313. * Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
  314. * @private
  315. * @method _deactivateShim
  316. */
  317. _deactivateShim: function() {
  318. if (!this.shim) {
  319. return false;
  320. }
  321. this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
  322. this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
  323. this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
  324.  
  325. if (this.get('useShim')) {
  326. this.shim.setStyles({
  327. top: '-999px',
  328. left: '-999px',
  329. zIndex: '1'
  330. });
  331. }
  332. this.overTarget = false;
  333. },
  334. /**
  335. * Activates the shim and adds some interaction CSS classes
  336. * @private
  337. * @method _activateShim
  338. */
  339. _activateShim: function() {
  340. if (!DDM.activeDrag) {
  341. return false; //Nothing is dragging, no reason to activate.
  342. }
  343. if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
  344. return false;
  345. }
  346. if (this.get('lock')) {
  347. return false;
  348. }
  349. var node = this.get(NODE);
  350. //TODO Visibility Check..
  351. //if (this.inGroup(DDM.activeDrag.get('groups')) && this.get(NODE).isVisible()) {
  352. if (this.inGroup(DDM.activeDrag.get('groups'))) {
  353. node.removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
  354. node.addClass(DDM.CSS_PREFIX + '-drop-active-valid');
  355. DDM._addValid(this);
  356. this.overTarget = false;
  357. if (!this.get('useShim')) {
  358. this.shim = this.get(NODE);
  359. }
  360. this.sizeShim();
  361. } else {
  362. DDM._removeValid(this);
  363. node.removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
  364. node.addClass(DDM.CSS_PREFIX + '-drop-active-invalid');
  365. }
  366. },
  367. /**
  368. * Positions and sizes the shim with the raw data from the node,
  369. * this can be used to programatically adjust the Targets shim for Animation..
  370. * @method sizeShim
  371. */
  372. sizeShim: function() {
  373. if (!DDM.activeDrag) {
  374. return false; //Nothing is dragging, no reason to activate.
  375. }
  376. if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
  377. return false;
  378. }
  379. //if (this.get('lock') || !this.get('useShim')) {
  380. if (this.get('lock')) {
  381. return false;
  382. }
  383. if (!this.shim) {
  384. Y.later(100, this, this.sizeShim);
  385. return false;
  386. }
  387. var node = this.get(NODE),
  388. nh = node.get(OFFSET_HEIGHT),
  389. nw = node.get(OFFSET_WIDTH),
  390. xy = node.getXY(),
  391. p = this.get('padding'),
  392. dd, dH, dW;
  393.  
  394.  
  395. //Apply padding
  396. nw = nw + p.left + p.right;
  397. nh = nh + p.top + p.bottom;
  398. xy[0] = xy[0] - p.left;
  399. xy[1] = xy[1] - p.top;
  400.  
  401.  
  402. if (DDM.activeDrag.get('dragMode') === DDM.INTERSECT) {
  403. //Intersect Mode, make the shim bigger
  404. dd = DDM.activeDrag;
  405. dH = dd.get(NODE).get(OFFSET_HEIGHT);
  406. dW = dd.get(NODE).get(OFFSET_WIDTH);
  407.  
  408. nh = (nh + dH);
  409. nw = (nw + dW);
  410. xy[0] = xy[0] - (dW - dd.deltaXY[0]);
  411. xy[1] = xy[1] - (dH - dd.deltaXY[1]);
  412.  
  413. }
  414.  
  415. if (this.get('useShim')) {
  416. //Set the style on the shim
  417. this.shim.setStyles({
  418. height: nh + 'px',
  419. width: nw + 'px',
  420. top: xy[1] + 'px',
  421. left: xy[0] + 'px'
  422. });
  423. }
  424.  
  425. //Create the region to be used by intersect when a drag node is over us.
  426. this.region = {
  427. '0': xy[0],
  428. '1': xy[1],
  429. area: 0,
  430. top: xy[1],
  431. right: xy[0] + nw,
  432. bottom: xy[1] + nh,
  433. left: xy[0]
  434. };
  435. },
  436. /**
  437. * Creates the Target shim and adds it to the DDM's playground..
  438. * @private
  439. * @method _createShim
  440. */
  441. _createShim: function() {
  442. //No playground, defer
  443. if (!DDM._pg) {
  444. Y.later(10, this, this._createShim);
  445. return;
  446. }
  447. //Shim already here, cancel
  448. if (this.shim) {
  449. return;
  450. }
  451. var s = this.get('node');
  452.  
  453. if (this.get('useShim')) {
  454. s = Y.Node.create('<div id="' + this.get(NODE).get('id') + '_shim"></div>');
  455. s.setStyles({
  456. height: this.get(NODE).get(OFFSET_HEIGHT) + 'px',
  457. width: this.get(NODE).get(OFFSET_WIDTH) + 'px',
  458. backgroundColor: 'yellow',
  459. opacity: '.5',
  460. zIndex: '1',
  461. overflow: 'hidden',
  462. top: '-900px',
  463. left: '-900px',
  464. position: 'absolute'
  465. });
  466.  
  467. DDM._pg.appendChild(s);
  468.  
  469. s.on('mouseover', Y.bind(this._handleOverEvent, this));
  470. s.on('mouseout', Y.bind(this._handleOutEvent, this));
  471. }
  472.  
  473.  
  474. this.shim = s;
  475. },
  476. /**
  477. * This handles the over target call made from this object or from the DDM
  478. * @private
  479. * @method _handleOverTarget
  480. */
  481. _handleTargetOver: function() {
  482. if (DDM.isOverTarget(this)) {
  483. this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-over');
  484. DDM.activeDrop = this;
  485. DDM.otherDrops[this] = this;
  486. if (this.overTarget) {
  487. DDM.activeDrag.fire('drag:over', { drop: this, drag: DDM.activeDrag });
  488. this.fire(EV_DROP_OVER, { drop: this, drag: DDM.activeDrag });
  489. } else {
  490. //Prevent an enter before a start..
  491. if (DDM.activeDrag.get('dragging')) {
  492. this.overTarget = true;
  493. this.fire(EV_DROP_ENTER, { drop: this, drag: DDM.activeDrag });
  494. DDM.activeDrag.fire('drag:enter', { drop: this, drag: DDM.activeDrag });
  495. DDM.activeDrag.get(NODE).addClass(DDM.CSS_PREFIX + '-drag-over');
  496. //TODO - Is this needed??
  497. //DDM._handleTargetOver();
  498. }
  499. }
  500. } else {
  501. this._handleOut();
  502. }
  503. },
  504. /**
  505. * Handles the mouseover DOM event on the Target Shim
  506. * @private
  507. * @method _handleOverEvent
  508. */
  509. _handleOverEvent: function() {
  510. this.shim.setStyle('zIndex', '999');
  511. DDM._addActiveShim(this);
  512. },
  513. /**
  514. * Handles the mouseout DOM event on the Target Shim
  515. * @private
  516. * @method _handleOutEvent
  517. */
  518. _handleOutEvent: function() {
  519. this.shim.setStyle('zIndex', '1');
  520. DDM._removeActiveShim(this);
  521. },
  522. /**
  523. * Handles out of target calls/checks
  524. * @private
  525. * @method _handleOut
  526. */
  527. _handleOut: function(force) {
  528. if (!DDM.isOverTarget(this) || force) {
  529. if (this.overTarget) {
  530. this.overTarget = false;
  531. if (!force) {
  532. DDM._removeActiveShim(this);
  533. }
  534. if (DDM.activeDrag) {
  535. this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
  536. DDM.activeDrag.get(NODE).removeClass(DDM.CSS_PREFIX + '-drag-over');
  537. this.fire(EV_DROP_EXIT, { drop: this, drag: DDM.activeDrag });
  538. DDM.activeDrag.fire('drag:exit', { drop: this, drag: DDM.activeDrag });
  539. delete DDM.otherDrops[this];
  540. }
  541. }
  542. }
  543. }
  544. });
  545.  
  546. Y.DD.Drop = Drop;
  547.  
  548.  
  549.