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