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.

1611 lines
46 KiB

1 year ago
  1. /*!
  2. * jQuery UI Sortable 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: Sortable
  10. //>>group: Interactions
  11. //>>description: Enables items in a list to be sorted using the mouse.
  12. //>>docs: http://api.jqueryui.com/sortable/
  13. //>>demos: http://jqueryui.com/sortable/
  14. //>>css.structure: ../../themes/base/sortable.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. return $.widget( "ui.sortable", $.ui.mouse, {
  31. version: "1.13.2",
  32. widgetEventPrefix: "sort",
  33. ready: false,
  34. options: {
  35. appendTo: "parent",
  36. axis: false,
  37. connectWith: false,
  38. containment: false,
  39. cursor: "auto",
  40. cursorAt: false,
  41. dropOnEmpty: true,
  42. forcePlaceholderSize: false,
  43. forceHelperSize: false,
  44. grid: false,
  45. handle: false,
  46. helper: "original",
  47. items: "> *",
  48. opacity: false,
  49. placeholder: false,
  50. revert: false,
  51. scroll: true,
  52. scrollSensitivity: 20,
  53. scrollSpeed: 20,
  54. scope: "default",
  55. tolerance: "intersect",
  56. zIndex: 1000,
  57. // Callbacks
  58. activate: null,
  59. beforeStop: null,
  60. change: null,
  61. deactivate: null,
  62. out: null,
  63. over: null,
  64. receive: null,
  65. remove: null,
  66. sort: null,
  67. start: null,
  68. stop: null,
  69. update: null
  70. },
  71. _isOverAxis: function( x, reference, size ) {
  72. return ( x >= reference ) && ( x < ( reference + size ) );
  73. },
  74. _isFloating: function( item ) {
  75. return ( /left|right/ ).test( item.css( "float" ) ) ||
  76. ( /inline|table-cell/ ).test( item.css( "display" ) );
  77. },
  78. _create: function() {
  79. this.containerCache = {};
  80. this._addClass( "ui-sortable" );
  81. //Get the items
  82. this.refresh();
  83. //Let's determine the parent's offset
  84. this.offset = this.element.offset();
  85. //Initialize mouse events for interaction
  86. this._mouseInit();
  87. this._setHandleClassName();
  88. //We're ready to go
  89. this.ready = true;
  90. },
  91. _setOption: function( key, value ) {
  92. this._super( key, value );
  93. if ( key === "handle" ) {
  94. this._setHandleClassName();
  95. }
  96. },
  97. _setHandleClassName: function() {
  98. var that = this;
  99. this._removeClass( this.element.find( ".ui-sortable-handle" ), "ui-sortable-handle" );
  100. $.each( this.items, function() {
  101. that._addClass(
  102. this.instance.options.handle ?
  103. this.item.find( this.instance.options.handle ) :
  104. this.item,
  105. "ui-sortable-handle"
  106. );
  107. } );
  108. },
  109. _destroy: function() {
  110. this._mouseDestroy();
  111. for ( var i = this.items.length - 1; i >= 0; i-- ) {
  112. this.items[ i ].item.removeData( this.widgetName + "-item" );
  113. }
  114. return this;
  115. },
  116. _mouseCapture: function( event, overrideHandle ) {
  117. var currentItem = null,
  118. validHandle = false,
  119. that = this;
  120. if ( this.reverting ) {
  121. return false;
  122. }
  123. if ( this.options.disabled || this.options.type === "static" ) {
  124. return false;
  125. }
  126. //We have to refresh the items data once first
  127. this._refreshItems( event );
  128. //Find out if the clicked node (or one of its parents) is a actual item in this.items
  129. $( event.target ).parents().each( function() {
  130. if ( $.data( this, that.widgetName + "-item" ) === that ) {
  131. currentItem = $( this );
  132. return false;
  133. }
  134. } );
  135. if ( $.data( event.target, that.widgetName + "-item" ) === that ) {
  136. currentItem = $( event.target );
  137. }
  138. if ( !currentItem ) {
  139. return false;
  140. }
  141. if ( this.options.handle && !overrideHandle ) {
  142. $( this.options.handle, currentItem ).find( "*" ).addBack().each( function() {
  143. if ( this === event.target ) {
  144. validHandle = true;
  145. }
  146. } );
  147. if ( !validHandle ) {
  148. return false;
  149. }
  150. }
  151. this.currentItem = currentItem;
  152. this._removeCurrentsFromItems();
  153. return true;
  154. },
  155. _mouseStart: function( event, overrideHandle, noActivation ) {
  156. var i, body,
  157. o = this.options;
  158. this.currentContainer = this;
  159. //We only need to call refreshPositions, because the refreshItems call has been moved to
  160. // mouseCapture
  161. this.refreshPositions();
  162. //Prepare the dragged items parent
  163. this.appendTo = $( o.appendTo !== "parent" ?
  164. o.appendTo :
  165. this.currentItem.parent() );
  166. //Create and append the visible helper
  167. this.helper = this._createHelper( event );
  168. //Cache the helper size
  169. this._cacheHelperProportions();
  170. /*
  171. * - Position generation -
  172. * This block generates everything position related - it's the core of draggables.
  173. */
  174. //Cache the margins of the original element
  175. this._cacheMargins();
  176. //The element's absolute position on the page minus margins
  177. this.offset = this.currentItem.offset();
  178. this.offset = {
  179. top: this.offset.top - this.margins.top,
  180. left: this.offset.left - this.margins.left
  181. };
  182. $.extend( this.offset, {
  183. click: { //Where the click happened, relative to the element
  184. left: event.pageX - this.offset.left,
  185. top: event.pageY - this.offset.top
  186. },
  187. // This is a relative to absolute position minus the actual position calculation -
  188. // only used for relative positioned helper
  189. relative: this._getRelativeOffset()
  190. } );
  191. // After we get the helper offset, but before we get the parent offset we can
  192. // change the helper's position to absolute
  193. // TODO: Still need to figure out a way to make relative sorting possible
  194. this.helper.css( "position", "absolute" );
  195. this.cssPosition = this.helper.css( "position" );
  196. //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
  197. if ( o.cursorAt ) {
  198. this._adjustOffsetFromHelper( o.cursorAt );
  199. }
  200. //Cache the former DOM position
  201. this.domPosition = {
  202. prev: this.currentItem.prev()[ 0 ],
  203. parent: this.currentItem.parent()[ 0 ]
  204. };
  205. // If the helper is not the original, hide the original so it's not playing any role during
  206. // the drag, won't cause anything bad this way
  207. if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
  208. this.currentItem.hide();
  209. }
  210. //Create the placeholder
  211. this._createPlaceholder();
  212. //Get the next scrolling parent
  213. this.scrollParent = this.placeholder.scrollParent();
  214. $.extend( this.offset, {
  215. parent: this._getParentOffset()
  216. } );
  217. //Set a containment if given in the options
  218. if ( o.containment ) {
  219. this._setContainment();
  220. }
  221. if ( o.cursor && o.cursor !== "auto" ) { // cursor option
  222. body = this.document.find( "body" );
  223. // Support: IE
  224. this.storedCursor = body.css( "cursor" );
  225. body.css( "cursor", o.cursor );
  226. this.storedStylesheet =
  227. $( "<style>*{ cursor: " + o.cursor + " !important; }</style>" ).appendTo( body );
  228. }
  229. // We need to make sure to grab the zIndex before setting the
  230. // opacity, because setting the opacity to anything lower than 1
  231. // causes the zIndex to change from "auto" to 0.
  232. if ( o.zIndex ) { // zIndex option
  233. if ( this.helper.css( "zIndex" ) ) {
  234. this._storedZIndex = this.helper.css( "zIndex" );
  235. }
  236. this.helper.css( "zIndex", o.zIndex );
  237. }
  238. if ( o.opacity ) { // opacity option
  239. if ( this.helper.css( "opacity" ) ) {
  240. this._storedOpacity = this.helper.css( "opacity" );
  241. }
  242. this.helper.css( "opacity", o.opacity );
  243. }
  244. //Prepare scrolling
  245. if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
  246. this.scrollParent[ 0 ].tagName !== "HTML" ) {
  247. this.overflowOffset = this.scrollParent.offset();
  248. }
  249. //Call callbacks
  250. this._trigger( "start", event, this._uiHash() );
  251. //Recache the helper size
  252. if ( !this._preserveHelperProportions ) {
  253. this._cacheHelperProportions();
  254. }
  255. //Post "activate" events to possible containers
  256. if ( !noActivation ) {
  257. for ( i = this.containers.length - 1; i >= 0; i-- ) {
  258. this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
  259. }
  260. }
  261. //Prepare possible droppables
  262. if ( $.ui.ddmanager ) {
  263. $.ui.ddmanager.current = this;
  264. }
  265. if ( $.ui.ddmanager && !o.dropBehaviour ) {
  266. $.ui.ddmanager.prepareOffsets( this, event );
  267. }
  268. this.dragging = true;
  269. this._addClass( this.helper, "ui-sortable-helper" );
  270. //Move the helper, if needed
  271. if ( !this.helper.parent().is( this.appendTo ) ) {
  272. this.helper.detach().appendTo( this.appendTo );
  273. //Update position
  274. this.offset.parent = this._getParentOffset();
  275. }
  276. //Generate the original position
  277. this.position = this.originalPosition = this._generatePosition( event );
  278. this.originalPageX = event.pageX;
  279. this.originalPageY = event.pageY;
  280. this.lastPositionAbs = this.positionAbs = this._convertPositionTo( "absolute" );
  281. this._mouseDrag( event );
  282. return true;
  283. },
  284. _scroll: function( event ) {
  285. var o = this.options,
  286. scrolled = false;
  287. if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
  288. this.scrollParent[ 0 ].tagName !== "HTML" ) {
  289. if ( ( this.overflowOffset.top + this.scrollParent[ 0 ].offsetHeight ) -
  290. event.pageY < o.scrollSensitivity ) {
  291. this.scrollParent[ 0 ].scrollTop =
  292. scrolled = this.scrollParent[ 0 ].scrollTop + o.scrollSpeed;
  293. } else if ( event.pageY - this.overflowOffset.top < o.scrollSensitivity ) {
  294. this.scrollParent[ 0 ].scrollTop =
  295. scrolled = this.scrollParent[ 0 ].scrollTop - o.scrollSpeed;
  296. }
  297. if ( ( this.overflowOffset.left + this.scrollParent[ 0 ].offsetWidth ) -
  298. event.pageX < o.scrollSensitivity ) {
  299. this.scrollParent[ 0 ].scrollLeft = scrolled =
  300. this.scrollParent[ 0 ].scrollLeft + o.scrollSpeed;
  301. } else if ( event.pageX - this.overflowOffset.left < o.scrollSensitivity ) {
  302. this.scrollParent[ 0 ].scrollLeft = scrolled =
  303. this.scrollParent[ 0 ].scrollLeft - o.scrollSpeed;
  304. }
  305. } else {
  306. if ( event.pageY - this.document.scrollTop() < o.scrollSensitivity ) {
  307. scrolled = this.document.scrollTop( this.document.scrollTop() - o.scrollSpeed );
  308. } else if ( this.window.height() - ( event.pageY - this.document.scrollTop() ) <
  309. o.scrollSensitivity ) {
  310. scrolled = this.document.scrollTop( this.document.scrollTop() + o.scrollSpeed );
  311. }
  312. if ( event.pageX - this.document.scrollLeft() < o.scrollSensitivity ) {
  313. scrolled = this.document.scrollLeft(
  314. this.document.scrollLeft() - o.scrollSpeed
  315. );
  316. } else if ( this.window.width() - ( event.pageX - this.document.scrollLeft() ) <
  317. o.scrollSensitivity ) {
  318. scrolled = this.document.scrollLeft(
  319. this.document.scrollLeft() + o.scrollSpeed
  320. );
  321. }
  322. }
  323. return scrolled;
  324. },
  325. _mouseDrag: function( event ) {
  326. var i, item, itemElement, intersection,
  327. o = this.options;
  328. //Compute the helpers position
  329. this.position = this._generatePosition( event );
  330. this.positionAbs = this._convertPositionTo( "absolute" );
  331. //Set the helper position
  332. if ( !this.options.axis || this.options.axis !== "y" ) {
  333. this.helper[ 0 ].style.left = this.position.left + "px";
  334. }
  335. if ( !this.options.axis || this.options.axis !== "x" ) {
  336. this.helper[ 0 ].style.top = this.position.top + "px";
  337. }
  338. //Do scrolling
  339. if ( o.scroll ) {
  340. if ( this._scroll( event ) !== false ) {
  341. //Update item positions used in position checks
  342. this._refreshItemPositions( true );
  343. if ( $.ui.ddmanager && !o.dropBehaviour ) {
  344. $.ui.ddmanager.prepareOffsets( this, event );
  345. }
  346. }
  347. }
  348. this.dragDirection = {
  349. vertical: this._getDragVerticalDirection(),
  350. horizontal: this._getDragHorizontalDirection()
  351. };
  352. //Rearrange
  353. for ( i = this.items.length - 1; i >= 0; i-- ) {
  354. //Cache variables and intersection, continue if no intersection
  355. item = this.items[ i ];
  356. itemElement = item.item[ 0 ];
  357. intersection = this._intersectsWithPointer( item );
  358. if ( !intersection ) {
  359. continue;
  360. }
  361. // Only put the placeholder inside the current Container, skip all
  362. // items from other containers. This works because when moving
  363. // an item from one container to another the
  364. // currentContainer is switched before the placeholder is moved.
  365. //
  366. // Without this, moving items in "sub-sortables" can cause
  367. // the placeholder to jitter between the outer and inner container.
  368. if ( item.instance !== this.currentContainer ) {
  369. continue;
  370. }
  371. // Cannot intersect with itself
  372. // no useless actions that have been done before
  373. // no action if the item moved is the parent of the item checked
  374. if ( itemElement !== this.currentItem[ 0 ] &&
  375. this.placeholder[ intersection === 1 ?
  376. "next" : "prev" ]()[ 0 ] !== itemElement &&
  377. !$.contains( this.placeholder[ 0 ], itemElement ) &&
  378. ( this.options.type === "semi-dynamic" ?
  379. !$.contains( this.element[ 0 ], itemElement ) :
  380. true
  381. )
  382. ) {
  383. this.direction = intersection === 1 ? "down" : "up";
  384. if ( this.options.tolerance === "pointer" ||
  385. this._intersectsWithSides( item ) ) {
  386. this._rearrange( event, item );
  387. } else {
  388. break;
  389. }
  390. this._trigger( "change", event, this._uiHash() );
  391. break;
  392. }
  393. }
  394. //Post events to containers
  395. this._contactContainers( event );
  396. //Interconnect with droppables
  397. if ( $.ui.ddmanager ) {
  398. $.ui.ddmanager.drag( this, event );
  399. }
  400. //Call callbacks
  401. this._trigger( "sort", event, this._uiHash() );
  402. this.lastPositionAbs = this.positionAbs;
  403. return false;
  404. },
  405. _mouseStop: function( event, noPropagation ) {
  406. if ( !event ) {
  407. return;
  408. }
  409. //If we are using droppables, inform the manager about the drop
  410. if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
  411. $.ui.ddmanager.drop( this, event );
  412. }
  413. if ( this.options.revert ) {
  414. var that = this,
  415. cur = this.placeholder.offset(),
  416. axis = this.options.axis,
  417. animation = {};
  418. if ( !axis || axis === "x" ) {
  419. animation.left = cur.left - this.offset.parent.left - this.margins.left +
  420. ( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
  421. 0 :
  422. this.offsetParent[ 0 ].scrollLeft
  423. );
  424. }
  425. if ( !axis || axis === "y" ) {
  426. animation.top = cur.top - this.offset.parent.top - this.margins.top +
  427. ( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
  428. 0 :
  429. this.offsetParent[ 0 ].scrollTop
  430. );
  431. }
  432. this.reverting = true;
  433. $( this.helper ).animate(
  434. animation,
  435. parseInt( this.options.revert, 10 ) || 500,
  436. function() {
  437. that._clear( event );
  438. }
  439. );
  440. } else {
  441. this._clear( event, noPropagation );
  442. }
  443. return false;
  444. },
  445. cancel: function() {
  446. if ( this.dragging ) {
  447. this._mouseUp( new $.Event( "mouseup", { target: null } ) );
  448. if ( this.options.helper === "original" ) {
  449. this.currentItem.css( this._storedCSS );
  450. this._removeClass( this.currentItem, "ui-sortable-helper" );
  451. } else {
  452. this.currentItem.show();
  453. }
  454. //Post deactivating events to containers
  455. for ( var i = this.containers.length - 1; i >= 0; i-- ) {
  456. this.containers[ i ]._trigger( "deactivate", null, this._uiHash( this ) );
  457. if ( this.containers[ i ].containerCache.over ) {
  458. this.containers[ i ]._trigger( "out", null, this._uiHash( this ) );
  459. this.containers[ i ].containerCache.over = 0;
  460. }
  461. }
  462. }
  463. if ( this.placeholder ) {
  464. //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
  465. // it unbinds ALL events from the original node!
  466. if ( this.placeholder[ 0 ].parentNode ) {
  467. this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
  468. }
  469. if ( this.options.helper !== "original" && this.helper &&
  470. this.helper[ 0 ].parentNode ) {
  471. this.helper.remove();
  472. }
  473. $.extend( this, {
  474. helper: null,
  475. dragging: false,
  476. reverting: false,
  477. _noFinalSort: null
  478. } );
  479. if ( this.domPosition.prev ) {
  480. $( this.domPosition.prev ).after( this.currentItem );
  481. } else {
  482. $( this.domPosition.parent ).prepend( this.currentItem );
  483. }
  484. }
  485. return this;
  486. },
  487. serialize: function( o ) {
  488. var items = this._getItemsAsjQuery( o && o.connected ),
  489. str = [];
  490. o = o || {};
  491. $( items ).each( function() {
  492. var res = ( $( o.item || this ).attr( o.attribute || "id" ) || "" )
  493. .match( o.expression || ( /(.+)[\-=_](.+)/ ) );
  494. if ( res ) {
  495. str.push(
  496. ( o.key || res[ 1 ] + "[]" ) +
  497. "=" + ( o.key && o.expression ? res[ 1 ] : res[ 2 ] ) );
  498. }
  499. } );
  500. if ( !str.length && o.key ) {
  501. str.push( o.key + "=" );
  502. }
  503. return str.join( "&" );
  504. },
  505. toArray: function( o ) {
  506. var items = this._getItemsAsjQuery( o && o.connected ),
  507. ret = [];
  508. o = o || {};
  509. items.each( function() {
  510. ret.push( $( o.item || this ).attr( o.attribute || "id" ) || "" );
  511. } );
  512. return ret;
  513. },
  514. /* Be careful with the following core functions */
  515. _intersectsWith: function( item ) {
  516. var x1 = this.positionAbs.left,
  517. x2 = x1 + this.helperProportions.width,
  518. y1 = this.positionAbs.top,
  519. y2 = y1 + this.helperProportions.height,
  520. l = item.left,
  521. r = l + item.width,
  522. t = item.top,
  523. b = t + item.height,
  524. dyClick = this.offset.click.top,
  525. dxClick = this.offset.click.left,
  526. isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t &&
  527. ( y1 + dyClick ) < b ),
  528. isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l &&
  529. ( x1 + dxClick ) < r ),
  530. isOverElement = isOverElementHeight && isOverElementWidth;
  531. if ( this.options.tolerance === "pointer" ||
  532. this.options.forcePointerForContainers ||
  533. ( this.options.tolerance !== "pointer" &&
  534. this.helperProportions[ this.floating ? "width" : "height" ] >
  535. item[ this.floating ? "width" : "height" ] )
  536. ) {
  537. return isOverElement;
  538. } else {
  539. return ( l < x1 + ( this.helperProportions.width / 2 ) && // Right Half
  540. x2 - ( this.helperProportions.width / 2 ) < r && // Left Half
  541. t < y1 + ( this.helperProportions.height / 2 ) && // Bottom Half
  542. y2 - ( this.helperProportions.height / 2 ) < b ); // Top Half
  543. }
  544. },
  545. _intersectsWithPointer: function( item ) {
  546. var verticalDirection, horizontalDirection,
  547. isOverElementHeight = ( this.options.axis === "x" ) ||
  548. this._isOverAxis(
  549. this.positionAbs.top + this.offset.click.top, item.top, item.height ),
  550. isOverElementWidth = ( this.options.axis === "y" ) ||
  551. this._isOverAxis(
  552. this.positionAbs.left + this.offset.click.left, item.left, item.width ),
  553. isOverElement = isOverElementHeight && isOverElementWidth;
  554. if ( !isOverElement ) {
  555. return false;
  556. }
  557. verticalDirection = this.dragDirection.vertical;
  558. horizontalDirection = this.dragDirection.horizontal;
  559. return this.floating ?
  560. ( ( horizontalDirection === "right" || verticalDirection === "down" ) ? 2 : 1 ) :
  561. ( verticalDirection && ( verticalDirection === "down" ? 2 : 1 ) );
  562. },
  563. _intersectsWithSides: function( item ) {
  564. var isOverBottomHalf = this._isOverAxis( this.positionAbs.top +
  565. this.offset.click.top, item.top + ( item.height / 2 ), item.height ),
  566. isOverRightHalf = this._isOverAxis( this.positionAbs.left +
  567. this.offset.click.left, item.left + ( item.width / 2 ), item.width ),
  568. verticalDirection = this.dragDirection.vertical,
  569. horizontalDirection = this.dragDirection.horizontal;
  570. if ( this.floating && horizontalDirection ) {
  571. return ( ( horizontalDirection === "right" && isOverRightHalf ) ||
  572. ( horizontalDirection === "left" && !isOverRightHalf ) );
  573. } else {
  574. return verticalDirection && ( ( verticalDirection === "down" && isOverBottomHalf ) ||
  575. ( verticalDirection === "up" && !isOverBottomHalf ) );
  576. }
  577. },
  578. _getDragVerticalDirection: function() {
  579. var delta = this.positionAbs.top - this.lastPositionAbs.top;
  580. return delta !== 0 && ( delta > 0 ? "down" : "up" );
  581. },
  582. _getDragHorizontalDirection: function() {
  583. var delta = this.positionAbs.left - this.lastPositionAbs.left;
  584. return delta !== 0 && ( delta > 0 ? "right" : "left" );
  585. },
  586. refresh: function( event ) {
  587. this._refreshItems( event );
  588. this._setHandleClassName();
  589. this.refreshPositions();
  590. return this;
  591. },
  592. _connectWith: function() {
  593. var options = this.options;
  594. return options.connectWith.constructor === String ?
  595. [ options.connectWith ] :
  596. options.connectWith;
  597. },
  598. _getItemsAsjQuery: function( connected ) {
  599. var i, j, cur, inst,
  600. items = [],
  601. queries = [],
  602. connectWith = this._connectWith();
  603. if ( connectWith && connected ) {
  604. for ( i = connectWith.length - 1; i >= 0; i-- ) {
  605. cur = $( connectWith[ i ], this.document[ 0 ] );
  606. for ( j = cur.length - 1; j >= 0; j-- ) {
  607. inst = $.data( cur[ j ], this.widgetFullName );
  608. if ( inst && inst !== this && !inst.options.disabled ) {
  609. queries.push( [ typeof inst.options.items === "function" ?
  610. inst.options.items.call( inst.element ) :
  611. $( inst.options.items, inst.element )
  612. .not( ".ui-sortable-helper" )
  613. .not( ".ui-sortable-placeholder" ), inst ] );
  614. }
  615. }
  616. }
  617. }
  618. queries.push( [ typeof this.options.items === "function" ?
  619. this.options.items
  620. .call( this.element, null, { options: this.options, item: this.currentItem } ) :
  621. $( this.options.items, this.element )
  622. .not( ".ui-sortable-helper" )
  623. .not( ".ui-sortable-placeholder" ), this ] );
  624. function addItems() {
  625. items.push( this );
  626. }
  627. for ( i = queries.length - 1; i >= 0; i-- ) {
  628. queries[ i ][ 0 ].each( addItems );
  629. }
  630. return $( items );
  631. },
  632. _removeCurrentsFromItems: function() {
  633. var list = this.currentItem.find( ":data(" + this.widgetName + "-item)" );
  634. this.items = $.grep( this.items, function( item ) {
  635. for ( var j = 0; j < list.length; j++ ) {
  636. if ( list[ j ] === item.item[ 0 ] ) {
  637. return false;
  638. }
  639. }
  640. return true;
  641. } );
  642. },
  643. _refreshItems: function( event ) {
  644. this.items = [];
  645. this.containers = [ this ];
  646. var i, j, cur, inst, targetData, _queries, item, queriesLength,
  647. items = this.items,
  648. queries = [ [ typeof this.options.items === "function" ?
  649. this.options.items.call( this.element[ 0 ], event, { item: this.currentItem } ) :
  650. $( this.options.items, this.element ), this ] ],
  651. connectWith = this._connectWith();
  652. //Shouldn't be run the first time through due to massive slow-down
  653. if ( connectWith && this.ready ) {
  654. for ( i = connectWith.length - 1; i >= 0; i-- ) {
  655. cur = $( connectWith[ i ], this.document[ 0 ] );
  656. for ( j = cur.length - 1; j >= 0; j-- ) {
  657. inst = $.data( cur[ j ], this.widgetFullName );
  658. if ( inst && inst !== this && !inst.options.disabled ) {
  659. queries.push( [ typeof inst.options.items === "function" ?
  660. inst.options.items
  661. .call( inst.element[ 0 ], event, { item: this.currentItem } ) :
  662. $( inst.options.items, inst.element ), inst ] );
  663. this.containers.push( inst );
  664. }
  665. }
  666. }
  667. }
  668. for ( i = queries.length - 1; i >= 0; i-- ) {
  669. targetData = queries[ i ][ 1 ];
  670. _queries = queries[ i ][ 0 ];
  671. for ( j = 0, queriesLength = _queries.length; j < queriesLength; j++ ) {
  672. item = $( _queries[ j ] );
  673. // Data for target checking (mouse manager)
  674. item.data( this.widgetName + "-item", targetData );
  675. items.push( {
  676. item: item,
  677. instance: targetData,
  678. width: 0, height: 0,
  679. left: 0, top: 0
  680. } );
  681. }
  682. }
  683. },
  684. _refreshItemPositions: function( fast ) {
  685. var i, item, t, p;
  686. for ( i = this.items.length - 1; i >= 0; i-- ) {
  687. item = this.items[ i ];
  688. //We ignore calculating positions of all connected containers when we're not over them
  689. if ( this.currentContainer && item.instance !== this.currentContainer &&
  690. item.item[ 0 ] !== this.currentItem[ 0 ] ) {
  691. continue;
  692. }
  693. t = this.options.toleranceElement ?
  694. $( this.options.toleranceElement, item.item ) :
  695. item.item;
  696. if ( !fast ) {
  697. item.width = t.outerWidth();
  698. item.height = t.outerHeight();
  699. }
  700. p = t.offset();
  701. item.left = p.left;
  702. item.top = p.top;
  703. }
  704. },
  705. refreshPositions: function( fast ) {
  706. // Determine whether items are being displayed horizontally
  707. this.floating = this.items.length ?
  708. this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) :
  709. false;
  710. // This has to be redone because due to the item being moved out/into the offsetParent,
  711. // the offsetParent's position will change
  712. if ( this.offsetParent && this.helper ) {
  713. this.offset.parent = this._getParentOffset();
  714. }
  715. this._refreshItemPositions( fast );
  716. var i, p;
  717. if ( this.options.custom && this.options.custom.refreshContainers ) {
  718. this.options.custom.refreshContainers.call( this );
  719. } else {
  720. for ( i = this.containers.length - 1; i >= 0; i-- ) {
  721. p = this.containers[ i ].element.offset();
  722. this.containers[ i ].containerCache.left = p.left;
  723. this.containers[ i ].containerCache.top = p.top;
  724. this.containers[ i ].containerCache.width =
  725. this.containers[ i ].element.outerWidth();
  726. this.containers[ i ].containerCache.height =
  727. this.containers[ i ].element.outerHeight();
  728. }
  729. }
  730. return this;
  731. },
  732. _createPlaceholder: function( that ) {
  733. that = that || this;
  734. var className, nodeName,
  735. o = that.options;
  736. if ( !o.placeholder || o.placeholder.constructor === String ) {
  737. className = o.placeholder;
  738. nodeName = that.currentItem[ 0 ].nodeName.toLowerCase();
  739. o.placeholder = {
  740. element: function() {
  741. var element = $( "<" + nodeName + ">", that.document[ 0 ] );
  742. that._addClass( element, "ui-sortable-placeholder",
  743. className || that.currentItem[ 0 ].className )
  744. ._removeClass( element, "ui-sortable-helper" );
  745. if ( nodeName === "tbody" ) {
  746. that._createTrPlaceholder(
  747. that.currentItem.find( "tr" ).eq( 0 ),
  748. $( "<tr>", that.document[ 0 ] ).appendTo( element )
  749. );
  750. } else if ( nodeName === "tr" ) {
  751. that._createTrPlaceholder( that.currentItem, element );
  752. } else if ( nodeName === "img" ) {
  753. element.attr( "src", that.currentItem.attr( "src" ) );
  754. }
  755. if ( !className ) {
  756. element.css( "visibility", "hidden" );
  757. }
  758. return element;
  759. },
  760. update: function( container, p ) {
  761. // 1. If a className is set as 'placeholder option, we don't force sizes -
  762. // the class is responsible for that
  763. // 2. The option 'forcePlaceholderSize can be enabled to force it even if a
  764. // class name is specified
  765. if ( className && !o.forcePlaceholderSize ) {
  766. return;
  767. }
  768. // If the element doesn't have a actual height or width by itself (without
  769. // styles coming from a stylesheet), it receives the inline height and width
  770. // from the dragged item. Or, if it's a tbody or tr, it's going to have a height
  771. // anyway since we're populating them with <td>s above, but they're unlikely to
  772. // be the correct height on their own if the row heights are dynamic, so we'll
  773. // always assign the height of the dragged item given forcePlaceholderSize
  774. // is true.
  775. if ( !p.height() || ( o.forcePlaceholderSize &&
  776. ( nodeName === "tbody" || nodeName === "tr" ) ) ) {
  777. p.height(
  778. that.currentItem.innerHeight() -
  779. parseInt( that.currentItem.css( "paddingTop" ) || 0, 10 ) -
  780. parseInt( that.currentItem.css( "paddingBottom" ) || 0, 10 ) );
  781. }
  782. if ( !p.width() ) {
  783. p.width(
  784. that.currentItem.innerWidth() -
  785. parseInt( that.currentItem.css( "paddingLeft" ) || 0, 10 ) -
  786. parseInt( that.currentItem.css( "paddingRight" ) || 0, 10 ) );
  787. }
  788. }
  789. };
  790. }
  791. //Create the placeholder
  792. that.placeholder = $( o.placeholder.element.call( that.element, that.currentItem ) );
  793. //Append it after the actual current item
  794. that.currentItem.after( that.placeholder );
  795. //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
  796. o.placeholder.update( that, that.placeholder );
  797. },
  798. _createTrPlaceholder: function( sourceTr, targetTr ) {
  799. var that = this;
  800. sourceTr.children().each( function() {
  801. $( "<td>&#160;</td>", that.document[ 0 ] )
  802. .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
  803. .appendTo( targetTr );
  804. } );
  805. },
  806. _contactContainers: function( event ) {
  807. var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom,
  808. floating, axis,
  809. innermostContainer = null,
  810. innermostIndex = null;
  811. // Get innermost container that intersects with item
  812. for ( i = this.containers.length - 1; i >= 0; i-- ) {
  813. // Never consider a container that's located within the item itself
  814. if ( $.contains( this.currentItem[ 0 ], this.containers[ i ].element[ 0 ] ) ) {
  815. continue;
  816. }
  817. if ( this._intersectsWith( this.containers[ i ].containerCache ) ) {
  818. // If we've already found a container and it's more "inner" than this, then continue
  819. if ( innermostContainer &&
  820. $.contains(
  821. this.containers[ i ].element[ 0 ],
  822. innermostContainer.element[ 0 ] ) ) {
  823. continue;
  824. }
  825. innermostContainer = this.containers[ i ];
  826. innermostIndex = i;
  827. } else {
  828. // container doesn't intersect. trigger "out" event if necessary
  829. if ( this.containers[ i ].containerCache.over ) {
  830. this.containers[ i ]._trigger( "out", event, this._uiHash( this ) );
  831. this.containers[ i ].containerCache.over = 0;
  832. }
  833. }
  834. }
  835. // If no intersecting containers found, return
  836. if ( !innermostContainer ) {
  837. return;
  838. }
  839. // Move the item into the container if it's not there already
  840. if ( this.containers.length === 1 ) {
  841. if ( !this.containers[ innermostIndex ].containerCache.over ) {
  842. this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
  843. this.containers[ innermostIndex ].containerCache.over = 1;
  844. }
  845. } else {
  846. // When entering a new container, we will find the item with the least distance and
  847. // append our item near it
  848. dist = 10000;
  849. itemWithLeastDistance = null;
  850. floating = innermostContainer.floating || this._isFloating( this.currentItem );
  851. posProperty = floating ? "left" : "top";
  852. sizeProperty = floating ? "width" : "height";
  853. axis = floating ? "pageX" : "pageY";
  854. for ( j = this.items.length - 1; j >= 0; j-- ) {
  855. if ( !$.contains(
  856. this.containers[ innermostIndex ].element[ 0 ], this.items[ j ].item[ 0 ] )
  857. ) {
  858. continue;
  859. }
  860. if ( this.items[ j ].item[ 0 ] === this.currentItem[ 0 ] ) {
  861. continue;
  862. }
  863. cur = this.items[ j ].item.offset()[ posProperty ];
  864. nearBottom = false;
  865. if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) {
  866. nearBottom = true;
  867. }
  868. if ( Math.abs( event[ axis ] - cur ) < dist ) {
  869. dist = Math.abs( event[ axis ] - cur );
  870. itemWithLeastDistance = this.items[ j ];
  871. this.direction = nearBottom ? "up" : "down";
  872. }
  873. }
  874. //Check if dropOnEmpty is enabled
  875. if ( !itemWithLeastDistance && !this.options.dropOnEmpty ) {
  876. return;
  877. }
  878. if ( this.currentContainer === this.containers[ innermostIndex ] ) {
  879. if ( !this.currentContainer.containerCache.over ) {
  880. this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() );
  881. this.currentContainer.containerCache.over = 1;
  882. }
  883. return;
  884. }
  885. if ( itemWithLeastDistance ) {
  886. this._rearrange( event, itemWithLeastDistance, null, true );
  887. } else {
  888. this._rearrange( event, null, this.containers[ innermostIndex ].element, true );
  889. }
  890. this._trigger( "change", event, this._uiHash() );
  891. this.containers[ innermostIndex ]._trigger( "change", event, this._uiHash( this ) );
  892. this.currentContainer = this.containers[ innermostIndex ];
  893. //Update the placeholder
  894. this.options.placeholder.update( this.currentContainer, this.placeholder );
  895. //Update scrollParent
  896. this.scrollParent = this.placeholder.scrollParent();
  897. //Update overflowOffset
  898. if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
  899. this.scrollParent[ 0 ].tagName !== "HTML" ) {
  900. this.overflowOffset = this.scrollParent.offset();
  901. }
  902. this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
  903. this.containers[ innermostIndex ].containerCache.over = 1;
  904. }
  905. },
  906. _createHelper: function( event ) {
  907. var o = this.options,
  908. helper = typeof o.helper === "function" ?
  909. $( o.helper.apply( this.element[ 0 ], [ event, this.currentItem ] ) ) :
  910. ( o.helper === "clone" ? this.currentItem.clone() : this.currentItem );
  911. //Add the helper to the DOM if that didn't happen already
  912. if ( !helper.parents( "body" ).length ) {
  913. this.appendTo[ 0 ].appendChild( helper[ 0 ] );
  914. }
  915. if ( helper[ 0 ] === this.currentItem[ 0 ] ) {
  916. this._storedCSS = {
  917. width: this.currentItem[ 0 ].style.width,
  918. height: this.currentItem[ 0 ].style.height,
  919. position: this.currentItem.css( "position" ),
  920. top: this.currentItem.css( "top" ),
  921. left: this.currentItem.css( "left" )
  922. };
  923. }
  924. if ( !helper[ 0 ].style.width || o.forceHelperSize ) {
  925. helper.width( this.currentItem.width() );
  926. }
  927. if ( !helper[ 0 ].style.height || o.forceHelperSize ) {
  928. helper.height( this.currentItem.height() );
  929. }
  930. return helper;
  931. },
  932. _adjustOffsetFromHelper: function( obj ) {
  933. if ( typeof obj === "string" ) {
  934. obj = obj.split( " " );
  935. }
  936. if ( Array.isArray( obj ) ) {
  937. obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
  938. }
  939. if ( "left" in obj ) {
  940. this.offset.click.left = obj.left + this.margins.left;
  941. }
  942. if ( "right" in obj ) {
  943. this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
  944. }
  945. if ( "top" in obj ) {
  946. this.offset.click.top = obj.top + this.margins.top;
  947. }
  948. if ( "bottom" in obj ) {
  949. this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
  950. }
  951. },
  952. _getParentOffset: function() {
  953. //Get the offsetParent and cache its position
  954. this.offsetParent = this.helper.offsetParent();
  955. var po = this.offsetParent.offset();
  956. // This is a special case where we need to modify a offset calculated on start, since the
  957. // following happened:
  958. // 1. The position of the helper is absolute, so it's position is calculated based on the
  959. // next positioned parent
  960. // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
  961. // the document, which means that the scroll is included in the initial calculation of the
  962. // offset of the parent, and never recalculated upon drag
  963. if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== this.document[ 0 ] &&
  964. $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
  965. po.left += this.scrollParent.scrollLeft();
  966. po.top += this.scrollParent.scrollTop();
  967. }
  968. // This needs to be actually done for all browsers, since pageX/pageY includes this
  969. // information with an ugly IE fix
  970. if ( this.offsetParent[ 0 ] === this.document[ 0 ].body ||
  971. ( this.offsetParent[ 0 ].tagName &&
  972. this.offsetParent[ 0 ].tagName.toLowerCase() === "html" && $.ui.ie ) ) {
  973. po = { top: 0, left: 0 };
  974. }
  975. return {
  976. top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
  977. left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
  978. };
  979. },
  980. _getRelativeOffset: function() {
  981. if ( this.cssPosition === "relative" ) {
  982. var p = this.currentItem.position();
  983. return {
  984. top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
  985. this.scrollParent.scrollTop(),
  986. left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
  987. this.scrollParent.scrollLeft()
  988. };
  989. } else {
  990. return { top: 0, left: 0 };
  991. }
  992. },
  993. _cacheMargins: function() {
  994. this.margins = {
  995. left: ( parseInt( this.currentItem.css( "marginLeft" ), 10 ) || 0 ),
  996. top: ( parseInt( this.currentItem.css( "marginTop" ), 10 ) || 0 )
  997. };
  998. },
  999. _cacheHelperProportions: function() {
  1000. this.helperProportions = {
  1001. width: this.helper.outerWidth(),
  1002. height: this.helper.outerHeight()
  1003. };
  1004. },
  1005. _setContainment: function() {
  1006. var ce, co, over,
  1007. o = this.options;
  1008. if ( o.containment === "parent" ) {
  1009. o.containment = this.helper[ 0 ].parentNode;
  1010. }
  1011. if ( o.containment === "document" || o.containment === "window" ) {
  1012. this.containment = [
  1013. 0 - this.offset.relative.left - this.offset.parent.left,
  1014. 0 - this.offset.relative.top - this.offset.parent.top,
  1015. o.containment === "document" ?
  1016. this.document.width() :
  1017. this.window.width() - this.helperProportions.width - this.margins.left,
  1018. ( o.containment === "document" ?
  1019. ( this.document.height() || document.body.parentNode.scrollHeight ) :
  1020. this.window.height() || this.document[ 0 ].body.parentNode.scrollHeight
  1021. ) - this.helperProportions.height - this.margins.top
  1022. ];
  1023. }
  1024. if ( !( /^(document|window|parent)$/ ).test( o.containment ) ) {
  1025. ce = $( o.containment )[ 0 ];
  1026. co = $( o.containment ).offset();
  1027. over = ( $( ce ).css( "overflow" ) !== "hidden" );
  1028. this.containment = [
  1029. co.left + ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) +
  1030. ( parseInt( $( ce ).css( "paddingLeft" ), 10 ) || 0 ) - this.margins.left,
  1031. co.top + ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) +
  1032. ( parseInt( $( ce ).css( "paddingTop" ), 10 ) || 0 ) - this.margins.top,
  1033. co.left + ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
  1034. ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) -
  1035. ( parseInt( $( ce ).css( "paddingRight" ), 10 ) || 0 ) -
  1036. this.helperProportions.width - this.margins.left,
  1037. co.top + ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
  1038. ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) -
  1039. ( parseInt( $( ce ).css( "paddingBottom" ), 10 ) || 0 ) -
  1040. this.helperProportions.height - this.margins.top
  1041. ];
  1042. }
  1043. },
  1044. _convertPositionTo: function( d, pos ) {
  1045. if ( !pos ) {
  1046. pos = this.position;
  1047. }
  1048. var mod = d === "absolute" ? 1 : -1,
  1049. scroll = this.cssPosition === "absolute" &&
  1050. !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
  1051. $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
  1052. this.offsetParent :
  1053. this.scrollParent,
  1054. scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
  1055. return {
  1056. top: (
  1057. // The absolute mouse position
  1058. pos.top +
  1059. // Only for relative positioned nodes: Relative offset from element to offset parent
  1060. this.offset.relative.top * mod +
  1061. // The offsetParent's offset without borders (offset + border)
  1062. this.offset.parent.top * mod -
  1063. ( ( this.cssPosition === "fixed" ?
  1064. -this.scrollParent.scrollTop() :
  1065. ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod )
  1066. ),
  1067. left: (
  1068. // The absolute mouse position
  1069. pos.left +
  1070. // Only for relative positioned nodes: Relative offset from element to offset parent
  1071. this.offset.relative.left * mod +
  1072. // The offsetParent's offset without borders (offset + border)
  1073. this.offset.parent.left * mod -
  1074. ( ( this.cssPosition === "fixed" ?
  1075. -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 :
  1076. scroll.scrollLeft() ) * mod )
  1077. )
  1078. };
  1079. },
  1080. _generatePosition: function( event ) {
  1081. var top, left,
  1082. o = this.options,
  1083. pageX = event.pageX,
  1084. pageY = event.pageY,
  1085. scroll = this.cssPosition === "absolute" &&
  1086. !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
  1087. $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
  1088. this.offsetParent :
  1089. this.scrollParent,
  1090. scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
  1091. // This is another very weird special case that only happens for relative elements:
  1092. // 1. If the css position is relative
  1093. // 2. and the scroll parent is the document or similar to the offset parent
  1094. // we have to refresh the relative offset during the scroll so there are no jumps
  1095. if ( this.cssPosition === "relative" && !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
  1096. this.scrollParent[ 0 ] !== this.offsetParent[ 0 ] ) ) {
  1097. this.offset.relative = this._getRelativeOffset();
  1098. }
  1099. /*
  1100. * - Position constraining -
  1101. * Constrain the position to a mix of grid, containment.
  1102. */
  1103. if ( this.originalPosition ) { //If we are not dragging yet, we won't check for options
  1104. if ( this.containment ) {
  1105. if ( event.pageX - this.offset.click.left < this.containment[ 0 ] ) {
  1106. pageX = this.containment[ 0 ] + this.offset.click.left;
  1107. }
  1108. if ( event.pageY - this.offset.click.top < this.containment[ 1 ] ) {
  1109. pageY = this.containment[ 1 ] + this.offset.click.top;
  1110. }
  1111. if ( event.pageX - this.offset.click.left > this.containment[ 2 ] ) {
  1112. pageX = this.containment[ 2 ] + this.offset.click.left;
  1113. }
  1114. if ( event.pageY - this.offset.click.top > this.containment[ 3 ] ) {
  1115. pageY = this.containment[ 3 ] + this.offset.click.top;
  1116. }
  1117. }
  1118. if ( o.grid ) {
  1119. top = this.originalPageY + Math.round( ( pageY - this.originalPageY ) /
  1120. o.grid[ 1 ] ) * o.grid[ 1 ];
  1121. pageY = this.containment ?
  1122. ( ( top - this.offset.click.top >= this.containment[ 1 ] &&
  1123. top - this.offset.click.top <= this.containment[ 3 ] ) ?
  1124. top :
  1125. ( ( top - this.offset.click.top >= this.containment[ 1 ] ) ?
  1126. top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) :
  1127. top;
  1128. left = this.originalPageX + Math.round( ( pageX - this.originalPageX ) /
  1129. o.grid[ 0 ] ) * o.grid[ 0 ];
  1130. pageX = this.containment ?
  1131. ( ( left - this.offset.click.left >= this.containment[ 0 ] &&
  1132. left - this.offset.click.left <= this.containment[ 2 ] ) ?
  1133. left :
  1134. ( ( left - this.offset.click.left >= this.containment[ 0 ] ) ?
  1135. left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) :
  1136. left;
  1137. }
  1138. }
  1139. return {
  1140. top: (
  1141. // The absolute mouse position
  1142. pageY -
  1143. // Click offset (relative to the element)
  1144. this.offset.click.top -
  1145. // Only for relative positioned nodes: Relative offset from element to offset parent
  1146. this.offset.relative.top -
  1147. // The offsetParent's offset without borders (offset + border)
  1148. this.offset.parent.top +
  1149. ( ( this.cssPosition === "fixed" ?
  1150. -this.scrollParent.scrollTop() :
  1151. ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) )
  1152. ),
  1153. left: (
  1154. // The absolute mouse position
  1155. pageX -
  1156. // Click offset (relative to the element)
  1157. this.offset.click.left -
  1158. // Only for relative positioned nodes: Relative offset from element to offset parent
  1159. this.offset.relative.left -
  1160. // The offsetParent's offset without borders (offset + border)
  1161. this.offset.parent.left +
  1162. ( ( this.cssPosition === "fixed" ?
  1163. -this.scrollParent.scrollLeft() :
  1164. scrollIsRootNode ? 0 : scroll.scrollLeft() ) )
  1165. )
  1166. };
  1167. },
  1168. _rearrange: function( event, i, a, hardRefresh ) {
  1169. if ( a ) {
  1170. a[ 0 ].appendChild( this.placeholder[ 0 ] );
  1171. } else {
  1172. i.item[ 0 ].parentNode.insertBefore( this.placeholder[ 0 ],
  1173. ( this.direction === "down" ? i.item[ 0 ] : i.item[ 0 ].nextSibling ) );
  1174. }
  1175. //Various things done here to improve the performance:
  1176. // 1. we create a setTimeout, that calls refreshPositions
  1177. // 2. on the instance, we have a counter variable, that get's higher after every append
  1178. // 3. on the local scope, we copy the counter variable, and check in the timeout,
  1179. // if it's still the same
  1180. // 4. this lets only the last addition to the timeout stack through
  1181. this.counter = this.counter ? ++this.counter : 1;
  1182. var counter = this.counter;
  1183. this._delay( function() {
  1184. if ( counter === this.counter ) {
  1185. //Precompute after each DOM insertion, NOT on mousemove
  1186. this.refreshPositions( !hardRefresh );
  1187. }
  1188. } );
  1189. },
  1190. _clear: function( event, noPropagation ) {
  1191. this.reverting = false;
  1192. // We delay all events that have to be triggered to after the point where the placeholder
  1193. // has been removed and everything else normalized again
  1194. var i,
  1195. delayedTriggers = [];
  1196. // We first have to update the dom position of the actual currentItem
  1197. // Note: don't do it if the current item is already removed (by a user), or it gets
  1198. // reappended (see #4088)
  1199. if ( !this._noFinalSort && this.currentItem.parent().length ) {
  1200. this.placeholder.before( this.currentItem );
  1201. }
  1202. this._noFinalSort = null;
  1203. if ( this.helper[ 0 ] === this.currentItem[ 0 ] ) {
  1204. for ( i in this._storedCSS ) {
  1205. if ( this._storedCSS[ i ] === "auto" || this._storedCSS[ i ] === "static" ) {
  1206. this._storedCSS[ i ] = "";
  1207. }
  1208. }
  1209. this.currentItem.css( this._storedCSS );
  1210. this._removeClass( this.currentItem, "ui-sortable-helper" );
  1211. } else {
  1212. this.currentItem.show();
  1213. }
  1214. if ( this.fromOutside && !noPropagation ) {
  1215. delayedTriggers.push( function( event ) {
  1216. this._trigger( "receive", event, this._uiHash( this.fromOutside ) );
  1217. } );
  1218. }
  1219. if ( ( this.fromOutside ||
  1220. this.domPosition.prev !==
  1221. this.currentItem.prev().not( ".ui-sortable-helper" )[ 0 ] ||
  1222. this.domPosition.parent !== this.currentItem.parent()[ 0 ] ) && !noPropagation ) {
  1223. // Trigger update callback if the DOM position has changed
  1224. delayedTriggers.push( function( event ) {
  1225. this._trigger( "update", event, this._uiHash() );
  1226. } );
  1227. }
  1228. // Check if the items Container has Changed and trigger appropriate
  1229. // events.
  1230. if ( this !== this.currentContainer ) {
  1231. if ( !noPropagation ) {
  1232. delayedTriggers.push( function( event ) {
  1233. this._trigger( "remove", event, this._uiHash() );
  1234. } );
  1235. delayedTriggers.push( ( function( c ) {
  1236. return function( event ) {
  1237. c._trigger( "receive", event, this._uiHash( this ) );
  1238. };
  1239. } ).call( this, this.currentContainer ) );
  1240. delayedTriggers.push( ( function( c ) {
  1241. return function( event ) {
  1242. c._trigger( "update", event, this._uiHash( this ) );
  1243. };
  1244. } ).call( this, this.currentContainer ) );
  1245. }
  1246. }
  1247. //Post events to containers
  1248. function delayEvent( type, instance, container ) {
  1249. return function( event ) {
  1250. container._trigger( type, event, instance._uiHash( instance ) );
  1251. };
  1252. }
  1253. for ( i = this.containers.length - 1; i >= 0; i-- ) {
  1254. if ( !noPropagation ) {
  1255. delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
  1256. }
  1257. if ( this.containers[ i ].containerCache.over ) {
  1258. delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
  1259. this.containers[ i ].containerCache.over = 0;
  1260. }
  1261. }
  1262. //Do what was originally in plugins
  1263. if ( this.storedCursor ) {
  1264. this.document.find( "body" ).css( "cursor", this.storedCursor );
  1265. this.storedStylesheet.remove();
  1266. }
  1267. if ( this._storedOpacity ) {
  1268. this.helper.css( "opacity", this._storedOpacity );
  1269. }
  1270. if ( this._storedZIndex ) {
  1271. this.helper.css( "zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex );
  1272. }
  1273. this.dragging = false;
  1274. if ( !noPropagation ) {
  1275. this._trigger( "beforeStop", event, this._uiHash() );
  1276. }
  1277. //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
  1278. // it unbinds ALL events from the original node!
  1279. this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
  1280. if ( !this.cancelHelperRemoval ) {
  1281. if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
  1282. this.helper.remove();
  1283. }
  1284. this.helper = null;
  1285. }
  1286. if ( !noPropagation ) {
  1287. for ( i = 0; i < delayedTriggers.length; i++ ) {
  1288. // Trigger all delayed events
  1289. delayedTriggers[ i ].call( this, event );
  1290. }
  1291. this._trigger( "stop", event, this._uiHash() );
  1292. }
  1293. this.fromOutside = false;
  1294. return !this.cancelHelperRemoval;
  1295. },
  1296. _trigger: function() {
  1297. if ( $.Widget.prototype._trigger.apply( this, arguments ) === false ) {
  1298. this.cancel();
  1299. }
  1300. },
  1301. _uiHash: function( _inst ) {
  1302. var inst = _inst || this;
  1303. return {
  1304. helper: inst.helper,
  1305. placeholder: inst.placeholder || $( [] ),
  1306. position: inst.position,
  1307. originalPosition: inst.originalPosition,
  1308. offset: inst.positionAbs,
  1309. item: inst.currentItem,
  1310. sender: _inst ? _inst.element : null
  1311. };
  1312. }
  1313. } );
  1314. } );