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