[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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


Generated: Sat Dec 7 01:00:02 2024 Cross-referenced by PHPXref 0.7.1