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


Generated : Thu Nov 21 08:20:01 2024 Cross-referenced by PHPXref