[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

   1  /*!
   2   * jQuery UI Sortable 1.13.0-rc.2
   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.0-rc.2",
  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          //Post events to containers
 421          this._contactContainers( event );
 422  
 423          if ( this.innermostContainer !== null ) {
 424  
 425              //Do scrolling
 426              if ( o.scroll ) {
 427                  if ( this._scroll( event ) !== false ) {
 428  
 429                      //Update item positions used in position checks
 430                      this._refreshItemPositions( true );
 431  
 432                      if ( $.ui.ddmanager && !o.dropBehaviour ) {
 433                          $.ui.ddmanager.prepareOffsets( this, event );
 434                      }
 435                  }
 436              }
 437  
 438              this.dragDirection = {
 439                  vertical: this._getDragVerticalDirection(),
 440                  horizontal: this._getDragHorizontalDirection()
 441              };
 442  
 443              //Rearrange
 444              for ( i = this.items.length - 1; i >= 0; i-- ) {
 445  
 446                  //Cache variables and intersection, continue if no intersection
 447                  item = this.items[ i ];
 448                  itemElement = item.item[ 0 ];
 449                  intersection = this._intersectsWithPointer( item );
 450                  if ( !intersection ) {
 451                      continue;
 452                  }
 453  
 454                  // Only put the placeholder inside the current Container, skip all
 455                  // items from other containers. This works because when moving
 456                  // an item from one container to another the
 457                  // currentContainer is switched before the placeholder is moved.
 458                  //
 459                  // Without this, moving items in "sub-sortables" can cause
 460                  // the placeholder to jitter between the outer and inner container.
 461                  if ( item.instance !== this.currentContainer ) {
 462                      continue;
 463                  }
 464  
 465                  // Cannot intersect with itself
 466                  // no useless actions that have been done before
 467                  // no action if the item moved is the parent of the item checked
 468                  if ( itemElement !== this.currentItem[ 0 ] &&
 469                      this.placeholder[ intersection === 1 ?
 470                      "next" : "prev" ]()[ 0 ] !== itemElement &&
 471                      !$.contains( this.placeholder[ 0 ], itemElement ) &&
 472                      ( this.options.type === "semi-dynamic" ?
 473                          !$.contains( this.element[ 0 ], itemElement ) :
 474                          true
 475                      )
 476                  ) {
 477  
 478                      this.direction = intersection === 1 ? "down" : "up";
 479  
 480                      if ( this.options.tolerance === "pointer" ||
 481                              this._intersectsWithSides( item ) ) {
 482                          this._rearrange( event, item );
 483                      } else {
 484                          break;
 485                      }
 486  
 487                      this._trigger( "change", event, this._uiHash() );
 488                      break;
 489                  }
 490              }
 491          }
 492  
 493          //Interconnect with droppables
 494          if ( $.ui.ddmanager ) {
 495              $.ui.ddmanager.drag( this, event );
 496          }
 497  
 498          //Call callbacks
 499          this._trigger( "sort", event, this._uiHash() );
 500  
 501          this.lastPositionAbs = this.positionAbs;
 502          return false;
 503  
 504      },
 505  
 506      _mouseStop: function( event, noPropagation ) {
 507  
 508          if ( !event ) {
 509              return;
 510          }
 511  
 512          //If we are using droppables, inform the manager about the drop
 513          if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
 514              $.ui.ddmanager.drop( this, event );
 515          }
 516  
 517          if ( this.options.revert ) {
 518              var that = this,
 519                  cur = this.placeholder.offset(),
 520                  axis = this.options.axis,
 521                  animation = {};
 522  
 523              if ( !axis || axis === "x" ) {
 524                  animation.left = cur.left - this.offset.parent.left - this.margins.left +
 525                      ( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
 526                          0 :
 527                          this.offsetParent[ 0 ].scrollLeft
 528                      );
 529              }
 530              if ( !axis || axis === "y" ) {
 531                  animation.top = cur.top - this.offset.parent.top - this.margins.top +
 532                      ( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
 533                          0 :
 534                          this.offsetParent[ 0 ].scrollTop
 535                      );
 536              }
 537              this.reverting = true;
 538              $( this.helper ).animate(
 539                  animation,
 540                  parseInt( this.options.revert, 10 ) || 500,
 541                  function() {
 542                      that._clear( event );
 543                  }
 544              );
 545          } else {
 546              this._clear( event, noPropagation );
 547          }
 548  
 549          return false;
 550  
 551      },
 552  
 553      cancel: function() {
 554  
 555          if ( this.dragging ) {
 556  
 557              this._mouseUp( new $.Event( "mouseup", { target: null } ) );
 558  
 559              if ( this.options.helper === "original" ) {
 560                  this.currentItem.css( this._storedCSS );
 561                  this._removeClass( this.currentItem, "ui-sortable-helper" );
 562              } else {
 563                  this.currentItem.show();
 564              }
 565  
 566              //Post deactivating events to containers
 567              for ( var i = this.containers.length - 1; i >= 0; i-- ) {
 568                  this.containers[ i ]._trigger( "deactivate", null, this._uiHash( this ) );
 569                  if ( this.containers[ i ].containerCache.over ) {
 570                      this.containers[ i ]._trigger( "out", null, this._uiHash( this ) );
 571                      this.containers[ i ].containerCache.over = 0;
 572                  }
 573              }
 574  
 575          }
 576  
 577          if ( this.placeholder ) {
 578  
 579              //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
 580              // it unbinds ALL events from the original node!
 581              if ( this.placeholder[ 0 ].parentNode ) {
 582                  this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
 583              }
 584              if ( this.options.helper !== "original" && this.helper &&
 585                      this.helper[ 0 ].parentNode ) {
 586                  this.helper.remove();
 587              }
 588  
 589              $.extend( this, {
 590                  helper: null,
 591                  dragging: false,
 592                  reverting: false,
 593                  _noFinalSort: null
 594              } );
 595  
 596              if ( this.domPosition.prev ) {
 597                  $( this.domPosition.prev ).after( this.currentItem );
 598              } else {
 599                  $( this.domPosition.parent ).prepend( this.currentItem );
 600              }
 601          }
 602  
 603          return this;
 604  
 605      },
 606  
 607      serialize: function( o ) {
 608  
 609          var items = this._getItemsAsjQuery( o && o.connected ),
 610              str = [];
 611          o = o || {};
 612  
 613          $( items ).each( function() {
 614              var res = ( $( o.item || this ).attr( o.attribute || "id" ) || "" )
 615                  .match( o.expression || ( /(.+)[\-=_](.+)/ ) );
 616              if ( res ) {
 617                  str.push(
 618                      ( o.key || res[ 1 ] + "[]" ) +
 619                      "=" + ( o.key && o.expression ? res[ 1 ] : res[ 2 ] ) );
 620              }
 621          } );
 622  
 623          if ( !str.length && o.key ) {
 624              str.push( o.key + "=" );
 625          }
 626  
 627          return str.join( "&" );
 628  
 629      },
 630  
 631      toArray: function( o ) {
 632  
 633          var items = this._getItemsAsjQuery( o && o.connected ),
 634              ret = [];
 635  
 636          o = o || {};
 637  
 638          items.each( function() {
 639              ret.push( $( o.item || this ).attr( o.attribute || "id" ) || "" );
 640          } );
 641          return ret;
 642  
 643      },
 644  
 645      /* Be careful with the following core functions */
 646      _intersectsWith: function( item ) {
 647  
 648          var x1 = this.positionAbs.left,
 649              x2 = x1 + this.helperProportions.width,
 650              y1 = this.positionAbs.top,
 651              y2 = y1 + this.helperProportions.height,
 652              l = item.left,
 653              r = l + item.width,
 654              t = item.top,
 655              b = t + item.height,
 656              dyClick = this.offset.click.top,
 657              dxClick = this.offset.click.left,
 658              isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t &&
 659                  ( y1 + dyClick ) < b ),
 660              isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l &&
 661                  ( x1 + dxClick ) < r ),
 662              isOverElement = isOverElementHeight && isOverElementWidth;
 663  
 664          if ( this.options.tolerance === "pointer" ||
 665              this.options.forcePointerForContainers ||
 666              ( this.options.tolerance !== "pointer" &&
 667                  this.helperProportions[ this.floating ? "width" : "height" ] >
 668                  item[ this.floating ? "width" : "height" ] )
 669          ) {
 670              return isOverElement;
 671          } else {
 672  
 673              return ( l < x1 + ( this.helperProportions.width / 2 ) && // Right Half
 674                  x2 - ( this.helperProportions.width / 2 ) < r && // Left Half
 675                  t < y1 + ( this.helperProportions.height / 2 ) && // Bottom Half
 676                  y2 - ( this.helperProportions.height / 2 ) < b ); // Top Half
 677  
 678          }
 679      },
 680  
 681      _intersectsWithPointer: function( item ) {
 682          var verticalDirection, horizontalDirection,
 683              isOverElementHeight = ( this.options.axis === "x" ) ||
 684                  this._isOverAxis(
 685                      this.positionAbs.top + this.offset.click.top, item.top, item.height ),
 686              isOverElementWidth = ( this.options.axis === "y" ) ||
 687                  this._isOverAxis(
 688                      this.positionAbs.left + this.offset.click.left, item.left, item.width ),
 689              isOverElement = isOverElementHeight && isOverElementWidth;
 690  
 691          if ( !isOverElement ) {
 692              return false;
 693          }
 694  
 695          verticalDirection = this.dragDirection.vertical;
 696          horizontalDirection = this.dragDirection.horizontal;
 697  
 698          return this.floating ?
 699              ( ( horizontalDirection === "right" || verticalDirection === "down" ) ? 2 : 1 ) :
 700              ( verticalDirection && ( verticalDirection === "down" ? 2 : 1 ) );
 701  
 702      },
 703  
 704      _intersectsWithSides: function( item ) {
 705  
 706          var isOverBottomHalf = this._isOverAxis( this.positionAbs.top +
 707                  this.offset.click.top, item.top + ( item.height / 2 ), item.height ),
 708              isOverRightHalf = this._isOverAxis( this.positionAbs.left +
 709                  this.offset.click.left, item.left + ( item.width / 2 ), item.width ),
 710              verticalDirection = this.dragDirection.vertical,
 711              horizontalDirection = this.dragDirection.horizontal;
 712  
 713          if ( this.floating && horizontalDirection ) {
 714              return ( ( horizontalDirection === "right" && isOverRightHalf ) ||
 715                  ( horizontalDirection === "left" && !isOverRightHalf ) );
 716          } else {
 717              return verticalDirection && ( ( verticalDirection === "down" && isOverBottomHalf ) ||
 718                  ( verticalDirection === "up" && !isOverBottomHalf ) );
 719          }
 720  
 721      },
 722  
 723      _getDragVerticalDirection: function() {
 724          var delta = this.positionAbs.top - this.lastPositionAbs.top;
 725          return delta !== 0 && ( delta > 0 ? "down" : "up" );
 726      },
 727  
 728      _getDragHorizontalDirection: function() {
 729          var delta = this.positionAbs.left - this.lastPositionAbs.left;
 730          return delta !== 0 && ( delta > 0 ? "right" : "left" );
 731      },
 732  
 733      refresh: function( event ) {
 734          this._refreshItems( event );
 735          this._setHandleClassName();
 736          this.refreshPositions();
 737          return this;
 738      },
 739  
 740      _connectWith: function() {
 741          var options = this.options;
 742          return options.connectWith.constructor === String ?
 743              [ options.connectWith ] :
 744              options.connectWith;
 745      },
 746  
 747      _getItemsAsjQuery: function( connected ) {
 748  
 749          var i, j, cur, inst,
 750              items = [],
 751              queries = [],
 752              connectWith = this._connectWith();
 753  
 754          if ( connectWith && connected ) {
 755              for ( i = connectWith.length - 1; i >= 0; i-- ) {
 756                  cur = $( connectWith[ i ], this.document[ 0 ] );
 757                  for ( j = cur.length - 1; j >= 0; j-- ) {
 758                      inst = $.data( cur[ j ], this.widgetFullName );
 759                      if ( inst && inst !== this && !inst.options.disabled ) {
 760                          queries.push( [ typeof inst.options.items === "function" ?
 761                              inst.options.items.call( inst.element ) :
 762                              $( inst.options.items, inst.element )
 763                                  .not( ".ui-sortable-helper" )
 764                                  .not( ".ui-sortable-placeholder" ), inst ] );
 765                      }
 766                  }
 767              }
 768          }
 769  
 770          queries.push( [ typeof this.options.items === "function" ?
 771              this.options.items
 772                  .call( this.element, null, { options: this.options, item: this.currentItem } ) :
 773              $( this.options.items, this.element )
 774                  .not( ".ui-sortable-helper" )
 775                  .not( ".ui-sortable-placeholder" ), this ] );
 776  
 777  		function addItems() {
 778              items.push( this );
 779          }
 780          for ( i = queries.length - 1; i >= 0; i-- ) {
 781              queries[ i ][ 0 ].each( addItems );
 782          }
 783  
 784          return $( items );
 785  
 786      },
 787  
 788      _removeCurrentsFromItems: function() {
 789  
 790          var list = this.currentItem.find( ":data(" + this.widgetName + "-item)" );
 791  
 792          this.items = $.grep( this.items, function( item ) {
 793              for ( var j = 0; j < list.length; j++ ) {
 794                  if ( list[ j ] === item.item[ 0 ] ) {
 795                      return false;
 796                  }
 797              }
 798              return true;
 799          } );
 800  
 801      },
 802  
 803      _refreshItems: function( event ) {
 804  
 805          this.items = [];
 806          this.containers = [ this ];
 807  
 808          var i, j, cur, inst, targetData, _queries, item, queriesLength,
 809              items = this.items,
 810              queries = [ [ typeof this.options.items === "function" ?
 811                  this.options.items.call( this.element[ 0 ], event, { item: this.currentItem } ) :
 812                  $( this.options.items, this.element ), this ] ],
 813              connectWith = this._connectWith();
 814  
 815          //Shouldn't be run the first time through due to massive slow-down
 816          if ( connectWith && this.ready ) {
 817              for ( i = connectWith.length - 1; i >= 0; i-- ) {
 818                  cur = $( connectWith[ i ], this.document[ 0 ] );
 819                  for ( j = cur.length - 1; j >= 0; j-- ) {
 820                      inst = $.data( cur[ j ], this.widgetFullName );
 821                      if ( inst && inst !== this && !inst.options.disabled ) {
 822                          queries.push( [ typeof inst.options.items === "function" ?
 823                              inst.options.items
 824                                  .call( inst.element[ 0 ], event, { item: this.currentItem } ) :
 825                              $( inst.options.items, inst.element ), inst ] );
 826                          this.containers.push( inst );
 827                      }
 828                  }
 829              }
 830          }
 831  
 832          for ( i = queries.length - 1; i >= 0; i-- ) {
 833              targetData = queries[ i ][ 1 ];
 834              _queries = queries[ i ][ 0 ];
 835  
 836              for ( j = 0, queriesLength = _queries.length; j < queriesLength; j++ ) {
 837                  item = $( _queries[ j ] );
 838  
 839                  // Data for target checking (mouse manager)
 840                  item.data( this.widgetName + "-item", targetData );
 841  
 842                  items.push( {
 843                      item: item,
 844                      instance: targetData,
 845                      width: 0, height: 0,
 846                      left: 0, top: 0
 847                  } );
 848              }
 849          }
 850  
 851      },
 852  
 853      _refreshItemPositions: function( fast ) {
 854          var i, item, t, p;
 855  
 856          for ( i = this.items.length - 1; i >= 0; i-- ) {
 857              item = this.items[ i ];
 858  
 859              //We ignore calculating positions of all connected containers when we're not over them
 860              if ( this.currentContainer && item.instance !== this.currentContainer &&
 861                      item.item[ 0 ] !== this.currentItem[ 0 ] ) {
 862                  continue;
 863              }
 864  
 865              t = this.options.toleranceElement ?
 866                  $( this.options.toleranceElement, item.item ) :
 867                  item.item;
 868  
 869              if ( !fast ) {
 870                  item.width = t.outerWidth();
 871                  item.height = t.outerHeight();
 872              }
 873  
 874              p = t.offset();
 875              item.left = p.left;
 876              item.top = p.top;
 877          }
 878      },
 879  
 880      refreshPositions: function( fast ) {
 881  
 882          // Determine whether items are being displayed horizontally
 883          this.floating = this.items.length ?
 884              this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) :
 885              false;
 886  
 887          if ( this.innermostContainer !== null ) {
 888              this._refreshItemPositions( fast );
 889          }
 890  
 891          var i, p;
 892  
 893          if ( this.options.custom && this.options.custom.refreshContainers ) {
 894              this.options.custom.refreshContainers.call( this );
 895          } else {
 896              for ( i = this.containers.length - 1; i >= 0; i-- ) {
 897                  p = this.containers[ i ].element.offset();
 898                  this.containers[ i ].containerCache.left = p.left;
 899                  this.containers[ i ].containerCache.top = p.top;
 900                  this.containers[ i ].containerCache.width =
 901                      this.containers[ i ].element.outerWidth();
 902                  this.containers[ i ].containerCache.height =
 903                      this.containers[ i ].element.outerHeight();
 904              }
 905          }
 906  
 907          return this;
 908      },
 909  
 910      _createPlaceholder: function( that ) {
 911          that = that || this;
 912          var className, nodeName,
 913              o = that.options;
 914  
 915          if ( !o.placeholder || o.placeholder.constructor === String ) {
 916              className = o.placeholder;
 917              nodeName = that.currentItem[ 0 ].nodeName.toLowerCase();
 918              o.placeholder = {
 919                  element: function() {
 920  
 921                      var element = $( "<" + nodeName + ">", that.document[ 0 ] );
 922  
 923                      that._addClass( element, "ui-sortable-placeholder",
 924                              className || that.currentItem[ 0 ].className )
 925                          ._removeClass( element, "ui-sortable-helper" );
 926  
 927                      if ( nodeName === "tbody" ) {
 928                          that._createTrPlaceholder(
 929                              that.currentItem.find( "tr" ).eq( 0 ),
 930                              $( "<tr>", that.document[ 0 ] ).appendTo( element )
 931                          );
 932                      } else if ( nodeName === "tr" ) {
 933                          that._createTrPlaceholder( that.currentItem, element );
 934                      } else if ( nodeName === "img" ) {
 935                          element.attr( "src", that.currentItem.attr( "src" ) );
 936                      }
 937  
 938                      if ( !className ) {
 939                          element.css( "visibility", "hidden" );
 940                      }
 941  
 942                      return element;
 943                  },
 944                  update: function( container, p ) {
 945  
 946                      // 1. If a className is set as 'placeholder option, we don't force sizes -
 947                      // the class is responsible for that
 948                      // 2. The option 'forcePlaceholderSize can be enabled to force it even if a
 949                      // class name is specified
 950                      if ( className && !o.forcePlaceholderSize ) {
 951                          return;
 952                      }
 953  
 954                      // If the element doesn't have a actual height or width by itself (without
 955                      // styles coming from a stylesheet), it receives the inline height and width
 956                      // from the dragged item. Or, if it's a tbody or tr, it's going to have a height
 957                      // anyway since we're populating them with <td>s above, but they're unlikely to
 958                      // be the correct height on their own if the row heights are dynamic, so we'll
 959                      // always assign the height of the dragged item given forcePlaceholderSize
 960                      // is true.
 961                      if ( !p.height() || ( o.forcePlaceholderSize &&
 962                              ( nodeName === "tbody" || nodeName === "tr" ) ) ) {
 963                          p.height(
 964                              that.currentItem.innerHeight() -
 965                              parseInt( that.currentItem.css( "paddingTop" ) || 0, 10 ) -
 966                              parseInt( that.currentItem.css( "paddingBottom" ) || 0, 10 ) );
 967                      }
 968                      if ( !p.width() ) {
 969                          p.width(
 970                              that.currentItem.innerWidth() -
 971                              parseInt( that.currentItem.css( "paddingLeft" ) || 0, 10 ) -
 972                              parseInt( that.currentItem.css( "paddingRight" ) || 0, 10 ) );
 973                      }
 974                  }
 975              };
 976          }
 977  
 978          //Create the placeholder
 979          that.placeholder = $( o.placeholder.element.call( that.element, that.currentItem ) );
 980  
 981          //Append it after the actual current item
 982          that.currentItem.after( that.placeholder );
 983  
 984          //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
 985          o.placeholder.update( that, that.placeholder );
 986  
 987      },
 988  
 989      _createTrPlaceholder: function( sourceTr, targetTr ) {
 990          var that = this;
 991  
 992          sourceTr.children().each( function() {
 993              $( "<td>&#160;</td>", that.document[ 0 ] )
 994                  .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
 995                  .appendTo( targetTr );
 996          } );
 997      },
 998  
 999      _contactContainers: function( event ) {
1000          var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom,
1001              floating, axis,
1002              innermostContainer = null,
1003              innermostIndex = null;
1004  
1005          // Get innermost container that intersects with item
1006          for ( i = this.containers.length - 1; i >= 0; i-- ) {
1007  
1008              // Never consider a container that's located within the item itself
1009              if ( $.contains( this.currentItem[ 0 ], this.containers[ i ].element[ 0 ] ) ) {
1010                  continue;
1011              }
1012  
1013              if ( this._intersectsWith( this.containers[ i ].containerCache ) ) {
1014  
1015                  // If we've already found a container and it's more "inner" than this, then continue
1016                  if ( innermostContainer &&
1017                          $.contains(
1018                              this.containers[ i ].element[ 0 ],
1019                              innermostContainer.element[ 0 ] ) ) {
1020                      continue;
1021                  }
1022  
1023                  innermostContainer = this.containers[ i ];
1024                  innermostIndex = i;
1025  
1026              } else {
1027  
1028                  // container doesn't intersect. trigger "out" event if necessary
1029                  if ( this.containers[ i ].containerCache.over ) {
1030                      this.containers[ i ]._trigger( "out", event, this._uiHash( this ) );
1031                      this.containers[ i ].containerCache.over = 0;
1032                  }
1033              }
1034  
1035          }
1036  
1037          this.innermostContainer = innermostContainer;
1038  
1039          // If no intersecting containers found, return
1040          if ( !innermostContainer ) {
1041              return;
1042          }
1043  
1044          // Move the item into the container if it's not there already
1045          if ( this.containers.length === 1 ) {
1046              if ( !this.containers[ innermostIndex ].containerCache.over ) {
1047                  this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
1048                  this.containers[ innermostIndex ].containerCache.over = 1;
1049              }
1050          } else {
1051  
1052              // When entering a new container, we will find the item with the least distance and
1053              // append our item near it
1054              dist = 10000;
1055              itemWithLeastDistance = null;
1056              floating = innermostContainer.floating || this._isFloating( this.currentItem );
1057              posProperty = floating ? "left" : "top";
1058              sizeProperty = floating ? "width" : "height";
1059              axis = floating ? "pageX" : "pageY";
1060  
1061              for ( j = this.items.length - 1; j >= 0; j-- ) {
1062                  if ( !$.contains(
1063                          this.containers[ innermostIndex ].element[ 0 ], this.items[ j ].item[ 0 ] )
1064                  ) {
1065                      continue;
1066                  }
1067                  if ( this.items[ j ].item[ 0 ] === this.currentItem[ 0 ] ) {
1068                      continue;
1069                  }
1070  
1071                  cur = this.items[ j ].item.offset()[ posProperty ];
1072                  nearBottom = false;
1073                  if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) {
1074                      nearBottom = true;
1075                  }
1076  
1077                  if ( Math.abs( event[ axis ] - cur ) < dist ) {
1078                      dist = Math.abs( event[ axis ] - cur );
1079                      itemWithLeastDistance = this.items[ j ];
1080                      this.direction = nearBottom ? "up" : "down";
1081                  }
1082              }
1083  
1084              //Check if dropOnEmpty is enabled
1085              if ( !itemWithLeastDistance && !this.options.dropOnEmpty ) {
1086                  return;
1087              }
1088  
1089              if ( this.currentContainer === this.containers[ innermostIndex ] ) {
1090                  if ( !this.currentContainer.containerCache.over ) {
1091                      this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() );
1092                      this.currentContainer.containerCache.over = 1;
1093                  }
1094                  return;
1095              }
1096  
1097              if ( itemWithLeastDistance ) {
1098                  this._rearrange( event, itemWithLeastDistance, null, true );
1099              } else {
1100                  this._rearrange( event, null, this.containers[ innermostIndex ].element, true );
1101              }
1102              this._trigger( "change", event, this._uiHash() );
1103              this.containers[ innermostIndex ]._trigger( "change", event, this._uiHash( this ) );
1104              this.currentContainer = this.containers[ innermostIndex ];
1105  
1106              //Update the placeholder
1107              this.options.placeholder.update( this.currentContainer, this.placeholder );
1108  
1109              //Update scrollParent
1110              this.scrollParent = this.placeholder.scrollParent();
1111  
1112              //Update overflowOffset
1113              if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
1114                      this.scrollParent[ 0 ].tagName !== "HTML" ) {
1115                  this.overflowOffset = this.scrollParent.offset();
1116              }
1117  
1118              this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
1119              this.containers[ innermostIndex ].containerCache.over = 1;
1120          }
1121  
1122      },
1123  
1124      _createHelper: function( event ) {
1125  
1126          var o = this.options,
1127              helper = typeof o.helper === "function" ?
1128                  $( o.helper.apply( this.element[ 0 ], [ event, this.currentItem ] ) ) :
1129                  ( o.helper === "clone" ? this.currentItem.clone() : this.currentItem );
1130  
1131          //Add the helper to the DOM if that didn't happen already
1132          if ( !helper.parents( "body" ).length ) {
1133              this.appendTo[ 0 ].appendChild( helper[ 0 ] );
1134          }
1135  
1136          if ( helper[ 0 ] === this.currentItem[ 0 ] ) {
1137              this._storedCSS = {
1138                  width: this.currentItem[ 0 ].style.width,
1139                  height: this.currentItem[ 0 ].style.height,
1140                  position: this.currentItem.css( "position" ),
1141                  top: this.currentItem.css( "top" ),
1142                  left: this.currentItem.css( "left" )
1143              };
1144          }
1145  
1146          if ( !helper[ 0 ].style.width || o.forceHelperSize ) {
1147              helper.width( this.currentItem.width() );
1148          }
1149          if ( !helper[ 0 ].style.height || o.forceHelperSize ) {
1150              helper.height( this.currentItem.height() );
1151          }
1152  
1153          return helper;
1154  
1155      },
1156  
1157      _adjustOffsetFromHelper: function( obj ) {
1158          if ( typeof obj === "string" ) {
1159              obj = obj.split( " " );
1160          }
1161          if ( Array.isArray( obj ) ) {
1162              obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
1163          }
1164          if ( "left" in obj ) {
1165              this.offset.click.left = obj.left + this.margins.left;
1166          }
1167          if ( "right" in obj ) {
1168              this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1169          }
1170          if ( "top" in obj ) {
1171              this.offset.click.top = obj.top + this.margins.top;
1172          }
1173          if ( "bottom" in obj ) {
1174              this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1175          }
1176      },
1177  
1178      _getParentOffset: function() {
1179  
1180          //Get the offsetParent and cache its position
1181          this.offsetParent = this.helper.offsetParent();
1182          var po = this.offsetParent.offset();
1183  
1184          // This is a special case where we need to modify a offset calculated on start, since the
1185          // following happened:
1186          // 1. The position of the helper is absolute, so it's position is calculated based on the
1187          // next positioned parent
1188          // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
1189          // the document, which means that the scroll is included in the initial calculation of the
1190          // offset of the parent, and never recalculated upon drag
1191          if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== this.document[ 0 ] &&
1192                  $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
1193              po.left += this.scrollParent.scrollLeft();
1194              po.top += this.scrollParent.scrollTop();
1195          }
1196  
1197          // This needs to be actually done for all browsers, since pageX/pageY includes this
1198          // information with an ugly IE fix
1199          if ( this.offsetParent[ 0 ] === this.document[ 0 ].body ||
1200                  ( this.offsetParent[ 0 ].tagName &&
1201                  this.offsetParent[ 0 ].tagName.toLowerCase() === "html" && $.ui.ie ) ) {
1202              po = { top: 0, left: 0 };
1203          }
1204  
1205          return {
1206              top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
1207              left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
1208          };
1209  
1210      },
1211  
1212      _getRelativeOffset: function() {
1213  
1214          if ( this.cssPosition === "relative" ) {
1215              var p = this.currentItem.position();
1216              return {
1217                  top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
1218                      this.scrollParent.scrollTop(),
1219                  left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
1220                      this.scrollParent.scrollLeft()
1221              };
1222          } else {
1223              return { top: 0, left: 0 };
1224          }
1225  
1226      },
1227  
1228      _cacheMargins: function() {
1229          this.margins = {
1230              left: ( parseInt( this.currentItem.css( "marginLeft" ), 10 ) || 0 ),
1231              top: ( parseInt( this.currentItem.css( "marginTop" ), 10 ) || 0 )
1232          };
1233      },
1234  
1235      _cacheHelperProportions: function() {
1236          this.helperProportions = {
1237              width: this.helper.outerWidth(),
1238              height: this.helper.outerHeight()
1239          };
1240      },
1241  
1242      _setContainment: function() {
1243  
1244          var ce, co, over,
1245              o = this.options;
1246          if ( o.containment === "parent" ) {
1247              o.containment = this.helper[ 0 ].parentNode;
1248          }
1249          if ( o.containment === "document" || o.containment === "window" ) {
1250              this.containment = [
1251                  0 - this.offset.relative.left - this.offset.parent.left,
1252                  0 - this.offset.relative.top - this.offset.parent.top,
1253                  o.containment === "document" ?
1254                      this.document.width() :
1255                      this.window.width() - this.helperProportions.width - this.margins.left,
1256                  ( o.containment === "document" ?
1257                      ( this.document.height() || document.body.parentNode.scrollHeight ) :
1258                      this.window.height() || this.document[ 0 ].body.parentNode.scrollHeight
1259                  ) - this.helperProportions.height - this.margins.top
1260              ];
1261          }
1262  
1263          if ( !( /^(document|window|parent)$/ ).test( o.containment ) ) {
1264              ce = $( o.containment )[ 0 ];
1265              co = $( o.containment ).offset();
1266              over = ( $( ce ).css( "overflow" ) !== "hidden" );
1267  
1268              this.containment = [
1269                  co.left + ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) +
1270                      ( parseInt( $( ce ).css( "paddingLeft" ), 10 ) || 0 ) - this.margins.left,
1271                  co.top + ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) +
1272                      ( parseInt( $( ce ).css( "paddingTop" ), 10 ) || 0 ) - this.margins.top,
1273                  co.left + ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
1274                      ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) -
1275                      ( parseInt( $( ce ).css( "paddingRight" ), 10 ) || 0 ) -
1276                      this.helperProportions.width - this.margins.left,
1277                  co.top + ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
1278                      ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) -
1279                      ( parseInt( $( ce ).css( "paddingBottom" ), 10 ) || 0 ) -
1280                      this.helperProportions.height - this.margins.top
1281              ];
1282          }
1283  
1284      },
1285  
1286      _convertPositionTo: function( d, pos ) {
1287  
1288          if ( !pos ) {
1289              pos = this.position;
1290          }
1291          var mod = d === "absolute" ? 1 : -1,
1292              scroll = this.cssPosition === "absolute" &&
1293                  !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
1294                  $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
1295                      this.offsetParent :
1296                      this.scrollParent,
1297              scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
1298  
1299          return {
1300              top: (
1301  
1302                  // The absolute mouse position
1303                  pos.top    +
1304  
1305                  // Only for relative positioned nodes: Relative offset from element to offset parent
1306                  this.offset.relative.top * mod +
1307  
1308                  // The offsetParent's offset without borders (offset + border)
1309                  this.offset.parent.top * mod -
1310                  ( ( this.cssPosition === "fixed" ?
1311                      -this.scrollParent.scrollTop() :
1312                      ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod )
1313              ),
1314              left: (
1315  
1316                  // The absolute mouse position
1317                  pos.left +
1318  
1319                  // Only for relative positioned nodes: Relative offset from element to offset parent
1320                  this.offset.relative.left * mod +
1321  
1322                  // The offsetParent's offset without borders (offset + border)
1323                  this.offset.parent.left * mod    -
1324                  ( ( this.cssPosition === "fixed" ?
1325                      -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 :
1326                      scroll.scrollLeft() ) * mod )
1327              )
1328          };
1329  
1330      },
1331  
1332      _generatePosition: function( event ) {
1333  
1334          var top, left,
1335              o = this.options,
1336              pageX = event.pageX,
1337              pageY = event.pageY,
1338              scroll = this.cssPosition === "absolute" &&
1339                  !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
1340                  $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
1341                      this.offsetParent :
1342                      this.scrollParent,
1343                  scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
1344  
1345          // This is another very weird special case that only happens for relative elements:
1346          // 1. If the css position is relative
1347          // 2. and the scroll parent is the document or similar to the offset parent
1348          // we have to refresh the relative offset during the scroll so there are no jumps
1349          if ( this.cssPosition === "relative" && !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
1350                  this.scrollParent[ 0 ] !== this.offsetParent[ 0 ] ) ) {
1351              this.offset.relative = this._getRelativeOffset();
1352          }
1353  
1354          /*
1355           * - Position constraining -
1356           * Constrain the position to a mix of grid, containment.
1357           */
1358  
1359          if ( this.originalPosition ) { //If we are not dragging yet, we won't check for options
1360  
1361              if ( this.containment ) {
1362                  if ( event.pageX - this.offset.click.left < this.containment[ 0 ] ) {
1363                      pageX = this.containment[ 0 ] + this.offset.click.left;
1364                  }
1365                  if ( event.pageY - this.offset.click.top < this.containment[ 1 ] ) {
1366                      pageY = this.containment[ 1 ] + this.offset.click.top;
1367                  }
1368                  if ( event.pageX - this.offset.click.left > this.containment[ 2 ] ) {
1369                      pageX = this.containment[ 2 ] + this.offset.click.left;
1370                  }
1371                  if ( event.pageY - this.offset.click.top > this.containment[ 3 ] ) {
1372                      pageY = this.containment[ 3 ] + this.offset.click.top;
1373                  }
1374              }
1375  
1376              if ( o.grid ) {
1377                  top = this.originalPageY + Math.round( ( pageY - this.originalPageY ) /
1378                      o.grid[ 1 ] ) * o.grid[ 1 ];
1379                  pageY = this.containment ?
1380                      ( ( top - this.offset.click.top >= this.containment[ 1 ] &&
1381                          top - this.offset.click.top <= this.containment[ 3 ] ) ?
1382                              top :
1383                              ( ( top - this.offset.click.top >= this.containment[ 1 ] ) ?
1384                                  top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) :
1385                                  top;
1386  
1387                  left = this.originalPageX + Math.round( ( pageX - this.originalPageX ) /
1388                      o.grid[ 0 ] ) * o.grid[ 0 ];
1389                  pageX = this.containment ?
1390                      ( ( left - this.offset.click.left >= this.containment[ 0 ] &&
1391                          left - this.offset.click.left <= this.containment[ 2 ] ) ?
1392                              left :
1393                              ( ( left - this.offset.click.left >= this.containment[ 0 ] ) ?
1394                                  left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) :
1395                                  left;
1396              }
1397  
1398          }
1399  
1400          return {
1401              top: (
1402  
1403                  // The absolute mouse position
1404                  pageY -
1405  
1406                  // Click offset (relative to the element)
1407                  this.offset.click.top -
1408  
1409                  // Only for relative positioned nodes: Relative offset from element to offset parent
1410                  this.offset.relative.top -
1411  
1412                  // The offsetParent's offset without borders (offset + border)
1413                  this.offset.parent.top +
1414                  ( ( this.cssPosition === "fixed" ?
1415                      -this.scrollParent.scrollTop() :
1416                      ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) )
1417              ),
1418              left: (
1419  
1420                  // The absolute mouse position
1421                  pageX -
1422  
1423                  // Click offset (relative to the element)
1424                  this.offset.click.left -
1425  
1426                  // Only for relative positioned nodes: Relative offset from element to offset parent
1427                  this.offset.relative.left -
1428  
1429                  // The offsetParent's offset without borders (offset + border)
1430                  this.offset.parent.left +
1431                  ( ( this.cssPosition === "fixed" ?
1432                      -this.scrollParent.scrollLeft() :
1433                      scrollIsRootNode ? 0 : scroll.scrollLeft() ) )
1434              )
1435          };
1436  
1437      },
1438  
1439      _rearrange: function( event, i, a, hardRefresh ) {
1440  
1441          if ( a ) {
1442              a[ 0 ].appendChild( this.placeholder[ 0 ] );
1443          } else {
1444              i.item[ 0 ].parentNode.insertBefore( this.placeholder[ 0 ],
1445                  ( this.direction === "down" ? i.item[ 0 ] : i.item[ 0 ].nextSibling ) );
1446          }
1447  
1448          //Various things done here to improve the performance:
1449          // 1. we create a setTimeout, that calls refreshPositions
1450          // 2. on the instance, we have a counter variable, that get's higher after every append
1451          // 3. on the local scope, we copy the counter variable, and check in the timeout,
1452          // if it's still the same
1453          // 4. this lets only the last addition to the timeout stack through
1454          this.counter = this.counter ? ++this.counter : 1;
1455          var counter = this.counter;
1456  
1457          this._delay( function() {
1458              if ( counter === this.counter ) {
1459  
1460                  //Precompute after each DOM insertion, NOT on mousemove
1461                  this.refreshPositions( !hardRefresh );
1462              }
1463          } );
1464  
1465      },
1466  
1467      _clear: function( event, noPropagation ) {
1468  
1469          this.reverting = false;
1470  
1471          // We delay all events that have to be triggered to after the point where the placeholder
1472          // has been removed and everything else normalized again
1473          var i,
1474              delayedTriggers = [];
1475  
1476          // We first have to update the dom position of the actual currentItem
1477          // Note: don't do it if the current item is already removed (by a user), or it gets
1478          // reappended (see #4088)
1479          if ( !this._noFinalSort && this.currentItem.parent().length ) {
1480              this.placeholder.before( this.currentItem );
1481          }
1482          this._noFinalSort = null;
1483  
1484          if ( this.helper[ 0 ] === this.currentItem[ 0 ] ) {
1485              for ( i in this._storedCSS ) {
1486                  if ( this._storedCSS[ i ] === "auto" || this._storedCSS[ i ] === "static" ) {
1487                      this._storedCSS[ i ] = "";
1488                  }
1489              }
1490              this.currentItem.css( this._storedCSS );
1491              this._removeClass( this.currentItem, "ui-sortable-helper" );
1492          } else {
1493              this.currentItem.show();
1494          }
1495  
1496          if ( this.fromOutside && !noPropagation ) {
1497              delayedTriggers.push( function( event ) {
1498                  this._trigger( "receive", event, this._uiHash( this.fromOutside ) );
1499              } );
1500          }
1501          if ( ( this.fromOutside ||
1502                  this.domPosition.prev !==
1503                  this.currentItem.prev().not( ".ui-sortable-helper" )[ 0 ] ||
1504                  this.domPosition.parent !== this.currentItem.parent()[ 0 ] ) && !noPropagation ) {
1505  
1506              // Trigger update callback if the DOM position has changed
1507              delayedTriggers.push( function( event ) {
1508                  this._trigger( "update", event, this._uiHash() );
1509              } );
1510          }
1511  
1512          // Check if the items Container has Changed and trigger appropriate
1513          // events.
1514          if ( this !== this.currentContainer ) {
1515              if ( !noPropagation ) {
1516                  delayedTriggers.push( function( event ) {
1517                      this._trigger( "remove", event, this._uiHash() );
1518                  } );
1519                  delayedTriggers.push( ( function( c ) {
1520                      return function( event ) {
1521                          c._trigger( "receive", event, this._uiHash( this ) );
1522                      };
1523                  } ).call( this, this.currentContainer ) );
1524                  delayedTriggers.push( ( function( c ) {
1525                      return function( event ) {
1526                          c._trigger( "update", event, this._uiHash( this ) );
1527                      };
1528                  } ).call( this, this.currentContainer ) );
1529              }
1530          }
1531  
1532          //Post events to containers
1533  		function delayEvent( type, instance, container ) {
1534              return function( event ) {
1535                  container._trigger( type, event, instance._uiHash( instance ) );
1536              };
1537          }
1538          for ( i = this.containers.length - 1; i >= 0; i-- ) {
1539              if ( !noPropagation ) {
1540                  delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
1541              }
1542              if ( this.containers[ i ].containerCache.over ) {
1543                  delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
1544                  this.containers[ i ].containerCache.over = 0;
1545              }
1546          }
1547  
1548          //Do what was originally in plugins
1549          if ( this.storedCursor ) {
1550              this.document.find( "body" ).css( "cursor", this.storedCursor );
1551              this.storedStylesheet.remove();
1552          }
1553          if ( this._storedOpacity ) {
1554              this.helper.css( "opacity", this._storedOpacity );
1555          }
1556          if ( this._storedZIndex ) {
1557              this.helper.css( "zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex );
1558          }
1559  
1560          this.dragging = false;
1561  
1562          if ( !noPropagation ) {
1563              this._trigger( "beforeStop", event, this._uiHash() );
1564          }
1565  
1566          //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
1567          // it unbinds ALL events from the original node!
1568          this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
1569  
1570          if ( !this.cancelHelperRemoval ) {
1571              if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
1572                  this.helper.remove();
1573              }
1574              this.helper = null;
1575          }
1576  
1577          if ( !noPropagation ) {
1578              for ( i = 0; i < delayedTriggers.length; i++ ) {
1579  
1580                  // Trigger all delayed events
1581                  delayedTriggers[ i ].call( this, event );
1582              }
1583              this._trigger( "stop", event, this._uiHash() );
1584          }
1585  
1586          this.fromOutside = false;
1587          return !this.cancelHelperRemoval;
1588  
1589      },
1590  
1591      _trigger: function() {
1592          if ( $.Widget.prototype._trigger.apply( this, arguments ) === false ) {
1593              this.cancel();
1594          }
1595      },
1596  
1597      _uiHash: function( _inst ) {
1598          var inst = _inst || this;
1599          return {
1600              helper: inst.helper,
1601              placeholder: inst.placeholder || $( [] ),
1602              position: inst.position,
1603              originalPosition: inst.originalPosition,
1604              offset: inst.positionAbs,
1605              item: inst.currentItem,
1606              sender: _inst ? _inst.element : null
1607          };
1608      }
1609  
1610  } );
1611  
1612  } );


Generated: Tue Sep 21 01:00:05 2021 Cross-referenced by PHPXref 0.7.1