[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/jquery/ui/ -> sortable.js (source)

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


Generated : Wed Nov 25 08:20:02 2020 Cross-referenced by PHPXref