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.

1253 lines
35 KiB

1 year ago
  1. /*!
  2. * jQuery UI Draggable 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: Draggable
  10. //>>group: Interactions
  11. //>>description: Enables dragging functionality for any element.
  12. //>>docs: http://api.jqueryui.com/draggable/
  13. //>>demos: http://jqueryui.com/draggable/
  14. //>>css.structure: ../../themes/base/draggable.css
  15. ( function( factory ) {
  16. "use strict";
  17. if ( typeof define === "function" && define.amd ) {
  18. // AMD. Register as an anonymous module.
  19. define( [
  20. "jquery",
  21. "./mouse",
  22. "./core"
  23. ], factory );
  24. } else {
  25. // Browser globals
  26. factory( jQuery );
  27. }
  28. } )( function( $ ) {
  29. "use strict";
  30. $.widget( "ui.draggable", $.ui.mouse, {
  31. version: "1.13.2",
  32. widgetEventPrefix: "drag",
  33. options: {
  34. addClasses: true,
  35. appendTo: "parent",
  36. axis: false,
  37. connectToSortable: false,
  38. containment: false,
  39. cursor: "auto",
  40. cursorAt: false,
  41. grid: false,
  42. handle: false,
  43. helper: "original",
  44. iframeFix: false,
  45. opacity: false,
  46. refreshPositions: false,
  47. revert: false,
  48. revertDuration: 500,
  49. scope: "default",
  50. scroll: true,
  51. scrollSensitivity: 20,
  52. scrollSpeed: 20,
  53. snap: false,
  54. snapMode: "both",
  55. snapTolerance: 20,
  56. stack: false,
  57. zIndex: false,
  58. // Callbacks
  59. drag: null,
  60. start: null,
  61. stop: null
  62. },
  63. _create: function() {
  64. if ( this.options.helper === "original" ) {
  65. this._setPositionRelative();
  66. }
  67. if ( this.options.addClasses ) {
  68. this._addClass( "ui-draggable" );
  69. }
  70. this._setHandleClassName();
  71. this._mouseInit();
  72. },
  73. _setOption: function( key, value ) {
  74. this._super( key, value );
  75. if ( key === "handle" ) {
  76. this._removeHandleClassName();
  77. this._setHandleClassName();
  78. }
  79. },
  80. _destroy: function() {
  81. if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
  82. this.destroyOnClear = true;
  83. return;
  84. }
  85. this._removeHandleClassName();
  86. this._mouseDestroy();
  87. },
  88. _mouseCapture: function( event ) {
  89. var o = this.options;
  90. // Among others, prevent a drag on a resizable-handle
  91. if ( this.helper || o.disabled ||
  92. $( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) {
  93. return false;
  94. }
  95. //Quit if we're not on a valid handle
  96. this.handle = this._getHandle( event );
  97. if ( !this.handle ) {
  98. return false;
  99. }
  100. this._blurActiveElement( event );
  101. this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );
  102. return true;
  103. },
  104. _blockFrames: function( selector ) {
  105. this.iframeBlocks = this.document.find( selector ).map( function() {
  106. var iframe = $( this );
  107. return $( "<div>" )
  108. .css( "position", "absolute" )
  109. .appendTo( iframe.parent() )
  110. .outerWidth( iframe.outerWidth() )
  111. .outerHeight( iframe.outerHeight() )
  112. .offset( iframe.offset() )[ 0 ];
  113. } );
  114. },
  115. _unblockFrames: function() {
  116. if ( this.iframeBlocks ) {
  117. this.iframeBlocks.remove();
  118. delete this.iframeBlocks;
  119. }
  120. },
  121. _blurActiveElement: function( event ) {
  122. var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
  123. target = $( event.target );
  124. // Don't blur if the event occurred on an element that is within
  125. // the currently focused element
  126. // See #10527, #12472
  127. if ( target.closest( activeElement ).length ) {
  128. return;
  129. }
  130. // Blur any element that currently has focus, see #4261
  131. $.ui.safeBlur( activeElement );
  132. },
  133. _mouseStart: function( event ) {
  134. var o = this.options;
  135. //Create and append the visible helper
  136. this.helper = this._createHelper( event );
  137. this._addClass( this.helper, "ui-draggable-dragging" );
  138. //Cache the helper size
  139. this._cacheHelperProportions();
  140. //If ddmanager is used for droppables, set the global draggable
  141. if ( $.ui.ddmanager ) {
  142. $.ui.ddmanager.current = this;
  143. }
  144. /*
  145. * - Position generation -
  146. * This block generates everything position related - it's the core of draggables.
  147. */
  148. //Cache the margins of the original element
  149. this._cacheMargins();
  150. //Store the helper's css position
  151. this.cssPosition = this.helper.css( "position" );
  152. this.scrollParent = this.helper.scrollParent( true );
  153. this.offsetParent = this.helper.offsetParent();
  154. this.hasFixedAncestor = this.helper.parents().filter( function() {
  155. return $( this ).css( "position" ) === "fixed";
  156. } ).length > 0;
  157. //The element's absolute position on the page minus margins
  158. this.positionAbs = this.element.offset();
  159. this._refreshOffsets( event );
  160. //Generate the original position
  161. this.originalPosition = this.position = this._generatePosition( event, false );
  162. this.originalPageX = event.pageX;
  163. this.originalPageY = event.pageY;
  164. //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
  165. if ( o.cursorAt ) {
  166. this._adjustOffsetFromHelper( o.cursorAt );
  167. }
  168. //Set a containment if given in the options
  169. this._setContainment();
  170. //Trigger event + callbacks
  171. if ( this._trigger( "start", event ) === false ) {
  172. this._clear();
  173. return false;
  174. }
  175. //Recache the helper size
  176. this._cacheHelperProportions();
  177. //Prepare the droppable offsets
  178. if ( $.ui.ddmanager && !o.dropBehaviour ) {
  179. $.ui.ddmanager.prepareOffsets( this, event );
  180. }
  181. // Execute the drag once - this causes the helper not to be visible before getting its
  182. // correct position
  183. this._mouseDrag( event, true );
  184. // If the ddmanager is used for droppables, inform the manager that dragging has started
  185. // (see #5003)
  186. if ( $.ui.ddmanager ) {
  187. $.ui.ddmanager.dragStart( this, event );
  188. }
  189. return true;
  190. },
  191. _refreshOffsets: function( event ) {
  192. this.offset = {
  193. top: this.positionAbs.top - this.margins.top,
  194. left: this.positionAbs.left - this.margins.left,
  195. scroll: false,
  196. parent: this._getParentOffset(),
  197. relative: this._getRelativeOffset()
  198. };
  199. this.offset.click = {
  200. left: event.pageX - this.offset.left,
  201. top: event.pageY - this.offset.top
  202. };
  203. },
  204. _mouseDrag: function( event, noPropagation ) {
  205. // reset any necessary cached properties (see #5009)
  206. if ( this.hasFixedAncestor ) {
  207. this.offset.parent = this._getParentOffset();
  208. }
  209. //Compute the helpers position
  210. this.position = this._generatePosition( event, true );
  211. this.positionAbs = this._convertPositionTo( "absolute" );
  212. //Call plugins and callbacks and use the resulting position if something is returned
  213. if ( !noPropagation ) {
  214. var ui = this._uiHash();
  215. if ( this._trigger( "drag", event, ui ) === false ) {
  216. this._mouseUp( new $.Event( "mouseup", event ) );
  217. return false;
  218. }
  219. this.position = ui.position;
  220. }
  221. this.helper[ 0 ].style.left = this.position.left + "px";
  222. this.helper[ 0 ].style.top = this.position.top + "px";
  223. if ( $.ui.ddmanager ) {
  224. $.ui.ddmanager.drag( this, event );
  225. }
  226. return false;
  227. },
  228. _mouseStop: function( event ) {
  229. //If we are using droppables, inform the manager about the drop
  230. var that = this,
  231. dropped = false;
  232. if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
  233. dropped = $.ui.ddmanager.drop( this, event );
  234. }
  235. //if a drop comes from outside (a sortable)
  236. if ( this.dropped ) {
  237. dropped = this.dropped;
  238. this.dropped = false;
  239. }
  240. if ( ( this.options.revert === "invalid" && !dropped ) ||
  241. ( this.options.revert === "valid" && dropped ) ||
  242. this.options.revert === true || ( typeof this.options.revert === "function" &&
  243. this.options.revert.call( this.element, dropped ) )
  244. ) {
  245. $( this.helper ).animate(
  246. this.originalPosition,
  247. parseInt( this.options.revertDuration, 10 ),
  248. function() {
  249. if ( that._trigger( "stop", event ) !== false ) {
  250. that._clear();
  251. }
  252. }
  253. );
  254. } else {
  255. if ( this._trigger( "stop", event ) !== false ) {
  256. this._clear();
  257. }
  258. }
  259. return false;
  260. },
  261. _mouseUp: function( event ) {
  262. this._unblockFrames();
  263. // If the ddmanager is used for droppables, inform the manager that dragging has stopped
  264. // (see #5003)
  265. if ( $.ui.ddmanager ) {
  266. $.ui.ddmanager.dragStop( this, event );
  267. }
  268. // Only need to focus if the event occurred on the draggable itself, see #10527
  269. if ( this.handleElement.is( event.target ) ) {
  270. // The interaction is over; whether or not the click resulted in a drag,
  271. // focus the element
  272. this.element.trigger( "focus" );
  273. }
  274. return $.ui.mouse.prototype._mouseUp.call( this, event );
  275. },
  276. cancel: function() {
  277. if ( this.helper.is( ".ui-draggable-dragging" ) ) {
  278. this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) );
  279. } else {
  280. this._clear();
  281. }
  282. return this;
  283. },
  284. _getHandle: function( event ) {
  285. return this.options.handle ?
  286. !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
  287. true;
  288. },
  289. _setHandleClassName: function() {
  290. this.handleElement = this.options.handle ?
  291. this.element.find( this.options.handle ) : this.element;
  292. this._addClass( this.handleElement, "ui-draggable-handle" );
  293. },
  294. _removeHandleClassName: function() {
  295. this._removeClass( this.handleElement, "ui-draggable-handle" );
  296. },
  297. _createHelper: function( event ) {
  298. var o = this.options,
  299. helperIsFunction = typeof o.helper === "function",
  300. helper = helperIsFunction ?
  301. $( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
  302. ( o.helper === "clone" ?
  303. this.element.clone().removeAttr( "id" ) :
  304. this.element );
  305. if ( !helper.parents( "body" ).length ) {
  306. helper.appendTo( ( o.appendTo === "parent" ?
  307. this.element[ 0 ].parentNode :
  308. o.appendTo ) );
  309. }
  310. // Http://bugs.jqueryui.com/ticket/9446
  311. // a helper function can return the original element
  312. // which wouldn't have been set to relative in _create
  313. if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
  314. this._setPositionRelative();
  315. }
  316. if ( helper[ 0 ] !== this.element[ 0 ] &&
  317. !( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) {
  318. helper.css( "position", "absolute" );
  319. }
  320. return helper;
  321. },
  322. _setPositionRelative: function() {
  323. if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
  324. this.element[ 0 ].style.position = "relative";
  325. }
  326. },
  327. _adjustOffsetFromHelper: function( obj ) {
  328. if ( typeof obj === "string" ) {
  329. obj = obj.split( " " );
  330. }
  331. if ( Array.isArray( obj ) ) {
  332. obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
  333. }
  334. if ( "left" in obj ) {
  335. this.offset.click.left = obj.left + this.margins.left;
  336. }
  337. if ( "right" in obj ) {
  338. this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
  339. }
  340. if ( "top" in obj ) {
  341. this.offset.click.top = obj.top + this.margins.top;
  342. }
  343. if ( "bottom" in obj ) {
  344. this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
  345. }
  346. },
  347. _isRootNode: function( element ) {
  348. return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
  349. },
  350. _getParentOffset: function() {
  351. //Get the offsetParent and cache its position
  352. var po = this.offsetParent.offset(),
  353. document = this.document[ 0 ];
  354. // This is a special case where we need to modify a offset calculated on start, since the
  355. // following happened:
  356. // 1. The position of the helper is absolute, so it's position is calculated based on the
  357. // next positioned parent
  358. // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
  359. // the document, which means that the scroll is included in the initial calculation of the
  360. // offset of the parent, and never recalculated upon drag
  361. if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document &&
  362. $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
  363. po.left += this.scrollParent.scrollLeft();
  364. po.top += this.scrollParent.scrollTop();
  365. }
  366. if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
  367. po = { top: 0, left: 0 };
  368. }
  369. return {
  370. top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
  371. left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
  372. };
  373. },
  374. _getRelativeOffset: function() {
  375. if ( this.cssPosition !== "relative" ) {
  376. return { top: 0, left: 0 };
  377. }
  378. var p = this.element.position(),
  379. scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
  380. return {
  381. top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
  382. ( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
  383. left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
  384. ( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
  385. };
  386. },
  387. _cacheMargins: function() {
  388. this.margins = {
  389. left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ),
  390. top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ),
  391. right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ),
  392. bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 )
  393. };
  394. },
  395. _cacheHelperProportions: function() {
  396. this.helperProportions = {
  397. width: this.helper.outerWidth(),
  398. height: this.helper.outerHeight()
  399. };
  400. },
  401. _setContainment: function() {
  402. var isUserScrollable, c, ce,
  403. o = this.options,
  404. document = this.document[ 0 ];
  405. this.relativeContainer = null;
  406. if ( !o.containment ) {
  407. this.containment = null;
  408. return;
  409. }
  410. if ( o.containment === "window" ) {
  411. this.containment = [
  412. $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
  413. $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
  414. $( window ).scrollLeft() + $( window ).width() -
  415. this.helperProportions.width - this.margins.left,
  416. $( window ).scrollTop() +
  417. ( $( window ).height() || document.body.parentNode.scrollHeight ) -
  418. this.helperProportions.height - this.margins.top
  419. ];
  420. return;
  421. }
  422. if ( o.containment === "document" ) {
  423. this.containment = [
  424. 0,
  425. 0,
  426. $( document ).width() - this.helperProportions.width - this.margins.left,
  427. ( $( document ).height() || document.body.parentNode.scrollHeight ) -
  428. this.helperProportions.height - this.margins.top
  429. ];
  430. return;
  431. }
  432. if ( o.containment.constructor === Array ) {
  433. this.containment = o.containment;
  434. return;
  435. }
  436. if ( o.containment === "parent" ) {
  437. o.containment = this.helper[ 0 ].parentNode;
  438. }
  439. c = $( o.containment );
  440. ce = c[ 0 ];
  441. if ( !ce ) {
  442. return;
  443. }
  444. isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );
  445. this.containment = [
  446. ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) +
  447. ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
  448. ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) +
  449. ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
  450. ( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
  451. ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
  452. ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
  453. this.helperProportions.width -
  454. this.margins.left -
  455. this.margins.right,
  456. ( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
  457. ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
  458. ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
  459. this.helperProportions.height -
  460. this.margins.top -
  461. this.margins.bottom
  462. ];
  463. this.relativeContainer = c;
  464. },
  465. _convertPositionTo: function( d, pos ) {
  466. if ( !pos ) {
  467. pos = this.position;
  468. }
  469. var mod = d === "absolute" ? 1 : -1,
  470. scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
  471. return {
  472. top: (
  473. // The absolute mouse position
  474. pos.top +
  475. // Only for relative positioned nodes: Relative offset from element to offset parent
  476. this.offset.relative.top * mod +
  477. // The offsetParent's offset without borders (offset + border)
  478. this.offset.parent.top * mod -
  479. ( ( this.cssPosition === "fixed" ?
  480. -this.offset.scroll.top :
  481. ( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod )
  482. ),
  483. left: (
  484. // The absolute mouse position
  485. pos.left +
  486. // Only for relative positioned nodes: Relative offset from element to offset parent
  487. this.offset.relative.left * mod +
  488. // The offsetParent's offset without borders (offset + border)
  489. this.offset.parent.left * mod -
  490. ( ( this.cssPosition === "fixed" ?
  491. -this.offset.scroll.left :
  492. ( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod )
  493. )
  494. };
  495. },
  496. _generatePosition: function( event, constrainPosition ) {
  497. var containment, co, top, left,
  498. o = this.options,
  499. scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
  500. pageX = event.pageX,
  501. pageY = event.pageY;
  502. // Cache the scroll
  503. if ( !scrollIsRootNode || !this.offset.scroll ) {
  504. this.offset.scroll = {
  505. top: this.scrollParent.scrollTop(),
  506. left: this.scrollParent.scrollLeft()
  507. };
  508. }
  509. /*
  510. * - Position constraining -
  511. * Constrain the position to a mix of grid, containment.
  512. */
  513. // If we are not dragging yet, we won't check for options
  514. if ( constrainPosition ) {
  515. if ( this.containment ) {
  516. if ( this.relativeContainer ) {
  517. co = this.relativeContainer.offset();
  518. containment = [
  519. this.containment[ 0 ] + co.left,
  520. this.containment[ 1 ] + co.top,
  521. this.containment[ 2 ] + co.left,
  522. this.containment[ 3 ] + co.top
  523. ];
  524. } else {
  525. containment = this.containment;
  526. }
  527. if ( event.pageX - this.offset.click.left < containment[ 0 ] ) {
  528. pageX = containment[ 0 ] + this.offset.click.left;
  529. }
  530. if ( event.pageY - this.offset.click.top < containment[ 1 ] ) {
  531. pageY = containment[ 1 ] + this.offset.click.top;
  532. }
  533. if ( event.pageX - this.offset.click.left > containment[ 2 ] ) {
  534. pageX = containment[ 2 ] + this.offset.click.left;
  535. }
  536. if ( event.pageY - this.offset.click.top > containment[ 3 ] ) {
  537. pageY = containment[ 3 ] + this.offset.click.top;
  538. }
  539. }
  540. if ( o.grid ) {
  541. //Check for grid elements set to 0 to prevent divide by 0 error causing invalid
  542. // argument errors in IE (see ticket #6950)
  543. top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY -
  544. this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY;
  545. pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] ||
  546. top - this.offset.click.top > containment[ 3 ] ) ?
  547. top :
  548. ( ( top - this.offset.click.top >= containment[ 1 ] ) ?
  549. top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top;
  550. left = o.grid[ 0 ] ? this.originalPageX +
  551. Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] :
  552. this.originalPageX;
  553. pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] ||
  554. left - this.offset.click.left > containment[ 2 ] ) ?
  555. left :
  556. ( ( left - this.offset.click.left >= containment[ 0 ] ) ?
  557. left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left;
  558. }
  559. if ( o.axis === "y" ) {
  560. pageX = this.originalPageX;
  561. }
  562. if ( o.axis === "x" ) {
  563. pageY = this.originalPageY;
  564. }
  565. }
  566. return {
  567. top: (
  568. // The absolute mouse position
  569. pageY -
  570. // Click offset (relative to the element)
  571. this.offset.click.top -
  572. // Only for relative positioned nodes: Relative offset from element to offset parent
  573. this.offset.relative.top -
  574. // The offsetParent's offset without borders (offset + border)
  575. this.offset.parent.top +
  576. ( this.cssPosition === "fixed" ?
  577. -this.offset.scroll.top :
  578. ( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
  579. ),
  580. left: (
  581. // The absolute mouse position
  582. pageX -
  583. // Click offset (relative to the element)
  584. this.offset.click.left -
  585. // Only for relative positioned nodes: Relative offset from element to offset parent
  586. this.offset.relative.left -
  587. // The offsetParent's offset without borders (offset + border)
  588. this.offset.parent.left +
  589. ( this.cssPosition === "fixed" ?
  590. -this.offset.scroll.left :
  591. ( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
  592. )
  593. };
  594. },
  595. _clear: function() {
  596. this._removeClass( this.helper, "ui-draggable-dragging" );
  597. if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) {
  598. this.helper.remove();
  599. }
  600. this.helper = null;
  601. this.cancelHelperRemoval = false;
  602. if ( this.destroyOnClear ) {
  603. this.destroy();
  604. }
  605. },
  606. // From now on bulk stuff - mainly helpers
  607. _trigger: function( type, event, ui ) {
  608. ui = ui || this._uiHash();
  609. $.ui.plugin.call( this, type, [ event, ui, this ], true );
  610. // Absolute position and offset (see #6884 ) have to be recalculated after plugins
  611. if ( /^(drag|start|stop)/.test( type ) ) {
  612. this.positionAbs = this._convertPositionTo( "absolute" );
  613. ui.offset = this.positionAbs;
  614. }
  615. return $.Widget.prototype._trigger.call( this, type, event, ui );
  616. },
  617. plugins: {},
  618. _uiHash: function() {
  619. return {
  620. helper: this.helper,
  621. position: this.position,
  622. originalPosition: this.originalPosition,
  623. offset: this.positionAbs
  624. };
  625. }
  626. } );
  627. $.ui.plugin.add( "draggable", "connectToSortable", {
  628. start: function( event, ui, draggable ) {
  629. var uiSortable = $.extend( {}, ui, {
  630. item: draggable.element
  631. } );
  632. draggable.sortables = [];
  633. $( draggable.options.connectToSortable ).each( function() {
  634. var sortable = $( this ).sortable( "instance" );
  635. if ( sortable && !sortable.options.disabled ) {
  636. draggable.sortables.push( sortable );
  637. // RefreshPositions is called at drag start to refresh the containerCache
  638. // which is used in drag. This ensures it's initialized and synchronized
  639. // with any changes that might have happened on the page since initialization.
  640. sortable.refreshPositions();
  641. sortable._trigger( "activate", event, uiSortable );
  642. }
  643. } );
  644. },
  645. stop: function( event, ui, draggable ) {
  646. var uiSortable = $.extend( {}, ui, {
  647. item: draggable.element
  648. } );
  649. draggable.cancelHelperRemoval = false;
  650. $.each( draggable.sortables, function() {
  651. var sortable = this;
  652. if ( sortable.isOver ) {
  653. sortable.isOver = 0;
  654. // Allow this sortable to handle removing the helper
  655. draggable.cancelHelperRemoval = true;
  656. sortable.cancelHelperRemoval = false;
  657. // Use _storedCSS To restore properties in the sortable,
  658. // as this also handles revert (#9675) since the draggable
  659. // may have modified them in unexpected ways (#8809)
  660. sortable._storedCSS = {
  661. position: sortable.placeholder.css( "position" ),
  662. top: sortable.placeholder.css( "top" ),
  663. left: sortable.placeholder.css( "left" )
  664. };
  665. sortable._mouseStop( event );
  666. // Once drag has ended, the sortable should return to using
  667. // its original helper, not the shared helper from draggable
  668. sortable.options.helper = sortable.options._helper;
  669. } else {
  670. // Prevent this Sortable from removing the helper.
  671. // However, don't set the draggable to remove the helper
  672. // either as another connected Sortable may yet handle the removal.
  673. sortable.cancelHelperRemoval = true;
  674. sortable._trigger( "deactivate", event, uiSortable );
  675. }
  676. } );
  677. },
  678. drag: function( event, ui, draggable ) {
  679. $.each( draggable.sortables, function() {
  680. var innermostIntersecting = false,
  681. sortable = this;
  682. // Copy over variables that sortable's _intersectsWith uses
  683. sortable.positionAbs = draggable.positionAbs;
  684. sortable.helperProportions = draggable.helperProportions;
  685. sortable.offset.click = draggable.offset.click;
  686. if ( sortable._intersectsWith( sortable.containerCache ) ) {
  687. innermostIntersecting = true;
  688. $.each( draggable.sortables, function() {
  689. // Copy over variables that sortable's _intersectsWith uses
  690. this.positionAbs = draggable.positionAbs;
  691. this.helperProportions = draggable.helperProportions;
  692. this.offset.click = draggable.offset.click;
  693. if ( this !== sortable &&
  694. this._intersectsWith( this.containerCache ) &&
  695. $.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
  696. innermostIntersecting = false;
  697. }
  698. return innermostIntersecting;
  699. } );
  700. }
  701. if ( innermostIntersecting ) {
  702. // If it intersects, we use a little isOver variable and set it once,
  703. // so that the move-in stuff gets fired only once.
  704. if ( !sortable.isOver ) {
  705. sortable.isOver = 1;
  706. // Store draggable's parent in case we need to reappend to it later.
  707. draggable._parent = ui.helper.parent();
  708. sortable.currentItem = ui.helper
  709. .appendTo( sortable.element )
  710. .data( "ui-sortable-item", true );
  711. // Store helper option to later restore it
  712. sortable.options._helper = sortable.options.helper;
  713. sortable.options.helper = function() {
  714. return ui.helper[ 0 ];
  715. };
  716. // Fire the start events of the sortable with our passed browser event,
  717. // and our own helper (so it doesn't create a new one)
  718. event.target = sortable.currentItem[ 0 ];
  719. sortable._mouseCapture( event, true );
  720. sortable._mouseStart( event, true, true );
  721. // Because the browser event is way off the new appended portlet,
  722. // modify necessary variables to reflect the changes
  723. sortable.offset.click.top = draggable.offset.click.top;
  724. sortable.offset.click.left = draggable.offset.click.left;
  725. sortable.offset.parent.left -= draggable.offset.parent.left -
  726. sortable.offset.parent.left;
  727. sortable.offset.parent.top -= draggable.offset.parent.top -
  728. sortable.offset.parent.top;
  729. draggable._trigger( "toSortable", event );
  730. // Inform draggable that the helper is in a valid drop zone,
  731. // used solely in the revert option to handle "valid/invalid".
  732. draggable.dropped = sortable.element;
  733. // Need to refreshPositions of all sortables in the case that
  734. // adding to one sortable changes the location of the other sortables (#9675)
  735. $.each( draggable.sortables, function() {
  736. this.refreshPositions();
  737. } );
  738. // Hack so receive/update callbacks work (mostly)
  739. draggable.currentItem = draggable.element;
  740. sortable.fromOutside = draggable;
  741. }
  742. if ( sortable.currentItem ) {
  743. sortable._mouseDrag( event );
  744. // Copy the sortable's position because the draggable's can potentially reflect
  745. // a relative position, while sortable is always absolute, which the dragged
  746. // element has now become. (#8809)
  747. ui.position = sortable.position;
  748. }
  749. } else {
  750. // If it doesn't intersect with the sortable, and it intersected before,
  751. // we fake the drag stop of the sortable, but make sure it doesn't remove
  752. // the helper by using cancelHelperRemoval.
  753. if ( sortable.isOver ) {
  754. sortable.isOver = 0;
  755. sortable.cancelHelperRemoval = true;
  756. // Calling sortable's mouseStop would trigger a revert,
  757. // so revert must be temporarily false until after mouseStop is called.
  758. sortable.options._revert = sortable.options.revert;
  759. sortable.options.revert = false;
  760. sortable._trigger( "out", event, sortable._uiHash( sortable ) );
  761. sortable._mouseStop( event, true );
  762. // Restore sortable behaviors that were modfied
  763. // when the draggable entered the sortable area (#9481)
  764. sortable.options.revert = sortable.options._revert;
  765. sortable.options.helper = sortable.options._helper;
  766. if ( sortable.placeholder ) {
  767. sortable.placeholder.remove();
  768. }
  769. // Restore and recalculate the draggable's offset considering the sortable
  770. // may have modified them in unexpected ways. (#8809, #10669)
  771. ui.helper.appendTo( draggable._parent );
  772. draggable._refreshOffsets( event );
  773. ui.position = draggable._generatePosition( event, true );
  774. draggable._trigger( "fromSortable", event );
  775. // Inform draggable that the helper is no longer in a valid drop zone
  776. draggable.dropped = false;
  777. // Need to refreshPositions of all sortables just in case removing
  778. // from one sortable changes the location of other sortables (#9675)
  779. $.each( draggable.sortables, function() {
  780. this.refreshPositions();
  781. } );
  782. }
  783. }
  784. } );
  785. }
  786. } );
  787. $.ui.plugin.add( "draggable", "cursor", {
  788. start: function( event, ui, instance ) {
  789. var t = $( "body" ),
  790. o = instance.options;
  791. if ( t.css( "cursor" ) ) {
  792. o._cursor = t.css( "cursor" );
  793. }
  794. t.css( "cursor", o.cursor );
  795. },
  796. stop: function( event, ui, instance ) {
  797. var o = instance.options;
  798. if ( o._cursor ) {
  799. $( "body" ).css( "cursor", o._cursor );
  800. }
  801. }
  802. } );
  803. $.ui.plugin.add( "draggable", "opacity", {
  804. start: function( event, ui, instance ) {
  805. var t = $( ui.helper ),
  806. o = instance.options;
  807. if ( t.css( "opacity" ) ) {
  808. o._opacity = t.css( "opacity" );
  809. }
  810. t.css( "opacity", o.opacity );
  811. },
  812. stop: function( event, ui, instance ) {
  813. var o = instance.options;
  814. if ( o._opacity ) {
  815. $( ui.helper ).css( "opacity", o._opacity );
  816. }
  817. }
  818. } );
  819. $.ui.plugin.add( "draggable", "scroll", {
  820. start: function( event, ui, i ) {
  821. if ( !i.scrollParentNotHidden ) {
  822. i.scrollParentNotHidden = i.helper.scrollParent( false );
  823. }
  824. if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] &&
  825. i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
  826. i.overflowOffset = i.scrollParentNotHidden.offset();
  827. }
  828. },
  829. drag: function( event, ui, i ) {
  830. var o = i.options,
  831. scrolled = false,
  832. scrollParent = i.scrollParentNotHidden[ 0 ],
  833. document = i.document[ 0 ];
  834. if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
  835. if ( !o.axis || o.axis !== "x" ) {
  836. if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY <
  837. o.scrollSensitivity ) {
  838. scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
  839. } else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
  840. scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
  841. }
  842. }
  843. if ( !o.axis || o.axis !== "y" ) {
  844. if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX <
  845. o.scrollSensitivity ) {
  846. scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
  847. } else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
  848. scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
  849. }
  850. }
  851. } else {
  852. if ( !o.axis || o.axis !== "x" ) {
  853. if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) {
  854. scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed );
  855. } else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) <
  856. o.scrollSensitivity ) {
  857. scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed );
  858. }
  859. }
  860. if ( !o.axis || o.axis !== "y" ) {
  861. if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) {
  862. scrolled = $( document ).scrollLeft(
  863. $( document ).scrollLeft() - o.scrollSpeed
  864. );
  865. } else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) <
  866. o.scrollSensitivity ) {
  867. scrolled = $( document ).scrollLeft(
  868. $( document ).scrollLeft() + o.scrollSpeed
  869. );
  870. }
  871. }
  872. }
  873. if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
  874. $.ui.ddmanager.prepareOffsets( i, event );
  875. }
  876. }
  877. } );
  878. $.ui.plugin.add( "draggable", "snap", {
  879. start: function( event, ui, i ) {
  880. var o = i.options;
  881. i.snapElements = [];
  882. $( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap )
  883. .each( function() {
  884. var $t = $( this ),
  885. $o = $t.offset();
  886. if ( this !== i.element[ 0 ] ) {
  887. i.snapElements.push( {
  888. item: this,
  889. width: $t.outerWidth(), height: $t.outerHeight(),
  890. top: $o.top, left: $o.left
  891. } );
  892. }
  893. } );
  894. },
  895. drag: function( event, ui, inst ) {
  896. var ts, bs, ls, rs, l, r, t, b, i, first,
  897. o = inst.options,
  898. d = o.snapTolerance,
  899. x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
  900. y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
  901. for ( i = inst.snapElements.length - 1; i >= 0; i-- ) {
  902. l = inst.snapElements[ i ].left - inst.margins.left;
  903. r = l + inst.snapElements[ i ].width;
  904. t = inst.snapElements[ i ].top - inst.margins.top;
  905. b = t + inst.snapElements[ i ].height;
  906. if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d ||
  907. !$.contains( inst.snapElements[ i ].item.ownerDocument,
  908. inst.snapElements[ i ].item ) ) {
  909. if ( inst.snapElements[ i ].snapping ) {
  910. if ( inst.options.snap.release ) {
  911. inst.options.snap.release.call(
  912. inst.element,
  913. event,
  914. $.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } )
  915. );
  916. }
  917. }
  918. inst.snapElements[ i ].snapping = false;
  919. continue;
  920. }
  921. if ( o.snapMode !== "inner" ) {
  922. ts = Math.abs( t - y2 ) <= d;
  923. bs = Math.abs( b - y1 ) <= d;
  924. ls = Math.abs( l - x2 ) <= d;
  925. rs = Math.abs( r - x1 ) <= d;
  926. if ( ts ) {
  927. ui.position.top = inst._convertPositionTo( "relative", {
  928. top: t - inst.helperProportions.height,
  929. left: 0
  930. } ).top;
  931. }
  932. if ( bs ) {
  933. ui.position.top = inst._convertPositionTo( "relative", {
  934. top: b,
  935. left: 0
  936. } ).top;
  937. }
  938. if ( ls ) {
  939. ui.position.left = inst._convertPositionTo( "relative", {
  940. top: 0,
  941. left: l - inst.helperProportions.width
  942. } ).left;
  943. }
  944. if ( rs ) {
  945. ui.position.left = inst._convertPositionTo( "relative", {
  946. top: 0,
  947. left: r
  948. } ).left;
  949. }
  950. }
  951. first = ( ts || bs || ls || rs );
  952. if ( o.snapMode !== "outer" ) {
  953. ts = Math.abs( t - y1 ) <= d;
  954. bs = Math.abs( b - y2 ) <= d;
  955. ls = Math.abs( l - x1 ) <= d;
  956. rs = Math.abs( r - x2 ) <= d;
  957. if ( ts ) {
  958. ui.position.top = inst._convertPositionTo( "relative", {
  959. top: t,
  960. left: 0
  961. } ).top;
  962. }
  963. if ( bs ) {
  964. ui.position.top = inst._convertPositionTo( "relative", {
  965. top: b - inst.helperProportions.height,
  966. left: 0
  967. } ).top;
  968. }
  969. if ( ls ) {
  970. ui.position.left = inst._convertPositionTo( "relative", {
  971. top: 0,
  972. left: l
  973. } ).left;
  974. }
  975. if ( rs ) {
  976. ui.position.left = inst._convertPositionTo( "relative", {
  977. top: 0,
  978. left: r - inst.helperProportions.width
  979. } ).left;
  980. }
  981. }
  982. if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) {
  983. if ( inst.options.snap.snap ) {
  984. inst.options.snap.snap.call(
  985. inst.element,
  986. event,
  987. $.extend( inst._uiHash(), {
  988. snapItem: inst.snapElements[ i ].item
  989. } ) );
  990. }
  991. }
  992. inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first );
  993. }
  994. }
  995. } );
  996. $.ui.plugin.add( "draggable", "stack", {
  997. start: function( event, ui, instance ) {
  998. var min,
  999. o = instance.options,
  1000. group = $.makeArray( $( o.stack ) ).sort( function( a, b ) {
  1001. return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) -
  1002. ( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 );
  1003. } );
  1004. if ( !group.length ) {
  1005. return;
  1006. }
  1007. min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0;
  1008. $( group ).each( function( i ) {
  1009. $( this ).css( "zIndex", min + i );
  1010. } );
  1011. this.css( "zIndex", ( min + group.length ) );
  1012. }
  1013. } );
  1014. $.ui.plugin.add( "draggable", "zIndex", {
  1015. start: function( event, ui, instance ) {
  1016. var t = $( ui.helper ),
  1017. o = instance.options;
  1018. if ( t.css( "zIndex" ) ) {
  1019. o._zIndex = t.css( "zIndex" );
  1020. }
  1021. t.css( "zIndex", o.zIndex );
  1022. },
  1023. stop: function( event, ui, instance ) {
  1024. var o = instance.options;
  1025. if ( o._zIndex ) {
  1026. $( ui.helper ).css( "zIndex", o._zIndex );
  1027. }
  1028. }
  1029. } );
  1030. return $.ui.draggable;
  1031. } );