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