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