1 /*! jQuery UI - v1.9.2 - 2012-12-12
3 * Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.draggable.js, jquery.ui.effect.js
4 * Copyright (c) 2012 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().andSelf().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 );
180 var body = document.body,
181 div = body.appendChild( div = document.createElement( "div" ) );
183 // access offsetHeight before setting the style to prevent a layout bug
184 // in IE 9 which causes the element to continue to take up space even
185 // after it is removed from the DOM (#8026)
188 $.extend( div.style, {
195 $.support.minHeight = div.offsetHeight === 100;
196 $.support.selectstart = "onselectstart" in div;
198 // set display to none to avoid a layout bug in IE
199 // http://dev.jquery.com/ticket/4014
200 body.removeChild( div ).style.display = "none";
203 // support: jQuery <1.8
204 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
205 $.each( [ "Width", "Height" ], function( i, name ) {
206 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
207 type = name.toLowerCase(),
209 innerWidth: $.fn.innerWidth,
210 innerHeight: $.fn.innerHeight,
211 outerWidth: $.fn.outerWidth,
212 outerHeight: $.fn.outerHeight
215 function reduce( elem, size, border, margin ) {
216 $.each( side, function() {
217 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
219 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
222 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
228 $.fn[ "inner" + name ] = function( size ) {
229 if ( size === undefined ) {
230 return orig[ "inner" + name ].call( this );
233 return this.each(function() {
234 $( this ).css( type, reduce( this, size ) + "px" );
238 $.fn[ "outer" + name] = function( size, margin ) {
239 if ( typeof size !== "number" ) {
240 return orig[ "outer" + name ].call( this, size );
243 return this.each(function() {
244 $( this).css( type, reduce( this, size, true, margin ) + "px" );
250 // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
251 if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
252 $.fn.removeData = (function( removeData ) {
253 return function( key ) {
254 if ( arguments.length ) {
255 return removeData.call( this, $.camelCase( key ) );
257 return removeData.call( this );
260 })( $.fn.removeData );
270 var uaMatch = /msie ([\w.]+)/.exec( navigator.userAgent.toLowerCase() ) || [];
271 $.ui.ie = uaMatch.length ? true : false;
272 $.ui.ie6 = parseFloat( uaMatch[ 1 ], 10 ) === 6;
276 disableSelection: function() {
277 return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
278 ".ui-disableSelection", function( event ) {
279 event.preventDefault();
283 enableSelection: function() {
284 return this.unbind( ".ui-disableSelection" );
289 // $.ui.plugin is deprecated. Use the proxy pattern instead.
291 add: function( module, option, set ) {
293 proto = $.ui[ module ].prototype;
295 proto.plugins[ i ] = proto.plugins[ i ] || [];
296 proto.plugins[ i ].push( [ option, set[ i ] ] );
299 call: function( instance, name, args ) {
301 set = instance.plugins[ name ];
302 if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
306 for ( i = 0; i < set.length; i++ ) {
307 if ( instance.options[ set[ i ][ 0 ] ] ) {
308 set[ i ][ 1 ].apply( instance.element, args );
314 contains: $.contains,
316 // only used by resizable
317 hasScroll: function( el, a ) {
319 //If overflow is hidden, the element might have extra content, but the user wants to hide it
320 if ( $( el ).css( "overflow" ) === "hidden") {
324 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
327 if ( el[ scroll ] > 0 ) {
331 // TODO: determine which cases actually cause this to happen
332 // if the element doesn't have the scroll set, see if it's possible to
335 has = ( el[ scroll ] > 0 );
340 // these are odd functions, fix the API or move into individual plugins
341 isOverAxis: function( x, reference, size ) {
342 //Determines when x coordinate is over "b" element axis
343 return ( x > reference ) && ( x < ( reference + size ) );
345 isOver: function( y, x, top, left, height, width ) {
346 //Determines when x, y coordinates is over "b" element
347 return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
352 (function( $, undefined ) {
355 slice = Array.prototype.slice,
356 _cleanData = $.cleanData;
357 $.cleanData = function( elems ) {
358 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
360 $( elem ).triggerHandler( "remove" );
361 // http://bugs.jquery.com/ticket/8235
367 $.widget = function( name, base, prototype ) {
368 var fullName, existingConstructor, constructor, basePrototype,
369 namespace = name.split( "." )[ 0 ];
371 name = name.split( "." )[ 1 ];
372 fullName = namespace + "-" + name;
379 // create selector for plugin
380 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
381 return !!$.data( elem, fullName );
384 $[ namespace ] = $[ namespace ] || {};
385 existingConstructor = $[ namespace ][ name ];
386 constructor = $[ namespace ][ name ] = function( options, element ) {
387 // allow instantiation without "new" keyword
388 if ( !this._createWidget ) {
389 return new constructor( options, element );
392 // allow instantiation without initializing for simple inheritance
393 // must use "new" keyword (the code above always passes args)
394 if ( arguments.length ) {
395 this._createWidget( options, element );
398 // extend with the existing constructor to carry over any static properties
399 $.extend( constructor, existingConstructor, {
400 version: prototype.version,
401 // copy the object used to create the prototype in case we need to
402 // redefine the widget later
403 _proto: $.extend( {}, prototype ),
404 // track widgets that inherit from this widget in case this widget is
405 // redefined after a widget inherits from it
406 _childConstructors: []
409 basePrototype = new base();
410 // we need to make the options hash a property directly on the new instance
411 // otherwise we'll modify the options hash on the prototype that we're
413 basePrototype.options = $.widget.extend( {}, basePrototype.options );
414 $.each( prototype, function( prop, value ) {
415 if ( $.isFunction( value ) ) {
416 prototype[ prop ] = (function() {
417 var _super = function() {
418 return base.prototype[ prop ].apply( this, arguments );
420 _superApply = function( args ) {
421 return base.prototype[ prop ].apply( this, args );
424 var __super = this._super,
425 __superApply = this._superApply,
428 this._super = _super;
429 this._superApply = _superApply;
431 returnValue = value.apply( this, arguments );
433 this._super = __super;
434 this._superApply = __superApply;
441 constructor.prototype = $.widget.extend( basePrototype, {
442 // TODO: remove support for widgetEventPrefix
443 // always use the name + a colon as the prefix, e.g., draggable:start
444 // don't prefix for widgets that aren't DOM-based
445 widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
447 constructor: constructor,
448 namespace: namespace,
450 // TODO remove widgetBaseClass, see #8155
451 widgetBaseClass: fullName,
452 widgetFullName: fullName
455 // If this widget is being redefined then we need to find all widgets that
456 // are inheriting from it and redefine all of them so that they inherit from
457 // the new version of this widget. We're essentially trying to replace one
458 // level in the prototype chain.
459 if ( existingConstructor ) {
460 $.each( existingConstructor._childConstructors, function( i, child ) {
461 var childPrototype = child.prototype;
463 // redefine the child widget using the same prototype that was
464 // originally used, but inherit from the new version of the base
465 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
467 // remove the list of existing child constructors from the old constructor
468 // so the old child constructors can be garbage collected
469 delete existingConstructor._childConstructors;
471 base._childConstructors.push( constructor );
474 $.widget.bridge( name, constructor );
477 $.widget.extend = function( target ) {
478 var input = slice.call( arguments, 1 ),
480 inputLength = input.length,
483 for ( ; inputIndex < inputLength; inputIndex++ ) {
484 for ( key in input[ inputIndex ] ) {
485 value = input[ inputIndex ][ key ];
486 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
488 if ( $.isPlainObject( value ) ) {
489 target[ key ] = $.isPlainObject( target[ key ] ) ?
490 $.widget.extend( {}, target[ key ], value ) :
491 // Don't extend strings, arrays, etc. with objects
492 $.widget.extend( {}, value );
493 // Copy everything else by reference
495 target[ key ] = value;
503 $.widget.bridge = function( name, object ) {
504 var fullName = object.prototype.widgetFullName || name;
505 $.fn[ name ] = function( options ) {
506 var isMethodCall = typeof options === "string",
507 args = slice.call( arguments, 1 ),
510 // allow multiple hashes to be passed on init
511 options = !isMethodCall && args.length ?
512 $.widget.extend.apply( null, [ options ].concat(args) ) :
515 if ( isMethodCall ) {
516 this.each(function() {
518 instance = $.data( this, fullName );
520 return $.error( "cannot call methods on " + name + " prior to initialization; " +
521 "attempted to call method '" + options + "'" );
523 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
524 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
526 methodValue = instance[ options ].apply( instance, args );
527 if ( methodValue !== instance && methodValue !== undefined ) {
528 returnValue = methodValue && methodValue.jquery ?
529 returnValue.pushStack( methodValue.get() ) :
535 this.each(function() {
536 var instance = $.data( this, fullName );
538 instance.option( options || {} )._init();
540 $.data( this, fullName, new object( options, this ) );
549 $.Widget = function( /* options, element */ ) {};
550 $.Widget._childConstructors = [];
552 $.Widget.prototype = {
553 widgetName: "widget",
554 widgetEventPrefix: "",
555 defaultElement: "<div>",
562 _createWidget: function( options, element ) {
563 element = $( element || this.defaultElement || this )[ 0 ];
564 this.element = $( element );
566 this.eventNamespace = "." + this.widgetName + this.uuid;
567 this.options = $.widget.extend( {},
569 this._getCreateOptions(),
573 this.hoverable = $();
574 this.focusable = $();
576 if ( element !== this ) {
578 // TODO remove dual storage
579 $.data( element, this.widgetName, this );
580 $.data( element, this.widgetFullName, this );
581 this._on( true, this.element, {
582 remove: function( event ) {
583 if ( event.target === element ) {
588 this.document = $( element.style ?
589 // element within the document
590 element.ownerDocument :
591 // element is window or document
592 element.document || element );
593 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
597 this._trigger( "create", null, this._getCreateEventData() );
600 _getCreateOptions: $.noop,
601 _getCreateEventData: $.noop,
605 destroy: function() {
607 // we can probably remove the unbind calls in 2.0
608 // all event bindings should go through this._on()
610 .unbind( this.eventNamespace )
612 // TODO remove dual storage
613 .removeData( this.widgetName )
614 .removeData( this.widgetFullName )
615 // support: jquery <1.6.3
616 // http://bugs.jquery.com/ticket/9413
617 .removeData( $.camelCase( this.widgetFullName ) );
619 .unbind( this.eventNamespace )
620 .removeAttr( "aria-disabled" )
622 this.widgetFullName + "-disabled " +
623 "ui-state-disabled" );
625 // clean up events and states
626 this.bindings.unbind( this.eventNamespace );
627 this.hoverable.removeClass( "ui-state-hover" );
628 this.focusable.removeClass( "ui-state-focus" );
636 option: function( key, value ) {
642 if ( arguments.length === 0 ) {
643 // don't return a reference to the internal hash
644 return $.widget.extend( {}, this.options );
647 if ( typeof key === "string" ) {
648 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
650 parts = key.split( "." );
652 if ( parts.length ) {
653 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
654 for ( i = 0; i < parts.length - 1; i++ ) {
655 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
656 curOption = curOption[ parts[ i ] ];
659 if ( value === undefined ) {
660 return curOption[ key ] === undefined ? null : curOption[ key ];
662 curOption[ key ] = value;
664 if ( value === undefined ) {
665 return this.options[ key ] === undefined ? null : this.options[ key ];
667 options[ key ] = value;
671 this._setOptions( options );
675 _setOptions: function( options ) {
678 for ( key in options ) {
679 this._setOption( key, options[ key ] );
684 _setOption: function( key, value ) {
685 this.options[ key ] = value;
687 if ( key === "disabled" ) {
689 .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
690 .attr( "aria-disabled", value );
691 this.hoverable.removeClass( "ui-state-hover" );
692 this.focusable.removeClass( "ui-state-focus" );
699 return this._setOption( "disabled", false );
701 disable: function() {
702 return this._setOption( "disabled", true );
705 _on: function( suppressDisabledCheck, element, handlers ) {
709 // no suppressDisabledCheck flag, shuffle arguments
710 if ( typeof suppressDisabledCheck !== "boolean" ) {
712 element = suppressDisabledCheck;
713 suppressDisabledCheck = false;
716 // no element argument, shuffle and use this.element
719 element = this.element;
720 delegateElement = this.widget();
722 // accept selectors, DOM elements
723 element = delegateElement = $( element );
724 this.bindings = this.bindings.add( element );
727 $.each( handlers, function( event, handler ) {
728 function handlerProxy() {
729 // allow widgets to customize the disabled handling
730 // - disabled as an array instead of boolean
731 // - disabled class as method for disabling individual parts
732 if ( !suppressDisabledCheck &&
733 ( instance.options.disabled === true ||
734 $( this ).hasClass( "ui-state-disabled" ) ) ) {
737 return ( typeof handler === "string" ? instance[ handler ] : handler )
738 .apply( instance, arguments );
741 // copy the guid so direct unbinding works
742 if ( typeof handler !== "string" ) {
743 handlerProxy.guid = handler.guid =
744 handler.guid || handlerProxy.guid || $.guid++;
747 var match = event.match( /^(\w+)\s*(.*)$/ ),
748 eventName = match[1] + instance.eventNamespace,
751 delegateElement.delegate( selector, eventName, handlerProxy );
753 element.bind( eventName, handlerProxy );
758 _off: function( element, eventName ) {
759 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
760 element.unbind( eventName ).undelegate( eventName );
763 _delay: function( handler, delay ) {
764 function handlerProxy() {
765 return ( typeof handler === "string" ? instance[ handler ] : handler )
766 .apply( instance, arguments );
769 return setTimeout( handlerProxy, delay || 0 );
772 _hoverable: function( element ) {
773 this.hoverable = this.hoverable.add( element );
775 mouseenter: function( event ) {
776 $( event.currentTarget ).addClass( "ui-state-hover" );
778 mouseleave: function( event ) {
779 $( event.currentTarget ).removeClass( "ui-state-hover" );
784 _focusable: function( element ) {
785 this.focusable = this.focusable.add( element );
787 focusin: function( event ) {
788 $( event.currentTarget ).addClass( "ui-state-focus" );
790 focusout: function( event ) {
791 $( event.currentTarget ).removeClass( "ui-state-focus" );
796 _trigger: function( type, event, data ) {
798 callback = this.options[ type ];
801 event = $.Event( event );
802 event.type = ( type === this.widgetEventPrefix ?
804 this.widgetEventPrefix + type ).toLowerCase();
805 // the original event may come from any element
806 // so we need to reset the target on the new event
807 event.target = this.element[ 0 ];
809 // copy original event properties over to the new event
810 orig = event.originalEvent;
812 for ( prop in orig ) {
813 if ( !( prop in event ) ) {
814 event[ prop ] = orig[ prop ];
819 this.element.trigger( event, data );
820 return !( $.isFunction( callback ) &&
821 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
822 event.isDefaultPrevented() );
826 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
827 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
828 if ( typeof options === "string" ) {
829 options = { effect: options };
832 effectName = !options ?
834 options === true || typeof options === "number" ?
836 options.effect || defaultEffect;
837 options = options || {};
838 if ( typeof options === "number" ) {
839 options = { duration: options };
841 hasOptions = !$.isEmptyObject( options );
842 options.complete = callback;
843 if ( options.delay ) {
844 element.delay( options.delay );
846 if ( hasOptions && $.effects && ( $.effects.effect[ effectName ] || $.uiBackCompat !== false && $.effects[ effectName ] ) ) {
847 element[ method ]( options );
848 } else if ( effectName !== method && element[ effectName ] ) {
849 element[ effectName ]( options.duration, options.easing, callback );
851 element.queue(function( next ) {
852 $( this )[ method ]();
854 callback.call( element[ 0 ] );
863 if ( $.uiBackCompat !== false ) {
864 $.Widget.prototype._getCreateOptions = function() {
865 return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
870 (function( $, undefined ) {
872 var mouseHandled = false;
873 $( document ).mouseup( function( e ) {
874 mouseHandled = false;
877 $.widget("ui.mouse", {
880 cancel: 'input,textarea,button,select,option',
884 _mouseInit: function() {
888 .bind('mousedown.'+this.widgetName, function(event) {
889 return that._mouseDown(event);
891 .bind('click.'+this.widgetName, function(event) {
892 if (true === $.data(event.target, that.widgetName + '.preventClickEvent')) {
893 $.removeData(event.target, that.widgetName + '.preventClickEvent');
894 event.stopImmediatePropagation();
899 this.started = false;
902 // TODO: make sure destroying one instance of mouse doesn't mess with
903 // other instances of mouse
904 _mouseDestroy: function() {
905 this.element.unbind('.'+this.widgetName);
906 if ( this._mouseMoveDelegate ) {
908 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
909 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
913 _mouseDown: function(event) {
914 // don't let more than one widget handle mouseStart
915 if( mouseHandled ) { return; }
917 // we may have missed mouseup (out of window)
918 (this._mouseStarted && this._mouseUp(event));
920 this._mouseDownEvent = event;
923 btnIsLeft = (event.which === 1),
924 // event.target.nodeName works around a bug in IE 8 with
925 // disabled inputs (#7620)
926 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
927 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
931 this.mouseDelayMet = !this.options.delay;
932 if (!this.mouseDelayMet) {
933 this._mouseDelayTimer = setTimeout(function() {
934 that.mouseDelayMet = true;
935 }, this.options.delay);
938 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
939 this._mouseStarted = (this._mouseStart(event) !== false);
940 if (!this._mouseStarted) {
941 event.preventDefault();
946 // Click event may never have fired (Gecko & Opera)
947 if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
948 $.removeData(event.target, this.widgetName + '.preventClickEvent');
951 // these delegates are required to keep context
952 this._mouseMoveDelegate = function(event) {
953 return that._mouseMove(event);
955 this._mouseUpDelegate = function(event) {
956 return that._mouseUp(event);
959 .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
960 .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
962 event.preventDefault();
968 _mouseMove: function(event) {
969 // IE mouseup check - mouseup happened when mouse was out of window
970 if ($.ui.ie && !(document.documentMode >= 9) && !event.button) {
971 return this._mouseUp(event);
974 if (this._mouseStarted) {
975 this._mouseDrag(event);
976 return event.preventDefault();
979 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
981 (this._mouseStart(this._mouseDownEvent, event) !== false);
982 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
985 return !this._mouseStarted;
988 _mouseUp: function(event) {
990 .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
991 .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
993 if (this._mouseStarted) {
994 this._mouseStarted = false;
996 if (event.target === this._mouseDownEvent.target) {
997 $.data(event.target, this.widgetName + '.preventClickEvent', true);
1000 this._mouseStop(event);
1006 _mouseDistanceMet: function(event) {
1008 Math.abs(this._mouseDownEvent.pageX - event.pageX),
1009 Math.abs(this._mouseDownEvent.pageY - event.pageY)
1010 ) >= this.options.distance
1014 _mouseDelayMet: function(event) {
1015 return this.mouseDelayMet;
1018 // These are placeholder methods, to be overriden by extending plugin
1019 _mouseStart: function(event) {},
1020 _mouseDrag: function(event) {},
1021 _mouseStop: function(event) {},
1022 _mouseCapture: function(event) { return true; }
1026 (function( $, undefined ) {
1030 var cachedScrollbarWidth,
1034 rhorizontal = /left|center|right/,
1035 rvertical = /top|center|bottom/,
1036 roffset = /[\+\-]\d+%?/,
1039 _position = $.fn.position;
1041 function getOffsets( offsets, width, height ) {
1043 parseInt( offsets[ 0 ], 10 ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
1044 parseInt( offsets[ 1 ], 10 ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
1047 function parseCss( element, property ) {
1048 return parseInt( $.css( element, property ), 10 ) || 0;
1052 scrollbarWidth: function() {
1053 if ( cachedScrollbarWidth !== undefined ) {
1054 return cachedScrollbarWidth;
1057 div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
1058 innerDiv = div.children()[0];
1060 $( "body" ).append( div );
1061 w1 = innerDiv.offsetWidth;
1062 div.css( "overflow", "scroll" );
1064 w2 = innerDiv.offsetWidth;
1067 w2 = div[0].clientWidth;
1072 return (cachedScrollbarWidth = w1 - w2);
1074 getScrollInfo: function( within ) {
1075 var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
1076 overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
1077 hasOverflowX = overflowX === "scroll" ||
1078 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
1079 hasOverflowY = overflowY === "scroll" ||
1080 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
1082 width: hasOverflowX ? $.position.scrollbarWidth() : 0,
1083 height: hasOverflowY ? $.position.scrollbarWidth() : 0
1086 getWithinInfo: function( element ) {
1087 var withinElement = $( element || window ),
1088 isWindow = $.isWindow( withinElement[0] );
1090 element: withinElement,
1092 offset: withinElement.offset() || { left: 0, top: 0 },
1093 scrollLeft: withinElement.scrollLeft(),
1094 scrollTop: withinElement.scrollTop(),
1095 width: isWindow ? withinElement.width() : withinElement.outerWidth(),
1096 height: isWindow ? withinElement.height() : withinElement.outerHeight()
1101 $.fn.position = function( options ) {
1102 if ( !options || !options.of ) {
1103 return _position.apply( this, arguments );
1106 // make a copy, we don't want to modify arguments
1107 options = $.extend( {}, options );
1109 var atOffset, targetWidth, targetHeight, targetOffset, basePosition,
1110 target = $( options.of ),
1111 within = $.position.getWithinInfo( options.within ),
1112 scrollInfo = $.position.getScrollInfo( within ),
1113 targetElem = target[0],
1114 collision = ( options.collision || "flip" ).split( " " ),
1117 if ( targetElem.nodeType === 9 ) {
1118 targetWidth = target.width();
1119 targetHeight = target.height();
1120 targetOffset = { top: 0, left: 0 };
1121 } else if ( $.isWindow( targetElem ) ) {
1122 targetWidth = target.width();
1123 targetHeight = target.height();
1124 targetOffset = { top: target.scrollTop(), left: target.scrollLeft() };
1125 } else if ( targetElem.preventDefault ) {
1126 // force left top to allow flipping
1127 options.at = "left top";
1128 targetWidth = targetHeight = 0;
1129 targetOffset = { top: targetElem.pageY, left: targetElem.pageX };
1131 targetWidth = target.outerWidth();
1132 targetHeight = target.outerHeight();
1133 targetOffset = target.offset();
1135 // clone to reuse original targetOffset later
1136 basePosition = $.extend( {}, targetOffset );
1138 // force my and at to have valid horizontal and vertical positions
1139 // if a value is missing or invalid, it will be converted to center
1140 $.each( [ "my", "at" ], function() {
1141 var pos = ( options[ this ] || "" ).split( " " ),
1145 if ( pos.length === 1) {
1146 pos = rhorizontal.test( pos[ 0 ] ) ?
1147 pos.concat( [ "center" ] ) :
1148 rvertical.test( pos[ 0 ] ) ?
1149 [ "center" ].concat( pos ) :
1150 [ "center", "center" ];
1152 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
1153 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
1155 // calculate offsets
1156 horizontalOffset = roffset.exec( pos[ 0 ] );
1157 verticalOffset = roffset.exec( pos[ 1 ] );
1159 horizontalOffset ? horizontalOffset[ 0 ] : 0,
1160 verticalOffset ? verticalOffset[ 0 ] : 0
1163 // reduce to just the positions without the offsets
1165 rposition.exec( pos[ 0 ] )[ 0 ],
1166 rposition.exec( pos[ 1 ] )[ 0 ]
1170 // normalize collision option
1171 if ( collision.length === 1 ) {
1172 collision[ 1 ] = collision[ 0 ];
1175 if ( options.at[ 0 ] === "right" ) {
1176 basePosition.left += targetWidth;
1177 } else if ( options.at[ 0 ] === "center" ) {
1178 basePosition.left += targetWidth / 2;
1181 if ( options.at[ 1 ] === "bottom" ) {
1182 basePosition.top += targetHeight;
1183 } else if ( options.at[ 1 ] === "center" ) {
1184 basePosition.top += targetHeight / 2;
1187 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
1188 basePosition.left += atOffset[ 0 ];
1189 basePosition.top += atOffset[ 1 ];
1191 return this.each(function() {
1192 var collisionPosition, using,
1194 elemWidth = elem.outerWidth(),
1195 elemHeight = elem.outerHeight(),
1196 marginLeft = parseCss( this, "marginLeft" ),
1197 marginTop = parseCss( this, "marginTop" ),
1198 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
1199 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
1200 position = $.extend( {}, basePosition ),
1201 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
1203 if ( options.my[ 0 ] === "right" ) {
1204 position.left -= elemWidth;
1205 } else if ( options.my[ 0 ] === "center" ) {
1206 position.left -= elemWidth / 2;
1209 if ( options.my[ 1 ] === "bottom" ) {
1210 position.top -= elemHeight;
1211 } else if ( options.my[ 1 ] === "center" ) {
1212 position.top -= elemHeight / 2;
1215 position.left += myOffset[ 0 ];
1216 position.top += myOffset[ 1 ];
1218 // if the browser doesn't support fractions, then round for consistent results
1219 if ( !$.support.offsetFractions ) {
1220 position.left = round( position.left );
1221 position.top = round( position.top );
1224 collisionPosition = {
1225 marginLeft: marginLeft,
1226 marginTop: marginTop
1229 $.each( [ "left", "top" ], function( i, dir ) {
1230 if ( $.ui.position[ collision[ i ] ] ) {
1231 $.ui.position[ collision[ i ] ][ dir ]( position, {
1232 targetWidth: targetWidth,
1233 targetHeight: targetHeight,
1234 elemWidth: elemWidth,
1235 elemHeight: elemHeight,
1236 collisionPosition: collisionPosition,
1237 collisionWidth: collisionWidth,
1238 collisionHeight: collisionHeight,
1239 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
1248 if ( $.fn.bgiframe ) {
1252 if ( options.using ) {
1253 // adds feedback as second argument to using callback, if present
1254 using = function( props ) {
1255 var left = targetOffset.left - position.left,
1256 right = left + targetWidth - elemWidth,
1257 top = targetOffset.top - position.top,
1258 bottom = top + targetHeight - elemHeight,
1262 left: targetOffset.left,
1263 top: targetOffset.top,
1265 height: targetHeight
1269 left: position.left,
1274 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
1275 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
1277 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
1278 feedback.horizontal = "center";
1280 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
1281 feedback.vertical = "middle";
1283 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
1284 feedback.important = "horizontal";
1286 feedback.important = "vertical";
1288 options.using.call( this, props, feedback );
1292 elem.offset( $.extend( position, { using: using } ) );
1298 left: function( position, data ) {
1299 var within = data.within,
1300 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
1301 outerWidth = within.width,
1302 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1303 overLeft = withinOffset - collisionPosLeft,
1304 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
1307 // element is wider than within
1308 if ( data.collisionWidth > outerWidth ) {
1309 // element is initially over the left side of within
1310 if ( overLeft > 0 && overRight <= 0 ) {
1311 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
1312 position.left += overLeft - newOverRight;
1313 // element is initially over right side of within
1314 } else if ( overRight > 0 && overLeft <= 0 ) {
1315 position.left = withinOffset;
1316 // element is initially over both left and right sides of within
1318 if ( overLeft > overRight ) {
1319 position.left = withinOffset + outerWidth - data.collisionWidth;
1321 position.left = withinOffset;
1324 // too far left -> align with left edge
1325 } else if ( overLeft > 0 ) {
1326 position.left += overLeft;
1327 // too far right -> align with right edge
1328 } else if ( overRight > 0 ) {
1329 position.left -= overRight;
1330 // adjust based on position and margin
1332 position.left = max( position.left - collisionPosLeft, position.left );
1335 top: function( position, data ) {
1336 var within = data.within,
1337 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
1338 outerHeight = data.within.height,
1339 collisionPosTop = position.top - data.collisionPosition.marginTop,
1340 overTop = withinOffset - collisionPosTop,
1341 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
1344 // element is taller than within
1345 if ( data.collisionHeight > outerHeight ) {
1346 // element is initially over the top of within
1347 if ( overTop > 0 && overBottom <= 0 ) {
1348 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
1349 position.top += overTop - newOverBottom;
1350 // element is initially over bottom of within
1351 } else if ( overBottom > 0 && overTop <= 0 ) {
1352 position.top = withinOffset;
1353 // element is initially over both top and bottom of within
1355 if ( overTop > overBottom ) {
1356 position.top = withinOffset + outerHeight - data.collisionHeight;
1358 position.top = withinOffset;
1361 // too far up -> align with top
1362 } else if ( overTop > 0 ) {
1363 position.top += overTop;
1364 // too far down -> align with bottom edge
1365 } else if ( overBottom > 0 ) {
1366 position.top -= overBottom;
1367 // adjust based on position and margin
1369 position.top = max( position.top - collisionPosTop, position.top );
1374 left: function( position, data ) {
1375 var within = data.within,
1376 withinOffset = within.offset.left + within.scrollLeft,
1377 outerWidth = within.width,
1378 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
1379 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1380 overLeft = collisionPosLeft - offsetLeft,
1381 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
1382 myOffset = data.my[ 0 ] === "left" ?
1384 data.my[ 0 ] === "right" ?
1387 atOffset = data.at[ 0 ] === "left" ?
1389 data.at[ 0 ] === "right" ?
1392 offset = -2 * data.offset[ 0 ],
1396 if ( overLeft < 0 ) {
1397 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
1398 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
1399 position.left += myOffset + atOffset + offset;
1402 else if ( overRight > 0 ) {
1403 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
1404 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
1405 position.left += myOffset + atOffset + offset;
1409 top: function( position, data ) {
1410 var within = data.within,
1411 withinOffset = within.offset.top + within.scrollTop,
1412 outerHeight = within.height,
1413 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
1414 collisionPosTop = position.top - data.collisionPosition.marginTop,
1415 overTop = collisionPosTop - offsetTop,
1416 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
1417 top = data.my[ 1 ] === "top",
1420 data.my[ 1 ] === "bottom" ?
1423 atOffset = data.at[ 1 ] === "top" ?
1425 data.at[ 1 ] === "bottom" ?
1426 -data.targetHeight :
1428 offset = -2 * data.offset[ 1 ],
1431 if ( overTop < 0 ) {
1432 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
1433 if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
1434 position.top += myOffset + atOffset + offset;
1437 else if ( overBottom > 0 ) {
1438 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
1439 if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
1440 position.top += myOffset + atOffset + offset;
1447 $.ui.position.flip.left.apply( this, arguments );
1448 $.ui.position.fit.left.apply( this, arguments );
1451 $.ui.position.flip.top.apply( this, arguments );
1452 $.ui.position.fit.top.apply( this, arguments );
1457 // fraction support test
1459 var testElement, testElementParent, testElementStyle, offsetLeft, i,
1460 body = document.getElementsByTagName( "body" )[ 0 ],
1461 div = document.createElement( "div" );
1463 //Create a "fake body" for testing based on method used in jQuery.support
1464 testElement = document.createElement( body ? "div" : "body" );
1465 testElementStyle = {
1466 visibility: "hidden",
1474 $.extend( testElementStyle, {
1475 position: "absolute",
1480 for ( i in testElementStyle ) {
1481 testElement.style[ i ] = testElementStyle[ i ];
1483 testElement.appendChild( div );
1484 testElementParent = body || document.documentElement;
1485 testElementParent.insertBefore( testElement, testElementParent.firstChild );
1487 div.style.cssText = "position: absolute; left: 10.7432222px;";
1489 offsetLeft = $( div ).offset().left;
1490 $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
1492 testElement.innerHTML = "";
1493 testElementParent.removeChild( testElement );
1497 if ( $.uiBackCompat !== false ) {
1500 var _position = $.fn.position;
1501 $.fn.position = function( options ) {
1502 if ( !options || !options.offset ) {
1503 return _position.call( this, options );
1505 var offset = options.offset.split( " " ),
1506 at = options.at.split( " " );
1507 if ( offset.length === 1 ) {
1508 offset[ 1 ] = offset[ 0 ];
1510 if ( /^\d/.test( offset[ 0 ] ) ) {
1511 offset[ 0 ] = "+" + offset[ 0 ];
1513 if ( /^\d/.test( offset[ 1 ] ) ) {
1514 offset[ 1 ] = "+" + offset[ 1 ];
1516 if ( at.length === 1 ) {
1517 if ( /left|center|right/.test( at[ 0 ] ) ) {
1524 return _position.call( this, $.extend( options, {
1525 at: at[ 0 ] + offset[ 0 ] + " " + at[ 1 ] + offset[ 1 ],
1533 (function( $, undefined ) {
1535 $.widget("ui.draggable", $.ui.mouse, {
1537 widgetEventPrefix: "drag",
1542 connectToSortable: false,
1551 refreshPositions: false,
1553 revertDuration: 500,
1556 scrollSensitivity: 20,
1564 _create: function() {
1566 if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
1567 this.element[0].style.position = 'relative';
1569 (this.options.addClasses && this.element.addClass("ui-draggable"));
1570 (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
1576 _destroy: function() {
1577 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
1578 this._mouseDestroy();
1581 _mouseCapture: function(event) {
1583 var o = this.options;
1585 // among others, prevent a drag on a resizable-handle
1586 if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
1589 //Quit if we're not on a valid handle
1590 this.handle = this._getHandle(event);
1594 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
1595 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
1597 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
1598 position: "absolute", opacity: "0.001", zIndex: 1000
1600 .css($(this).offset())
1608 _mouseStart: function(event) {
1610 var o = this.options;
1612 //Create and append the visible helper
1613 this.helper = this._createHelper(event);
1615 this.helper.addClass("ui-draggable-dragging");
1617 //Cache the helper size
1618 this._cacheHelperProportions();
1620 //If ddmanager is used for droppables, set the global draggable
1622 $.ui.ddmanager.current = this;
1625 * - Position generation -
1626 * This block generates everything position related - it's the core of draggables.
1629 //Cache the margins of the original element
1630 this._cacheMargins();
1632 //Store the helper's css position
1633 this.cssPosition = this.helper.css("position");
1634 this.scrollParent = this.helper.scrollParent();
1636 //The element's absolute position on the page minus margins
1637 this.offset = this.positionAbs = this.element.offset();
1639 top: this.offset.top - this.margins.top,
1640 left: this.offset.left - this.margins.left
1643 $.extend(this.offset, {
1644 click: { //Where the click happened, relative to the element
1645 left: event.pageX - this.offset.left,
1646 top: event.pageY - this.offset.top
1648 parent: this._getParentOffset(),
1649 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
1652 //Generate the original position
1653 this.originalPosition = this.position = this._generatePosition(event);
1654 this.originalPageX = event.pageX;
1655 this.originalPageY = event.pageY;
1657 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
1658 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1660 //Set a containment if given in the options
1662 this._setContainment();
1664 //Trigger event + callbacks
1665 if(this._trigger("start", event) === false) {
1670 //Recache the helper size
1671 this._cacheHelperProportions();
1673 //Prepare the droppable offsets
1674 if ($.ui.ddmanager && !o.dropBehaviour)
1675 $.ui.ddmanager.prepareOffsets(this, event);
1678 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1680 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1681 if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
1686 _mouseDrag: function(event, noPropagation) {
1688 //Compute the helpers position
1689 this.position = this._generatePosition(event);
1690 this.positionAbs = this._convertPositionTo("absolute");
1692 //Call plugins and callbacks and use the resulting position if something is returned
1693 if (!noPropagation) {
1694 var ui = this._uiHash();
1695 if(this._trigger('drag', event, ui) === false) {
1699 this.position = ui.position;
1702 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
1703 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
1704 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
1709 _mouseStop: function(event) {
1711 //If we are using droppables, inform the manager about the drop
1712 var dropped = false;
1713 if ($.ui.ddmanager && !this.options.dropBehaviour)
1714 dropped = $.ui.ddmanager.drop(this, event);
1716 //if a drop comes from outside (a sortable)
1718 dropped = this.dropped;
1719 this.dropped = false;
1722 //if the original element is no longer in the DOM don't bother to continue (see #8269)
1723 var element = this.element[0], elementInDom = false;
1724 while ( element && (element = element.parentNode) ) {
1725 if (element == document ) {
1726 elementInDom = true;
1729 if ( !elementInDom && this.options.helper === "original" )
1732 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))) {
1734 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1735 if(that._trigger("stop", event) !== false) {
1740 if(this._trigger("stop", event) !== false) {
1748 _mouseUp: function(event) {
1749 //Remove frame helpers
1750 $("div.ui-draggable-iframeFix").each(function() {
1751 this.parentNode.removeChild(this);
1754 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1755 if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
1757 return $.ui.mouse.prototype._mouseUp.call(this, event);
1760 cancel: function() {
1762 if(this.helper.is(".ui-draggable-dragging")) {
1772 _getHandle: function(event) {
1774 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
1775 $(this.options.handle, this.element)
1779 if(this == event.target) handle = true;
1786 _createHelper: function(event) {
1788 var o = this.options;
1789 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
1791 if(!helper.parents('body').length)
1792 helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
1794 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
1795 helper.css("position", "absolute");
1801 _adjustOffsetFromHelper: function(obj) {
1802 if (typeof obj == 'string') {
1803 obj = obj.split(' ');
1805 if ($.isArray(obj)) {
1806 obj = {left: +obj[0], top: +obj[1] || 0};
1808 if ('left' in obj) {
1809 this.offset.click.left = obj.left + this.margins.left;
1811 if ('right' in obj) {
1812 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1815 this.offset.click.top = obj.top + this.margins.top;
1817 if ('bottom' in obj) {
1818 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1822 _getParentOffset: function() {
1824 //Get the offsetParent and cache its position
1825 this.offsetParent = this.helper.offsetParent();
1826 var po = this.offsetParent.offset();
1828 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1829 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1830 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1831 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1832 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1833 po.left += this.scrollParent.scrollLeft();
1834 po.top += this.scrollParent.scrollTop();
1837 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
1838 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.ui.ie)) //Ugly IE fix
1839 po = { top: 0, left: 0 };
1842 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
1843 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
1848 _getRelativeOffset: function() {
1850 if(this.cssPosition == "relative") {
1851 var p = this.element.position();
1853 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
1854 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
1857 return { top: 0, left: 0 };
1862 _cacheMargins: function() {
1864 left: (parseInt(this.element.css("marginLeft"),10) || 0),
1865 top: (parseInt(this.element.css("marginTop"),10) || 0),
1866 right: (parseInt(this.element.css("marginRight"),10) || 0),
1867 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
1871 _cacheHelperProportions: function() {
1872 this.helperProportions = {
1873 width: this.helper.outerWidth(),
1874 height: this.helper.outerHeight()
1878 _setContainment: function() {
1880 var o = this.options;
1881 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
1882 if(o.containment == 'document' || o.containment == 'window') this.containment = [
1883 o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
1884 o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
1885 (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
1886 (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
1889 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
1890 var c = $(o.containment);
1891 var ce = c[0]; if(!ce) return;
1892 var co = c.offset();
1893 var over = ($(ce).css("overflow") != 'hidden');
1895 this.containment = [
1896 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
1897 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
1898 (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,
1899 (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
1901 this.relative_container = c;
1903 } else if(o.containment.constructor == Array) {
1904 this.containment = o.containment;
1909 _convertPositionTo: function(d, pos) {
1911 if(!pos) pos = this.position;
1912 var mod = d == "absolute" ? 1 : -1;
1913 var o = this.options, 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);
1917 pos.top // The absolute mouse position
1918 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1919 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
1920 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
1923 pos.left // The absolute mouse position
1924 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
1925 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
1926 - ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
1932 _generatePosition: function(event) {
1934 var o = this.options, 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);
1935 var pageX = event.pageX;
1936 var pageY = event.pageY;
1939 * - Position constraining -
1940 * Constrain the position to a mix of grid, containment.
1943 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
1945 if(this.containment) {
1946 if (this.relative_container){
1947 var co = this.relative_container.offset();
1948 containment = [ this.containment[0] + co.left,
1949 this.containment[1] + co.top,
1950 this.containment[2] + co.left,
1951 this.containment[3] + co.top ];
1954 containment = this.containment;
1957 if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
1958 if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
1959 if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
1960 if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
1964 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
1965 var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
1966 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;
1968 var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
1969 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;
1976 pageY // The absolute mouse position
1977 - this.offset.click.top // Click offset (relative to the element)
1978 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
1979 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
1980 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
1983 pageX // The absolute mouse position
1984 - this.offset.click.left // Click offset (relative to the element)
1985 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
1986 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
1987 + ( ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
1993 _clear: function() {
1994 this.helper.removeClass("ui-draggable-dragging");
1995 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
1996 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
1998 this.cancelHelperRemoval = false;
2001 // From now on bulk stuff - mainly helpers
2003 _trigger: function(type, event, ui) {
2004 ui = ui || this._uiHash();
2005 $.ui.plugin.call(this, type, [event, ui]);
2006 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
2007 return $.Widget.prototype._trigger.call(this, type, event, ui);
2012 _uiHash: function(event) {
2014 helper: this.helper,
2015 position: this.position,
2016 originalPosition: this.originalPosition,
2017 offset: this.positionAbs
2023 $.ui.plugin.add("draggable", "connectToSortable", {
2024 start: function(event, ui) {
2026 var inst = $(this).data("draggable"), o = inst.options,
2027 uiSortable = $.extend({}, ui, { item: inst.element });
2028 inst.sortables = [];
2029 $(o.connectToSortable).each(function() {
2030 var sortable = $.data(this, 'sortable');
2031 if (sortable && !sortable.options.disabled) {
2032 inst.sortables.push({
2034 shouldRevert: sortable.options.revert
2036 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).
2037 sortable._trigger("activate", event, uiSortable);
2042 stop: function(event, ui) {
2044 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
2045 var inst = $(this).data("draggable"),
2046 uiSortable = $.extend({}, ui, { item: inst.element });
2048 $.each(inst.sortables, function() {
2049 if(this.instance.isOver) {
2051 this.instance.isOver = 0;
2053 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
2054 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
2056 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
2057 if(this.shouldRevert) this.instance.options.revert = true;
2059 //Trigger the stop of the sortable
2060 this.instance._mouseStop(event);
2062 this.instance.options.helper = this.instance.options._helper;
2064 //If the helper has been the original item, restore properties in the sortable
2065 if(inst.options.helper == 'original')
2066 this.instance.currentItem.css({ top: 'auto', left: 'auto' });
2069 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
2070 this.instance._trigger("deactivate", event, uiSortable);
2076 drag: function(event, ui) {
2078 var inst = $(this).data("draggable"), that = this;
2080 var checkPos = function(o) {
2081 var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
2082 var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
2083 var itemHeight = o.height, itemWidth = o.width;
2084 var itemTop = o.top, itemLeft = o.left;
2086 return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
2089 $.each(inst.sortables, function(i) {
2091 var innermostIntersecting = false;
2092 var thisSortable = this;
2093 //Copy over some variables to allow calling the sortable's native _intersectsWith
2094 this.instance.positionAbs = inst.positionAbs;
2095 this.instance.helperProportions = inst.helperProportions;
2096 this.instance.offset.click = inst.offset.click;
2098 if(this.instance._intersectsWith(this.instance.containerCache)) {
2099 innermostIntersecting = true;
2100 $.each(inst.sortables, function () {
2101 this.instance.positionAbs = inst.positionAbs;
2102 this.instance.helperProportions = inst.helperProportions;
2103 this.instance.offset.click = inst.offset.click;
2104 if (this != thisSortable
2105 && this.instance._intersectsWith(this.instance.containerCache)
2106 && $.ui.contains(thisSortable.instance.element[0], this.instance.element[0]))
2107 innermostIntersecting = false;
2108 return innermostIntersecting;
2113 if(innermostIntersecting) {
2114 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
2115 if(!this.instance.isOver) {
2117 this.instance.isOver = 1;
2118 //Now we fake the start of dragging for the sortable instance,
2119 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
2120 //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)
2121 this.instance.currentItem = $(that).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
2122 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
2123 this.instance.options.helper = function() { return ui.helper[0]; };
2125 event.target = this.instance.currentItem[0];
2126 this.instance._mouseCapture(event, true);
2127 this.instance._mouseStart(event, true, true);
2129 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
2130 this.instance.offset.click.top = inst.offset.click.top;
2131 this.instance.offset.click.left = inst.offset.click.left;
2132 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
2133 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
2135 inst._trigger("toSortable", event);
2136 inst.dropped = this.instance.element; //draggable revert needs that
2137 //hack so receive/update callbacks work (mostly)
2138 inst.currentItem = inst.element;
2139 this.instance.fromOutside = inst;
2143 //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
2144 if(this.instance.currentItem) this.instance._mouseDrag(event);
2148 //If it doesn't intersect with the sortable, and it intersected before,
2149 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
2150 if(this.instance.isOver) {
2152 this.instance.isOver = 0;
2153 this.instance.cancelHelperRemoval = true;
2155 //Prevent reverting on this forced stop
2156 this.instance.options.revert = false;
2158 // The out event needs to be triggered independently
2159 this.instance._trigger('out', event, this.instance._uiHash(this.instance));
2161 this.instance._mouseStop(event, true);
2162 this.instance.options.helper = this.instance.options._helper;
2164 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
2165 this.instance.currentItem.remove();
2166 if(this.instance.placeholder) this.instance.placeholder.remove();
2168 inst._trigger("fromSortable", event);
2169 inst.dropped = false; //draggable revert needs that
2179 $.ui.plugin.add("draggable", "cursor", {
2180 start: function(event, ui) {
2181 var t = $('body'), o = $(this).data('draggable').options;
2182 if (t.css("cursor")) o._cursor = t.css("cursor");
2183 t.css("cursor", o.cursor);
2185 stop: function(event, ui) {
2186 var o = $(this).data('draggable').options;
2187 if (o._cursor) $('body').css("cursor", o._cursor);
2191 $.ui.plugin.add("draggable", "opacity", {
2192 start: function(event, ui) {
2193 var t = $(ui.helper), o = $(this).data('draggable').options;
2194 if(t.css("opacity")) o._opacity = t.css("opacity");
2195 t.css('opacity', o.opacity);
2197 stop: function(event, ui) {
2198 var o = $(this).data('draggable').options;
2199 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
2203 $.ui.plugin.add("draggable", "scroll", {
2204 start: function(event, ui) {
2205 var i = $(this).data("draggable");
2206 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
2208 drag: function(event, ui) {
2210 var i = $(this).data("draggable"), o = i.options, scrolled = false;
2212 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
2214 if(!o.axis || o.axis != 'x') {
2215 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
2216 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
2217 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
2218 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
2221 if(!o.axis || o.axis != 'y') {
2222 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
2223 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
2224 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
2225 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
2230 if(!o.axis || o.axis != 'x') {
2231 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
2232 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
2233 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
2234 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
2237 if(!o.axis || o.axis != 'y') {
2238 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
2239 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
2240 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
2241 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
2246 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
2247 $.ui.ddmanager.prepareOffsets(i, event);
2252 $.ui.plugin.add("draggable", "snap", {
2253 start: function(event, ui) {
2255 var i = $(this).data("draggable"), o = i.options;
2256 i.snapElements = [];
2258 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
2259 var $t = $(this); var $o = $t.offset();
2260 if(this != i.element[0]) i.snapElements.push({
2262 width: $t.outerWidth(), height: $t.outerHeight(),
2263 top: $o.top, left: $o.left
2268 drag: function(event, ui) {
2270 var inst = $(this).data("draggable"), o = inst.options;
2271 var d = o.snapTolerance;
2273 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
2274 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
2276 for (var i = inst.snapElements.length - 1; i >= 0; i--){
2278 var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
2279 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
2281 //Yes, I know, this is insane ;)
2282 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))) {
2283 if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2284 inst.snapElements[i].snapping = false;
2288 if(o.snapMode != 'inner') {
2289 var ts = Math.abs(t - y2) <= d;
2290 var bs = Math.abs(b - y1) <= d;
2291 var ls = Math.abs(l - x2) <= d;
2292 var rs = Math.abs(r - x1) <= d;
2293 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2294 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
2295 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
2296 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
2299 var first = (ts || bs || ls || rs);
2301 if(o.snapMode != 'outer') {
2302 var ts = Math.abs(t - y1) <= d;
2303 var bs = Math.abs(b - y2) <= d;
2304 var ls = Math.abs(l - x1) <= d;
2305 var rs = Math.abs(r - x2) <= d;
2306 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
2307 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
2308 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
2309 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
2312 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
2313 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2314 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
2321 $.ui.plugin.add("draggable", "stack", {
2322 start: function(event, ui) {
2324 var o = $(this).data("draggable").options;
2326 var group = $.makeArray($(o.stack)).sort(function(a,b) {
2327 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
2329 if (!group.length) { return; }
2331 var min = parseInt(group[0].style.zIndex) || 0;
2332 $(group).each(function(i) {
2333 this.style.zIndex = min + i;
2336 this[0].style.zIndex = min + group.length;
2341 $.ui.plugin.add("draggable", "zIndex", {
2342 start: function(event, ui) {
2343 var t = $(ui.helper), o = $(this).data("draggable").options;
2344 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
2345 t.css('zIndex', o.zIndex);
2347 stop: function(event, ui) {
2348 var o = $(this).data("draggable").options;
2349 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
2354 ;(jQuery.effects || (function($, undefined) {
2356 var backCompat = $.uiBackCompat !== false,
2357 // prefix used for storing data on .data()
2358 dataSpace = "ui-effects-";
2365 * jQuery Color Animations v2.0.0
2366 * http://jquery.com/
2368 * Copyright 2012 jQuery Foundation and other contributors
2369 * Released under the MIT license.
2370 * http://jquery.org/license
2372 * Date: Mon Aug 13 13:41:02 2012 -0500
2374 (function( jQuery, undefined ) {
2376 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor".split(" "),
2378 // plusequals test for += 100 -= 100
2379 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
2380 // a set of RE's that can match strings and generate color tuples.
2382 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
2383 parse: function( execResult ) {
2392 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
2393 parse: function( execResult ) {
2395 execResult[ 1 ] * 2.55,
2396 execResult[ 2 ] * 2.55,
2397 execResult[ 3 ] * 2.55,
2402 // this regex ignores A-F because it's compared against an already lowercased string
2403 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
2404 parse: function( execResult ) {
2406 parseInt( execResult[ 1 ], 16 ),
2407 parseInt( execResult[ 2 ], 16 ),
2408 parseInt( execResult[ 3 ], 16 )
2412 // this regex ignores A-F because it's compared against an already lowercased string
2413 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
2414 parse: function( execResult ) {
2416 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
2417 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
2418 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
2422 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
2424 parse: function( execResult ) {
2427 execResult[ 2 ] / 100,
2428 execResult[ 3 ] / 100,
2435 color = jQuery.Color = function( color, green, blue, alpha ) {
2436 return new jQuery.Color.fn.parse( color, green, blue, alpha );
2486 support = color.support = {},
2488 // element for support tests
2489 supportElem = jQuery( "<p>" )[ 0 ],
2491 // colors = jQuery.Color.names
2494 // local aliases of functions called often
2497 // determine rgba support immediately
2498 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
2499 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
2501 // define cache name and alpha properties
2502 // for rgba and hsla spaces
2503 each( spaces, function( spaceName, space ) {
2504 space.cache = "_" + spaceName;
2505 space.props.alpha = {
2512 function clamp( value, prop, allowEmpty ) {
2513 var type = propTypes[ prop.type ] || {};
2515 if ( value == null ) {
2516 return (allowEmpty || !prop.def) ? null : prop.def;
2519 // ~~ is an short way of doing floor for positive numbers
2520 value = type.floor ? ~~value : parseFloat( value );
2522 // IE will pass in empty strings as value for alpha,
2523 // which will hit this case
2524 if ( isNaN( value ) ) {
2529 // we add mod before modding to make sure that negatives values
2530 // get converted properly: -10 -> 350
2531 return (value + type.mod) % type.mod;
2534 // for now all property types without mod have min and max
2535 return 0 > value ? 0 : type.max < value ? type.max : value;
2538 function stringParse( string ) {
2540 rgba = inst._rgba = [];
2542 string = string.toLowerCase();
2544 each( stringParsers, function( i, parser ) {
2546 match = parser.re.exec( string ),
2547 values = match && parser.parse( match ),
2548 spaceName = parser.space || "rgba";
2551 parsed = inst[ spaceName ]( values );
2553 // if this was an rgba parse the assignment might happen twice
2555 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
2556 rgba = inst._rgba = parsed._rgba;
2558 // exit each( stringParsers ) here because we matched
2563 // Found a stringParser that handled it
2564 if ( rgba.length ) {
2566 // if this came from a parsed string, force "transparent" when alpha is 0
2567 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
2568 if ( rgba.join() === "0,0,0,0" ) {
2569 jQuery.extend( rgba, colors.transparent );
2575 return colors[ string ];
2578 color.fn = jQuery.extend( color.prototype, {
2579 parse: function( red, green, blue, alpha ) {
2580 if ( red === undefined ) {
2581 this._rgba = [ null, null, null, null ];
2584 if ( red.jquery || red.nodeType ) {
2585 red = jQuery( red ).css( green );
2590 type = jQuery.type( red ),
2591 rgba = this._rgba = [];
2593 // more than 1 argument specified - assume ( red, green, blue, alpha )
2594 if ( green !== undefined ) {
2595 red = [ red, green, blue, alpha ];
2599 if ( type === "string" ) {
2600 return this.parse( stringParse( red ) || colors._default );
2603 if ( type === "array" ) {
2604 each( spaces.rgba.props, function( key, prop ) {
2605 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
2610 if ( type === "object" ) {
2611 if ( red instanceof color ) {
2612 each( spaces, function( spaceName, space ) {
2613 if ( red[ space.cache ] ) {
2614 inst[ space.cache ] = red[ space.cache ].slice();
2618 each( spaces, function( spaceName, space ) {
2619 var cache = space.cache;
2620 each( space.props, function( key, prop ) {
2622 // if the cache doesn't exist, and we know how to convert
2623 if ( !inst[ cache ] && space.to ) {
2625 // if the value was null, we don't need to copy it
2626 // if the key was alpha, we don't need to copy it either
2627 if ( key === "alpha" || red[ key ] == null ) {
2630 inst[ cache ] = space.to( inst._rgba );
2633 // this is the only case where we allow nulls for ALL properties.
2634 // call clamp with alwaysAllowEmpty
2635 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
2638 // everything defined but alpha?
2639 if ( inst[ cache ] && $.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
2640 // use the default of 1
2641 inst[ cache ][ 3 ] = 1;
2643 inst._rgba = space.from( inst[ cache ] );
2651 is: function( compare ) {
2652 var is = color( compare ),
2656 each( spaces, function( _, space ) {
2658 isCache = is[ space.cache ];
2660 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
2661 each( space.props, function( _, prop ) {
2662 if ( isCache[ prop.idx ] != null ) {
2663 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
2672 _space: function() {
2675 each( spaces, function( spaceName, space ) {
2676 if ( inst[ space.cache ] ) {
2677 used.push( spaceName );
2682 transition: function( other, distance ) {
2683 var end = color( other ),
2684 spaceName = end._space(),
2685 space = spaces[ spaceName ],
2686 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
2687 start = startColor[ space.cache ] || space.to( startColor._rgba ),
2688 result = start.slice();
2690 end = end[ space.cache ];
2691 each( space.props, function( key, prop ) {
2692 var index = prop.idx,
2693 startValue = start[ index ],
2694 endValue = end[ index ],
2695 type = propTypes[ prop.type ] || {};
2697 // if null, don't override start value
2698 if ( endValue === null ) {
2701 // if null - use end
2702 if ( startValue === null ) {
2703 result[ index ] = endValue;
2706 if ( endValue - startValue > type.mod / 2 ) {
2707 startValue += type.mod;
2708 } else if ( startValue - endValue > type.mod / 2 ) {
2709 startValue -= type.mod;
2712 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
2715 return this[ spaceName ]( result );
2717 blend: function( opaque ) {
2718 // if we are already opaque - return ourself
2719 if ( this._rgba[ 3 ] === 1 ) {
2723 var rgb = this._rgba.slice(),
2725 blend = color( opaque )._rgba;
2727 return color( jQuery.map( rgb, function( v, i ) {
2728 return ( 1 - a ) * blend[ i ] + a * v;
2731 toRgbaString: function() {
2732 var prefix = "rgba(",
2733 rgba = jQuery.map( this._rgba, function( v, i ) {
2734 return v == null ? ( i > 2 ? 1 : 0 ) : v;
2737 if ( rgba[ 3 ] === 1 ) {
2742 return prefix + rgba.join() + ")";
2744 toHslaString: function() {
2745 var prefix = "hsla(",
2746 hsla = jQuery.map( this.hsla(), function( v, i ) {
2753 v = Math.round( v * 100 ) + "%";
2758 if ( hsla[ 3 ] === 1 ) {
2762 return prefix + hsla.join() + ")";
2764 toHexString: function( includeAlpha ) {
2765 var rgba = this._rgba.slice(),
2768 if ( includeAlpha ) {
2769 rgba.push( ~~( alpha * 255 ) );
2772 return "#" + jQuery.map( rgba, function( v ) {
2774 // default to 0 when nulls exist
2775 v = ( v || 0 ).toString( 16 );
2776 return v.length === 1 ? "0" + v : v;
2779 toString: function() {
2780 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
2783 color.fn.parse.prototype = color.fn;
2785 // hsla conversions adapted from:
2786 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
2788 function hue2rgb( p, q, h ) {
2791 return p + (q - p) * h * 6;
2797 return p + (q - p) * ((2/3) - h) * 6;
2802 spaces.hsla.to = function ( rgba ) {
2803 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
2804 return [ null, null, null, rgba[ 3 ] ];
2806 var r = rgba[ 0 ] / 255,
2807 g = rgba[ 1 ] / 255,
2808 b = rgba[ 2 ] / 255,
2810 max = Math.max( r, g, b ),
2811 min = Math.min( r, g, b ),
2817 if ( min === max ) {
2819 } else if ( r === max ) {
2820 h = ( 60 * ( g - b ) / diff ) + 360;
2821 } else if ( g === max ) {
2822 h = ( 60 * ( b - r ) / diff ) + 120;
2824 h = ( 60 * ( r - g ) / diff ) + 240;
2827 if ( l === 0 || l === 1 ) {
2829 } else if ( l <= 0.5 ) {
2832 s = diff / ( 2 - add );
2834 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
2837 spaces.hsla.from = function ( hsla ) {
2838 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
2839 return [ null, null, null, hsla[ 3 ] ];
2841 var h = hsla[ 0 ] / 360,
2845 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
2849 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
2850 Math.round( hue2rgb( p, q, h ) * 255 ),
2851 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
2857 each( spaces, function( spaceName, space ) {
2858 var props = space.props,
2859 cache = space.cache,
2863 // makes rgba() and hsla()
2864 color.fn[ spaceName ] = function( value ) {
2866 // generate a cache for this space if it doesn't exist
2867 if ( to && !this[ cache ] ) {
2868 this[ cache ] = to( this._rgba );
2870 if ( value === undefined ) {
2871 return this[ cache ].slice();
2875 type = jQuery.type( value ),
2876 arr = ( type === "array" || type === "object" ) ? value : arguments,
2877 local = this[ cache ].slice();
2879 each( props, function( key, prop ) {
2880 var val = arr[ type === "object" ? key : prop.idx ];
2881 if ( val == null ) {
2882 val = local[ prop.idx ];
2884 local[ prop.idx ] = clamp( val, prop );
2888 ret = color( from( local ) );
2889 ret[ cache ] = local;
2892 return color( local );
2896 // makes red() green() blue() alpha() hue() saturation() lightness()
2897 each( props, function( key, prop ) {
2898 // alpha is included in more than one space
2899 if ( color.fn[ key ] ) {
2902 color.fn[ key ] = function( value ) {
2903 var vtype = jQuery.type( value ),
2904 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
2905 local = this[ fn ](),
2906 cur = local[ prop.idx ],
2909 if ( vtype === "undefined" ) {
2913 if ( vtype === "function" ) {
2914 value = value.call( this, cur );
2915 vtype = jQuery.type( value );
2917 if ( value == null && prop.empty ) {
2920 if ( vtype === "string" ) {
2921 match = rplusequals.exec( value );
2923 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
2926 local[ prop.idx ] = value;
2927 return this[ fn ]( local );
2932 // add .fx.step functions
2933 each( stepHooks, function( i, hook ) {
2934 jQuery.cssHooks[ hook ] = {
2935 set: function( elem, value ) {
2936 var parsed, curElem,
2937 backgroundColor = "";
2939 if ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) {
2940 value = color( parsed || value );
2941 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
2942 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
2944 (backgroundColor === "" || backgroundColor === "transparent") &&
2945 curElem && curElem.style
2948 backgroundColor = jQuery.css( curElem, "backgroundColor" );
2949 curElem = curElem.parentNode;
2954 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
2959 value = value.toRgbaString();
2962 elem.style[ hook ] = value;
2964 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
2968 jQuery.fx.step[ hook ] = function( fx ) {
2969 if ( !fx.colorInit ) {
2970 fx.start = color( fx.elem, hook );
2971 fx.end = color( fx.end );
2972 fx.colorInit = true;
2974 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
2978 jQuery.cssHooks.borderColor = {
2979 expand: function( value ) {
2982 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
2983 expanded[ "border" + part + "Color" ] = value;
2989 // Basic color names only.
2990 // Usage of any of the other color names requires adding yourself or including
2991 // jquery.color.svg-names.js.
2992 colors = jQuery.Color.names = {
2993 // 4.1. Basic color keywords
3011 // 4.2.3. "transparent" color keyword
3012 transparent: [ null, null, null, 0 ],
3021 /******************************************************************************/
3022 /****************************** CLASS ANIMATIONS ******************************/
3023 /******************************************************************************/
3026 var classAnimationActions = [ "add", "remove", "toggle" ],
3039 $.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
3040 $.fx.step[ prop ] = function( fx ) {
3041 if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
3042 jQuery.style( fx.elem, prop, fx.end );
3048 function getElementStyles() {
3049 var style = this.ownerDocument.defaultView ?
3050 this.ownerDocument.defaultView.getComputedStyle( this, null ) :
3056 // webkit enumerates style porperties
3057 if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
3061 if ( typeof style[ key ] === "string" ) {
3062 newStyle[ $.camelCase( key ) ] = style[ key ];
3066 for ( key in style ) {
3067 if ( typeof style[ key ] === "string" ) {
3068 newStyle[ key ] = style[ key ];
3077 function styleDifference( oldStyle, newStyle ) {
3081 for ( name in newStyle ) {
3082 value = newStyle[ name ];
3083 if ( oldStyle[ name ] !== value ) {
3084 if ( !shorthandStyles[ name ] ) {
3085 if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
3086 diff[ name ] = value;
3095 $.effects.animateClass = function( value, duration, easing, callback ) {
3096 var o = $.speed( duration, easing, callback );
3098 return this.queue( function() {
3099 var animated = $( this ),
3100 baseClass = animated.attr( "class" ) || "",
3102 allAnimations = o.children ? animated.find( "*" ).andSelf() : animated;
3104 // map the animated objects to store the original styles.
3105 allAnimations = allAnimations.map(function() {
3109 start: getElementStyles.call( this )
3113 // apply class change
3114 applyClassChange = function() {
3115 $.each( classAnimationActions, function(i, action) {
3116 if ( value[ action ] ) {
3117 animated[ action + "Class" ]( value[ action ] );
3123 // map all animated objects again - calculate new styles and diff
3124 allAnimations = allAnimations.map(function() {
3125 this.end = getElementStyles.call( this.el[ 0 ] );
3126 this.diff = styleDifference( this.start, this.end );
3130 // apply original class
3131 animated.attr( "class", baseClass );
3133 // map all animated objects again - this time collecting a promise
3134 allAnimations = allAnimations.map(function() {
3135 var styleInfo = this,
3137 opts = jQuery.extend({}, o, {
3139 complete: function() {
3140 dfd.resolve( styleInfo );
3144 this.el.animate( this.diff, opts );
3145 return dfd.promise();
3148 // once all animations have completed:
3149 $.when.apply( $, allAnimations.get() ).done(function() {
3151 // set the final class
3154 // for each animated element,
3155 // clear all css properties that were animated
3156 $.each( arguments, function() {
3158 $.each( this.diff, function(key) {
3163 // this is guarnteed to be there if you use jQuery.speed()
3164 // it also handles dequeuing the next anim...
3165 o.complete.call( animated[ 0 ] );
3171 _addClass: $.fn.addClass,
3172 addClass: function( classNames, speed, easing, callback ) {
3174 $.effects.animateClass.call( this,
3175 { add: classNames }, speed, easing, callback ) :
3176 this._addClass( classNames );
3179 _removeClass: $.fn.removeClass,
3180 removeClass: function( classNames, speed, easing, callback ) {
3182 $.effects.animateClass.call( this,
3183 { remove: classNames }, speed, easing, callback ) :
3184 this._removeClass( classNames );
3187 _toggleClass: $.fn.toggleClass,
3188 toggleClass: function( classNames, force, speed, easing, callback ) {
3189 if ( typeof force === "boolean" || force === undefined ) {
3191 // without speed parameter
3192 return this._toggleClass( classNames, force );
3194 return $.effects.animateClass.call( this,
3195 (force ? { add: classNames } : { remove: classNames }),
3196 speed, easing, callback );
3199 // without force parameter
3200 return $.effects.animateClass.call( this,
3201 { toggle: classNames }, force, speed, easing );
3205 switchClass: function( remove, add, speed, easing, callback) {
3206 return $.effects.animateClass.call( this, {
3209 }, speed, easing, callback );
3215 /******************************************************************************/
3216 /*********************************** EFFECTS **********************************/
3217 /******************************************************************************/
3221 $.extend( $.effects, {
3224 // Saves a set of properties in a data storage
3225 save: function( element, set ) {
3226 for( var i=0; i < set.length; i++ ) {
3227 if ( set[ i ] !== null ) {
3228 element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
3233 // Restores a set of previously saved properties from a data storage
3234 restore: function( element, set ) {
3236 for( i=0; i < set.length; i++ ) {
3237 if ( set[ i ] !== null ) {
3238 val = element.data( dataSpace + set[ i ] );
3239 // support: jQuery 1.6.2
3240 // http://bugs.jquery.com/ticket/9917
3241 // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
3242 // We can't differentiate between "" and 0 here, so we just assume
3243 // empty string since it's likely to be a more common value...
3244 if ( val === undefined ) {
3247 element.css( set[ i ], val );
3252 setMode: function( el, mode ) {
3253 if (mode === "toggle") {
3254 mode = el.is( ":hidden" ) ? "show" : "hide";
3259 // Translates a [top,left] array into a baseline value
3260 // this should be a little more flexible in the future to handle a string & hash
3261 getBaseline: function( origin, original ) {
3263 switch ( origin[ 0 ] ) {
3264 case "top": y = 0; break;
3265 case "middle": y = 0.5; break;
3266 case "bottom": y = 1; break;
3267 default: y = origin[ 0 ] / original.height;
3269 switch ( origin[ 1 ] ) {
3270 case "left": x = 0; break;
3271 case "center": x = 0.5; break;
3272 case "right": x = 1; break;
3273 default: x = origin[ 1 ] / original.width;
3281 // Wraps the element around a wrapper that copies position properties
3282 createWrapper: function( element ) {
3284 // if the element is already wrapped, return it
3285 if ( element.parent().is( ".ui-effects-wrapper" )) {
3286 return element.parent();
3291 width: element.outerWidth(true),
3292 height: element.outerHeight(true),
3293 "float": element.css( "float" )
3295 wrapper = $( "<div></div>" )
3296 .addClass( "ui-effects-wrapper" )
3299 background: "transparent",
3304 // Store the size in case width/height are defined in % - Fixes #5245
3306 width: element.width(),
3307 height: element.height()
3309 active = document.activeElement;
3312 // Firefox incorrectly exposes anonymous content
3313 // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
3317 active = document.body;
3320 element.wrap( wrapper );
3322 // Fixes #7595 - Elements lose focus when wrapped.
3323 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
3324 $( active ).focus();
3327 wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
3329 // transfer positioning properties to the wrapper
3330 if ( element.css( "position" ) === "static" ) {
3331 wrapper.css({ position: "relative" });
3332 element.css({ position: "relative" });
3335 position: element.css( "position" ),
3336 zIndex: element.css( "z-index" )
3338 $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
3339 props[ pos ] = element.css( pos );
3340 if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
3341 props[ pos ] = "auto";
3345 position: "relative",
3354 return wrapper.css( props ).show();
3357 removeWrapper: function( element ) {
3358 var active = document.activeElement;
3360 if ( element.parent().is( ".ui-effects-wrapper" ) ) {
3361 element.parent().replaceWith( element );
3363 // Fixes #7595 - Elements lose focus when wrapped.
3364 if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
3365 $( active ).focus();
3373 setTransition: function( element, list, factor, value ) {
3374 value = value || {};
3375 $.each( list, function( i, x ) {
3376 var unit = element.cssUnit( x );
3377 if ( unit[ 0 ] > 0 ) {
3378 value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
3385 // return an effect options object for the given parameters:
3386 function _normalizeArguments( effect, options, speed, callback ) {
3388 // allow passing all options as the first parameter
3389 if ( $.isPlainObject( effect ) ) {
3391 effect = effect.effect;
3394 // convert to an object
3395 effect = { effect: effect };
3397 // catch (effect, null, ...)
3398 if ( options == null ) {
3402 // catch (effect, callback)
3403 if ( $.isFunction( options ) ) {
3409 // catch (effect, speed, ?)
3410 if ( typeof options === "number" || $.fx.speeds[ options ] ) {
3416 // catch (effect, options, callback)
3417 if ( $.isFunction( speed ) ) {
3422 // add options to effect
3424 $.extend( effect, options );
3427 speed = speed || options.duration;
3428 effect.duration = $.fx.off ? 0 :
3429 typeof speed === "number" ? speed :
3430 speed in $.fx.speeds ? $.fx.speeds[ speed ] :
3431 $.fx.speeds._default;
3433 effect.complete = callback || options.complete;
3438 function standardSpeed( speed ) {
3439 // valid standard speeds
3440 if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
3444 // invalid strings - treat as "normal" speed
3445 if ( typeof speed === "string" && !$.effects.effect[ speed ] ) {
3446 // TODO: remove in 2.0 (#7115)
3447 if ( backCompat && $.effects[ speed ] ) {
3457 effect: function( /* effect, options, speed, callback */ ) {
3458 var args = _normalizeArguments.apply( this, arguments ),
3461 effectMethod = $.effects.effect[ args.effect ],
3463 // DEPRECATED: remove in 2.0 (#7115)
3464 oldEffectMethod = !effectMethod && backCompat && $.effects[ args.effect ];
3466 if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) {
3467 // delegate to the original method (e.g., .show()) if possible
3469 return this[ mode ]( args.duration, args.complete );
3471 return this.each( function() {
3472 if ( args.complete ) {
3473 args.complete.call( this );
3479 function run( next ) {
3480 var elem = $( this ),
3481 complete = args.complete,
3485 if ( $.isFunction( complete ) ) {
3486 complete.call( elem[0] );
3488 if ( $.isFunction( next ) ) {
3493 // if the element is hiddden and mode is hide,
3494 // or element is visible and mode is show
3495 if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
3498 effectMethod.call( elem[0], args, done );
3502 // TODO: remove this check in 2.0, effectMethod will always be true
3503 if ( effectMethod ) {
3504 return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
3506 // DEPRECATED: remove in 2.0 (#7115)
3507 return oldEffectMethod.call(this, {
3509 duration: args.duration,
3510 callback: args.complete,
3517 show: function( speed ) {
3518 if ( standardSpeed( speed ) ) {
3519 return this._show.apply( this, arguments );
3521 var args = _normalizeArguments.apply( this, arguments );
3523 return this.effect.call( this, args );
3528 hide: function( speed ) {
3529 if ( standardSpeed( speed ) ) {
3530 return this._hide.apply( this, arguments );
3532 var args = _normalizeArguments.apply( this, arguments );
3534 return this.effect.call( this, args );
3538 // jQuery core overloads toggle and creates _toggle
3539 __toggle: $.fn.toggle,
3540 toggle: function( speed ) {
3541 if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
3542 return this.__toggle.apply( this, arguments );
3544 var args = _normalizeArguments.apply( this, arguments );
3545 args.mode = "toggle";
3546 return this.effect.call( this, args );
3551 cssUnit: function(key) {
3552 var style = this.css( key ),
3555 $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
3556 if ( style.indexOf( unit ) > 0 ) {
3557 val = [ parseFloat( style ), unit ];
3566 /******************************************************************************/
3567 /*********************************** EASING ***********************************/
3568 /******************************************************************************/
3572 // based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
3574 var baseEasings = {};
3576 $.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
3577 baseEasings[ name ] = function( p ) {
3578 return Math.pow( p, i + 2 );
3582 $.extend( baseEasings, {
3583 Sine: function ( p ) {
3584 return 1 - Math.cos( p * Math.PI / 2 );
3586 Circ: function ( p ) {
3587 return 1 - Math.sqrt( 1 - p * p );
3589 Elastic: function( p ) {
3590 return p === 0 || p === 1 ? p :
3591 -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
3593 Back: function( p ) {
3594 return p * p * ( 3 * p - 2 );
3596 Bounce: function ( p ) {
3600 while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
3601 return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
3605 $.each( baseEasings, function( name, easeIn ) {
3606 $.easing[ "easeIn" + name ] = easeIn;
3607 $.easing[ "easeOut" + name ] = function( p ) {
3608 return 1 - easeIn( 1 - p );
3610 $.easing[ "easeInOut" + name ] = function( p ) {
3612 easeIn( p * 2 ) / 2 :
3613 1 - easeIn( p * -2 + 2 ) / 2;