Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

1616 řádky
46 KiB

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