MDL-26502 check browser - fix for Symbian (backported)
[moodle.git] / lib / yui / animation / animation.js
bloba198559008157fe9baa36dbd674d77aed12426b0
1 /*
2 Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.6.0
6 */
7 (function() {
9 var Y = YAHOO.util;
12 Copyright (c) 2006, Yahoo! Inc. All rights reserved.
13 Code licensed under the BSD License:
14 http://developer.yahoo.net/yui/license.txt
17 /**
18  * The animation module provides allows effects to be added to HTMLElements.
19  * @module animation
20  * @requires yahoo, event, dom
21  */
23 /**
24  *
25  * Base animation class that provides the interface for building animated effects.
26  * <p>Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);</p>
27  * @class Anim
28  * @namespace YAHOO.util
29  * @requires YAHOO.util.AnimMgr
30  * @requires YAHOO.util.Easing
31  * @requires YAHOO.util.Dom
32  * @requires YAHOO.util.Event
33  * @requires YAHOO.util.CustomEvent
34  * @constructor
35  * @param {String | HTMLElement} el Reference to the element that will be animated
36  * @param {Object} attributes The attribute(s) to be animated.  
37  * Each attribute is an object with at minimum a "to" or "by" member defined.  
38  * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").  
39  * All attribute names use camelCase.
40  * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
41  * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
42  */
44 var Anim = function(el, attributes, duration, method) {
45     if (!el) {
46     }
47     this.init(el, attributes, duration, method); 
50 Anim.NAME = 'Anim';
52 Anim.prototype = {
53     /**
54      * Provides a readable name for the Anim instance.
55      * @method toString
56      * @return {String}
57      */
58     toString: function() {
59         var el = this.getEl() || {};
60         var id = el.id || el.tagName;
61         return (this.constructor.NAME + ': ' + id);
62     },
63     
64     patterns: { // cached for performance
65         noNegatives:        /width|height|opacity|padding/i, // keep at zero or above
66         offsetAttribute:  /^((width|height)|(top|left))$/, // use offsetValue as default
67         defaultUnit:        /width|height|top$|bottom$|left$|right$/i, // use 'px' by default
68         offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset
69     },
70     
71     /**
72      * Returns the value computed by the animation's "method".
73      * @method doMethod
74      * @param {String} attr The name of the attribute.
75      * @param {Number} start The value this attribute should start from for this animation.
76      * @param {Number} end  The value this attribute should end at for this animation.
77      * @return {Number} The Value to be applied to the attribute.
78      */
79     doMethod: function(attr, start, end) {
80         return this.method(this.currentFrame, start, end - start, this.totalFrames);
81     },
82     
83     /**
84      * Applies a value to an attribute.
85      * @method setAttribute
86      * @param {String} attr The name of the attribute.
87      * @param {Number} val The value to be applied to the attribute.
88      * @param {String} unit The unit ('px', '%', etc.) of the value.
89      */
90     setAttribute: function(attr, val, unit) {
91         if ( this.patterns.noNegatives.test(attr) ) {
92             val = (val > 0) ? val : 0;
93         }
95         Y.Dom.setStyle(this.getEl(), attr, val + unit);
96     },                        
97     
98     /**
99      * Returns current value of the attribute.
100      * @method getAttribute
101      * @param {String} attr The name of the attribute.
102      * @return {Number} val The current value of the attribute.
103      */
104     getAttribute: function(attr) {
105         var el = this.getEl();
106         var val = Y.Dom.getStyle(el, attr);
108         if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
109             return parseFloat(val);
110         }
111         
112         var a = this.patterns.offsetAttribute.exec(attr) || [];
113         var pos = !!( a[3] ); // top or left
114         var box = !!( a[2] ); // width or height
115         
116         // use offsets for width/height and abs pos top/left
117         if ( box || (Y.Dom.getStyle(el, 'position') == 'absolute' && pos) ) {
118             val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
119         } else { // default to zero for other 'auto'
120             val = 0;
121         }
123         return val;
124     },
125     
126     /**
127      * Returns the unit to use when none is supplied.
128      * @method getDefaultUnit
129      * @param {attr} attr The name of the attribute.
130      * @return {String} The default unit to be used.
131      */
132     getDefaultUnit: function(attr) {
133          if ( this.patterns.defaultUnit.test(attr) ) {
134             return 'px';
135          }
136          
137          return '';
138     },
139         
140     /**
141      * Sets the actual values to be used during the animation.  Should only be needed for subclass use.
142      * @method setRuntimeAttribute
143      * @param {Object} attr The attribute object
144      * @private 
145      */
146     setRuntimeAttribute: function(attr) {
147         var start;
148         var end;
149         var attributes = this.attributes;
151         this.runtimeAttributes[attr] = {};
152         
153         var isset = function(prop) {
154             return (typeof prop !== 'undefined');
155         };
156         
157         if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) {
158             return false; // note return; nothing to animate to
159         }
160         
161         start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
163         // To beats by, per SMIL 2.1 spec
164         if ( isset(attributes[attr]['to']) ) {
165             end = attributes[attr]['to'];
166         } else if ( isset(attributes[attr]['by']) ) {
167             if (start.constructor == Array) {
168                 end = [];
169                 for (var i = 0, len = start.length; i < len; ++i) {
170                     end[i] = start[i] + attributes[attr]['by'][i] * 1; // times 1 to cast "by" 
171                 }
172             } else {
173                 end = start + attributes[attr]['by'] * 1;
174             }
175         }
176         
177         this.runtimeAttributes[attr].start = start;
178         this.runtimeAttributes[attr].end = end;
180         // set units if needed
181         this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ?
182                 attributes[attr]['unit'] : this.getDefaultUnit(attr);
183         return true;
184     },
186     /**
187      * Constructor for Anim instance.
188      * @method init
189      * @param {String | HTMLElement} el Reference to the element that will be animated
190      * @param {Object} attributes The attribute(s) to be animated.  
191      * Each attribute is an object with at minimum a "to" or "by" member defined.  
192      * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").  
193      * All attribute names use camelCase.
194      * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
195      * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
196      */ 
197     init: function(el, attributes, duration, method) {
198         /**
199          * Whether or not the animation is running.
200          * @property isAnimated
201          * @private
202          * @type Boolean
203          */
204         var isAnimated = false;
205         
206         /**
207          * A Date object that is created when the animation begins.
208          * @property startTime
209          * @private
210          * @type Date
211          */
212         var startTime = null;
213         
214         /**
215          * The number of frames this animation was able to execute.
216          * @property actualFrames
217          * @private
218          * @type Int
219          */
220         var actualFrames = 0; 
222         /**
223          * The element to be animated.
224          * @property el
225          * @private
226          * @type HTMLElement
227          */
228         el = Y.Dom.get(el);
229         
230         /**
231          * The collection of attributes to be animated.  
232          * Each attribute must have at least a "to" or "by" defined in order to animate.  
233          * If "to" is supplied, the animation will end with the attribute at that value.  
234          * If "by" is supplied, the animation will end at that value plus its starting value. 
235          * If both are supplied, "to" is used, and "by" is ignored. 
236          * Optional additional member include "from" (the value the attribute should start animating from, defaults to current value), and "unit" (the units to apply to the values).
237          * @property attributes
238          * @type Object
239          */
240         this.attributes = attributes || {};
241         
242         /**
243          * The length of the animation.  Defaults to "1" (second).
244          * @property duration
245          * @type Number
246          */
247         this.duration = !YAHOO.lang.isUndefined(duration) ? duration : 1;
248         
249         /**
250          * The method that will provide values to the attribute(s) during the animation. 
251          * Defaults to "YAHOO.util.Easing.easeNone".
252          * @property method
253          * @type Function
254          */
255         this.method = method || Y.Easing.easeNone;
257         /**
258          * Whether or not the duration should be treated as seconds.
259          * Defaults to true.
260          * @property useSeconds
261          * @type Boolean
262          */
263         this.useSeconds = true; // default to seconds
264         
265         /**
266          * The location of the current animation on the timeline.
267          * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
268          * @property currentFrame
269          * @type Int
270          */
271         this.currentFrame = 0;
272         
273         /**
274          * The total number of frames to be executed.
275          * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
276          * @property totalFrames
277          * @type Int
278          */
279         this.totalFrames = Y.AnimMgr.fps;
280         
281         /**
282          * Changes the animated element
283          * @method setEl
284          */
285         this.setEl = function(element) {
286             el = Y.Dom.get(element);
287         };
288         
289         /**
290          * Returns a reference to the animated element.
291          * @method getEl
292          * @return {HTMLElement}
293          */
294         this.getEl = function() { return el; };
295         
296         /**
297          * Checks whether the element is currently animated.
298          * @method isAnimated
299          * @return {Boolean} current value of isAnimated.     
300          */
301         this.isAnimated = function() {
302             return isAnimated;
303         };
304         
305         /**
306          * Returns the animation start time.
307          * @method getStartTime
308          * @return {Date} current value of startTime.      
309          */
310         this.getStartTime = function() {
311             return startTime;
312         };        
313         
314         this.runtimeAttributes = {};
315         
316         
317         
318         /**
319          * Starts the animation by registering it with the animation manager. 
320          * @method animate  
321          */
322         this.animate = function() {
323             if ( this.isAnimated() ) {
324                 return false;
325             }
326             
327             this.currentFrame = 0;
328             
329             this.totalFrames = ( this.useSeconds ) ? Math.ceil(Y.AnimMgr.fps * this.duration) : this.duration;
330     
331             if (this.duration === 0 && this.useSeconds) { // jump to last frame if zero second duration 
332                 this.totalFrames = 1; 
333             }
334             Y.AnimMgr.registerElement(this);
335             return true;
336         };
337           
338         /**
339          * Stops the animation.  Normally called by AnimMgr when animation completes.
340          * @method stop
341          * @param {Boolean} finish (optional) If true, animation will jump to final frame.
342          */ 
343         this.stop = function(finish) {
344             if (!this.isAnimated()) { // nothing to stop
345                 return false;
346             }
348             if (finish) {
349                  this.currentFrame = this.totalFrames;
350                  this._onTween.fire();
351             }
352             Y.AnimMgr.stop(this);
353         };
354         
355         var onStart = function() {            
356             this.onStart.fire();
357             
358             this.runtimeAttributes = {};
359             for (var attr in this.attributes) {
360                 this.setRuntimeAttribute(attr);
361             }
362             
363             isAnimated = true;
364             actualFrames = 0;
365             startTime = new Date(); 
366         };
367         
368         /**
369          * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s).
370          * @private
371          */
372          
373         var onTween = function() {
374             var data = {
375                 duration: new Date() - this.getStartTime(),
376                 currentFrame: this.currentFrame
377             };
378             
379             data.toString = function() {
380                 return (
381                     'duration: ' + data.duration +
382                     ', currentFrame: ' + data.currentFrame
383                 );
384             };
385             
386             this.onTween.fire(data);
387             
388             var runtimeAttributes = this.runtimeAttributes;
389             
390             for (var attr in runtimeAttributes) {
391                 this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit); 
392             }
393             
394             actualFrames += 1;
395         };
396         
397         var onComplete = function() {
398             var actual_duration = (new Date() - startTime) / 1000 ;
399             
400             var data = {
401                 duration: actual_duration,
402                 frames: actualFrames,
403                 fps: actualFrames / actual_duration
404             };
405             
406             data.toString = function() {
407                 return (
408                     'duration: ' + data.duration +
409                     ', frames: ' + data.frames +
410                     ', fps: ' + data.fps
411                 );
412             };
413             
414             isAnimated = false;
415             actualFrames = 0;
416             this.onComplete.fire(data);
417         };
418         
419         /**
420          * Custom event that fires after onStart, useful in subclassing
421          * @private
422          */    
423         this._onStart = new Y.CustomEvent('_start', this, true);
425         /**
426          * Custom event that fires when animation begins
427          * Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction)
428          * @event onStart
429          */    
430         this.onStart = new Y.CustomEvent('start', this);
431         
432         /**
433          * Custom event that fires between each frame
434          * Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction)
435          * @event onTween
436          */
437         this.onTween = new Y.CustomEvent('tween', this);
438         
439         /**
440          * Custom event that fires after onTween
441          * @private
442          */
443         this._onTween = new Y.CustomEvent('_tween', this, true);
444         
445         /**
446          * Custom event that fires when animation ends
447          * Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction)
448          * @event onComplete
449          */
450         this.onComplete = new Y.CustomEvent('complete', this);
451         /**
452          * Custom event that fires after onComplete
453          * @private
454          */
455         this._onComplete = new Y.CustomEvent('_complete', this, true);
457         this._onStart.subscribe(onStart);
458         this._onTween.subscribe(onTween);
459         this._onComplete.subscribe(onComplete);
460     }
463     Y.Anim = Anim;
464 })();
466  * Handles animation queueing and threading.
467  * Used by Anim and subclasses.
468  * @class AnimMgr
469  * @namespace YAHOO.util
470  */
471 YAHOO.util.AnimMgr = new function() {
472     /** 
473      * Reference to the animation Interval.
474      * @property thread
475      * @private
476      * @type Int
477      */
478     var thread = null;
479     
480     /** 
481      * The current queue of registered animation objects.
482      * @property queue
483      * @private
484      * @type Array
485      */    
486     var queue = [];
488     /** 
489      * The number of active animations.
490      * @property tweenCount
491      * @private
492      * @type Int
493      */        
494     var tweenCount = 0;
496     /** 
497      * Base frame rate (frames per second). 
498      * Arbitrarily high for better x-browser calibration (slower browsers drop more frames).
499      * @property fps
500      * @type Int
501      * 
502      */
503     this.fps = 1000;
505     /** 
506      * Interval delay in milliseconds, defaults to fastest possible.
507      * @property delay
508      * @type Int
509      * 
510      */
511     this.delay = 1;
513     /**
514      * Adds an animation instance to the animation queue.
515      * All animation instances must be registered in order to animate.
516      * @method registerElement
517      * @param {object} tween The Anim instance to be be registered
518      */
519     this.registerElement = function(tween) {
520         queue[queue.length] = tween;
521         tweenCount += 1;
522         tween._onStart.fire();
523         this.start();
524     };
525     
526     /**
527      * removes an animation instance from the animation queue.
528      * All animation instances must be registered in order to animate.
529      * @method unRegister
530      * @param {object} tween The Anim instance to be be registered
531      * @param {Int} index The index of the Anim instance
532      * @private
533      */
534     this.unRegister = function(tween, index) {
535         index = index || getIndex(tween);
536         if (!tween.isAnimated() || index == -1) {
537             return false;
538         }
539         
540         tween._onComplete.fire();
541         queue.splice(index, 1);
543         tweenCount -= 1;
544         if (tweenCount <= 0) {
545             this.stop();
546         }
548         return true;
549     };
550     
551     /**
552      * Starts the animation thread.
553         * Only one thread can run at a time.
554      * @method start
555      */    
556     this.start = function() {
557         if (thread === null) {
558             thread = setInterval(this.run, this.delay);
559         }
560     };
562     /**
563      * Stops the animation thread or a specific animation instance.
564      * @method stop
565      * @param {object} tween A specific Anim instance to stop (optional)
566      * If no instance given, Manager stops thread and all animations.
567      */    
568     this.stop = function(tween) {
569         if (!tween) {
570             clearInterval(thread);
571             
572             for (var i = 0, len = queue.length; i < len; ++i) {
573                 this.unRegister(queue[0], 0);  
574             }
576             queue = [];
577             thread = null;
578             tweenCount = 0;
579         }
580         else {
581             this.unRegister(tween);
582         }
583     };
584     
585     /**
586      * Called per Interval to handle each animation frame.
587      * @method run
588      */    
589     this.run = function() {
590         for (var i = 0, len = queue.length; i < len; ++i) {
591             var tween = queue[i];
592             if ( !tween || !tween.isAnimated() ) { continue; }
594             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
595             {
596                 tween.currentFrame += 1;
597                 
598                 if (tween.useSeconds) {
599                     correctFrame(tween);
600                 }
601                 tween._onTween.fire();          
602             }
603             else { YAHOO.util.AnimMgr.stop(tween, i); }
604         }
605     };
606     
607     var getIndex = function(anim) {
608         for (var i = 0, len = queue.length; i < len; ++i) {
609             if (queue[i] == anim) {
610                 return i; // note return;
611             }
612         }
613         return -1;
614     };
615     
616     /**
617      * On the fly frame correction to keep animation on time.
618      * @method correctFrame
619      * @private
620      * @param {Object} tween The Anim instance being corrected.
621      */
622     var correctFrame = function(tween) {
623         var frames = tween.totalFrames;
624         var frame = tween.currentFrame;
625         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
626         var elapsed = (new Date() - tween.getStartTime());
627         var tweak = 0;
628         
629         if (elapsed < tween.duration * 1000) { // check if falling behind
630             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
631         } else { // went over duration, so jump to end
632             tweak = frames - (frame + 1); 
633         }
634         if (tweak > 0 && isFinite(tweak)) { // adjust if needed
635             if (tween.currentFrame + tweak >= frames) {// dont go past last frame
636                 tweak = frames - (frame + 1);
637             }
638             
639             tween.currentFrame += tweak;      
640         }
641     };
644  * Used to calculate Bezier splines for any number of control points.
645  * @class Bezier
646  * @namespace YAHOO.util
648  */
649 YAHOO.util.Bezier = new function() {
650     /**
651      * Get the current position of the animated element based on t.
652      * Each point is an array of "x" and "y" values (0 = x, 1 = y)
653      * At least 2 points are required (start and end).
654      * First point is start. Last point is end.
655      * Additional control points are optional.     
656      * @method getPosition
657      * @param {Array} points An array containing Bezier points
658      * @param {Number} t A number between 0 and 1 which is the basis for determining current position
659      * @return {Array} An array containing int x and y member data
660      */
661     this.getPosition = function(points, t) {  
662         var n = points.length;
663         var tmp = [];
665         for (var i = 0; i < n; ++i){
666             tmp[i] = [points[i][0], points[i][1]]; // save input
667         }
668         
669         for (var j = 1; j < n; ++j) {
670             for (i = 0; i < n - j; ++i) {
671                 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
672                 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1]; 
673             }
674         }
675     
676         return [ tmp[0][0], tmp[0][1] ]; 
677     
678     };
680 (function() {
682  * Anim subclass for color transitions.
683  * <p>Usage: <code>var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut);</code> Color values can be specified with either 112233, #112233, 
684  * [255,255,255], or rgb(255,255,255)</p>
685  * @class ColorAnim
686  * @namespace YAHOO.util
687  * @requires YAHOO.util.Anim
688  * @requires YAHOO.util.AnimMgr
689  * @requires YAHOO.util.Easing
690  * @requires YAHOO.util.Bezier
691  * @requires YAHOO.util.Dom
692  * @requires YAHOO.util.Event
693  * @constructor
694  * @extends YAHOO.util.Anim
695  * @param {HTMLElement | String} el Reference to the element that will be animated
696  * @param {Object} attributes The attribute(s) to be animated.
697  * Each attribute is an object with at minimum a "to" or "by" member defined.
698  * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
699  * All attribute names use camelCase.
700  * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
701  * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
702  */
703     var ColorAnim = function(el, attributes, duration,  method) {
704         ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
705     };
706     
707     ColorAnim.NAME = 'ColorAnim';
709     ColorAnim.DEFAULT_BGCOLOR = '#fff';
710     // shorthand
711     var Y = YAHOO.util;
712     YAHOO.extend(ColorAnim, Y.Anim);
714     var superclass = ColorAnim.superclass;
715     var proto = ColorAnim.prototype;
716     
717     proto.patterns.color = /color$/i;
718     proto.patterns.rgb            = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
719     proto.patterns.hex            = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
720     proto.patterns.hex3          = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
721     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/; // need rgba for safari
722     
723     /**
724      * Attempts to parse the given string and return a 3-tuple.
725      * @method parseColor
726      * @param {String} s The string to parse.
727      * @return {Array} The 3-tuple of rgb values.
728      */
729     proto.parseColor = function(s) {
730         if (s.length == 3) { return s; }
731     
732         var c = this.patterns.hex.exec(s);
733         if (c && c.length == 4) {
734             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
735         }
736     
737         c = this.patterns.rgb.exec(s);
738         if (c && c.length == 4) {
739             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
740         }
741     
742         c = this.patterns.hex3.exec(s);
743         if (c && c.length == 4) {
744             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
745         }
746         
747         return null;
748     };
750     proto.getAttribute = function(attr) {
751         var el = this.getEl();
752         if (this.patterns.color.test(attr) ) {
753             var val = YAHOO.util.Dom.getStyle(el, attr);
754             
755             var that = this;
756             if (this.patterns.transparent.test(val)) { // bgcolor default
757                 var parent = YAHOO.util.Dom.getAncestorBy(el, function(node) {
758                     return !that.patterns.transparent.test(val);
759                 });
761                 if (parent) {
762                     val = Y.Dom.getStyle(parent, attr);
763                 } else {
764                     val = ColorAnim.DEFAULT_BGCOLOR;
765                 }
766             }
767         } else {
768             val = superclass.getAttribute.call(this, attr);
769         }
771         return val;
772     };
773     
774     proto.doMethod = function(attr, start, end) {
775         var val;
776     
777         if ( this.patterns.color.test(attr) ) {
778             val = [];
779             for (var i = 0, len = start.length; i < len; ++i) {
780                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
781             }
782             
783             val = 'rgb('+Math.floor(val[0])+','+Math.floor(val[1])+','+Math.floor(val[2])+')';
784         }
785         else {
786             val = superclass.doMethod.call(this, attr, start, end);
787         }
789         return val;
790     };
792     proto.setRuntimeAttribute = function(attr) {
793         superclass.setRuntimeAttribute.call(this, attr);
794         
795         if ( this.patterns.color.test(attr) ) {
796             var attributes = this.attributes;
797             var start = this.parseColor(this.runtimeAttributes[attr].start);
798             var end = this.parseColor(this.runtimeAttributes[attr].end);
799             // fix colors if going "by"
800             if ( typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined' ) {
801                 end = this.parseColor(attributes[attr].by);
802             
803                 for (var i = 0, len = start.length; i < len; ++i) {
804                     end[i] = start[i] + end[i];
805                 }
806             }
807             
808             this.runtimeAttributes[attr].start = start;
809             this.runtimeAttributes[attr].end = end;
810         }
811     };
813     Y.ColorAnim = ColorAnim;
814 })();
816 TERMS OF USE - EASING EQUATIONS
817 Open source under the BSD License.
818 Copyright 2001 Robert Penner All rights reserved.
820 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
822  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
823  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
824  * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
826 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
830  * Singleton that determines how an animation proceeds from start to end.
831  * @class Easing
832  * @namespace YAHOO.util
835 YAHOO.util.Easing = {
837     /**
838      * Uniform speed between points.
839      * @method easeNone
840      * @param {Number} t Time value used to compute current value
841      * @param {Number} b Starting value
842      * @param {Number} c Delta between start and end values
843      * @param {Number} d Total length of animation
844      * @return {Number} The computed value for the current animation frame
845      */
846     easeNone: function (t, b, c, d) {
847         return c*t/d + b;
848     },
849     
850     /**
851      * Begins slowly and accelerates towards end.
852      * @method easeIn
853      * @param {Number} t Time value used to compute current value
854      * @param {Number} b Starting value
855      * @param {Number} c Delta between start and end values
856      * @param {Number} d Total length of animation
857      * @return {Number} The computed value for the current animation frame
858      */
859     easeIn: function (t, b, c, d) {
860         return c*(t/=d)*t + b;
861     },
863     /**
864      * Begins quickly and decelerates towards end.
865      * @method easeOut
866      * @param {Number} t Time value used to compute current value
867      * @param {Number} b Starting value
868      * @param {Number} c Delta between start and end values
869      * @param {Number} d Total length of animation
870      * @return {Number} The computed value for the current animation frame
871      */
872     easeOut: function (t, b, c, d) {
873         return -c *(t/=d)*(t-2) + b;
874     },
875     
876     /**
877      * Begins slowly and decelerates towards end.
878      * @method easeBoth
879      * @param {Number} t Time value used to compute current value
880      * @param {Number} b Starting value
881      * @param {Number} c Delta between start and end values
882      * @param {Number} d Total length of animation
883      * @return {Number} The computed value for the current animation frame
884      */
885     easeBoth: function (t, b, c, d) {
886         if ((t/=d/2) < 1) {
887             return c/2*t*t + b;
888         }
889         
890         return -c/2 * ((--t)*(t-2) - 1) + b;
891     },
892     
893     /**
894      * Begins slowly and accelerates towards end.
895      * @method easeInStrong
896      * @param {Number} t Time value used to compute current value
897      * @param {Number} b Starting value
898      * @param {Number} c Delta between start and end values
899      * @param {Number} d Total length of animation
900      * @return {Number} The computed value for the current animation frame
901      */
902     easeInStrong: function (t, b, c, d) {
903         return c*(t/=d)*t*t*t + b;
904     },
905     
906     /**
907      * Begins quickly and decelerates towards end.
908      * @method easeOutStrong
909      * @param {Number} t Time value used to compute current value
910      * @param {Number} b Starting value
911      * @param {Number} c Delta between start and end values
912      * @param {Number} d Total length of animation
913      * @return {Number} The computed value for the current animation frame
914      */
915     easeOutStrong: function (t, b, c, d) {
916         return -c * ((t=t/d-1)*t*t*t - 1) + b;
917     },
918     
919     /**
920      * Begins slowly and decelerates towards end.
921      * @method easeBothStrong
922      * @param {Number} t Time value used to compute current value
923      * @param {Number} b Starting value
924      * @param {Number} c Delta between start and end values
925      * @param {Number} d Total length of animation
926      * @return {Number} The computed value for the current animation frame
927      */
928     easeBothStrong: function (t, b, c, d) {
929         if ((t/=d/2) < 1) {
930             return c/2*t*t*t*t + b;
931         }
932         
933         return -c/2 * ((t-=2)*t*t*t - 2) + b;
934     },
936     /**
937      * Snap in elastic effect.
938      * @method elasticIn
939      * @param {Number} t Time value used to compute current value
940      * @param {Number} b Starting value
941      * @param {Number} c Delta between start and end values
942      * @param {Number} d Total length of animation
943      * @param {Number} a Amplitude (optional)
944      * @param {Number} p Period (optional)
945      * @return {Number} The computed value for the current animation frame
946      */
948     elasticIn: function (t, b, c, d, a, p) {
949         if (t == 0) {
950             return b;
951         }
952         if ( (t /= d) == 1 ) {
953             return b+c;
954         }
955         if (!p) {
956             p=d*.3;
957         }
958         
959         if (!a || a < Math.abs(c)) {
960             a = c; 
961             var s = p/4;
962         }
963         else {
964             var s = p/(2*Math.PI) * Math.asin (c/a);
965         }
966         
967         return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
968     },
970     /**
971      * Snap out elastic effect.
972      * @method elasticOut
973      * @param {Number} t Time value used to compute current value
974      * @param {Number} b Starting value
975      * @param {Number} c Delta between start and end values
976      * @param {Number} d Total length of animation
977      * @param {Number} a Amplitude (optional)
978      * @param {Number} p Period (optional)
979      * @return {Number} The computed value for the current animation frame
980      */
981     elasticOut: function (t, b, c, d, a, p) {
982         if (t == 0) {
983             return b;
984         }
985         if ( (t /= d) == 1 ) {
986             return b+c;
987         }
988         if (!p) {
989             p=d*.3;
990         }
991         
992         if (!a || a < Math.abs(c)) {
993             a = c;
994             var s = p / 4;
995         }
996         else {
997             var s = p/(2*Math.PI) * Math.asin (c/a);
998         }
999         
1000         return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
1001     },
1002     
1003     /**
1004      * Snap both elastic effect.
1005      * @method elasticBoth
1006      * @param {Number} t Time value used to compute current value
1007      * @param {Number} b Starting value
1008      * @param {Number} c Delta between start and end values
1009      * @param {Number} d Total length of animation
1010      * @param {Number} a Amplitude (optional)
1011      * @param {Number} p Period (optional)
1012      * @return {Number} The computed value for the current animation frame
1013      */
1014     elasticBoth: function (t, b, c, d, a, p) {
1015         if (t == 0) {
1016             return b;
1017         }
1018         
1019         if ( (t /= d/2) == 2 ) {
1020             return b+c;
1021         }
1022         
1023         if (!p) {
1024             p = d*(.3*1.5);
1025         }
1026         
1027         if ( !a || a < Math.abs(c) ) {
1028             a = c; 
1029             var s = p/4;
1030         }
1031         else {
1032             var s = p/(2*Math.PI) * Math.asin (c/a);
1033         }
1034         
1035         if (t < 1) {
1036             return -.5*(a*Math.pow(2,10*(t-=1)) * 
1037                     Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
1038         }
1039         return a*Math.pow(2,-10*(t-=1)) * 
1040                 Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
1041     },
1044     /**
1045      * Backtracks slightly, then reverses direction and moves to end.
1046      * @method backIn
1047      * @param {Number} t Time value used to compute current value
1048      * @param {Number} b Starting value
1049      * @param {Number} c Delta between start and end values
1050      * @param {Number} d Total length of animation
1051      * @param {Number} s Overshoot (optional)
1052      * @return {Number} The computed value for the current animation frame
1053      */
1054     backIn: function (t, b, c, d, s) {
1055         if (typeof s == 'undefined') {
1056             s = 1.70158;
1057         }
1058         return c*(t/=d)*t*((s+1)*t - s) + b;
1059     },
1061     /**
1062      * Overshoots end, then reverses and comes back to end.
1063      * @method backOut
1064      * @param {Number} t Time value used to compute current value
1065      * @param {Number} b Starting value
1066      * @param {Number} c Delta between start and end values
1067      * @param {Number} d Total length of animation
1068      * @param {Number} s Overshoot (optional)
1069      * @return {Number} The computed value for the current animation frame
1070      */
1071     backOut: function (t, b, c, d, s) {
1072         if (typeof s == 'undefined') {
1073             s = 1.70158;
1074         }
1075         return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
1076     },
1077     
1078     /**
1079      * Backtracks slightly, then reverses direction, overshoots end, 
1080      * then reverses and comes back to end.
1081      * @method backBoth
1082      * @param {Number} t Time value used to compute current value
1083      * @param {Number} b Starting value
1084      * @param {Number} c Delta between start and end values
1085      * @param {Number} d Total length of animation
1086      * @param {Number} s Overshoot (optional)
1087      * @return {Number} The computed value for the current animation frame
1088      */
1089     backBoth: function (t, b, c, d, s) {
1090         if (typeof s == 'undefined') {
1091             s = 1.70158; 
1092         }
1093         
1094         if ((t /= d/2 ) < 1) {
1095             return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
1096         }
1097         return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
1098     },
1100     /**
1101      * Bounce off of start.
1102      * @method bounceIn
1103      * @param {Number} t Time value used to compute current value
1104      * @param {Number} b Starting value
1105      * @param {Number} c Delta between start and end values
1106      * @param {Number} d Total length of animation
1107      * @return {Number} The computed value for the current animation frame
1108      */
1109     bounceIn: function (t, b, c, d) {
1110         return c - YAHOO.util.Easing.bounceOut(d-t, 0, c, d) + b;
1111     },
1112     
1113     /**
1114      * Bounces off end.
1115      * @method bounceOut
1116      * @param {Number} t Time value used to compute current value
1117      * @param {Number} b Starting value
1118      * @param {Number} c Delta between start and end values
1119      * @param {Number} d Total length of animation
1120      * @return {Number} The computed value for the current animation frame
1121      */
1122     bounceOut: function (t, b, c, d) {
1123         if ((t/=d) < (1/2.75)) {
1124                 return c*(7.5625*t*t) + b;
1125         } else if (t < (2/2.75)) {
1126                 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
1127         } else if (t < (2.5/2.75)) {
1128                 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
1129         }
1130         return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
1131     },
1132     
1133     /**
1134      * Bounces off start and end.
1135      * @method bounceBoth
1136      * @param {Number} t Time value used to compute current value
1137      * @param {Number} b Starting value
1138      * @param {Number} c Delta between start and end values
1139      * @param {Number} d Total length of animation
1140      * @return {Number} The computed value for the current animation frame
1141      */
1142     bounceBoth: function (t, b, c, d) {
1143         if (t < d/2) {
1144             return YAHOO.util.Easing.bounceIn(t*2, 0, c, d) * .5 + b;
1145         }
1146         return YAHOO.util.Easing.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
1147     }
1150 (function() {
1152  * Anim subclass for moving elements along a path defined by the "points" 
1153  * member of "attributes".  All "points" are arrays with x, y coordinates.
1154  * <p>Usage: <code>var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p>
1155  * @class Motion
1156  * @namespace YAHOO.util
1157  * @requires YAHOO.util.Anim
1158  * @requires YAHOO.util.AnimMgr
1159  * @requires YAHOO.util.Easing
1160  * @requires YAHOO.util.Bezier
1161  * @requires YAHOO.util.Dom
1162  * @requires YAHOO.util.Event
1163  * @requires YAHOO.util.CustomEvent 
1164  * @constructor
1165  * @extends YAHOO.util.ColorAnim
1166  * @param {String | HTMLElement} el Reference to the element that will be animated
1167  * @param {Object} attributes The attribute(s) to be animated.  
1168  * Each attribute is an object with at minimum a "to" or "by" member defined.  
1169  * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").  
1170  * All attribute names use camelCase.
1171  * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
1172  * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
1173  */
1174     var Motion = function(el, attributes, duration,  method) {
1175         if (el) { // dont break existing subclasses not using YAHOO.extend
1176             Motion.superclass.constructor.call(this, el, attributes, duration, method);
1177         }
1178     };
1181     Motion.NAME = 'Motion';
1183     // shorthand
1184     var Y = YAHOO.util;
1185     YAHOO.extend(Motion, Y.ColorAnim);
1186     
1187     var superclass = Motion.superclass;
1188     var proto = Motion.prototype;
1190     proto.patterns.points = /^points$/i;
1191     
1192     proto.setAttribute = function(attr, val, unit) {
1193         if (  this.patterns.points.test(attr) ) {
1194             unit = unit || 'px';
1195             superclass.setAttribute.call(this, 'left', val[0], unit);
1196             superclass.setAttribute.call(this, 'top', val[1], unit);
1197         } else {
1198             superclass.setAttribute.call(this, attr, val, unit);
1199         }
1200     };
1202     proto.getAttribute = function(attr) {
1203         if (  this.patterns.points.test(attr) ) {
1204             var val = [
1205                 superclass.getAttribute.call(this, 'left'),
1206                 superclass.getAttribute.call(this, 'top')
1207             ];
1208         } else {
1209             val = superclass.getAttribute.call(this, attr);
1210         }
1212         return val;
1213     };
1215     proto.doMethod = function(attr, start, end) {
1216         var val = null;
1218         if ( this.patterns.points.test(attr) ) {
1219             var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;                             
1220             val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
1221         } else {
1222             val = superclass.doMethod.call(this, attr, start, end);
1223         }
1224         return val;
1225     };
1227     proto.setRuntimeAttribute = function(attr) {
1228         if ( this.patterns.points.test(attr) ) {
1229             var el = this.getEl();
1230             var attributes = this.attributes;
1231             var start;
1232             var control = attributes['points']['control'] || [];
1233             var end;
1234             var i, len;
1235             
1236             if (control.length > 0 && !(control[0] instanceof Array) ) { // could be single point or array of points
1237                 control = [control];
1238             } else { // break reference to attributes.points.control
1239                 var tmp = []; 
1240                 for (i = 0, len = control.length; i< len; ++i) {
1241                     tmp[i] = control[i];
1242                 }
1243                 control = tmp;
1244             }
1246             if (Y.Dom.getStyle(el, 'position') == 'static') { // default to relative
1247                 Y.Dom.setStyle(el, 'position', 'relative');
1248             }
1249     
1250             if ( isset(attributes['points']['from']) ) {
1251                 Y.Dom.setXY(el, attributes['points']['from']); // set position to from point
1252             } 
1253             else { Y.Dom.setXY( el, Y.Dom.getXY(el) ); } // set it to current position
1254             
1255             start = this.getAttribute('points'); // get actual top & left
1256             
1257             // TO beats BY, per SMIL 2.1 spec
1258             if ( isset(attributes['points']['to']) ) {
1259                 end = translateValues.call(this, attributes['points']['to'], start);
1260                 
1261                 var pageXY = Y.Dom.getXY(this.getEl());
1262                 for (i = 0, len = control.length; i < len; ++i) {
1263                     control[i] = translateValues.call(this, control[i], start);
1264                 }
1266                 
1267             } else if ( isset(attributes['points']['by']) ) {
1268                 end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
1269                 
1270                 for (i = 0, len = control.length; i < len; ++i) {
1271                     control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
1272                 }
1273             }
1275             this.runtimeAttributes[attr] = [start];
1276             
1277             if (control.length > 0) {
1278                 this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control); 
1279             }
1281             this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
1282         }
1283         else {
1284             superclass.setRuntimeAttribute.call(this, attr);
1285         }
1286     };
1287     
1288     var translateValues = function(val, start) {
1289         var pageXY = Y.Dom.getXY(this.getEl());
1290         val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
1292         return val; 
1293     };
1294     
1295     var isset = function(prop) {
1296         return (typeof prop !== 'undefined');
1297     };
1299     Y.Motion = Motion;
1300 })();
1301 (function() {
1303  * Anim subclass for scrolling elements to a position defined by the "scroll"
1304  * member of "attributes".  All "scroll" members are arrays with x, y scroll positions.
1305  * <p>Usage: <code>var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p>
1306  * @class Scroll
1307  * @namespace YAHOO.util
1308  * @requires YAHOO.util.Anim
1309  * @requires YAHOO.util.AnimMgr
1310  * @requires YAHOO.util.Easing
1311  * @requires YAHOO.util.Bezier
1312  * @requires YAHOO.util.Dom
1313  * @requires YAHOO.util.Event
1314  * @requires YAHOO.util.CustomEvent 
1315  * @extends YAHOO.util.ColorAnim
1316  * @constructor
1317  * @param {String or HTMLElement} el Reference to the element that will be animated
1318  * @param {Object} attributes The attribute(s) to be animated.  
1319  * Each attribute is an object with at minimum a "to" or "by" member defined.  
1320  * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").  
1321  * All attribute names use camelCase.
1322  * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
1323  * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
1324  */
1325     var Scroll = function(el, attributes, duration,  method) {
1326         if (el) { // dont break existing subclasses not using YAHOO.extend
1327             Scroll.superclass.constructor.call(this, el, attributes, duration, method);
1328         }
1329     };
1331     Scroll.NAME = 'Scroll';
1333     // shorthand
1334     var Y = YAHOO.util;
1335     YAHOO.extend(Scroll, Y.ColorAnim);
1336     
1337     var superclass = Scroll.superclass;
1338     var proto = Scroll.prototype;
1340     proto.doMethod = function(attr, start, end) {
1341         var val = null;
1342     
1343         if (attr == 'scroll') {
1344             val = [
1345                 this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
1346                 this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
1347             ];
1348             
1349         } else {
1350             val = superclass.doMethod.call(this, attr, start, end);
1351         }
1352         return val;
1353     };
1355     proto.getAttribute = function(attr) {
1356         var val = null;
1357         var el = this.getEl();
1358         
1359         if (attr == 'scroll') {
1360             val = [ el.scrollLeft, el.scrollTop ];
1361         } else {
1362             val = superclass.getAttribute.call(this, attr);
1363         }
1364         
1365         return val;
1366     };
1368     proto.setAttribute = function(attr, val, unit) {
1369         var el = this.getEl();
1370         
1371         if (attr == 'scroll') {
1372             el.scrollLeft = val[0];
1373             el.scrollTop = val[1];
1374         } else {
1375             superclass.setAttribute.call(this, attr, val, unit);
1376         }
1377     };
1379     Y.Scroll = Scroll;
1380 })();
1381 YAHOO.register("animation", YAHOO.util.Anim, {version: "2.6.0", build: "1321"});