jquery core: closes #3541. Added isArray.
[jquery.git] / src / fx.js
index 4ccb089..cbf067f 100644 (file)
--- a/src/fx.js
+++ b/src/fx.js
@@ -4,34 +4,38 @@ jQuery.fn.extend({
                        this.animate({
                                height: "show", width: "show", opacity: "show"
                        }, speed, callback) :
-                       
+
                        this.filter(":hidden").each(function(){
-                               this.style.display = this.oldblock ? this.oldblock : "";
-                               if ( jQuery.css(this,"display") == "none" )
-                                       this.style.display = "block";
+                               this.style.display = this.oldblock || "";
+                               if ( jQuery.css(this,"display") == "none" ) {
+                                       var elem = jQuery("<" + this.tagName + " />").appendTo("body");
+                                       this.style.display = elem.css("display");
+                                       // handle an edge condition where css is - div { display:none; } or similar
+                                       if (this.style.display == "none")
+                                               this.style.display = "block";
+                                       elem.remove();
+                               }
                        }).end();
        },
-       
+
        hide: function(speed,callback){
                return speed ?
                        this.animate({
                                height: "hide", width: "hide", opacity: "hide"
                        }, speed, callback) :
-                       
+
                        this.filter(":visible").each(function(){
                                this.oldblock = this.oldblock || jQuery.css(this,"display");
-                               if ( this.oldblock == "none" )
-                                       this.oldblock = "block";
                                this.style.display = "none";
                        }).end();
        },
 
        // Save the old toggle function
        _toggle: jQuery.fn.toggle,
-       
+
        toggle: function( fn, fn2 ){
                return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
-                       this._toggle( fn, fn2 ) :
+                       this._toggle.apply( this, arguments ) :
                        fn ?
                                this.animate({
                                        height: "toggle", width: "toggle", opacity: "toggle"
@@ -40,43 +44,25 @@ jQuery.fn.extend({
                                        jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
                                });
        },
-       
-       slideDown: function(speed,callback){
-               return this.animate({height: "show"}, speed, callback);
-       },
-       
-       slideUp: function(speed,callback){
-               return this.animate({height: "hide"}, speed, callback);
-       },
 
-       slideToggle: function(speed, callback){
-               return this.animate({height: "toggle"}, speed, callback);
-       },
-       
-       fadeIn: function(speed, callback){
-               return this.animate({opacity: "show"}, speed, callback);
-       },
-       
-       fadeOut: function(speed, callback){
-               return this.animate({opacity: "hide"}, speed, callback);
-       },
-       
        fadeTo: function(speed,to,callback){
                return this.animate({opacity: to}, speed, callback);
        },
-       
+
        animate: function( prop, speed, easing, callback ) {
                var optall = jQuery.speed(speed, easing, callback);
 
                return this[ optall.queue === false ? "each" : "queue" ](function(){
-                       var opt = jQuery.extend({}, optall);
-                       var hidden = jQuery(this).is(":hidden"), self = this;
-                       
-                       for ( var p in prop ) {
+               
+                       var opt = jQuery.extend({}, optall), p,
+                               hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
+                               self = this;
+       
+                       for ( p in prop ) {
                                if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
-                                       return jQuery.isFunction(opt.complete) && opt.complete.apply(this);
+                                       return opt.complete.call(this);
 
-                               if ( p == "height" || p == "width" ) {
+                               if ( ( p == "height" || p == "width" ) && this.style ) {
                                        // Store display property
                                        opt.display = jQuery.css(this, "display");
 
@@ -89,7 +75,7 @@ jQuery.fn.extend({
                                this.style.overflow = "hidden";
 
                        opt.curAnim = jQuery.extend({}, prop);
-                       
+
                        jQuery.each( prop, function(name, val){
                                var e = new jQuery.fx( self, opt, name );
 
@@ -124,9 +110,9 @@ jQuery.fn.extend({
                        return true;
                });
        },
-       
+
        queue: function(type, fn){
-               if ( jQuery.isFunction(type) ) {
+               if ( jQuery.isFunction(type) || jQuery.isArray(type) ) {
                        fn = type;
                        type = "fx";
                }
@@ -135,39 +121,67 @@ jQuery.fn.extend({
                        return queue( this[0], type );
 
                return this.each(function(){
-                       if ( fn.constructor == Array )
+                       if ( jQuery.isArray(fn) )
                                queue(this, type, fn);
                        else {
                                queue(this, type).push( fn );
-                       
+
                                if ( queue(this, type).length == 1 )
-                                       fn.apply(this);
+                                       fn.call(this);
                        }
                });
        },
 
-       stop: function(){
+       stop: function(clearQueue, gotoEnd){
                var timers = jQuery.timers;
 
-               return this.each(function(){
-                       for ( var i = 0; i < timers.length; i++ )
-                               if ( timers[i].elem == this )
-                                       timers.splice(i--, 1);
-               }).dequeue();
+               if (clearQueue)
+                       this.queue([]);
+
+               this.each(function(){
+                       // go in reverse order so anything added to the queue during the loop is ignored
+                       for ( var i = timers.length - 1; i >= 0; i-- )
+                               if ( timers[i].elem == this ) {
+                                       if (gotoEnd)
+                                               // force the next step to be the last
+                                               timers[i](true);
+                                       timers.splice(i, 1);
+                               }
+               });
+
+               // start the next in the queue if the last step wasn't forced
+               if (!gotoEnd)
+                       this.dequeue();
+
+               return this;
        }
 
 });
 
+// Generate shortcuts for custom animations
+jQuery.each({
+       slideDown: { height:"show" },
+       slideUp: { height: "hide" },
+       slideToggle: { height: "toggle" },
+       fadeIn: { opacity: "show" },
+       fadeOut: { opacity: "hide" }
+}, function( name, props ){
+       jQuery.fn[ name ] = function( speed, callback ){
+               return this.animate( props, speed, callback );
+       };
+});
+
 var queue = function( elem, type, array ) {
-       if ( !elem )
-               return;
+       if ( elem ){
 
-       var q = jQuery.data( elem, type + "queue" );
+               type = type || "fx";
 
-       if ( !q || array )
-               q = jQuery.data( elem, type + "queue", 
-                       array ? jQuery.makeArray(array) : [] );
+               var q = jQuery.data( elem, type + "queue" );
 
+               if ( !q || array )
+                       q = jQuery.data( elem, type + "queue", jQuery.makeArray(array) );
+
+       }
        return q;
 };
 
@@ -180,36 +194,35 @@ jQuery.fn.dequeue = function(type){
                q.shift();
 
                if ( q.length )
-                       q[0].apply( this );
+                       q[0].call( this );
        });
 };
 
 jQuery.extend({
-       
+
        speed: function(speed, easing, fn) {
                var opt = speed && speed.constructor == Object ? speed : {
-                       complete: fn || !fn && easing || 
+                       complete: fn || !fn && easing ||
                                jQuery.isFunction( speed ) && speed,
                        duration: speed,
                        easing: fn && easing || easing && easing.constructor != Function && easing
                };
 
-               opt.duration = (opt.duration && opt.duration.constructor == Number ? 
-                       opt.duration : 
-                       { slow: 600, fast: 200 }[opt.duration]) || 400;
-       
+               opt.duration = jQuery.fx.off ? 0 : typeof opt.duration == 'number' ? opt.duration :
+                       jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
                // Queueing
                opt.old = opt.complete;
                opt.complete = function(){
                        if ( opt.queue !== false )
                                jQuery(this).dequeue();
                        if ( jQuery.isFunction( opt.old ) )
-                               opt.old.apply( this );
+                               opt.old.call( this );
                };
-       
+
                return opt;
        },
-       
+
        easing: {
                linear: function( p, n, firstNum, diff ) {
                        return firstNum + diff * p;
@@ -218,8 +231,9 @@ jQuery.extend({
                        return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
                }
        },
-       
+
        timers: [],
+       timerId: null,
 
        fx: function( elem, options, prop ){
                this.options = options;
@@ -237,53 +251,54 @@ jQuery.fx.prototype = {
        // Simple function for setting a style value
        update: function(){
                if ( this.options.step )
-                       this.options.step.apply( this.elem, [ this.now, this ] );
+                       this.options.step.call( this.elem, this.now, this );
 
                (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
 
                // Set display property to block for height/width animations
-               if ( this.prop == "height" || this.prop == "width" )
+               if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
                        this.elem.style.display = "block";
        },
 
        // Get the current size
        cur: function(force){
-               if ( this.elem[this.prop] != null && this.elem.style[this.prop] == null )
+               if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
                        return this.elem[ this.prop ];
 
-               var r = parseFloat(jQuery.curCSS(this.elem, this.prop, force));
-               return r && r > -10000 ? r : parseFloat(jQuery.css(this.elem, this.prop)) || 0;
+               var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+               return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
        },
 
        // Start an animation from one number to another
        custom: function(from, to, unit){
-               this.startTime = (new Date()).getTime();
+               this.startTime = now();
                this.start = from;
                this.end = to;
                this.unit = unit || this.unit || "px";
                this.now = this.start;
                this.pos = this.state = 0;
-               this.update();
 
                var self = this;
-               function t(){
-                       return self.step();
+               function t(gotoEnd){
+                       return self.step(gotoEnd);
                }
 
                t.elem = this.elem;
 
                jQuery.timers.push(t);
 
-               if ( jQuery.timers.length == 1 ) {
-                       var timer = setInterval(function(){
+               if ( t() && jQuery.timerId == null ) {
+                       jQuery.timerId = setInterval(function(){
                                var timers = jQuery.timers;
-                               
+
                                for ( var i = 0; i < timers.length; i++ )
                                        if ( !timers[i]() )
                                                timers.splice(i--, 1);
 
-                               if ( !timers.length )
-                                       clearInterval( timer );
+                               if ( !timers.length ) {
+                                       clearInterval( jQuery.timerId );
+                                       jQuery.timerId = null;
+                               }
                        }, 13);
                }
        },
@@ -301,7 +316,7 @@ jQuery.fx.prototype = {
                // flash of content
                if ( this.prop == "width" || this.prop == "height" )
                        this.elem.style[this.prop] = "1px";
-               
+
                // Start by showing the element
                jQuery(this.elem).show();
        },
@@ -317,10 +332,10 @@ jQuery.fx.prototype = {
        },
 
        // Each step of an animation
-       step: function(){
-               var t = (new Date()).getTime();
+       step: function(gotoEnd){
+               var t = now();
 
-               if ( t > this.options.duration + this.startTime ) {
+               if ( gotoEnd || t >= this.options.duration + this.startTime ) {
                        this.now = this.end;
                        this.pos = this.state = 1;
                        this.update();
@@ -336,7 +351,7 @@ jQuery.fx.prototype = {
                                if ( this.options.display != null ) {
                                        // Reset the overflow
                                        this.elem.style.overflow = this.options.overflow;
-                               
+
                                        // Reset the display
                                        this.elem.style.display = this.options.display;
                                        if ( jQuery.css(this.elem, "display") == "none" )
@@ -353,10 +368,9 @@ jQuery.fx.prototype = {
                                                jQuery.attr(this.elem.style, p, this.options.orig[p]);
                        }
 
-                       // If a callback was provided, execute it
-                       if ( done && jQuery.isFunction( this.options.complete ) )
+                       if ( done )
                                // Execute the complete function
-                               this.options.complete.apply( this.elem );
+                               this.options.complete.call( this.elem );
 
                        return false;
                } else {
@@ -376,20 +390,24 @@ jQuery.fx.prototype = {
 
 };
 
-jQuery.fx.step = {
-       scrollLeft: function(fx){
-               fx.elem.scrollLeft = fx.now;
+jQuery.extend( jQuery.fx, {
+       speeds:{
+               slow: 600,
+               fast: 200,
+               // Default speed
+               _default: 400
        },
+       step: {
 
-       scrollTop: function(fx){
-               fx.elem.scrollTop = fx.now;
-       },
-
-       opacity: function(fx){
-               jQuery.attr(fx.elem.style, "opacity", fx.now);
-       },
+               opacity: function(fx){
+                       jQuery.attr(fx.elem.style, "opacity", fx.now);
+               },
 
-       _default: function(fx){
-               fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+               _default: function(fx){
+                       if( fx.prop in fx.elem ) 
+                               fx.elem[ fx.prop ] = fx.now;
+                       else if( fx.elem.style )
+                               fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+               }
        }
-};
+});