More bug fixes and added documentation - passes the test suite now.
[jquery.git] / fx / fx.js
1 jQuery.fn.extend({
2
3         // overwrite the old show method
4         _show: jQuery.fn.show,
5         
6         /**
7          * The effects module overloads the show method to now allow 
8          * for a speed to the show operation. What actually happens is 
9          * that the height, width, and opacity to the matched elements 
10          * are changed dynamically. The only three current speeds are 
11          * "slow", "normal", and "fast". For example:
12          *   $("p").show("slow");
13          * Note: You should not run the show method on things 
14          * that are already shown. This can be circumvented by doing this:
15          *   $("p:hidden").show("slow");
16          */
17         show: function(speed,callback){
18                 return speed ? this.animate({
19                         height: "show", width: "show", opacity: "show"
20                 }, speed, callback) : this._show();
21         },
22         
23         // We're overwriting the old hide method
24         _hide: jQuery.fn.hide,
25         
26         /**
27          * The hide function behaves very similary to the show function, 
28          * but is just the opposite.
29          *   $("p:visible").hide("slow");
30          */
31         hide: function(speed,callback){
32                 return speed ? this.animate({
33                         height: "hide", width: "hide", opacity: "hide"
34                 }, speed, callback) : this._hide();
35         },
36         
37         /**
38          * This function increases the height and opacity for all matched 
39          * elements. This is very similar to 'show', but does not change 
40          * the width - creating a neat sliding effect.
41          *   $("p:hidden").slideDown("slow");
42          */
43         slideDown: function(speed,callback){
44                 return this.animate({height: "show"}, speed, callback);
45         },
46         
47         /**
48          * Just like slideDown, only it hides all matched elements.
49          *   $("p:visible").slideUp("slow");
50          */
51         slideUp: function(speed,callback){
52                 return this.animate({height: "hide"}, speed, callback);
53         },
54         
55         /**
56          * Adjusts the opacity of all matched elements from a hidden, 
57          * to a fully visible, state.
58          *   $("p:hidden").fadeIn("slow");
59          */
60         fadeIn: function(speed,callback){
61                 return this.animate({opacity: "show"}, speed, callback);
62         },
63         
64         /**
65          * Same as fadeIn, but transitions from a visible, to a hidden state.
66          *   $("p:visible").fadeOut("slow");
67          */
68         fadeOut: function(speed,callback){
69                 return this.animate({opacity: "hide"}, speed, callback);
70         },
71         
72         /**
73          * ...
74          */
75         fadeTo: function(speed,to,callback){
76                 return this.animate({opacity: to}, speed, callback);
77         },
78         
79         /**
80          *
81          */
82         animate: function(prop,speed,callback) {
83                 return this.queue(function(){
84                         var i = 0;
85                         for ( var p in prop ) {
86                                 var e = new jQuery.fx( this, jQuery.speed(speed,callback,i++), p );
87                                 if ( prop[p].constructor == Number )
88                                         e.custom( e.cur(), prop[p] );
89                                 else
90                                         e[ prop[p] ]();
91                         }
92                 });
93         },
94         
95         /**
96          *
97          * @private
98          */
99         queue: function(type,fn){
100                 if ( !fn ) {
101                         fn = type;
102                         type = "fx";
103                 }
104         
105                 return this.each(function(){
106                         if ( !this.queue )
107                                 this.queue = {};
108         
109                         if ( !this.queue[type] )
110                                 this.queue[type] = [];
111         
112                         this.queue[type].push( fn );
113                 
114                         if ( this.queue[type].length == 1 )
115                                 fn.apply(this);
116                 });
117         }
118
119 });
120
121 jQuery.extend({
122
123         setAuto: function(e,p) {
124                 var a = e.style[p];
125                 var o = jQuery.css(e,p);
126                 e.style[p] = "auto";
127                 var n = jQuery.css(e,p);
128                 if ( o != n )
129                         e.style[p] = a;
130         },
131         
132         speed: function(s,o,i) {
133                 o = o || {};
134                 
135                 if ( o.constructor == Function )
136                         o = { complete: o };
137                 
138                 var ss = { slow: 600, fast: 200 };
139                 o.duration = (s && s.constructor == Number ? s : ss[s]) || 400;
140         
141                 // Queueing
142                 o.oldComplete = o.complete;
143                 o.complete = function(){
144                         jQuery.dequeue(this, "fx");
145                         if ( o.oldComplete && o.oldComplete.constructor == Function )
146                                 o.oldComplete.apply( this );
147                 };
148                 
149                 if ( i > 0 )
150                         o.complete = null;
151         
152                 return o;
153         },
154         
155         queue: {},
156         
157         dequeue: function(elem,type){
158                 type = type || "fx";
159         
160                 if ( elem.queue && elem.queue[type] ) {
161                         // Remove self
162                         elem.queue[type].shift();
163         
164                         // Get next function
165                         var f = elem.queue[type][0];
166                 
167                         if ( f )
168                                 f.apply( elem );
169                 }
170         },
171
172         /*
173          * I originally wrote fx() as a clone of moo.fx and in the process
174          * of making it small in size the code became illegible to sane
175          * people. You've been warned.
176          */
177         
178         fx: function( elem, options, prop ){
179         
180                 var z = this;
181         
182                 // The users options
183                 z.o = {
184                         duration: options.duration || 400,
185                         complete: options.complete
186                 };
187         
188                 // The element
189                 z.el = elem;
190         
191                 // The styles
192                 var y = z.el.style;
193         
194                 // Simple function for setting a style value
195                 z.a = function(){
196                         if ( prop == "opacity" ) {
197                                 if (z.now == 1) z.now = 0.9999;
198                                 if (window.ActiveXObject)
199                                         y.filter = "alpha(opacity=" + z.now*100 + ")";
200                                 y.opacity = z.now;
201                         } else
202                                 y[prop] = z.now+"px";
203                 };
204         
205                 // Figure out the maximum number to run to
206                 z.max = function(){
207                         return z.el["orig"+prop] || z.cur();
208                 };
209         
210                 // Get the current size
211                 z.cur = function(){
212                         return parseFloat( jQuery.css(z.el,prop) );
213                 };
214         
215                 // Start an animation from one number to another
216                 z.custom = function(from,to){
217                         z.startTime = (new Date()).getTime();
218                         z.now = from;
219                         z.a();
220         
221                         z.timer = setInterval(function(){
222                                 z.step(from, to);
223                         }, 13);
224                 };
225         
226                 // Simple 'show' function
227                 z.show = function(){
228                         y.display = "block";
229                         z.o.auto = true;
230                         z.custom(0,z.max());
231                 };
232         
233                 // Simple 'hide' function
234                 z.hide = function(){
235                         // Remember where we started, so that we can go back to it later
236                         z.el["orig"+prop] = this.cur();
237         
238                         // Begin the animation
239                         z.custom(z.cur(),0);
240                 };
241         
242                 // IE has trouble with opacity if it doesn't have layout
243                 if ( jQuery.browser.msie && !z.el.currentStyle.hasLayout )
244                         y.zoom = 1;
245         
246                 // Remember  the overflow of the element
247                 z.oldOverflow = y.overflow;
248         
249                 // Make sure that nothing sneaks out
250                 y.overflow = "hidden";
251         
252                 // Each step of an animation
253                 z.step = function(firstNum, lastNum){
254                         var t = (new Date()).getTime();
255         
256                         if (t > z.o.duration + z.startTime) {
257                                 // Stop the timer
258                                 clearInterval(z.timer);
259                                 z.timer = null;
260         
261                                 z.now = lastNum;
262                                 z.a();
263         
264                                 // Reset the overflow
265                                 y.overflow = z.oldOverflow;
266         
267                                 // If the element was shown, and not using a custom number,
268                                 // set its height and/or width to auto
269                                 if ( (prop == "height" || prop == "width") && z.o.auto )
270                                         jQuery.setAuto( z.el, prop );
271         
272                                 // If a callback was provided, execute it
273                                 if( z.o.complete && z.o.complete.constructor == Function ) {
274         
275                                         // Yes, this is a weird place for this, but it needs to be executed
276                                         // only once per cluster of effects.
277                                         // If the element is, effectively, hidden - hide it
278                                         if ( y.height == "0px" || y.width == "0px" )
279                                                 y.display = "none";
280         
281                                         // Execute the complete function
282                                         z.o.complete.apply( z.el );
283                                 }
284                         } else {
285                                 // Figure out where in the animation we are and set the number
286                                 var p = (t - this.startTime) / z.o.duration;
287                                 z.now = ((-Math.cos(p*Math.PI)/2) + 0.5) * (lastNum-firstNum) + firstNum;
288         
289                                 // Perform the next step of the animation
290                                 z.a();
291                         }
292                 };
293         
294         }
295
296 });