Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / src / ext-core / src / core / Element.fx.js
1 /*!
2  * Ext JS Library 3.3.1
3  * Copyright(c) 2006-2010 Sencha Inc.
4  * licensing@sencha.com
5  * http://www.sencha.com/license
6  */
7 /**
8  * @class Ext.Element
9  */
10 /**
11  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
12  * @static
13  * @type Number
14  */
15 Ext.Element.VISIBILITY = 1;
16 /**
17  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
18  * @static
19  * @type Number
20  */
21 Ext.Element.DISPLAY = 2;
22
23 /**
24  * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
25  * to hide element.
26  * @static
27  * @type Number
28  */
29 Ext.Element.OFFSETS = 3;
30
31
32 Ext.Element.ASCLASS = 4;
33
34 /**
35  * Defaults to 'x-hide-nosize'
36  * @static
37  * @type String
38  */
39 Ext.Element.visibilityCls = 'x-hide-nosize';
40
41 Ext.Element.addMethods(function(){
42     var El = Ext.Element,
43         OPACITY = "opacity",
44         VISIBILITY = "visibility",
45         DISPLAY = "display",
46         HIDDEN = "hidden",
47         OFFSETS = "offsets",
48         ASCLASS = "asclass",
49         NONE = "none",
50         NOSIZE = 'nosize',
51         ORIGINALDISPLAY = 'originalDisplay',
52         VISMODE = 'visibilityMode',
53         ISVISIBLE = 'isVisible',
54         data = El.data,
55         getDisplay = function(dom){
56             var d = data(dom, ORIGINALDISPLAY);
57             if(d === undefined){
58                 data(dom, ORIGINALDISPLAY, d = '');
59             }
60             return d;
61         },
62         getVisMode = function(dom){
63             var m = data(dom, VISMODE);
64             if(m === undefined){
65                 data(dom, VISMODE, m = 1);
66             }
67             return m;
68         };
69
70     return {
71         /**
72          * The element's default display mode  (defaults to "")
73          * @type String
74          */
75         originalDisplay : "",
76         visibilityMode : 1,
77
78         /**
79          * Sets the element's visibility mode. When setVisible() is called it
80          * will use this to determine whether to set the visibility or the display property.
81          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
82          * @return {Ext.Element} this
83          */
84         setVisibilityMode : function(visMode){
85             data(this.dom, VISMODE, visMode);
86             return this;
87         },
88
89         /**
90          * Perform custom animation on this element.
91          * <div><ul class="mdetail-params">
92          * <li><u>Animation Properties</u></li>
93          *
94          * <p>The Animation Control Object enables gradual transitions for any member of an
95          * element's style object that takes a numeric value including but not limited to
96          * these properties:</p><div><ul class="mdetail-params">
97          * <li><tt>bottom, top, left, right</tt></li>
98          * <li><tt>height, width</tt></li>
99          * <li><tt>margin, padding</tt></li>
100          * <li><tt>borderWidth</tt></li>
101          * <li><tt>opacity</tt></li>
102          * <li><tt>fontSize</tt></li>
103          * <li><tt>lineHeight</tt></li>
104          * </ul></div>
105          *
106          *
107          * <li><u>Animation Property Attributes</u></li>
108          *
109          * <p>Each Animation Property is a config object with optional properties:</p>
110          * <div><ul class="mdetail-params">
111          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>
112          * <li><tt>from</tt> : ignore current value, start from this value</li>
113          * <li><tt>to</tt>*  : start at current value, go to this value</li>
114          * <li><tt>unit</tt> : any allowable unit specification</li>
115          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
116          * </ul></div>
117          *
118          * <li><u>Animation Types</u></li>
119          *
120          * <p>The supported animation types:</p><div><ul class="mdetail-params">
121          * <li><tt>'run'</tt> : Default
122          * <pre><code>
123 var el = Ext.get('complexEl');
124 el.animate(
125     // animation control object
126     {
127         borderWidth: {to: 3, from: 0},
128         opacity: {to: .3, from: 1},
129         height: {to: 50, from: el.getHeight()},
130         width: {to: 300, from: el.getWidth()},
131         top  : {by: - 100, unit: 'px'},
132     },
133     0.35,      // animation duration
134     null,      // callback
135     'easeOut', // easing method
136     'run'      // animation type ('run','color','motion','scroll')
137 );
138          * </code></pre>
139          * </li>
140          * <li><tt>'color'</tt>
141          * <p>Animates transition of background, text, or border colors.</p>
142          * <pre><code>
143 el.animate(
144     // animation control object
145     {
146         color: { to: '#06e' },
147         backgroundColor: { to: '#e06' }
148     },
149     0.35,      // animation duration
150     null,      // callback
151     'easeOut', // easing method
152     'color'    // animation type ('run','color','motion','scroll')
153 );
154          * </code></pre>
155          * </li>
156          *
157          * <li><tt>'motion'</tt>
158          * <p>Animates the motion of an element to/from specific points using optional bezier
159          * way points during transit.</p>
160          * <pre><code>
161 el.animate(
162     // animation control object
163     {
164         borderWidth: {to: 3, from: 0},
165         opacity: {to: .3, from: 1},
166         height: {to: 50, from: el.getHeight()},
167         width: {to: 300, from: el.getWidth()},
168         top  : {by: - 100, unit: 'px'},
169         points: {
170             to: [50, 100],  // go to this point
171             control: [      // optional bezier way points
172                 [ 600, 800],
173                 [-100, 200]
174             ]
175         }
176     },
177     3000,      // animation duration (milliseconds!)
178     null,      // callback
179     'easeOut', // easing method
180     'motion'   // animation type ('run','color','motion','scroll')
181 );
182          * </code></pre>
183          * </li>
184          * <li><tt>'scroll'</tt>
185          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
186          * <pre><code>
187 el.animate(
188     // animation control object
189     {
190         scroll: {to: [400, 300]}
191     },
192     0.35,      // animation duration
193     null,      // callback
194     'easeOut', // easing method
195     'scroll'   // animation type ('run','color','motion','scroll')
196 );
197          * </code></pre>
198          * </li>
199          * </ul></div>
200          *
201          * </ul></div>
202          *
203          * @param {Object} args The animation control args
204          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
205          * @param {Function} onComplete (optional) Function to call when animation completes
206          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
207          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
208          * <tt>'motion'</tt>, or <tt>'scroll'</tt>
209          * @return {Ext.Element} this
210          */
211         animate : function(args, duration, onComplete, easing, animType){
212             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
213             return this;
214         },
215
216         /*
217          * @private Internal animation call
218          */
219         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
220             animType = animType || 'run';
221             opt = opt || {};
222             var me = this,
223                 anim = Ext.lib.Anim[animType](
224                     me.dom,
225                     args,
226                     (opt.duration || defaultDur) || .35,
227                     (opt.easing || defaultEase) || 'easeOut',
228                     function(){
229                         if(cb) cb.call(me);
230                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
231                     },
232                     me
233                 );
234             opt.anim = anim;
235             return anim;
236         },
237
238         // private legacy anim prep
239         preanim : function(a, i){
240             return !a[i] ? false : (typeof a[i] == 'object' ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
241         },
242
243         /**
244          * Checks whether the element is currently visible using both visibility and display properties.
245          * @return {Boolean} True if the element is currently visible, else false
246          */
247         isVisible : function() {
248             var me = this,
249                 dom = me.dom,
250                 visible = data(dom, ISVISIBLE);
251
252             if(typeof visible == 'boolean'){ //return the cached value if registered
253                 return visible;
254             }
255             //Determine the current state based on display states
256             visible = !me.isStyle(VISIBILITY, HIDDEN) &&
257                       !me.isStyle(DISPLAY, NONE) &&
258                       !((getVisMode(dom) == El.ASCLASS) && me.hasClass(me.visibilityCls || El.visibilityCls));
259
260             data(dom, ISVISIBLE, visible);
261             return visible;
262         },
263
264         /**
265          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
266          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
267          * @param {Boolean} visible Whether the element is visible
268          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
269          * @return {Ext.Element} this
270          */
271         setVisible : function(visible, animate){
272             var me = this, isDisplay, isVisibility, isOffsets, isNosize,
273                 dom = me.dom,
274                 visMode = getVisMode(dom);
275
276
277             // hideMode string override
278             if (typeof animate == 'string'){
279                 switch (animate) {
280                     case DISPLAY:
281                         visMode = El.DISPLAY;
282                         break;
283                     case VISIBILITY:
284                         visMode = El.VISIBILITY;
285                         break;
286                     case OFFSETS:
287                         visMode = El.OFFSETS;
288                         break;
289                     case NOSIZE:
290                     case ASCLASS:
291                         visMode = El.ASCLASS;
292                         break;
293                 }
294                 me.setVisibilityMode(visMode);
295                 animate = false;
296             }
297
298             if (!animate || !me.anim) {
299                 if(visMode == El.ASCLASS ){
300
301                     me[visible?'removeClass':'addClass'](me.visibilityCls || El.visibilityCls);
302
303                 } else if (visMode == El.DISPLAY){
304
305                     return me.setDisplayed(visible);
306
307                 } else if (visMode == El.OFFSETS){
308
309                     if (!visible){
310                         me.hideModeStyles = {
311                             position: me.getStyle('position'),
312                             top: me.getStyle('top'),
313                             left: me.getStyle('left')
314                         };
315                         me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
316                     } else {
317                         me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
318                         delete me.hideModeStyles;
319                     }
320
321                 }else{
322                     me.fixDisplay();
323                     dom.style.visibility = visible ? "visible" : HIDDEN;
324                 }
325             }else{
326                 // closure for composites
327                 if(visible){
328                     me.setOpacity(.01);
329                     me.setVisible(true);
330                 }
331                 me.anim({opacity: { to: (visible?1:0) }},
332                         me.preanim(arguments, 1),
333                         null,
334                         .35,
335                         'easeIn',
336                         function(){
337                             visible || me.setVisible(false).setOpacity(1);
338                         });
339             }
340             data(dom, ISVISIBLE, visible);  //set logical visibility state
341             return me;
342         },
343
344
345         /**
346          * @private
347          * Determine if the Element has a relevant height and width available based
348          * upon current logical visibility state
349          */
350         hasMetrics  : function(){
351             var dom = this.dom;
352             return this.isVisible() || (getVisMode(dom) == El.VISIBILITY);
353         },
354
355         /**
356          * Toggles the element's visibility or display, depending on visibility mode.
357          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
358          * @return {Ext.Element} this
359          */
360         toggle : function(animate){
361             var me = this;
362             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
363             return me;
364         },
365
366         /**
367          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
368          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
369          * @return {Ext.Element} this
370          */
371         setDisplayed : function(value) {
372             if(typeof value == "boolean"){
373                value = value ? getDisplay(this.dom) : NONE;
374             }
375             this.setStyle(DISPLAY, value);
376             return this;
377         },
378
379         // private
380         fixDisplay : function(){
381             var me = this;
382             if(me.isStyle(DISPLAY, NONE)){
383                 me.setStyle(VISIBILITY, HIDDEN);
384                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
385                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
386                     me.setStyle(DISPLAY, "block");
387                 }
388             }
389         },
390
391         /**
392          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
393          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
394          * @return {Ext.Element} this
395          */
396         hide : function(animate){
397             // hideMode override
398             if (typeof animate == 'string'){
399                 this.setVisible(false, animate);
400                 return this;
401             }
402             this.setVisible(false, this.preanim(arguments, 0));
403             return this;
404         },
405
406         /**
407         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
408         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
409          * @return {Ext.Element} this
410          */
411         show : function(animate){
412             // hideMode override
413             if (typeof animate == 'string'){
414                 this.setVisible(true, animate);
415                 return this;
416             }
417             this.setVisible(true, this.preanim(arguments, 0));
418             return this;
419         }
420     };
421 }());