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.

502 lines
13 KiB

1 year ago
  1. /*!
  2. * jQuery UI Droppable 1.13.2
  3. * http://jqueryui.com
  4. *
  5. * Copyright jQuery Foundation and other contributors
  6. * Released under the MIT license.
  7. * http://jquery.org/license
  8. */
  9. //>>label: Droppable
  10. //>>group: Interactions
  11. //>>description: Enables drop targets for draggable elements.
  12. //>>docs: http://api.jqueryui.com/droppable/
  13. //>>demos: http://jqueryui.com/droppable/
  14. ( function( factory ) {
  15. "use strict";
  16. if ( typeof define === "function" && define.amd ) {
  17. // AMD. Register as an anonymous module.
  18. define( [
  19. "jquery",
  20. "./draggable",
  21. "./mouse",
  22. "./core"
  23. ], factory );
  24. } else {
  25. // Browser globals
  26. factory( jQuery );
  27. }
  28. } )( function( $ ) {
  29. "use strict";
  30. $.widget( "ui.droppable", {
  31. version: "1.13.2",
  32. widgetEventPrefix: "drop",
  33. options: {
  34. accept: "*",
  35. addClasses: true,
  36. greedy: false,
  37. scope: "default",
  38. tolerance: "intersect",
  39. // Callbacks
  40. activate: null,
  41. deactivate: null,
  42. drop: null,
  43. out: null,
  44. over: null
  45. },
  46. _create: function() {
  47. var proportions,
  48. o = this.options,
  49. accept = o.accept;
  50. this.isover = false;
  51. this.isout = true;
  52. this.accept = typeof accept === "function" ? accept : function( d ) {
  53. return d.is( accept );
  54. };
  55. this.proportions = function( /* valueToWrite */ ) {
  56. if ( arguments.length ) {
  57. // Store the droppable's proportions
  58. proportions = arguments[ 0 ];
  59. } else {
  60. // Retrieve or derive the droppable's proportions
  61. return proportions ?
  62. proportions :
  63. proportions = {
  64. width: this.element[ 0 ].offsetWidth,
  65. height: this.element[ 0 ].offsetHeight
  66. };
  67. }
  68. };
  69. this._addToManager( o.scope );
  70. if ( o.addClasses ) {
  71. this._addClass( "ui-droppable" );
  72. }
  73. },
  74. _addToManager: function( scope ) {
  75. // Add the reference and positions to the manager
  76. $.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || [];
  77. $.ui.ddmanager.droppables[ scope ].push( this );
  78. },
  79. _splice: function( drop ) {
  80. var i = 0;
  81. for ( ; i < drop.length; i++ ) {
  82. if ( drop[ i ] === this ) {
  83. drop.splice( i, 1 );
  84. }
  85. }
  86. },
  87. _destroy: function() {
  88. var drop = $.ui.ddmanager.droppables[ this.options.scope ];
  89. this._splice( drop );
  90. },
  91. _setOption: function( key, value ) {
  92. if ( key === "accept" ) {
  93. this.accept = typeof value === "function" ? value : function( d ) {
  94. return d.is( value );
  95. };
  96. } else if ( key === "scope" ) {
  97. var drop = $.ui.ddmanager.droppables[ this.options.scope ];
  98. this._splice( drop );
  99. this._addToManager( value );
  100. }
  101. this._super( key, value );
  102. },
  103. _activate: function( event ) {
  104. var draggable = $.ui.ddmanager.current;
  105. this._addActiveClass();
  106. if ( draggable ) {
  107. this._trigger( "activate", event, this.ui( draggable ) );
  108. }
  109. },
  110. _deactivate: function( event ) {
  111. var draggable = $.ui.ddmanager.current;
  112. this._removeActiveClass();
  113. if ( draggable ) {
  114. this._trigger( "deactivate", event, this.ui( draggable ) );
  115. }
  116. },
  117. _over: function( event ) {
  118. var draggable = $.ui.ddmanager.current;
  119. // Bail if draggable and droppable are same element
  120. if ( !draggable || ( draggable.currentItem ||
  121. draggable.element )[ 0 ] === this.element[ 0 ] ) {
  122. return;
  123. }
  124. if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
  125. draggable.element ) ) ) {
  126. this._addHoverClass();
  127. this._trigger( "over", event, this.ui( draggable ) );
  128. }
  129. },
  130. _out: function( event ) {
  131. var draggable = $.ui.ddmanager.current;
  132. // Bail if draggable and droppable are same element
  133. if ( !draggable || ( draggable.currentItem ||
  134. draggable.element )[ 0 ] === this.element[ 0 ] ) {
  135. return;
  136. }
  137. if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
  138. draggable.element ) ) ) {
  139. this._removeHoverClass();
  140. this._trigger( "out", event, this.ui( draggable ) );
  141. }
  142. },
  143. _drop: function( event, custom ) {
  144. var draggable = custom || $.ui.ddmanager.current,
  145. childrenIntersection = false;
  146. // Bail if draggable and droppable are same element
  147. if ( !draggable || ( draggable.currentItem ||
  148. draggable.element )[ 0 ] === this.element[ 0 ] ) {
  149. return false;
  150. }
  151. this.element
  152. .find( ":data(ui-droppable)" )
  153. .not( ".ui-draggable-dragging" )
  154. .each( function() {
  155. var inst = $( this ).droppable( "instance" );
  156. if (
  157. inst.options.greedy &&
  158. !inst.options.disabled &&
  159. inst.options.scope === draggable.options.scope &&
  160. inst.accept.call(
  161. inst.element[ 0 ], ( draggable.currentItem || draggable.element )
  162. ) &&
  163. $.ui.intersect(
  164. draggable,
  165. $.extend( inst, { offset: inst.element.offset() } ),
  166. inst.options.tolerance, event
  167. )
  168. ) {
  169. childrenIntersection = true;
  170. return false;
  171. }
  172. } );
  173. if ( childrenIntersection ) {
  174. return false;
  175. }
  176. if ( this.accept.call( this.element[ 0 ],
  177. ( draggable.currentItem || draggable.element ) ) ) {
  178. this._removeActiveClass();
  179. this._removeHoverClass();
  180. this._trigger( "drop", event, this.ui( draggable ) );
  181. return this.element;
  182. }
  183. return false;
  184. },
  185. ui: function( c ) {
  186. return {
  187. draggable: ( c.currentItem || c.element ),
  188. helper: c.helper,
  189. position: c.position,
  190. offset: c.positionAbs
  191. };
  192. },
  193. // Extension points just to make backcompat sane and avoid duplicating logic
  194. // TODO: Remove in 1.14 along with call to it below
  195. _addHoverClass: function() {
  196. this._addClass( "ui-droppable-hover" );
  197. },
  198. _removeHoverClass: function() {
  199. this._removeClass( "ui-droppable-hover" );
  200. },
  201. _addActiveClass: function() {
  202. this._addClass( "ui-droppable-active" );
  203. },
  204. _removeActiveClass: function() {
  205. this._removeClass( "ui-droppable-active" );
  206. }
  207. } );
  208. $.ui.intersect = ( function() {
  209. function isOverAxis( x, reference, size ) {
  210. return ( x >= reference ) && ( x < ( reference + size ) );
  211. }
  212. return function( draggable, droppable, toleranceMode, event ) {
  213. if ( !droppable.offset ) {
  214. return false;
  215. }
  216. var x1 = ( draggable.positionAbs ||
  217. draggable.position.absolute ).left + draggable.margins.left,
  218. y1 = ( draggable.positionAbs ||
  219. draggable.position.absolute ).top + draggable.margins.top,
  220. x2 = x1 + draggable.helperProportions.width,
  221. y2 = y1 + draggable.helperProportions.height,
  222. l = droppable.offset.left,
  223. t = droppable.offset.top,
  224. r = l + droppable.proportions().width,
  225. b = t + droppable.proportions().height;
  226. switch ( toleranceMode ) {
  227. case "fit":
  228. return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
  229. case "intersect":
  230. return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
  231. x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
  232. t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
  233. y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
  234. case "pointer":
  235. return isOverAxis( event.pageY, t, droppable.proportions().height ) &&
  236. isOverAxis( event.pageX, l, droppable.proportions().width );
  237. case "touch":
  238. return (
  239. ( y1 >= t && y1 <= b ) || // Top edge touching
  240. ( y2 >= t && y2 <= b ) || // Bottom edge touching
  241. ( y1 < t && y2 > b ) // Surrounded vertically
  242. ) && (
  243. ( x1 >= l && x1 <= r ) || // Left edge touching
  244. ( x2 >= l && x2 <= r ) || // Right edge touching
  245. ( x1 < l && x2 > r ) // Surrounded horizontally
  246. );
  247. default:
  248. return false;
  249. }
  250. };
  251. } )();
  252. /*
  253. This manager tracks offsets of draggables and droppables
  254. */
  255. $.ui.ddmanager = {
  256. current: null,
  257. droppables: { "default": [] },
  258. prepareOffsets: function( t, event ) {
  259. var i, j,
  260. m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
  261. type = event ? event.type : null, // workaround for #2317
  262. list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack();
  263. droppablesLoop: for ( i = 0; i < m.length; i++ ) {
  264. // No disabled and non-accepted
  265. if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ],
  266. ( t.currentItem || t.element ) ) ) ) {
  267. continue;
  268. }
  269. // Filter out elements in the current dragged item
  270. for ( j = 0; j < list.length; j++ ) {
  271. if ( list[ j ] === m[ i ].element[ 0 ] ) {
  272. m[ i ].proportions().height = 0;
  273. continue droppablesLoop;
  274. }
  275. }
  276. m[ i ].visible = m[ i ].element.css( "display" ) !== "none";
  277. if ( !m[ i ].visible ) {
  278. continue;
  279. }
  280. // Activate the droppable if used directly from draggables
  281. if ( type === "mousedown" ) {
  282. m[ i ]._activate.call( m[ i ], event );
  283. }
  284. m[ i ].offset = m[ i ].element.offset();
  285. m[ i ].proportions( {
  286. width: m[ i ].element[ 0 ].offsetWidth,
  287. height: m[ i ].element[ 0 ].offsetHeight
  288. } );
  289. }
  290. },
  291. drop: function( draggable, event ) {
  292. var dropped = false;
  293. // Create a copy of the droppables in case the list changes during the drop (#9116)
  294. $.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {
  295. if ( !this.options ) {
  296. return;
  297. }
  298. if ( !this.options.disabled && this.visible &&
  299. $.ui.intersect( draggable, this, this.options.tolerance, event ) ) {
  300. dropped = this._drop.call( this, event ) || dropped;
  301. }
  302. if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ],
  303. ( draggable.currentItem || draggable.element ) ) ) {
  304. this.isout = true;
  305. this.isover = false;
  306. this._deactivate.call( this, event );
  307. }
  308. } );
  309. return dropped;
  310. },
  311. dragStart: function( draggable, event ) {
  312. // Listen for scrolling so that if the dragging causes scrolling the position of the
  313. // droppables can be recalculated (see #5003)
  314. draggable.element.parentsUntil( "body" ).on( "scroll.droppable", function() {
  315. if ( !draggable.options.refreshPositions ) {
  316. $.ui.ddmanager.prepareOffsets( draggable, event );
  317. }
  318. } );
  319. },
  320. drag: function( draggable, event ) {
  321. // If you have a highly dynamic page, you might try this option. It renders positions
  322. // every time you move the mouse.
  323. if ( draggable.options.refreshPositions ) {
  324. $.ui.ddmanager.prepareOffsets( draggable, event );
  325. }
  326. // Run through all droppables and check their positions based on specific tolerance options
  327. $.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
  328. if ( this.options.disabled || this.greedyChild || !this.visible ) {
  329. return;
  330. }
  331. var parentInstance, scope, parent,
  332. intersects = $.ui.intersect( draggable, this, this.options.tolerance, event ),
  333. c = !intersects && this.isover ?
  334. "isout" :
  335. ( intersects && !this.isover ? "isover" : null );
  336. if ( !c ) {
  337. return;
  338. }
  339. if ( this.options.greedy ) {
  340. // find droppable parents with same scope
  341. scope = this.options.scope;
  342. parent = this.element.parents( ":data(ui-droppable)" ).filter( function() {
  343. return $( this ).droppable( "instance" ).options.scope === scope;
  344. } );
  345. if ( parent.length ) {
  346. parentInstance = $( parent[ 0 ] ).droppable( "instance" );
  347. parentInstance.greedyChild = ( c === "isover" );
  348. }
  349. }
  350. // We just moved into a greedy child
  351. if ( parentInstance && c === "isover" ) {
  352. parentInstance.isover = false;
  353. parentInstance.isout = true;
  354. parentInstance._out.call( parentInstance, event );
  355. }
  356. this[ c ] = true;
  357. this[ c === "isout" ? "isover" : "isout" ] = false;
  358. this[ c === "isover" ? "_over" : "_out" ].call( this, event );
  359. // We just moved out of a greedy child
  360. if ( parentInstance && c === "isout" ) {
  361. parentInstance.isout = false;
  362. parentInstance.isover = true;
  363. parentInstance._over.call( parentInstance, event );
  364. }
  365. } );
  366. },
  367. dragStop: function( draggable, event ) {
  368. draggable.element.parentsUntil( "body" ).off( "scroll.droppable" );
  369. // Call prepareOffsets one final time since IE does not fire return scroll events when
  370. // overflow was caused by drag (see #5003)
  371. if ( !draggable.options.refreshPositions ) {
  372. $.ui.ddmanager.prepareOffsets( draggable, event );
  373. }
  374. }
  375. };
  376. // DEPRECATED
  377. // TODO: switch return back to widget declaration at top of file when this is removed
  378. if ( $.uiBackCompat !== false ) {
  379. // Backcompat for activeClass and hoverClass options
  380. $.widget( "ui.droppable", $.ui.droppable, {
  381. options: {
  382. hoverClass: false,
  383. activeClass: false
  384. },
  385. _addActiveClass: function() {
  386. this._super();
  387. if ( this.options.activeClass ) {
  388. this.element.addClass( this.options.activeClass );
  389. }
  390. },
  391. _removeActiveClass: function() {
  392. this._super();
  393. if ( this.options.activeClass ) {
  394. this.element.removeClass( this.options.activeClass );
  395. }
  396. },
  397. _addHoverClass: function() {
  398. this._super();
  399. if ( this.options.hoverClass ) {
  400. this.element.addClass( this.options.hoverClass );
  401. }
  402. },
  403. _removeHoverClass: function() {
  404. this._super();
  405. if ( this.options.hoverClass ) {
  406. this.element.removeClass( this.options.hoverClass );
  407. }
  408. }
  409. } );
  410. }
  411. return $.ui.droppable;
  412. } );