Exclude Opera from toggle test, too (#863)
[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.animate({height: "show"}, speed, callback);
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.animate({height: "hide"}, speed, callback);
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.animate({opacity: "show"}, speed, callback);
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.animate({opacity: "hide"}, speed, callback);
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          * The value associated with the key represents to what end the property
288          * will be animated. If a number is provided as the value, then the style
289          * property will be transitioned from its current state to that new number.
290          * Oterwise if the string "hide", "show", or "toggle" is provided, a default
291          * animation will be constructed for that property.
292          *
293          * @example $("p").animate({
294          *   height: 'toggle', opacity: 'toggle'
295          * }, "slow");
296          *
297          * @example $("p").animate({
298          *   left: 50, opacity: 'show'
299          * }, 500);
300          *
301          * @example $("p").animate({
302          *   opacity: 'show'
303          * }, "slow", "easein");
304          * @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).
305          *
306          * @name animate
307          * @type jQuery
308          * @param Hash params A set of style attributes that you wish to animate, and to what end.
309          * @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).
310          * @param String easing (optional) The name of the easing effect that you want to use (Plugin Required).
311          * @param Function callback (optional) A function to be executed whenever the animation completes.
312          * @cat Effects
313          */
314         animate: function( prop, speed, easing, callback ) {
315                 return this.queue(function(){
316                 
317                         this.curAnim = jQuery.extend({}, prop);
318                         var opt = jQuery.speed(speed, easing, callback);
319                         
320                         for ( var p in prop ) {
321                                 var e = new jQuery.fx( this, opt, p );
322                                 if ( prop[p].constructor == Number )
323                                         e.custom( e.cur(), prop[p] );
324                                 else
325                                         e[ prop[p] ]( prop );
326                         }
327                         
328                 });
329         },
330         
331         /**
332          *
333          * @private
334          */
335         queue: function(type,fn){
336                 if ( !fn ) {
337                         fn = type;
338                         type = "fx";
339                 }
340         
341                 return this.each(function(){
342                         if ( !this.queue )
343                                 this.queue = {};
344         
345                         if ( !this.queue[type] )
346                                 this.queue[type] = [];
347         
348                         this.queue[type].push( fn );
349                 
350                         if ( this.queue[type].length == 1 )
351                                 fn.apply(this);
352                 });
353         }
354
355 });
356
357 jQuery.extend({
358         
359         speed: function(speed, easing, fn) {
360                 var opt = speed && speed.constructor == Object ? speed : {
361                         complete: fn || !fn && easing || 
362                                 jQuery.isFunction( speed ) && speed,
363                         duration: speed,
364                         easing: fn && easing || easing && easing.constructor != Function && easing
365                 };
366
367                 opt.duration = (opt.duration && opt.duration.constructor == Number ? 
368                         opt.duration : 
369                         { slow: 600, fast: 200 }[opt.duration]) || 400;
370         
371                 // Queueing
372                 opt.old = opt.complete;
373                 opt.complete = function(){
374                         jQuery.dequeue(this, "fx");
375                         if ( jQuery.isFunction( opt.old ) )
376                                 opt.old.apply( this );
377                 };
378         
379                 return opt;
380         },
381         
382         easing: {},
383         
384         queue: {},
385         
386         dequeue: function(elem,type){
387                 type = type || "fx";
388         
389                 if ( elem.queue && elem.queue[type] ) {
390                         // Remove self
391                         elem.queue[type].shift();
392         
393                         // Get next function
394                         var f = elem.queue[type][0];
395                 
396                         if ( f ) f.apply( elem );
397                 }
398         },
399
400         /*
401          * I originally wrote fx() as a clone of moo.fx and in the process
402          * of making it small in size the code became illegible to sane
403          * people. You've been warned.
404          */
405         
406         fx: function( elem, options, prop ){
407
408                 var z = this;
409
410                 // The styles
411                 var y = elem.style;
412                 
413                 // Store display property
414                 var oldDisplay = jQuery.css(elem, "display");
415
416                 // Set display property to block for animation
417                 y.display = "block";
418
419                 // Make sure that nothing sneaks out
420                 y.overflow = "hidden";
421
422                 // Simple function for setting a style value
423                 z.a = function(){
424                         if ( options.step )
425                                 options.step.apply( elem, [ z.now ] );
426
427                         if ( prop == "opacity" )
428                                 jQuery.attr(y, "opacity", z.now); // Let attr handle opacity
429                         else if ( parseInt(z.now) ) // My hate for IE will never die
430                                 y[prop] = parseInt(z.now) + "px";
431                 };
432
433                 // Figure out the maximum number to run to
434                 z.max = function(){
435                         return parseFloat( jQuery.css(elem,prop) );
436                 };
437
438                 // Get the current size
439                 z.cur = function(){
440                         var r = parseFloat( jQuery.curCSS(elem, prop) );
441                         return r && r > -10000 ? r : z.max();
442                 };
443
444                 // Start an animation from one number to another
445                 z.custom = function(from,to){
446                         z.startTime = (new Date()).getTime();
447                         z.now = from;
448                         z.a();
449
450                         z.timer = setInterval(function(){
451                                 z.step(from, to);
452                         }, 13);
453                 };
454
455                 // Simple 'show' function
456                 z.show = function(){
457                         if ( !elem.orig ) elem.orig = {};
458
459                         // Remember where we started, so that we can go back to it later
460                         elem.orig[prop] = this.cur();
461
462                         options.show = true;
463
464                         // Begin the animation
465                         z.custom(0, elem.orig[prop]);
466
467                         // Stupid IE, look what you made me do
468                         if ( prop != "opacity" )
469                                 y[prop] = "1px";
470                 };
471
472                 // Simple 'hide' function
473                 z.hide = function(){
474                         if ( !elem.orig ) elem.orig = {};
475
476                         // Remember where we started, so that we can go back to it later
477                         elem.orig[prop] = this.cur();
478
479                         options.hide = true;
480
481                         // Begin the animation
482                         z.custom(elem.orig[prop], 0);
483                 };
484                 
485                 //Simple 'toggle' function
486                 z.toggle = function() {
487                         if ( !elem.orig ) elem.orig = {};
488
489                         // Remember where we started, so that we can go back to it later
490                         elem.orig[prop] = this.cur();
491
492                         if(oldDisplay == "none")  {
493                                 options.show = true;
494                                 
495                                 // Stupid IE, look what you made me do
496                                 if ( prop != "opacity" )
497                                         y[prop] = "1px";
498
499                                 // Begin the animation
500                                 z.custom(0, elem.orig[prop]);   
501                         } else {
502                                 options.hide = true;
503
504                                 // Begin the animation
505                                 z.custom(elem.orig[prop], 0);
506                         }               
507                 };
508
509                 // Each step of an animation
510                 z.step = function(firstNum, lastNum){
511                         var t = (new Date()).getTime();
512
513                         if (t > options.duration + z.startTime) {
514                                 // Stop the timer
515                                 clearInterval(z.timer);
516                                 z.timer = null;
517
518                                 z.now = lastNum;
519                                 z.a();
520
521                                 if (elem.curAnim) elem.curAnim[ prop ] = true;
522
523                                 var done = true;
524                                 for ( var i in elem.curAnim )
525                                         if ( elem.curAnim[i] !== true )
526                                                 done = false;
527
528                                 if ( done ) {
529                                         // Reset the overflow
530                                         y.overflow = "";
531                                         
532                                         // Reset the display
533                                         y.display = oldDisplay;
534                                         if (jQuery.css(elem, "display") == "none")
535                                                 y.display = "block";
536
537                                         // Hide the element if the "hide" operation was done
538                                         if ( options.hide ) 
539                                                 y.display = "none";
540
541                                         // Reset the properties, if the item has been hidden or shown
542                                         if ( options.hide || options.show )
543                                                 for ( var p in elem.curAnim )
544                                                         if (p == "opacity")
545                                                                 jQuery.attr(y, p, elem.orig[p]);
546                                                         else
547                                                                 y[p] = "";
548                                 }
549
550                                 // If a callback was provided, execute it
551                                 if ( done && jQuery.isFunction( options.complete ) )
552                                         // Execute the complete function
553                                         options.complete.apply( elem );
554                         } else {
555                                 var n = t - this.startTime;
556                                 // Figure out where in the animation we are and set the number
557                                 var p = n / options.duration;
558                                 
559                                 // If the easing function exists, then use it 
560                                 z.now = options.easing && jQuery.easing[options.easing] ?
561                                         jQuery.easing[options.easing](p, n,  firstNum, (lastNum-firstNum), options.duration) :
562                                         // else use default linear easing
563                                         ((-Math.cos(p*Math.PI)/2) + 0.5) * (lastNum-firstNum) + firstNum;
564
565                                 // Perform the next step of the animation
566                                 z.a();
567                         }
568                 };
569         
570         }
571 });