Cleaned up how event removing was handled. All expandos are now removed when they...
[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          * 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 (Plugin Required).
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
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         
387         queue: {},
388         
389         dequeue: function(elem,type){
390                 type = type || "fx";
391         
392                 if ( elem.queue && elem.queue[type] ) {
393                         // Remove self
394                         elem.queue[type].shift();
395         
396                         // Get next function
397                         var f = elem.queue[type][0];
398                 
399                         if ( f ) f.apply( elem );
400                 }
401         },
402
403         /*
404          * I originally wrote fx() as a clone of moo.fx and in the process
405          * of making it small in size the code became illegible to sane
406          * people. You've been warned.
407          */
408         
409         fx: function( elem, options, prop ){
410
411                 var z = this;
412
413                 // The styles
414                 var y = elem.style;
415                 
416                 // Store display property
417                 var oldDisplay = jQuery.css(elem, "display");
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                         y.display = "block"; // Set display property to block for animation
433                 };
434
435                 // Figure out the maximum number to run to
436                 z.max = function(){
437                         return parseFloat( jQuery.css(elem,prop) );
438                 };
439
440                 // Get the current size
441                 z.cur = function(){
442                         var r = parseFloat( jQuery.curCSS(elem, prop) );
443                         return r && r > -10000 ? r : z.max();
444                 };
445
446                 // Start an animation from one number to another
447                 z.custom = function(from,to){
448                         z.startTime = (new Date()).getTime();
449                         z.now = from;
450                         z.a();
451
452                         z.timer = setInterval(function(){
453                                 z.step(from, to);
454                         }, 13);
455                 };
456
457                 // Simple 'show' function
458                 z.show = function(){
459                         if ( !elem.orig ) elem.orig = {};
460
461                         // Remember where we started, so that we can go back to it later
462                         elem.orig[prop] = this.cur();
463
464                         options.show = true;
465
466                         // Begin the animation
467                         z.custom(0, elem.orig[prop]);
468
469                         // Stupid IE, look what you made me do
470                         if ( prop != "opacity" )
471                                 y[prop] = "1px";
472                 };
473
474                 // Simple 'hide' function
475                 z.hide = function(){
476                         if ( !elem.orig ) elem.orig = {};
477
478                         // Remember where we started, so that we can go back to it later
479                         elem.orig[prop] = this.cur();
480
481                         options.hide = true;
482
483                         // Begin the animation
484                         z.custom(elem.orig[prop], 0);
485                 };
486                 
487                 //Simple 'toggle' function
488                 z.toggle = function() {
489                         if ( !elem.orig ) elem.orig = {};
490
491                         // Remember where we started, so that we can go back to it later
492                         elem.orig[prop] = this.cur();
493
494                         if(oldDisplay == "none")  {
495                                 options.show = true;
496                                 
497                                 // Stupid IE, look what you made me do
498                                 if ( prop != "opacity" )
499                                         y[prop] = "1px";
500
501                                 // Begin the animation
502                                 z.custom(0, elem.orig[prop]);   
503                         } else {
504                                 options.hide = true;
505
506                                 // Begin the animation
507                                 z.custom(elem.orig[prop], 0);
508                         }               
509                 };
510
511                 // Each step of an animation
512                 z.step = function(firstNum, lastNum){
513                         var t = (new Date()).getTime();
514
515                         if (t > options.duration + z.startTime) {
516                                 // Stop the timer
517                                 clearInterval(z.timer);
518                                 z.timer = null;
519
520                                 z.now = lastNum;
521                                 z.a();
522
523                                 if (elem.curAnim) elem.curAnim[ prop ] = true;
524
525                                 var done = true;
526                                 for ( var i in elem.curAnim )
527                                         if ( elem.curAnim[i] !== true )
528                                                 done = false;
529
530                                 if ( done ) {
531                                         // Reset the overflow
532                                         y.overflow = "";
533                                         
534                                         // Reset the display
535                                         y.display = oldDisplay;
536                                         if (jQuery.css(elem, "display") == "none")
537                                                 y.display = "block";
538
539                                         // Hide the element if the "hide" operation was done
540                                         if ( options.hide ) 
541                                                 y.display = "none";
542
543                                         // Reset the properties, if the item has been hidden or shown
544                                         if ( options.hide || options.show )
545                                                 for ( var p in elem.curAnim )
546                                                         if (p == "opacity")
547                                                                 jQuery.attr(y, p, elem.orig[p]);
548                                                         else
549                                                                 y[p] = "";
550                                 }
551
552                                 // If a callback was provided, execute it
553                                 if ( done && jQuery.isFunction( options.complete ) )
554                                         // Execute the complete function
555                                         options.complete.apply( elem );
556                         } else {
557                                 var n = t - this.startTime;
558                                 // Figure out where in the animation we are and set the number
559                                 var p = n / options.duration;
560                                 
561                                 // If the easing function exists, then use it 
562                                 z.now = options.easing && jQuery.easing[options.easing] ?
563                                         jQuery.easing[options.easing](p, n,  firstNum, (lastNum-firstNum), options.duration) :
564                                         // else use default linear easing
565                                         ((-Math.cos(p*Math.PI)/2) + 0.5) * (lastNum-firstNum) + firstNum;
566
567                                 // Perform the next step of the animation
568                                 z.a();
569                         }
570                 };
571         
572         }
573 });