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