2 * jQuery UI Draggable 1.11.4
5 * Copyright jQuery Foundation and other contributors
6 * Released under the MIT license.
7 * http://jquery.org/license
9 * http://api.jqueryui.com/draggable/
11 (function( factory
) {
12 if ( typeof define
=== "function" && define
.amd
) {
14 // AMD. Register as an anonymous module.
28 $.widget("ui.draggable", $.ui
.mouse
, {
30 widgetEventPrefix
: "drag",
35 connectToSortable
: false,
44 refreshPositions
: false,
49 scrollSensitivity
: 20,
64 if ( this.options
.helper
=== "original" ) {
65 this._setPositionRelative();
67 if (this.options
.addClasses
){
68 this.element
.addClass("ui-draggable");
70 if (this.options
.disabled
){
71 this.element
.addClass("ui-draggable-disabled");
73 this._setHandleClassName();
78 _setOption: function( key
, value
) {
79 this._super( key
, value
);
80 if ( key
=== "handle" ) {
81 this._removeHandleClassName();
82 this._setHandleClassName();
86 _destroy: function() {
87 if ( ( this.helper
|| this.element
).is( ".ui-draggable-dragging" ) ) {
88 this.destroyOnClear
= true;
91 this.element
.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
92 this._removeHandleClassName();
96 _mouseCapture: function(event
) {
99 this._blurActiveElement( event
);
101 // among others, prevent a drag on a resizable-handle
102 if (this.helper
|| o
.disabled
|| $(event
.target
).closest(".ui-resizable-handle").length
> 0) {
106 //Quit if we're not on a valid handle
107 this.handle
= this._getHandle(event
);
112 this._blockFrames( o
.iframeFix
=== true ? "iframe" : o
.iframeFix
);
118 _blockFrames: function( selector
) {
119 this.iframeBlocks
= this.document
.find( selector
).map(function() {
120 var iframe
= $( this );
123 .css( "position", "absolute" )
124 .appendTo( iframe
.parent() )
125 .outerWidth( iframe
.outerWidth() )
126 .outerHeight( iframe
.outerHeight() )
127 .offset( iframe
.offset() )[ 0 ];
131 _unblockFrames: function() {
132 if ( this.iframeBlocks
) {
133 this.iframeBlocks
.remove();
134 delete this.iframeBlocks
;
138 _blurActiveElement: function( event
) {
139 var document
= this.document
[ 0 ];
141 // Only need to blur if the event occurred on the draggable itself, see #10527
142 if ( !this.handleElement
.is( event
.target
) ) {
147 // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
150 // Support: IE9, IE10
151 // If the <body> is blurred, IE will switch windows, see #9520
152 if ( document
.activeElement
&& document
.activeElement
.nodeName
.toLowerCase() !== "body" ) {
154 // Blur any element that currently has focus, see #4261
155 $( document
.activeElement
).blur();
160 _mouseStart: function(event
) {
162 var o
= this.options
;
164 //Create and append the visible helper
165 this.helper
= this._createHelper(event
);
167 this.helper
.addClass("ui-draggable-dragging");
169 //Cache the helper size
170 this._cacheHelperProportions();
172 //If ddmanager is used for droppables, set the global draggable
173 if ($.ui
.ddmanager
) {
174 $.ui
.ddmanager
.current
= this;
178 * - Position generation -
179 * This block generates everything position related - it's the core of draggables.
182 //Cache the margins of the original element
183 this._cacheMargins();
185 //Store the helper's css position
186 this.cssPosition
= this.helper
.css( "position" );
187 this.scrollParent
= this.helper
.scrollParent( true );
188 this.offsetParent
= this.helper
.offsetParent();
189 this.hasFixedAncestor
= this.helper
.parents().filter(function() {
190 return $( this ).css( "position" ) === "fixed";
193 //The element's absolute position on the page minus margins
194 this.positionAbs
= this.element
.offset();
195 this._refreshOffsets( event
);
197 //Generate the original position
198 this.originalPosition
= this.position
= this._generatePosition( event
, false );
199 this.originalPageX
= event
.pageX
;
200 this.originalPageY
= event
.pageY
;
202 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
203 (o
.cursorAt
&& this._adjustOffsetFromHelper(o
.cursorAt
));
205 //Set a containment if given in the options
206 this._setContainment();
208 //Trigger event + callbacks
209 if (this._trigger("start", event
) === false) {
214 //Recache the helper size
215 this._cacheHelperProportions();
217 //Prepare the droppable offsets
218 if ($.ui
.ddmanager
&& !o
.dropBehaviour
) {
219 $.ui
.ddmanager
.prepareOffsets(this, event
);
222 // Reset helper's right/bottom css if they're set and set explicit width/height instead
223 // as this prevents resizing of elements with right/bottom set (see #7772)
224 this._normalizeRightBottom();
226 this._mouseDrag(event
, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
228 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
229 if ( $.ui
.ddmanager
) {
230 $.ui
.ddmanager
.dragStart(this, event
);
236 _refreshOffsets: function( event
) {
238 top
: this.positionAbs
.top
- this.margins
.top
,
239 left
: this.positionAbs
.left
- this.margins
.left
,
241 parent
: this._getParentOffset(),
242 relative
: this._getRelativeOffset()
245 this.offset
.click
= {
246 left
: event
.pageX
- this.offset
.left
,
247 top
: event
.pageY
- this.offset
.top
251 _mouseDrag: function(event
, noPropagation
) {
252 // reset any necessary cached properties (see #5009)
253 if ( this.hasFixedAncestor
) {
254 this.offset
.parent
= this._getParentOffset();
257 //Compute the helpers position
258 this.position
= this._generatePosition( event
, true );
259 this.positionAbs
= this._convertPositionTo("absolute");
261 //Call plugins and callbacks and use the resulting position if something is returned
262 if (!noPropagation
) {
263 var ui
= this._uiHash();
264 if (this._trigger("drag", event
, ui
) === false) {
268 this.position
= ui
.position
;
271 this.helper
[ 0 ].style
.left
= this.position
.left
+ "px";
272 this.helper
[ 0 ].style
.top
= this.position
.top
+ "px";
274 if ($.ui
.ddmanager
) {
275 $.ui
.ddmanager
.drag(this, event
);
281 _mouseStop: function(event
) {
283 //If we are using droppables, inform the manager about the drop
286 if ($.ui
.ddmanager
&& !this.options
.dropBehaviour
) {
287 dropped
= $.ui
.ddmanager
.drop(this, event
);
290 //if a drop comes from outside (a sortable)
292 dropped
= this.dropped
;
293 this.dropped
= false;
296 if ((this.options
.revert
=== "invalid" && !dropped
) || (this.options
.revert
=== "valid" && dropped
) || this.options
.revert
=== true || ($.isFunction(this.options
.revert
) && this.options
.revert
.call(this.element
, dropped
))) {
297 $(this.helper
).animate(this.originalPosition
, parseInt(this.options
.revertDuration
, 10), function() {
298 if (that
._trigger("stop", event
) !== false) {
303 if (this._trigger("stop", event
) !== false) {
311 _mouseUp: function( event
) {
312 this._unblockFrames();
314 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
315 if ( $.ui
.ddmanager
) {
316 $.ui
.ddmanager
.dragStop(this, event
);
319 // Only need to focus if the event occurred on the draggable itself, see #10527
320 if ( this.handleElement
.is( event
.target
) ) {
321 // The interaction is over; whether or not the click resulted in a drag, focus the element
322 this.element
.focus();
325 return $.ui
.mouse
.prototype._mouseUp
.call(this, event
);
330 if (this.helper
.is(".ui-draggable-dragging")) {
340 _getHandle: function(event
) {
341 return this.options
.handle
?
342 !!$( event
.target
).closest( this.element
.find( this.options
.handle
) ).length
:
346 _setHandleClassName: function() {
347 this.handleElement
= this.options
.handle
?
348 this.element
.find( this.options
.handle
) : this.element
;
349 this.handleElement
.addClass( "ui-draggable-handle" );
352 _removeHandleClassName: function() {
353 this.handleElement
.removeClass( "ui-draggable-handle" );
356 _createHelper: function(event
) {
358 var o
= this.options
,
359 helperIsFunction
= $.isFunction( o
.helper
),
360 helper
= helperIsFunction
?
361 $( o
.helper
.apply( this.element
[ 0 ], [ event
] ) ) :
362 ( o
.helper
=== "clone" ?
363 this.element
.clone().removeAttr( "id" ) :
366 if (!helper
.parents("body").length
) {
367 helper
.appendTo((o
.appendTo
=== "parent" ? this.element
[0].parentNode
: o
.appendTo
));
370 // http://bugs.jqueryui.com/ticket/9446
371 // a helper function can return the original element
372 // which wouldn't have been set to relative in _create
373 if ( helperIsFunction
&& helper
[ 0 ] === this.element
[ 0 ] ) {
374 this._setPositionRelative();
377 if (helper
[0] !== this.element
[0] && !(/(fixed|absolute)/).test(helper
.css("position"))) {
378 helper
.css("position", "absolute");
385 _setPositionRelative: function() {
386 if ( !( /^(?:r|a|f)/ ).test( this.element
.css( "position" ) ) ) {
387 this.element
[ 0 ].style
.position
= "relative";
391 _adjustOffsetFromHelper: function(obj
) {
392 if (typeof obj
=== "string") {
393 obj
= obj
.split(" ");
395 if ($.isArray(obj
)) {
396 obj
= { left
: +obj
[0], top
: +obj
[1] || 0 };
399 this.offset
.click
.left
= obj
.left
+ this.margins
.left
;
401 if ("right" in obj
) {
402 this.offset
.click
.left
= this.helperProportions
.width
- obj
.right
+ this.margins
.left
;
405 this.offset
.click
.top
= obj
.top
+ this.margins
.top
;
407 if ("bottom" in obj
) {
408 this.offset
.click
.top
= this.helperProportions
.height
- obj
.bottom
+ this.margins
.top
;
412 _isRootNode: function( element
) {
413 return ( /(html|body)/i ).test( element
.tagName
) || element
=== this.document
[ 0 ];
416 _getParentOffset: function() {
418 //Get the offsetParent and cache its position
419 var po
= this.offsetParent
.offset(),
420 document
= this.document
[ 0 ];
422 // This is a special case where we need to modify a offset calculated on start, since the following happened:
423 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
424 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
425 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
426 if (this.cssPosition
=== "absolute" && this.scrollParent
[0] !== document
&& $.contains(this.scrollParent
[0], this.offsetParent
[0])) {
427 po
.left
+= this.scrollParent
.scrollLeft();
428 po
.top
+= this.scrollParent
.scrollTop();
431 if ( this._isRootNode( this.offsetParent
[ 0 ] ) ) {
432 po
= { top
: 0, left
: 0 };
436 top
: po
.top
+ (parseInt(this.offsetParent
.css("borderTopWidth"), 10) || 0),
437 left
: po
.left
+ (parseInt(this.offsetParent
.css("borderLeftWidth"), 10) || 0)
442 _getRelativeOffset: function() {
443 if ( this.cssPosition
!== "relative" ) {
444 return { top
: 0, left
: 0 };
447 var p
= this.element
.position(),
448 scrollIsRootNode
= this._isRootNode( this.scrollParent
[ 0 ] );
451 top
: p
.top
- ( parseInt(this.helper
.css( "top" ), 10) || 0 ) + ( !scrollIsRootNode
? this.scrollParent
.scrollTop() : 0 ),
452 left
: p
.left
- ( parseInt(this.helper
.css( "left" ), 10) || 0 ) + ( !scrollIsRootNode
? this.scrollParent
.scrollLeft() : 0 )
457 _cacheMargins: function() {
459 left
: (parseInt(this.element
.css("marginLeft"), 10) || 0),
460 top
: (parseInt(this.element
.css("marginTop"), 10) || 0),
461 right
: (parseInt(this.element
.css("marginRight"), 10) || 0),
462 bottom
: (parseInt(this.element
.css("marginBottom"), 10) || 0)
466 _cacheHelperProportions: function() {
467 this.helperProportions
= {
468 width
: this.helper
.outerWidth(),
469 height
: this.helper
.outerHeight()
473 _setContainment: function() {
475 var isUserScrollable
, c
, ce
,
477 document
= this.document
[ 0 ];
479 this.relativeContainer
= null;
481 if ( !o
.containment
) {
482 this.containment
= null;
486 if ( o
.containment
=== "window" ) {
488 $( window
).scrollLeft() - this.offset
.relative
.left
- this.offset
.parent
.left
,
489 $( window
).scrollTop() - this.offset
.relative
.top
- this.offset
.parent
.top
,
490 $( window
).scrollLeft() + $( window
).width() - this.helperProportions
.width
- this.margins
.left
,
491 $( window
).scrollTop() + ( $( window
).height() || document
.body
.parentNode
.scrollHeight
) - this.helperProportions
.height
- this.margins
.top
496 if ( o
.containment
=== "document") {
500 $( document
).width() - this.helperProportions
.width
- this.margins
.left
,
501 ( $( document
).height() || document
.body
.parentNode
.scrollHeight
) - this.helperProportions
.height
- this.margins
.top
506 if ( o
.containment
.constructor === Array
) {
507 this.containment
= o
.containment
;
511 if ( o
.containment
=== "parent" ) {
512 o
.containment
= this.helper
[ 0 ].parentNode
;
515 c
= $( o
.containment
);
522 isUserScrollable
= /(scroll|auto)/.test( c
.css( "overflow" ) );
525 ( parseInt( c
.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c
.css( "paddingLeft" ), 10 ) || 0 ),
526 ( parseInt( c
.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c
.css( "paddingTop" ), 10 ) || 0 ),
527 ( isUserScrollable
? Math
.max( ce
.scrollWidth
, ce
.offsetWidth
) : ce
.offsetWidth
) -
528 ( parseInt( c
.css( "borderRightWidth" ), 10 ) || 0 ) -
529 ( parseInt( c
.css( "paddingRight" ), 10 ) || 0 ) -
530 this.helperProportions
.width
-
533 ( isUserScrollable
? Math
.max( ce
.scrollHeight
, ce
.offsetHeight
) : ce
.offsetHeight
) -
534 ( parseInt( c
.css( "borderBottomWidth" ), 10 ) || 0 ) -
535 ( parseInt( c
.css( "paddingBottom" ), 10 ) || 0 ) -
536 this.helperProportions
.height
-
540 this.relativeContainer
= c
;
543 _convertPositionTo: function(d
, pos
) {
549 var mod
= d
=== "absolute" ? 1 : -1,
550 scrollIsRootNode
= this._isRootNode( this.scrollParent
[ 0 ] );
554 pos
.top
+ // The absolute mouse position
555 this.offset
.relative
.top
* mod
+ // Only for relative positioned nodes: Relative offset from element to offset parent
556 this.offset
.parent
.top
* mod
- // The offsetParent's offset without borders (offset + border)
557 ( ( this.cssPosition
=== "fixed" ? -this.offset
.scroll
.top
: ( scrollIsRootNode
? 0 : this.offset
.scroll
.top
) ) * mod
)
560 pos
.left
+ // The absolute mouse position
561 this.offset
.relative
.left
* mod
+ // Only for relative positioned nodes: Relative offset from element to offset parent
562 this.offset
.parent
.left
* mod
- // The offsetParent's offset without borders (offset + border)
563 ( ( this.cssPosition
=== "fixed" ? -this.offset
.scroll
.left
: ( scrollIsRootNode
? 0 : this.offset
.scroll
.left
) ) * mod
)
569 _generatePosition: function( event
, constrainPosition
) {
571 var containment
, co
, top
, left
,
573 scrollIsRootNode
= this._isRootNode( this.scrollParent
[ 0 ] ),
578 if ( !scrollIsRootNode
|| !this.offset
.scroll
) {
579 this.offset
.scroll
= {
580 top
: this.scrollParent
.scrollTop(),
581 left
: this.scrollParent
.scrollLeft()
586 * - Position constraining -
587 * Constrain the position to a mix of grid, containment.
590 // If we are not dragging yet, we won't check for options
591 if ( constrainPosition
) {
592 if ( this.containment
) {
593 if ( this.relativeContainer
){
594 co
= this.relativeContainer
.offset();
596 this.containment
[ 0 ] + co
.left
,
597 this.containment
[ 1 ] + co
.top
,
598 this.containment
[ 2 ] + co
.left
,
599 this.containment
[ 3 ] + co
.top
602 containment
= this.containment
;
605 if (event
.pageX
- this.offset
.click
.left
< containment
[0]) {
606 pageX
= containment
[0] + this.offset
.click
.left
;
608 if (event
.pageY
- this.offset
.click
.top
< containment
[1]) {
609 pageY
= containment
[1] + this.offset
.click
.top
;
611 if (event
.pageX
- this.offset
.click
.left
> containment
[2]) {
612 pageX
= containment
[2] + this.offset
.click
.left
;
614 if (event
.pageY
- this.offset
.click
.top
> containment
[3]) {
615 pageY
= containment
[3] + this.offset
.click
.top
;
620 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
621 top
= o
.grid
[1] ? this.originalPageY
+ Math
.round((pageY
- this.originalPageY
) / o
.grid
[1]) * o
.grid
[1] : this.originalPageY
;
622 pageY
= containment
? ((top
- this.offset
.click
.top
>= containment
[1] || top
- this.offset
.click
.top
> containment
[3]) ? top
: ((top
- this.offset
.click
.top
>= containment
[1]) ? top
- o
.grid
[1] : top
+ o
.grid
[1])) : top
;
624 left
= o
.grid
[0] ? this.originalPageX
+ Math
.round((pageX
- this.originalPageX
) / o
.grid
[0]) * o
.grid
[0] : this.originalPageX
;
625 pageX
= containment
? ((left
- this.offset
.click
.left
>= containment
[0] || left
- this.offset
.click
.left
> containment
[2]) ? left
: ((left
- this.offset
.click
.left
>= containment
[0]) ? left
- o
.grid
[0] : left
+ o
.grid
[0])) : left
;
628 if ( o
.axis
=== "y" ) {
629 pageX
= this.originalPageX
;
632 if ( o
.axis
=== "x" ) {
633 pageY
= this.originalPageY
;
639 pageY
- // The absolute mouse position
640 this.offset
.click
.top
- // Click offset (relative to the element)
641 this.offset
.relative
.top
- // Only for relative positioned nodes: Relative offset from element to offset parent
642 this.offset
.parent
.top
+ // The offsetParent's offset without borders (offset + border)
643 ( this.cssPosition
=== "fixed" ? -this.offset
.scroll
.top
: ( scrollIsRootNode
? 0 : this.offset
.scroll
.top
) )
646 pageX
- // The absolute mouse position
647 this.offset
.click
.left
- // Click offset (relative to the element)
648 this.offset
.relative
.left
- // Only for relative positioned nodes: Relative offset from element to offset parent
649 this.offset
.parent
.left
+ // The offsetParent's offset without borders (offset + border)
650 ( this.cssPosition
=== "fixed" ? -this.offset
.scroll
.left
: ( scrollIsRootNode
? 0 : this.offset
.scroll
.left
) )
657 this.helper
.removeClass("ui-draggable-dragging");
658 if (this.helper
[0] !== this.element
[0] && !this.cancelHelperRemoval
) {
659 this.helper
.remove();
662 this.cancelHelperRemoval
= false;
663 if ( this.destroyOnClear
) {
668 _normalizeRightBottom: function() {
669 if ( this.options
.axis
!== "y" && this.helper
.css( "right" ) !== "auto" ) {
670 this.helper
.width( this.helper
.width() );
671 this.helper
.css( "right", "auto" );
673 if ( this.options
.axis
!== "x" && this.helper
.css( "bottom" ) !== "auto" ) {
674 this.helper
.height( this.helper
.height() );
675 this.helper
.css( "bottom", "auto" );
679 // From now on bulk stuff - mainly helpers
681 _trigger: function( type
, event
, ui
) {
682 ui
= ui
|| this._uiHash();
683 $.ui
.plugin
.call( this, type
, [ event
, ui
, this ], true );
685 // Absolute position and offset (see #6884 ) have to be recalculated after plugins
686 if ( /^(drag|start|stop)/.test( type
) ) {
687 this.positionAbs
= this._convertPositionTo( "absolute" );
688 ui
.offset
= this.positionAbs
;
690 return $.Widget
.prototype._trigger
.call( this, type
, event
, ui
);
695 _uiHash: function() {
698 position
: this.position
,
699 originalPosition
: this.originalPosition
,
700 offset
: this.positionAbs
706 $.ui
.plugin
.add( "draggable", "connectToSortable", {
707 start: function( event
, ui
, draggable
) {
708 var uiSortable
= $.extend( {}, ui
, {
709 item
: draggable
.element
712 draggable
.sortables
= [];
713 $( draggable
.options
.connectToSortable
).each(function() {
714 var sortable
= $( this ).sortable( "instance" );
716 if ( sortable
&& !sortable
.options
.disabled
) {
717 draggable
.sortables
.push( sortable
);
719 // refreshPositions is called at drag start to refresh the containerCache
720 // which is used in drag. This ensures it's initialized and synchronized
721 // with any changes that might have happened on the page since initialization.
722 sortable
.refreshPositions();
723 sortable
._trigger("activate", event
, uiSortable
);
727 stop: function( event
, ui
, draggable
) {
728 var uiSortable
= $.extend( {}, ui
, {
729 item
: draggable
.element
732 draggable
.cancelHelperRemoval
= false;
734 $.each( draggable
.sortables
, function() {
737 if ( sortable
.isOver
) {
740 // Allow this sortable to handle removing the helper
741 draggable
.cancelHelperRemoval
= true;
742 sortable
.cancelHelperRemoval
= false;
744 // Use _storedCSS To restore properties in the sortable,
745 // as this also handles revert (#9675) since the draggable
746 // may have modified them in unexpected ways (#8809)
747 sortable
._storedCSS
= {
748 position
: sortable
.placeholder
.css( "position" ),
749 top
: sortable
.placeholder
.css( "top" ),
750 left
: sortable
.placeholder
.css( "left" )
753 sortable
._mouseStop(event
);
755 // Once drag has ended, the sortable should return to using
756 // its original helper, not the shared helper from draggable
757 sortable
.options
.helper
= sortable
.options
._helper
;
759 // Prevent this Sortable from removing the helper.
760 // However, don't set the draggable to remove the helper
761 // either as another connected Sortable may yet handle the removal.
762 sortable
.cancelHelperRemoval
= true;
764 sortable
._trigger( "deactivate", event
, uiSortable
);
768 drag: function( event
, ui
, draggable
) {
769 $.each( draggable
.sortables
, function() {
770 var innermostIntersecting
= false,
773 // Copy over variables that sortable's _intersectsWith uses
774 sortable
.positionAbs
= draggable
.positionAbs
;
775 sortable
.helperProportions
= draggable
.helperProportions
;
776 sortable
.offset
.click
= draggable
.offset
.click
;
778 if ( sortable
._intersectsWith( sortable
.containerCache
) ) {
779 innermostIntersecting
= true;
781 $.each( draggable
.sortables
, function() {
782 // Copy over variables that sortable's _intersectsWith uses
783 this.positionAbs
= draggable
.positionAbs
;
784 this.helperProportions
= draggable
.helperProportions
;
785 this.offset
.click
= draggable
.offset
.click
;
787 if ( this !== sortable
&&
788 this._intersectsWith( this.containerCache
) &&
789 $.contains( sortable
.element
[ 0 ], this.element
[ 0 ] ) ) {
790 innermostIntersecting
= false;
793 return innermostIntersecting
;
797 if ( innermostIntersecting
) {
798 // If it intersects, we use a little isOver variable and set it once,
799 // so that the move-in stuff gets fired only once.
800 if ( !sortable
.isOver
) {
803 // Store draggable's parent in case we need to reappend to it later.
804 draggable
._parent
= ui
.helper
.parent();
806 sortable
.currentItem
= ui
.helper
807 .appendTo( sortable
.element
)
808 .data( "ui-sortable-item", true );
810 // Store helper option to later restore it
811 sortable
.options
._helper
= sortable
.options
.helper
;
813 sortable
.options
.helper = function() {
814 return ui
.helper
[ 0 ];
817 // Fire the start events of the sortable with our passed browser event,
818 // and our own helper (so it doesn't create a new one)
819 event
.target
= sortable
.currentItem
[ 0 ];
820 sortable
._mouseCapture( event
, true );
821 sortable
._mouseStart( event
, true, true );
823 // Because the browser event is way off the new appended portlet,
824 // modify necessary variables to reflect the changes
825 sortable
.offset
.click
.top
= draggable
.offset
.click
.top
;
826 sortable
.offset
.click
.left
= draggable
.offset
.click
.left
;
827 sortable
.offset
.parent
.left
-= draggable
.offset
.parent
.left
-
828 sortable
.offset
.parent
.left
;
829 sortable
.offset
.parent
.top
-= draggable
.offset
.parent
.top
-
830 sortable
.offset
.parent
.top
;
832 draggable
._trigger( "toSortable", event
);
834 // Inform draggable that the helper is in a valid drop zone,
835 // used solely in the revert option to handle "valid/invalid".
836 draggable
.dropped
= sortable
.element
;
838 // Need to refreshPositions of all sortables in the case that
839 // adding to one sortable changes the location of the other sortables (#9675)
840 $.each( draggable
.sortables
, function() {
841 this.refreshPositions();
844 // hack so receive/update callbacks work (mostly)
845 draggable
.currentItem
= draggable
.element
;
846 sortable
.fromOutside
= draggable
;
849 if ( sortable
.currentItem
) {
850 sortable
._mouseDrag( event
);
851 // Copy the sortable's position because the draggable's can potentially reflect
852 // a relative position, while sortable is always absolute, which the dragged
853 // element has now become. (#8809)
854 ui
.position
= sortable
.position
;
857 // If it doesn't intersect with the sortable, and it intersected before,
858 // we fake the drag stop of the sortable, but make sure it doesn't remove
859 // the helper by using cancelHelperRemoval.
860 if ( sortable
.isOver
) {
863 sortable
.cancelHelperRemoval
= true;
865 // Calling sortable's mouseStop would trigger a revert,
866 // so revert must be temporarily false until after mouseStop is called.
867 sortable
.options
._revert
= sortable
.options
.revert
;
868 sortable
.options
.revert
= false;
870 sortable
._trigger( "out", event
, sortable
._uiHash( sortable
) );
871 sortable
._mouseStop( event
, true );
873 // restore sortable behaviors that were modfied
874 // when the draggable entered the sortable area (#9481)
875 sortable
.options
.revert
= sortable
.options
._revert
;
876 sortable
.options
.helper
= sortable
.options
._helper
;
878 if ( sortable
.placeholder
) {
879 sortable
.placeholder
.remove();
882 // Restore and recalculate the draggable's offset considering the sortable
883 // may have modified them in unexpected ways. (#8809, #10669)
884 ui
.helper
.appendTo( draggable
._parent
);
885 draggable
._refreshOffsets( event
);
886 ui
.position
= draggable
._generatePosition( event
, true );
888 draggable
._trigger( "fromSortable", event
);
890 // Inform draggable that the helper is no longer in a valid drop zone
891 draggable
.dropped
= false;
893 // Need to refreshPositions of all sortables just in case removing
894 // from one sortable changes the location of other sortables (#9675)
895 $.each( draggable
.sortables
, function() {
896 this.refreshPositions();
904 $.ui
.plugin
.add("draggable", "cursor", {
905 start: function( event
, ui
, instance
) {
907 o
= instance
.options
;
909 if (t
.css("cursor")) {
910 o
._cursor
= t
.css("cursor");
912 t
.css("cursor", o
.cursor
);
914 stop: function( event
, ui
, instance
) {
915 var o
= instance
.options
;
917 $("body").css("cursor", o
._cursor
);
922 $.ui
.plugin
.add("draggable", "opacity", {
923 start: function( event
, ui
, instance
) {
924 var t
= $( ui
.helper
),
925 o
= instance
.options
;
926 if (t
.css("opacity")) {
927 o
._opacity
= t
.css("opacity");
929 t
.css("opacity", o
.opacity
);
931 stop: function( event
, ui
, instance
) {
932 var o
= instance
.options
;
934 $(ui
.helper
).css("opacity", o
._opacity
);
939 $.ui
.plugin
.add("draggable", "scroll", {
940 start: function( event
, ui
, i
) {
941 if ( !i
.scrollParentNotHidden
) {
942 i
.scrollParentNotHidden
= i
.helper
.scrollParent( false );
945 if ( i
.scrollParentNotHidden
[ 0 ] !== i
.document
[ 0 ] && i
.scrollParentNotHidden
[ 0 ].tagName
!== "HTML" ) {
946 i
.overflowOffset
= i
.scrollParentNotHidden
.offset();
949 drag: function( event
, ui
, i
) {
953 scrollParent
= i
.scrollParentNotHidden
[ 0 ],
954 document
= i
.document
[ 0 ];
956 if ( scrollParent
!== document
&& scrollParent
.tagName
!== "HTML" ) {
957 if ( !o
.axis
|| o
.axis
!== "x" ) {
958 if ( ( i
.overflowOffset
.top
+ scrollParent
.offsetHeight
) - event
.pageY
< o
.scrollSensitivity
) {
959 scrollParent
.scrollTop
= scrolled
= scrollParent
.scrollTop
+ o
.scrollSpeed
;
960 } else if ( event
.pageY
- i
.overflowOffset
.top
< o
.scrollSensitivity
) {
961 scrollParent
.scrollTop
= scrolled
= scrollParent
.scrollTop
- o
.scrollSpeed
;
965 if ( !o
.axis
|| o
.axis
!== "y" ) {
966 if ( ( i
.overflowOffset
.left
+ scrollParent
.offsetWidth
) - event
.pageX
< o
.scrollSensitivity
) {
967 scrollParent
.scrollLeft
= scrolled
= scrollParent
.scrollLeft
+ o
.scrollSpeed
;
968 } else if ( event
.pageX
- i
.overflowOffset
.left
< o
.scrollSensitivity
) {
969 scrollParent
.scrollLeft
= scrolled
= scrollParent
.scrollLeft
- o
.scrollSpeed
;
975 if (!o
.axis
|| o
.axis
!== "x") {
976 if (event
.pageY
- $(document
).scrollTop() < o
.scrollSensitivity
) {
977 scrolled
= $(document
).scrollTop($(document
).scrollTop() - o
.scrollSpeed
);
978 } else if ($(window
).height() - (event
.pageY
- $(document
).scrollTop()) < o
.scrollSensitivity
) {
979 scrolled
= $(document
).scrollTop($(document
).scrollTop() + o
.scrollSpeed
);
983 if (!o
.axis
|| o
.axis
!== "y") {
984 if (event
.pageX
- $(document
).scrollLeft() < o
.scrollSensitivity
) {
985 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() - o
.scrollSpeed
);
986 } else if ($(window
).width() - (event
.pageX
- $(document
).scrollLeft()) < o
.scrollSensitivity
) {
987 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() + o
.scrollSpeed
);
993 if (scrolled
!== false && $.ui
.ddmanager
&& !o
.dropBehaviour
) {
994 $.ui
.ddmanager
.prepareOffsets(i
, event
);
1000 $.ui
.plugin
.add("draggable", "snap", {
1001 start: function( event
, ui
, i
) {
1005 i
.snapElements
= [];
1007 $(o
.snap
.constructor !== String
? ( o
.snap
.items
|| ":data(ui-draggable)" ) : o
.snap
).each(function() {
1010 if (this !== i
.element
[0]) {
1011 i
.snapElements
.push({
1013 width
: $t
.outerWidth(), height
: $t
.outerHeight(),
1014 top
: $o
.top
, left
: $o
.left
1020 drag: function( event
, ui
, inst
) {
1022 var ts
, bs
, ls
, rs
, l
, r
, t
, b
, i
, first
,
1024 d
= o
.snapTolerance
,
1025 x1
= ui
.offset
.left
, x2
= x1
+ inst
.helperProportions
.width
,
1026 y1
= ui
.offset
.top
, y2
= y1
+ inst
.helperProportions
.height
;
1028 for (i
= inst
.snapElements
.length
- 1; i
>= 0; i
--){
1030 l
= inst
.snapElements
[i
].left
- inst
.margins
.left
;
1031 r
= l
+ inst
.snapElements
[i
].width
;
1032 t
= inst
.snapElements
[i
].top
- inst
.margins
.top
;
1033 b
= t
+ inst
.snapElements
[i
].height
;
1035 if ( x2
< l
- d
|| x1
> r
+ d
|| y2
< t
- d
|| y1
> b
+ d
|| !$.contains( inst
.snapElements
[ i
].item
.ownerDocument
, inst
.snapElements
[ i
].item
) ) {
1036 if (inst
.snapElements
[i
].snapping
) {
1037 (inst
.options
.snap
.release
&& inst
.options
.snap
.release
.call(inst
.element
, event
, $.extend(inst
._uiHash(), { snapItem
: inst
.snapElements
[i
].item
})));
1039 inst
.snapElements
[i
].snapping
= false;
1043 if (o
.snapMode
!== "inner") {
1044 ts
= Math
.abs(t
- y2
) <= d
;
1045 bs
= Math
.abs(b
- y1
) <= d
;
1046 ls
= Math
.abs(l
- x2
) <= d
;
1047 rs
= Math
.abs(r
- x1
) <= d
;
1049 ui
.position
.top
= inst
._convertPositionTo("relative", { top
: t
- inst
.helperProportions
.height
, left
: 0 }).top
;
1052 ui
.position
.top
= inst
._convertPositionTo("relative", { top
: b
, left
: 0 }).top
;
1055 ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: l
- inst
.helperProportions
.width
}).left
;
1058 ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: r
}).left
;
1062 first
= (ts
|| bs
|| ls
|| rs
);
1064 if (o
.snapMode
!== "outer") {
1065 ts
= Math
.abs(t
- y1
) <= d
;
1066 bs
= Math
.abs(b
- y2
) <= d
;
1067 ls
= Math
.abs(l
- x1
) <= d
;
1068 rs
= Math
.abs(r
- x2
) <= d
;
1070 ui
.position
.top
= inst
._convertPositionTo("relative", { top
: t
, left
: 0 }).top
;
1073 ui
.position
.top
= inst
._convertPositionTo("relative", { top
: b
- inst
.helperProportions
.height
, left
: 0 }).top
;
1076 ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: l
}).left
;
1079 ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: r
- inst
.helperProportions
.width
}).left
;
1083 if (!inst
.snapElements
[i
].snapping
&& (ts
|| bs
|| ls
|| rs
|| first
)) {
1084 (inst
.options
.snap
.snap
&& inst
.options
.snap
.snap
.call(inst
.element
, event
, $.extend(inst
._uiHash(), { snapItem
: inst
.snapElements
[i
].item
})));
1086 inst
.snapElements
[i
].snapping
= (ts
|| bs
|| ls
|| rs
|| first
);
1093 $.ui
.plugin
.add("draggable", "stack", {
1094 start: function( event
, ui
, instance
) {
1096 o
= instance
.options
,
1097 group
= $.makeArray($(o
.stack
)).sort(function(a
, b
) {
1098 return (parseInt($(a
).css("zIndex"), 10) || 0) - (parseInt($(b
).css("zIndex"), 10) || 0);
1101 if (!group
.length
) { return; }
1103 min
= parseInt($(group
[0]).css("zIndex"), 10) || 0;
1104 $(group
).each(function(i
) {
1105 $(this).css("zIndex", min
+ i
);
1107 this.css("zIndex", (min
+ group
.length
));
1111 $.ui
.plugin
.add("draggable", "zIndex", {
1112 start: function( event
, ui
, instance
) {
1113 var t
= $( ui
.helper
),
1114 o
= instance
.options
;
1116 if (t
.css("zIndex")) {
1117 o
._zIndex
= t
.css("zIndex");
1119 t
.css("zIndex", o
.zIndex
);
1121 stop: function( event
, ui
, instance
) {
1122 var o
= instance
.options
;
1125 $(ui
.helper
).css("zIndex", o
._zIndex
);
1130 return $.ui
.draggable
;