1 /*! jQuery UI - v1.10.0 - 2013-01-24
3 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.effect.js
4 * Copyright (c) 2013 jQuery Foundation and other contributors Licensed MIT */
6 (function( $, undefined ) {
9 runiqueId = /^ui-id-\d+$/;
11 // prevent duplicate loading
12 // this is only a problem because we proxy existing functions
13 // and we don't want to double proxy them
51 focus: function( delay, fn ) {
52 return typeof delay === "number" ?
53 this.each(function() {
55 setTimeout(function() {
62 this._focus.apply( this, arguments );
65 scrollParent: function() {
67 if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
68 scrollParent = this.parents().filter(function() {
69 return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
72 scrollParent = this.parents().filter(function() {
73 return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
77 return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
80 zIndex: function( zIndex ) {
81 if ( zIndex !== undefined ) {
82 return this.css( "zIndex", zIndex );
86 var elem = $( this[ 0 ] ), position, value;
87 while ( elem.length && elem[ 0 ] !== document ) {
88 // Ignore z-index if position is set to a value where z-index is ignored by the browser
89 // This makes behavior of this function consistent across browsers
90 // WebKit always returns auto if the element is positioned
91 position = elem.css( "position" );
92 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
93 // IE returns 0 when zIndex is not specified
94 // other browsers return a string
95 // we ignore the case of nested elements with an explicit value of 0
96 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
97 value = parseInt( elem.css( "zIndex" ), 10 );
98 if ( !isNaN( value ) && value !== 0 ) {
102 elem = elem.parent();
109 uniqueId: function() {
110 return this.each(function() {
112 this.id = "ui-id-" + (++uuid);
117 removeUniqueId: function() {
118 return this.each(function() {
119 if ( runiqueId.test( this.id ) ) {
120 $( this ).removeAttr( "id" );
127 function focusable( element, isTabIndexNotNaN ) {
128 var map, mapName, img,
129 nodeName = element.nodeName.toLowerCase();
130 if ( "area" === nodeName ) {
131 map = element.parentNode;
133 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
136 img = $( "img[usemap=#" + mapName + "]" )[0];
137 return !!img && visible( img );
139 return ( /input|select|textarea|button|object/.test( nodeName ) ?
142 element.href || isTabIndexNotNaN :
144 // the element and all of its ancestors must be visible
148 function visible( element ) {
149 return $.expr.filters.visible( element ) &&
150 !$( element ).parents().addBack().filter(function() {
151 return $.css( this, "visibility" ) === "hidden";
155 $.extend( $.expr[ ":" ], {
156 data: $.expr.createPseudo ?
157 $.expr.createPseudo(function( dataName ) {
158 return function( elem ) {
159 return !!$.data( elem, dataName );
162 // support: jQuery <1.8
163 function( elem, i, match ) {
164 return !!$.data( elem, match[ 3 ] );
167 focusable: function( element ) {
168 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
171 tabbable: function( element ) {
172 var tabIndex = $.attr( element, "tabindex" ),
173 isTabIndexNaN = isNaN( tabIndex );
174 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
178 // support: jQuery <1.8
179 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
180 $.each( [ "Width", "Height" ], function( i, name ) {
181 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
182 type = name.toLowerCase(),
184 innerWidth: $.fn.innerWidth,
185 innerHeight: $.fn.innerHeight,
186 outerWidth: $.fn.outerWidth,
187 outerHeight: $.fn.outerHeight
190 function reduce( elem, size, border, margin ) {
191 $.each( side, function() {
192 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
194 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
197 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
203 $.fn[ "inner" + name ] = function( size ) {
204 if ( size === undefined ) {
205 return orig[ "inner" + name ].call( this );
208 return this.each(function() {
209 $( this ).css( type, reduce( this, size ) + "px" );
213 $.fn[ "outer" + name] = function( size, margin ) {
214 if ( typeof size !== "number" ) {
215 return orig[ "outer" + name ].call( this, size );
218 return this.each(function() {
219 $( this).css( type, reduce( this, size, true, margin ) + "px" );
225 // support: jQuery <1.8
226 if ( !$.fn.addBack ) {
227 $.fn.addBack = function( selector ) {
228 return this.add( selector == null ?
229 this.prevObject : this.prevObject.filter( selector )
234 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
235 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
236 $.fn.removeData = (function( removeData ) {
237 return function( key ) {
238 if ( arguments.length ) {
239 return removeData.call( this, $.camelCase( key ) );
241 return removeData.call( this );
244 })( $.fn.removeData );
252 $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
254 $.support.selectstart = "onselectstart" in document.createElement( "div" );
256 disableSelection: function() {
257 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
258 ".ui-disableSelection", function( event ) {
259 event.preventDefault();
263 enableSelection: function() {
264 return this.unbind( ".ui-disableSelection" );
269 // $.ui.plugin is deprecated. Use the proxy pattern instead.
271 add: function( module, option, set ) {
273 proto = $.ui[ module ].prototype;
275 proto.plugins[ i ] = proto.plugins[ i ] || [];
276 proto.plugins[ i ].push( [ option, set[ i ] ] );
279 call: function( instance, name, args ) {
281 set = instance.plugins[ name ];
282 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
286 for ( i = 0; i < set.length; i++ ) {
287 if ( instance.options[ set[ i ][ 0 ] ] ) {
288 set[ i ][ 1 ].apply( instance.element, args );
294 // only used by resizable
295 hasScroll: function( el, a ) {
297 //If overflow is hidden, the element might have extra content, but the user wants to hide it
298 if ( $( el ).css( "overflow" ) === "hidden") {
302 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
305 if ( el[ scroll ] > 0 ) {
309 // TODO: determine which cases actually cause this to happen
310 // if the element doesn't have the scroll set, see if it's possible to
313 has = ( el[ scroll ] > 0 );
320 (function( $, undefined ) {
323 slice = Array.prototype.slice,
324 _cleanData = $.cleanData;
325 $.cleanData = function( elems ) {
326 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
328 $( elem ).triggerHandler( "remove" );
329 // http://bugs.jquery.com/ticket/8235
335 $.widget = function( name, base, prototype ) {
336 var fullName, existingConstructor, constructor, basePrototype,
337 // proxiedPrototype allows the provided prototype to remain unmodified
338 // so that it can be used as a mixin for multiple widgets (#8876)
339 proxiedPrototype = {},
340 namespace = name.split( "." )[ 0 ];
342 name = name.split( "." )[ 1 ];
343 fullName = namespace + "-" + name;
350 // create selector for plugin
351 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
352 return !!$.data( elem, fullName );
355 $[ namespace ] = $[ namespace ] || {};
356 existingConstructor = $[ namespace ][ name ];
357 constructor = $[ namespace ][ name ] = function( options, element ) {
358 // allow instantiation without "new" keyword
359 if ( !this._createWidget ) {
360 return new constructor( options, element );
363 // allow instantiation without initializing for simple inheritance
364 // must use "new" keyword (the code above always passes args)
365 if ( arguments.length ) {
366 this._createWidget( options, element );
369 // extend with the existing constructor to carry over any static properties
370 $.extend( constructor, existingConstructor, {
371 version: prototype.version,
372 // copy the object used to create the prototype in case we need to
373 // redefine the widget later
374 _proto: $.extend( {}, prototype ),
375 // track widgets that inherit from this widget in case this widget is
376 // redefined after a widget inherits from it
377 _childConstructors: []
380 basePrototype = new base();
381 // we need to make the options hash a property directly on the new instance
382 // otherwise we'll modify the options hash on the prototype that we're
384 basePrototype.options = $.widget.extend( {}, basePrototype.options );
385 $.each( prototype, function( prop, value ) {
386 if ( !$.isFunction( value ) ) {
387 proxiedPrototype[ prop ] = value;
390 proxiedPrototype[ prop ] = (function() {
391 var _super = function() {
392 return base.prototype[ prop ].apply( this, arguments );
394 _superApply = function( args ) {
395 return base.prototype[ prop ].apply( this, args );
398 var __super = this._super,
399 __superApply = this._superApply,
402 this._super = _super;
403 this._superApply = _superApply;
405 returnValue = value.apply( this, arguments );
407 this._super = __super;
408 this._superApply = __superApply;
414 constructor.prototype = $.widget.extend( basePrototype, {
415 // TODO: remove support for widgetEventPrefix
416 // always use the name + a colon as the prefix, e.g., draggable:start
417 // don't prefix for widgets that aren't DOM-based
418 widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
419 }, proxiedPrototype, {
420 constructor: constructor,
421 namespace: namespace,
423 widgetFullName: fullName
426 // If this widget is being redefined then we need to find all widgets that
427 // are inheriting from it and redefine all of them so that they inherit from
428 // the new version of this widget. We're essentially trying to replace one
429 // level in the prototype chain.
430 if ( existingConstructor ) {
431 $.each( existingConstructor._childConstructors, function( i, child ) {
432 var childPrototype = child.prototype;
434 // redefine the child widget using the same prototype that was
435 // originally used, but inherit from the new version of the base
436 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
438 // remove the list of existing child constructors from the old constructor
439 // so the old child constructors can be garbage collected
440 delete existingConstructor._childConstructors;
442 base._childConstructors.push( constructor );
445 $.widget.bridge( name, constructor );
448 $.widget.extend = function( target ) {
449 var input = slice.call( arguments, 1 ),
451 inputLength = input.length,
454 for ( ; inputIndex < inputLength; inputIndex++ ) {
455 for ( key in input[ inputIndex ] ) {
456 value = input[ inputIndex ][ key ];
457 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
459 if ( $.isPlainObject( value ) ) {
460 target[ key ] = $.isPlainObject( target[ key ] ) ?
461 $.widget.extend( {}, target[ key ], value ) :
462 // Don't extend strings, arrays, etc. with objects
463 $.widget.extend( {}, value );
464 // Copy everything else by reference
466 target[ key ] = value;
474 $.widget.bridge = function( name, object ) {
475 var fullName = object.prototype.widgetFullName || name;
476 $.fn[ name ] = function( options ) {
477 var isMethodCall = typeof options === "string",
478 args = slice.call( arguments, 1 ),
481 // allow multiple hashes to be passed on init
482 options = !isMethodCall && args.length ?
483 $.widget.extend.apply( null, [ options ].concat(args) ) :
486 if ( isMethodCall ) {
487 this.each(function() {
489 instance = $.data( this, fullName );
491 return $.error( "cannot call methods on " + name + " prior to initialization; " +
492 "attempted to call method '" + options + "'" );
494 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
495 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
497 methodValue = instance[ options ].apply( instance, args );
498 if ( methodValue !== instance && methodValue !== undefined ) {
499 returnValue = methodValue && methodValue.jquery ?
500 returnValue.pushStack( methodValue.get() ) :
506 this.each(function() {
507 var instance = $.data( this, fullName );
509 instance.option( options || {} )._init();
511 $.data( this, fullName, new object( options, this ) );
520 $.Widget = function( /* options, element */ ) {};
521 $.Widget._childConstructors = [];
523 $.Widget.prototype = {
524 widgetName: "widget",
525 widgetEventPrefix: "",
526 defaultElement: "<div>",
533 _createWidget: function( options, element ) {
534 element = $( element || this.defaultElement || this )[ 0 ];
535 this.element = $( element );
537 this.eventNamespace = "." + this.widgetName + this.uuid;
538 this.options = $.widget.extend( {},
540 this._getCreateOptions(),
544 this.hoverable = $();
545 this.focusable = $();
547 if ( element !== this ) {
548 $.data( element, this.widgetFullName, this );
549 this._on( true, this.element, {
550 remove: function( event ) {
551 if ( event.target === element ) {
556 this.document = $( element.style ?
557 // element within the document
558 element.ownerDocument :
559 // element is window or document
560 element.document || element );
561 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
565 this._trigger( "create", null, this._getCreateEventData() );
568 _getCreateOptions: $.noop,
569 _getCreateEventData: $.noop,
573 destroy: function() {
575 // we can probably remove the unbind calls in 2.0
576 // all event bindings should go through this._on()
578 .unbind( this.eventNamespace )
580 // TODO remove dual storage
581 .removeData( this.widgetName )
582 .removeData( this.widgetFullName )
583 // support: jquery <1.6.3
584 // http://bugs.jquery.com/ticket/9413
585 .removeData( $.camelCase( this.widgetFullName ) );
587 .unbind( this.eventNamespace )
588 .removeAttr( "aria-disabled" )
590 this.widgetFullName + "-disabled " +
591 "ui-state-disabled" );
593 // clean up events and states
594 this.bindings.unbind( this.eventNamespace );
595 this.hoverable.removeClass( "ui-state-hover" );
596 this.focusable.removeClass( "ui-state-focus" );
604 option: function( key, value ) {
610 if ( arguments.length === 0 ) {
611 // don't return a reference to the internal hash
612 return $.widget.extend( {}, this.options );
615 if ( typeof key === "string" ) {
616 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
618 parts = key.split( "." );
620 if ( parts.length ) {
621 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
622 for ( i = 0; i < parts.length - 1; i++ ) {
623 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
624 curOption = curOption[ parts[ i ] ];
627 if ( value === undefined ) {
628 return curOption[ key ] === undefined ? null : curOption[ key ];
630 curOption[ key ] = value;
632 if ( value === undefined ) {
633 return this.options[ key ] === undefined ? null : this.options[ key ];
635 options[ key ] = value;
639 this._setOptions( options );
643 _setOptions: function( options ) {
646 for ( key in options ) {
647 this._setOption( key, options[ key ] );
652 _setOption: function( key, value ) {
653 this.options[ key ] = value;
655 if ( key === "disabled" ) {
657 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
658 .attr( "aria-disabled", value );
659 this.hoverable.removeClass( "ui-state-hover" );
660 this.focusable.removeClass( "ui-state-focus" );
667 return this._setOption( "disabled", false );
669 disable: function() {
670 return this._setOption( "disabled", true );
673 _on: function( suppressDisabledCheck, element, handlers ) {
677 // no suppressDisabledCheck flag, shuffle arguments
678 if ( typeof suppressDisabledCheck !== "boolean" ) {
680 element = suppressDisabledCheck;
681 suppressDisabledCheck = false;
684 // no element argument, shuffle and use this.element
687 element = this.element;
688 delegateElement = this.widget();
690 // accept selectors, DOM elements
691 element = delegateElement = $( element );
692 this.bindings = this.bindings.add( element );
695 $.each( handlers, function( event, handler ) {
696 function handlerProxy() {
697 // allow widgets to customize the disabled handling
698 // - disabled as an array instead of boolean
699 // - disabled class as method for disabling individual parts
700 if ( !suppressDisabledCheck &&
701 ( instance.options.disabled === true ||
702 $( this ).hasClass( "ui-state-disabled" ) ) ) {
705 return ( typeof handler === "string" ? instance[ handler ] : handler )
706 .apply( instance, arguments );
709 // copy the guid so direct unbinding works
710 if ( typeof handler !== "string" ) {
711 handlerProxy.guid = handler.guid =
712 handler.guid || handlerProxy.guid || $.guid++;
715 var match = event.match( /^(\w+)\s*(.*)$/ ),
716 eventName = match[1] + instance.eventNamespace,
719 delegateElement.delegate( selector, eventName, handlerProxy );
721 element.bind( eventName, handlerProxy );
726 _off: function( element, eventName ) {
727 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
728 element.unbind( eventName ).undelegate( eventName );
731 _delay: function( handler, delay ) {
732 function handlerProxy() {
733 return ( typeof handler === "string" ? instance[ handler ] : handler )
734 .apply( instance, arguments );
737 return setTimeout( handlerProxy, delay || 0 );
740 _hoverable: function( element ) {
741 this.hoverable = this.hoverable.add( element );
743 mouseenter: function( event ) {
744 $( event.currentTarget ).addClass( "ui-state-hover" );
746 mouseleave: function( event ) {
747 $( event.currentTarget ).removeClass( "ui-state-hover" );
752 _focusable: function( element ) {
753 this.focusable = this.focusable.add( element );
755 focusin: function( event ) {
756 $( event.currentTarget ).addClass( "ui-state-focus" );
758 focusout: function( event ) {
759 $( event.currentTarget ).removeClass( "ui-state-focus" );
764 _trigger: function( type, event, data ) {
766 callback = this.options[ type ];
769 event = $.Event( event );
770 event.type = ( type === this.widgetEventPrefix ?
772 this.widgetEventPrefix + type ).toLowerCase();
773 // the original event may come from any element
774 // so we need to reset the target on the new event
775 event.target = this.element[ 0 ];
777 // copy original event properties over to the new event
778 orig = event.originalEvent;
780 for ( prop in orig ) {
781 if ( !( prop in event ) ) {
782 event[ prop ] = orig[ prop ];
787 this.element.trigger( event, data );
788 return !( $.isFunction( callback ) &&
789 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
790 event.isDefaultPrevented() );
794 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
795 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
796 if ( typeof options === "string" ) {
797 options = { effect: options };
800 effectName = !options ?
802 options === true || typeof options === "number" ?
804 options.effect || defaultEffect;
805 options = options || {};
806 if ( typeof options === "number" ) {
807 options = { duration: options };
809 hasOptions = !$.isEmptyObject( options );
810 options.complete = callback;
811 if ( options.delay ) {
812 element.delay( options.delay );
814 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
815 element[ method ]( options );
816 } else if ( effectName !== method && element[ effectName ] ) {
817 element[ effectName ]( options.duration, options.easing, callback );
819 element.queue(function( next ) {
820 $( this )[ method ]();
822 callback.call( element[ 0 ] );
831 (function( $, undefined ) {
833 var mouseHandled = false;
834 $( document ).mouseup( function() {
835 mouseHandled = false;
838 $.widget("ui.mouse", {
841 cancel: "input,textarea,button,select,option",
845 _mouseInit: function() {
849 .bind("mousedown."+this.widgetName, function(event) {
850 return that._mouseDown(event);
852 .bind("click."+this.widgetName, function(event) {
853 if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
854 $.removeData(event.target, that.widgetName + ".preventClickEvent");
855 event.stopImmediatePropagation();
860 this.started = false;
863 // TODO: make sure destroying one instance of mouse doesn't mess with
864 // other instances of mouse
865 _mouseDestroy: function() {
866 this.element.unbind("."+this.widgetName);
867 if ( this._mouseMoveDelegate ) {
869 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
870 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
874 _mouseDown: function(event) {
875 // don't let more than one widget handle mouseStart
876 if( mouseHandled ) { return; }
878 // we may have missed mouseup (out of window)
879 (this._mouseStarted && this._mouseUp(event));
881 this._mouseDownEvent = event;
884 btnIsLeft = (event.which === 1),
885 // event.target.nodeName works around a bug in IE 8 with
886 // disabled inputs (#7620)
887 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
888 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
892 this.mouseDelayMet = !this.options.delay;
893 if (!this.mouseDelayMet) {
894 this._mouseDelayTimer = setTimeout(function() {
895 that.mouseDelayMet = true;
896 }, this.options.delay);
899 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
900 this._mouseStarted = (this._mouseStart(event) !== false);
901 if (!this._mouseStarted) {
902 event.preventDefault();
907 // Click event may never have fired (Gecko & Opera)
908 if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
909 $.removeData(event.target, this.widgetName + ".preventClickEvent");
912 // these delegates are required to keep context
913 this._mouseMoveDelegate = function(event) {
914 return that._mouseMove(event);
916 this._mouseUpDelegate = function(event) {
917 return that._mouseUp(event);
920 .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
921 .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
923 event.preventDefault();
929 _mouseMove: function(event) {
930 // IE mouseup check - mouseup happened when mouse was out of window
931 if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
932 return this._mouseUp(event);
935 if (this._mouseStarted) {
936 this._mouseDrag(event);
937 return event.preventDefault();
940 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
942 (this._mouseStart(this._mouseDownEvent, event) !== false);
943 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
946 return !this._mouseStarted;
949 _mouseUp: function(event) {
951 .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
952 .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
954 if (this._mouseStarted) {
955 this._mouseStarted = false;
957 if (event.target === this._mouseDownEvent.target) {
958 $.data(event.target, this.widgetName + ".preventClickEvent", true);
961 this._mouseStop(event);
967 _mouseDistanceMet: function(event) {
969 Math.abs(this._mouseDownEvent.pageX - event.pageX),
970 Math.abs(this._mouseDownEvent.pageY - event.pageY)
971 ) >= this.options.distance
975 _mouseDelayMet: function(/* event */) {
976 return this.mouseDelayMet;
979 // These are placeholder methods, to be overriden by extending plugin
980 _mouseStart: function(/* event */) {},
981 _mouseDrag: function(/* event */) {},
982 _mouseStop: function(/* event */) {},
983 _mouseCapture: function(/* event */) { return true; }
987 (function( $, undefined ) {
989 $.widget("ui.draggable", $.ui.mouse, {
991 widgetEventPrefix: "drag",
996 connectToSortable: false,
1005 refreshPositions: false,
1007 revertDuration: 500,
1010 scrollSensitivity: 20,
1023 _create: function() {
1025 if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
1026 this.element[0].style.position = "relative";
1028 if (this.options.addClasses){
1029 this.element.addClass("ui-draggable");
1031 if (this.options.disabled){
1032 this.element.addClass("ui-draggable-disabled");
1039 _destroy: function() {
1040 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
1041 this._mouseDestroy();
1044 _mouseCapture: function(event) {
1046 var o = this.options;
1048 // among others, prevent a drag on a resizable-handle
1049 if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
1053 //Quit if we're not on a valid handle
1054 this.handle = this._getHandle(event);
1059 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1060 $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
1062 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1063 position: "absolute", opacity: "0.001", zIndex: 1000
1065 .css($(this).offset())
1073 _mouseStart: function(event) {
1075 var o = this.options;
1077 //Create and append the visible helper
1078 this.helper = this._createHelper(event);
1080 this.helper.addClass("ui-draggable-dragging");
1082 //Cache the helper size
1083 this._cacheHelperProportions();
1085 //If ddmanager is used for droppables, set the global draggable
1086 if($.ui.ddmanager) {
1087 $.ui.ddmanager.current = this;
1091 * - Position generation -
1092 * This block generates everything position related - it's the core of draggables.
1095 //Cache the margins of the original element
1096 this._cacheMargins();
1098 //Store the helper's css position
1099 this.cssPosition = this.helper.css("position");
1100 this.scrollParent = this.helper.scrollParent();
1102 //The element's absolute position on the page minus margins
1103 this.offset = this.positionAbs = this.element.offset();
1105 top: this.offset.top - this.margins.top,
1106 left: this.offset.left - this.margins.left
1109 $.extend(this.offset, {
1110 click: { //Where the click happened, relative to the element
1111 left: event.pageX - this.offset.left,
1112 top: event.pageY - this.offset.top
1114 parent: this._getParentOffset(),
1115 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1118 //Generate the original position
1119 this.originalPosition = this.position = this._generatePosition(event);
1120 this.originalPageX = event.pageX;
1121 this.originalPageY = event.pageY;
1123 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1124 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1126 //Set a containment if given in the options
1128 this._setContainment();
1131 //Trigger event + callbacks
1132 if(this._trigger("start", event) === false) {
1137 //Recache the helper size
1138 this._cacheHelperProportions();
1140 //Prepare the droppable offsets
1141 if ($.ui.ddmanager && !o.dropBehaviour) {
1142 $.ui.ddmanager.prepareOffsets(this, event);
1146 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1148 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1149 if ( $.ui.ddmanager ) {
1150 $.ui.ddmanager.dragStart(this, event);
1156 _mouseDrag: function(event, noPropagation) {
1158 //Compute the helpers position
1159 this.position = this._generatePosition(event);
1160 this.positionAbs = this._convertPositionTo("absolute");
1162 //Call plugins and callbacks and use the resulting position if something is returned
1163 if (!noPropagation) {
1164 var ui = this._uiHash();
1165 if(this._trigger("drag", event, ui) === false) {
1169 this.position = ui.position;
1172 if(!this.options.axis || this.options.axis !== "y") {
1173 this.helper[0].style.left = this.position.left+"px";
1175 if(!this.options.axis || this.options.axis !== "x") {
1176 this.helper[0].style.top = this.position.top+"px";
1178 if($.ui.ddmanager) {
1179 $.ui.ddmanager.drag(this, event);
1185 _mouseStop: function(event) {
1187 //If we are using droppables, inform the manager about the drop
1190 elementInDom = false,
1192 if ($.ui.ddmanager && !this.options.dropBehaviour) {
1193 dropped = $.ui.ddmanager.drop(this, event);
1196 //if a drop comes from outside (a sortable)
1198 dropped = this.dropped;
1199 this.dropped = false;
1202 //if the original element is no longer in the DOM don't bother to continue (see #8269)
1203 element = this.element[0];
1204 while ( element && (element = element.parentNode) ) {
1205 if (element === document ) {
1206 elementInDom = true;
1209 if ( !elementInDom && this.options.helper === "original" ) {
1213 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))) {
1214 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1215 if(that._trigger("stop", event) !== false) {
1220 if(this._trigger("stop", event) !== false) {
1228 _mouseUp: function(event) {
1229 //Remove frame helpers
1230 $("div.ui-draggable-iframeFix").each(function() {
1231 this.parentNode.removeChild(this);
1234 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1235 if( $.ui.ddmanager ) {
1236 $.ui.ddmanager.dragStop(this, event);
1239 return $.ui.mouse.prototype._mouseUp.call(this, event);
1242 cancel: function() {
1244 if(this.helper.is(".ui-draggable-dragging")) {
1254 _getHandle: function(event) {
1256 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
1257 $(this.options.handle, this.element)
1261 if(this === event.target) {
1270 _createHelper: function(event) {
1272 var o = this.options,
1273 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
1275 if(!helper.parents("body").length) {
1276 helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
1279 if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
1280 helper.css("position", "absolute");
1287 _adjustOffsetFromHelper: function(obj) {
1288 if (typeof obj === "string") {
1289 obj = obj.split(" ");
1291 if ($.isArray(obj)) {
1292 obj = {left: +obj[0], top: +obj[1] || 0};
1294 if ("left" in obj) {
1295 this.offset.click.left = obj.left + this.margins.left;
1297 if ("right" in obj) {
1298 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1301 this.offset.click.top = obj.top + this.margins.top;
1303 if ("bottom" in obj) {
1304 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1308 _getParentOffset: function() {
1310 //Get the offsetParent and cache its position
1311 this.offsetParent = this.helper.offsetParent();
1312 var po = this.offsetParent.offset();
1314 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1315 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1316 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1317 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1318 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1319 po.left += this.scrollParent.scrollLeft();
1320 po.top += this.scrollParent.scrollTop();
1323 //This needs to be actually done for all browsers, since pageX/pageY includes this information
1325 if((this.offsetParent[0] === document.body) ||
1326 (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
1327 po = { top: 0, left: 0 };
1331 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1332 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1337 _getRelativeOffset: function() {
1339 if(this.cssPosition === "relative") {
1340 var p = this.element.position();
1342 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1343 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1346 return { top: 0, left: 0 };
1351 _cacheMargins: function() {
1353 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1354 top: (parseInt(this.element.css("marginTop"),10) || 0),
1355 right: (parseInt(this.element.css("marginRight"),10) || 0),
1356 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1360 _cacheHelperProportions: function() {
1361 this.helperProportions = {
1362 width: this.helper.outerWidth(),
1363 height: this.helper.outerHeight()
1367 _setContainment: function() {
1372 if(o.containment === "parent") {
1373 o.containment = this.helper[0].parentNode;
1375 if(o.containment === "document" || o.containment === "window") {
1376 this.containment = [
1377 o.containment === "document" ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1378 o.containment === "document" ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1379 (o.containment === "document" ? 0 : $(window).scrollLeft()) + $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
1380 (o.containment === "document" ? 0 : $(window).scrollTop()) + ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
1384 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor !== Array) {
1385 c = $(o.containment);
1392 over = ($(ce).css("overflow") !== "hidden");
1394 this.containment = [
1395 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
1396 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
1397 (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,
1398 (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
1400 this.relative_container = c;
1402 } else if(o.containment.constructor === Array) {
1403 this.containment = o.containment;
1408 _convertPositionTo: function(d, pos) {
1411 pos = this.position;
1414 var mod = d === "absolute" ? 1 : -1,
1415 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
1419 pos.top + // The absolute mouse position
1420 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1421 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
1422 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
1425 pos.left + // The absolute mouse position
1426 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
1427 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
1428 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
1434 _generatePosition: function(event) {
1436 var containment, co, top, left,
1438 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
1439 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName),
1440 pageX = event.pageX,
1441 pageY = event.pageY;
1444 * - Position constraining -
1445 * Constrain the position to a mix of grid, containment.
1448 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
1449 if(this.containment) {
1450 if (this.relative_container){
1451 co = this.relative_container.offset();
1452 containment = [ this.containment[0] + co.left,
1453 this.containment[1] + co.top,
1454 this.containment[2] + co.left,
1455 this.containment[3] + co.top ];
1458 containment = this.containment;
1461 if(event.pageX - this.offset.click.left < containment[0]) {
1462 pageX = containment[0] + this.offset.click.left;
1464 if(event.pageY - this.offset.click.top < containment[1]) {
1465 pageY = containment[1] + this.offset.click.top;
1467 if(event.pageX - this.offset.click.left > containment[2]) {
1468 pageX = containment[2] + this.offset.click.left;
1470 if(event.pageY - this.offset.click.top > containment[3]) {
1471 pageY = containment[3] + this.offset.click.top;
1476 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1477 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1478 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;
1480 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1481 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;
1488 pageY - // The absolute mouse position
1489 this.offset.click.top - // Click offset (relative to the element)
1490 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
1491 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
1492 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
1495 pageX - // The absolute mouse position
1496 this.offset.click.left - // Click offset (relative to the element)
1497 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
1498 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
1499 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
1505 _clear: function() {
1506 this.helper.removeClass("ui-draggable-dragging");
1507 if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
1508 this.helper.remove();
1511 this.cancelHelperRemoval = false;
1514 // From now on bulk stuff - mainly helpers
1516 _trigger: function(type, event, ui) {
1517 ui = ui || this._uiHash();
1518 $.ui.plugin.call(this, type, [event, ui]);
1519 //The absolute position has to be recalculated after plugins
1520 if(type === "drag") {
1521 this.positionAbs = this._convertPositionTo("absolute");
1523 return $.Widget.prototype._trigger.call(this, type, event, ui);
1528 _uiHash: function() {
1530 helper: this.helper,
1531 position: this.position,
1532 originalPosition: this.originalPosition,
1533 offset: this.positionAbs
1539 $.ui.plugin.add("draggable", "connectToSortable", {
1540 start: function(event, ui) {
1542 var inst = $(this).data("ui-draggable"), o = inst.options,
1543 uiSortable = $.extend({}, ui, { item: inst.element });
1544 inst.sortables = [];
1545 $(o.connectToSortable).each(function() {
1546 var sortable = $.data(this, "ui-sortable");
1547 if (sortable && !sortable.options.disabled) {
1548 inst.sortables.push({
1550 shouldRevert: sortable.options.revert
1552 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).
1553 sortable._trigger("activate", event, uiSortable);
1558 stop: function(event, ui) {
1560 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
1561 var inst = $(this).data("ui-draggable"),
1562 uiSortable = $.extend({}, ui, { item: inst.element });
1564 $.each(inst.sortables, function() {
1565 if(this.instance.isOver) {
1567 this.instance.isOver = 0;
1569 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
1570 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
1572 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
1573 if(this.shouldRevert) {
1574 this.instance.options.revert = true;
1577 //Trigger the stop of the sortable
1578 this.instance._mouseStop(event);
1580 this.instance.options.helper = this.instance.options._helper;
1582 //If the helper has been the original item, restore properties in the sortable
1583 if(inst.options.helper === "original") {
1584 this.instance.currentItem.css({ top: "auto", left: "auto" });
1588 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
1589 this.instance._trigger("deactivate", event, uiSortable);
1595 drag: function(event, ui) {
1597 var inst = $(this).data("ui-draggable"), that = this;
1599 $.each(inst.sortables, function() {
1601 var innermostIntersecting = false,
1602 thisSortable = this;
1604 //Copy over some variables to allow calling the sortable's native _intersectsWith
1605 this.instance.positionAbs = inst.positionAbs;
1606 this.instance.helperProportions = inst.helperProportions;
1607 this.instance.offset.click = inst.offset.click;
1609 if(this.instance._intersectsWith(this.instance.containerCache)) {
1610 innermostIntersecting = true;
1611 $.each(inst.sortables, function () {
1612 this.instance.positionAbs = inst.positionAbs;
1613 this.instance.helperProportions = inst.helperProportions;
1614 this.instance.offset.click = inst.offset.click;
1615 if (this !== thisSortable &&
1616 this.instance._intersectsWith(this.instance.containerCache) &&
1617 $.ui.contains(thisSortable.instance.element[0], this.instance.element[0])
1619 innermostIntersecting = false;
1621 return innermostIntersecting;
1626 if(innermostIntersecting) {
1627 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
1628 if(!this.instance.isOver) {
1630 this.instance.isOver = 1;
1631 //Now we fake the start of dragging for the sortable instance,
1632 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
1633 //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)
1634 this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
1635 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
1636 this.instance.options.helper = function() { return ui.helper[0]; };
1638 event.target = this.instance.currentItem[0];
1639 this.instance._mouseCapture(event, true);
1640 this.instance._mouseStart(event, true, true);
1642 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
1643 this.instance.offset.click.top = inst.offset.click.top;
1644 this.instance.offset.click.left = inst.offset.click.left;
1645 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
1646 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
1648 inst._trigger("toSortable", event);
1649 inst.dropped = this.instance.element; //draggable revert needs that
1650 //hack so receive/update callbacks work (mostly)
1651 inst.currentItem = inst.element;
1652 this.instance.fromOutside = inst;
1656 //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
1657 if(this.instance.currentItem) {
1658 this.instance._mouseDrag(event);
1663 //If it doesn't intersect with the sortable, and it intersected before,
1664 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
1665 if(this.instance.isOver) {
1667 this.instance.isOver = 0;
1668 this.instance.cancelHelperRemoval = true;
1670 //Prevent reverting on this forced stop
1671 this.instance.options.revert = false;
1673 // The out event needs to be triggered independently
1674 this.instance._trigger("out", event, this.instance._uiHash(this.instance));
1676 this.instance._mouseStop(event, true);
1677 this.instance.options.helper = this.instance.options._helper;
1679 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
1680 this.instance.currentItem.remove();
1681 if(this.instance.placeholder) {
1682 this.instance.placeholder.remove();
1685 inst._trigger("fromSortable", event);
1686 inst.dropped = false; //draggable revert needs that
1696 $.ui.plugin.add("draggable", "cursor", {
1698 var t = $("body"), o = $(this).data("ui-draggable").options;
1699 if (t.css("cursor")) {
1700 o._cursor = t.css("cursor");
1702 t.css("cursor", o.cursor);
1705 var o = $(this).data("ui-draggable").options;
1707 $("body").css("cursor", o._cursor);
1712 $.ui.plugin.add("draggable", "opacity", {
1713 start: function(event, ui) {
1714 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1715 if(t.css("opacity")) {
1716 o._opacity = t.css("opacity");
1718 t.css("opacity", o.opacity);
1720 stop: function(event, ui) {
1721 var o = $(this).data("ui-draggable").options;
1723 $(ui.helper).css("opacity", o._opacity);
1728 $.ui.plugin.add("draggable", "scroll", {
1730 var i = $(this).data("ui-draggable");
1731 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1732 i.overflowOffset = i.scrollParent.offset();
1735 drag: function( event ) {
1737 var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
1739 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
1741 if(!o.axis || o.axis !== "x") {
1742 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
1743 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
1744 } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
1745 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
1749 if(!o.axis || o.axis !== "y") {
1750 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
1751 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
1752 } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
1753 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
1759 if(!o.axis || o.axis !== "x") {
1760 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
1761 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
1762 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
1763 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
1767 if(!o.axis || o.axis !== "y") {
1768 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
1769 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
1770 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
1771 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
1777 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
1778 $.ui.ddmanager.prepareOffsets(i, event);
1784 $.ui.plugin.add("draggable", "snap", {
1787 var i = $(this).data("ui-draggable"),
1790 i.snapElements = [];
1792 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
1795 if(this !== i.element[0]) {
1796 i.snapElements.push({
1798 width: $t.outerWidth(), height: $t.outerHeight(),
1799 top: $o.top, left: $o.left
1805 drag: function(event, ui) {
1807 var ts, bs, ls, rs, l, r, t, b, i, first,
1808 inst = $(this).data("ui-draggable"),
1810 d = o.snapTolerance,
1811 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
1812 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
1814 for (i = inst.snapElements.length - 1; i >= 0; i--){
1816 l = inst.snapElements[i].left;
1817 r = l + inst.snapElements[i].width;
1818 t = inst.snapElements[i].top;
1819 b = t + inst.snapElements[i].height;
1821 //Yes, I know, this is insane ;)
1822 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))) {
1823 if(inst.snapElements[i].snapping) {
1824 (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1826 inst.snapElements[i].snapping = false;
1830 if(o.snapMode !== "inner") {
1831 ts = Math.abs(t - y2) <= d;
1832 bs = Math.abs(b - y1) <= d;
1833 ls = Math.abs(l - x2) <= d;
1834 rs = Math.abs(r - x1) <= d;
1836 ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1839 ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
1842 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
1845 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
1849 first = (ts || bs || ls || rs);
1851 if(o.snapMode !== "outer") {
1852 ts = Math.abs(t - y1) <= d;
1853 bs = Math.abs(b - y2) <= d;
1854 ls = Math.abs(l - x1) <= d;
1855 rs = Math.abs(r - x2) <= d;
1857 ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
1860 ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
1863 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
1866 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
1870 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
1871 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
1873 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
1880 $.ui.plugin.add("draggable", "stack", {
1884 o = $(this).data("ui-draggable").options,
1885 group = $.makeArray($(o.stack)).sort(function(a,b) {
1886 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
1889 if (!group.length) { return; }
1891 min = parseInt(group[0].style.zIndex, 10) || 0;
1892 $(group).each(function(i) {
1893 this.style.zIndex = min + i;
1896 this[0].style.zIndex = min + group.length;
1901 $.ui.plugin.add("draggable", "zIndex", {
1902 start: function(event, ui) {
1903 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
1904 if(t.css("zIndex")) {
1905 o._zIndex = t.css("zIndex");
1907 t.css("zIndex", o.zIndex);
1909 stop: function(event, ui) {
1910 var o = $(this).data("ui-draggable").options;
1912 $(ui.helper).css("zIndex", o._zIndex);
1918 (function( $, undefined ) {
1920 function isOverAxis( x, reference, size ) {
1921 return ( x > reference ) && ( x < ( reference + size ) );
1924 $.widget("ui.droppable", {
1926 widgetEventPrefix: "drop",
1934 tolerance: "intersect",
1943 _create: function() {
1945 var o = this.options,
1948 this.isover = false;
1951 this.accept = $.isFunction(accept) ? accept : function(d) {
1952 return d.is(accept);
1955 //Store the droppable's proportions
1956 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
1958 // Add the reference and positions to the manager
1959 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
1960 $.ui.ddmanager.droppables[o.scope].push(this);
1962 (o.addClasses && this.element.addClass("ui-droppable"));
1966 _destroy: function() {
1968 drop = $.ui.ddmanager.droppables[this.options.scope];
1970 for ( ; i < drop.length; i++ ) {
1971 if ( drop[i] === this ) {
1976 this.element.removeClass("ui-droppable ui-droppable-disabled");
1979 _setOption: function(key, value) {
1981 if(key === "accept") {
1982 this.accept = $.isFunction(value) ? value : function(d) {
1986 $.Widget.prototype._setOption.apply(this, arguments);
1989 _activate: function(event) {
1990 var draggable = $.ui.ddmanager.current;
1991 if(this.options.activeClass) {
1992 this.element.addClass(this.options.activeClass);
1995 this._trigger("activate", event, this.ui(draggable));
1999 _deactivate: function(event) {
2000 var draggable = $.ui.ddmanager.current;
2001 if(this.options.activeClass) {
2002 this.element.removeClass(this.options.activeClass);
2005 this._trigger("deactivate", event, this.ui(draggable));
2009 _over: function(event) {
2011 var draggable = $.ui.ddmanager.current;
2013 // Bail if draggable and droppable are same element
2014 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2018 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2019 if(this.options.hoverClass) {
2020 this.element.addClass(this.options.hoverClass);
2022 this._trigger("over", event, this.ui(draggable));
2027 _out: function(event) {
2029 var draggable = $.ui.ddmanager.current;
2031 // Bail if draggable and droppable are same element
2032 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2036 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2037 if(this.options.hoverClass) {
2038 this.element.removeClass(this.options.hoverClass);
2040 this._trigger("out", event, this.ui(draggable));
2045 _drop: function(event,custom) {
2047 var draggable = custom || $.ui.ddmanager.current,
2048 childrenIntersection = false;
2050 // Bail if draggable and droppable are same element
2051 if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
2055 this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
2056 var inst = $.data(this, "ui-droppable");
2058 inst.options.greedy &&
2059 !inst.options.disabled &&
2060 inst.options.scope === draggable.options.scope &&
2061 inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
2062 $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
2063 ) { childrenIntersection = true; return false; }
2065 if(childrenIntersection) {
2069 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2070 if(this.options.activeClass) {
2071 this.element.removeClass(this.options.activeClass);
2073 if(this.options.hoverClass) {
2074 this.element.removeClass(this.options.hoverClass);
2076 this._trigger("drop", event, this.ui(draggable));
2077 return this.element;
2086 draggable: (c.currentItem || c.element),
2088 position: c.position,
2089 offset: c.positionAbs
2095 $.ui.intersect = function(draggable, droppable, toleranceMode) {
2097 if (!droppable.offset) {
2101 var draggableLeft, draggableTop,
2102 x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
2103 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
2104 l = droppable.offset.left, r = l + droppable.proportions.width,
2105 t = droppable.offset.top, b = t + droppable.proportions.height;
2107 switch (toleranceMode) {
2109 return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
2111 return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
2112 x2 - (draggable.helperProportions.width / 2) < r && // Left Half
2113 t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
2114 y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
2116 draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
2117 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
2118 return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
2121 (y1 >= t && y1 <= b) || // Top edge touching
2122 (y2 >= t && y2 <= b) || // Bottom edge touching
2123 (y1 < t && y2 > b) // Surrounded vertically
2125 (x1 >= l && x1 <= r) || // Left edge touching
2126 (x2 >= l && x2 <= r) || // Right edge touching
2127 (x1 < l && x2 > r) // Surrounded horizontally
2136 This manager tracks offsets of draggables and droppables
2140 droppables: { "default": [] },
2141 prepareOffsets: function(t, event) {
2144 m = $.ui.ddmanager.droppables[t.options.scope] || [],
2145 type = event ? event.type : null, // workaround for #2317
2146 list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
2148 droppablesLoop: for (i = 0; i < m.length; i++) {
2150 //No disabled and non-accepted
2151 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
2155 // Filter out elements in the current dragged item
2156 for (j=0; j < list.length; j++) {
2157 if(list[j] === m[i].element[0]) {
2158 m[i].proportions.height = 0;
2159 continue droppablesLoop;
2163 m[i].visible = m[i].element.css("display") !== "none";
2168 //Activate the droppable if used directly from draggables
2169 if(type === "mousedown") {
2170 m[i]._activate.call(m[i], event);
2173 m[i].offset = m[i].element.offset();
2174 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
2179 drop: function(draggable, event) {
2181 var dropped = false;
2182 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2187 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
2188 dropped = this._drop.call(this, event) || dropped;
2191 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
2193 this.isover = false;
2194 this._deactivate.call(this, event);
2201 dragStart: function( draggable, event ) {
2202 //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2203 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2204 if( !draggable.options.refreshPositions ) {
2205 $.ui.ddmanager.prepareOffsets( draggable, event );
2209 drag: function(draggable, event) {
2211 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
2212 if(draggable.options.refreshPositions) {
2213 $.ui.ddmanager.prepareOffsets(draggable, event);
2216 //Run through all droppables and check their positions based on specific tolerance options
2217 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
2219 if(this.options.disabled || this.greedyChild || !this.visible) {
2223 var parentInstance, scope, parent,
2224 intersects = $.ui.intersect(draggable, this, this.options.tolerance),
2225 c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
2230 if (this.options.greedy) {
2231 // find droppable parents with same scope
2232 scope = this.options.scope;
2233 parent = this.element.parents(":data(ui-droppable)").filter(function () {
2234 return $.data(this, "ui-droppable").options.scope === scope;
2237 if (parent.length) {
2238 parentInstance = $.data(parent[0], "ui-droppable");
2239 parentInstance.greedyChild = (c === "isover");
2243 // we just moved into a greedy child
2244 if (parentInstance && c === "isover") {
2245 parentInstance.isover = false;
2246 parentInstance.isout = true;
2247 parentInstance._out.call(parentInstance, event);
2251 this[c === "isout" ? "isover" : "isout"] = false;
2252 this[c === "isover" ? "_over" : "_out"].call(this, event);
2254 // we just moved out of a greedy child
2255 if (parentInstance && c === "isout") {
2256 parentInstance.isout = false;
2257 parentInstance.isover = true;
2258 parentInstance._over.call(parentInstance, event);
2263 dragStop: function( draggable, event ) {
2264 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
2265 //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
2266 if( !draggable.options.refreshPositions ) {
2267 $.ui.ddmanager.prepareOffsets( draggable, event );
2273 (function( $, undefined ) {
2275 $.widget("ui.selectable", $.ui.mouse, {
2292 _create: function() {
2296 this.element.addClass("ui-selectable");
2298 this.dragged = false;
2300 // cache selectee children based on filter
2301 this.refresh = function() {
2302 selectees = $(that.options.filter, that.element[0]);
2303 selectees.addClass("ui-selectee");
2304 selectees.each(function() {
2305 var $this = $(this),
2306 pos = $this.offset();
2307 $.data(this, "selectable-item", {
2312 right: pos.left + $this.outerWidth(),
2313 bottom: pos.top + $this.outerHeight(),
2314 startselected: false,
2315 selected: $this.hasClass("ui-selected"),
2316 selecting: $this.hasClass("ui-selecting"),
2317 unselecting: $this.hasClass("ui-unselecting")
2323 this.selectees = selectees.addClass("ui-selectee");
2327 this.helper = $("<div class='ui-selectable-helper'></div>");
2330 _destroy: function() {
2332 .removeClass("ui-selectee")
2333 .removeData("selectable-item");
2335 .removeClass("ui-selectable ui-selectable-disabled");
2336 this._mouseDestroy();
2339 _mouseStart: function(event) {
2341 options = this.options;
2343 this.opos = [event.pageX, event.pageY];
2345 if (this.options.disabled) {
2349 this.selectees = $(options.filter, this.element[0]);
2351 this._trigger("start", event);
2353 $(options.appendTo).append(this.helper);
2354 // position helper (lasso)
2356 "left": event.pageX,
2362 if (options.autoRefresh) {
2366 this.selectees.filter(".ui-selected").each(function() {
2367 var selectee = $.data(this, "selectable-item");
2368 selectee.startselected = true;
2369 if (!event.metaKey && !event.ctrlKey) {
2370 selectee.$element.removeClass("ui-selected");
2371 selectee.selected = false;
2372 selectee.$element.addClass("ui-unselecting");
2373 selectee.unselecting = true;
2374 // selectable UNSELECTING callback
2375 that._trigger("unselecting", event, {
2376 unselecting: selectee.element
2381 $(event.target).parents().addBack().each(function() {
2383 selectee = $.data(this, "selectable-item");
2385 doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
2387 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
2388 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
2389 selectee.unselecting = !doSelect;
2390 selectee.selecting = doSelect;
2391 selectee.selected = doSelect;
2392 // selectable (UN)SELECTING callback
2394 that._trigger("selecting", event, {
2395 selecting: selectee.element
2398 that._trigger("unselecting", event, {
2399 unselecting: selectee.element
2408 _mouseDrag: function(event) {
2410 this.dragged = true;
2412 if (this.options.disabled) {
2418 options = this.options,
2424 if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
2425 if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
2426 this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
2428 this.selectees.each(function() {
2429 var selectee = $.data(this, "selectable-item"),
2432 //prevent helper from being selected if appendTo: selectable
2433 if (!selectee || selectee.element === that.element[0]) {
2437 if (options.tolerance === "touch") {
2438 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
2439 } else if (options.tolerance === "fit") {
2440 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
2445 if (selectee.selected) {
2446 selectee.$element.removeClass("ui-selected");
2447 selectee.selected = false;
2449 if (selectee.unselecting) {
2450 selectee.$element.removeClass("ui-unselecting");
2451 selectee.unselecting = false;
2453 if (!selectee.selecting) {
2454 selectee.$element.addClass("ui-selecting");
2455 selectee.selecting = true;
2456 // selectable SELECTING callback
2457 that._trigger("selecting", event, {
2458 selecting: selectee.element
2463 if (selectee.selecting) {
2464 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
2465 selectee.$element.removeClass("ui-selecting");
2466 selectee.selecting = false;
2467 selectee.$element.addClass("ui-selected");
2468 selectee.selected = true;
2470 selectee.$element.removeClass("ui-selecting");
2471 selectee.selecting = false;
2472 if (selectee.startselected) {
2473 selectee.$element.addClass("ui-unselecting");
2474 selectee.unselecting = true;
2476 // selectable UNSELECTING callback
2477 that._trigger("unselecting", event, {
2478 unselecting: selectee.element
2482 if (selectee.selected) {
2483 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
2484 selectee.$element.removeClass("ui-selected");
2485 selectee.selected = false;
2487 selectee.$element.addClass("ui-unselecting");
2488 selectee.unselecting = true;
2489 // selectable UNSELECTING callback
2490 that._trigger("unselecting", event, {
2491 unselecting: selectee.element
2501 _mouseStop: function(event) {
2504 this.dragged = false;
2506 $(".ui-unselecting", this.element[0]).each(function() {
2507 var selectee = $.data(this, "selectable-item");
2508 selectee.$element.removeClass("ui-unselecting");
2509 selectee.unselecting = false;
2510 selectee.startselected = false;
2511 that._trigger("unselected", event, {
2512 unselected: selectee.element
2515 $(".ui-selecting", this.element[0]).each(function() {
2516 var selectee = $.data(this, "selectable-item");
2517 selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
2518 selectee.selecting = false;
2519 selectee.selected = true;
2520 selectee.startselected = true;
2521 that._trigger("selected", event, {
2522 selected: selectee.element
2525 this._trigger("stop", event);
2527 this.helper.remove();
2535 (function( $, undefined ) {
2537 /*jshint loopfunc: true */
2539 function isOverAxis( x, reference, size ) {
2540 return ( x > reference ) && ( x < ( reference + size ) );
2543 $.widget("ui.sortable", $.ui.mouse, {
2545 widgetEventPrefix: "sort",
2555 forcePlaceholderSize: false,
2556 forceHelperSize: false,
2565 scrollSensitivity: 20,
2568 tolerance: "intersect",
2585 _create: function() {
2587 var o = this.options;
2588 this.containerCache = {};
2589 this.element.addClass("ui-sortable");
2594 //Let's determine if the items are being displayed horizontally
2595 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;
2597 //Let's determine the parent's offset
2598 this.offset = this.element.offset();
2600 //Initialize mouse events for interaction
2608 _destroy: function() {
2610 .removeClass("ui-sortable ui-sortable-disabled");
2611 this._mouseDestroy();
2613 for ( var i = this.items.length - 1; i >= 0; i-- ) {
2614 this.items[i].item.removeData(this.widgetName + "-item");
2620 _setOption: function(key, value){
2621 if ( key === "disabled" ) {
2622 this.options[ key ] = value;
2624 this.widget().toggleClass( "ui-sortable-disabled", !!value );
2626 // Don't call widget base _setOption for disable as it adds ui-state-disabled class
2627 $.Widget.prototype._setOption.apply(this, arguments);
2631 _mouseCapture: function(event, overrideHandle) {
2632 var currentItem = null,
2633 validHandle = false,
2636 if (this.reverting) {
2640 if(this.options.disabled || this.options.type === "static") {
2644 //We have to refresh the items data once first
2645 this._refreshItems(event);
2647 //Find out if the clicked node (or one of its parents) is a actual item in this.items
2648 $(event.target).parents().each(function() {
2649 if($.data(this, that.widgetName + "-item") === that) {
2650 currentItem = $(this);
2654 if($.data(event.target, that.widgetName + "-item") === that) {
2655 currentItem = $(event.target);
2661 if(this.options.handle && !overrideHandle) {
2662 $(this.options.handle, currentItem).find("*").addBack().each(function() {
2663 if(this === event.target) {
2672 this.currentItem = currentItem;
2673 this._removeCurrentsFromItems();
2678 _mouseStart: function(event, overrideHandle, noActivation) {
2683 this.currentContainer = this;
2685 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
2686 this.refreshPositions();
2688 //Create and append the visible helper
2689 this.helper = this._createHelper(event);
2691 //Cache the helper size
2692 this._cacheHelperProportions();
2695 * - Position generation -
2696 * This block generates everything position related - it's the core of draggables.
2699 //Cache the margins of the original element
2700 this._cacheMargins();
2702 //Get the next scrolling parent
2703 this.scrollParent = this.helper.scrollParent();
2705 //The element's absolute position on the page minus margins
2706 this.offset = this.currentItem.offset();
2708 top: this.offset.top - this.margins.top,
2709 left: this.offset.left - this.margins.left
2712 $.extend(this.offset, {
2713 click: { //Where the click happened, relative to the element
2714 left: event.pageX - this.offset.left,
2715 top: event.pageY - this.offset.top
2717 parent: this._getParentOffset(),
2718 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
2721 // Only after we got the offset, we can change the helper's position to absolute
2722 // TODO: Still need to figure out a way to make relative sorting possible
2723 this.helper.css("position", "absolute");
2724 this.cssPosition = this.helper.css("position");
2726 //Generate the original position
2727 this.originalPosition = this._generatePosition(event);
2728 this.originalPageX = event.pageX;
2729 this.originalPageY = event.pageY;
2731 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
2732 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
2734 //Cache the former DOM position
2735 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
2737 //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
2738 if(this.helper[0] !== this.currentItem[0]) {
2739 this.currentItem.hide();
2742 //Create the placeholder
2743 this._createPlaceholder();
2745 //Set a containment if given in the options
2747 this._setContainment();
2750 if(o.cursor) { // cursor option
2751 if ($("body").css("cursor")) {
2752 this._storedCursor = $("body").css("cursor");
2754 $("body").css("cursor", o.cursor);
2757 if(o.opacity) { // opacity option
2758 if (this.helper.css("opacity")) {
2759 this._storedOpacity = this.helper.css("opacity");
2761 this.helper.css("opacity", o.opacity);
2764 if(o.zIndex) { // zIndex option
2765 if (this.helper.css("zIndex")) {
2766 this._storedZIndex = this.helper.css("zIndex");
2768 this.helper.css("zIndex", o.zIndex);
2772 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
2773 this.overflowOffset = this.scrollParent.offset();
2777 this._trigger("start", event, this._uiHash());
2779 //Recache the helper size
2780 if(!this._preserveHelperProportions) {
2781 this._cacheHelperProportions();
2785 //Post "activate" events to possible containers
2786 if( !noActivation ) {
2787 for ( i = this.containers.length - 1; i >= 0; i-- ) {
2788 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
2792 //Prepare possible droppables
2793 if($.ui.ddmanager) {
2794 $.ui.ddmanager.current = this;
2797 if ($.ui.ddmanager && !o.dropBehaviour) {
2798 $.ui.ddmanager.prepareOffsets(this, event);
2801 this.dragging = true;
2803 this.helper.addClass("ui-sortable-helper");
2804 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
2809 _mouseDrag: function(event) {
2810 var i, item, itemElement, intersection,
2814 //Compute the helpers position
2815 this.position = this._generatePosition(event);
2816 this.positionAbs = this._convertPositionTo("absolute");
2818 if (!this.lastPositionAbs) {
2819 this.lastPositionAbs = this.positionAbs;
2823 if(this.options.scroll) {
2824 if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
2826 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
2827 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
2828 } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
2829 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
2832 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
2833 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
2834 } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
2835 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
2840 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
2841 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
2842 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
2843 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
2846 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
2847 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
2848 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
2849 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
2854 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
2855 $.ui.ddmanager.prepareOffsets(this, event);
2859 //Regenerate the absolute position used for position checks
2860 this.positionAbs = this._convertPositionTo("absolute");
2862 //Set the helper position
2863 if(!this.options.axis || this.options.axis !== "y") {
2864 this.helper[0].style.left = this.position.left+"px";
2866 if(!this.options.axis || this.options.axis !== "x") {
2867 this.helper[0].style.top = this.position.top+"px";
2871 for (i = this.items.length - 1; i >= 0; i--) {
2873 //Cache variables and intersection, continue if no intersection
2874 item = this.items[i];
2875 itemElement = item.item[0];
2876 intersection = this._intersectsWithPointer(item);
2877 if (!intersection) {
2881 // Only put the placeholder inside the current Container, skip all
2882 // items form other containers. This works because when moving
2883 // an item from one container to another the
2884 // currentContainer is switched before the placeholder is moved.
2886 // Without this moving items in "sub-sortables" can cause the placeholder to jitter
2887 // beetween the outer and inner container.
2888 if (item.instance !== this.currentContainer) {
2892 // cannot intersect with itself
2893 // no useless actions that have been done before
2894 // no action if the item moved is the parent of the item checked
2895 if (itemElement !== this.currentItem[0] &&
2896 this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
2897 !$.contains(this.placeholder[0], itemElement) &&
2898 (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
2901 this.direction = intersection === 1 ? "down" : "up";
2903 if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
2904 this._rearrange(event, item);
2909 this._trigger("change", event, this._uiHash());
2914 //Post events to containers
2915 this._contactContainers(event);
2917 //Interconnect with droppables
2918 if($.ui.ddmanager) {
2919 $.ui.ddmanager.drag(this, event);
2923 this._trigger("sort", event, this._uiHash());
2925 this.lastPositionAbs = this.positionAbs;
2930 _mouseStop: function(event, noPropagation) {
2936 //If we are using droppables, inform the manager about the drop
2937 if ($.ui.ddmanager && !this.options.dropBehaviour) {
2938 $.ui.ddmanager.drop(this, event);
2941 if(this.options.revert) {
2943 cur = this.placeholder.offset();
2945 this.reverting = true;
2947 $(this.helper).animate({
2948 left: cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft),
2949 top: cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop)
2950 }, parseInt(this.options.revert, 10) || 500, function() {
2954 this._clear(event, noPropagation);
2961 cancel: function() {
2965 this._mouseUp({ target: null });
2967 if(this.options.helper === "original") {
2968 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
2970 this.currentItem.show();
2973 //Post deactivating events to containers
2974 for (var i = this.containers.length - 1; i >= 0; i--){
2975 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
2976 if(this.containers[i].containerCache.over) {
2977 this.containers[i]._trigger("out", null, this._uiHash(this));
2978 this.containers[i].containerCache.over = 0;
2984 if (this.placeholder) {
2985 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
2986 if(this.placeholder[0].parentNode) {
2987 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
2989 if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
2990 this.helper.remove();
3000 if(this.domPosition.prev) {
3001 $(this.domPosition.prev).after(this.currentItem);
3003 $(this.domPosition.parent).prepend(this.currentItem);
3011 serialize: function(o) {
3013 var items = this._getItemsAsjQuery(o && o.connected),
3017 $(items).each(function() {
3018 var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
3020 str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
3024 if(!str.length && o.key) {
3025 str.push(o.key + "=");
3028 return str.join("&");
3032 toArray: function(o) {
3034 var items = this._getItemsAsjQuery(o && o.connected),
3039 items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
3044 /* Be careful with the following core functions */
3045 _intersectsWith: function(item) {
3047 var x1 = this.positionAbs.left,
3048 x2 = x1 + this.helperProportions.width,
3049 y1 = this.positionAbs.top,
3050 y2 = y1 + this.helperProportions.height,
3054 b = t + item.height,
3055 dyClick = this.offset.click.top,
3056 dxClick = this.offset.click.left,
3057 isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
3059 if ( this.options.tolerance === "pointer" ||
3060 this.options.forcePointerForContainers ||
3061 (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
3063 return isOverElement;
3066 return (l < x1 + (this.helperProportions.width / 2) && // Right Half
3067 x2 - (this.helperProportions.width / 2) < r && // Left Half
3068 t < y1 + (this.helperProportions.height / 2) && // Bottom Half
3069 y2 - (this.helperProportions.height / 2) < b ); // Top Half
3074 _intersectsWithPointer: function(item) {
3076 var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
3077 isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
3078 isOverElement = isOverElementHeight && isOverElementWidth,
3079 verticalDirection = this._getDragVerticalDirection(),
3080 horizontalDirection = this._getDragHorizontalDirection();
3082 if (!isOverElement) {
3086 return this.floating ?
3087 ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
3088 : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
3092 _intersectsWithSides: function(item) {
3094 var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
3095 isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
3096 verticalDirection = this._getDragVerticalDirection(),
3097 horizontalDirection = this._getDragHorizontalDirection();
3099 if (this.floating && horizontalDirection) {
3100 return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
3102 return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
3107 _getDragVerticalDirection: function() {
3108 var delta = this.positionAbs.top - this.lastPositionAbs.top;
3109 return delta !== 0 && (delta > 0 ? "down" : "up");
3112 _getDragHorizontalDirection: function() {
3113 var delta = this.positionAbs.left - this.lastPositionAbs.left;
3114 return delta !== 0 && (delta > 0 ? "right" : "left");
3117 refresh: function(event) {
3118 this._refreshItems(event);
3119 this.refreshPositions();
3123 _connectWith: function() {
3124 var options = this.options;
3125 return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
3128 _getItemsAsjQuery: function(connected) {
3130 var i, j, cur, inst,
3133 connectWith = this._connectWith();
3135 if(connectWith && connected) {
3136 for (i = connectWith.length - 1; i >= 0; i--){
3137 cur = $(connectWith[i]);
3138 for ( j = cur.length - 1; j >= 0; j--){
3139 inst = $.data(cur[j], this.widgetFullName);
3140 if(inst && inst !== this && !inst.options.disabled) {
3141 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]);
3147 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]);
3149 for (i = queries.length - 1; i >= 0; i--){
3150 queries[i][0].each(function() {
3159 _removeCurrentsFromItems: function() {
3161 var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
3163 this.items = $.grep(this.items, function (item) {
3164 for (var j=0; j < list.length; j++) {
3165 if(list[j] === item.item[0]) {
3174 _refreshItems: function(event) {
3177 this.containers = [this];
3179 var i, j, cur, inst, targetData, _queries, item, queriesLength,
3181 queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
3182 connectWith = this._connectWith();
3184 if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
3185 for (i = connectWith.length - 1; i >= 0; i--){
3186 cur = $(connectWith[i]);
3187 for (j = cur.length - 1; j >= 0; j--){
3188 inst = $.data(cur[j], this.widgetFullName);
3189 if(inst && inst !== this && !inst.options.disabled) {
3190 queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
3191 this.containers.push(inst);
3197 for (i = queries.length - 1; i >= 0; i--) {
3198 targetData = queries[i][1];
3199 _queries = queries[i][0];
3201 for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
3202 item = $(_queries[j]);
3204 item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
3208 instance: targetData,
3209 width: 0, height: 0,
3217 refreshPositions: function(fast) {
3219 //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
3220 if(this.offsetParent && this.helper) {
3221 this.offset.parent = this._getParentOffset();
3226 for (i = this.items.length - 1; i >= 0; i--){
3227 item = this.items[i];
3229 //We ignore calculating positions of all connected containers when we're not over them
3230 if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
3234 t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
3237 item.width = t.outerWidth();
3238 item.height = t.outerHeight();
3246 if(this.options.custom && this.options.custom.refreshContainers) {
3247 this.options.custom.refreshContainers.call(this);
3249 for (i = this.containers.length - 1; i >= 0; i--){
3250 p = this.containers[i].element.offset();
3251 this.containers[i].containerCache.left = p.left;
3252 this.containers[i].containerCache.top = p.top;
3253 this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
3254 this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
3261 _createPlaceholder: function(that) {
3262 that = that || this;
3266 if(!o.placeholder || o.placeholder.constructor === String) {
3267 className = o.placeholder;
3269 element: function() {
3271 var el = $(document.createElement(that.currentItem[0].nodeName))
3272 .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
3273 .removeClass("ui-sortable-helper")[0];
3276 el.style.visibility = "hidden";
3281 update: function(container, p) {
3283 // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
3284 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
3285 if(className && !o.forcePlaceholderSize) {
3289 //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
3290 if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
3291 if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
3296 //Create the placeholder
3297 that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
3299 //Append it after the actual current item
3300 that.currentItem.after(that.placeholder);
3302 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
3303 o.placeholder.update(that, that.placeholder);
3307 _contactContainers: function(event) {
3308 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom,
3309 innermostContainer = null,
3310 innermostIndex = null;
3312 // get innermost container that intersects with item
3313 for (i = this.containers.length - 1; i >= 0; i--) {
3315 // never consider a container that's located within the item itself
3316 if($.contains(this.currentItem[0], this.containers[i].element[0])) {
3320 if(this._intersectsWith(this.containers[i].containerCache)) {
3322 // if we've already found a container and it's more "inner" than this, then continue
3323 if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
3327 innermostContainer = this.containers[i];
3331 // container doesn't intersect. trigger "out" event if necessary
3332 if(this.containers[i].containerCache.over) {
3333 this.containers[i]._trigger("out", event, this._uiHash(this));
3334 this.containers[i].containerCache.over = 0;
3340 // if no intersecting containers found, return
3341 if(!innermostContainer) {
3345 // move the item into the container if it's not there already
3346 if(this.containers.length === 1) {
3347 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
3348 this.containers[innermostIndex].containerCache.over = 1;
3351 //When entering a new container, we will find the item with the least distance and append our item near it
3353 itemWithLeastDistance = null;
3354 posProperty = this.containers[innermostIndex].floating ? "left" : "top";
3355 sizeProperty = this.containers[innermostIndex].floating ? "width" : "height";
3356 base = this.positionAbs[posProperty] + this.offset.click[posProperty];
3357 for (j = this.items.length - 1; j >= 0; j--) {
3358 if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
3361 if(this.items[j].item[0] === this.currentItem[0]) {
3364 cur = this.items[j].item.offset()[posProperty];
3366 if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
3368 cur += this.items[j][sizeProperty];
3371 if(Math.abs(cur - base) < dist) {
3372 dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
3373 this.direction = nearBottom ? "up": "down";
3377 //Check if dropOnEmpty is enabled
3378 if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
3382 this.currentContainer = this.containers[innermostIndex];
3383 itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
3384 this._trigger("change", event, this._uiHash());
3385 this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
3387 //Update the placeholder
3388 this.options.placeholder.update(this.currentContainer, this.placeholder);
3390 this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
3391 this.containers[innermostIndex].containerCache.over = 1;
3397 _createHelper: function(event) {
3399 var o = this.options,
3400 helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
3402 //Add the helper to the DOM if that didn't happen already
3403 if(!helper.parents("body").length) {
3404 $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
3407 if(helper[0] === this.currentItem[0]) {
3408 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") };
3411 if(!helper[0].style.width || o.forceHelperSize) {
3412 helper.width(this.currentItem.width());
3414 if(!helper[0].style.height || o.forceHelperSize) {
3415 helper.height(this.currentItem.height());
3422 _adjustOffsetFromHelper: function(obj) {
3423 if (typeof obj === "string") {
3424 obj = obj.split(" ");
3426 if ($.isArray(obj)) {
3427 obj = {left: +obj[0], top: +obj[1] || 0};
3429 if ("left" in obj) {
3430 this.offset.click.left = obj.left + this.margins.left;
3432 if ("right" in obj) {
3433 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
3436 this.offset.click.top = obj.top + this.margins.top;
3438 if ("bottom" in obj) {
3439 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
3443 _getParentOffset: function() {
3446 //Get the offsetParent and cache its position
3447 this.offsetParent = this.helper.offsetParent();
3448 var po = this.offsetParent.offset();
3450 // This is a special case where we need to modify a offset calculated on start, since the following happened:
3451 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
3452 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
3453 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
3454 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
3455 po.left += this.scrollParent.scrollLeft();
3456 po.top += this.scrollParent.scrollTop();
3459 // This needs to be actually done for all browsers, since pageX/pageY includes this information
3460 // with an ugly IE fix
3461 if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
3462 po = { top: 0, left: 0 };
3466 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
3467 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
3472 _getRelativeOffset: function() {
3474 if(this.cssPosition === "relative") {
3475 var p = this.currentItem.position();
3477 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
3478 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
3481 return { top: 0, left: 0 };
3486 _cacheMargins: function() {
3488 left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
3489 top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
3493 _cacheHelperProportions: function() {
3494 this.helperProportions = {
3495 width: this.helper.outerWidth(),
3496 height: this.helper.outerHeight()
3500 _setContainment: function() {
3504 if(o.containment === "parent") {
3505 o.containment = this.helper[0].parentNode;
3507 if(o.containment === "document" || o.containment === "window") {
3508 this.containment = [
3509 0 - this.offset.relative.left - this.offset.parent.left,
3510 0 - this.offset.relative.top - this.offset.parent.top,
3511 $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
3512 ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
3516 if(!(/^(document|window|parent)$/).test(o.containment)) {
3517 ce = $(o.containment)[0];
3518 co = $(o.containment).offset();
3519 over = ($(ce).css("overflow") !== "hidden");
3521 this.containment = [
3522 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
3523 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
3524 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,
3525 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
3531 _convertPositionTo: function(d, pos) {
3534 pos = this.position;
3536 var mod = d === "absolute" ? 1 : -1,
3537 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
3538 scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
3542 pos.top + // The absolute mouse position
3543 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
3544 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
3545 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
3548 pos.left + // The absolute mouse position
3549 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
3550 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
3551 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
3557 _generatePosition: function(event) {
3561 pageX = event.pageX,
3562 pageY = event.pageY,
3563 scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
3565 // This is another very weird special case that only happens for relative elements:
3566 // 1. If the css position is relative
3567 // 2. and the scroll parent is the document or similar to the offset parent
3568 // we have to refresh the relative offset during the scroll so there are no jumps
3569 if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
3570 this.offset.relative = this._getRelativeOffset();
3574 * - Position constraining -
3575 * Constrain the position to a mix of grid, containment.
3578 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
3580 if(this.containment) {
3581 if(event.pageX - this.offset.click.left < this.containment[0]) {
3582 pageX = this.containment[0] + this.offset.click.left;
3584 if(event.pageY - this.offset.click.top < this.containment[1]) {
3585 pageY = this.containment[1] + this.offset.click.top;
3587 if(event.pageX - this.offset.click.left > this.containment[2]) {
3588 pageX = this.containment[2] + this.offset.click.left;
3590 if(event.pageY - this.offset.click.top > this.containment[3]) {
3591 pageY = this.containment[3] + this.offset.click.top;
3596 top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
3597 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;
3599 left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
3600 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;
3607 pageY - // The absolute mouse position
3608 this.offset.click.top - // Click offset (relative to the element)
3609 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
3610 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
3611 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
3614 pageX - // The absolute mouse position
3615 this.offset.click.left - // Click offset (relative to the element)
3616 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
3617 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
3618 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
3624 _rearrange: function(event, i, a, hardRefresh) {
3626 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));
3628 //Various things done here to improve the performance:
3629 // 1. we create a setTimeout, that calls refreshPositions
3630 // 2. on the instance, we have a counter variable, that get's higher after every append
3631 // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
3632 // 4. this lets only the last addition to the timeout stack through
3633 this.counter = this.counter ? ++this.counter : 1;
3634 var counter = this.counter;
3636 this._delay(function() {
3637 if(counter === this.counter) {
3638 this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
3644 _clear: function(event, noPropagation) {
3646 this.reverting = false;
3647 // We delay all events that have to be triggered to after the point where the placeholder has been removed and
3648 // everything else normalized again
3650 delayedTriggers = [];
3652 // We first have to update the dom position of the actual currentItem
3653 // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
3654 if(!this._noFinalSort && this.currentItem.parent().length) {
3655 this.placeholder.before(this.currentItem);
3657 this._noFinalSort = null;
3659 if(this.helper[0] === this.currentItem[0]) {
3660 for(i in this._storedCSS) {
3661 if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
3662 this._storedCSS[i] = "";
3665 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
3667 this.currentItem.show();
3670 if(this.fromOutside && !noPropagation) {
3671 delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
3673 if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
3674 delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
3677 // Check if the items Container has Changed and trigger appropriate
3679 if (this !== this.currentContainer) {
3680 if(!noPropagation) {
3681 delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
3682 delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
3683 delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
3688 //Post events to containers
3689 for (i = this.containers.length - 1; i >= 0; i--){
3690 if(!noPropagation) {
3691 delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
3693 if(this.containers[i].containerCache.over) {
3694 delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
3695 this.containers[i].containerCache.over = 0;
3699 //Do what was originally in plugins
3700 if(this._storedCursor) {
3701 $("body").css("cursor", this._storedCursor);
3703 if(this._storedOpacity) {
3704 this.helper.css("opacity", this._storedOpacity);
3706 if(this._storedZIndex) {
3707 this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
3710 this.dragging = false;
3711 if(this.cancelHelperRemoval) {
3712 if(!noPropagation) {
3713 this._trigger("beforeStop", event, this._uiHash());
3714 for (i=0; i < delayedTriggers.length; i++) {
3715 delayedTriggers[i].call(this, event);
3716 } //Trigger all delayed events
3717 this._trigger("stop", event, this._uiHash());
3720 this.fromOutside = false;
3724 if(!noPropagation) {
3725 this._trigger("beforeStop", event, this._uiHash());
3728 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
3729 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
3731 if(this.helper[0] !== this.currentItem[0]) {
3732 this.helper.remove();
3736 if(!noPropagation) {
3737 for (i=0; i < delayedTriggers.length; i++) {
3738 delayedTriggers[i].call(this, event);
3739 } //Trigger all delayed events
3740 this._trigger("stop", event, this._uiHash());
3743 this.fromOutside = false;
3748 _trigger: function() {
3749 if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
3754 _uiHash: function(_inst) {
3755 var inst = _inst || this;
3757 helper: inst.helper,
3758 placeholder: inst.placeholder || $([]),
3759 position: inst.position,
3760 originalPosition: inst.originalPosition,
3761 offset: inst.positionAbs,
3762 item: inst.currentItem,
3763 sender: _inst ? _inst.element : null
3770 ;(jQuery.effects || (function($, undefined) {
3772 var dataSpace = "ui-effects-";
3779 * jQuery Color Animations v2.1.2
3780 * https://github.com/jquery/jquery-color
3782 * Copyright 2013 jQuery Foundation and other contributors
3783 * Released under the MIT license.
3784 * http://jquery.org/license
3786 * Date: Wed Jan 16 08:47:09 2013 -0600
3788 (function( jQuery, undefined ) {
3790 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
3792 // plusequals test for += 100 -= 100
3793 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
3794 // a set of RE's that can match strings and generate color tuples.
3796 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
3797 parse: function( execResult ) {
3806 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
3807 parse: function( execResult ) {
3809 execResult[ 1 ] * 2.55,
3810 execResult[ 2 ] * 2.55,
3811 execResult[ 3 ] * 2.55,
3816 // this regex ignores A-F because it's compared against an already lowercased string
3817 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
3818 parse: function( execResult ) {
3820 parseInt( execResult[ 1 ], 16 ),
3821 parseInt( execResult[ 2 ], 16 ),
3822 parseInt( execResult[ 3 ], 16 )
3826 // this regex ignores A-F because it's compared against an already lowercased string
3827 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
3828 parse: function( execResult ) {
3830 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
3831 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
3832 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
3836 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
3838 parse: function( execResult ) {
3841 execResult[ 2 ] / 100,
3842 execResult[ 3 ] / 100,
3849 color = jQuery.Color = function( color, green, blue, alpha ) {
3850 return new jQuery.Color.fn.parse( color, green, blue, alpha );
3900 support = color.support = {},
3902 // element for support tests
3903 supportElem = jQuery( "<p>" )[ 0 ],
3905 // colors = jQuery.Color.names
3908 // local aliases of functions called often
3911 // determine rgba support immediately
3912 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
3913 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
3915 // define cache name and alpha properties
3916 // for rgba and hsla spaces
3917 each( spaces, function( spaceName, space ) {
3918 space.cache = "_" + spaceName;
3919 space.props.alpha = {
3926 function clamp( value, prop, allowEmpty ) {
3927 var type = propTypes[ prop.type ] || {};
3929 if ( value == null ) {
3930 return (allowEmpty || !prop.def) ? null : prop.def;
3933 // ~~ is an short way of doing floor for positive numbers
3934 value = type.floor ? ~~value : parseFloat( value );
3936 // IE will pass in empty strings as value for alpha,
3937 // which will hit this case
3938 if ( isNaN( value ) ) {
3943 // we add mod before modding to make sure that negatives values
3944 // get converted properly: -10 -> 350
3945 return (value + type.mod) % type.mod;
3948 // for now all property types without mod have min and max
3949 return 0 > value ? 0 : type.max < value ? type.max : value;
3952 function stringParse( string ) {
3954 rgba = inst._rgba = [];
3956 string = string.toLowerCase();
3958 each( stringParsers, function( i, parser ) {
3960 match = parser.re.exec( string ),
3961 values = match && parser.parse( match ),
3962 spaceName = parser.space || "rgba";
3965 parsed = inst[ spaceName ]( values );
3967 // if this was an rgba parse the assignment might happen twice
3969 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
3970 rgba = inst._rgba = parsed._rgba;
3972 // exit each( stringParsers ) here because we matched
3977 // Found a stringParser that handled it
3978 if ( rgba.length ) {
3980 // if this came from a parsed string, force "transparent" when alpha is 0
3981 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
3982 if ( rgba.join() === "0,0,0,0" ) {
3983 jQuery.extend( rgba, colors.transparent );
3989 return colors[ string ];
3992 color.fn = jQuery.extend( color.prototype, {
3993 parse: function( red, green, blue, alpha ) {
3994 if ( red === undefined ) {
3995 this._rgba = [ null, null, null, null ];
3998 if ( red.jquery || red.nodeType ) {
3999 red = jQuery( red ).css( green );
4004 type = jQuery.type( red ),
4005 rgba = this._rgba = [];
4007 // more than 1 argument specified - assume ( red, green, blue, alpha )
4008 if ( green !== undefined ) {
4009 red = [ red, green, blue, alpha ];
4013 if ( type === "string" ) {
4014 return this.parse( stringParse( red ) || colors._default );
4017 if ( type === "array" ) {
4018 each( spaces.rgba.props, function( key, prop ) {
4019 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
4024 if ( type === "object" ) {
4025 if ( red instanceof color ) {
4026 each( spaces, function( spaceName, space ) {
4027 if ( red[ space.cache ] ) {
4028 inst[ space.cache ] = red[ space.cache ].slice();
4032 each( spaces, function( spaceName, space ) {
4033 var cache = space.cache;
4034 each( space.props, function( key, prop ) {
4036 // if the cache doesn't exist, and we know how to convert
4037 if ( !inst[ cache ] && space.to ) {
4039 // if the value was null, we don't need to copy it
4040 // if the key was alpha, we don't need to copy it either
4041 if ( key === "alpha" || red[ key ] == null ) {
4044 inst[ cache ] = space.to( inst._rgba );
4047 // this is the only case where we allow nulls for ALL properties.
4048 // call clamp with alwaysAllowEmpty
4049 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
4052 // everything defined but alpha?
4053 if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
4054 // use the default of 1
4055 inst[ cache ][ 3 ] = 1;
4057 inst._rgba = space.from( inst[ cache ] );
4065 is: function( compare ) {
4066 var is = color( compare ),
4070 each( spaces, function( _, space ) {
4072 isCache = is[ space.cache ];
4074 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
4075 each( space.props, function( _, prop ) {
4076 if ( isCache[ prop.idx ] != null ) {
4077 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
4086 _space: function() {
4089 each( spaces, function( spaceName, space ) {
4090 if ( inst[ space.cache ] ) {
4091 used.push( spaceName );
4096 transition: function( other, distance ) {
4097 var end = color( other ),
4098 spaceName = end._space(),
4099 space = spaces[ spaceName ],
4100 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
4101 start = startColor[ space.cache ] || space.to( startColor._rgba ),
4102 result = start.slice();
4104 end = end[ space.cache ];
4105 each( space.props, function( key, prop ) {
4106 var index = prop.idx,
4107 startValue = start[ index ],
4108 endValue = end[ index ],
4109 type = propTypes[ prop.type ] || {};
4111 // if null, don't override start value
4112 if ( endValue === null ) {
4115 // if null - use end
4116 if ( startValue === null ) {
4117 result[ index ] = endValue;
4120 if ( endValue - startValue > type.mod / 2 ) {
4121 startValue += type.mod;
4122 } else if ( startValue - endValue > type.mod / 2 ) {
4123 startValue -= type.mod;
4126 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
4129 return this[ spaceName ]( result );
4131 blend: function( opaque ) {
4132 // if we are already opaque - return ourself
4133 if ( this._rgba[ 3 ] === 1 ) {
4137 var rgb = this._rgba.slice(),
4139 blend = color( opaque )._rgba;
4141 return color( jQuery.map( rgb, function( v, i ) {
4142 return ( 1 - a ) * blend[ i ] + a * v;
4145 toRgbaString: function() {
4146 var prefix = "rgba(",
4147 rgba = jQuery.map( this._rgba, function( v, i ) {
4148 return v == null ? ( i > 2 ? 1 : 0 ) : v;
4151 if ( rgba[ 3 ] === 1 ) {
4156 return prefix + rgba.join() + ")";
4158 toHslaString: function() {
4159 var prefix = "hsla(",
4160 hsla = jQuery.map( this.hsla(), function( v, i ) {
4167 v = Math.round( v * 100 ) + "%";
4172 if ( hsla[ 3 ] === 1 ) {
4176 return prefix + hsla.join() + ")";
4178 toHexString: function( includeAlpha ) {
4179 var rgba = this._rgba.slice(),
4182 if ( includeAlpha ) {
4183 rgba.push( ~~( alpha * 255 ) );
4186 return "#" + jQuery.map( rgba, function( v ) {
4188 // default to 0 when nulls exist
4189 v = ( v || 0 ).toString( 16 );
4190 return v.length === 1 ? "0" + v : v;
4193 toString: function() {
4194 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
4197 color.fn.parse.prototype = color.fn;
4199 // hsla conversions adapted from:
4200 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
4202 function hue2rgb( p, q, h ) {
4205 return p + (q - p) * h * 6;
4211 return p + (q - p) * ((2/3) - h) * 6;
4216 spaces.hsla.to = function ( rgba ) {
4217 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
4218 return [ null, null, null, rgba[ 3 ] ];
4220 var r = rgba[ 0 ] / 255,
4221 g = rgba[ 1 ] / 255,
4222 b = rgba[ 2 ] / 255,
4224 max = Math.max( r, g, b ),
4225 min = Math.min( r, g, b ),
4231 if ( min === max ) {
4233 } else if ( r === max ) {
4234 h = ( 60 * ( g - b ) / diff ) + 360;
4235 } else if ( g === max ) {
4236 h = ( 60 * ( b - r ) / diff ) + 120;
4238 h = ( 60 * ( r - g ) / diff ) + 240;
4241 // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
4242 // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
4245 } else if ( l <= 0.5 ) {
4248 s = diff / ( 2 - add );
4250 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
4253 spaces.hsla.from = function ( hsla ) {
4254 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
4255 return [ null, null, null, hsla[ 3 ] ];
4257 var h = hsla[ 0 ] / 360,
4261 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
4265 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
4266 Math.round( hue2rgb( p, q, h ) * 255 ),
4267 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
4273 each( spaces, function( spaceName, space ) {
4274 var props = space.props,
4275 cache = space.cache,
4279 // makes rgba() and hsla()
4280 color.fn[ spaceName ] = function( value ) {
4282 // generate a cache for this space if it doesn't exist
4283 if ( to && !this[ cache ] ) {
4284 this[ cache ] = to( this._rgba );
4286 if ( value === undefined ) {
4287 return this[ cache ].slice();
4291 type = jQuery.type( value ),
4292 arr = ( type === "array" || type === "object" ) ? value : arguments,
4293 local = this[ cache ].slice();
4295 each( props, function( key, prop ) {
4296 var val = arr[ type === "object" ? key : prop.idx ];
4297 if ( val == null ) {
4298 val = local[ prop.idx ];
4300 local[ prop.idx ] = clamp( val, prop );
4304 ret = color( from( local ) );
4305 ret[ cache ] = local;
4308 return color( local );
4312 // makes red() green() blue() alpha() hue() saturation() lightness()
4313 each( props, function( key, prop ) {
4314 // alpha is included in more than one space
4315 if ( color.fn[ key ] ) {
4318 color.fn[ key ] = function( value ) {
4319 var vtype = jQuery.type( value ),
4320 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
4321 local = this[ fn ](),
4322 cur = local[ prop.idx ],
4325 if ( vtype === "undefined" ) {
4329 if ( vtype === "function" ) {
4330 value = value.call( this, cur );
4331 vtype = jQuery.type( value );
4333 if ( value == null && prop.empty ) {
4336 if ( vtype === "string" ) {
4337 match = rplusequals.exec( value );
4339 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
4342 local[ prop.idx ] = value;
4343 return this[ fn ]( local );
4348 // add cssHook and .fx.step function for each named hook.
4349 // accept a space separated string of properties
4350 color.hook = function( hook ) {
4351 var hooks = hook.split( " " );
4352 each( hooks, function( i, hook ) {
4353 jQuery.cssHooks[ hook ] = {
4354 set: function( elem, value ) {
4355 var parsed, curElem,
4356 backgroundColor = "";
4358 if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
4359 value = color( parsed || value );
4360 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
4361 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
4363 (backgroundColor === "" || backgroundColor === "transparent") &&
4364 curElem && curElem.style
4367 backgroundColor = jQuery.css( curElem, "backgroundColor" );
4368 curElem = curElem.parentNode;
4373 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
4378 value = value.toRgbaString();
4381 elem.style[ hook ] = value;
4383 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
4387 jQuery.fx.step[ hook ] = function( fx ) {
4388 if ( !fx.colorInit ) {
4389 fx.start = color( fx.elem, hook );
4390 fx.end = color( fx.end );
4391 fx.colorInit = true;
4393 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
4399 color.hook( stepHooks );
4401 jQuery.cssHooks.borderColor = {
4402 expand: function( value ) {
4405 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
4406 expanded[ "border" + part + "Color" ] = value;
4412 // Basic color names only.
4413 // Usage of any of the other color names requires adding yourself or including
4414 // jquery.color.svg-names.js.
4415 colors = jQuery.Color.names = {
4416 // 4.1. Basic color keywords
4434 // 4.2.3. "transparent" color keyword
4435 transparent: [ null, null, null, 0 ],
4443 /******************************************************************************/
4444 /****************************** CLASS ANIMATIONS ******************************/
4445 /******************************************************************************/
4448 var classAnimationActions = [ "add", "remove", "toggle" ],
4461 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
4462 $.fx.step[ prop ] = function( fx ) {
4463 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
4464 jQuery.style( fx.elem, prop, fx.end );
4470 function getElementStyles( elem ) {
4472 style = elem.ownerDocument.defaultView ?
4473 elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
4477 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
4481 if ( typeof style[ key ] === "string" ) {
4482 styles[ $.camelCase( key ) ] = style[ key ];
4485 // support: Opera, IE <9
4487 for ( key in style ) {
4488 if ( typeof style[ key ] === "string" ) {
4489 styles[ key ] = style[ key ];
4498 function styleDifference( oldStyle, newStyle ) {
4502 for ( name in newStyle ) {
4503 value = newStyle[ name ];
4504 if ( oldStyle[ name ] !== value ) {
4505 if ( !shorthandStyles[ name ] ) {
4506 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
4507 diff[ name ] = value;
4516 // support: jQuery <1.8
4517 if ( !$.fn.addBack ) {
4518 $.fn.addBack = function( selector ) {
4519 return this.add( selector == null ?
4520 this.prevObject : this.prevObject.filter( selector )
4525 $.effects.animateClass = function( value, duration, easing, callback ) {
4526 var o = $.speed( duration, easing, callback );
4528 return this.queue( function() {
4529 var animated = $( this ),
4530 baseClass = animated.attr( "class" ) || "",
4532 allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
4534 // map the animated objects to store the original styles.
4535 allAnimations = allAnimations.map(function() {
4539 start: getElementStyles( this )
4543 // apply class change
4544 applyClassChange = function() {
4545 $.each( classAnimationActions, function(i, action) {
4546 if ( value[ action ] ) {
4547 animated[ action + "Class" ]( value[ action ] );
4553 // map all animated objects again - calculate new styles and diff
4554 allAnimations = allAnimations.map(function() {
4555 this.end = getElementStyles( this.el[ 0 ] );
4556 this.diff = styleDifference( this.start, this.end );
4560 // apply original class
4561 animated.attr( "class", baseClass );
4563 // map all animated objects again - this time collecting a promise
4564 allAnimations = allAnimations.map(function() {
4565 var styleInfo = this,
4567 opts = $.extend({}, o, {
4569 complete: function() {
4570 dfd.resolve( styleInfo );
4574 this.el.animate( this.diff, opts );
4575 return dfd.promise();
4578 // once all animations have completed:
4579 $.when.apply( $, allAnimations.get() ).done(function() {
4581 // set the final class
4584 // for each animated element,
4585 // clear all css properties that were animated
4586 $.each( arguments, function() {
4588 $.each( this.diff, function(key) {
4593 // this is guarnteed to be there if you use jQuery.speed()
4594 // it also handles dequeuing the next anim...
4595 o.complete.call( animated[ 0 ] );
4601 _addClass: $.fn.addClass,
4602 addClass: function( classNames, speed, easing, callback ) {
4604 $.effects.animateClass.call( this,
4605 { add: classNames }, speed, easing, callback ) :
4606 this._addClass( classNames );
4609 _removeClass: $.fn.removeClass,
4610 removeClass: function( classNames, speed, easing, callback ) {
4612 $.effects.animateClass.call( this,
4613 { remove: classNames }, speed, easing, callback ) :
4614 this._removeClass( classNames );
4617 _toggleClass: $.fn.toggleClass,
4618 toggleClass: function( classNames, force, speed, easing, callback ) {
4619 if ( typeof force === "boolean" || force === undefined ) {
4621 // without speed parameter
4622 return this._toggleClass( classNames, force );
4624 return $.effects.animateClass.call( this,
4625 (force ? { add: classNames } : { remove: classNames }),
4626 speed, easing, callback );
4629 // without force parameter
4630 return $.effects.animateClass.call( this,
4631 { toggle: classNames }, force, speed, easing );
4635 switchClass: function( remove, add, speed, easing, callback) {
4636 return $.effects.animateClass.call( this, {
4639 }, speed, easing, callback );
4645 /******************************************************************************/
4646 /*********************************** EFFECTS **********************************/
4647 /******************************************************************************/
4651 $.extend( $.effects, {
4654 // Saves a set of properties in a data storage
4655 save: function( element, set ) {
4656 for( var i=0; i < set.length; i++ ) {
4657 if ( set[ i ] !== null ) {
4658 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
4663 // Restores a set of previously saved properties from a data storage
4664 restore: function( element, set ) {
4666 for( i=0; i < set.length; i++ ) {
4667 if ( set[ i ] !== null ) {
4668 val = element.data( dataSpace + set[ i ] );
4669 // support: jQuery 1.6.2
4670 // http://bugs.jquery.com/ticket/9917
4671 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
4672 // We can't differentiate between "" and 0 here, so we just assume
4673 // empty string since it's likely to be a more common value...
4674 if ( val === undefined ) {
4677 element.css( set[ i ], val );
4682 setMode: function( el, mode ) {
4683 if (mode === "toggle") {
4684 mode = el.is( ":hidden" ) ? "show" : "hide";
4689 // Translates a [top,left] array into a baseline value
4690 // this should be a little more flexible in the future to handle a string & hash
4691 getBaseline: function( origin, original ) {
4693 switch ( origin[ 0 ] ) {
4694 case "top": y = 0; break;
4695 case "middle": y = 0.5; break;
4696 case "bottom": y = 1; break;
4697 default: y = origin[ 0 ] / original.height;
4699 switch ( origin[ 1 ] ) {
4700 case "left": x = 0; break;
4701 case "center": x = 0.5; break;
4702 case "right": x = 1; break;
4703 default: x = origin[ 1 ] / original.width;
4711 // Wraps the element around a wrapper that copies position properties
4712 createWrapper: function( element ) {
4714 // if the element is already wrapped, return it
4715 if ( element.parent().is( ".ui-effects-wrapper" )) {
4716 return element.parent();
4721 width: element.outerWidth(true),
4722 height: element.outerHeight(true),
4723 "float": element.css( "float" )
4725 wrapper = $( "<div></div>" )
4726 .addClass( "ui-effects-wrapper" )
4729 background: "transparent",
4734 // Store the size in case width/height are defined in % - Fixes #5245
4736 width: element.width(),
4737 height: element.height()
4739 active = document.activeElement;
4742 // Firefox incorrectly exposes anonymous content
4743 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
4747 active = document.body;
4750 element.wrap( wrapper );
4752 // Fixes #7595 - Elements lose focus when wrapped.
4753 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
4754 $( active ).focus();
4757 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
4759 // transfer positioning properties to the wrapper
4760 if ( element.css( "position" ) === "static" ) {
4761 wrapper.css({ position: "relative" });
4762 element.css({ position: "relative" });
4765 position: element.css( "position" ),
4766 zIndex: element.css( "z-index" )
4768 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
4769 props[ pos ] = element.css( pos );
4770 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
4771 props[ pos ] = "auto";
4775 position: "relative",
4784 return wrapper.css( props ).show();
4787 removeWrapper: function( element ) {
4788 var active = document.activeElement;
4790 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
4791 element.parent().replaceWith( element );
4793 // Fixes #7595 - Elements lose focus when wrapped.
4794 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
4795 $( active ).focus();
4803 setTransition: function( element, list, factor, value ) {
4804 value = value || {};
4805 $.each( list, function( i, x ) {
4806 var unit = element.cssUnit( x );
4807 if ( unit[ 0 ] > 0 ) {
4808 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
4815 // return an effect options object for the given parameters:
4816 function _normalizeArguments( effect, options, speed, callback ) {
4818 // allow passing all options as the first parameter
4819 if ( $.isPlainObject( effect ) ) {
4821 effect = effect.effect;
4824 // convert to an object
4825 effect = { effect: effect };
4827 // catch (effect, null, ...)
4828 if ( options == null ) {
4832 // catch (effect, callback)
4833 if ( $.isFunction( options ) ) {
4839 // catch (effect, speed, ?)
4840 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
4846 // catch (effect, options, callback)
4847 if ( $.isFunction( speed ) ) {
4852 // add options to effect
4854 $.extend( effect, options );
4857 speed = speed || options.duration;
4858 effect.duration = $.fx.off ? 0 :
4859 typeof speed === "number" ? speed :
4860 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
4861 $.fx.speeds._default;
4863 effect.complete = callback || options.complete;
4868 function standardSpeed( speed ) {
4869 // valid standard speeds
4870 if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
4874 // invalid strings - treat as "normal" speed
4875 return typeof speed === "string" && !$.effects.effect[ speed ];
4879 effect: function( /* effect, options, speed, callback */ ) {
4880 var args = _normalizeArguments.apply( this, arguments ),
4883 effectMethod = $.effects.effect[ args.effect ];
4885 if ( $.fx.off || !effectMethod ) {
4886 // delegate to the original method (e.g., .show()) if possible
4888 return this[ mode ]( args.duration, args.complete );
4890 return this.each( function() {
4891 if ( args.complete ) {
4892 args.complete.call( this );
4898 function run( next ) {
4899 var elem = $( this ),
4900 complete = args.complete,
4904 if ( $.isFunction( complete ) ) {
4905 complete.call( elem[0] );
4907 if ( $.isFunction( next ) ) {
4912 // if the element is hiddden and mode is hide,
4913 // or element is visible and mode is show
4914 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
4917 effectMethod.call( elem[0], args, done );
4921 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
4925 show: function( speed ) {
4926 if ( standardSpeed( speed ) ) {
4927 return this._show.apply( this, arguments );
4929 var args = _normalizeArguments.apply( this, arguments );
4931 return this.effect.call( this, args );
4936 hide: function( speed ) {
4937 if ( standardSpeed( speed ) ) {
4938 return this._hide.apply( this, arguments );
4940 var args = _normalizeArguments.apply( this, arguments );
4942 return this.effect.call( this, args );
4946 // jQuery core overloads toggle and creates _toggle
4947 __toggle: $.fn.toggle,
4948 toggle: function( speed ) {
4949 if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
4950 return this.__toggle.apply( this, arguments );
4952 var args = _normalizeArguments.apply( this, arguments );
4953 args.mode = "toggle";
4954 return this.effect.call( this, args );
4959 cssUnit: function(key) {
4960 var style = this.css( key ),
4963 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
4964 if ( style.indexOf( unit ) > 0 ) {
4965 val = [ parseFloat( style ), unit ];
4974 /******************************************************************************/
4975 /*********************************** EASING ***********************************/
4976 /******************************************************************************/
4980 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
4982 var baseEasings = {};
4984 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
4985 baseEasings[ name ] = function( p ) {
4986 return Math.pow( p, i + 2 );
4990 $.extend( baseEasings, {
4991 Sine: function ( p ) {
4992 return 1 - Math.cos( p * Math.PI / 2 );
4994 Circ: function ( p ) {
4995 return 1 - Math.sqrt( 1 - p * p );
4997 Elastic: function( p ) {
4998 return p === 0 || p === 1 ? p :
4999 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
5001 Back: function( p ) {
5002 return p * p * ( 3 * p - 2 );
5004 Bounce: function ( p ) {
5008 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
5009 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
5013 $.each( baseEasings, function( name, easeIn ) {
5014 $.easing[ "easeIn" + name ] = easeIn;
5015 $.easing[ "easeOut" + name ] = function( p ) {
5016 return 1 - easeIn( 1 - p );
5018 $.easing[ "easeInOut" + name ] = function( p ) {
5020 easeIn( p * 2 ) / 2 :
5021 1 - easeIn( p * -2 + 2 ) / 2;