Moving constructor code to jQuery.prototype.init for more flexibility
[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                 var args = arguments;
116                 return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
117                         this._toggle( fn, fn2 ) :
118                         this.each(function(){
119                                 jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]
120                                         .apply( jQuery(this), args );
121                         });
122         },
123         
124         /**
125          * Reveal all matched elements by adjusting their height and firing an
126          * optional callback after completion.
127          *
128          * Only the height is adjusted for this animation, causing all matched
129          * elements to be revealed in a "sliding" manner.
130          *
131          * @example $("p").slideDown("slow");
132          *
133          * @example $("p").slideDown("slow",function(){
134          *   alert("Animation Done.");
135          * });
136          *
137          * @name slideDown
138          * @type jQuery
139          * @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).
140          * @param Function callback (optional) A function to be executed whenever the animation completes.
141          * @cat Effects
142          * @see slideUp(String|Number,Function)
143          * @see slideToggle(String|Number,Function)
144          */
145         slideDown: function(speed,callback){
146                 return this.filter(":hidden").animate({height: "show"}, speed, callback).end();
147         },
148         
149         /**
150          * Hide all matched elements by adjusting their height and firing an
151          * optional callback after completion.
152          *
153          * Only the height is adjusted for this animation, causing all matched
154          * elements to be hidden in a "sliding" manner.
155          *
156          * @example $("p").slideUp("slow");
157          *
158          * @example $("p").slideUp("slow",function(){
159          *   alert("Animation Done.");
160          * });
161          *
162          * @name slideUp
163          * @type jQuery
164          * @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).
165          * @param Function callback (optional) A function to be executed whenever the animation completes.
166          * @cat Effects
167          * @see slideDown(String|Number,Function)
168          * @see slideToggle(String|Number,Function)
169          */
170         slideUp: function(speed,callback){
171                 return this.filter(":visible").animate({height: "hide"}, speed, callback).end();
172         },
173
174         /**
175          * Toggle the visibility of all matched elements by adjusting their height and firing an
176          * optional callback after completion.
177          *
178          * Only the height is adjusted for this animation, causing all matched
179          * elements to be hidden in a "sliding" manner.
180          *
181          * @example $("p").slideToggle("slow");
182          *
183          * @example $("p").slideToggle("slow",function(){
184          *   alert("Animation Done.");
185          * });
186          *
187          * @name slideToggle
188          * @type jQuery
189          * @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).
190          * @param Function callback (optional) A function to be executed whenever the animation completes.
191          * @cat Effects
192          * @see slideDown(String|Number,Function)
193          * @see slideUp(String|Number,Function)
194          */
195         slideToggle: function(speed, callback){
196                 return this.each(function(){
197                         var state = jQuery(this).is(":hidden") ? "show" : "hide";
198                         jQuery(this).animate({height: state}, speed, callback);
199                 });
200         },
201         
202         /**
203          * Fade in all matched elements by adjusting their opacity and firing an
204          * optional callback after completion.
205          *
206          * Only the opacity is adjusted for this animation, meaning that
207          * all of the matched elements should already have some form of height
208          * and width associated with them.
209          *
210          * @example $("p").fadeIn("slow");
211          *
212          * @example $("p").fadeIn("slow",function(){
213          *   alert("Animation Done.");
214          * });
215          *
216          * @name fadeIn
217          * @type jQuery
218          * @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).
219          * @param Function callback (optional) A function to be executed whenever the animation completes.
220          * @cat Effects
221          * @see fadeOut(String|Number,Function)
222          * @see fadeTo(String|Number,Number,Function)
223          */
224         fadeIn: function(speed, callback){
225                 return this.filter(":hidden").animate({opacity: "show"}, speed, callback).end();
226         },
227         
228         /**
229          * Fade out all matched elements by adjusting their opacity and firing an
230          * optional callback after completion.
231          *
232          * Only the opacity is adjusted for this animation, meaning that
233          * all of the matched elements should already have some form of height
234          * and width associated with them.
235          *
236          * @example $("p").fadeOut("slow");
237          *
238          * @example $("p").fadeOut("slow",function(){
239          *   alert("Animation Done.");
240          * });
241          *
242          * @name fadeOut
243          * @type jQuery
244          * @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).
245          * @param Function callback (optional) A function to be executed whenever the animation completes.
246          * @cat Effects
247          * @see fadeIn(String|Number,Function)
248          * @see fadeTo(String|Number,Number,Function)
249          */
250         fadeOut: function(speed, callback){
251                 return this.filter(":visible").animate({opacity: "hide"}, speed, callback).end();
252         },
253         
254         /**
255          * Fade the opacity of all matched elements to a specified opacity and firing an
256          * optional callback after completion.
257          *
258          * Only the opacity is adjusted for this animation, meaning that
259          * all of the matched elements should already have some form of height
260          * and width associated with them.
261          *
262          * @example $("p").fadeTo("slow", 0.5);
263          *
264          * @example $("p").fadeTo("slow", 0.5, function(){
265          *   alert("Animation Done.");
266          * });
267          *
268          * @name fadeTo
269          * @type jQuery
270          * @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).
271          * @param Number opacity The opacity to fade to (a number from 0 to 1).
272          * @param Function callback (optional) A function to be executed whenever the animation completes.
273          * @cat Effects
274          * @see fadeIn(String|Number,Function)
275          * @see fadeOut(String|Number,Function)
276          */
277         fadeTo: function(speed,to,callback){
278                 return this.animate({opacity: to}, speed, callback);
279         },
280         
281         /**
282          * A function for making your own, custom animations. The key aspect of
283          * this function is the object of style properties that will be animated,
284          * and to what end. Each key within the object represents a style property
285          * that will also be animated (for example: "height", "top", or "opacity").
286          *
287          * Note that properties should be specified using camel case
288          * eg. marginLeft instead of margin-left.
289          *
290          * The value associated with the key represents to what end the property
291          * will be animated. If a number is provided as the value, then the style
292          * property will be transitioned from its current state to that new number.
293          * Otherwise if the string "hide", "show", or "toggle" is provided, a default
294          * animation will be constructed for that property.
295          *
296          * @example $("p").animate({
297          *   height: 'toggle', opacity: 'toggle'
298          * }, "slow");
299          *
300          * @example $("p").animate({
301          *   left: 50, opacity: 'show'
302          * }, 500);
303          *
304          * @example $("p").animate({
305          *   opacity: 'show'
306          * }, "slow", "easein");
307          * @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).
308          *
309          * @name animate
310          * @type jQuery
311          * @param Hash params A set of style attributes that you wish to animate, and to what end.
312          * @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).
313          * @param String easing (optional) The name of the easing effect that you want to use (e.g. swing or linear). Defaults to "swing".
314          * @param Function callback (optional) A function to be executed whenever the animation completes.
315          * @cat Effects
316          */
317         animate: function( prop, speed, easing, callback ) {
318                 return this.queue(function(){
319                 
320                         this.curAnim = jQuery.extend({}, prop);
321                         var opt = jQuery.speed(speed, easing, callback);
322                         
323                         for ( var p in prop ) {
324                                 var e = new jQuery.fx( this, opt, p );
325                                 if ( prop[p].constructor == Number )
326                                         e.custom( e.cur(), prop[p] );
327                                 else
328                                         e[ prop[p] ]( prop );
329                         }
330                         
331                 });
332         },
333         
334         /**
335          *
336          * @private
337          */
338         queue: function(type,fn){
339                 if ( !fn ) {
340                         fn = type;
341                         type = "fx";
342                 }
343         
344                 return this.each(function(){
345                         if ( !this.queue )
346                                 this.queue = {};
347         
348                         if ( !this.queue[type] )
349                                 this.queue[type] = [];
350         
351                         this.queue[type].push( fn );
352                 
353                         if ( this.queue[type].length == 1 )
354                                 fn.apply(this);
355                 });
356         }
357
358 });
359
360 jQuery.extend({
361         
362         speed: function(speed, easing, fn) {
363                 var opt = speed && speed.constructor == Object ? speed : {
364                         complete: fn || !fn && easing || 
365                                 jQuery.isFunction( speed ) && speed,
366                         duration: speed,
367                         easing: fn && easing || easing && easing.constructor != Function && easing || "swing"
368                 };
369
370                 opt.duration = (opt.duration && opt.duration.constructor == Number ? 
371                         opt.duration : 
372                         { slow: 600, fast: 200 }[opt.duration]) || 400;
373         
374                 // Queueing
375                 opt.old = opt.complete;
376                 opt.complete = function(){
377                         jQuery.dequeue(this, "fx");
378                         if ( jQuery.isFunction( opt.old ) )
379                                 opt.old.apply( this );
380                 };
381         
382                 return opt;
383         },
384         
385         easing: {
386                 linear: function( p, n, firstNum, diff ) {
387                         return firstNum + diff * p;
388                 },
389                 swing: function( p, n, firstNum, diff ) {
390                         return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
391                 }
392         },
393         
394         queue: {},
395         
396         dequeue: function(elem,type){
397                 type = type || "fx";
398         
399                 if ( elem.queue && elem.queue[type] ) {
400                         // Remove self
401                         elem.queue[type].shift();
402         
403                         // Get next function
404                         var f = elem.queue[type][0];
405                 
406                         if ( f ) f.apply( elem );
407                 }
408         },
409
410         timers: [],
411
412         /*
413          * I originally wrote fx() as a clone of moo.fx and in the process
414          * of making it small in size the code became illegible to sane
415          * people. You've been warned.
416          */
417         
418         fx: function( elem, options, prop ){
419
420                 var z = this;
421
422                 // The styles
423                 var y = elem.style;
424                 
425                 if ( prop == "height" || prop == "width" ) {
426                         // Store display property
427                         var oldDisplay = jQuery.css(elem, "display");
428
429                         // Make sure that nothing sneaks out
430                         var oldOverflow = y.overflow;
431                         y.overflow = "hidden";
432                 }
433
434                 // Simple function for setting a style value
435                 z.a = function(){
436                         if ( options.step )
437                                 options.step.apply( elem, [ z.now ] );
438
439                         if ( prop == "opacity" )
440                                 jQuery.attr(y, "opacity", z.now); // Let attr handle opacity
441                         else {
442                                 y[prop] = parseInt(z.now) + "px";
443                                 y.display = "block"; // Set display property to block for animation
444                         }
445                 };
446
447                 // Figure out the maximum number to run to
448                 z.max = function(){
449                         return parseFloat( jQuery.css(elem,prop) );
450                 };
451
452                 // Get the current size
453                 z.cur = function(){
454                         var r = parseFloat( jQuery.curCSS(elem, prop) );
455                         return r && r > -10000 ? r : z.max();
456                 };
457
458                 // Start an animation from one number to another
459                 z.custom = function(from,to){
460                         z.startTime = (new Date()).getTime();
461                         z.now = from;
462                         z.a();
463
464                         jQuery.timers.push(function(){
465                                 return z.step(from, to);
466                         });
467
468                         if ( jQuery.timers.length == 1 ) {
469                                 var timer = setInterval(function(){
470                                         jQuery.timers = jQuery.grep( jQuery.timers, function(fn){
471                                                 return fn();
472                                         });
473
474                                         if ( !jQuery.timers.length )
475                                                 clearInterval( timer );
476                                 }, 13);
477                         }
478                 };
479
480                 // Simple 'show' function
481                 z.show = function(){
482                         if ( !elem.orig ) elem.orig = {};
483
484                         // Remember where we started, so that we can go back to it later
485                         elem.orig[prop] = jQuery.attr( elem.style, prop );
486
487                         options.show = true;
488
489                         // Begin the animation
490                         z.custom(0, this.cur());
491
492                         // Stupid IE, look what you made me do
493                         if ( prop != "opacity" )
494                                 y[prop] = "1px";
495                 };
496
497                 // Simple 'hide' function
498                 z.hide = function(){
499                         if ( !elem.orig ) elem.orig = {};
500
501                         // Remember where we started, so that we can go back to it later
502                         elem.orig[prop] = jQuery.attr( elem.style, prop );
503
504                         options.hide = true;
505
506                         // Begin the animation
507                         z.custom(this.cur(), 0);
508                 };
509                 
510                 //Simple 'toggle' function
511                 z.toggle = function() {
512                         if ( !elem.orig ) elem.orig = {};
513
514                         // Remember where we started, so that we can go back to it later
515                         elem.orig[prop] = jQuery.attr( elem.style, prop );
516
517                         if(oldDisplay == "none")  {
518                                 options.show = true;
519                                 
520                                 // Stupid IE, look what you made me do
521                                 if ( prop != "opacity" )
522                                         y[prop] = "1px";
523
524                                 // Begin the animation
525                                 z.custom(0, this.cur());        
526                         } else {
527                                 options.hide = true;
528
529                                 // Begin the animation
530                                 z.custom(this.cur(), 0);
531                         }               
532                 };
533
534                 // Each step of an animation
535                 z.step = function(firstNum, lastNum){
536                         var t = (new Date()).getTime();
537
538                         if (t > options.duration + z.startTime) {
539                                 z.now = lastNum;
540                                 z.a();
541
542                                 if (elem.curAnim) elem.curAnim[ prop ] = true;
543
544                                 var done = true;
545                                 for ( var i in elem.curAnim )
546                                         if ( elem.curAnim[i] !== true )
547                                                 done = false;
548
549                                 if ( done ) {
550                                         if ( oldDisplay ) {
551                                                 // Reset the overflow
552                                                 y.overflow = oldOverflow;
553                                         
554                                                 // Reset the display
555                                                 y.display = oldDisplay;
556                                                 if (jQuery.css(elem, "display") == "none")
557                                                         y.display = "block";
558                                         }
559
560                                         // Hide the element if the "hide" operation was done
561                                         if ( options.hide ) 
562                                                 y.display = "none";
563
564                                         // Reset the properties, if the item has been hidden or shown
565                                         if ( options.hide || options.show )
566                                                 for ( var p in elem.curAnim )
567                                                         jQuery.attr(y, p, elem.orig[p]);
568                                 }
569
570                                 // If a callback was provided, execute it
571                                 if ( done && jQuery.isFunction( options.complete ) )
572                                         // Execute the complete function
573                                         options.complete.apply( elem );
574
575                                 return false;
576                         } else {
577                                 var n = t - this.startTime;
578                                 // Figure out where in the animation we are and set the number
579                                 var p = n / options.duration;
580                                 
581                                 // Perform the easing function, defaults to swing
582                                 z.now = jQuery.easing[options.easing](p, n,  firstNum, (lastNum-firstNum), options.duration);
583
584                                 // Perform the next step of the animation
585                                 z.a();
586                         }
587
588                         return true;
589                 };
590         
591         }
592 });