4 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
8 * http://docs.jquery.com/UI
10 (function( $, undefined ) {
12 // prevent duplicate loading
13 // this is only a problem because we proxy existing functions
14 // and we don't want to double proxy them
29 COMMAND_LEFT
: 91, // COMMAND
40 MENU
: 93, // COMMAND_RIGHT
55 WINDOWS
: 91 // COMMAND
61 propAttr
: $.fn
.prop
|| $.fn
.attr
,
64 focus: function( delay
, fn
) {
65 return typeof delay
=== "number" ?
66 this.each(function() {
68 setTimeout(function() {
75 this._focus
.apply( this, arguments
);
78 scrollParent: function() {
80 if (($.browser
.msie
&& (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
81 scrollParent
= this.parents().filter(function() {
82 return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
85 scrollParent
= this.parents().filter(function() {
86 return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
90 return (/fixed/).test(this.css('position')) || !scrollParent
.length
? $(document
) : scrollParent
;
93 zIndex: function( zIndex
) {
94 if ( zIndex
!== undefined ) {
95 return this.css( "zIndex", zIndex
);
99 var elem
= $( this[ 0 ] ), position
, value
;
100 while ( elem
.length
&& elem
[ 0 ] !== document
) {
101 // Ignore z-index if position is set to a value where z-index is ignored by the browser
102 // This makes behavior of this function consistent across browsers
103 // WebKit always returns auto if the element is positioned
104 position
= elem
.css( "position" );
105 if ( position
=== "absolute" || position
=== "relative" || position
=== "fixed" ) {
106 // IE returns 0 when zIndex is not specified
107 // other browsers return a string
108 // we ignore the case of nested elements with an explicit value of 0
109 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
110 value
= parseInt( elem
.css( "zIndex" ), 10 );
111 if ( !isNaN( value
) && value
!== 0 ) {
115 elem
= elem
.parent();
122 disableSelection: function() {
123 return this.bind( ( $.support
.selectstart
? "selectstart" : "mousedown" ) +
124 ".ui-disableSelection", function( event
) {
125 event
.preventDefault();
129 enableSelection: function() {
130 return this.unbind( ".ui-disableSelection" );
134 $.each( [ "Width", "Height" ], function( i
, name
) {
135 var side
= name
=== "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
136 type
= name
.toLowerCase(),
138 innerWidth
: $.fn
.innerWidth
,
139 innerHeight
: $.fn
.innerHeight
,
140 outerWidth
: $.fn
.outerWidth
,
141 outerHeight
: $.fn
.outerHeight
144 function reduce( elem
, size
, border
, margin
) {
145 $.each( side
, function() {
146 size
-= parseFloat( $.curCSS( elem
, "padding" + this, true) ) || 0;
148 size
-= parseFloat( $.curCSS( elem
, "border" + this + "Width", true) ) || 0;
151 size
-= parseFloat( $.curCSS( elem
, "margin" + this, true) ) || 0;
157 $.fn
[ "inner" + name
] = function( size
) {
158 if ( size
=== undefined ) {
159 return orig
[ "inner" + name
].call( this );
162 return this.each(function() {
163 $( this ).css( type
, reduce( this, size
) + "px" );
167 $.fn
[ "outer" + name
] = function( size
, margin
) {
168 if ( typeof size
!== "number" ) {
169 return orig
[ "outer" + name
].call( this, size
);
172 return this.each(function() {
173 $( this).css( type
, reduce( this, size
, true, margin
) + "px" );
179 function focusable( element
, isTabIndexNotNaN
) {
180 var nodeName
= element
.nodeName
.toLowerCase();
181 if ( "area" === nodeName
) {
182 var map
= element
.parentNode
,
185 if ( !element
.href
|| !mapName
|| map
.nodeName
.toLowerCase() !== "map" ) {
188 img
= $( "img[usemap=#" + mapName
+ "]" )[0];
189 return !!img
&& visible( img
);
191 return ( /input|select|textarea|button|object/.test( nodeName
)
194 ? element
.href
|| isTabIndexNotNaN
196 // the element and all of its ancestors must be visible
197 && visible( element
);
200 function visible( element
) {
201 return !$( element
).parents().andSelf().filter(function() {
202 return $.curCSS( this, "visibility" ) === "hidden" ||
203 $.expr
.filters
.hidden( this );
207 $.extend( $.expr
[ ":" ], {
208 data: function( elem
, i
, match
) {
209 return !!$.data( elem
, match
[ 3 ] );
212 focusable: function( element
) {
213 return focusable( element
, !isNaN( $.attr( element
, "tabindex" ) ) );
216 tabbable: function( element
) {
217 var tabIndex
= $.attr( element
, "tabindex" ),
218 isTabIndexNaN
= isNaN( tabIndex
);
219 return ( isTabIndexNaN
|| tabIndex
>= 0 ) && focusable( element
, !isTabIndexNaN
);
225 var body
= document
.body
,
226 div
= body
.appendChild( div
= document
.createElement( "div" ) );
228 // access offsetHeight before setting the style to prevent a layout bug
229 // in IE 9 which causes the elemnt to continue to take up space even
230 // after it is removed from the DOM (#8026)
233 $.extend( div
.style
, {
240 $.support
.minHeight
= div
.offsetHeight
=== 100;
241 $.support
.selectstart
= "onselectstart" in div
;
243 // set display to none to avoid a layout bug in IE
244 // http://dev.jquery.com/ticket/4014
245 body
.removeChild( div
).style
.display
= "none";
254 // $.ui.plugin is deprecated. Use the proxy pattern instead.
256 add: function( module
, option
, set ) {
257 var proto
= $.ui
[ module
].prototype;
258 for ( var i
in set ) {
259 proto
.plugins
[ i
] = proto
.plugins
[ i
] || [];
260 proto
.plugins
[ i
].push( [ option
, set[ i
] ] );
263 call: function( instance
, name
, args
) {
264 var set = instance
.plugins
[ name
];
265 if ( !set || !instance
.element
[ 0 ].parentNode
) {
269 for ( var i
= 0; i
< set.length
; i
++ ) {
270 if ( instance
.options
[ set[ i
][ 0 ] ] ) {
271 set[ i
][ 1 ].apply( instance
.element
, args
);
277 // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains()
278 contains: function( a
, b
) {
279 return document
.compareDocumentPosition
?
280 a
.compareDocumentPosition( b
) & 16 :
281 a
!== b
&& a
.contains( b
);
284 // only used by resizable
285 hasScroll: function( el
, a
) {
287 //If overflow is hidden, the element might have extra content, but the user wants to hide it
288 if ( $( el
).css( "overflow" ) === "hidden") {
292 var scroll
= ( a
&& a
=== "left" ) ? "scrollLeft" : "scrollTop",
295 if ( el
[ scroll
] > 0 ) {
299 // TODO: determine which cases actually cause this to happen
300 // if the element doesn't have the scroll set, see if it's possible to
303 has
= ( el
[ scroll
] > 0 );
308 // these are odd functions, fix the API or move into individual plugins
309 isOverAxis: function( x
, reference
, size
) {
310 //Determines when x coordinate is over "b" element axis
311 return ( x
> reference
) && ( x
< ( reference
+ size
) );
313 isOver: function( y
, x
, top
, left
, height
, width
) {
314 //Determines when x, y coordinates is over "b" element
315 return $.ui
.isOverAxis( y
, top
, height
) && $.ui
.isOverAxis( x
, left
, width
);
321 * jQuery UI Widget 1.8.21
323 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
324 * Dual licensed under the MIT or GPL Version 2 licenses.
325 * http://jquery.org/license
327 * http://docs.jquery.com/UI/Widget
329 (function( $, undefined ) {
333 var _cleanData
= $.cleanData
;
334 $.cleanData = function( elems
) {
335 for ( var i
= 0, elem
; (elem
= elems
[i
]) != null; i
++ ) {
337 $( elem
).triggerHandler( "remove" );
338 // http://bugs.jquery.com/ticket/8235
344 var _remove
= $.fn
.remove
;
345 $.fn
.remove = function( selector
, keepData
) {
346 return this.each(function() {
348 if ( !selector
|| $.filter( selector
, [ this ] ).length
) {
349 $( "*", this ).add( [ this ] ).each(function() {
351 $( this ).triggerHandler( "remove" );
352 // http://bugs.jquery.com/ticket/8235
357 return _remove
.call( $(this), selector
, keepData
);
362 $.widget = function( name
, base
, prototype ) {
363 var namespace = name
.split( "." )[ 0 ],
365 name
= name
.split( "." )[ 1 ];
366 fullName
= namespace + "-" + name
;
373 // create selector for plugin
374 $.expr
[ ":" ][ fullName
] = function( elem
) {
375 return !!$.data( elem
, name
);
378 $[ namespace ] = $[ namespace ] || {};
379 $[ namespace ][ name
] = function( options
, element
) {
380 // allow instantiation without initializing for simple inheritance
381 if ( arguments
.length
) {
382 this._createWidget( options
, element
);
386 var basePrototype
= new base();
387 // we need to make the options hash a property directly on the new instance
388 // otherwise we'll modify the options hash on the prototype that we're
390 // $.each( basePrototype, function( key, val ) {
391 // if ( $.isPlainObject(val) ) {
392 // basePrototype[ key ] = $.extend( {}, val );
395 basePrototype
.options
= $.extend( true, {}, basePrototype
.options
);
396 $[ namespace ][ name
].prototype = $.extend( true, basePrototype
, {
397 namespace: namespace,
399 widgetEventPrefix
: $[ namespace ][ name
].prototype.widgetEventPrefix
|| name
,
400 widgetBaseClass
: fullName
403 $.widget
.bridge( name
, $[ namespace ][ name
] );
406 $.widget
.bridge = function( name
, object
) {
407 $.fn
[ name
] = function( options
) {
408 var isMethodCall
= typeof options
=== "string",
409 args
= Array
.prototype.slice
.call( arguments
, 1 ),
412 // allow multiple hashes to be passed on init
413 options
= !isMethodCall
&& args
.length
?
414 $.extend
.apply( null, [ true, options
].concat(args
) ) :
417 // prevent calls to internal methods
418 if ( isMethodCall
&& options
.charAt( 0 ) === "_" ) {
422 if ( isMethodCall
) {
423 this.each(function() {
424 var instance
= $.data( this, name
),
425 methodValue
= instance
&& $.isFunction( instance
[options
] ) ?
426 instance
[ options
].apply( instance
, args
) :
428 // TODO: add this back in 1.9 and use $.error() (see #5972)
429 // if ( !instance ) {
430 // throw "cannot call methods on " + name + " prior to initialization; " +
431 // "attempted to call method '" + options + "'";
433 // if ( !$.isFunction( instance[options] ) ) {
434 // throw "no such method '" + options + "' for " + name + " widget instance";
436 // var methodValue = instance[ options ].apply( instance, args );
437 if ( methodValue
!== instance
&& methodValue
!== undefined ) {
438 returnValue
= methodValue
;
443 this.each(function() {
444 var instance
= $.data( this, name
);
446 instance
.option( options
|| {} )._init();
448 $.data( this, name
, new object( options
, this ) );
457 $.Widget = function( options
, element
) {
458 // allow instantiation without initializing for simple inheritance
459 if ( arguments
.length
) {
460 this._createWidget( options
, element
);
464 $.Widget
.prototype = {
465 widgetName
: "widget",
466 widgetEventPrefix
: "",
470 _createWidget: function( options
, element
) {
471 // $.widget.bridge stores the plugin instance, but we do it anyway
472 // so that it's stored even before the _create function runs
473 $.data( element
, this.widgetName
, this );
474 this.element
= $( element
);
475 this.options
= $.extend( true, {},
477 this._getCreateOptions(),
481 this.element
.bind( "remove." + this.widgetName
, function() {
486 this._trigger( "create" );
489 _getCreateOptions: function() {
490 return $.metadata
&& $.metadata
.get( this.element
[0] )[ this.widgetName
];
492 _create: function() {},
493 _init: function() {},
495 destroy: function() {
497 .unbind( "." + this.widgetName
)
498 .removeData( this.widgetName
);
500 .unbind( "." + this.widgetName
)
501 .removeAttr( "aria-disabled" )
503 this.widgetBaseClass
+ "-disabled " +
504 "ui-state-disabled" );
511 option: function( key
, value
) {
514 if ( arguments
.length
=== 0 ) {
515 // don't return a reference to the internal hash
516 return $.extend( {}, this.options
);
519 if (typeof key
=== "string" ) {
520 if ( value
=== undefined ) {
521 return this.options
[ key
];
524 options
[ key
] = value
;
527 this._setOptions( options
);
531 _setOptions: function( options
) {
533 $.each( options
, function( key
, value
) {
534 self
._setOption( key
, value
);
539 _setOption: function( key
, value
) {
540 this.options
[ key
] = value
;
542 if ( key
=== "disabled" ) {
544 [ value
? "addClass" : "removeClass"](
545 this.widgetBaseClass
+ "-disabled" + " " +
546 "ui-state-disabled" )
547 .attr( "aria-disabled", value
);
554 return this._setOption( "disabled", false );
556 disable: function() {
557 return this._setOption( "disabled", true );
560 _trigger: function( type
, event
, data
) {
562 callback
= this.options
[ type
];
565 event
= $.Event( event
);
566 event
.type
= ( type
=== this.widgetEventPrefix
?
568 this.widgetEventPrefix
+ type
).toLowerCase();
569 // the original event may come from any element
570 // so we need to reset the target on the new event
571 event
.target
= this.element
[ 0 ];
573 // copy original event properties over to the new event
574 orig
= event
.originalEvent
;
576 for ( prop
in orig
) {
577 if ( !( prop
in event
) ) {
578 event
[ prop
] = orig
[ prop
];
583 this.element
.trigger( event
, data
);
585 return !( $.isFunction(callback
) &&
586 callback
.call( this.element
[0], event
, data
) === false ||
587 event
.isDefaultPrevented() );
593 * jQuery UI Mouse 1.8.21
595 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
596 * Dual licensed under the MIT or GPL Version 2 licenses.
597 * http://jquery.org/license
599 * http://docs.jquery.com/UI/Mouse
602 * jquery.ui.widget.js
604 (function( $, undefined ) {
606 var mouseHandled
= false;
607 $( document
).mouseup( function( e
) {
608 mouseHandled
= false;
611 $.widget("ui.mouse", {
613 cancel
: ':input,option',
617 _mouseInit: function() {
621 .bind('mousedown.'+this.widgetName
, function(event
) {
622 return self
._mouseDown(event
);
624 .bind('click.'+this.widgetName
, function(event
) {
625 if (true === $.data(event
.target
, self
.widgetName
+ '.preventClickEvent')) {
626 $.removeData(event
.target
, self
.widgetName
+ '.preventClickEvent');
627 event
.stopImmediatePropagation();
632 this.started
= false;
635 // TODO: make sure destroying one instance of mouse doesn't mess with
636 // other instances of mouse
637 _mouseDestroy: function() {
638 this.element
.unbind('.'+this.widgetName
);
640 .unbind('mousemove.'+this.widgetName
, this._mouseMoveDelegate
)
641 .unbind('mouseup.'+this.widgetName
, this._mouseUpDelegate
);
644 _mouseDown: function(event
) {
645 // don't let more than one widget handle mouseStart
646 if( mouseHandled
) { return };
648 // we may have missed mouseup (out of window)
649 (this._mouseStarted
&& this._mouseUp(event
));
651 this._mouseDownEvent
= event
;
654 btnIsLeft
= (event
.which
== 1),
655 // event.target.nodeName works around a bug in IE 8 with
656 // disabled inputs (#7620)
657 elIsCancel
= (typeof this.options
.cancel
== "string" && event
.target
.nodeName
? $(event
.target
).closest(this.options
.cancel
).length
: false);
658 if (!btnIsLeft
|| elIsCancel
|| !this._mouseCapture(event
)) {
662 this.mouseDelayMet
= !this.options
.delay
;
663 if (!this.mouseDelayMet
) {
664 this._mouseDelayTimer
= setTimeout(function() {
665 self
.mouseDelayMet
= true;
666 }, this.options
.delay
);
669 if (this._mouseDistanceMet(event
) && this._mouseDelayMet(event
)) {
670 this._mouseStarted
= (this._mouseStart(event
) !== false);
671 if (!this._mouseStarted
) {
672 event
.preventDefault();
677 // Click event may never have fired (Gecko & Opera)
678 if (true === $.data(event
.target
, this.widgetName
+ '.preventClickEvent')) {
679 $.removeData(event
.target
, this.widgetName
+ '.preventClickEvent');
682 // these delegates are required to keep context
683 this._mouseMoveDelegate = function(event
) {
684 return self
._mouseMove(event
);
686 this._mouseUpDelegate = function(event
) {
687 return self
._mouseUp(event
);
690 .bind('mousemove.'+this.widgetName
, this._mouseMoveDelegate
)
691 .bind('mouseup.'+this.widgetName
, this._mouseUpDelegate
);
693 event
.preventDefault();
699 _mouseMove: function(event
) {
700 // IE mouseup check - mouseup happened when mouse was out of window
701 if ($.browser
.msie
&& !(document
.documentMode
>= 9) && !event
.button
) {
702 return this._mouseUp(event
);
705 if (this._mouseStarted
) {
706 this._mouseDrag(event
);
707 return event
.preventDefault();
710 if (this._mouseDistanceMet(event
) && this._mouseDelayMet(event
)) {
712 (this._mouseStart(this._mouseDownEvent
, event
) !== false);
713 (this._mouseStarted
? this._mouseDrag(event
) : this._mouseUp(event
));
716 return !this._mouseStarted
;
719 _mouseUp: function(event
) {
721 .unbind('mousemove.'+this.widgetName
, this._mouseMoveDelegate
)
722 .unbind('mouseup.'+this.widgetName
, this._mouseUpDelegate
);
724 if (this._mouseStarted
) {
725 this._mouseStarted
= false;
727 if (event
.target
== this._mouseDownEvent
.target
) {
728 $.data(event
.target
, this.widgetName
+ '.preventClickEvent', true);
731 this._mouseStop(event
);
737 _mouseDistanceMet: function(event
) {
739 Math
.abs(this._mouseDownEvent
.pageX
- event
.pageX
),
740 Math
.abs(this._mouseDownEvent
.pageY
- event
.pageY
)
741 ) >= this.options
.distance
745 _mouseDelayMet: function(event
) {
746 return this.mouseDelayMet
;
749 // These are placeholder methods, to be overriden by extending plugin
750 _mouseStart: function(event
) {},
751 _mouseDrag: function(event
) {},
752 _mouseStop: function(event
) {},
753 _mouseCapture: function(event
) { return true; }
758 * jQuery UI Draggable 1.8.21
760 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
761 * Dual licensed under the MIT or GPL Version 2 licenses.
762 * http://jquery.org/license
764 * http://docs.jquery.com/UI/Draggables
769 * jquery.ui.widget.js
771 (function( $, undefined ) {
773 $.widget("ui.draggable", $.ui
.mouse
, {
774 widgetEventPrefix
: "drag",
779 connectToSortable
: false,
788 refreshPositions
: false,
793 scrollSensitivity
: 20,
801 _create: function() {
803 if (this.options
.helper
== 'original' && !(/^(?:r|a|f)/).test(this.element
.css("position")))
804 this.element
[0].style
.position
= 'relative';
806 (this.options
.addClasses
&& this.element
.addClass("ui-draggable"));
807 (this.options
.disabled
&& this.element
.addClass("ui-draggable-disabled"));
813 destroy: function() {
814 if(!this.element
.data('draggable')) return;
816 .removeData("draggable")
817 .unbind(".draggable")
818 .removeClass("ui-draggable"
819 + " ui-draggable-dragging"
820 + " ui-draggable-disabled");
821 this._mouseDestroy();
826 _mouseCapture: function(event
) {
828 var o
= this.options
;
830 // among others, prevent a drag on a resizable-handle
831 if (this.helper
|| o
.disabled
|| $(event
.target
).is('.ui-resizable-handle'))
834 //Quit if we're not on a valid handle
835 this.handle
= this._getHandle(event
);
840 $(o
.iframeFix
=== true ? "iframe" : o
.iframeFix
).each(function() {
841 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
843 width
: this.offsetWidth
+"px", height
: this.offsetHeight
+"px",
844 position
: "absolute", opacity
: "0.001", zIndex
: 1000
846 .css($(this).offset())
855 _mouseStart: function(event
) {
857 var o
= this.options
;
859 //Create and append the visible helper
860 this.helper
= this._createHelper(event
);
862 this.helper
.addClass("ui-draggable-dragging");
864 //Cache the helper size
865 this._cacheHelperProportions();
867 //If ddmanager is used for droppables, set the global draggable
869 $.ui
.ddmanager
.current
= this;
872 * - Position generation -
873 * This block generates everything position related - it's the core of draggables.
876 //Cache the margins of the original element
877 this._cacheMargins();
879 //Store the helper's css position
880 this.cssPosition
= this.helper
.css("position");
881 this.scrollParent
= this.helper
.scrollParent();
883 //The element's absolute position on the page minus margins
884 this.offset
= this.positionAbs
= this.element
.offset();
886 top
: this.offset
.top
- this.margins
.top
,
887 left
: this.offset
.left
- this.margins
.left
890 $.extend(this.offset
, {
891 click
: { //Where the click happened, relative to the element
892 left
: event
.pageX
- this.offset
.left
,
893 top
: event
.pageY
- this.offset
.top
895 parent
: this._getParentOffset(),
896 relative
: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
899 //Generate the original position
900 this.originalPosition
= this.position
= this._generatePosition(event
);
901 this.originalPageX
= event
.pageX
;
902 this.originalPageY
= event
.pageY
;
904 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
905 (o
.cursorAt
&& this._adjustOffsetFromHelper(o
.cursorAt
));
907 //Set a containment if given in the options
909 this._setContainment();
911 //Trigger event + callbacks
912 if(this._trigger("start", event
) === false) {
917 //Recache the helper size
918 this._cacheHelperProportions();
920 //Prepare the droppable offsets
921 if ($.ui
.ddmanager
&& !o
.dropBehaviour
)
922 $.ui
.ddmanager
.prepareOffsets(this, event
);
925 this._mouseDrag(event
, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
927 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
928 if ( $.ui
.ddmanager
) $.ui
.ddmanager
.dragStart(this, event
);
933 _mouseDrag: function(event
, noPropagation
) {
935 //Compute the helpers position
936 this.position
= this._generatePosition(event
);
937 this.positionAbs
= this._convertPositionTo("absolute");
939 //Call plugins and callbacks and use the resulting position if something is returned
940 if (!noPropagation
) {
941 var ui
= this._uiHash();
942 if(this._trigger('drag', event
, ui
) === false) {
946 this.position
= ui
.position
;
949 if(!this.options
.axis
|| this.options
.axis
!= "y") this.helper
[0].style
.left
= this.position
.left
+'px';
950 if(!this.options
.axis
|| this.options
.axis
!= "x") this.helper
[0].style
.top
= this.position
.top
+'px';
951 if($.ui
.ddmanager
) $.ui
.ddmanager
.drag(this, event
);
956 _mouseStop: function(event
) {
958 //If we are using droppables, inform the manager about the drop
960 if ($.ui
.ddmanager
&& !this.options
.dropBehaviour
)
961 dropped
= $.ui
.ddmanager
.drop(this, event
);
963 //if a drop comes from outside (a sortable)
965 dropped
= this.dropped
;
966 this.dropped
= false;
969 //if the original element is no longer in the DOM don't bother to continue (see #8269)
970 var element
= this.element
[0], elementInDom
= false;
971 while ( element
&& (element
= element
.parentNode
) ) {
972 if (element
== document
) {
976 if ( !elementInDom
&& this.options
.helper
=== "original" )
979 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
))) {
981 $(this.helper
).animate(this.originalPosition
, parseInt(this.options
.revertDuration
, 10), function() {
982 if(self
._trigger("stop", event
) !== false) {
987 if(this._trigger("stop", event
) !== false) {
995 _mouseUp: function(event
) {
996 if (this.options
.iframeFix
=== true) {
997 $("div.ui-draggable-iframeFix").each(function() {
998 this.parentNode
.removeChild(this);
999 }); //Remove frame helpers
1002 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1003 if( $.ui
.ddmanager
) $.ui
.ddmanager
.dragStop(this, event
);
1005 return $.ui
.mouse
.prototype._mouseUp
.call(this, event
);
1008 cancel: function() {
1010 if(this.helper
.is(".ui-draggable-dragging")) {
1020 _getHandle: function(event
) {
1022 var handle
= !this.options
.handle
|| !$(this.options
.handle
, this.element
).length
? true : false;
1023 $(this.options
.handle
, this.element
)
1027 if(this == event
.target
) handle
= true;
1034 _createHelper: function(event
) {
1036 var o
= this.options
;
1037 var helper
= $.isFunction(o
.helper
) ? $(o
.helper
.apply(this.element
[0], [event
])) : (o
.helper
== 'clone' ? this.element
.clone().removeAttr('id') : this.element
);
1039 if(!helper
.parents('body').length
)
1040 helper
.appendTo((o
.appendTo
== 'parent' ? this.element
[0].parentNode
: o
.appendTo
));
1042 if(helper
[0] != this.element
[0] && !(/(fixed|absolute)/).test(helper
.css("position")))
1043 helper
.css("position", "absolute");
1049 _adjustOffsetFromHelper: function(obj
) {
1050 if (typeof obj
== 'string') {
1051 obj
= obj
.split(' ');
1053 if ($.isArray(obj
)) {
1054 obj
= {left
: +obj
[0], top
: +obj
[1] || 0};
1056 if ('left' in obj
) {
1057 this.offset
.click
.left
= obj
.left
+ this.margins
.left
;
1059 if ('right' in obj
) {
1060 this.offset
.click
.left
= this.helperProportions
.width
- obj
.right
+ this.margins
.left
;
1063 this.offset
.click
.top
= obj
.top
+ this.margins
.top
;
1065 if ('bottom' in obj
) {
1066 this.offset
.click
.top
= this.helperProportions
.height
- obj
.bottom
+ this.margins
.top
;
1070 _getParentOffset: function() {
1072 //Get the offsetParent and cache its position
1073 this.offsetParent
= this.helper
.offsetParent();
1074 var po
= this.offsetParent
.offset();
1076 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1077 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1078 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1079 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1080 if(this.cssPosition
== 'absolute' && this.scrollParent
[0] != document
&& $.ui
.contains(this.scrollParent
[0], this.offsetParent
[0])) {
1081 po
.left
+= this.scrollParent
.scrollLeft();
1082 po
.top
+= this.scrollParent
.scrollTop();
1085 if((this.offsetParent
[0] == document
.body
) //This needs to be actually done for all browsers, since pageX/pageY includes this information
1086 || (this.offsetParent
[0].tagName
&& this.offsetParent
[0].tagName
.toLowerCase() == 'html' && $.browser
.msie
)) //Ugly IE fix
1087 po
= { top
: 0, left
: 0 };
1090 top
: po
.top
+ (parseInt(this.offsetParent
.css("borderTopWidth"),10) || 0),
1091 left
: po
.left
+ (parseInt(this.offsetParent
.css("borderLeftWidth"),10) || 0)
1096 _getRelativeOffset: function() {
1098 if(this.cssPosition
== "relative") {
1099 var p
= this.element
.position();
1101 top
: p
.top
- (parseInt(this.helper
.css("top"),10) || 0) + this.scrollParent
.scrollTop(),
1102 left
: p
.left
- (parseInt(this.helper
.css("left"),10) || 0) + this.scrollParent
.scrollLeft()
1105 return { top
: 0, left
: 0 };
1110 _cacheMargins: function() {
1112 left
: (parseInt(this.element
.css("marginLeft"),10) || 0),
1113 top
: (parseInt(this.element
.css("marginTop"),10) || 0),
1114 right
: (parseInt(this.element
.css("marginRight"),10) || 0),
1115 bottom
: (parseInt(this.element
.css("marginBottom"),10) || 0)
1119 _cacheHelperProportions: function() {
1120 this.helperProportions
= {
1121 width
: this.helper
.outerWidth(),
1122 height
: this.helper
.outerHeight()
1126 _setContainment: function() {
1128 var o
= this.options
;
1129 if(o
.containment
== 'parent') o
.containment
= this.helper
[0].parentNode
;
1130 if(o
.containment
== 'document' || o
.containment
== 'window') this.containment
= [
1131 o
.containment
== 'document' ? 0 : $(window
).scrollLeft() - this.offset
.relative
.left
- this.offset
.parent
.left
,
1132 o
.containment
== 'document' ? 0 : $(window
).scrollTop() - this.offset
.relative
.top
- this.offset
.parent
.top
,
1133 (o
.containment
== 'document' ? 0 : $(window
).scrollLeft()) + $(o
.containment
== 'document' ? document
: window
).width() - this.helperProportions
.width
- this.margins
.left
,
1134 (o
.containment
== 'document' ? 0 : $(window
).scrollTop()) + ($(o
.containment
== 'document' ? document
: window
).height() || document
.body
.parentNode
.scrollHeight
) - this.helperProportions
.height
- this.margins
.top
1137 if(!(/^(document|window|parent)$/).test(o
.containment
) && o
.containment
.constructor != Array
) {
1138 var c
= $(o
.containment
);
1139 var ce
= c
[0]; if(!ce
) return;
1140 var co
= c
.offset();
1141 var over
= ($(ce
).css("overflow") != 'hidden');
1143 this.containment
= [
1144 (parseInt($(ce
).css("borderLeftWidth"),10) || 0) + (parseInt($(ce
).css("paddingLeft"),10) || 0),
1145 (parseInt($(ce
).css("borderTopWidth"),10) || 0) + (parseInt($(ce
).css("paddingTop"),10) || 0),
1146 (over
? Math
.max(ce
.scrollWidth
,ce
.offsetWidth
) : ce
.offsetWidth
) - (parseInt($(ce
).css("borderLeftWidth"),10) || 0) - (parseInt($(ce
).css("paddingRight"),10) || 0) - this.helperProportions
.width
- this.margins
.left
- this.margins
.right
,
1147 (over
? Math
.max(ce
.scrollHeight
,ce
.offsetHeight
) : ce
.offsetHeight
) - (parseInt($(ce
).css("borderTopWidth"),10) || 0) - (parseInt($(ce
).css("paddingBottom"),10) || 0) - this.helperProportions
.height
- this.margins
.top
- this.margins
.bottom
1149 this.relative_container
= c
;
1151 } else if(o
.containment
.constructor == Array
) {
1152 this.containment
= o
.containment
;
1157 _convertPositionTo: function(d
, pos
) {
1159 if(!pos
) pos
= this.position
;
1160 var mod
= d
== "absolute" ? 1 : -1;
1161 var o
= this.options
, scroll
= this.cssPosition
== 'absolute' && !(this.scrollParent
[0] != document
&& $.ui
.contains(this.scrollParent
[0], this.offsetParent
[0])) ? this.offsetParent
: this.scrollParent
, scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
1165 pos
.top
// The absolute mouse position
1166 + this.offset
.relative
.top
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
1167 + this.offset
.parent
.top
* mod
// The offsetParent's offset without borders (offset + border)
1168 - ($.browser
.safari
&& $.browser
.version
< 526 && this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) ) * mod
)
1171 pos
.left
// The absolute mouse position
1172 + this.offset
.relative
.left
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
1173 + this.offset
.parent
.left
* mod
// The offsetParent's offset without borders (offset + border)
1174 - ($.browser
.safari
&& $.browser
.version
< 526 && this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : scrollIsRootNode
? 0 : scroll
.scrollLeft() ) * mod
)
1180 _generatePosition: function(event
) {
1182 var o
= this.options
, scroll
= this.cssPosition
== 'absolute' && !(this.scrollParent
[0] != document
&& $.ui
.contains(this.scrollParent
[0], this.offsetParent
[0])) ? this.offsetParent
: this.scrollParent
, scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
1183 var pageX
= event
.pageX
;
1184 var pageY
= event
.pageY
;
1187 * - Position constraining -
1188 * Constrain the position to a mix of grid, containment.
1191 if(this.originalPosition
) { //If we are not dragging yet, we won't check for options
1193 if(this.containment
) {
1194 if (this.relative_container
){
1195 var co
= this.relative_container
.offset();
1196 containment
= [ this.containment
[0] + co
.left
,
1197 this.containment
[1] + co
.top
,
1198 this.containment
[2] + co
.left
,
1199 this.containment
[3] + co
.top
];
1202 containment
= this.containment
;
1205 if(event
.pageX
- this.offset
.click
.left
< containment
[0]) pageX
= containment
[0] + this.offset
.click
.left
;
1206 if(event
.pageY
- this.offset
.click
.top
< containment
[1]) pageY
= containment
[1] + this.offset
.click
.top
;
1207 if(event
.pageX
- this.offset
.click
.left
> containment
[2]) pageX
= containment
[2] + this.offset
.click
.left
;
1208 if(event
.pageY
- this.offset
.click
.top
> containment
[3]) pageY
= containment
[3] + this.offset
.click
.top
;
1212 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1213 var top
= o
.grid
[1] ? this.originalPageY
+ Math
.round((pageY
- this.originalPageY
) / o
.grid
[1]) * o
.grid
[1] : this.originalPageY
;
1214 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
;
1216 var left
= o
.grid
[0] ? this.originalPageX
+ Math
.round((pageX
- this.originalPageX
) / o
.grid
[0]) * o
.grid
[0] : this.originalPageX
;
1217 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
;
1224 pageY
// The absolute mouse position
1225 - this.offset
.click
.top
// Click offset (relative to the element)
1226 - this.offset
.relative
.top
// Only for relative positioned nodes: Relative offset from element to offset parent
1227 - this.offset
.parent
.top
// The offsetParent's offset without borders (offset + border)
1228 + ($.browser
.safari
&& $.browser
.version
< 526 && this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) ))
1231 pageX
// The absolute mouse position
1232 - this.offset
.click
.left
// Click offset (relative to the element)
1233 - this.offset
.relative
.left
// Only for relative positioned nodes: Relative offset from element to offset parent
1234 - this.offset
.parent
.left
// The offsetParent's offset without borders (offset + border)
1235 + ($.browser
.safari
&& $.browser
.version
< 526 && this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : scrollIsRootNode
? 0 : scroll
.scrollLeft() ))
1241 _clear: function() {
1242 this.helper
.removeClass("ui-draggable-dragging");
1243 if(this.helper
[0] != this.element
[0] && !this.cancelHelperRemoval
) this.helper
.remove();
1244 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
1246 this.cancelHelperRemoval
= false;
1249 // From now on bulk stuff - mainly helpers
1251 _trigger: function(type
, event
, ui
) {
1252 ui
= ui
|| this._uiHash();
1253 $.ui
.plugin
.call(this, type
, [event
, ui
]);
1254 if(type
== "drag") this.positionAbs
= this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
1255 return $.Widget
.prototype._trigger
.call(this, type
, event
, ui
);
1260 _uiHash: function(event
) {
1262 helper
: this.helper
,
1263 position
: this.position
,
1264 originalPosition
: this.originalPosition
,
1265 offset
: this.positionAbs
1271 $.extend($.ui
.draggable
, {
1275 $.ui
.plugin
.add("draggable", "connectToSortable", {
1276 start: function(event
, ui
) {
1278 var inst
= $(this).data("draggable"), o
= inst
.options
,
1279 uiSortable
= $.extend({}, ui
, { item
: inst
.element
});
1280 inst
.sortables
= [];
1281 $(o
.connectToSortable
).each(function() {
1282 var sortable
= $.data(this, 'sortable');
1283 if (sortable
&& !sortable
.options
.disabled
) {
1284 inst
.sortables
.push({
1286 shouldRevert
: sortable
.options
.revert
1288 sortable
.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
1289 sortable
._trigger("activate", event
, uiSortable
);
1294 stop: function(event
, ui
) {
1296 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
1297 var inst
= $(this).data("draggable"),
1298 uiSortable
= $.extend({}, ui
, { item
: inst
.element
});
1300 $.each(inst
.sortables
, function() {
1301 if(this.instance
.isOver
) {
1303 this.instance
.isOver
= 0;
1305 inst
.cancelHelperRemoval
= true; //Don't remove the helper in the draggable instance
1306 this.instance
.cancelHelperRemoval
= false; //Remove it in the sortable instance (so sortable plugins like revert still work)
1308 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
1309 if(this.shouldRevert
) this.instance
.options
.revert
= true;
1311 //Trigger the stop of the sortable
1312 this.instance
._mouseStop(event
);
1314 this.instance
.options
.helper
= this.instance
.options
._helper
;
1316 //If the helper has been the original item, restore properties in the sortable
1317 if(inst
.options
.helper
== 'original')
1318 this.instance
.currentItem
.css({ top
: 'auto', left
: 'auto' });
1321 this.instance
.cancelHelperRemoval
= false; //Remove the helper in the sortable instance
1322 this.instance
._trigger("deactivate", event
, uiSortable
);
1328 drag: function(event
, ui
) {
1330 var inst
= $(this).data("draggable"), self
= this;
1332 var checkPos = function(o
) {
1333 var dyClick
= this.offset
.click
.top
, dxClick
= this.offset
.click
.left
;
1334 var helperTop
= this.positionAbs
.top
, helperLeft
= this.positionAbs
.left
;
1335 var itemHeight
= o
.height
, itemWidth
= o
.width
;
1336 var itemTop
= o
.top
, itemLeft
= o
.left
;
1338 return $.ui
.isOver(helperTop
+ dyClick
, helperLeft
+ dxClick
, itemTop
, itemLeft
, itemHeight
, itemWidth
);
1341 $.each(inst
.sortables
, function(i
) {
1343 //Copy over some variables to allow calling the sortable's native _intersectsWith
1344 this.instance
.positionAbs
= inst
.positionAbs
;
1345 this.instance
.helperProportions
= inst
.helperProportions
;
1346 this.instance
.offset
.click
= inst
.offset
.click
;
1348 if(this.instance
._intersectsWith(this.instance
.containerCache
)) {
1350 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1351 if(!this.instance
.isOver
) {
1353 this.instance
.isOver
= 1;
1354 //Now we fake the start of dragging for the sortable instance,
1355 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1356 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
1357 this.instance
.currentItem
= $(self
).clone().removeAttr('id').appendTo(this.instance
.element
).data("sortable-item", true);
1358 this.instance
.options
._helper
= this.instance
.options
.helper
; //Store helper option to later restore it
1359 this.instance
.options
.helper = function() { return ui
.helper
[0]; };
1361 event
.target
= this.instance
.currentItem
[0];
1362 this.instance
._mouseCapture(event
, true);
1363 this.instance
._mouseStart(event
, true, true);
1365 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1366 this.instance
.offset
.click
.top
= inst
.offset
.click
.top
;
1367 this.instance
.offset
.click
.left
= inst
.offset
.click
.left
;
1368 this.instance
.offset
.parent
.left
-= inst
.offset
.parent
.left
- this.instance
.offset
.parent
.left
;
1369 this.instance
.offset
.parent
.top
-= inst
.offset
.parent
.top
- this.instance
.offset
.parent
.top
;
1371 inst
._trigger("toSortable", event
);
1372 inst
.dropped
= this.instance
.element
; //draggable revert needs that
1373 //hack so receive/update callbacks work (mostly)
1374 inst
.currentItem
= inst
.element
;
1375 this.instance
.fromOutside
= inst
;
1379 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
1380 if(this.instance
.currentItem
) this.instance
._mouseDrag(event
);
1384 //If it doesn't intersect with the sortable, and it intersected before,
1385 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1386 if(this.instance
.isOver
) {
1388 this.instance
.isOver
= 0;
1389 this.instance
.cancelHelperRemoval
= true;
1391 //Prevent reverting on this forced stop
1392 this.instance
.options
.revert
= false;
1394 // The out event needs to be triggered independently
1395 this.instance
._trigger('out', event
, this.instance
._uiHash(this.instance
));
1397 this.instance
._mouseStop(event
, true);
1398 this.instance
.options
.helper
= this.instance
.options
._helper
;
1400 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1401 this.instance
.currentItem
.remove();
1402 if(this.instance
.placeholder
) this.instance
.placeholder
.remove();
1404 inst
._trigger("fromSortable", event
);
1405 inst
.dropped
= false; //draggable revert needs that
1415 $.ui
.plugin
.add("draggable", "cursor", {
1416 start: function(event
, ui
) {
1417 var t
= $('body'), o
= $(this).data('draggable').options
;
1418 if (t
.css("cursor")) o
._cursor
= t
.css("cursor");
1419 t
.css("cursor", o
.cursor
);
1421 stop: function(event
, ui
) {
1422 var o
= $(this).data('draggable').options
;
1423 if (o
._cursor
) $('body').css("cursor", o
._cursor
);
1427 $.ui
.plugin
.add("draggable", "opacity", {
1428 start: function(event
, ui
) {
1429 var t
= $(ui
.helper
), o
= $(this).data('draggable').options
;
1430 if(t
.css("opacity")) o
._opacity
= t
.css("opacity");
1431 t
.css('opacity', o
.opacity
);
1433 stop: function(event
, ui
) {
1434 var o
= $(this).data('draggable').options
;
1435 if(o
._opacity
) $(ui
.helper
).css('opacity', o
._opacity
);
1439 $.ui
.plugin
.add("draggable", "scroll", {
1440 start: function(event
, ui
) {
1441 var i
= $(this).data("draggable");
1442 if(i
.scrollParent
[0] != document
&& i
.scrollParent
[0].tagName
!= 'HTML') i
.overflowOffset
= i
.scrollParent
.offset();
1444 drag: function(event
, ui
) {
1446 var i
= $(this).data("draggable"), o
= i
.options
, scrolled
= false;
1448 if(i
.scrollParent
[0] != document
&& i
.scrollParent
[0].tagName
!= 'HTML') {
1450 if(!o
.axis
|| o
.axis
!= 'x') {
1451 if((i
.overflowOffset
.top
+ i
.scrollParent
[0].offsetHeight
) - event
.pageY
< o
.scrollSensitivity
)
1452 i
.scrollParent
[0].scrollTop
= scrolled
= i
.scrollParent
[0].scrollTop
+ o
.scrollSpeed
;
1453 else if(event
.pageY
- i
.overflowOffset
.top
< o
.scrollSensitivity
)
1454 i
.scrollParent
[0].scrollTop
= scrolled
= i
.scrollParent
[0].scrollTop
- o
.scrollSpeed
;
1457 if(!o
.axis
|| o
.axis
!= 'y') {
1458 if((i
.overflowOffset
.left
+ i
.scrollParent
[0].offsetWidth
) - event
.pageX
< o
.scrollSensitivity
)
1459 i
.scrollParent
[0].scrollLeft
= scrolled
= i
.scrollParent
[0].scrollLeft
+ o
.scrollSpeed
;
1460 else if(event
.pageX
- i
.overflowOffset
.left
< o
.scrollSensitivity
)
1461 i
.scrollParent
[0].scrollLeft
= scrolled
= i
.scrollParent
[0].scrollLeft
- o
.scrollSpeed
;
1466 if(!o
.axis
|| o
.axis
!= 'x') {
1467 if(event
.pageY
- $(document
).scrollTop() < o
.scrollSensitivity
)
1468 scrolled
= $(document
).scrollTop($(document
).scrollTop() - o
.scrollSpeed
);
1469 else if($(window
).height() - (event
.pageY
- $(document
).scrollTop()) < o
.scrollSensitivity
)
1470 scrolled
= $(document
).scrollTop($(document
).scrollTop() + o
.scrollSpeed
);
1473 if(!o
.axis
|| o
.axis
!= 'y') {
1474 if(event
.pageX
- $(document
).scrollLeft() < o
.scrollSensitivity
)
1475 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() - o
.scrollSpeed
);
1476 else if($(window
).width() - (event
.pageX
- $(document
).scrollLeft()) < o
.scrollSensitivity
)
1477 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() + o
.scrollSpeed
);
1482 if(scrolled
!== false && $.ui
.ddmanager
&& !o
.dropBehaviour
)
1483 $.ui
.ddmanager
.prepareOffsets(i
, event
);
1488 $.ui
.plugin
.add("draggable", "snap", {
1489 start: function(event
, ui
) {
1491 var i
= $(this).data("draggable"), o
= i
.options
;
1492 i
.snapElements
= [];
1494 $(o
.snap
.constructor != String
? ( o
.snap
.items
|| ':data(draggable)' ) : o
.snap
).each(function() {
1495 var $t
= $(this); var $o
= $t
.offset();
1496 if(this != i
.element
[0]) i
.snapElements
.push({
1498 width
: $t
.outerWidth(), height
: $t
.outerHeight(),
1499 top
: $o
.top
, left
: $o
.left
1504 drag: function(event
, ui
) {
1506 var inst
= $(this).data("draggable"), o
= inst
.options
;
1507 var d
= o
.snapTolerance
;
1509 var x1
= ui
.offset
.left
, x2
= x1
+ inst
.helperProportions
.width
,
1510 y1
= ui
.offset
.top
, y2
= y1
+ inst
.helperProportions
.height
;
1512 for (var i
= inst
.snapElements
.length
- 1; i
>= 0; i
--){
1514 var l
= inst
.snapElements
[i
].left
, r
= l
+ inst
.snapElements
[i
].width
,
1515 t
= inst
.snapElements
[i
].top
, b
= t
+ inst
.snapElements
[i
].height
;
1517 //Yes, I know, this is insane ;)
1518 if(!((l
-d
< x1
&& x1
< r
+d
&& t
-d
< y1
&& y1
< b
+d
) || (l
-d
< x1
&& x1
< r
+d
&& t
-d
< y2
&& y2
< b
+d
) || (l
-d
< x2
&& x2
< r
+d
&& t
-d
< y1
&& y1
< b
+d
) || (l
-d
< x2
&& x2
< r
+d
&& t
-d
< y2
&& y2
< b
+d
))) {
1519 if(inst
.snapElements
[i
].snapping
) (inst
.options
.snap
.release
&& inst
.options
.snap
.release
.call(inst
.element
, event
, $.extend(inst
._uiHash(), { snapItem
: inst
.snapElements
[i
].item
})));
1520 inst
.snapElements
[i
].snapping
= false;
1524 if(o
.snapMode
!= 'inner') {
1525 var ts
= Math
.abs(t
- y2
) <= d
;
1526 var bs
= Math
.abs(b
- y1
) <= d
;
1527 var ls
= Math
.abs(l
- x2
) <= d
;
1528 var rs
= Math
.abs(r
- x1
) <= d
;
1529 if(ts
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: t
- inst
.helperProportions
.height
, left
: 0 }).top
- inst
.margins
.top
;
1530 if(bs
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: b
, left
: 0 }).top
- inst
.margins
.top
;
1531 if(ls
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: l
- inst
.helperProportions
.width
}).left
- inst
.margins
.left
;
1532 if(rs
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: r
}).left
- inst
.margins
.left
;
1535 var first
= (ts
|| bs
|| ls
|| rs
);
1537 if(o
.snapMode
!= 'outer') {
1538 var ts
= Math
.abs(t
- y1
) <= d
;
1539 var bs
= Math
.abs(b
- y2
) <= d
;
1540 var ls
= Math
.abs(l
- x1
) <= d
;
1541 var rs
= Math
.abs(r
- x2
) <= d
;
1542 if(ts
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: t
, left
: 0 }).top
- inst
.margins
.top
;
1543 if(bs
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: b
- inst
.helperProportions
.height
, left
: 0 }).top
- inst
.margins
.top
;
1544 if(ls
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: l
}).left
- inst
.margins
.left
;
1545 if(rs
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: r
- inst
.helperProportions
.width
}).left
- inst
.margins
.left
;
1548 if(!inst
.snapElements
[i
].snapping
&& (ts
|| bs
|| ls
|| rs
|| first
))
1549 (inst
.options
.snap
.snap
&& inst
.options
.snap
.snap
.call(inst
.element
, event
, $.extend(inst
._uiHash(), { snapItem
: inst
.snapElements
[i
].item
})));
1550 inst
.snapElements
[i
].snapping
= (ts
|| bs
|| ls
|| rs
|| first
);
1557 $.ui
.plugin
.add("draggable", "stack", {
1558 start: function(event
, ui
) {
1560 var o
= $(this).data("draggable").options
;
1562 var group
= $.makeArray($(o
.stack
)).sort(function(a
,b
) {
1563 return (parseInt($(a
).css("zIndex"),10) || 0) - (parseInt($(b
).css("zIndex"),10) || 0);
1565 if (!group
.length
) { return; }
1567 var min
= parseInt(group
[0].style
.zIndex
) || 0;
1568 $(group
).each(function(i
) {
1569 this.style
.zIndex
= min
+ i
;
1572 this[0].style
.zIndex
= min
+ group
.length
;
1577 $.ui
.plugin
.add("draggable", "zIndex", {
1578 start: function(event
, ui
) {
1579 var t
= $(ui
.helper
), o
= $(this).data("draggable").options
;
1580 if(t
.css("zIndex")) o
._zIndex
= t
.css("zIndex");
1581 t
.css('zIndex', o
.zIndex
);
1583 stop: function(event
, ui
) {
1584 var o
= $(this).data("draggable").options
;
1585 if(o
._zIndex
) $(ui
.helper
).css('zIndex', o
._zIndex
);
1591 * jQuery UI Sortable 1.8.21
1593 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
1594 * Dual licensed under the MIT or GPL Version 2 licenses.
1595 * http://jquery.org/license
1597 * http://docs.jquery.com/UI/Sortables
1601 * jquery.ui.mouse.js
1602 * jquery.ui.widget.js
1604 (function( $, undefined ) {
1606 $.widget("ui.sortable", $.ui
.mouse
, {
1607 widgetEventPrefix
: "sort",
1617 forcePlaceholderSize
: false,
1618 forceHelperSize
: false,
1627 scrollSensitivity
: 20,
1630 tolerance
: "intersect",
1633 _create: function() {
1635 var o
= this.options
;
1636 this.containerCache
= {};
1637 this.element
.addClass("ui-sortable");
1642 //Let's determine if the items are being displayed horizontally
1643 this.floating
= this.items
.length
? o
.axis
=== 'x' || (/left|right/).test(this.items
[0].item
.css('float')) || (/inline|table-cell/).test(this.items
[0].item
.css('display')) : false;
1645 //Let's determine the parent's offset
1646 this.offset
= this.element
.offset();
1648 //Initialize mouse events for interaction
1656 destroy: function() {
1657 $.Widget
.prototype.destroy
.call( this );
1659 .removeClass("ui-sortable ui-sortable-disabled");
1660 this._mouseDestroy();
1662 for ( var i
= this.items
.length
- 1; i
>= 0; i
-- )
1663 this.items
[i
].item
.removeData(this.widgetName
+ "-item");
1668 _setOption: function(key
, value
){
1669 if ( key
=== "disabled" ) {
1670 this.options
[ key
] = value
;
1673 [ value
? "addClass" : "removeClass"]( "ui-sortable-disabled" );
1675 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
1676 $.Widget
.prototype._setOption
.apply(this, arguments
);
1680 _mouseCapture: function(event
, overrideHandle
) {
1683 if (this.reverting
) {
1687 if(this.options
.disabled
|| this.options
.type
== 'static') return false;
1689 //We have to refresh the items data once first
1690 this._refreshItems(event
);
1692 //Find out if the clicked node (or one of its parents) is a actual item in this.items
1693 var currentItem
= null, self
= this, nodes
= $(event
.target
).parents().each(function() {
1694 if($.data(this, that
.widgetName
+ '-item') == self
) {
1695 currentItem
= $(this);
1699 if($.data(event
.target
, that
.widgetName
+ '-item') == self
) currentItem
= $(event
.target
);
1701 if(!currentItem
) return false;
1702 if(this.options
.handle
&& !overrideHandle
) {
1703 var validHandle
= false;
1705 $(this.options
.handle
, currentItem
).find("*").andSelf().each(function() { if(this == event
.target
) validHandle
= true; });
1706 if(!validHandle
) return false;
1709 this.currentItem
= currentItem
;
1710 this._removeCurrentsFromItems();
1715 _mouseStart: function(event
, overrideHandle
, noActivation
) {
1717 var o
= this.options
, self
= this;
1718 this.currentContainer
= this;
1720 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
1721 this.refreshPositions();
1723 //Create and append the visible helper
1724 this.helper
= this._createHelper(event
);
1726 //Cache the helper size
1727 this._cacheHelperProportions();
1730 * - Position generation -
1731 * This block generates everything position related - it's the core of draggables.
1734 //Cache the margins of the original element
1735 this._cacheMargins();
1737 //Get the next scrolling parent
1738 this.scrollParent
= this.helper
.scrollParent();
1740 //The element's absolute position on the page minus margins
1741 this.offset
= this.currentItem
.offset();
1743 top
: this.offset
.top
- this.margins
.top
,
1744 left
: this.offset
.left
- this.margins
.left
1747 $.extend(this.offset
, {
1748 click
: { //Where the click happened, relative to the element
1749 left
: event
.pageX
- this.offset
.left
,
1750 top
: event
.pageY
- this.offset
.top
1752 parent
: this._getParentOffset(),
1753 relative
: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1756 // Only after we got the offset, we can change the helper's position to absolute
1757 // TODO: Still need to figure out a way to make relative sorting possible
1758 this.helper
.css("position", "absolute");
1759 this.cssPosition
= this.helper
.css("position");
1761 //Generate the original position
1762 this.originalPosition
= this._generatePosition(event
);
1763 this.originalPageX
= event
.pageX
;
1764 this.originalPageY
= event
.pageY
;
1766 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
1767 (o
.cursorAt
&& this._adjustOffsetFromHelper(o
.cursorAt
));
1769 //Cache the former DOM position
1770 this.domPosition
= { prev
: this.currentItem
.prev()[0], parent
: this.currentItem
.parent()[0] };
1772 //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
1773 if(this.helper
[0] != this.currentItem
[0]) {
1774 this.currentItem
.hide();
1777 //Create the placeholder
1778 this._createPlaceholder();
1780 //Set a containment if given in the options
1782 this._setContainment();
1784 if(o
.cursor
) { // cursor option
1785 if ($('body').css("cursor")) this._storedCursor
= $('body').css("cursor");
1786 $('body').css("cursor", o
.cursor
);
1789 if(o
.opacity
) { // opacity option
1790 if (this.helper
.css("opacity")) this._storedOpacity
= this.helper
.css("opacity");
1791 this.helper
.css("opacity", o
.opacity
);
1794 if(o
.zIndex
) { // zIndex option
1795 if (this.helper
.css("zIndex")) this._storedZIndex
= this.helper
.css("zIndex");
1796 this.helper
.css("zIndex", o
.zIndex
);
1800 if(this.scrollParent
[0] != document
&& this.scrollParent
[0].tagName
!= 'HTML')
1801 this.overflowOffset
= this.scrollParent
.offset();
1804 this._trigger("start", event
, this._uiHash());
1806 //Recache the helper size
1807 if(!this._preserveHelperProportions
)
1808 this._cacheHelperProportions();
1811 //Post 'activate' events to possible containers
1813 for (var i
= this.containers
.length
- 1; i
>= 0; i
--) { this.containers
[i
]._trigger("activate", event
, self
._uiHash(this)); }
1816 //Prepare possible droppables
1818 $.ui
.ddmanager
.current
= this;
1820 if ($.ui
.ddmanager
&& !o
.dropBehaviour
)
1821 $.ui
.ddmanager
.prepareOffsets(this, event
);
1823 this.dragging
= true;
1825 this.helper
.addClass("ui-sortable-helper");
1826 this._mouseDrag(event
); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1831 _mouseDrag: function(event
) {
1833 //Compute the helpers position
1834 this.position
= this._generatePosition(event
);
1835 this.positionAbs
= this._convertPositionTo("absolute");
1837 if (!this.lastPositionAbs
) {
1838 this.lastPositionAbs
= this.positionAbs
;
1842 if(this.options
.scroll
) {
1843 var o
= this.options
, scrolled
= false;
1844 if(this.scrollParent
[0] != document
&& this.scrollParent
[0].tagName
!= 'HTML') {
1846 if((this.overflowOffset
.top
+ this.scrollParent
[0].offsetHeight
) - event
.pageY
< o
.scrollSensitivity
)
1847 this.scrollParent
[0].scrollTop
= scrolled
= this.scrollParent
[0].scrollTop
+ o
.scrollSpeed
;
1848 else if(event
.pageY
- this.overflowOffset
.top
< o
.scrollSensitivity
)
1849 this.scrollParent
[0].scrollTop
= scrolled
= this.scrollParent
[0].scrollTop
- o
.scrollSpeed
;
1851 if((this.overflowOffset
.left
+ this.scrollParent
[0].offsetWidth
) - event
.pageX
< o
.scrollSensitivity
)
1852 this.scrollParent
[0].scrollLeft
= scrolled
= this.scrollParent
[0].scrollLeft
+ o
.scrollSpeed
;
1853 else if(event
.pageX
- this.overflowOffset
.left
< o
.scrollSensitivity
)
1854 this.scrollParent
[0].scrollLeft
= scrolled
= this.scrollParent
[0].scrollLeft
- o
.scrollSpeed
;
1858 if(event
.pageY
- $(document
).scrollTop() < o
.scrollSensitivity
)
1859 scrolled
= $(document
).scrollTop($(document
).scrollTop() - o
.scrollSpeed
);
1860 else if($(window
).height() - (event
.pageY
- $(document
).scrollTop()) < o
.scrollSensitivity
)
1861 scrolled
= $(document
).scrollTop($(document
).scrollTop() + o
.scrollSpeed
);
1863 if(event
.pageX
- $(document
).scrollLeft() < o
.scrollSensitivity
)
1864 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() - o
.scrollSpeed
);
1865 else if($(window
).width() - (event
.pageX
- $(document
).scrollLeft()) < o
.scrollSensitivity
)
1866 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() + o
.scrollSpeed
);
1870 if(scrolled
!== false && $.ui
.ddmanager
&& !o
.dropBehaviour
)
1871 $.ui
.ddmanager
.prepareOffsets(this, event
);
1874 //Regenerate the absolute position used for position checks
1875 this.positionAbs
= this._convertPositionTo("absolute");
1877 //Set the helper position
1878 if(!this.options
.axis
|| this.options
.axis
!= "y") this.helper
[0].style
.left
= this.position
.left
+'px';
1879 if(!this.options
.axis
|| this.options
.axis
!= "x") this.helper
[0].style
.top
= this.position
.top
+'px';
1882 for (var i
= this.items
.length
- 1; i
>= 0; i
--) {
1884 //Cache variables and intersection, continue if no intersection
1885 var item
= this.items
[i
], itemElement
= item
.item
[0], intersection
= this._intersectsWithPointer(item
);
1886 if (!intersection
) continue;
1888 if(itemElement
!= this.currentItem
[0] //cannot intersect with itself
1889 && this.placeholder
[intersection
== 1 ? "next" : "prev"]()[0] != itemElement
//no useless actions that have been done before
1890 && !$.ui
.contains(this.placeholder
[0], itemElement
) //no action if the item moved is the parent of the item checked
1891 && (this.options
.type
== 'semi-dynamic' ? !$.ui
.contains(this.element
[0], itemElement
) : true)
1892 //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
1895 this.direction
= intersection
== 1 ? "down" : "up";
1897 if (this.options
.tolerance
== "pointer" || this._intersectsWithSides(item
)) {
1898 this._rearrange(event
, item
);
1903 this._trigger("change", event
, this._uiHash());
1908 //Post events to containers
1909 this._contactContainers(event
);
1911 //Interconnect with droppables
1912 if($.ui
.ddmanager
) $.ui
.ddmanager
.drag(this, event
);
1915 this._trigger('sort', event
, this._uiHash());
1917 this.lastPositionAbs
= this.positionAbs
;
1922 _mouseStop: function(event
, noPropagation
) {
1926 //If we are using droppables, inform the manager about the drop
1927 if ($.ui
.ddmanager
&& !this.options
.dropBehaviour
)
1928 $.ui
.ddmanager
.drop(this, event
);
1930 if(this.options
.revert
) {
1932 var cur
= self
.placeholder
.offset();
1934 self
.reverting
= true;
1936 $(this.helper
).animate({
1937 left
: cur
.left
- this.offset
.parent
.left
- self
.margins
.left
+ (this.offsetParent
[0] == document
.body
? 0 : this.offsetParent
[0].scrollLeft
),
1938 top
: cur
.top
- this.offset
.parent
.top
- self
.margins
.top
+ (this.offsetParent
[0] == document
.body
? 0 : this.offsetParent
[0].scrollTop
)
1939 }, parseInt(this.options
.revert
, 10) || 500, function() {
1943 this._clear(event
, noPropagation
);
1950 cancel: function() {
1956 this._mouseUp({ target
: null });
1958 if(this.options
.helper
== "original")
1959 this.currentItem
.css(this._storedCSS
).removeClass("ui-sortable-helper");
1961 this.currentItem
.show();
1963 //Post deactivating events to containers
1964 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
1965 this.containers
[i
]._trigger("deactivate", null, self
._uiHash(this));
1966 if(this.containers
[i
].containerCache
.over
) {
1967 this.containers
[i
]._trigger("out", null, self
._uiHash(this));
1968 this.containers
[i
].containerCache
.over
= 0;
1974 if (this.placeholder
) {
1975 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
1976 if(this.placeholder
[0].parentNode
) this.placeholder
[0].parentNode
.removeChild(this.placeholder
[0]);
1977 if(this.options
.helper
!= "original" && this.helper
&& this.helper
[0].parentNode
) this.helper
.remove();
1986 if(this.domPosition
.prev
) {
1987 $(this.domPosition
.prev
).after(this.currentItem
);
1989 $(this.domPosition
.parent
).prepend(this.currentItem
);
1997 serialize: function(o
) {
1999 var items
= this._getItemsAsjQuery(o
&& o
.connected
);
2000 var str
= []; o
= o
|| {};
2002 $(items
).each(function() {
2003 var res
= ($(o
.item
|| this).attr(o
.attribute
|| 'id') || '').match(o
.expression
|| (/(.+)[-=_](.+)/));
2004 if(res
) str
.push((o
.key
|| res
[1]+'[]')+'='+(o
.key
&& o
.expression
? res
[1] : res
[2]));
2007 if(!str
.length
&& o
.key
) {
2008 str
.push(o
.key
+ '=');
2011 return str
.join('&');
2015 toArray: function(o
) {
2017 var items
= this._getItemsAsjQuery(o
&& o
.connected
);
2018 var ret
= []; o
= o
|| {};
2020 items
.each(function() { ret
.push($(o
.item
|| this).attr(o
.attribute
|| 'id') || ''); });
2025 /* Be careful with the following core functions */
2026 _intersectsWith: function(item
) {
2028 var x1
= this.positionAbs
.left
,
2029 x2
= x1
+ this.helperProportions
.width
,
2030 y1
= this.positionAbs
.top
,
2031 y2
= y1
+ this.helperProportions
.height
;
2036 b
= t
+ item
.height
;
2038 var dyClick
= this.offset
.click
.top
,
2039 dxClick
= this.offset
.click
.left
;
2041 var isOverElement
= (y1
+ dyClick
) > t
&& (y1
+ dyClick
) < b
&& (x1
+ dxClick
) > l
&& (x1
+ dxClick
) < r
;
2043 if( this.options
.tolerance
== "pointer"
2044 || this.options
.forcePointerForContainers
2045 || (this.options
.tolerance
!= "pointer" && this.helperProportions
[this.floating
? 'width' : 'height'] > item
[this.floating
? 'width' : 'height'])
2047 return isOverElement
;
2050 return (l
< x1
+ (this.helperProportions
.width
/ 2) // Right Half
2051 && x2
- (this.helperProportions
.width
/ 2) < r
// Left Half
2052 && t
< y1
+ (this.helperProportions
.height
/ 2) // Bottom Half
2053 && y2
- (this.helperProportions
.height
/ 2) < b
); // Top Half
2058 _intersectsWithPointer: function(item
) {
2060 var isOverElementHeight
= (this.options
.axis
=== 'x') || $.ui
.isOverAxis(this.positionAbs
.top
+ this.offset
.click
.top
, item
.top
, item
.height
),
2061 isOverElementWidth
= (this.options
.axis
=== 'y') || $.ui
.isOverAxis(this.positionAbs
.left
+ this.offset
.click
.left
, item
.left
, item
.width
),
2062 isOverElement
= isOverElementHeight
&& isOverElementWidth
,
2063 verticalDirection
= this._getDragVerticalDirection(),
2064 horizontalDirection
= this._getDragHorizontalDirection();
2069 return this.floating
?
2070 ( ((horizontalDirection
&& horizontalDirection
== "right") || verticalDirection
== "down") ? 2 : 1 )
2071 : ( verticalDirection
&& (verticalDirection
== "down" ? 2 : 1) );
2075 _intersectsWithSides: function(item
) {
2077 var isOverBottomHalf
= $.ui
.isOverAxis(this.positionAbs
.top
+ this.offset
.click
.top
, item
.top
+ (item
.height
/2), item
.height
),
2078 isOverRightHalf
= $.ui
.isOverAxis(this.positionAbs
.left
+ this.offset
.click
.left
, item
.left
+ (item
.width
/2), item
.width
),
2079 verticalDirection
= this._getDragVerticalDirection(),
2080 horizontalDirection
= this._getDragHorizontalDirection();
2082 if (this.floating
&& horizontalDirection
) {
2083 return ((horizontalDirection
== "right" && isOverRightHalf
) || (horizontalDirection
== "left" && !isOverRightHalf
));
2085 return verticalDirection
&& ((verticalDirection
== "down" && isOverBottomHalf
) || (verticalDirection
== "up" && !isOverBottomHalf
));
2090 _getDragVerticalDirection: function() {
2091 var delta
= this.positionAbs
.top
- this.lastPositionAbs
.top
;
2092 return delta
!= 0 && (delta
> 0 ? "down" : "up");
2095 _getDragHorizontalDirection: function() {
2096 var delta
= this.positionAbs
.left
- this.lastPositionAbs
.left
;
2097 return delta
!= 0 && (delta
> 0 ? "right" : "left");
2100 refresh: function(event
) {
2101 this._refreshItems(event
);
2102 this.refreshPositions();
2106 _connectWith: function() {
2107 var options
= this.options
;
2108 return options
.connectWith
.constructor == String
2109 ? [options
.connectWith
]
2110 : options
.connectWith
;
2113 _getItemsAsjQuery: function(connected
) {
2118 var connectWith
= this._connectWith();
2120 if(connectWith
&& connected
) {
2121 for (var i
= connectWith
.length
- 1; i
>= 0; i
--){
2122 var cur
= $(connectWith
[i
]);
2123 for (var j
= cur
.length
- 1; j
>= 0; j
--){
2124 var inst
= $.data(cur
[j
], this.widgetName
);
2125 if(inst
&& inst
!= this && !inst
.options
.disabled
) {
2126 queries
.push([$.isFunction(inst
.options
.items
) ? inst
.options
.items
.call(inst
.element
) : $(inst
.options
.items
, inst
.element
).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst
]);
2132 queries
.push([$.isFunction(this.options
.items
) ? this.options
.items
.call(this.element
, null, { options
: this.options
, item
: this.currentItem
}) : $(this.options
.items
, this.element
).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), this]);
2134 for (var i
= queries
.length
- 1; i
>= 0; i
--){
2135 queries
[i
][0].each(function() {
2144 _removeCurrentsFromItems: function() {
2146 var list
= this.currentItem
.find(":data(" + this.widgetName
+ "-item)");
2148 for (var i
=0; i
< this.items
.length
; i
++) {
2150 for (var j
=0; j
< list
.length
; j
++) {
2151 if(list
[j
] == this.items
[i
].item
[0])
2152 this.items
.splice(i
,1);
2159 _refreshItems: function(event
) {
2162 this.containers
= [this];
2163 var items
= this.items
;
2165 var queries
= [[$.isFunction(this.options
.items
) ? this.options
.items
.call(this.element
[0], event
, { item
: this.currentItem
}) : $(this.options
.items
, this.element
), this]];
2166 var connectWith
= this._connectWith();
2168 if(connectWith
&& this.ready
) { //Shouldn't be run the first time through due to massive slow-down
2169 for (var i
= connectWith
.length
- 1; i
>= 0; i
--){
2170 var cur
= $(connectWith
[i
]);
2171 for (var j
= cur
.length
- 1; j
>= 0; j
--){
2172 var inst
= $.data(cur
[j
], this.widgetName
);
2173 if(inst
&& inst
!= this && !inst
.options
.disabled
) {
2174 queries
.push([$.isFunction(inst
.options
.items
) ? inst
.options
.items
.call(inst
.element
[0], event
, { item
: this.currentItem
}) : $(inst
.options
.items
, inst
.element
), inst
]);
2175 this.containers
.push(inst
);
2181 for (var i
= queries
.length
- 1; i
>= 0; i
--) {
2182 var targetData
= queries
[i
][1];
2183 var _queries
= queries
[i
][0];
2185 for (var j
=0, queriesLength
= _queries
.length
; j
< queriesLength
; j
++) {
2186 var item
= $(_queries
[j
]);
2188 item
.data(this.widgetName
+ '-item', targetData
); // Data for target checking (mouse manager)
2192 instance
: targetData
,
2193 width
: 0, height
: 0,
2201 refreshPositions: function(fast
) {
2203 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
2204 if(this.offsetParent
&& this.helper
) {
2205 this.offset
.parent
= this._getParentOffset();
2208 for (var i
= this.items
.length
- 1; i
>= 0; i
--){
2209 var item
= this.items
[i
];
2211 //We ignore calculating positions of all connected containers when we're not over them
2212 if(item
.instance
!= this.currentContainer
&& this.currentContainer
&& item
.item
[0] != this.currentItem
[0])
2215 var t
= this.options
.toleranceElement
? $(this.options
.toleranceElement
, item
.item
) : item
.item
;
2218 item
.width
= t
.outerWidth();
2219 item
.height
= t
.outerHeight();
2227 if(this.options
.custom
&& this.options
.custom
.refreshContainers
) {
2228 this.options
.custom
.refreshContainers
.call(this);
2230 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
2231 var p
= this.containers
[i
].element
.offset();
2232 this.containers
[i
].containerCache
.left
= p
.left
;
2233 this.containers
[i
].containerCache
.top
= p
.top
;
2234 this.containers
[i
].containerCache
.width
= this.containers
[i
].element
.outerWidth();
2235 this.containers
[i
].containerCache
.height
= this.containers
[i
].element
.outerHeight();
2242 _createPlaceholder: function(that
) {
2244 var self
= that
|| this, o
= self
.options
;
2246 if(!o
.placeholder
|| o
.placeholder
.constructor == String
) {
2247 var className
= o
.placeholder
;
2249 element: function() {
2251 var el
= $(document
.createElement(self
.currentItem
[0].nodeName
))
2252 .addClass(className
|| self
.currentItem
[0].className
+" ui-sortable-placeholder")
2253 .removeClass("ui-sortable-helper")[0];
2256 el
.style
.visibility
= "hidden";
2260 update: function(container
, p
) {
2262 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
2263 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
2264 if(className
&& !o
.forcePlaceholderSize
) return;
2266 //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
2267 if(!p
.height()) { p
.height(self
.currentItem
.innerHeight() - parseInt(self
.currentItem
.css('paddingTop')||0, 10) - parseInt(self
.currentItem
.css('paddingBottom')||0, 10)); };
2268 if(!p
.width()) { p
.width(self
.currentItem
.innerWidth() - parseInt(self
.currentItem
.css('paddingLeft')||0, 10) - parseInt(self
.currentItem
.css('paddingRight')||0, 10)); };
2273 //Create the placeholder
2274 self
.placeholder
= $(o
.placeholder
.element
.call(self
.element
, self
.currentItem
));
2276 //Append it after the actual current item
2277 self
.currentItem
.after(self
.placeholder
);
2279 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
2280 o
.placeholder
.update(self
, self
.placeholder
);
2284 _contactContainers: function(event
) {
2286 // get innermost container that intersects with item
2287 var innermostContainer
= null, innermostIndex
= null;
2290 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
2292 // never consider a container that's located within the item itself
2293 if($.ui
.contains(this.currentItem
[0], this.containers
[i
].element
[0]))
2296 if(this._intersectsWith(this.containers
[i
].containerCache
)) {
2298 // if we've already found a container and it's more "inner" than this, then continue
2299 if(innermostContainer
&& $.ui
.contains(this.containers
[i
].element
[0], innermostContainer
.element
[0]))
2302 innermostContainer
= this.containers
[i
];
2306 // container doesn't intersect. trigger "out" event if necessary
2307 if(this.containers
[i
].containerCache
.over
) {
2308 this.containers
[i
]._trigger("out", event
, this._uiHash(this));
2309 this.containers
[i
].containerCache
.over
= 0;
2315 // if no intersecting containers found, return
2316 if(!innermostContainer
) return;
2318 // move the item into the container if it's not there already
2319 if(this.containers
.length
=== 1) {
2320 this.containers
[innermostIndex
]._trigger("over", event
, this._uiHash(this));
2321 this.containers
[innermostIndex
].containerCache
.over
= 1;
2322 } else if(this.currentContainer
!= this.containers
[innermostIndex
]) {
2324 //When entering a new container, we will find the item with the least distance and append our item near it
2325 var dist
= 10000; var itemWithLeastDistance
= null; var base
= this.positionAbs
[this.containers
[innermostIndex
].floating
? 'left' : 'top'];
2326 for (var j
= this.items
.length
- 1; j
>= 0; j
--) {
2327 if(!$.ui
.contains(this.containers
[innermostIndex
].element
[0], this.items
[j
].item
[0])) continue;
2328 var cur
= this.containers
[innermostIndex
].floating
? this.items
[j
].item
.offset().left
: this.items
[j
].item
.offset().top
;
2329 if(Math
.abs(cur
- base
) < dist
) {
2330 dist
= Math
.abs(cur
- base
); itemWithLeastDistance
= this.items
[j
];
2331 this.direction
= (cur
- base
> 0) ? 'down' : 'up';
2335 if(!itemWithLeastDistance
&& !this.options
.dropOnEmpty
) //Check if dropOnEmpty is enabled
2338 this.currentContainer
= this.containers
[innermostIndex
];
2339 itemWithLeastDistance
? this._rearrange(event
, itemWithLeastDistance
, null, true) : this._rearrange(event
, null, this.containers
[innermostIndex
].element
, true);
2340 this._trigger("change", event
, this._uiHash());
2341 this.containers
[innermostIndex
]._trigger("change", event
, this._uiHash(this));
2343 //Update the placeholder
2344 this.options
.placeholder
.update(this.currentContainer
, this.placeholder
);
2346 this.containers
[innermostIndex
]._trigger("over", event
, this._uiHash(this));
2347 this.containers
[innermostIndex
].containerCache
.over
= 1;
2353 _createHelper: function(event
) {
2355 var o
= this.options
;
2356 var helper
= $.isFunction(o
.helper
) ? $(o
.helper
.apply(this.element
[0], [event
, this.currentItem
])) : (o
.helper
== 'clone' ? this.currentItem
.clone() : this.currentItem
);
2358 if(!helper
.parents('body').length
) //Add the helper to the DOM if that didn't happen already
2359 $(o
.appendTo
!= 'parent' ? o
.appendTo
: this.currentItem
[0].parentNode
)[0].appendChild(helper
[0]);
2361 if(helper
[0] == this.currentItem
[0])
2362 this._storedCSS
= { width
: this.currentItem
[0].style
.width
, height
: this.currentItem
[0].style
.height
, position
: this.currentItem
.css("position"), top
: this.currentItem
.css("top"), left
: this.currentItem
.css("left") };
2364 if(helper
[0].style
.width
== '' || o
.forceHelperSize
) helper
.width(this.currentItem
.width());
2365 if(helper
[0].style
.height
== '' || o
.forceHelperSize
) helper
.height(this.currentItem
.height());
2371 _adjustOffsetFromHelper: function(obj
) {
2372 if (typeof obj
== 'string') {
2373 obj
= obj
.split(' ');
2375 if ($.isArray(obj
)) {
2376 obj
= {left
: +obj
[0], top
: +obj
[1] || 0};
2378 if ('left' in obj
) {
2379 this.offset
.click
.left
= obj
.left
+ this.margins
.left
;
2381 if ('right' in obj
) {
2382 this.offset
.click
.left
= this.helperProportions
.width
- obj
.right
+ this.margins
.left
;
2385 this.offset
.click
.top
= obj
.top
+ this.margins
.top
;
2387 if ('bottom' in obj
) {
2388 this.offset
.click
.top
= this.helperProportions
.height
- obj
.bottom
+ this.margins
.top
;
2392 _getParentOffset: function() {
2395 //Get the offsetParent and cache its position
2396 this.offsetParent
= this.helper
.offsetParent();
2397 var po
= this.offsetParent
.offset();
2399 // This is a special case where we need to modify a offset calculated on start, since the following happened:
2400 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
2401 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
2402 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
2403 if(this.cssPosition
== 'absolute' && this.scrollParent
[0] != document
&& $.ui
.contains(this.scrollParent
[0], this.offsetParent
[0])) {
2404 po
.left
+= this.scrollParent
.scrollLeft();
2405 po
.top
+= this.scrollParent
.scrollTop();
2408 if((this.offsetParent
[0] == document
.body
) //This needs to be actually done for all browsers, since pageX/pageY includes this information
2409 || (this.offsetParent
[0].tagName
&& this.offsetParent
[0].tagName
.toLowerCase() == 'html' && $.browser
.msie
)) //Ugly IE fix
2410 po
= { top
: 0, left
: 0 };
2413 top
: po
.top
+ (parseInt(this.offsetParent
.css("borderTopWidth"),10) || 0),
2414 left
: po
.left
+ (parseInt(this.offsetParent
.css("borderLeftWidth"),10) || 0)
2419 _getRelativeOffset: function() {
2421 if(this.cssPosition
== "relative") {
2422 var p
= this.currentItem
.position();
2424 top
: p
.top
- (parseInt(this.helper
.css("top"),10) || 0) + this.scrollParent
.scrollTop(),
2425 left
: p
.left
- (parseInt(this.helper
.css("left"),10) || 0) + this.scrollParent
.scrollLeft()
2428 return { top
: 0, left
: 0 };
2433 _cacheMargins: function() {
2435 left
: (parseInt(this.currentItem
.css("marginLeft"),10) || 0),
2436 top
: (parseInt(this.currentItem
.css("marginTop"),10) || 0)
2440 _cacheHelperProportions: function() {
2441 this.helperProportions
= {
2442 width
: this.helper
.outerWidth(),
2443 height
: this.helper
.outerHeight()
2447 _setContainment: function() {
2449 var o
= this.options
;
2450 if(o
.containment
== 'parent') o
.containment
= this.helper
[0].parentNode
;
2451 if(o
.containment
== 'document' || o
.containment
== 'window') this.containment
= [
2452 0 - this.offset
.relative
.left
- this.offset
.parent
.left
,
2453 0 - this.offset
.relative
.top
- this.offset
.parent
.top
,
2454 $(o
.containment
== 'document' ? document
: window
).width() - this.helperProportions
.width
- this.margins
.left
,
2455 ($(o
.containment
== 'document' ? document
: window
).height() || document
.body
.parentNode
.scrollHeight
) - this.helperProportions
.height
- this.margins
.top
2458 if(!(/^(document|window|parent)$/).test(o
.containment
)) {
2459 var ce
= $(o
.containment
)[0];
2460 var co
= $(o
.containment
).offset();
2461 var over
= ($(ce
).css("overflow") != 'hidden');
2463 this.containment
= [
2464 co
.left
+ (parseInt($(ce
).css("borderLeftWidth"),10) || 0) + (parseInt($(ce
).css("paddingLeft"),10) || 0) - this.margins
.left
,
2465 co
.top
+ (parseInt($(ce
).css("borderTopWidth"),10) || 0) + (parseInt($(ce
).css("paddingTop"),10) || 0) - this.margins
.top
,
2466 co
.left
+(over
? Math
.max(ce
.scrollWidth
,ce
.offsetWidth
) : ce
.offsetWidth
) - (parseInt($(ce
).css("borderLeftWidth"),10) || 0) - (parseInt($(ce
).css("paddingRight"),10) || 0) - this.helperProportions
.width
- this.margins
.left
,
2467 co
.top
+(over
? Math
.max(ce
.scrollHeight
,ce
.offsetHeight
) : ce
.offsetHeight
) - (parseInt($(ce
).css("borderTopWidth"),10) || 0) - (parseInt($(ce
).css("paddingBottom"),10) || 0) - this.helperProportions
.height
- this.margins
.top
2473 _convertPositionTo: function(d
, pos
) {
2475 if(!pos
) pos
= this.position
;
2476 var mod
= d
== "absolute" ? 1 : -1;
2477 var o
= this.options
, scroll
= this.cssPosition
== 'absolute' && !(this.scrollParent
[0] != document
&& $.ui
.contains(this.scrollParent
[0], this.offsetParent
[0])) ? this.offsetParent
: this.scrollParent
, scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
2481 pos
.top
// The absolute mouse position
2482 + this.offset
.relative
.top
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
2483 + this.offset
.parent
.top
* mod
// The offsetParent's offset without borders (offset + border)
2484 - ($.browser
.safari
&& this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) ) * mod
)
2487 pos
.left
// The absolute mouse position
2488 + this.offset
.relative
.left
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
2489 + this.offset
.parent
.left
* mod
// The offsetParent's offset without borders (offset + border)
2490 - ($.browser
.safari
&& this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : scrollIsRootNode
? 0 : scroll
.scrollLeft() ) * mod
)
2496 _generatePosition: function(event
) {
2498 var o
= this.options
, scroll
= this.cssPosition
== 'absolute' && !(this.scrollParent
[0] != document
&& $.ui
.contains(this.scrollParent
[0], this.offsetParent
[0])) ? this.offsetParent
: this.scrollParent
, scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
2500 // This is another very weird special case that only happens for relative elements:
2501 // 1. If the css position is relative
2502 // 2. and the scroll parent is the document or similar to the offset parent
2503 // we have to refresh the relative offset during the scroll so there are no jumps
2504 if(this.cssPosition
== 'relative' && !(this.scrollParent
[0] != document
&& this.scrollParent
[0] != this.offsetParent
[0])) {
2505 this.offset
.relative
= this._getRelativeOffset();
2508 var pageX
= event
.pageX
;
2509 var pageY
= event
.pageY
;
2512 * - Position constraining -
2513 * Constrain the position to a mix of grid, containment.
2516 if(this.originalPosition
) { //If we are not dragging yet, we won't check for options
2518 if(this.containment
) {
2519 if(event
.pageX
- this.offset
.click
.left
< this.containment
[0]) pageX
= this.containment
[0] + this.offset
.click
.left
;
2520 if(event
.pageY
- this.offset
.click
.top
< this.containment
[1]) pageY
= this.containment
[1] + this.offset
.click
.top
;
2521 if(event
.pageX
- this.offset
.click
.left
> this.containment
[2]) pageX
= this.containment
[2] + this.offset
.click
.left
;
2522 if(event
.pageY
- this.offset
.click
.top
> this.containment
[3]) pageY
= this.containment
[3] + this.offset
.click
.top
;
2526 var top
= this.originalPageY
+ Math
.round((pageY
- this.originalPageY
) / o
.grid
[1]) * o
.grid
[1];
2527 pageY
= this.containment
? (!(top
- this.offset
.click
.top
< this.containment
[1] || top
- this.offset
.click
.top
> this.containment
[3]) ? top
: (!(top
- this.offset
.click
.top
< this.containment
[1]) ? top
- o
.grid
[1] : top
+ o
.grid
[1])) : top
;
2529 var left
= this.originalPageX
+ Math
.round((pageX
- this.originalPageX
) / o
.grid
[0]) * o
.grid
[0];
2530 pageX
= this.containment
? (!(left
- this.offset
.click
.left
< this.containment
[0] || left
- this.offset
.click
.left
> this.containment
[2]) ? left
: (!(left
- this.offset
.click
.left
< this.containment
[0]) ? left
- o
.grid
[0] : left
+ o
.grid
[0])) : left
;
2537 pageY
// The absolute mouse position
2538 - this.offset
.click
.top
// Click offset (relative to the element)
2539 - this.offset
.relative
.top
// Only for relative positioned nodes: Relative offset from element to offset parent
2540 - this.offset
.parent
.top
// The offsetParent's offset without borders (offset + border)
2541 + ($.browser
.safari
&& this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) ))
2544 pageX
// The absolute mouse position
2545 - this.offset
.click
.left
// Click offset (relative to the element)
2546 - this.offset
.relative
.left
// Only for relative positioned nodes: Relative offset from element to offset parent
2547 - this.offset
.parent
.left
// The offsetParent's offset without borders (offset + border)
2548 + ($.browser
.safari
&& this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : scrollIsRootNode
? 0 : scroll
.scrollLeft() ))
2554 _rearrange: function(event
, i
, a
, hardRefresh
) {
2556 a
? a
[0].appendChild(this.placeholder
[0]) : i
.item
[0].parentNode
.insertBefore(this.placeholder
[0], (this.direction
== 'down' ? i
.item
[0] : i
.item
[0].nextSibling
));
2558 //Various things done here to improve the performance:
2559 // 1. we create a setTimeout, that calls refreshPositions
2560 // 2. on the instance, we have a counter variable, that get's higher after every append
2561 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
2562 // 4. this lets only the last addition to the timeout stack through
2563 this.counter
= this.counter
? ++this.counter
: 1;
2564 var self
= this, counter
= this.counter
;
2566 window
.setTimeout(function() {
2567 if(counter
== self
.counter
) self
.refreshPositions(!hardRefresh
); //Precompute after each DOM insertion, NOT on mousemove
2572 _clear: function(event
, noPropagation
) {
2574 this.reverting
= false;
2575 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
2576 // everything else normalized again
2577 var delayedTriggers
= [], self
= this;
2579 // We first have to update the dom position of the actual currentItem
2580 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
2581 if(!this._noFinalSort
&& this.currentItem
.parent().length
) this.placeholder
.before(this.currentItem
);
2582 this._noFinalSort
= null;
2584 if(this.helper
[0] == this.currentItem
[0]) {
2585 for(var i
in this._storedCSS
) {
2586 if(this._storedCSS
[i
] == 'auto' || this._storedCSS
[i
] == 'static') this._storedCSS
[i
] = '';
2588 this.currentItem
.css(this._storedCSS
).removeClass("ui-sortable-helper");
2590 this.currentItem
.show();
2593 if(this.fromOutside
&& !noPropagation
) delayedTriggers
.push(function(event
) { this._trigger("receive", event
, this._uiHash(this.fromOutside
)); });
2594 if((this.fromOutside
|| this.domPosition
.prev
!= this.currentItem
.prev().not(".ui-sortable-helper")[0] || this.domPosition
.parent
!= this.currentItem
.parent()[0]) && !noPropagation
) delayedTriggers
.push(function(event
) { this._trigger("update", event
, this._uiHash()); }); //Trigger update callback if the DOM position has changed
2595 if(!$.ui
.contains(this.element
[0], this.currentItem
[0])) { //Node was moved out of the current element
2596 if(!noPropagation
) delayedTriggers
.push(function(event
) { this._trigger("remove", event
, this._uiHash()); });
2597 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
2598 if($.ui
.contains(this.containers
[i
].element
[0], this.currentItem
[0]) && !noPropagation
) {
2599 delayedTriggers
.push((function(c
) { return function(event
) { c
._trigger("receive", event
, this._uiHash(this)); }; }).call(this, this.containers
[i
]));
2600 delayedTriggers
.push((function(c
) { return function(event
) { c
._trigger("update", event
, this._uiHash(this)); }; }).call(this, this.containers
[i
]));
2605 //Post events to containers
2606 for (var i
= this.containers
.length
- 1; i
>= 0; i
--){
2607 if(!noPropagation
) delayedTriggers
.push((function(c
) { return function(event
) { c
._trigger("deactivate", event
, this._uiHash(this)); }; }).call(this, this.containers
[i
]));
2608 if(this.containers
[i
].containerCache
.over
) {
2609 delayedTriggers
.push((function(c
) { return function(event
) { c
._trigger("out", event
, this._uiHash(this)); }; }).call(this, this.containers
[i
]));
2610 this.containers
[i
].containerCache
.over
= 0;
2614 //Do what was originally in plugins
2615 if(this._storedCursor
) $('body').css("cursor", this._storedCursor
); //Reset cursor
2616 if(this._storedOpacity
) this.helper
.css("opacity", this._storedOpacity
); //Reset opacity
2617 if(this._storedZIndex
) this.helper
.css("zIndex", this._storedZIndex
== 'auto' ? '' : this._storedZIndex
); //Reset z-index
2619 this.dragging
= false;
2620 if(this.cancelHelperRemoval
) {
2621 if(!noPropagation
) {
2622 this._trigger("beforeStop", event
, this._uiHash());
2623 for (var i
=0; i
< delayedTriggers
.length
; i
++) { delayedTriggers
[i
].call(this, event
); }; //Trigger all delayed events
2624 this._trigger("stop", event
, this._uiHash());
2629 if(!noPropagation
) this._trigger("beforeStop", event
, this._uiHash());
2631 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
2632 this.placeholder
[0].parentNode
.removeChild(this.placeholder
[0]);
2634 if(this.helper
[0] != this.currentItem
[0]) this.helper
.remove(); this.helper
= null;
2636 if(!noPropagation
) {
2637 for (var i
=0; i
< delayedTriggers
.length
; i
++) { delayedTriggers
[i
].call(this, event
); }; //Trigger all delayed events
2638 this._trigger("stop", event
, this._uiHash());
2641 this.fromOutside
= false;
2646 _trigger: function() {
2647 if ($.Widget
.prototype._trigger
.apply(this, arguments
) === false) {
2652 _uiHash: function(inst
) {
2653 var self
= inst
|| this;
2655 helper
: self
.helper
,
2656 placeholder
: self
.placeholder
|| $([]),
2657 position
: self
.position
,
2658 originalPosition
: self
.originalPosition
,
2659 offset
: self
.positionAbs
,
2660 item
: self
.currentItem
,
2661 sender
: inst
? inst
.element
: null
2667 $.extend($.ui
.sortable
, {