More jQuery.speed() fixes.
[jquery.git] / src / fx / fx.js
1 jQuery.fn.extend({
2
3         /**
4          * Displays each of the set of matched elements if they are hidden.
5          *
6          * @example $("p").show()
7          * @before <p style="display: none">Hello</p>
8          * @result [ <p style="display: block">Hello</p> ]
9          *
10          * @name show
11          * @type jQuery
12          * @cat Effects
13          */
14         
15         /**
16          * Show all matched elements using a graceful animation and firing an
17          * optional callback after completion.
18          *
19          * The height, width, and opacity of each of the matched elements 
20          * are changed dynamically according to the specified speed.
21          *
22          * @example $("p").show("slow");
23          *
24          * @example $("p").show("slow",function(){
25          *   alert("Animation Done.");
26          * });
27          *
28          * @name show
29          * @type jQuery
30          * @param String|Number speed A string representing one of the three predefined speeds ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
31          * @param Function callback (optional) A function to be executed whenever the animation completes.
32          * @cat Effects
33          * @see hide(String|Number,Function)
34          */
35         show: function(speed,callback){
36                 var hidden = this.filter(":hidden");
37                 speed ?
38                         hidden.animate({
39                                 height: "show", width: "show", opacity: "show"
40                         }, speed, callback) :
41                         
42                         hidden.each(function(){
43                                 this.style.display = this.oldblock ? this.oldblock : "";
44                                 if ( jQuery.css(this,"display") == "none" )
45                                         this.style.display = "block";
46                         });
47                 return this;
48         },
49         
50         /**
51          * Hides each of the set of matched elements if they are shown.
52          *
53          * @example $("p").hide()
54          * @before <p>Hello</p>
55          * @result [ <p style="display: none">Hello</p> ]
56          *
57          * @name hide
58          * @type jQuery
59          * @cat Effects
60          */
61         
62         /**
63          * Hide all matched elements using a graceful animation and firing an
64          * optional callback after completion.
65          *
66          * The height, width, and opacity of each of the matched elements 
67          * are changed dynamically according to the specified speed.
68          *
69          * @example $("p").hide("slow");
70          *
71          * @example $("p").hide("slow",function(){
72          *   alert("Animation Done.");
73          * });
74          *
75          * @name hide
76          * @type jQuery
77          * @param String|Number speed A string representing one of the three predefined speeds ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
78          * @param Function callback (optional) A function to be executed whenever the animation completes.
79          * @cat Effects
80          * @see show(String|Number,Function)
81          */
82         hide: function(speed,callback){
83                 var visible = this.filter(":visible");
84                 speed ?
85                         visible.animate({
86                                 height: "hide", width: "hide", opacity: "hide"
87                         }, speed, callback) :
88                         
89                         visible.each(function(){
90                                 this.oldblock = this.oldblock || jQuery.css(this,"display");
91                                 if ( this.oldblock == "none" )
92                                         this.oldblock = "block";
93                                 this.style.display = "none";
94                         });
95                 return this;
96         },
97
98         // Save the old toggle function
99         _toggle: jQuery.fn.toggle,
100         
101         /**
102          * Toggles each of the set of matched elements. If they are shown,
103          * toggle makes them hidden. If they are hidden, toggle
104          * makes them shown.
105          *
106          * @example $("p").toggle()
107          * @before <p>Hello</p><p style="display: none">Hello Again</p>
108          * @result [ <p style="display: none">Hello</p>, <p style="display: block">Hello Again</p> ]
109          *
110          * @name toggle
111          * @type jQuery
112          * @cat Effects
113          */
114         toggle: function( fn, fn2 ){
115                 return fn ?
116                         this._toggle( fn, fn2 ) :
117                         this.each(function(){
118                                 jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]
119                                         .apply( jQuery(this), arguments );
120                         });
121         },
122         
123         /**
124          * Reveal all matched elements by adjusting their height and firing an
125          * optional callback after completion.
126          *
127          * Only the height is adjusted for this animation, causing all matched
128          * elements to be revealed in a "sliding" manner.
129          *
130          * @example $("p").slideDown("slow");
131          *
132          * @example $("p").slideDown("slow",function(){
133          *   alert("Animation Done.");
134          * });
135          *
136          * @name slideDown
137          * @type jQuery
138          * @param String|Number speed (optional) A string representing one of the three predefined speeds ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
139          * @param Function callback (optional) A function to be executed whenever the animation completes.
140          * @cat Effects
141          * @see slideUp(String|Number,Function)
142          * @see slideToggle(String|Number,Function)
143          */
144         slideDown: function(speed,callback){
145                 return this.animate({height: "show"}, speed, callback);
146         },
147         
148         /**
149          * Hide all matched elements by adjusting their height and firing an
150          * optional callback after completion.
151          *
152          * Only the height is adjusted for this animation, causing all matched
153          * elements to be hidden in a "sliding" manner.
154          *
155          * @example $("p").slideUp("slow");
156          *
157          * @example $("p").slideUp("slow",function(){
158          *   alert("Animation Done.");
159          * });
160          *
161          * @name slideUp
162          * @type jQuery
163          * @param String|Number speed (optional) A string representing one of the three predefined speeds ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
164          * @param Function callback (optional) A function to be executed whenever the animation completes.
165          * @cat Effects
166          * @see slideDown(String|Number,Function)
167          * @see slideToggle(String|Number,Function)
168          */
169         slideUp: function(speed,callback){
170                 return this.animate({height: "hide"}, speed, callback);
171         },
172
173         /**
174          * Toggle the visibility of all matched elements by adjusting their height and firing an
175          * optional callback after completion.
176          *
177          * Only the height is adjusted for this animation, causing all matched
178          * elements to be hidden in a "sliding" manner.
179          *
180          * @example $("p").slideToggle("slow");
181          *
182          * @example $("p").slideToggle("slow",function(){
183          *   alert("Animation Done.");
184          * });
185          *
186          * @name slideToggle
187          * @type jQuery
188          * @param String|Number speed (optional) A string representing one of the three predefined speeds ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
189          * @param Function callback (optional) A function to be executed whenever the animation completes.
190          * @cat Effects
191          * @see slideDown(String|Number,Function)
192          * @see slideUp(String|Number,Function)
193          */
194         slideToggle: function(speed, callback){
195                 return this.each(function(){
196                         var state = jQuery(this).is(":hidden") ? "show" : "hide";
197                         jQuery(this).animate({height: state}, speed, callback);
198                 });
199         },
200         
201         /**
202          * Fade in all matched elements by adjusting their opacity and firing an
203          * optional callback after completion.
204          *
205          * Only the opacity is adjusted for this animation, meaning that
206          * all of the matched elements should already have some form of height
207          * and width associated with them.
208          *
209          * @example $("p").fadeIn("slow");
210          *
211          * @example $("p").fadeIn("slow",function(){
212          *   alert("Animation Done.");
213          * });
214          *
215          * @name fadeIn
216          * @type jQuery
217          * @param String|Number speed (optional) A string representing one of the three predefined speeds ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
218          * @param Function callback (optional) A function to be executed whenever the animation completes.
219          * @cat Effects
220          * @see fadeOut(String|Number,Function)
221          * @see fadeTo(String|Number,Number,Function)
222          */
223         fadeIn: function(speed, callback){
224                 return this.animate({opacity: "show"}, speed, callback);
225         },
226         
227         /**
228          * Fade out all matched elements by adjusting their opacity and firing an
229          * optional callback after completion.
230          *
231          * Only the opacity is adjusted for this animation, meaning that
232          * all of the matched elements should already have some form of height
233          * and width associated with them.
234          *
235          * @example $("p").fadeOut("slow");
236          *
237          * @example $("p").fadeOut("slow",function(){
238          *   alert("Animation Done.");
239          * });
240          *
241          * @name fadeOut
242          * @type jQuery
243          * @param String|Number speed (optional) A string representing one of the three predefined speeds ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
244          * @param Function callback (optional) A function to be executed whenever the animation completes.
245          * @cat Effects
246          * @see fadeIn(String|Number,Function)
247          * @see fadeTo(String|Number,Number,Function)
248          */
249         fadeOut: function(speed, callback){
250                 return this.animate({opacity: "hide"}, speed, callback);
251         },
252         
253         /**
254          * Fade the opacity of all matched elements to a specified opacity and firing an
255          * optional callback after completion.
256          *
257          * Only the opacity is adjusted for this animation, meaning that
258          * all of the matched elements should already have some form of height
259          * and width associated with them.
260          *
261          * @example $("p").fadeTo("slow", 0.5);
262          *
263          * @example $("p").fadeTo("slow", 0.5, function(){
264          *   alert("Animation Done.");
265          * });
266          *
267          * @name fadeTo
268          * @type jQuery
269          * @param String|Number speed A string representing one of the three predefined speeds ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
270          * @param Number opacity The opacity to fade to (a number from 0 to 1).
271          * @param Function callback (optional) A function to be executed whenever the animation completes.
272          * @cat Effects
273          * @see fadeIn(String|Number,Function)
274          * @see fadeOut(String|Number,Function)
275          */
276         fadeTo: function(speed,to,callback){
277                 return this.animate({opacity: to}, speed, callback);
278         },
279         
280         /**
281          * A function for making your own, custom, animations. The key aspect of
282          * this function is the object of style properties that will be animated,
283          * and to what end. Each key within the object represents a style property
284          * that will also be animated (for example: "height", "top", or "opacity").
285          *
286          * The value associated with the key represents to what end the property
287          * will be animated. If a number is provided as the value, then the style
288          * property will be transitioned from its current state to that new number.
289          * Oterwise if the string "hide", "show", or "toggle" is provided, a default
290          * animation will be constructed for that property.
291          *
292          * @example $("p").animate({
293          *   height: 'toggle', opacity: 'toggle'
294          * }, "slow");
295          *
296          * @example $("p").animate({
297          *   left: 50, opacity: 'show'
298          * }, 500);
299          *
300          * @example $("p").animate({
301          *   opacity: 'show'
302          * }, "slow", "easein");
303          * @desc An example of using an 'easing' function to provide a different style of animation. This will only work if you have a plugin that provides this easing function (Only 'linear' is provided by default, with jQuery).
304          *
305          * @name animate
306          * @type jQuery
307          * @param Hash params A set of style attributes that you wish to animate, and to what end.
308          * @param String|Number speed (optional) A string representing one of the three predefined speeds ("slow", "normal", or "fast") or the number of milliseconds to run the animation (e.g. 1000).
309          * @param String easing (optional) The name of the easing effect that you want to use (Plugin Required).
310          * @param Function callback (optional) A function to be executed whenever the animation completes.
311          * @cat Effects
312          */
313         animate: function( prop, speed, easing, callback ) {
314                 return this.queue(function(){
315                 
316                         this.curAnim = jQuery.extend({}, prop);
317                         var opt = jQuery.speed(speed, easing, callback);
318                         
319                         for ( var p in prop ) {
320                                 var e = new jQuery.fx( this, opt, p );
321                                 if ( prop[p].constructor == Number )
322                                         e.custom( e.cur(), prop[p] );
323                                 else
324                                         e[ prop[p] ]( prop );
325                         }
326                         
327                 });
328         },
329         
330         /**
331          *
332          * @private
333          */
334         queue: function(type,fn){
335                 if ( !fn ) {
336                         fn = type;
337                         type = "fx";
338                 }
339         
340                 return this.each(function(){
341                         if ( !this.queue )
342                                 this.queue = {};
343         
344                         if ( !this.queue[type] )
345                                 this.queue[type] = [];
346         
347                         this.queue[type].push( fn );
348                 
349                         if ( this.queue[type].length == 1 )
350                                 fn.apply(this);
351                 });
352         }
353
354 });
355
356 jQuery.extend({
357         
358         speed: function(speed, easing, fn) {
359                 var opt = speed && speed.constructor == Object ? speed : {
360                         complete: fn || !fn && easing || 
361                                 speed && speed.constructor == Function && speed,
362                         duration: speed,
363                         easing: fn && easing || easing && easing.constructor != Function && easing
364                 };
365
366                 opt.duration = (opt.duration && opt.duration.constructor == Number ? 
367                         opt.duration : 
368                         { slow: 600, fast: 200 }[opt.duration]) || 400;
369         
370                 // Queueing
371                 opt.oldComplete = opt.complete;
372                 opt.complete = function(){
373                         jQuery.dequeue(this, "fx");
374                         if ( opt.oldComplete && opt.oldComplete.constructor == Function )
375                                 opt.oldComplete.apply( this );
376                 };
377         
378                 return opt;
379         },
380         
381         easing: {},
382         
383         queue: {},
384         
385         dequeue: function(elem,type){
386                 type = type || "fx";
387         
388                 if ( elem.queue && elem.queue[type] ) {
389                         // Remove self
390                         elem.queue[type].shift();
391         
392                         // Get next function
393                         var f = elem.queue[type][0];
394                 
395                         if ( f ) f.apply( elem );
396                 }
397         },
398
399         /*
400          * I originally wrote fx() as a clone of moo.fx and in the process
401          * of making it small in size the code became illegible to sane
402          * people. You've been warned.
403          */
404         
405         fx: function( elem, options, prop ){
406
407                 var z = this;
408
409                 // The styles
410                 var y = elem.style;
411                 
412                 // Store display property
413                 var oldDisplay = jQuery.css(elem, 'display');
414                 // Set display property to block for animation
415                 y.display = "block";
416                 // Make sure that nothing sneaks out
417                 y.overflow = "hidden";
418
419                 // Simple function for setting a style value
420                 z.a = function(){
421                         if ( options.step )
422                                 options.step.apply( elem, [ z.now ] );
423
424                         if ( prop == "opacity" )
425                                 jQuery.attr(y, "opacity", z.now); // Let attr handle opacity
426                         else if ( parseInt(z.now) ) // My hate for IE will never die
427                                 y[prop] = parseInt(z.now) + "px";
428                 };
429
430                 // Figure out the maximum number to run to
431                 z.max = function(){
432                         return parseFloat( jQuery.css(elem,prop) );
433                 };
434
435                 // Get the current size
436                 z.cur = function(){
437                         var r = parseFloat( jQuery.curCSS(elem, prop) );
438                         return r && r > -10000 ? r : z.max();
439                 };
440
441                 // Start an animation from one number to another
442                 z.custom = function(from,to){
443                         z.startTime = (new Date()).getTime();
444                         z.now = from;
445                         z.a();
446
447                         z.timer = setInterval(function(){
448                                 z.step(from, to);
449                         }, 13);
450                 };
451
452                 // Simple 'show' function
453                 z.show = function(){
454                         if ( !elem.orig ) elem.orig = {};
455
456                         // Remember where we started, so that we can go back to it later
457                         elem.orig[prop] = this.cur();
458
459                         options.show = true;
460
461                         // Begin the animation
462                         z.custom(0, elem.orig[prop]);
463
464                         // Stupid IE, look what you made me do
465                         if ( prop != "opacity" )
466                                 y[prop] = "1px";
467                 };
468
469                 // Simple 'hide' function
470                 z.hide = function(){
471                         if ( !elem.orig ) elem.orig = {};
472
473                         // Remember where we started, so that we can go back to it later
474                         elem.orig[prop] = this.cur();
475
476                         options.hide = true;
477
478                         // Begin the animation
479                         z.custom(elem.orig[prop], 0);
480                 };
481                 
482                 //Simple 'toggle' function
483                 z.toggle = function() {
484                         if ( !elem.orig ) elem.orig = {};
485
486                         // Remember where we started, so that we can go back to it later
487                         elem.orig[prop] = this.cur();
488
489                         if(oldDisplay == 'none')  {
490                                 options.show = true;
491                                 
492                                 // Stupid IE, look what you made me do
493                                 if ( prop != "opacity" )
494                                         y[prop] = "1px";
495
496                                 // Begin the animation
497                                 z.custom(0, elem.orig[prop]);   
498                         } else {
499                                 options.hide = true;
500
501                                 // Begin the animation
502                                 z.custom(elem.orig[prop], 0);
503                         }               
504                 };
505
506                 // Each step of an animation
507                 z.step = function(firstNum, lastNum){
508                         var t = (new Date()).getTime();
509
510                         if (t > options.duration + z.startTime) {
511                                 // Stop the timer
512                                 clearInterval(z.timer);
513                                 z.timer = null;
514
515                                 z.now = lastNum;
516                                 z.a();
517
518                                 if (elem.curAnim) elem.curAnim[ prop ] = true;
519
520                                 var done = true;
521                                 for ( var i in elem.curAnim )
522                                         if ( elem.curAnim[i] !== true )
523                                                 done = false;
524
525                                 if ( done ) {
526                                         // Reset the overflow
527                                         y.overflow = '';
528                                         
529                                         // Reset the display
530                                         y.display = oldDisplay;
531                                         if (jQuery.css(elem, 'display') == 'none')
532                                                 y.display = 'block';
533
534                                         // Hide the element if the "hide" operation was done
535                                         if ( options.hide ) 
536                                                 y.display = 'none';
537
538                                         // Reset the properties, if the item has been hidden or shown
539                                         if ( options.hide || options.show )
540                                                 for ( var p in elem.curAnim )
541                                                         if (p == "opacity")
542                                                                 jQuery.attr(y, p, elem.orig[p]);
543                                                         else
544                                                                 y[p] = '';
545                                 }
546
547                                 // If a callback was provided, execute it
548                                 if ( done && options.complete && options.complete.constructor == Function )
549                                         // Execute the complete function
550                                         options.complete.apply( elem );
551                         } else {
552                                 var n = t - this.startTime;
553                                 // Figure out where in the animation we are and set the number
554                                 var p = n / options.duration;
555                                 
556                                 // If the easing function exists, then use it 
557                                 z.now = options.easing && jQuery.easing[options.easing] ?
558                                         jQuery.easing[options.easing](p, n,  firstNum, (lastNum-firstNum), options.duration) :
559                                         // else use default linear easing
560                                         ((-Math.cos(p*Math.PI)/2) + 0.5) * (lastNum-firstNum) + firstNum;
561
562                                 // Perform the next step of the animation
563                                 z.a();
564                         }
565                 };
566         
567         }
568 });