diff --git a/lib/yui/2.8.1/build/animation/animation-debug.js b/lib/yui/2.8.1/build/animation/animation-debug.js
new file mode 100644
index 00000000000..13638e94cbd
--- /dev/null
+++ b/lib/yui/2.8.1/build/animation/animation-debug.js
@@ -0,0 +1,1396 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
+(function() {
+
+var Y = YAHOO.util;
+
+/*
+Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+*/
+
+/**
+ * The animation module provides allows effects to be added to HTMLElements.
+ * @module animation
+ * @requires yahoo, event, dom
+ */
+
+/**
+ *
+ * Base animation class that provides the interface for building animated effects.
+ *
Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);
+ * @class Anim
+ * @namespace YAHOO.util
+ * @requires YAHOO.util.AnimMgr
+ * @requires YAHOO.util.Easing
+ * @requires YAHOO.util.Dom
+ * @requires YAHOO.util.Event
+ * @requires YAHOO.util.CustomEvent
+ * @constructor
+ * @param {String | HTMLElement} el Reference to the element that will be animated
+ * @param {Object} attributes The attribute(s) to be animated.
+ * Each attribute is an object with at minimum a "to" or "by" member defined.
+ * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
+ * All attribute names use camelCase.
+ * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
+ * @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)
+ */
+
+var Anim = function(el, attributes, duration, method) {
+ if (!el) {
+ YAHOO.log('element required to create Anim instance', 'error', 'Anim');
+ }
+ this.init(el, attributes, duration, method);
+};
+
+Anim.NAME = 'Anim';
+
+Anim.prototype = {
+ /**
+ * Provides a readable name for the Anim instance.
+ * @method toString
+ * @return {String}
+ */
+ toString: function() {
+ var el = this.getEl() || {};
+ var id = el.id || el.tagName;
+ return (this.constructor.NAME + ': ' + id);
+ },
+
+ patterns: { // cached for performance
+ noNegatives: /width|height|opacity|padding/i, // keep at zero or above
+ offsetAttribute: /^((width|height)|(top|left))$/, // use offsetValue as default
+ defaultUnit: /width|height|top$|bottom$|left$|right$/i, // use 'px' by default
+ offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset
+ },
+
+ /**
+ * Returns the value computed by the animation's "method".
+ * @method doMethod
+ * @param {String} attr The name of the attribute.
+ * @param {Number} start The value this attribute should start from for this animation.
+ * @param {Number} end The value this attribute should end at for this animation.
+ * @return {Number} The Value to be applied to the attribute.
+ */
+ doMethod: function(attr, start, end) {
+ return this.method(this.currentFrame, start, end - start, this.totalFrames);
+ },
+
+ /**
+ * Applies a value to an attribute.
+ * @method setAttribute
+ * @param {String} attr The name of the attribute.
+ * @param {Number} val The value to be applied to the attribute.
+ * @param {String} unit The unit ('px', '%', etc.) of the value.
+ */
+ setAttribute: function(attr, val, unit) {
+ var el = this.getEl();
+ if ( this.patterns.noNegatives.test(attr) ) {
+ val = (val > 0) ? val : 0;
+ }
+
+ if (attr in el && !('style' in el && attr in el.style)) {
+ el[attr] = val;
+ } else {
+ Y.Dom.setStyle(el, attr, val + unit);
+ }
+ },
+
+ /**
+ * Returns current value of the attribute.
+ * @method getAttribute
+ * @param {String} attr The name of the attribute.
+ * @return {Number} val The current value of the attribute.
+ */
+ getAttribute: function(attr) {
+ var el = this.getEl();
+ var val = Y.Dom.getStyle(el, attr);
+
+ if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
+ return parseFloat(val);
+ }
+
+ var a = this.patterns.offsetAttribute.exec(attr) || [];
+ var pos = !!( a[3] ); // top or left
+ var box = !!( a[2] ); // width or height
+
+ if ('style' in el) {
+ // use offsets for width/height and abs pos top/left
+ if ( box || (Y.Dom.getStyle(el, 'position') == 'absolute' && pos) ) {
+ val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
+ } else { // default to zero for other 'auto'
+ val = 0;
+ }
+ } else if (attr in el) {
+ val = el[attr];
+ }
+
+ return val;
+ },
+
+ /**
+ * Returns the unit to use when none is supplied.
+ * @method getDefaultUnit
+ * @param {attr} attr The name of the attribute.
+ * @return {String} The default unit to be used.
+ */
+ getDefaultUnit: function(attr) {
+ if ( this.patterns.defaultUnit.test(attr) ) {
+ return 'px';
+ }
+
+ return '';
+ },
+
+ /**
+ * Sets the actual values to be used during the animation. Should only be needed for subclass use.
+ * @method setRuntimeAttribute
+ * @param {Object} attr The attribute object
+ * @private
+ */
+ setRuntimeAttribute: function(attr) {
+ var start;
+ var end;
+ var attributes = this.attributes;
+
+ this.runtimeAttributes[attr] = {};
+
+ var isset = function(prop) {
+ return (typeof prop !== 'undefined');
+ };
+
+ if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) {
+ return false; // note return; nothing to animate to
+ }
+
+ start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
+
+ // To beats by, per SMIL 2.1 spec
+ if ( isset(attributes[attr]['to']) ) {
+ end = attributes[attr]['to'];
+ } else if ( isset(attributes[attr]['by']) ) {
+ if (start.constructor == Array) {
+ end = [];
+ for (var i = 0, len = start.length; i < len; ++i) {
+ end[i] = start[i] + attributes[attr]['by'][i] * 1; // times 1 to cast "by"
+ }
+ } else {
+ end = start + attributes[attr]['by'] * 1;
+ }
+ }
+
+ this.runtimeAttributes[attr].start = start;
+ this.runtimeAttributes[attr].end = end;
+
+ // set units if needed
+ this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ?
+ attributes[attr]['unit'] : this.getDefaultUnit(attr);
+ return true;
+ },
+
+ /**
+ * Constructor for Anim instance.
+ * @method init
+ * @param {String | HTMLElement} el Reference to the element that will be animated
+ * @param {Object} attributes The attribute(s) to be animated.
+ * Each attribute is an object with at minimum a "to" or "by" member defined.
+ * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
+ * All attribute names use camelCase.
+ * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
+ * @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)
+ */
+ init: function(el, attributes, duration, method) {
+ /**
+ * Whether or not the animation is running.
+ * @property isAnimated
+ * @private
+ * @type Boolean
+ */
+ var isAnimated = false;
+
+ /**
+ * A Date object that is created when the animation begins.
+ * @property startTime
+ * @private
+ * @type Date
+ */
+ var startTime = null;
+
+ /**
+ * The number of frames this animation was able to execute.
+ * @property actualFrames
+ * @private
+ * @type Int
+ */
+ var actualFrames = 0;
+
+ /**
+ * The element to be animated.
+ * @property el
+ * @private
+ * @type HTMLElement
+ */
+ el = Y.Dom.get(el);
+
+ /**
+ * The collection of attributes to be animated.
+ * Each attribute must have at least a "to" or "by" defined in order to animate.
+ * If "to" is supplied, the animation will end with the attribute at that value.
+ * If "by" is supplied, the animation will end at that value plus its starting value.
+ * If both are supplied, "to" is used, and "by" is ignored.
+ * 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).
+ * @property attributes
+ * @type Object
+ */
+ this.attributes = attributes || {};
+
+ /**
+ * The length of the animation. Defaults to "1" (second).
+ * @property duration
+ * @type Number
+ */
+ this.duration = !YAHOO.lang.isUndefined(duration) ? duration : 1;
+
+ /**
+ * The method that will provide values to the attribute(s) during the animation.
+ * Defaults to "YAHOO.util.Easing.easeNone".
+ * @property method
+ * @type Function
+ */
+ this.method = method || Y.Easing.easeNone;
+
+ /**
+ * Whether or not the duration should be treated as seconds.
+ * Defaults to true.
+ * @property useSeconds
+ * @type Boolean
+ */
+ this.useSeconds = true; // default to seconds
+
+ /**
+ * The location of the current animation on the timeline.
+ * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
+ * @property currentFrame
+ * @type Int
+ */
+ this.currentFrame = 0;
+
+ /**
+ * The total number of frames to be executed.
+ * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
+ * @property totalFrames
+ * @type Int
+ */
+ this.totalFrames = Y.AnimMgr.fps;
+
+ /**
+ * Changes the animated element
+ * @method setEl
+ */
+ this.setEl = function(element) {
+ el = Y.Dom.get(element);
+ };
+
+ /**
+ * Returns a reference to the animated element.
+ * @method getEl
+ * @return {HTMLElement}
+ */
+ this.getEl = function() { return el; };
+
+ /**
+ * Checks whether the element is currently animated.
+ * @method isAnimated
+ * @return {Boolean} current value of isAnimated.
+ */
+ this.isAnimated = function() {
+ return isAnimated;
+ };
+
+ /**
+ * Returns the animation start time.
+ * @method getStartTime
+ * @return {Date} current value of startTime.
+ */
+ this.getStartTime = function() {
+ return startTime;
+ };
+
+ this.runtimeAttributes = {};
+
+ var logger = {};
+ logger.log = function() {YAHOO.log.apply(window, arguments)};
+
+ logger.log('creating new instance of ' + this);
+
+ /**
+ * Starts the animation by registering it with the animation manager.
+ * @method animate
+ */
+ this.animate = function() {
+ if ( this.isAnimated() ) {
+ return false;
+ }
+
+ this.currentFrame = 0;
+
+ this.totalFrames = ( this.useSeconds ) ? Math.ceil(Y.AnimMgr.fps * this.duration) : this.duration;
+
+ if (this.duration === 0 && this.useSeconds) { // jump to last frame if zero second duration
+ this.totalFrames = 1;
+ }
+ Y.AnimMgr.registerElement(this);
+ return true;
+ };
+
+ /**
+ * Stops the animation. Normally called by AnimMgr when animation completes.
+ * @method stop
+ * @param {Boolean} finish (optional) If true, animation will jump to final frame.
+ */
+ this.stop = function(finish) {
+ if (!this.isAnimated()) { // nothing to stop
+ return false;
+ }
+
+ if (finish) {
+ this.currentFrame = this.totalFrames;
+ this._onTween.fire();
+ }
+ Y.AnimMgr.stop(this);
+ };
+
+ var onStart = function() {
+ this.onStart.fire();
+
+ this.runtimeAttributes = {};
+ for (var attr in this.attributes) {
+ this.setRuntimeAttribute(attr);
+ }
+
+ isAnimated = true;
+ actualFrames = 0;
+ startTime = new Date();
+ };
+
+ /**
+ * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s).
+ * @private
+ */
+
+ var onTween = function() {
+ var data = {
+ duration: new Date() - this.getStartTime(),
+ currentFrame: this.currentFrame
+ };
+
+ data.toString = function() {
+ return (
+ 'duration: ' + data.duration +
+ ', currentFrame: ' + data.currentFrame
+ );
+ };
+
+ this.onTween.fire(data);
+
+ var runtimeAttributes = this.runtimeAttributes;
+
+ for (var attr in runtimeAttributes) {
+ this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
+ }
+
+ actualFrames += 1;
+ };
+
+ var onComplete = function() {
+ var actual_duration = (new Date() - startTime) / 1000 ;
+
+ var data = {
+ duration: actual_duration,
+ frames: actualFrames,
+ fps: actualFrames / actual_duration
+ };
+
+ data.toString = function() {
+ return (
+ 'duration: ' + data.duration +
+ ', frames: ' + data.frames +
+ ', fps: ' + data.fps
+ );
+ };
+
+ isAnimated = false;
+ actualFrames = 0;
+ this.onComplete.fire(data);
+ };
+
+ /**
+ * Custom event that fires after onStart, useful in subclassing
+ * @private
+ */
+ this._onStart = new Y.CustomEvent('_start', this, true);
+
+ /**
+ * Custom event that fires when animation begins
+ * Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction)
+ * @event onStart
+ */
+ this.onStart = new Y.CustomEvent('start', this);
+
+ /**
+ * Custom event that fires between each frame
+ * Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction)
+ * @event onTween
+ */
+ this.onTween = new Y.CustomEvent('tween', this);
+
+ /**
+ * Custom event that fires after onTween
+ * @private
+ */
+ this._onTween = new Y.CustomEvent('_tween', this, true);
+
+ /**
+ * Custom event that fires when animation ends
+ * Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction)
+ * @event onComplete
+ */
+ this.onComplete = new Y.CustomEvent('complete', this);
+ /**
+ * Custom event that fires after onComplete
+ * @private
+ */
+ this._onComplete = new Y.CustomEvent('_complete', this, true);
+
+ this._onStart.subscribe(onStart);
+ this._onTween.subscribe(onTween);
+ this._onComplete.subscribe(onComplete);
+ }
+};
+
+ Y.Anim = Anim;
+})();
+/**
+ * Handles animation queueing and threading.
+ * Used by Anim and subclasses.
+ * @class AnimMgr
+ * @namespace YAHOO.util
+ */
+YAHOO.util.AnimMgr = new function() {
+ /**
+ * Reference to the animation Interval.
+ * @property thread
+ * @private
+ * @type Int
+ */
+ var thread = null;
+
+ /**
+ * The current queue of registered animation objects.
+ * @property queue
+ * @private
+ * @type Array
+ */
+ var queue = [];
+
+ /**
+ * The number of active animations.
+ * @property tweenCount
+ * @private
+ * @type Int
+ */
+ var tweenCount = 0;
+
+ /**
+ * Base frame rate (frames per second).
+ * Arbitrarily high for better x-browser calibration (slower browsers drop more frames).
+ * @property fps
+ * @type Int
+ *
+ */
+ this.fps = 1000;
+
+ /**
+ * Interval delay in milliseconds, defaults to fastest possible.
+ * @property delay
+ * @type Int
+ *
+ */
+ this.delay = 1;
+
+ /**
+ * Adds an animation instance to the animation queue.
+ * All animation instances must be registered in order to animate.
+ * @method registerElement
+ * @param {object} tween The Anim instance to be be registered
+ */
+ this.registerElement = function(tween) {
+ queue[queue.length] = tween;
+ tweenCount += 1;
+ tween._onStart.fire();
+ this.start();
+ };
+
+ /**
+ * removes an animation instance from the animation queue.
+ * All animation instances must be registered in order to animate.
+ * @method unRegister
+ * @param {object} tween The Anim instance to be be registered
+ * @param {Int} index The index of the Anim instance
+ * @private
+ */
+ this.unRegister = function(tween, index) {
+ index = index || getIndex(tween);
+ if (!tween.isAnimated() || index === -1) {
+ return false;
+ }
+
+ tween._onComplete.fire();
+ queue.splice(index, 1);
+
+ tweenCount -= 1;
+ if (tweenCount <= 0) {
+ this.stop();
+ }
+
+ return true;
+ };
+
+ /**
+ * Starts the animation thread.
+ * Only one thread can run at a time.
+ * @method start
+ */
+ this.start = function() {
+ if (thread === null) {
+ thread = setInterval(this.run, this.delay);
+ }
+ };
+
+ /**
+ * Stops the animation thread or a specific animation instance.
+ * @method stop
+ * @param {object} tween A specific Anim instance to stop (optional)
+ * If no instance given, Manager stops thread and all animations.
+ */
+ this.stop = function(tween) {
+ if (!tween) {
+ clearInterval(thread);
+
+ for (var i = 0, len = queue.length; i < len; ++i) {
+ this.unRegister(queue[0], 0);
+ }
+
+ queue = [];
+ thread = null;
+ tweenCount = 0;
+ }
+ else {
+ this.unRegister(tween);
+ }
+ };
+
+ /**
+ * Called per Interval to handle each animation frame.
+ * @method run
+ */
+ this.run = function() {
+ for (var i = 0, len = queue.length; i < len; ++i) {
+ var tween = queue[i];
+ if ( !tween || !tween.isAnimated() ) { continue; }
+
+ if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
+ {
+ tween.currentFrame += 1;
+
+ if (tween.useSeconds) {
+ correctFrame(tween);
+ }
+ tween._onTween.fire();
+ }
+ else { YAHOO.util.AnimMgr.stop(tween, i); }
+ }
+ };
+
+ var getIndex = function(anim) {
+ for (var i = 0, len = queue.length; i < len; ++i) {
+ if (queue[i] === anim) {
+ return i; // note return;
+ }
+ }
+ return -1;
+ };
+
+ /**
+ * On the fly frame correction to keep animation on time.
+ * @method correctFrame
+ * @private
+ * @param {Object} tween The Anim instance being corrected.
+ */
+ var correctFrame = function(tween) {
+ var frames = tween.totalFrames;
+ var frame = tween.currentFrame;
+ var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
+ var elapsed = (new Date() - tween.getStartTime());
+ var tweak = 0;
+
+ if (elapsed < tween.duration * 1000) { // check if falling behind
+ tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
+ } else { // went over duration, so jump to end
+ tweak = frames - (frame + 1);
+ }
+ if (tweak > 0 && isFinite(tweak)) { // adjust if needed
+ if (tween.currentFrame + tweak >= frames) {// dont go past last frame
+ tweak = frames - (frame + 1);
+ }
+
+ tween.currentFrame += tweak;
+ }
+ };
+ this._queue = queue;
+ this._getIndex = getIndex;
+};
+/**
+ * Used to calculate Bezier splines for any number of control points.
+ * @class Bezier
+ * @namespace YAHOO.util
+ *
+ */
+YAHOO.util.Bezier = new function() {
+ /**
+ * Get the current position of the animated element based on t.
+ * Each point is an array of "x" and "y" values (0 = x, 1 = y)
+ * At least 2 points are required (start and end).
+ * First point is start. Last point is end.
+ * Additional control points are optional.
+ * @method getPosition
+ * @param {Array} points An array containing Bezier points
+ * @param {Number} t A number between 0 and 1 which is the basis for determining current position
+ * @return {Array} An array containing int x and y member data
+ */
+ this.getPosition = function(points, t) {
+ var n = points.length;
+ var tmp = [];
+
+ for (var i = 0; i < n; ++i){
+ tmp[i] = [points[i][0], points[i][1]]; // save input
+ }
+
+ for (var j = 1; j < n; ++j) {
+ for (i = 0; i < n - j; ++i) {
+ tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
+ tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
+ }
+ }
+
+ return [ tmp[0][0], tmp[0][1] ];
+
+ };
+};
+(function() {
+/**
+ * Anim subclass for color transitions.
+ *
Usage: var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut); Color values can be specified with either 112233, #112233,
+ * [255,255,255], or rgb(255,255,255)
+ * @class ColorAnim
+ * @namespace YAHOO.util
+ * @requires YAHOO.util.Anim
+ * @requires YAHOO.util.AnimMgr
+ * @requires YAHOO.util.Easing
+ * @requires YAHOO.util.Bezier
+ * @requires YAHOO.util.Dom
+ * @requires YAHOO.util.Event
+ * @constructor
+ * @extends YAHOO.util.Anim
+ * @param {HTMLElement | String} el Reference to the element that will be animated
+ * @param {Object} attributes The attribute(s) to be animated.
+ * Each attribute is an object with at minimum a "to" or "by" member defined.
+ * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
+ * All attribute names use camelCase.
+ * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
+ * @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)
+ */
+ var ColorAnim = function(el, attributes, duration, method) {
+ ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
+ };
+
+ ColorAnim.NAME = 'ColorAnim';
+
+ ColorAnim.DEFAULT_BGCOLOR = '#fff';
+ // shorthand
+ var Y = YAHOO.util;
+ YAHOO.extend(ColorAnim, Y.Anim);
+
+ var superclass = ColorAnim.superclass;
+ var proto = ColorAnim.prototype;
+
+ proto.patterns.color = /color$/i;
+ proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
+ proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
+ proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
+ proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/; // need rgba for safari
+
+ /**
+ * Attempts to parse the given string and return a 3-tuple.
+ * @method parseColor
+ * @param {String} s The string to parse.
+ * @return {Array} The 3-tuple of rgb values.
+ */
+ proto.parseColor = function(s) {
+ if (s.length == 3) { return s; }
+
+ var c = this.patterns.hex.exec(s);
+ if (c && c.length == 4) {
+ return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
+ }
+
+ c = this.patterns.rgb.exec(s);
+ if (c && c.length == 4) {
+ return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
+ }
+
+ c = this.patterns.hex3.exec(s);
+ if (c && c.length == 4) {
+ return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
+ }
+
+ return null;
+ };
+
+ proto.getAttribute = function(attr) {
+ var el = this.getEl();
+ if (this.patterns.color.test(attr) ) {
+ var val = YAHOO.util.Dom.getStyle(el, attr);
+
+ var that = this;
+ if (this.patterns.transparent.test(val)) { // bgcolor default
+ var parent = YAHOO.util.Dom.getAncestorBy(el, function(node) {
+ return !that.patterns.transparent.test(val);
+ });
+
+ if (parent) {
+ val = Y.Dom.getStyle(parent, attr);
+ } else {
+ val = ColorAnim.DEFAULT_BGCOLOR;
+ }
+ }
+ } else {
+ val = superclass.getAttribute.call(this, attr);
+ }
+
+ return val;
+ };
+
+ proto.doMethod = function(attr, start, end) {
+ var val;
+
+ if ( this.patterns.color.test(attr) ) {
+ val = [];
+ for (var i = 0, len = start.length; i < len; ++i) {
+ val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
+ }
+
+ val = 'rgb('+Math.floor(val[0])+','+Math.floor(val[1])+','+Math.floor(val[2])+')';
+ }
+ else {
+ val = superclass.doMethod.call(this, attr, start, end);
+ }
+
+ return val;
+ };
+
+ proto.setRuntimeAttribute = function(attr) {
+ superclass.setRuntimeAttribute.call(this, attr);
+
+ if ( this.patterns.color.test(attr) ) {
+ var attributes = this.attributes;
+ var start = this.parseColor(this.runtimeAttributes[attr].start);
+ var end = this.parseColor(this.runtimeAttributes[attr].end);
+ // fix colors if going "by"
+ if ( typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined' ) {
+ end = this.parseColor(attributes[attr].by);
+
+ for (var i = 0, len = start.length; i < len; ++i) {
+ end[i] = start[i] + end[i];
+ }
+ }
+
+ this.runtimeAttributes[attr].start = start;
+ this.runtimeAttributes[attr].end = end;
+ }
+ };
+
+ Y.ColorAnim = ColorAnim;
+})();
+/*!
+TERMS OF USE - EASING EQUATIONS
+Open source under the BSD License.
+Copyright 2001 Robert Penner All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * 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.
+ * 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.
+
+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.
+*/
+
+/**
+ * Singleton that determines how an animation proceeds from start to end.
+ * @class Easing
+ * @namespace YAHOO.util
+*/
+
+YAHOO.util.Easing = {
+
+ /**
+ * Uniform speed between points.
+ * @method easeNone
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeNone: function (t, b, c, d) {
+ return c*t/d + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end.
+ * @method easeIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeIn: function (t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end.
+ * @method easeOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOut: function (t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end.
+ * @method easeBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBoth: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t + b;
+ }
+
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end.
+ * @method easeInStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeInStrong: function (t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end.
+ * @method easeOutStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOutStrong: function (t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end.
+ * @method easeBothStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBothStrong: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t*t*t + b;
+ }
+
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+
+ /**
+ * Snap in elastic effect.
+ * @method elasticIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+
+ elasticIn: function (t, b, c, d, a, p) {
+ if (t == 0) {
+ return b;
+ }
+ if ( (t /= d) == 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p=d*.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ var s = p/4;
+ }
+ else {
+ var s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+
+ /**
+ * Snap out elastic effect.
+ * @method elasticOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticOut: function (t, b, c, d, a, p) {
+ if (t == 0) {
+ return b;
+ }
+ if ( (t /= d) == 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p=d*.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ var s = p / 4;
+ }
+ else {
+ var s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+
+ /**
+ * Snap both elastic effect.
+ * @method elasticBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticBoth: function (t, b, c, d, a, p) {
+ if (t == 0) {
+ return b;
+ }
+
+ if ( (t /= d/2) == 2 ) {
+ return b+c;
+ }
+
+ if (!p) {
+ p = d*(.3*1.5);
+ }
+
+ if ( !a || a < Math.abs(c) ) {
+ a = c;
+ var s = p/4;
+ }
+ else {
+ var s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ if (t < 1) {
+ return -.5*(a*Math.pow(2,10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ }
+ return a*Math.pow(2,-10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
+ },
+
+
+ /**
+ * Backtracks slightly, then reverses direction and moves to end.
+ * @method backIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backIn: function (t, b, c, d, s) {
+ if (typeof s == 'undefined') {
+ s = 1.70158;
+ }
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+
+ /**
+ * Overshoots end, then reverses and comes back to end.
+ * @method backOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backOut: function (t, b, c, d, s) {
+ if (typeof s == 'undefined') {
+ s = 1.70158;
+ }
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+
+ /**
+ * Backtracks slightly, then reverses direction, overshoots end,
+ * then reverses and comes back to end.
+ * @method backBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backBoth: function (t, b, c, d, s) {
+ if (typeof s == 'undefined') {
+ s = 1.70158;
+ }
+
+ if ((t /= d/2 ) < 1) {
+ return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ }
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+
+ /**
+ * Bounce off of start.
+ * @method bounceIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceIn: function (t, b, c, d) {
+ return c - YAHOO.util.Easing.bounceOut(d-t, 0, c, d) + b;
+ },
+
+ /**
+ * Bounces off end.
+ * @method bounceOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceOut: function (t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
+ }
+ return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
+ },
+
+ /**
+ * Bounces off start and end.
+ * @method bounceBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceBoth: function (t, b, c, d) {
+ if (t < d/2) {
+ return YAHOO.util.Easing.bounceIn(t*2, 0, c, d) * .5 + b;
+ }
+ return YAHOO.util.Easing.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
+ }
+};
+
+(function() {
+/**
+ * Anim subclass for moving elements along a path defined by the "points"
+ * member of "attributes". All "points" are arrays with x, y coordinates.
+ *
Usage: var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);
+ * @class Motion
+ * @namespace YAHOO.util
+ * @requires YAHOO.util.Anim
+ * @requires YAHOO.util.AnimMgr
+ * @requires YAHOO.util.Easing
+ * @requires YAHOO.util.Bezier
+ * @requires YAHOO.util.Dom
+ * @requires YAHOO.util.Event
+ * @requires YAHOO.util.CustomEvent
+ * @constructor
+ * @extends YAHOO.util.ColorAnim
+ * @param {String | HTMLElement} el Reference to the element that will be animated
+ * @param {Object} attributes The attribute(s) to be animated.
+ * Each attribute is an object with at minimum a "to" or "by" member defined.
+ * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
+ * All attribute names use camelCase.
+ * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
+ * @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)
+ */
+ var Motion = function(el, attributes, duration, method) {
+ if (el) { // dont break existing subclasses not using YAHOO.extend
+ Motion.superclass.constructor.call(this, el, attributes, duration, method);
+ }
+ };
+
+
+ Motion.NAME = 'Motion';
+
+ // shorthand
+ var Y = YAHOO.util;
+ YAHOO.extend(Motion, Y.ColorAnim);
+
+ var superclass = Motion.superclass;
+ var proto = Motion.prototype;
+
+ proto.patterns.points = /^points$/i;
+
+ proto.setAttribute = function(attr, val, unit) {
+ if ( this.patterns.points.test(attr) ) {
+ unit = unit || 'px';
+ superclass.setAttribute.call(this, 'left', val[0], unit);
+ superclass.setAttribute.call(this, 'top', val[1], unit);
+ } else {
+ superclass.setAttribute.call(this, attr, val, unit);
+ }
+ };
+
+ proto.getAttribute = function(attr) {
+ if ( this.patterns.points.test(attr) ) {
+ var val = [
+ superclass.getAttribute.call(this, 'left'),
+ superclass.getAttribute.call(this, 'top')
+ ];
+ } else {
+ val = superclass.getAttribute.call(this, attr);
+ }
+
+ return val;
+ };
+
+ proto.doMethod = function(attr, start, end) {
+ var val = null;
+
+ if ( this.patterns.points.test(attr) ) {
+ var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
+ val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
+ } else {
+ val = superclass.doMethod.call(this, attr, start, end);
+ }
+ return val;
+ };
+
+ proto.setRuntimeAttribute = function(attr) {
+ if ( this.patterns.points.test(attr) ) {
+ var el = this.getEl();
+ var attributes = this.attributes;
+ var start;
+ var control = attributes['points']['control'] || [];
+ var end;
+ var i, len;
+
+ if (control.length > 0 && !(control[0] instanceof Array) ) { // could be single point or array of points
+ control = [control];
+ } else { // break reference to attributes.points.control
+ var tmp = [];
+ for (i = 0, len = control.length; i< len; ++i) {
+ tmp[i] = control[i];
+ }
+ control = tmp;
+ }
+
+ if (Y.Dom.getStyle(el, 'position') == 'static') { // default to relative
+ Y.Dom.setStyle(el, 'position', 'relative');
+ }
+
+ if ( isset(attributes['points']['from']) ) {
+ Y.Dom.setXY(el, attributes['points']['from']); // set position to from point
+ }
+ else { Y.Dom.setXY( el, Y.Dom.getXY(el) ); } // set it to current position
+
+ start = this.getAttribute('points'); // get actual top & left
+
+ // TO beats BY, per SMIL 2.1 spec
+ if ( isset(attributes['points']['to']) ) {
+ end = translateValues.call(this, attributes['points']['to'], start);
+
+ var pageXY = Y.Dom.getXY(this.getEl());
+ for (i = 0, len = control.length; i < len; ++i) {
+ control[i] = translateValues.call(this, control[i], start);
+ }
+
+
+ } else if ( isset(attributes['points']['by']) ) {
+ end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
+
+ for (i = 0, len = control.length; i < len; ++i) {
+ control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
+ }
+ }
+
+ this.runtimeAttributes[attr] = [start];
+
+ if (control.length > 0) {
+ this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
+ }
+
+ this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
+ }
+ else {
+ superclass.setRuntimeAttribute.call(this, attr);
+ }
+ };
+
+ var translateValues = function(val, start) {
+ var pageXY = Y.Dom.getXY(this.getEl());
+ val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
+
+ return val;
+ };
+
+ var isset = function(prop) {
+ return (typeof prop !== 'undefined');
+ };
+
+ Y.Motion = Motion;
+})();
+(function() {
+/**
+ * Anim subclass for scrolling elements to a position defined by the "scroll"
+ * member of "attributes". All "scroll" members are arrays with x, y scroll positions.
+ *
Usage: var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);
+ * @class Scroll
+ * @namespace YAHOO.util
+ * @requires YAHOO.util.Anim
+ * @requires YAHOO.util.AnimMgr
+ * @requires YAHOO.util.Easing
+ * @requires YAHOO.util.Bezier
+ * @requires YAHOO.util.Dom
+ * @requires YAHOO.util.Event
+ * @requires YAHOO.util.CustomEvent
+ * @extends YAHOO.util.ColorAnim
+ * @constructor
+ * @param {String or HTMLElement} el Reference to the element that will be animated
+ * @param {Object} attributes The attribute(s) to be animated.
+ * Each attribute is an object with at minimum a "to" or "by" member defined.
+ * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
+ * All attribute names use camelCase.
+ * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
+ * @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)
+ */
+ var Scroll = function(el, attributes, duration, method) {
+ if (el) { // dont break existing subclasses not using YAHOO.extend
+ Scroll.superclass.constructor.call(this, el, attributes, duration, method);
+ }
+ };
+
+ Scroll.NAME = 'Scroll';
+
+ // shorthand
+ var Y = YAHOO.util;
+ YAHOO.extend(Scroll, Y.ColorAnim);
+
+ var superclass = Scroll.superclass;
+ var proto = Scroll.prototype;
+
+ proto.doMethod = function(attr, start, end) {
+ var val = null;
+
+ if (attr == 'scroll') {
+ val = [
+ this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
+ this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
+ ];
+
+ } else {
+ val = superclass.doMethod.call(this, attr, start, end);
+ }
+ return val;
+ };
+
+ proto.getAttribute = function(attr) {
+ var val = null;
+ var el = this.getEl();
+
+ if (attr == 'scroll') {
+ val = [ el.scrollLeft, el.scrollTop ];
+ } else {
+ val = superclass.getAttribute.call(this, attr);
+ }
+
+ return val;
+ };
+
+ proto.setAttribute = function(attr, val, unit) {
+ var el = this.getEl();
+
+ if (attr == 'scroll') {
+ el.scrollLeft = val[0];
+ el.scrollTop = val[1];
+ } else {
+ superclass.setAttribute.call(this, attr, val, unit);
+ }
+ };
+
+ Y.Scroll = Scroll;
+})();
+YAHOO.register("animation", YAHOO.util.Anim, {version: "2.8.1", build: "19"});
diff --git a/lib/yui/2.8.1/build/animation/animation-min.js b/lib/yui/2.8.1/build/animation/animation-min.js
new file mode 100644
index 00000000000..7ea805537dd
--- /dev/null
+++ b/lib/yui/2.8.1/build/animation/animation-min.js
@@ -0,0 +1,23 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
+(function(){var B=YAHOO.util;var A=function(D,C,E,F){if(!D){}this.init(D,C,E,F);};A.NAME="Anim";A.prototype={toString:function(){var C=this.getEl()||{};var D=C.id||C.tagName;return(this.constructor.NAME+": "+D);},patterns:{noNegatives:/width|height|opacity|padding/i,offsetAttribute:/^((width|height)|(top|left))$/,defaultUnit:/width|height|top$|bottom$|left$|right$/i,offsetUnit:/\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i},doMethod:function(C,E,D){return this.method(this.currentFrame,E,D-E,this.totalFrames);},setAttribute:function(C,F,E){var D=this.getEl();if(this.patterns.noNegatives.test(C)){F=(F>0)?F:0;}if(C in D&&!("style" in D&&C in D.style)){D[C]=F;}else{B.Dom.setStyle(D,C,F+E);}},getAttribute:function(C){var E=this.getEl();var G=B.Dom.getStyle(E,C);if(G!=="auto"&&!this.patterns.offsetUnit.test(G)){return parseFloat(G);}var D=this.patterns.offsetAttribute.exec(C)||[];var H=!!(D[3]);var F=!!(D[2]);if("style" in E){if(F||(B.Dom.getStyle(E,"position")=="absolute"&&H)){G=E["offset"+D[0].charAt(0).toUpperCase()+D[0].substr(1)];}else{G=0;}}else{if(C in E){G=E[C];}}return G;},getDefaultUnit:function(C){if(this.patterns.defaultUnit.test(C)){return"px";}return"";},setRuntimeAttribute:function(D){var I;var E;var F=this.attributes;this.runtimeAttributes[D]={};var H=function(J){return(typeof J!=="undefined");};if(!H(F[D]["to"])&&!H(F[D]["by"])){return false;}I=(H(F[D]["from"]))?F[D]["from"]:this.getAttribute(D);if(H(F[D]["to"])){E=F[D]["to"];}else{if(H(F[D]["by"])){if(I.constructor==Array){E=[];for(var G=0,C=I.length;G0&&isFinite(K)){if(G.currentFrame+K>=J){K=J-(I+1);}G.currentFrame+=K;}};this._queue=B;this._getIndex=E;};YAHOO.util.Bezier=new function(){this.getPosition=function(E,D){var F=E.length;var C=[];for(var B=0;B0&&!(L[0] instanceof Array)){L=[L];}else{var K=[];for(M=0,O=L.length;M0){this.runtimeAttributes[P]=this.runtimeAttributes[P].concat(L);}this.runtimeAttributes[P][this.runtimeAttributes[P].length]=I;}else{F.setRuntimeAttribute.call(this,P);}};var B=function(G,I){var H=E.Dom.getXY(this.getEl());G=[G[0]-H[0]+I[0],G[1]-H[1]+I[1]];return G;};var D=function(G){return(typeof G!=="undefined");};E.Motion=A;})();(function(){var D=function(F,E,G,H){if(F){D.superclass.constructor.call(this,F,E,G,H);}};D.NAME="Scroll";var B=YAHOO.util;YAHOO.extend(D,B.ColorAnim);var C=D.superclass;var A=D.prototype;A.doMethod=function(E,H,F){var G=null;if(E=="scroll"){G=[this.method(this.currentFrame,H[0],F[0]-H[0],this.totalFrames),this.method(this.currentFrame,H[1],F[1]-H[1],this.totalFrames)];}else{G=C.doMethod.call(this,E,H,F);}return G;};A.getAttribute=function(E){var G=null;var F=this.getEl();if(E=="scroll"){G=[F.scrollLeft,F.scrollTop];}else{G=C.getAttribute.call(this,E);}return G;};A.setAttribute=function(E,H,G){var F=this.getEl();if(E=="scroll"){F.scrollLeft=H[0];F.scrollTop=H[1];}else{C.setAttribute.call(this,E,H,G);}};B.Scroll=D;})();YAHOO.register("animation",YAHOO.util.Anim,{version:"2.8.1",build:"19"});
\ No newline at end of file
diff --git a/lib/yui/2.8.1/build/animation/animation.js b/lib/yui/2.8.1/build/animation/animation.js
new file mode 100644
index 00000000000..ebc8e87c0a6
--- /dev/null
+++ b/lib/yui/2.8.1/build/animation/animation.js
@@ -0,0 +1,1392 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
+(function() {
+
+var Y = YAHOO.util;
+
+/*
+Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+*/
+
+/**
+ * The animation module provides allows effects to be added to HTMLElements.
+ * @module animation
+ * @requires yahoo, event, dom
+ */
+
+/**
+ *
+ * Base animation class that provides the interface for building animated effects.
+ *
Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);
+ * @class Anim
+ * @namespace YAHOO.util
+ * @requires YAHOO.util.AnimMgr
+ * @requires YAHOO.util.Easing
+ * @requires YAHOO.util.Dom
+ * @requires YAHOO.util.Event
+ * @requires YAHOO.util.CustomEvent
+ * @constructor
+ * @param {String | HTMLElement} el Reference to the element that will be animated
+ * @param {Object} attributes The attribute(s) to be animated.
+ * Each attribute is an object with at minimum a "to" or "by" member defined.
+ * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
+ * All attribute names use camelCase.
+ * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
+ * @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)
+ */
+
+var Anim = function(el, attributes, duration, method) {
+ if (!el) {
+ }
+ this.init(el, attributes, duration, method);
+};
+
+Anim.NAME = 'Anim';
+
+Anim.prototype = {
+ /**
+ * Provides a readable name for the Anim instance.
+ * @method toString
+ * @return {String}
+ */
+ toString: function() {
+ var el = this.getEl() || {};
+ var id = el.id || el.tagName;
+ return (this.constructor.NAME + ': ' + id);
+ },
+
+ patterns: { // cached for performance
+ noNegatives: /width|height|opacity|padding/i, // keep at zero or above
+ offsetAttribute: /^((width|height)|(top|left))$/, // use offsetValue as default
+ defaultUnit: /width|height|top$|bottom$|left$|right$/i, // use 'px' by default
+ offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset
+ },
+
+ /**
+ * Returns the value computed by the animation's "method".
+ * @method doMethod
+ * @param {String} attr The name of the attribute.
+ * @param {Number} start The value this attribute should start from for this animation.
+ * @param {Number} end The value this attribute should end at for this animation.
+ * @return {Number} The Value to be applied to the attribute.
+ */
+ doMethod: function(attr, start, end) {
+ return this.method(this.currentFrame, start, end - start, this.totalFrames);
+ },
+
+ /**
+ * Applies a value to an attribute.
+ * @method setAttribute
+ * @param {String} attr The name of the attribute.
+ * @param {Number} val The value to be applied to the attribute.
+ * @param {String} unit The unit ('px', '%', etc.) of the value.
+ */
+ setAttribute: function(attr, val, unit) {
+ var el = this.getEl();
+ if ( this.patterns.noNegatives.test(attr) ) {
+ val = (val > 0) ? val : 0;
+ }
+
+ if (attr in el && !('style' in el && attr in el.style)) {
+ el[attr] = val;
+ } else {
+ Y.Dom.setStyle(el, attr, val + unit);
+ }
+ },
+
+ /**
+ * Returns current value of the attribute.
+ * @method getAttribute
+ * @param {String} attr The name of the attribute.
+ * @return {Number} val The current value of the attribute.
+ */
+ getAttribute: function(attr) {
+ var el = this.getEl();
+ var val = Y.Dom.getStyle(el, attr);
+
+ if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
+ return parseFloat(val);
+ }
+
+ var a = this.patterns.offsetAttribute.exec(attr) || [];
+ var pos = !!( a[3] ); // top or left
+ var box = !!( a[2] ); // width or height
+
+ if ('style' in el) {
+ // use offsets for width/height and abs pos top/left
+ if ( box || (Y.Dom.getStyle(el, 'position') == 'absolute' && pos) ) {
+ val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
+ } else { // default to zero for other 'auto'
+ val = 0;
+ }
+ } else if (attr in el) {
+ val = el[attr];
+ }
+
+ return val;
+ },
+
+ /**
+ * Returns the unit to use when none is supplied.
+ * @method getDefaultUnit
+ * @param {attr} attr The name of the attribute.
+ * @return {String} The default unit to be used.
+ */
+ getDefaultUnit: function(attr) {
+ if ( this.patterns.defaultUnit.test(attr) ) {
+ return 'px';
+ }
+
+ return '';
+ },
+
+ /**
+ * Sets the actual values to be used during the animation. Should only be needed for subclass use.
+ * @method setRuntimeAttribute
+ * @param {Object} attr The attribute object
+ * @private
+ */
+ setRuntimeAttribute: function(attr) {
+ var start;
+ var end;
+ var attributes = this.attributes;
+
+ this.runtimeAttributes[attr] = {};
+
+ var isset = function(prop) {
+ return (typeof prop !== 'undefined');
+ };
+
+ if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) {
+ return false; // note return; nothing to animate to
+ }
+
+ start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
+
+ // To beats by, per SMIL 2.1 spec
+ if ( isset(attributes[attr]['to']) ) {
+ end = attributes[attr]['to'];
+ } else if ( isset(attributes[attr]['by']) ) {
+ if (start.constructor == Array) {
+ end = [];
+ for (var i = 0, len = start.length; i < len; ++i) {
+ end[i] = start[i] + attributes[attr]['by'][i] * 1; // times 1 to cast "by"
+ }
+ } else {
+ end = start + attributes[attr]['by'] * 1;
+ }
+ }
+
+ this.runtimeAttributes[attr].start = start;
+ this.runtimeAttributes[attr].end = end;
+
+ // set units if needed
+ this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ?
+ attributes[attr]['unit'] : this.getDefaultUnit(attr);
+ return true;
+ },
+
+ /**
+ * Constructor for Anim instance.
+ * @method init
+ * @param {String | HTMLElement} el Reference to the element that will be animated
+ * @param {Object} attributes The attribute(s) to be animated.
+ * Each attribute is an object with at minimum a "to" or "by" member defined.
+ * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
+ * All attribute names use camelCase.
+ * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
+ * @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)
+ */
+ init: function(el, attributes, duration, method) {
+ /**
+ * Whether or not the animation is running.
+ * @property isAnimated
+ * @private
+ * @type Boolean
+ */
+ var isAnimated = false;
+
+ /**
+ * A Date object that is created when the animation begins.
+ * @property startTime
+ * @private
+ * @type Date
+ */
+ var startTime = null;
+
+ /**
+ * The number of frames this animation was able to execute.
+ * @property actualFrames
+ * @private
+ * @type Int
+ */
+ var actualFrames = 0;
+
+ /**
+ * The element to be animated.
+ * @property el
+ * @private
+ * @type HTMLElement
+ */
+ el = Y.Dom.get(el);
+
+ /**
+ * The collection of attributes to be animated.
+ * Each attribute must have at least a "to" or "by" defined in order to animate.
+ * If "to" is supplied, the animation will end with the attribute at that value.
+ * If "by" is supplied, the animation will end at that value plus its starting value.
+ * If both are supplied, "to" is used, and "by" is ignored.
+ * 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).
+ * @property attributes
+ * @type Object
+ */
+ this.attributes = attributes || {};
+
+ /**
+ * The length of the animation. Defaults to "1" (second).
+ * @property duration
+ * @type Number
+ */
+ this.duration = !YAHOO.lang.isUndefined(duration) ? duration : 1;
+
+ /**
+ * The method that will provide values to the attribute(s) during the animation.
+ * Defaults to "YAHOO.util.Easing.easeNone".
+ * @property method
+ * @type Function
+ */
+ this.method = method || Y.Easing.easeNone;
+
+ /**
+ * Whether or not the duration should be treated as seconds.
+ * Defaults to true.
+ * @property useSeconds
+ * @type Boolean
+ */
+ this.useSeconds = true; // default to seconds
+
+ /**
+ * The location of the current animation on the timeline.
+ * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
+ * @property currentFrame
+ * @type Int
+ */
+ this.currentFrame = 0;
+
+ /**
+ * The total number of frames to be executed.
+ * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
+ * @property totalFrames
+ * @type Int
+ */
+ this.totalFrames = Y.AnimMgr.fps;
+
+ /**
+ * Changes the animated element
+ * @method setEl
+ */
+ this.setEl = function(element) {
+ el = Y.Dom.get(element);
+ };
+
+ /**
+ * Returns a reference to the animated element.
+ * @method getEl
+ * @return {HTMLElement}
+ */
+ this.getEl = function() { return el; };
+
+ /**
+ * Checks whether the element is currently animated.
+ * @method isAnimated
+ * @return {Boolean} current value of isAnimated.
+ */
+ this.isAnimated = function() {
+ return isAnimated;
+ };
+
+ /**
+ * Returns the animation start time.
+ * @method getStartTime
+ * @return {Date} current value of startTime.
+ */
+ this.getStartTime = function() {
+ return startTime;
+ };
+
+ this.runtimeAttributes = {};
+
+
+
+ /**
+ * Starts the animation by registering it with the animation manager.
+ * @method animate
+ */
+ this.animate = function() {
+ if ( this.isAnimated() ) {
+ return false;
+ }
+
+ this.currentFrame = 0;
+
+ this.totalFrames = ( this.useSeconds ) ? Math.ceil(Y.AnimMgr.fps * this.duration) : this.duration;
+
+ if (this.duration === 0 && this.useSeconds) { // jump to last frame if zero second duration
+ this.totalFrames = 1;
+ }
+ Y.AnimMgr.registerElement(this);
+ return true;
+ };
+
+ /**
+ * Stops the animation. Normally called by AnimMgr when animation completes.
+ * @method stop
+ * @param {Boolean} finish (optional) If true, animation will jump to final frame.
+ */
+ this.stop = function(finish) {
+ if (!this.isAnimated()) { // nothing to stop
+ return false;
+ }
+
+ if (finish) {
+ this.currentFrame = this.totalFrames;
+ this._onTween.fire();
+ }
+ Y.AnimMgr.stop(this);
+ };
+
+ var onStart = function() {
+ this.onStart.fire();
+
+ this.runtimeAttributes = {};
+ for (var attr in this.attributes) {
+ this.setRuntimeAttribute(attr);
+ }
+
+ isAnimated = true;
+ actualFrames = 0;
+ startTime = new Date();
+ };
+
+ /**
+ * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s).
+ * @private
+ */
+
+ var onTween = function() {
+ var data = {
+ duration: new Date() - this.getStartTime(),
+ currentFrame: this.currentFrame
+ };
+
+ data.toString = function() {
+ return (
+ 'duration: ' + data.duration +
+ ', currentFrame: ' + data.currentFrame
+ );
+ };
+
+ this.onTween.fire(data);
+
+ var runtimeAttributes = this.runtimeAttributes;
+
+ for (var attr in runtimeAttributes) {
+ this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
+ }
+
+ actualFrames += 1;
+ };
+
+ var onComplete = function() {
+ var actual_duration = (new Date() - startTime) / 1000 ;
+
+ var data = {
+ duration: actual_duration,
+ frames: actualFrames,
+ fps: actualFrames / actual_duration
+ };
+
+ data.toString = function() {
+ return (
+ 'duration: ' + data.duration +
+ ', frames: ' + data.frames +
+ ', fps: ' + data.fps
+ );
+ };
+
+ isAnimated = false;
+ actualFrames = 0;
+ this.onComplete.fire(data);
+ };
+
+ /**
+ * Custom event that fires after onStart, useful in subclassing
+ * @private
+ */
+ this._onStart = new Y.CustomEvent('_start', this, true);
+
+ /**
+ * Custom event that fires when animation begins
+ * Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction)
+ * @event onStart
+ */
+ this.onStart = new Y.CustomEvent('start', this);
+
+ /**
+ * Custom event that fires between each frame
+ * Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction)
+ * @event onTween
+ */
+ this.onTween = new Y.CustomEvent('tween', this);
+
+ /**
+ * Custom event that fires after onTween
+ * @private
+ */
+ this._onTween = new Y.CustomEvent('_tween', this, true);
+
+ /**
+ * Custom event that fires when animation ends
+ * Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction)
+ * @event onComplete
+ */
+ this.onComplete = new Y.CustomEvent('complete', this);
+ /**
+ * Custom event that fires after onComplete
+ * @private
+ */
+ this._onComplete = new Y.CustomEvent('_complete', this, true);
+
+ this._onStart.subscribe(onStart);
+ this._onTween.subscribe(onTween);
+ this._onComplete.subscribe(onComplete);
+ }
+};
+
+ Y.Anim = Anim;
+})();
+/**
+ * Handles animation queueing and threading.
+ * Used by Anim and subclasses.
+ * @class AnimMgr
+ * @namespace YAHOO.util
+ */
+YAHOO.util.AnimMgr = new function() {
+ /**
+ * Reference to the animation Interval.
+ * @property thread
+ * @private
+ * @type Int
+ */
+ var thread = null;
+
+ /**
+ * The current queue of registered animation objects.
+ * @property queue
+ * @private
+ * @type Array
+ */
+ var queue = [];
+
+ /**
+ * The number of active animations.
+ * @property tweenCount
+ * @private
+ * @type Int
+ */
+ var tweenCount = 0;
+
+ /**
+ * Base frame rate (frames per second).
+ * Arbitrarily high for better x-browser calibration (slower browsers drop more frames).
+ * @property fps
+ * @type Int
+ *
+ */
+ this.fps = 1000;
+
+ /**
+ * Interval delay in milliseconds, defaults to fastest possible.
+ * @property delay
+ * @type Int
+ *
+ */
+ this.delay = 1;
+
+ /**
+ * Adds an animation instance to the animation queue.
+ * All animation instances must be registered in order to animate.
+ * @method registerElement
+ * @param {object} tween The Anim instance to be be registered
+ */
+ this.registerElement = function(tween) {
+ queue[queue.length] = tween;
+ tweenCount += 1;
+ tween._onStart.fire();
+ this.start();
+ };
+
+ /**
+ * removes an animation instance from the animation queue.
+ * All animation instances must be registered in order to animate.
+ * @method unRegister
+ * @param {object} tween The Anim instance to be be registered
+ * @param {Int} index The index of the Anim instance
+ * @private
+ */
+ this.unRegister = function(tween, index) {
+ index = index || getIndex(tween);
+ if (!tween.isAnimated() || index === -1) {
+ return false;
+ }
+
+ tween._onComplete.fire();
+ queue.splice(index, 1);
+
+ tweenCount -= 1;
+ if (tweenCount <= 0) {
+ this.stop();
+ }
+
+ return true;
+ };
+
+ /**
+ * Starts the animation thread.
+ * Only one thread can run at a time.
+ * @method start
+ */
+ this.start = function() {
+ if (thread === null) {
+ thread = setInterval(this.run, this.delay);
+ }
+ };
+
+ /**
+ * Stops the animation thread or a specific animation instance.
+ * @method stop
+ * @param {object} tween A specific Anim instance to stop (optional)
+ * If no instance given, Manager stops thread and all animations.
+ */
+ this.stop = function(tween) {
+ if (!tween) {
+ clearInterval(thread);
+
+ for (var i = 0, len = queue.length; i < len; ++i) {
+ this.unRegister(queue[0], 0);
+ }
+
+ queue = [];
+ thread = null;
+ tweenCount = 0;
+ }
+ else {
+ this.unRegister(tween);
+ }
+ };
+
+ /**
+ * Called per Interval to handle each animation frame.
+ * @method run
+ */
+ this.run = function() {
+ for (var i = 0, len = queue.length; i < len; ++i) {
+ var tween = queue[i];
+ if ( !tween || !tween.isAnimated() ) { continue; }
+
+ if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
+ {
+ tween.currentFrame += 1;
+
+ if (tween.useSeconds) {
+ correctFrame(tween);
+ }
+ tween._onTween.fire();
+ }
+ else { YAHOO.util.AnimMgr.stop(tween, i); }
+ }
+ };
+
+ var getIndex = function(anim) {
+ for (var i = 0, len = queue.length; i < len; ++i) {
+ if (queue[i] === anim) {
+ return i; // note return;
+ }
+ }
+ return -1;
+ };
+
+ /**
+ * On the fly frame correction to keep animation on time.
+ * @method correctFrame
+ * @private
+ * @param {Object} tween The Anim instance being corrected.
+ */
+ var correctFrame = function(tween) {
+ var frames = tween.totalFrames;
+ var frame = tween.currentFrame;
+ var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
+ var elapsed = (new Date() - tween.getStartTime());
+ var tweak = 0;
+
+ if (elapsed < tween.duration * 1000) { // check if falling behind
+ tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
+ } else { // went over duration, so jump to end
+ tweak = frames - (frame + 1);
+ }
+ if (tweak > 0 && isFinite(tweak)) { // adjust if needed
+ if (tween.currentFrame + tweak >= frames) {// dont go past last frame
+ tweak = frames - (frame + 1);
+ }
+
+ tween.currentFrame += tweak;
+ }
+ };
+ this._queue = queue;
+ this._getIndex = getIndex;
+};
+/**
+ * Used to calculate Bezier splines for any number of control points.
+ * @class Bezier
+ * @namespace YAHOO.util
+ *
+ */
+YAHOO.util.Bezier = new function() {
+ /**
+ * Get the current position of the animated element based on t.
+ * Each point is an array of "x" and "y" values (0 = x, 1 = y)
+ * At least 2 points are required (start and end).
+ * First point is start. Last point is end.
+ * Additional control points are optional.
+ * @method getPosition
+ * @param {Array} points An array containing Bezier points
+ * @param {Number} t A number between 0 and 1 which is the basis for determining current position
+ * @return {Array} An array containing int x and y member data
+ */
+ this.getPosition = function(points, t) {
+ var n = points.length;
+ var tmp = [];
+
+ for (var i = 0; i < n; ++i){
+ tmp[i] = [points[i][0], points[i][1]]; // save input
+ }
+
+ for (var j = 1; j < n; ++j) {
+ for (i = 0; i < n - j; ++i) {
+ tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
+ tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
+ }
+ }
+
+ return [ tmp[0][0], tmp[0][1] ];
+
+ };
+};
+(function() {
+/**
+ * Anim subclass for color transitions.
+ *
Usage: var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut); Color values can be specified with either 112233, #112233,
+ * [255,255,255], or rgb(255,255,255)
+ * @class ColorAnim
+ * @namespace YAHOO.util
+ * @requires YAHOO.util.Anim
+ * @requires YAHOO.util.AnimMgr
+ * @requires YAHOO.util.Easing
+ * @requires YAHOO.util.Bezier
+ * @requires YAHOO.util.Dom
+ * @requires YAHOO.util.Event
+ * @constructor
+ * @extends YAHOO.util.Anim
+ * @param {HTMLElement | String} el Reference to the element that will be animated
+ * @param {Object} attributes The attribute(s) to be animated.
+ * Each attribute is an object with at minimum a "to" or "by" member defined.
+ * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
+ * All attribute names use camelCase.
+ * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
+ * @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)
+ */
+ var ColorAnim = function(el, attributes, duration, method) {
+ ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
+ };
+
+ ColorAnim.NAME = 'ColorAnim';
+
+ ColorAnim.DEFAULT_BGCOLOR = '#fff';
+ // shorthand
+ var Y = YAHOO.util;
+ YAHOO.extend(ColorAnim, Y.Anim);
+
+ var superclass = ColorAnim.superclass;
+ var proto = ColorAnim.prototype;
+
+ proto.patterns.color = /color$/i;
+ proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
+ proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
+ proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
+ proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/; // need rgba for safari
+
+ /**
+ * Attempts to parse the given string and return a 3-tuple.
+ * @method parseColor
+ * @param {String} s The string to parse.
+ * @return {Array} The 3-tuple of rgb values.
+ */
+ proto.parseColor = function(s) {
+ if (s.length == 3) { return s; }
+
+ var c = this.patterns.hex.exec(s);
+ if (c && c.length == 4) {
+ return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
+ }
+
+ c = this.patterns.rgb.exec(s);
+ if (c && c.length == 4) {
+ return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
+ }
+
+ c = this.patterns.hex3.exec(s);
+ if (c && c.length == 4) {
+ return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
+ }
+
+ return null;
+ };
+
+ proto.getAttribute = function(attr) {
+ var el = this.getEl();
+ if (this.patterns.color.test(attr) ) {
+ var val = YAHOO.util.Dom.getStyle(el, attr);
+
+ var that = this;
+ if (this.patterns.transparent.test(val)) { // bgcolor default
+ var parent = YAHOO.util.Dom.getAncestorBy(el, function(node) {
+ return !that.patterns.transparent.test(val);
+ });
+
+ if (parent) {
+ val = Y.Dom.getStyle(parent, attr);
+ } else {
+ val = ColorAnim.DEFAULT_BGCOLOR;
+ }
+ }
+ } else {
+ val = superclass.getAttribute.call(this, attr);
+ }
+
+ return val;
+ };
+
+ proto.doMethod = function(attr, start, end) {
+ var val;
+
+ if ( this.patterns.color.test(attr) ) {
+ val = [];
+ for (var i = 0, len = start.length; i < len; ++i) {
+ val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
+ }
+
+ val = 'rgb('+Math.floor(val[0])+','+Math.floor(val[1])+','+Math.floor(val[2])+')';
+ }
+ else {
+ val = superclass.doMethod.call(this, attr, start, end);
+ }
+
+ return val;
+ };
+
+ proto.setRuntimeAttribute = function(attr) {
+ superclass.setRuntimeAttribute.call(this, attr);
+
+ if ( this.patterns.color.test(attr) ) {
+ var attributes = this.attributes;
+ var start = this.parseColor(this.runtimeAttributes[attr].start);
+ var end = this.parseColor(this.runtimeAttributes[attr].end);
+ // fix colors if going "by"
+ if ( typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined' ) {
+ end = this.parseColor(attributes[attr].by);
+
+ for (var i = 0, len = start.length; i < len; ++i) {
+ end[i] = start[i] + end[i];
+ }
+ }
+
+ this.runtimeAttributes[attr].start = start;
+ this.runtimeAttributes[attr].end = end;
+ }
+ };
+
+ Y.ColorAnim = ColorAnim;
+})();
+/*!
+TERMS OF USE - EASING EQUATIONS
+Open source under the BSD License.
+Copyright 2001 Robert Penner All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * 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.
+ * 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.
+
+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.
+*/
+
+/**
+ * Singleton that determines how an animation proceeds from start to end.
+ * @class Easing
+ * @namespace YAHOO.util
+*/
+
+YAHOO.util.Easing = {
+
+ /**
+ * Uniform speed between points.
+ * @method easeNone
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeNone: function (t, b, c, d) {
+ return c*t/d + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end.
+ * @method easeIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeIn: function (t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end.
+ * @method easeOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOut: function (t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end.
+ * @method easeBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBoth: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t + b;
+ }
+
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end.
+ * @method easeInStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeInStrong: function (t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end.
+ * @method easeOutStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOutStrong: function (t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end.
+ * @method easeBothStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBothStrong: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t*t*t + b;
+ }
+
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+
+ /**
+ * Snap in elastic effect.
+ * @method elasticIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+
+ elasticIn: function (t, b, c, d, a, p) {
+ if (t == 0) {
+ return b;
+ }
+ if ( (t /= d) == 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p=d*.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ var s = p/4;
+ }
+ else {
+ var s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+
+ /**
+ * Snap out elastic effect.
+ * @method elasticOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticOut: function (t, b, c, d, a, p) {
+ if (t == 0) {
+ return b;
+ }
+ if ( (t /= d) == 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p=d*.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ var s = p / 4;
+ }
+ else {
+ var s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+
+ /**
+ * Snap both elastic effect.
+ * @method elasticBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticBoth: function (t, b, c, d, a, p) {
+ if (t == 0) {
+ return b;
+ }
+
+ if ( (t /= d/2) == 2 ) {
+ return b+c;
+ }
+
+ if (!p) {
+ p = d*(.3*1.5);
+ }
+
+ if ( !a || a < Math.abs(c) ) {
+ a = c;
+ var s = p/4;
+ }
+ else {
+ var s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ if (t < 1) {
+ return -.5*(a*Math.pow(2,10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ }
+ return a*Math.pow(2,-10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
+ },
+
+
+ /**
+ * Backtracks slightly, then reverses direction and moves to end.
+ * @method backIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backIn: function (t, b, c, d, s) {
+ if (typeof s == 'undefined') {
+ s = 1.70158;
+ }
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+
+ /**
+ * Overshoots end, then reverses and comes back to end.
+ * @method backOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backOut: function (t, b, c, d, s) {
+ if (typeof s == 'undefined') {
+ s = 1.70158;
+ }
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+
+ /**
+ * Backtracks slightly, then reverses direction, overshoots end,
+ * then reverses and comes back to end.
+ * @method backBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backBoth: function (t, b, c, d, s) {
+ if (typeof s == 'undefined') {
+ s = 1.70158;
+ }
+
+ if ((t /= d/2 ) < 1) {
+ return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ }
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+
+ /**
+ * Bounce off of start.
+ * @method bounceIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceIn: function (t, b, c, d) {
+ return c - YAHOO.util.Easing.bounceOut(d-t, 0, c, d) + b;
+ },
+
+ /**
+ * Bounces off end.
+ * @method bounceOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceOut: function (t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
+ }
+ return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
+ },
+
+ /**
+ * Bounces off start and end.
+ * @method bounceBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceBoth: function (t, b, c, d) {
+ if (t < d/2) {
+ return YAHOO.util.Easing.bounceIn(t*2, 0, c, d) * .5 + b;
+ }
+ return YAHOO.util.Easing.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
+ }
+};
+
+(function() {
+/**
+ * Anim subclass for moving elements along a path defined by the "points"
+ * member of "attributes". All "points" are arrays with x, y coordinates.
+ *
Usage: var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);
+ * @class Motion
+ * @namespace YAHOO.util
+ * @requires YAHOO.util.Anim
+ * @requires YAHOO.util.AnimMgr
+ * @requires YAHOO.util.Easing
+ * @requires YAHOO.util.Bezier
+ * @requires YAHOO.util.Dom
+ * @requires YAHOO.util.Event
+ * @requires YAHOO.util.CustomEvent
+ * @constructor
+ * @extends YAHOO.util.ColorAnim
+ * @param {String | HTMLElement} el Reference to the element that will be animated
+ * @param {Object} attributes The attribute(s) to be animated.
+ * Each attribute is an object with at minimum a "to" or "by" member defined.
+ * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
+ * All attribute names use camelCase.
+ * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
+ * @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)
+ */
+ var Motion = function(el, attributes, duration, method) {
+ if (el) { // dont break existing subclasses not using YAHOO.extend
+ Motion.superclass.constructor.call(this, el, attributes, duration, method);
+ }
+ };
+
+
+ Motion.NAME = 'Motion';
+
+ // shorthand
+ var Y = YAHOO.util;
+ YAHOO.extend(Motion, Y.ColorAnim);
+
+ var superclass = Motion.superclass;
+ var proto = Motion.prototype;
+
+ proto.patterns.points = /^points$/i;
+
+ proto.setAttribute = function(attr, val, unit) {
+ if ( this.patterns.points.test(attr) ) {
+ unit = unit || 'px';
+ superclass.setAttribute.call(this, 'left', val[0], unit);
+ superclass.setAttribute.call(this, 'top', val[1], unit);
+ } else {
+ superclass.setAttribute.call(this, attr, val, unit);
+ }
+ };
+
+ proto.getAttribute = function(attr) {
+ if ( this.patterns.points.test(attr) ) {
+ var val = [
+ superclass.getAttribute.call(this, 'left'),
+ superclass.getAttribute.call(this, 'top')
+ ];
+ } else {
+ val = superclass.getAttribute.call(this, attr);
+ }
+
+ return val;
+ };
+
+ proto.doMethod = function(attr, start, end) {
+ var val = null;
+
+ if ( this.patterns.points.test(attr) ) {
+ var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
+ val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
+ } else {
+ val = superclass.doMethod.call(this, attr, start, end);
+ }
+ return val;
+ };
+
+ proto.setRuntimeAttribute = function(attr) {
+ if ( this.patterns.points.test(attr) ) {
+ var el = this.getEl();
+ var attributes = this.attributes;
+ var start;
+ var control = attributes['points']['control'] || [];
+ var end;
+ var i, len;
+
+ if (control.length > 0 && !(control[0] instanceof Array) ) { // could be single point or array of points
+ control = [control];
+ } else { // break reference to attributes.points.control
+ var tmp = [];
+ for (i = 0, len = control.length; i< len; ++i) {
+ tmp[i] = control[i];
+ }
+ control = tmp;
+ }
+
+ if (Y.Dom.getStyle(el, 'position') == 'static') { // default to relative
+ Y.Dom.setStyle(el, 'position', 'relative');
+ }
+
+ if ( isset(attributes['points']['from']) ) {
+ Y.Dom.setXY(el, attributes['points']['from']); // set position to from point
+ }
+ else { Y.Dom.setXY( el, Y.Dom.getXY(el) ); } // set it to current position
+
+ start = this.getAttribute('points'); // get actual top & left
+
+ // TO beats BY, per SMIL 2.1 spec
+ if ( isset(attributes['points']['to']) ) {
+ end = translateValues.call(this, attributes['points']['to'], start);
+
+ var pageXY = Y.Dom.getXY(this.getEl());
+ for (i = 0, len = control.length; i < len; ++i) {
+ control[i] = translateValues.call(this, control[i], start);
+ }
+
+
+ } else if ( isset(attributes['points']['by']) ) {
+ end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
+
+ for (i = 0, len = control.length; i < len; ++i) {
+ control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
+ }
+ }
+
+ this.runtimeAttributes[attr] = [start];
+
+ if (control.length > 0) {
+ this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
+ }
+
+ this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
+ }
+ else {
+ superclass.setRuntimeAttribute.call(this, attr);
+ }
+ };
+
+ var translateValues = function(val, start) {
+ var pageXY = Y.Dom.getXY(this.getEl());
+ val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
+
+ return val;
+ };
+
+ var isset = function(prop) {
+ return (typeof prop !== 'undefined');
+ };
+
+ Y.Motion = Motion;
+})();
+(function() {
+/**
+ * Anim subclass for scrolling elements to a position defined by the "scroll"
+ * member of "attributes". All "scroll" members are arrays with x, y scroll positions.
+ *
Usage: var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);
Basic push button that can execute a user-specified command when
+* pressed.
+*
link
+*
Navigates to a specified url when pressed.
+*
submit
+*
Submits the parent form when pressed.
+*
reset
+*
Resets the parent form when pressed.
+*
checkbox
+*
Maintains a "checked" state that can be toggled on and off.
+*
radio
+*
Maintains a "checked" state that can be toggled on and off. Use with
+* the ButtonGroup class to create a set of controls that are mutually
+* exclusive; checking one button in the set will uncheck all others in
+* the group.
+*
menu
+*
When pressed will show/hide a menu.
+*
split
+*
Can execute a user-specified command or display a menu when pressed.
+*
+* @title Button
+* @namespace YAHOO.widget
+* @requires yahoo, dom, element, event
+* @optional container, menu
+*/
+
+
+(function () {
+
+
+ /**
+ * The Button class creates a rich, graphical button.
+ * @param {String} p_oElement String specifying the id attribute of the
+ * <input>, <button>,
+ * <a>, or <span> element to
+ * be used to create the button.
+ * @param {HTMLInputElement|
+ * HTMLButtonElement|HTMLElement} p_oElement Object reference for the
+ * <input>, <button>,
+ * <a>, or <span> element to be
+ * used to create the button.
+ * @param {Object} p_oElement Object literal specifying a set of
+ * configuration attributes used to create the button.
+ * @param {Object} p_oAttributes Optional. Object literal specifying a set
+ * of configuration attributes used to create the button.
+ * @namespace YAHOO.widget
+ * @class Button
+ * @constructor
+ * @extends YAHOO.util.Element
+ */
+
+
+
+ // Shorthard for utilities
+
+ var Dom = YAHOO.util.Dom,
+ Event = YAHOO.util.Event,
+ Lang = YAHOO.lang,
+ UA = YAHOO.env.ua,
+ Overlay = YAHOO.widget.Overlay,
+ Menu = YAHOO.widget.Menu,
+
+
+ // Private member variables
+
+ m_oButtons = {}, // Collection of all Button instances
+ m_oOverlayManager = null, // YAHOO.widget.OverlayManager instance
+ m_oSubmitTrigger = null, // The button that submitted the form
+ m_oFocusedButton = null; // The button that has focus
+
+
+
+ // Private methods
+
+
+
+ /**
+ * @method createInputElement
+ * @description Creates an <input> element of the
+ * specified type.
+ * @private
+ * @param {String} p_sType String specifying the type of
+ * <input> element to create.
+ * @param {String} p_sName String specifying the name of
+ * <input> element to create.
+ * @param {String} p_sValue String specifying the value of
+ * <input> element to create.
+ * @param {String} p_bChecked Boolean specifying if the
+ * <input> element is to be checked.
+ * @return {HTMLInputElement}
+ */
+ function createInputElement(p_sType, p_sName, p_sValue, p_bChecked) {
+
+ var oInput,
+ sInput;
+
+ if (Lang.isString(p_sType) && Lang.isString(p_sName)) {
+
+ if (UA.ie) {
+
+ /*
+ For IE it is necessary to create the element with the
+ "type," "name," "value," and "checked" properties set all
+ at once.
+ */
+
+ sInput = "";
+
+ oInput = document.createElement(sInput);
+
+ }
+ else {
+
+ oInput = document.createElement("input");
+ oInput.name = p_sName;
+ oInput.type = p_sType;
+
+ if (p_bChecked) {
+
+ oInput.checked = true;
+
+ }
+
+ }
+
+ oInput.value = p_sValue;
+
+ }
+
+ return oInput;
+
+ }
+
+
+ /**
+ * @method setAttributesFromSrcElement
+ * @description Gets the values for all the attributes of the source element
+ * (either <input> or <a>) that
+ * map to Button configuration attributes and sets them into a collection
+ * that is passed to the Button constructor.
+ * @private
+ * @param {HTMLInputElement|HTMLAnchorElement} p_oElement Object reference to the HTML
+ * element (either <input> or <span>
+ * ) used to create the button.
+ * @param {Object} p_oAttributes Object reference for the collection of
+ * configuration attributes used to create the button.
+ */
+ function setAttributesFromSrcElement(p_oElement, p_oAttributes) {
+
+ var sSrcElementNodeName = p_oElement.nodeName.toUpperCase(),
+ sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME),
+ me = this,
+ oAttribute,
+ oRootNode,
+ sText;
+
+
+ /**
+ * @method setAttributeFromDOMAttribute
+ * @description Gets the value of the specified DOM attribute and sets it
+ * into the collection of configuration attributes used to configure
+ * the button.
+ * @private
+ * @param {String} p_sAttribute String representing the name of the
+ * attribute to retrieve from the DOM element.
+ */
+ function setAttributeFromDOMAttribute(p_sAttribute) {
+
+ if (!(p_sAttribute in p_oAttributes)) {
+
+ /*
+ Need to use "getAttributeNode" instead of "getAttribute"
+ because using "getAttribute," IE will return the innerText
+ of a <button> for the value attribute
+ rather than the value of the "value" attribute.
+ */
+
+ oAttribute = p_oElement.getAttributeNode(p_sAttribute);
+
+
+ if (oAttribute && ("value" in oAttribute)) {
+
+ YAHOO.log("Setting attribute \"" + p_sAttribute +
+ "\" using source element's attribute value of \"" +
+ oAttribute.value + "\"", "info", me.toString());
+
+ p_oAttributes[p_sAttribute] = oAttribute.value;
+
+ }
+
+ }
+
+ }
+
+
+ /**
+ * @method setFormElementProperties
+ * @description Gets the value of the attributes from the form element
+ * and sets them into the collection of configuration attributes used to
+ * configure the button.
+ * @private
+ */
+ function setFormElementProperties() {
+
+ setAttributeFromDOMAttribute("type");
+
+ if (p_oAttributes.type == "button") {
+
+ p_oAttributes.type = "push";
+
+ }
+
+ if (!("disabled" in p_oAttributes)) {
+
+ p_oAttributes.disabled = p_oElement.disabled;
+
+ }
+
+ setAttributeFromDOMAttribute("name");
+ setAttributeFromDOMAttribute("value");
+ setAttributeFromDOMAttribute("title");
+
+ }
+
+
+ switch (sSrcElementNodeName) {
+
+ case "A":
+
+ p_oAttributes.type = "link";
+
+ setAttributeFromDOMAttribute("href");
+ setAttributeFromDOMAttribute("target");
+
+ break;
+
+ case "INPUT":
+
+ setFormElementProperties();
+
+ if (!("checked" in p_oAttributes)) {
+
+ p_oAttributes.checked = p_oElement.checked;
+
+ }
+
+ break;
+
+ case "BUTTON":
+
+ setFormElementProperties();
+
+ oRootNode = p_oElement.parentNode.parentNode;
+
+ if (Dom.hasClass(oRootNode, sClass + "-checked")) {
+
+ p_oAttributes.checked = true;
+
+ }
+
+ if (Dom.hasClass(oRootNode, sClass + "-disabled")) {
+
+ p_oAttributes.disabled = true;
+
+ }
+
+ p_oElement.removeAttribute("value");
+
+ p_oElement.setAttribute("type", "button");
+
+ break;
+
+ }
+
+ p_oElement.removeAttribute("id");
+ p_oElement.removeAttribute("name");
+
+ if (!("tabindex" in p_oAttributes)) {
+
+ p_oAttributes.tabindex = p_oElement.tabIndex;
+
+ }
+
+ if (!("label" in p_oAttributes)) {
+
+ // Set the "label" property
+
+ sText = sSrcElementNodeName == "INPUT" ?
+ p_oElement.value : p_oElement.innerHTML;
+
+
+ if (sText && sText.length > 0) {
+
+ p_oAttributes.label = sText;
+
+ }
+
+ }
+
+ }
+
+
+ /**
+ * @method initConfig
+ * @description Initializes the set of configuration attributes that are
+ * used to instantiate the button.
+ * @private
+ * @param {Object} Object representing the button's set of
+ * configuration attributes.
+ */
+ function initConfig(p_oConfig) {
+
+ var oAttributes = p_oConfig.attributes,
+ oSrcElement = oAttributes.srcelement,
+ sSrcElementNodeName = oSrcElement.nodeName.toUpperCase(),
+ me = this;
+
+
+ if (sSrcElementNodeName == this.NODE_NAME) {
+
+ p_oConfig.element = oSrcElement;
+ p_oConfig.id = oSrcElement.id;
+
+ Dom.getElementsBy(function (p_oElement) {
+
+ switch (p_oElement.nodeName.toUpperCase()) {
+
+ case "BUTTON":
+ case "A":
+ case "INPUT":
+
+ setAttributesFromSrcElement.call(me, p_oElement,
+ oAttributes);
+
+ break;
+
+ }
+
+ }, "*", oSrcElement);
+
+ }
+ else {
+
+ switch (sSrcElementNodeName) {
+
+ case "BUTTON":
+ case "A":
+ case "INPUT":
+
+ setAttributesFromSrcElement.call(this, oSrcElement,
+ oAttributes);
+
+ break;
+
+ }
+
+ }
+
+ }
+
+
+
+ // Constructor
+
+ YAHOO.widget.Button = function (p_oElement, p_oAttributes) {
+
+ if (!Overlay && YAHOO.widget.Overlay) {
+
+ Overlay = YAHOO.widget.Overlay;
+
+ }
+
+
+ if (!Menu && YAHOO.widget.Menu) {
+
+ Menu = YAHOO.widget.Menu;
+
+ }
+
+
+ var fnSuperClass = YAHOO.widget.Button.superclass.constructor,
+ oConfig,
+ oElement;
+
+
+ if (arguments.length == 1 && !Lang.isString(p_oElement) && !p_oElement.nodeName) {
+
+ if (!p_oElement.id) {
+
+ p_oElement.id = Dom.generateId();
+
+ YAHOO.log("No value specified for the button's \"id\" " +
+ "attribute. Setting button id to \"" + p_oElement.id +
+ "\".", "info", this.toString());
+
+ }
+
+ YAHOO.log("No source HTML element. Building the button " +
+ "using the set of configuration attributes.", "info", this.toString());
+
+ fnSuperClass.call(this, (this.createButtonElement(p_oElement.type)), p_oElement);
+
+ }
+ else {
+
+ oConfig = { element: null, attributes: (p_oAttributes || {}) };
+
+
+ if (Lang.isString(p_oElement)) {
+
+ oElement = Dom.get(p_oElement);
+
+ if (oElement) {
+
+ if (!oConfig.attributes.id) {
+
+ oConfig.attributes.id = p_oElement;
+
+ }
+
+ YAHOO.log("Building the button using an existing " +
+ "HTML element as a source element.", "info", this.toString());
+
+
+ oConfig.attributes.srcelement = oElement;
+
+ initConfig.call(this, oConfig);
+
+
+ if (!oConfig.element) {
+
+ YAHOO.log("Source element could not be used " +
+ "as is. Creating a new HTML element for " +
+ "the button.", "info", this.toString());
+
+ oConfig.element = this.createButtonElement(oConfig.attributes.type);
+
+ }
+
+ fnSuperClass.call(this, oConfig.element, oConfig.attributes);
+
+ }
+
+ }
+ else if (p_oElement.nodeName) {
+
+ if (!oConfig.attributes.id) {
+
+ if (p_oElement.id) {
+
+ oConfig.attributes.id = p_oElement.id;
+
+ }
+ else {
+
+ oConfig.attributes.id = Dom.generateId();
+
+ YAHOO.log("No value specified for the button's " +
+ "\"id\" attribute. Setting button id to \"" +
+ oConfig.attributes.id + "\".", "info", this.toString());
+
+ }
+
+ }
+
+ YAHOO.log("Building the button using an existing HTML " +
+ "element as a source element.", "info", this.toString());
+
+
+ oConfig.attributes.srcelement = p_oElement;
+
+ initConfig.call(this, oConfig);
+
+
+ if (!oConfig.element) {
+
+ YAHOO.log("Source element could not be used as is." +
+ " Creating a new HTML element for the button.",
+ "info", this.toString());
+
+ oConfig.element = this.createButtonElement(oConfig.attributes.type);
+
+ }
+
+ fnSuperClass.call(this, oConfig.element, oConfig.attributes);
+
+ }
+
+ }
+
+ };
+
+
+
+ YAHOO.extend(YAHOO.widget.Button, YAHOO.util.Element, {
+
+
+ // Protected properties
+
+
+ /**
+ * @property _button
+ * @description Object reference to the button's internal
+ * <a> or <button> element.
+ * @default null
+ * @protected
+ * @type HTMLAnchorElement|HTMLButtonElement
+ */
+ _button: null,
+
+
+ /**
+ * @property _menu
+ * @description Object reference to the button's menu.
+ * @default null
+ * @protected
+ * @type {YAHOO.widget.Overlay|
+ * YAHOO.widget.Menu}
+ */
+ _menu: null,
+
+
+ /**
+ * @property _hiddenFields
+ * @description Object reference to the <input>
+ * element, or array of HTML form elements used to represent the button
+ * when its parent form is submitted.
+ * @default null
+ * @protected
+ * @type HTMLInputElement|Array
+ */
+ _hiddenFields: null,
+
+
+ /**
+ * @property _onclickAttributeValue
+ * @description Object reference to the button's current value for the
+ * "onclick" configuration attribute.
+ * @default null
+ * @protected
+ * @type Object
+ */
+ _onclickAttributeValue: null,
+
+
+ /**
+ * @property _activationKeyPressed
+ * @description Boolean indicating if the key(s) that toggle the button's
+ * "active" state have been pressed.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _activationKeyPressed: false,
+
+
+ /**
+ * @property _activationButtonPressed
+ * @description Boolean indicating if the mouse button that toggles
+ * the button's "active" state has been pressed.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _activationButtonPressed: false,
+
+
+ /**
+ * @property _hasKeyEventHandlers
+ * @description Boolean indicating if the button's "blur", "keydown" and
+ * "keyup" event handlers are assigned
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _hasKeyEventHandlers: false,
+
+
+ /**
+ * @property _hasMouseEventHandlers
+ * @description Boolean indicating if the button's "mouseout,"
+ * "mousedown," and "mouseup" event handlers are assigned
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _hasMouseEventHandlers: false,
+
+
+ /**
+ * @property _nOptionRegionX
+ * @description Number representing the X coordinate of the leftmost edge of the Button's
+ * option region. Applies only to Buttons of type "split".
+ * @default 0
+ * @protected
+ * @type Number
+ */
+ _nOptionRegionX: 0,
+
+
+
+ // Constants
+
+ /**
+ * @property CLASS_NAME_PREFIX
+ * @description Prefix used for all class names applied to a Button.
+ * @default "yui-"
+ * @final
+ * @type String
+ */
+ CLASS_NAME_PREFIX: "yui-",
+
+
+ /**
+ * @property NODE_NAME
+ * @description The name of the node to be used for the button's
+ * root element.
+ * @default "SPAN"
+ * @final
+ * @type String
+ */
+ NODE_NAME: "SPAN",
+
+
+ /**
+ * @property CHECK_ACTIVATION_KEYS
+ * @description Array of numbers representing keys that (when pressed)
+ * toggle the button's "checked" attribute.
+ * @default [32]
+ * @final
+ * @type Array
+ */
+ CHECK_ACTIVATION_KEYS: [32],
+
+
+ /**
+ * @property ACTIVATION_KEYS
+ * @description Array of numbers representing keys that (when presed)
+ * toggle the button's "active" state.
+ * @default [13, 32]
+ * @final
+ * @type Array
+ */
+ ACTIVATION_KEYS: [13, 32],
+
+
+ /**
+ * @property OPTION_AREA_WIDTH
+ * @description Width (in pixels) of the area of a split button that
+ * when pressed will display a menu.
+ * @default 20
+ * @final
+ * @type Number
+ */
+ OPTION_AREA_WIDTH: 20,
+
+
+ /**
+ * @property CSS_CLASS_NAME
+ * @description String representing the CSS class(es) to be applied to
+ * the button's root element.
+ * @default "button"
+ * @final
+ * @type String
+ */
+ CSS_CLASS_NAME: "button",
+
+
+
+ // Protected attribute setter methods
+
+
+ /**
+ * @method _setType
+ * @description Sets the value of the button's "type" attribute.
+ * @protected
+ * @param {String} p_sType String indicating the value for the button's
+ * "type" attribute.
+ */
+ _setType: function (p_sType) {
+
+ if (p_sType == "split") {
+
+ this.on("option", this._onOption);
+
+ }
+
+ },
+
+
+ /**
+ * @method _setLabel
+ * @description Sets the value of the button's "label" attribute.
+ * @protected
+ * @param {String} p_sLabel String indicating the value for the button's
+ * "label" attribute.
+ */
+ _setLabel: function (p_sLabel) {
+
+ this._button.innerHTML = p_sLabel;
+
+
+ /*
+ Remove and add the default class name from the root element
+ for Gecko to ensure that the button shrinkwraps to the label.
+ Without this the button will not be rendered at the correct
+ width when the label changes. The most likely cause for this
+ bug is button's use of the Gecko-specific CSS display type of
+ "-moz-inline-box" to simulate "inline-block" supported by IE,
+ Safari and Opera.
+ */
+
+ var sClass,
+ nGeckoVersion = UA.gecko;
+
+
+ if (nGeckoVersion && nGeckoVersion < 1.9 && Dom.inDocument(this.get("element"))) {
+
+ sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME);
+
+ this.removeClass(sClass);
+
+ Lang.later(0, this, this.addClass, sClass);
+
+ }
+
+ },
+
+
+ /**
+ * @method _setTabIndex
+ * @description Sets the value of the button's "tabindex" attribute.
+ * @protected
+ * @param {Number} p_nTabIndex Number indicating the value for the
+ * button's "tabindex" attribute.
+ */
+ _setTabIndex: function (p_nTabIndex) {
+
+ this._button.tabIndex = p_nTabIndex;
+
+ },
+
+
+ /**
+ * @method _setTitle
+ * @description Sets the value of the button's "title" attribute.
+ * @protected
+ * @param {String} p_nTabIndex Number indicating the value for
+ * the button's "title" attribute.
+ */
+ _setTitle: function (p_sTitle) {
+
+ if (this.get("type") != "link") {
+
+ this._button.title = p_sTitle;
+
+ }
+
+ },
+
+
+ /**
+ * @method _setDisabled
+ * @description Sets the value of the button's "disabled" attribute.
+ * @protected
+ * @param {Boolean} p_bDisabled Boolean indicating the value for
+ * the button's "disabled" attribute.
+ */
+ _setDisabled: function (p_bDisabled) {
+
+ if (this.get("type") != "link") {
+
+ if (p_bDisabled) {
+
+ if (this._menu) {
+
+ this._menu.hide();
+
+ }
+
+ if (this.hasFocus()) {
+
+ this.blur();
+
+ }
+
+ this._button.setAttribute("disabled", "disabled");
+
+ this.addStateCSSClasses("disabled");
+
+ this.removeStateCSSClasses("hover");
+ this.removeStateCSSClasses("active");
+ this.removeStateCSSClasses("focus");
+
+ }
+ else {
+
+ this._button.removeAttribute("disabled");
+
+ this.removeStateCSSClasses("disabled");
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _setHref
+ * @description Sets the value of the button's "href" attribute.
+ * @protected
+ * @param {String} p_sHref String indicating the value for the button's
+ * "href" attribute.
+ */
+ _setHref: function (p_sHref) {
+
+ if (this.get("type") == "link") {
+
+ this._button.href = p_sHref;
+
+ }
+
+ },
+
+
+ /**
+ * @method _setTarget
+ * @description Sets the value of the button's "target" attribute.
+ * @protected
+ * @param {String} p_sTarget String indicating the value for the button's
+ * "target" attribute.
+ */
+ _setTarget: function (p_sTarget) {
+
+ if (this.get("type") == "link") {
+
+ this._button.setAttribute("target", p_sTarget);
+
+ }
+
+ },
+
+
+ /**
+ * @method _setChecked
+ * @description Sets the value of the button's "target" attribute.
+ * @protected
+ * @param {Boolean} p_bChecked Boolean indicating the value for
+ * the button's "checked" attribute.
+ */
+ _setChecked: function (p_bChecked) {
+
+ var sType = this.get("type");
+
+ if (sType == "checkbox" || sType == "radio") {
+
+ if (p_bChecked) {
+ this.addStateCSSClasses("checked");
+ }
+ else {
+ this.removeStateCSSClasses("checked");
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _setMenu
+ * @description Sets the value of the button's "menu" attribute.
+ * @protected
+ * @param {Object} p_oMenu Object indicating the value for the button's
+ * "menu" attribute.
+ */
+ _setMenu: function (p_oMenu) {
+
+ var bLazyLoad = this.get("lazyloadmenu"),
+ oButtonElement = this.get("element"),
+ sMenuCSSClassName,
+
+ /*
+ Boolean indicating if the value of p_oMenu is an instance
+ of YAHOO.widget.Menu or YAHOO.widget.Overlay.
+ */
+
+ bInstance = false,
+ oMenu,
+ oMenuElement,
+ oSrcElement;
+
+
+ function onAppendTo() {
+
+ oMenu.render(oButtonElement.parentNode);
+
+ this.removeListener("appendTo", onAppendTo);
+
+ }
+
+
+ function setMenuContainer() {
+
+ oMenu.cfg.queueProperty("container", oButtonElement.parentNode);
+
+ this.removeListener("appendTo", setMenuContainer);
+
+ }
+
+
+ function initMenu() {
+
+ var oContainer;
+
+ if (oMenu) {
+
+ Dom.addClass(oMenu.element, this.get("menuclassname"));
+ Dom.addClass(oMenu.element, this.CLASS_NAME_PREFIX + this.get("type") + "-button-menu");
+
+ oMenu.showEvent.subscribe(this._onMenuShow, null, this);
+ oMenu.hideEvent.subscribe(this._onMenuHide, null, this);
+ oMenu.renderEvent.subscribe(this._onMenuRender, null, this);
+
+
+ if (Menu && oMenu instanceof Menu) {
+
+ if (bLazyLoad) {
+
+ oContainer = this.get("container");
+
+ if (oContainer) {
+
+ oMenu.cfg.queueProperty("container", oContainer);
+
+ }
+ else {
+
+ this.on("appendTo", setMenuContainer);
+
+ }
+
+ }
+
+ oMenu.cfg.queueProperty("clicktohide", false);
+
+ oMenu.keyDownEvent.subscribe(this._onMenuKeyDown, this, true);
+ oMenu.subscribe("click", this._onMenuClick, this, true);
+
+ this.on("selectedMenuItemChange", this._onSelectedMenuItemChange);
+
+ oSrcElement = oMenu.srcElement;
+
+ if (oSrcElement && oSrcElement.nodeName.toUpperCase() == "SELECT") {
+
+ oSrcElement.style.display = "none";
+ oSrcElement.parentNode.removeChild(oSrcElement);
+
+ }
+
+ }
+ else if (Overlay && oMenu instanceof Overlay) {
+
+ if (!m_oOverlayManager) {
+
+ m_oOverlayManager = new YAHOO.widget.OverlayManager();
+
+ }
+
+ m_oOverlayManager.register(oMenu);
+
+ }
+
+
+ this._menu = oMenu;
+
+
+ if (!bInstance && !bLazyLoad) {
+
+ if (Dom.inDocument(oButtonElement)) {
+
+ oMenu.render(oButtonElement.parentNode);
+
+ }
+ else {
+
+ this.on("appendTo", onAppendTo);
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+ if (Overlay) {
+
+ if (Menu) {
+
+ sMenuCSSClassName = Menu.prototype.CSS_CLASS_NAME;
+
+ }
+
+ if (p_oMenu && Menu && (p_oMenu instanceof Menu)) {
+
+ oMenu = p_oMenu;
+ bInstance = true;
+
+ initMenu.call(this);
+
+ }
+ else if (Overlay && p_oMenu && (p_oMenu instanceof Overlay)) {
+
+ oMenu = p_oMenu;
+ bInstance = true;
+
+ oMenu.cfg.queueProperty("visible", false);
+
+ initMenu.call(this);
+
+ }
+ else if (Menu && Lang.isArray(p_oMenu)) {
+
+ oMenu = new Menu(Dom.generateId(), { lazyload: bLazyLoad, itemdata: p_oMenu });
+
+ this._menu = oMenu;
+
+ this.on("appendTo", initMenu);
+
+ }
+ else if (Lang.isString(p_oMenu)) {
+
+ oMenuElement = Dom.get(p_oMenu);
+
+ if (oMenuElement) {
+
+ if (Menu && Dom.hasClass(oMenuElement, sMenuCSSClassName) ||
+ oMenuElement.nodeName.toUpperCase() == "SELECT") {
+
+ oMenu = new Menu(p_oMenu, { lazyload: bLazyLoad });
+
+ initMenu.call(this);
+
+ }
+ else if (Overlay) {
+
+ oMenu = new Overlay(p_oMenu, { visible: false });
+
+ initMenu.call(this);
+
+ }
+
+ }
+
+ }
+ else if (p_oMenu && p_oMenu.nodeName) {
+
+ if (Menu && Dom.hasClass(p_oMenu, sMenuCSSClassName) ||
+ p_oMenu.nodeName.toUpperCase() == "SELECT") {
+
+ oMenu = new Menu(p_oMenu, { lazyload: bLazyLoad });
+
+ initMenu.call(this);
+
+ }
+ else if (Overlay) {
+
+ if (!p_oMenu.id) {
+
+ Dom.generateId(p_oMenu);
+
+ }
+
+ oMenu = new Overlay(p_oMenu, { visible: false });
+
+ initMenu.call(this);
+
+ }
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _setOnClick
+ * @description Sets the value of the button's "onclick" attribute.
+ * @protected
+ * @param {Object} p_oObject Object indicating the value for the button's
+ * "onclick" attribute.
+ */
+ _setOnClick: function (p_oObject) {
+
+ /*
+ Remove any existing listeners if a "click" event handler
+ has already been specified.
+ */
+
+ if (this._onclickAttributeValue &&
+ (this._onclickAttributeValue != p_oObject)) {
+
+ this.removeListener("click", this._onclickAttributeValue.fn);
+
+ this._onclickAttributeValue = null;
+
+ }
+
+
+ if (!this._onclickAttributeValue &&
+ Lang.isObject(p_oObject) &&
+ Lang.isFunction(p_oObject.fn)) {
+
+ this.on("click", p_oObject.fn, p_oObject.obj, p_oObject.scope);
+
+ this._onclickAttributeValue = p_oObject;
+
+ }
+
+ },
+
+
+
+ // Protected methods
+
+
+
+ /**
+ * @method _isActivationKey
+ * @description Determines if the specified keycode is one that toggles
+ * the button's "active" state.
+ * @protected
+ * @param {Number} p_nKeyCode Number representing the keycode to
+ * be evaluated.
+ * @return {Boolean}
+ */
+ _isActivationKey: function (p_nKeyCode) {
+
+ var sType = this.get("type"),
+ aKeyCodes = (sType == "checkbox" || sType == "radio") ?
+ this.CHECK_ACTIVATION_KEYS : this.ACTIVATION_KEYS,
+
+ nKeyCodes = aKeyCodes.length,
+ bReturnVal = false,
+ i;
+
+
+ if (nKeyCodes > 0) {
+
+ i = nKeyCodes - 1;
+
+ do {
+
+ if (p_nKeyCode == aKeyCodes[i]) {
+
+ bReturnVal = true;
+ break;
+
+ }
+
+ }
+ while (i--);
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _isSplitButtonOptionKey
+ * @description Determines if the specified keycode is one that toggles
+ * the display of the split button's menu.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ * @return {Boolean}
+ */
+ _isSplitButtonOptionKey: function (p_oEvent) {
+
+ var bShowMenu = (Event.getCharCode(p_oEvent) == 40);
+
+
+ var onKeyPress = function (p_oEvent) {
+
+ Event.preventDefault(p_oEvent);
+
+ this.removeListener("keypress", onKeyPress);
+
+ };
+
+
+ // Prevent the browser from scrolling the window
+ if (bShowMenu) {
+
+ if (UA.opera) {
+
+ this.on("keypress", onKeyPress);
+
+ }
+
+ Event.preventDefault(p_oEvent);
+ }
+
+ return bShowMenu;
+
+ },
+
+
+ /**
+ * @method _addListenersToForm
+ * @description Adds event handlers to the button's form.
+ * @protected
+ */
+ _addListenersToForm: function () {
+
+ var oForm = this.getForm(),
+ onFormKeyPress = YAHOO.widget.Button.onFormKeyPress,
+ bHasKeyPressListener,
+ oSrcElement,
+ aListeners,
+ nListeners,
+ i;
+
+
+ if (oForm) {
+
+ Event.on(oForm, "reset", this._onFormReset, null, this);
+ Event.on(oForm, "submit", this._onFormSubmit, null, this);
+
+ oSrcElement = this.get("srcelement");
+
+
+ if (this.get("type") == "submit" ||
+ (oSrcElement && oSrcElement.type == "submit"))
+ {
+
+ aListeners = Event.getListeners(oForm, "keypress");
+ bHasKeyPressListener = false;
+
+ if (aListeners) {
+
+ nListeners = aListeners.length;
+
+ if (nListeners > 0) {
+
+ i = nListeners - 1;
+
+ do {
+
+ if (aListeners[i].fn == onFormKeyPress) {
+
+ bHasKeyPressListener = true;
+ break;
+
+ }
+
+ }
+ while (i--);
+
+ }
+
+ }
+
+
+ if (!bHasKeyPressListener) {
+
+ Event.on(oForm, "keypress", onFormKeyPress);
+
+ }
+
+ }
+
+ }
+
+ },
+
+
+
+ /**
+ * @method _showMenu
+ * @description Shows the button's menu.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event) that triggered
+ * the display of the menu.
+ */
+ _showMenu: function (p_oEvent) {
+
+ if (YAHOO.widget.MenuManager) {
+ YAHOO.widget.MenuManager.hideVisible();
+ }
+
+
+ if (m_oOverlayManager) {
+ m_oOverlayManager.hideAll();
+ }
+
+
+ var oMenu = this._menu,
+ aMenuAlignment = this.get("menualignment"),
+ bFocusMenu = this.get("focusmenu"),
+ fnFocusMethod;
+
+
+ if (this._renderedMenu) {
+
+ oMenu.cfg.setProperty("context",
+ [this.get("element"), aMenuAlignment[0], aMenuAlignment[1]]);
+
+ oMenu.cfg.setProperty("preventcontextoverlap", true);
+ oMenu.cfg.setProperty("constraintoviewport", true);
+
+ }
+ else {
+
+ oMenu.cfg.queueProperty("context",
+ [this.get("element"), aMenuAlignment[0], aMenuAlignment[1]]);
+
+ oMenu.cfg.queueProperty("preventcontextoverlap", true);
+ oMenu.cfg.queueProperty("constraintoviewport", true);
+
+ }
+
+
+ /*
+ Refocus the Button before showing its Menu in case the call to
+ YAHOO.widget.MenuManager.hideVisible() resulted in another element in the
+ DOM being focused after another Menu was hidden.
+ */
+
+ this.focus();
+
+
+ if (Menu && oMenu && (oMenu instanceof Menu)) {
+
+ // Since Menus automatically focus themselves when made visible, temporarily
+ // replace the Menu focus method so that the value of the Button's "focusmenu"
+ // attribute determines if the Menu should be focus when made visible.
+
+ fnFocusMethod = oMenu.focus;
+
+ oMenu.focus = function () {};
+
+ if (this._renderedMenu) {
+
+ oMenu.cfg.setProperty("minscrollheight", this.get("menuminscrollheight"));
+ oMenu.cfg.setProperty("maxheight", this.get("menumaxheight"));
+
+ }
+ else {
+
+ oMenu.cfg.queueProperty("minscrollheight", this.get("menuminscrollheight"));
+ oMenu.cfg.queueProperty("maxheight", this.get("menumaxheight"));
+
+ }
+
+
+ oMenu.show();
+
+ oMenu.focus = fnFocusMethod;
+
+ oMenu.align();
+
+
+ /*
+ Stop the propagation of the event so that the MenuManager
+ doesn't blur the menu after it gets focus.
+ */
+
+ if (p_oEvent.type == "mousedown") {
+ Event.stopPropagation(p_oEvent);
+ }
+
+
+ if (bFocusMenu) {
+ oMenu.focus();
+ }
+
+ }
+ else if (Overlay && oMenu && (oMenu instanceof Overlay)) {
+
+ if (!this._renderedMenu) {
+ oMenu.render(this.get("element").parentNode);
+ }
+
+ oMenu.show();
+ oMenu.align();
+
+ }
+
+ },
+
+
+ /**
+ * @method _hideMenu
+ * @description Hides the button's menu.
+ * @protected
+ */
+ _hideMenu: function () {
+
+ var oMenu = this._menu;
+
+ if (oMenu) {
+
+ oMenu.hide();
+
+ }
+
+ },
+
+
+
+
+ // Protected event handlers
+
+
+ /**
+ * @method _onMouseOver
+ * @description "mouseover" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseOver: function (p_oEvent) {
+
+ var sType = this.get("type"),
+ oElement,
+ nOptionRegionX;
+
+
+ if (sType === "split") {
+
+ oElement = this.get("element");
+ nOptionRegionX =
+ (Dom.getX(oElement) + (oElement.offsetWidth - this.OPTION_AREA_WIDTH));
+
+ this._nOptionRegionX = nOptionRegionX;
+
+ }
+
+
+ if (!this._hasMouseEventHandlers) {
+
+ if (sType === "split") {
+
+ this.on("mousemove", this._onMouseMove);
+
+ }
+
+ this.on("mouseout", this._onMouseOut);
+
+ this._hasMouseEventHandlers = true;
+
+ }
+
+
+ this.addStateCSSClasses("hover");
+
+
+ if (sType === "split" && (Event.getPageX(p_oEvent) > nOptionRegionX)) {
+
+ this.addStateCSSClasses("hoveroption");
+
+ }
+
+
+ if (this._activationButtonPressed) {
+
+ this.addStateCSSClasses("active");
+
+ }
+
+
+ if (this._bOptionPressed) {
+
+ this.addStateCSSClasses("activeoption");
+
+ }
+
+
+ if (this._activationButtonPressed || this._bOptionPressed) {
+
+ Event.removeListener(document, "mouseup", this._onDocumentMouseUp);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMouseMove
+ * @description "mousemove" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseMove: function (p_oEvent) {
+
+ var nOptionRegionX = this._nOptionRegionX;
+
+ if (nOptionRegionX) {
+
+ if (Event.getPageX(p_oEvent) > nOptionRegionX) {
+
+ this.addStateCSSClasses("hoveroption");
+
+ }
+ else {
+
+ this.removeStateCSSClasses("hoveroption");
+
+ }
+
+ }
+
+ },
+
+ /**
+ * @method _onMouseOut
+ * @description "mouseout" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseOut: function (p_oEvent) {
+
+ var sType = this.get("type");
+
+ this.removeStateCSSClasses("hover");
+
+
+ if (sType != "menu") {
+
+ this.removeStateCSSClasses("active");
+
+ }
+
+
+ if (this._activationButtonPressed || this._bOptionPressed) {
+
+ Event.on(document, "mouseup", this._onDocumentMouseUp, null, this);
+
+ }
+
+
+ if (sType === "split" && (Event.getPageX(p_oEvent) > this._nOptionRegionX)) {
+
+ this.removeStateCSSClasses("hoveroption");
+
+ }
+
+ },
+
+
+ /**
+ * @method _onDocumentMouseUp
+ * @description "mouseup" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onDocumentMouseUp: function (p_oEvent) {
+
+ this._activationButtonPressed = false;
+ this._bOptionPressed = false;
+
+ var sType = this.get("type"),
+ oTarget,
+ oMenuElement;
+
+ if (sType == "menu" || sType == "split") {
+
+ oTarget = Event.getTarget(p_oEvent);
+ oMenuElement = this._menu.element;
+
+ if (oTarget != oMenuElement &&
+ !Dom.isAncestor(oMenuElement, oTarget)) {
+
+ this.removeStateCSSClasses((sType == "menu" ?
+ "active" : "activeoption"));
+
+ this._hideMenu();
+
+ }
+
+ }
+
+ Event.removeListener(document, "mouseup", this._onDocumentMouseUp);
+
+ },
+
+
+ /**
+ * @method _onMouseDown
+ * @description "mousedown" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseDown: function (p_oEvent) {
+
+ var sType,
+ bReturnVal = true;
+
+
+ function onMouseUp() {
+
+ this._hideMenu();
+ this.removeListener("mouseup", onMouseUp);
+
+ }
+
+
+ if ((p_oEvent.which || p_oEvent.button) == 1) {
+
+
+ if (!this.hasFocus()) {
+
+ this.focus();
+
+ }
+
+
+ sType = this.get("type");
+
+
+ if (sType == "split") {
+
+ if (Event.getPageX(p_oEvent) > this._nOptionRegionX) {
+
+ this.fireEvent("option", p_oEvent);
+ bReturnVal = false;
+
+ }
+ else {
+
+ this.addStateCSSClasses("active");
+
+ this._activationButtonPressed = true;
+
+ }
+
+ }
+ else if (sType == "menu") {
+
+ if (this.isActive()) {
+
+ this._hideMenu();
+
+ this._activationButtonPressed = false;
+
+ }
+ else {
+
+ this._showMenu(p_oEvent);
+
+ this._activationButtonPressed = true;
+
+ }
+
+ }
+ else {
+
+ this.addStateCSSClasses("active");
+
+ this._activationButtonPressed = true;
+
+ }
+
+
+
+ if (sType == "split" || sType == "menu") {
+
+ this._hideMenuTimer = Lang.later(250, this, this.on, ["mouseup", onMouseUp]);
+
+ }
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _onMouseUp
+ * @description "mouseup" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseUp: function (p_oEvent) {
+
+ var sType = this.get("type"),
+ oHideMenuTimer = this._hideMenuTimer,
+ bReturnVal = true;
+
+
+ if (oHideMenuTimer) {
+
+ oHideMenuTimer.cancel();
+
+ }
+
+
+ if (sType == "checkbox" || sType == "radio") {
+
+ this.set("checked", !(this.get("checked")));
+
+ }
+
+
+ this._activationButtonPressed = false;
+
+
+ if (sType != "menu") {
+
+ this.removeStateCSSClasses("active");
+
+ }
+
+
+ if (sType == "split" && Event.getPageX(p_oEvent) > this._nOptionRegionX) {
+
+ bReturnVal = false;
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _onFocus
+ * @description "focus" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onFocus: function (p_oEvent) {
+
+ var oElement;
+
+ this.addStateCSSClasses("focus");
+
+ if (this._activationKeyPressed) {
+
+ this.addStateCSSClasses("active");
+
+ }
+
+ m_oFocusedButton = this;
+
+
+ if (!this._hasKeyEventHandlers) {
+
+ oElement = this._button;
+
+ Event.on(oElement, "blur", this._onBlur, null, this);
+ Event.on(oElement, "keydown", this._onKeyDown, null, this);
+ Event.on(oElement, "keyup", this._onKeyUp, null, this);
+
+ this._hasKeyEventHandlers = true;
+
+ }
+
+
+ this.fireEvent("focus", p_oEvent);
+
+ },
+
+
+ /**
+ * @method _onBlur
+ * @description "blur" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onBlur: function (p_oEvent) {
+
+ this.removeStateCSSClasses("focus");
+
+ if (this.get("type") != "menu") {
+
+ this.removeStateCSSClasses("active");
+
+ }
+
+ if (this._activationKeyPressed) {
+
+ Event.on(document, "keyup", this._onDocumentKeyUp, null, this);
+
+ }
+
+
+ m_oFocusedButton = null;
+
+ this.fireEvent("blur", p_oEvent);
+
+ },
+
+
+ /**
+ * @method _onDocumentKeyUp
+ * @description "keyup" event handler for the document.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onDocumentKeyUp: function (p_oEvent) {
+
+ if (this._isActivationKey(Event.getCharCode(p_oEvent))) {
+
+ this._activationKeyPressed = false;
+
+ Event.removeListener(document, "keyup", this._onDocumentKeyUp);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onKeyDown
+ * @description "keydown" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onKeyDown: function (p_oEvent) {
+
+ var oMenu = this._menu;
+
+
+ if (this.get("type") == "split" &&
+ this._isSplitButtonOptionKey(p_oEvent)) {
+
+ this.fireEvent("option", p_oEvent);
+
+ }
+ else if (this._isActivationKey(Event.getCharCode(p_oEvent))) {
+
+ if (this.get("type") == "menu") {
+
+ this._showMenu(p_oEvent);
+
+ }
+ else {
+
+ this._activationKeyPressed = true;
+
+ this.addStateCSSClasses("active");
+
+ }
+
+ }
+
+
+ if (oMenu && oMenu.cfg.getProperty("visible") &&
+ Event.getCharCode(p_oEvent) == 27) {
+
+ oMenu.hide();
+ this.focus();
+
+ }
+
+ },
+
+
+ /**
+ * @method _onKeyUp
+ * @description "keyup" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onKeyUp: function (p_oEvent) {
+
+ var sType;
+
+ if (this._isActivationKey(Event.getCharCode(p_oEvent))) {
+
+ sType = this.get("type");
+
+ if (sType == "checkbox" || sType == "radio") {
+
+ this.set("checked", !(this.get("checked")));
+
+ }
+
+ this._activationKeyPressed = false;
+
+ if (this.get("type") != "menu") {
+
+ this.removeStateCSSClasses("active");
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onClick
+ * @description "click" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onClick: function (p_oEvent) {
+
+ var sType = this.get("type"),
+ oForm,
+ oSrcElement,
+ bReturnVal;
+
+
+ switch (sType) {
+
+ case "submit":
+
+ if (p_oEvent.returnValue !== false) {
+
+ this.submitForm();
+
+ }
+
+ break;
+
+ case "reset":
+
+ oForm = this.getForm();
+
+ if (oForm) {
+
+ oForm.reset();
+
+ }
+
+ break;
+
+
+ case "split":
+
+ if (this._nOptionRegionX > 0 &&
+ (Event.getPageX(p_oEvent) > this._nOptionRegionX)) {
+
+ bReturnVal = false;
+
+ }
+ else {
+
+ this._hideMenu();
+
+ oSrcElement = this.get("srcelement");
+
+ if (oSrcElement && oSrcElement.type == "submit" &&
+ p_oEvent.returnValue !== false) {
+
+ this.submitForm();
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _onDblClick
+ * @description "dblclick" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onDblClick: function (p_oEvent) {
+
+ var bReturnVal = true;
+
+ if (this.get("type") == "split" && Event.getPageX(p_oEvent) > this._nOptionRegionX) {
+
+ bReturnVal = false;
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _onAppendTo
+ * @description "appendTo" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onAppendTo: function (p_oEvent) {
+
+ /*
+ It is necessary to call "_addListenersToForm" using
+ "setTimeout" to make sure that the button's "form" property
+ returns a node reference. Sometimes, if you try to get the
+ reference immediately after appending the field, it is null.
+ */
+
+ Lang.later(0, this, this._addListenersToForm);
+
+ },
+
+
+ /**
+ * @method _onFormReset
+ * @description "reset" event handler for the button's form.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event
+ * object passed back by the event utility (YAHOO.util.Event).
+ */
+ _onFormReset: function (p_oEvent) {
+
+ var sType = this.get("type"),
+ oMenu = this._menu;
+
+ if (sType == "checkbox" || sType == "radio") {
+
+ this.resetValue("checked");
+
+ }
+
+
+ if (Menu && oMenu && (oMenu instanceof Menu)) {
+
+ this.resetValue("selectedMenuItem");
+
+ }
+
+ },
+
+
+ /**
+ * @method _onFormSubmit
+ * @description "submit" event handler for the button's form.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event
+ * object passed back by the event utility (YAHOO.util.Event).
+ */
+ _onFormSubmit: function (p_oEvent) {
+
+ this.createHiddenFields();
+
+ },
+
+
+ /**
+ * @method _onDocumentMouseDown
+ * @description "mousedown" event handler for the document.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onDocumentMouseDown: function (p_oEvent) {
+
+ var oTarget = Event.getTarget(p_oEvent),
+ oButtonElement = this.get("element"),
+ oMenuElement = this._menu.element;
+
+
+ if (oTarget != oButtonElement &&
+ !Dom.isAncestor(oButtonElement, oTarget) &&
+ oTarget != oMenuElement &&
+ !Dom.isAncestor(oMenuElement, oTarget)) {
+
+ this._hideMenu();
+
+ // In IE when the user mouses down on a focusable element
+ // that element will be focused and become the "activeElement".
+ // (http://msdn.microsoft.com/en-us/library/ms533065(VS.85).aspx)
+ // However, there is a bug in IE where if there is a
+ // positioned element with a focused descendant that is
+ // hidden in response to the mousedown event, the target of
+ // the mousedown event will appear to have focus, but will
+ // not be set as the activeElement. This will result
+ // in the element not firing key events, even though it
+ // appears to have focus. The following call to "setActive"
+ // fixes this bug.
+
+ if (UA.ie && oTarget.focus) {
+ oTarget.setActive();
+ }
+
+ Event.removeListener(document, "mousedown",
+ this._onDocumentMouseDown);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onOption
+ * @description "option" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onOption: function (p_oEvent) {
+
+ if (this.hasClass(this.CLASS_NAME_PREFIX + "split-button-activeoption")) {
+
+ this._hideMenu();
+
+ this._bOptionPressed = false;
+
+ }
+ else {
+
+ this._showMenu(p_oEvent);
+
+ this._bOptionPressed = true;
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuShow
+ * @description "show" event handler for the button's menu.
+ * @private
+ * @param {String} p_sType String representing the name of the event
+ * that was fired.
+ */
+ _onMenuShow: function (p_sType) {
+
+ Event.on(document, "mousedown", this._onDocumentMouseDown,
+ null, this);
+
+ var sState = (this.get("type") == "split") ? "activeoption" : "active";
+
+ this.addStateCSSClasses(sState);
+
+ },
+
+
+ /**
+ * @method _onMenuHide
+ * @description "hide" event handler for the button's menu.
+ * @private
+ * @param {String} p_sType String representing the name of the event
+ * that was fired.
+ */
+ _onMenuHide: function (p_sType) {
+
+ var sState = (this.get("type") == "split") ? "activeoption" : "active";
+
+ this.removeStateCSSClasses(sState);
+
+
+ if (this.get("type") == "split") {
+
+ this._bOptionPressed = false;
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuKeyDown
+ * @description "keydown" event handler for the button's menu.
+ * @private
+ * @param {String} p_sType String representing the name of the event
+ * that was fired.
+ * @param {Array} p_aArgs Array of arguments sent when the event
+ * was fired.
+ */
+ _onMenuKeyDown: function (p_sType, p_aArgs) {
+
+ var oEvent = p_aArgs[0];
+
+ if (Event.getCharCode(oEvent) == 27) {
+
+ this.focus();
+
+ if (this.get("type") == "split") {
+
+ this._bOptionPressed = false;
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuRender
+ * @description "render" event handler for the button's menu.
+ * @private
+ * @param {String} p_sType String representing the name of the
+ * event thatwas fired.
+ */
+ _onMenuRender: function (p_sType) {
+
+ var oButtonElement = this.get("element"),
+ oButtonParent = oButtonElement.parentNode,
+ oMenu = this._menu,
+ oMenuElement = oMenu.element,
+ oSrcElement = oMenu.srcElement,
+ oItem;
+
+
+ if (oButtonParent != oMenuElement.parentNode) {
+
+ oButtonParent.appendChild(oMenuElement);
+
+ }
+
+ this._renderedMenu = true;
+
+ // If the user has designated an ";
+ el.innerHTML = html;
+
+ pager.appendChild(el);
+ }
+
+ // Show the pager now
+ Dom.setStyle(pager, "visibility", "visible");
+ },
+
+ /**
+ * Update the UI for the pager menu based on the current page and
+ * the number of pages. If the number of pages is greater than
+ * MAX_PAGER_BUTTONS, then the selection of pages is provided by a drop
+ * down menu instead of a set of buttons.
+ *
+ * @method _updatePagerMenu
+ * @protected
+ */
+ _updatePagerMenu: function () {
+ var carousel = this,
+ css = carousel.CLASSES,
+ cur = carousel._pages.cur, // current page
+ el,
+ i,
+ item,
+ n = carousel.get("numVisible"),
+ num = carousel._pages.num, // total pages
+ pager = carousel._pages.el, // the pager container element
+ sel;
+
+ if (num === 0) {
+ return;// don't do anything if number of pages is 0
+ }
+
+ sel = document.createElement("SELECT");
+
+
+ if (!sel) {
+ YAHOO.log("Unable to create the pager menu", "error",
+ WidgetName);
+ return;
+ }
+
+ // Hide the pager before redrawing it
+ Dom.setStyle(pager, "visibility", "hidden");
+
+ // Remove all nodes from the pager
+ while (pager.firstChild) {
+ pager.removeChild(pager.firstChild);
+ }
+
+ for (i = 0; i < num; i++) {
+
+ el = document.createElement("OPTION");
+ el.value = i+1;
+ el.innerHTML = carousel.STRINGS.PAGER_PREFIX_TEXT+" "+(i+1);
+
+ if (i == cur) {
+ el.setAttribute("selected", "selected");
+ }
+
+ sel.appendChild(el);
+ }
+
+ el = document.createElement("FORM");
+ if (!el) {
+ YAHOO.log("Unable to create the pager menu", "error",
+ WidgetName);
+ } else {
+ el.appendChild(sel);
+ pager.appendChild(el);
+ }
+
+ // Show the pager now
+ Event.addListener(sel, "change", carousel._pagerChangeHandler, this, true);
+ Dom.setStyle(pager, "visibility", "visible");
+ },
+
+ /**
+ * Set the correct tab index for the Carousel items.
+ *
+ * @method _updateTabIndex
+ * @param el {Object} The element to be focussed
+ * @protected
+ */
+ _updateTabIndex: function (el) {
+ var carousel = this;
+
+ if (el) {
+ if (carousel._focusableItemEl) {
+ carousel._focusableItemEl.tabIndex = -1;
+ }
+ carousel._focusableItemEl = el;
+ el.tabIndex = 0;
+ }
+ },
+
+ /**
+ * Validate animation parameters.
+ *
+ * @method _validateAnimation
+ * @param cfg {Object} The animation configuration
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateAnimation: function (cfg) {
+ var rv = true;
+
+ if (JS.isObject(cfg)) {
+ if (cfg.speed) {
+ rv = rv && JS.isNumber(cfg.speed);
+ }
+ if (cfg.effect) {
+ rv = rv && JS.isFunction(cfg.effect);
+ } else if (!JS.isUndefined(YAHOO.util.Easing)) {
+ cfg.effect = YAHOO.util.Easing.easeOut;
+ }
+ } else {
+ rv = false;
+ }
+
+ return rv;
+ },
+
+ /**
+ * Validate the firstVisible value.
+ *
+ * @method _validateFirstVisible
+ * @param val {Number} The first visible value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateFirstVisible: function (val) {
+ var carousel = this, numItems = carousel.get("numItems");
+
+ if (JS.isNumber(val)) {
+ if (numItems === 0 && val == numItems) {
+ return true;
+ } else {
+ return (val >= 0 && val < numItems);
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Validate and navigation parameters.
+ *
+ * @method _validateNavigation
+ * @param cfg {Object} The navigation configuration
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateNavigation : function (cfg) {
+ var i;
+
+ if (!JS.isObject(cfg)) {
+ return false;
+ }
+
+ if (cfg.prev) {
+ if (!JS.isArray(cfg.prev)) {
+ return false;
+ }
+ for (i in cfg.prev) {
+ if (cfg.prev.hasOwnProperty(i)) {
+ if (!JS.isString(cfg.prev[i].nodeName)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ if (cfg.next) {
+ if (!JS.isArray(cfg.next)) {
+ return false;
+ }
+ for (i in cfg.next) {
+ if (cfg.next.hasOwnProperty(i)) {
+ if (!JS.isString(cfg.next[i].nodeName)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Validate the numItems value.
+ *
+ * @method _validateNumItems
+ * @param val {Number} The numItems value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateNumItems: function (val) {
+ return JS.isNumber(val) && (val >= 0);
+ },
+
+ /**
+ * Validate the numVisible value.
+ *
+ * @method _validateNumVisible
+ * @param val {Number} The numVisible value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateNumVisible: function (val) {
+ var rv = false;
+
+ if (JS.isNumber(val)) {
+ rv = val > 0 && val <= this.get("numItems");
+ } else if (JS.isArray(val)) {
+ if (JS.isNumber(val[0]) && JS.isNumber(val[1])) {
+ rv = val[0] * val[1] > 0 && val.length == 2;
+ }
+ }
+
+ return rv;
+ },
+
+ /**
+ * Validate the revealAmount value.
+ *
+ * @method _validateRevealAmount
+ * @param val {Number} The revealAmount value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateRevealAmount: function (val) {
+ var rv = false;
+
+ if (JS.isNumber(val)) {
+ rv = val >= 0 && val < 100;
+ }
+
+ return rv;
+ },
+
+ /**
+ * Validate the scrollIncrement value.
+ *
+ * @method _validateScrollIncrement
+ * @param val {Number} The scrollIncrement value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateScrollIncrement: function (val) {
+ var rv = false;
+
+ if (JS.isNumber(val)) {
+ rv = (val > 0 && val < this.get("numItems"));
+ }
+
+ return rv;
+ }
+
+ });
+
+})();
+/*
+;; Local variables: **
+;; mode: js2 **
+;; indent-tabs-mode: nil **
+;; End: **
+*/
+YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.8.1", build: "19"});
diff --git a/lib/yui/2.8.1/build/carousel/carousel-min.js b/lib/yui/2.8.1/build/carousel/carousel-min.js
new file mode 100644
index 00000000000..257caa31868
--- /dev/null
+++ b/lib/yui/2.8.1/build/carousel/carousel-min.js
@@ -0,0 +1,12 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
+(function(){var P;YAHOO.widget.Carousel=function(s,r){YAHOO.widget.Carousel.superclass.constructor.call(this,s,r);};var U=YAHOO.widget.Carousel,e=YAHOO.util.Dom,c=YAHOO.util.Event,p=YAHOO.lang;P="Carousel";var T={},F="afterScroll",g="allItemsRemoved",b="beforeHide",J="beforePageChange",i="beforeScroll",Y="beforeShow",B="blur",X="focus",a="hide",S="itemAdded",o="itemRemoved",Q="itemReplaced",C="itemSelected",L="loadItems",I="navigationStateChange",h="pageChange",H="render",V="show",Z="startAutoPlay",q="stopAutoPlay",K="uiUpdate";function G(r,s){var t;for(t in s){if(s.hasOwnProperty(t)){e.setStyle(r,t,s[t]);}}}function W(s,r){var t=document.createElement(s);r=r||{};if(r.className){e.addClass(t,r.className);}if(r.styles){G(t,r.styles);}if(r.parent){r.parent.appendChild(t);}if(r.id){t.setAttribute("id",r.id);}if(r.content){if(r.content.nodeName){t.appendChild(r.content);}else{t.innerHTML=r.content;}}return t;}function d(t,s,r){var v;if(!t){return 0;}function u(y,x){var z;if(x=="marginRight"&&YAHOO.env.ua.webkit){z=parseInt(e.getStyle(y,"marginLeft"),10);}else{z=parseInt(e.getStyle(y,x),10);}return p.isNumber(z)?z:0;}function w(y,x){var z;if(x=="marginRight"&&YAHOO.env.ua.webkit){z=parseFloat(e.getStyle(y,"marginLeft"));}else{z=parseFloat(e.getStyle(y,x));}return p.isNumber(z)?z:0;}if(typeof r=="undefined"){r="int";}switch(s){case"height":v=t.offsetHeight;if(v>0){v+=u(t,"marginTop")+u(t,"marginBottom");}else{v=w(t,"height")+u(t,"marginTop")+u(t,"marginBottom")+u(t,"borderTopWidth")+u(t,"borderBottomWidth")+u(t,"paddingTop")+u(t,"paddingBottom");}break;case"width":v=t.offsetWidth;if(v>0){v+=u(t,"marginLeft")+u(t,"marginRight");}else{v=w(t,"width")+u(t,"marginLeft")+u(t,"marginRight")+u(t,"borderLeftWidth")+u(t,"borderRightWidth")+u(t,"paddingLeft")+u(t,"paddingRight");}break;default:if(r=="int"){v=u(t,s);}else{if(r=="float"){v=w(t,s);}else{v=e.getStyle(t,s);}}break;}return v;}function O(w){var u=this,x,t,s=0,v=u.get("firstVisible"),r=false;if(u._itemsTable.numItems===0){return 0;}t=u._itemsTable.items[v]||u._itemsTable.loading[v];if(p.isUndefined(t)){return 0;}x=e.get(t.id);if(typeof w=="undefined"){r=u.get("isVertical");}else{r=w=="height";}if(this._itemAttrCache[w]){return this._itemAttrCache[w];}if(r){s=d(x,"height");}else{s=d(x,"width");}this._itemAttrCache[w]=s;return s;}function N(){var s=this,t,r;t=s.get("isVertical");r=O.call(s,t?"height":"width");return(r*s.get("revealAmount")/100);}function m(w){var AH=this,z=AH._cols,v=AH._rows,u,AC,AB,t,x,AD,AJ=0,AE,s,AG,AA={},y=0,AI=AH._itemsTable,AF=AI.items,r=AI.loading;AB=AH.get("isVertical");AC=O.call(AH,AB?"height":"width");AG=N.call(AH);while(y=0&&vu){w=D.call(AA,w);AA.scrollTo(w);}}function l(){var t=false,w=this,s=w.CLASSES,v,r,u;if(!w._hasRendered){return;}r=w.get("navigation");u=w._firstItem+w.get("numVisible");if(r.prev){if(w.get("numItems")===0||w._firstItem===0){if(w.get("numItems")===0||!w.get("isCircular")){c.removeListener(r.prev,"click",f);e.addClass(r.prev,s.FIRST_NAV_DISABLED);for(v=0;v=w.get("numItems")){if(!w.get("isCircular")){c.removeListener(r.next,"click",k);e.addClass(r.next,s.DISABLED);for(v=0;vu.CONFIG.MAX_PAGER_BUTTONS){u._updatePagerMenu();}else{u._updatePagerButtons();}}function M(r,s){switch(s){case"height":return d(r,"marginTop")+d(r,"marginBottom")+d(r,"paddingTop")+d(r,"paddingBottom")+d(r,"borderTopWidth")+d(r,"borderBottomWidth");case"width":return d(r,"marginLeft")+d(r,"marginRight")+d(r,"paddingLeft")+d(r,"paddingRight")+d(r,"borderLeftWidth")+d(r,"borderRightWidth");default:break;}return d(r,s);}function A(s){var r=this;if(!p.isObject(s)){return;}switch(s.ev){case S:r._syncUiForItemAdd(s);break;case o:r._syncUiForItemRemove(s);break;case Q:r._syncUiForItemReplace(s);break;case L:r._syncUiForLazyLoading(s);break;}r.fireEvent(K);}function E(u,s){var w=this,v=w.get("currentPage"),t,r=w.get("numVisible");
+t=parseInt(w._firstItem/r,10);if(t!=v){w.setAttributeConfig("currentPage",{value:t});w.fireEvent(h,t);}if(w.get("selectOnScroll")){if(w.get("selectedItem")!=w._selectedItem){w.set("selectedItem",w._selectedItem);}}clearTimeout(w._autoPlayTimer);delete w._autoPlayTimer;if(w.isAutoPlayOn()){w.startAutoPlay();}w.fireEvent(F,{first:w._firstItem,last:s},w);}U.getById=function(r){return T[r]?T[r].object:false;};YAHOO.extend(U,YAHOO.util.Element,{_rows:null,_cols:null,_animObj:null,_carouselEl:null,_clipEl:null,_firstItem:0,_hasFocus:false,_hasRendered:false,_isAnimationInProgress:false,_isAutoPlayInProgress:false,_itemsTable:null,_navBtns:null,_navEl:null,_nextEnabled:true,_pages:null,_pagination:{},_prevEnabled:true,_recomputeSize:true,_itemAttrCache:{},CLASSES:{BUTTON:"yui-carousel-button",CAROUSEL:"yui-carousel",CAROUSEL_EL:"yui-carousel-element",CONTAINER:"yui-carousel-container",CONTENT:"yui-carousel-content",DISABLED:"yui-carousel-button-disabled",FIRST_NAV:" yui-carousel-first-button",FIRST_NAV_DISABLED:"yui-carousel-first-button-disabled",FIRST_PAGE:"yui-carousel-nav-first-page",FOCUSSED_BUTTON:"yui-carousel-button-focus",HORIZONTAL:"yui-carousel-horizontal",ITEM_LOADING:"yui-carousel-item-loading",MIN_WIDTH:"yui-carousel-min-width",NAVIGATION:"yui-carousel-nav",NEXT_NAV:" yui-carousel-next-button",NEXT_PAGE:"yui-carousel-next",NAV_CONTAINER:"yui-carousel-buttons",PAGER_ITEM:"yui-carousel-pager-item",PAGINATION:"yui-carousel-pagination",PAGE_FOCUS:"yui-carousel-nav-page-focus",PREV_PAGE:"yui-carousel-prev",SELECTED_ITEM:"yui-carousel-item-selected",SELECTED_NAV:"yui-carousel-nav-page-selected",VERTICAL:"yui-carousel-vertical",MULTI_ROW:"yui-carousel-multi-row",ROW:"yui-carousel-row",VERTICAL_CONTAINER:"yui-carousel-vertical-container",VISIBLE:"yui-carousel-visible"},CONFIG:{FIRST_VISIBLE:0,HORZ_MIN_WIDTH:180,MAX_PAGER_BUTTONS:5,VERT_MIN_WIDTH:115,NUM_VISIBLE:3},STRINGS:{ITEM_LOADING_CONTENT:"Loading",NEXT_BUTTON_TEXT:"Next Page",PAGER_PREFIX_TEXT:"Go to page ",PREVIOUS_BUTTON_TEXT:"Previous Page"},addItem:function(y,s){var x=this,u,t,r,z=0,w,v=x.get("numItems");if(!y){return false;}if(p.isString(y)||y.nodeName){t=y.nodeName?y.innerHTML:y;}else{if(p.isObject(y)){t=y.content;}else{return false;}}u=y.className||"";r=y.id?y.id:e.generateId();if(p.isUndefined(s)){x._itemsTable.items.push({item:t,className:u,id:r});w=x._itemsTable.items.length-1;}else{if(s<0||s>v){return false;}if(!x._itemsTable.items[s]){x._itemsTable.items[s]=undefined;z=1;}x._itemsTable.items.splice(s,z,{item:t,className:u,id:r});}x._itemsTable.numItems++;if(v0){if(!r.removeItem(0)){}if(r._itemsTable.numItems===0){r.set("numItems",0);break;}s--;}r.fireEvent(g);},focus:function(){var AA=this,v,w,x,u,z,AB,s,t,r;if(!AA._hasRendered){return;}if(AA.isAnimating()){return;}r=AA.get("selectedItem");AB=AA.get("numVisible");s=AA.get("selectOnScroll");t=(r>=0)?AA.getItem(r):null;v=AA.get("firstVisible");z=v+AB-1;x=(rz);w=(t&&t.id)?e.get(t.id):null;u=AA._itemsTable;if(!s&&x){w=(u&&u.items&&u.items[v])?e.get(u.items[v].id):null;}if(w){try{w.focus();}catch(y){}}AA.fireEvent(X);},hide:function(){var r=this;if(r.fireEvent(b)!==false){r.removeClass(r.CLASSES.VISIBLE);r.fireEvent(a);}},init:function(u,s){var v=this,r=u,w=false,t;if(!u){return;}v._hasRendered=false;v._navBtns={prev:[],next:[]};v._pages={el:null,num:0,cur:0};v._pagination={};v._itemAttrCache={};v._itemsTable={loading:{},numItems:0,items:[],size:0};if(p.isString(u)){u=e.get(u);}else{if(!u.nodeName){return;}}U.superclass.init.call(v,u,s);t=v.get("selectedItem");if(t>0){v.set("firstVisible",D.call(v,t));}if(u){if(!u.id){u.setAttribute("id",e.generateId());}w=v._parseCarousel(u);if(!w){v._createCarousel(r);}}else{u=v._createCarousel(r);}r=u.id;v.initEvents();if(w){v._parseCarouselItems();}if(t>0){n.call(v,t,0);}if(!s||typeof s.isVertical=="undefined"){v.set("isVertical",false);}v._parseCarouselNavigation(u);v._navEl=v._setupCarouselNavigation();T[r]={object:v};v._loadItems(Math.min(v.get("firstVisible")+v.get("numVisible"),v.get("numItems"))-1);},initAttributes:function(r){var s=this;r=r||{};U.superclass.initAttributes.call(s,r);s.setAttributeConfig("carouselEl",{validator:p.isString,value:r.carouselEl||"OL"});s.setAttributeConfig("carouselItemEl",{validator:p.isString,value:r.carouselItemEl||"LI"});s.setAttributeConfig("currentPage",{readOnly:true,value:0});s.setAttributeConfig("firstVisible",{method:s._setFirstVisible,validator:s._validateFirstVisible,value:r.firstVisible||s.CONFIG.FIRST_VISIBLE});s.setAttributeConfig("selectOnScroll",{validator:p.isBoolean,value:r.selectOnScroll||true});s.setAttributeConfig("numVisible",{setter:s._numVisibleSetter,method:s._setNumVisible,validator:s._validateNumVisible,value:r.numVisible||s.CONFIG.NUM_VISIBLE});s.setAttributeConfig("numItems",{method:s._setNumItems,validator:s._validateNumItems,value:s._itemsTable.numItems});s.setAttributeConfig("scrollIncrement",{validator:s._validateScrollIncrement,value:r.scrollIncrement||1});s.setAttributeConfig("selectedItem",{setter:s._selectedItemSetter,method:s._setSelectedItem,validator:p.isNumber,value:-1});s.setAttributeConfig("revealAmount",{method:s._setRevealAmount,validator:s._validateRevealAmount,value:r.revealAmount||0});s.setAttributeConfig("isCircular",{validator:p.isBoolean,value:r.isCircular||false});s.setAttributeConfig("isVertical",{method:s._setOrientation,validator:p.isBoolean,value:r.isVertical||false});s.setAttributeConfig("navigation",{method:s._setNavigation,validator:s._validateNavigation,value:r.navigation||{prev:null,next:null,page:null}});s.setAttributeConfig("animation",{validator:s._validateAnimation,value:r.animation||{speed:0,effect:null}});
+s.setAttributeConfig("autoPlay",{validator:p.isNumber,value:r.autoPlay||0});s.setAttributeConfig("autoPlayInterval",{validator:p.isNumber,value:r.autoPlayInterval||0});s.setAttributeConfig("numPages",{readOnly:true,getter:s._getNumPages});s.setAttributeConfig("lastVisible",{readOnly:true,getter:s._getLastVisible});},initEvents:function(){var t=this,s=t.CLASSES,r;t.on("keydown",t._keyboardEventHandler);t.on(F,l);t.on(S,A);t.on(o,A);t.on(Q,A);t.on(C,function(){if(t._hasFocus){t.focus();}});t.on(L,A);t.on(g,function(u){t.scrollTo(0);l.call(t);R.call(t);});t.on(h,R,t);t.on(H,function(u){if(t.get("selectedItem")===null||t.get("selectedItem")<=0){t.set("selectedItem",t.get("firstVisible"));}l.call(t,u);R.call(t,u);t._setClipContainerSize();t.show();});t.on("selectedItemChange",function(u){n.call(t,u.newValue,u.prevValue);if(u.newValue>=0){t._updateTabIndex(t.getElementForItem(u.newValue));}t.fireEvent(C,u.newValue);});t.on(K,function(u){l.call(t,u);R.call(t,u);});t.on("firstVisibleChange",function(u){if(!t.get("selectOnScroll")){if(u.newValue>=0){t._updateTabIndex(t.getElementForItem(u.newValue));}}});t.on("click",function(u){if(t.isAutoPlayOn()){t.stopAutoPlay();}t._itemClickHandler(u);t._pagerClickHandler(u);});c.onFocus(t.get("element"),function(u,w){var v=c.getTarget(u);if(v&&v.nodeName.toUpperCase()=="A"&&e.getAncestorByClassName(v,s.NAVIGATION)){if(r){e.removeClass(r,s.PAGE_FOCUS);}r=v.parentNode;e.addClass(r,s.PAGE_FOCUS);}else{if(r){e.removeClass(r,s.PAGE_FOCUS);}}w._hasFocus=true;w._updateNavButtons(c.getTarget(u),true);},t);c.onBlur(t.get("element"),function(u,v){v._hasFocus=false;v._updateNavButtons(c.getTarget(u),false);},t);},isAnimating:function(){return this._isAnimationInProgress;},isAutoPlayOn:function(){return this._isAutoPlayInProgress;},getElementForItem:function(r){var s=this;if(r<0||r>=s.get("numItems")){return null;}if(s._itemsTable.items[r]){return e.get(s._itemsTable.items[r].id);}return null;},getElementForItems:function(){var t=this,s=[],r;for(r=0;r=s.get("numItems")){return null;}if(s._itemsTable.numItems>r){if(!p.isUndefined(s._itemsTable.items[r])){return s._itemsTable.items[r];}}return null;},getItems:function(){return this._itemsTable.items;},getLoadingItems:function(){return this._itemsTable.loading;},getRows:function(){return this._rows;},getCols:function(){return this._cols;},getItemPositionById:function(w){var u=this,v=u.get("numItems"),s=0,r=u._itemsTable.items,t;while(s=r){return false;}t=u._itemsTable.items.splice(s,1);if(t&&t.length==1){u._itemsTable.numItems--;u.set("numItems",r-1);u.fireEvent(o,{item:t[0],pos:s,ev:o});return true;}return false;},replaceItem:function(z,u){var y=this,w,v,t,x=y.get("numItems"),s,r=z;if(!z){return false;}if(p.isString(z)||z.nodeName){v=z.nodeName?z.innerHTML:z;}else{if(p.isObject(z)){v=z.content;}else{return false;}}if(p.isUndefined(u)){return false;}else{if(u<0||u>=x){return false;}s=y._itemsTable.items[u];if(!s){s=y._itemsTable.loading[u];y._itemsTable.items[u]=undefined;}y._itemsTable.items.splice(u,1,{item:v,className:z.className||"",id:e.generateId()});r=y._itemsTable.items[u];}y.fireEvent(Q,{newItem:r,oldItem:s,pos:u,ev:Q});return true;},replaceItems:function(r){var s,u,t=true;if(!p.isArray(r)){return false;}for(s=0,u=r.length;ss.get("numItems")){r=0;}if(s.get("selectOnScroll")){s._selectedItem=s._getSelectedItem(r);}s.scrollTo(r);},scrollTo:function(AL,AI){var AH=this,u,AJ,z,AB,AC,AM,AN,AO,AD,AA,v,AF,s,w,t,x,AE,y,AP,AK=AH._itemsTable,AG=AK.items,r=AK.loading;if(p.isUndefined(AL)||AL==AH._firstItem||AH.isAnimating()){return;}AJ=AH.get("animation");z=AH.get("isCircular");AB=AH.get("isVertical");AA=AH._cols;v=AH._rows;AO=AH._firstItem;AF=AH.get("numItems");s=AH.get("numVisible");t=AH.get("currentPage");AP=function(){if(AH.isAutoPlayOn()){AH.stopAutoPlay();}};if(AL<0){if(z){AL=AF+AL;}else{AP.call(AH);return;}}else{if(AF>0&&AL>AF-1){if(AH.get("isCircular")){AL=AF-AL;}else{AP.call(AH);return;}}}if(isNaN(AL)){return;}AN=(AH._firstItem>AL)?"backward":"forward";AE=AO+s;AE=(AE>AF-1)?AF-1:AE;x=AH.fireEvent(i,{dir:AN,first:AO,last:AE});if(x===false){return;}AH.fireEvent(J,{page:t});AD=AL+s-1;AH._loadItems(AD>AF-1?AF-1:AD);AM=0-AL;if(v){if(AB){AM=parseInt(AM/AA,10);}else{AM=parseInt(AM/v,10);}}y=0;while(AM<0&&yAF-1)?AF-1:AE;w=j.call(AH,AM);u=AJ.speed>0;if(u){AH._animateAndSetCarouselOffset(w,AL,AE,AI);}else{AH._setCarouselOffset(w);E.call(AH,AL,AE);}},getPageForItem:function(r){return Math.ceil((r+1)/parseInt(this.get("numVisible"),10));},getFirstVisibleOnPage:function(r){return(r-1)*this.get("numVisible");
+},selectPreviousItem:function(){var t=this,s=0,r=t.get("selectedItem");if(r==this._firstItem){s=r-t.get("numVisible");t._selectedItem=t._getSelectedItem(r-1);t.scrollTo(s);}else{s=t.get("selectedItem")-t.get("scrollIncrement");t.set("selectedItem",t._getSelectedItem(s));}},selectNextItem:function(){var s=this,r=0;r=s.get("selectedItem")+s.get("scrollIncrement");s.set("selectedItem",s._getSelectedItem(r));},show:function(){var s=this,r=s.CLASSES;if(s.fireEvent(Y)!==false){s.addClass(r.VISIBLE);s.fireEvent(V);}},startAutoPlay:function(){var r=this,s;if(p.isUndefined(r._autoPlayTimer)){s=r.get("autoPlayInterval");if(s<=0){return;}r._isAutoPlayInProgress=true;r.fireEvent(Z);r._autoPlayTimer=setTimeout(function(){r._autoScroll();},s);}},stopAutoPlay:function(){var r=this;if(!p.isUndefined(r._autoPlayTimer)){clearTimeout(r._autoPlayTimer);delete r._autoPlayTimer;r._isAutoPlayInProgress=false;r.fireEvent(q);}},updatePagination:function(){var z=this,x=z._pagination;if(!x.el){return false;}var w=z.get("numItems"),AA=z.get("numVisible"),u=z.get("firstVisible")+1,v=z.get("currentPage")+1,r=z.get("numPages"),t={"numVisible":AA,"numPages":r,"numItems":w,"selectedItem":z.get("selectedItem")+1,"currentPage":v,"firstVisible":u,"lastVisible":z.get("lastVisible")+1},s=x.callback||{},y=s.scope&&s.obj?s.obj:z;x.el.innerHTML=p.isFunction(s.fn)?s.fn.apply(y,[x.template,t]):YAHOO.lang.substitute(x.template,t);},registerPagination:function(s,u,r){var t=this;t._pagination.template=s;t._pagination.callback=r||{};if(!t._pagination.el){t._pagination.el=W("DIV",{className:t.CLASSES.PAGINATION});if(u=="before"){t._navEl.insertBefore(t._pagination.el,t._navEl.firstChild);}else{t._navEl.appendChild(t._pagination.el);}t.on("itemSelected",t.updatePagination);t.on("pageChange",t.updatePagination);}t.updatePagination();},toString:function(){return P+(this.get?" (#"+this.get("id")+")":"");},_animateAndSetCarouselOffset:function(w,u,s){var v=this,t=v.get("animation"),r=null;if(v.get("isVertical")){r=new YAHOO.util.Motion(v._carouselEl,{top:{to:w}},t.speed,t.effect);}else{r=new YAHOO.util.Motion(v._carouselEl,{left:{to:w}},t.speed,t.effect);}v._isAnimationInProgress=true;r.onComplete.subscribe(v._animationCompleteHandler,{scope:v,item:u,last:s});r.animate();},_animationCompleteHandler:function(r,s,t){t.scope._isAnimationInProgress=false;E.call(t.scope,t.item,t.last);},_autoScroll:function(){var s=this,t=s._firstItem,r;if(t>=s.get("numItems")-1){if(s.get("isCircular")){r=0;}else{s.stopAutoPlay();}}else{r=t+s.get("numVisible");}s._selectedItem=s._getSelectedItem(r);s.scrollTo.call(s,r);},_createCarousel:function(s){var u=this,r=u.CLASSES,t=e.get(s);if(!t){t=W("DIV",{className:r.CAROUSEL,id:s});}if(!u._carouselEl){u._carouselEl=W(u.get("carouselEl"),{className:r.CAROUSEL_EL});}return t;},_createCarouselClip:function(){return W("DIV",{className:this.CLASSES.CONTENT});},_createCarouselItem:function(u){var r,t=this,s=m.call(t,u.pos);return W(t.get("carouselItemEl"),{className:u.className,styles:u.styles,content:u.content,id:u.id});},_getValidIndex:function(t){var w=this,r=w.get("isCircular"),u=w.get("numItems"),v=w.get("numVisible"),s=u-1;if(t<0){t=r?Math.ceil(u/v)*v+t:0;}else{if(t>s){t=r?0:s;}}return t;},_getSelectedItem:function(v){var u=this,r=u.get("isCircular"),t=u.get("numItems"),s=t-1;if(v<0){if(r){v=t+v;}else{v=u.get("selectedItem");}}else{if(v>s){if(r){v=v-t;}else{v=u.get("selectedItem");}}}return v;},_itemClickHandler:function(v){var y=this,w=y.get("carouselItemEl"),s=y.get("element"),t,u,x=c.getTarget(v),r=x.tagName.toUpperCase();if(r==="INPUT"||r==="SELECT"||r==="TEXTAREA"){return;}while(x&&x!=s&&x.id!=y._carouselEl){t=x.nodeName;if(t.toUpperCase()==w){break;}x=x.parentNode;}if((u=y.getItemPositionById(x.id))>=0){y.set("selectedItem",y._getSelectedItem(u));y.focus();}},_keyboardEventHandler:function(t){var v=this,s=c.getCharCode(t),u=c.getTarget(t),r=false;if(v.isAnimating()||u.tagName.toUpperCase()==="SELECT"){return;}switch(s){case 37:case 38:v.selectPreviousItem();r=true;break;case 39:case 40:v.selectNextItem();r=true;break;case 33:v.scrollPageBackward();r=true;break;case 34:v.scrollPageForward();r=true;break;}if(r){if(v.isAutoPlayOn()){v.stopAutoPlay();}c.preventDefault(t);}},_loadItems:function(t){var w=this,s=w.get("numItems"),u=w.get("numVisible"),v=w.get("revealAmount"),x=w._itemsTable.items.length,r=w.get("lastVisible");if(x>t&&t+1>=u){x=t%u||t==r?t-t%u:t-u+1;}if(v&&t=x&&(!w.getItem(x)||!w.getItem(t))){w.fireEvent(L,{ev:L,first:x,last:t,num:t-x+1});}},_pagerChangeHandler:function(s){var v=this,u=c.getTarget(s),t=u.value,r;if(t){r=v.getFirstVisibleOnPage(t);v._selectedItem=r;v.scrollTo(r);v.focus();}},_pagerClickHandler:function(x){var z=this,t=z.CLASSES,u=c.getTarget(x),s=u.nodeName.toUpperCase(),r,w,v,y;if(e.hasClass(u,t.PAGER_ITEM)||e.hasClass(u.parentNode,t.PAGER_ITEM)){if(s=="EM"){u=u.parentNode;}r=u.href;w=r.lastIndexOf("#");v=parseInt(r.substring(w+1),10);if(v!=-1){y=z.getFirstVisibleOnPage(v);z._selectedItem=y;z.scrollTo(y);z.focus();}c.preventDefault(x);}},_parseCarousel:function(t){var w=this,x,r,s,v,u;r=w.CLASSES;s=w.get("carouselEl");v=false;for(x=t.firstChild;x;x=x.nextSibling){if(x.nodeType==1){u=x.nodeName;if(u.toUpperCase()==s){w._carouselEl=x;e.addClass(w._carouselEl,w.CLASSES.CAROUSEL_EL);v=true;}}}return v;},_parseCarouselItems:function(){var y=this,AA=y.CLASSES,v=0,z,r,t,u,s,w=y.get("firstVisible"),x=y._carouselEl;z=y._rows;t=y.get("carouselItemEl");for(r=x.firstChild;r;r=r.nextSibling){if(r.nodeType==1){s=r.nodeName;if(s.toUpperCase()==t){if(r.id){u=r.id;}else{u=e.generateId();r.setAttribute("id",u);}y.addItem(r,w);w++;}}}},_parseCarouselNavigation:function(x){var y=this,w,z=y.CLASSES,s,v,u,r,t=false;r=e.getElementsByClassName(z.PREV_PAGE,"*",x);if(r.length>0){for(v in r){if(r.hasOwnProperty(v)){s=r[v];if(s.nodeName=="INPUT"||s.nodeName=="BUTTON"||s.nodeName=="A"){y._navBtns.prev.push(s);}else{u=s.getElementsByTagName("INPUT");if(p.isArray(u)&&u.length>0){y._navBtns.prev.push(u[0]);
+}else{u=s.getElementsByTagName("BUTTON");if(p.isArray(u)&&u.length>0){y._navBtns.prev.push(u[0]);}}}}}w={prev:r};}r=e.getElementsByClassName(z.NEXT_PAGE,"*",x);if(r.length>0){for(v in r){if(r.hasOwnProperty(v)){s=r[v];if(s.nodeName=="INPUT"||s.nodeName=="BUTTON"||s.nodeName=="A"){y._navBtns.next.push(s);}else{u=s.getElementsByTagName("INPUT");if(p.isArray(u)&&u.length>0){y._navBtns.next.push(u[0]);}else{u=s.getElementsByTagName("BUTTON");if(p.isArray(u)&&u.length>0){y._navBtns.next.push(u[0]);}}}}}if(w){w.next=r;}else{w={next:r};}}if(w){y.set("navigation",w);t=true;}return t;},_refreshUi:function(){var v=this,s,w=v.get("isVertical"),y=v.get("firstVisible"),t,x,r,u;if(v._itemsTable.numItems<1){return;}u=O.call(v,w?"height":"width");t=v._itemsTable.items[y].id;u=w?d(t,"width"):d(t,"height");e.setStyle(v._carouselEl,w?"width":"height",u+"px");v._hasRendered=true;v.fireEvent(H);},_setCarouselOffset:function(t){var r=this,s;s=r.get("isVertical")?"top":"left";e.setStyle(r._carouselEl,s,t+"px");},_setupCarouselNavigation:function(){var w=this,u,s,r,y,v,x,t;r=w.CLASSES;v=e.getElementsByClassName(r.NAVIGATION,"DIV",w.get("element"));if(v.length===0){v=W("DIV",{className:r.NAVIGATION});w.insertBefore(v,e.getFirstChild(w.get("element")));}else{v=v[0];}w._pages.el=W("UL");v.appendChild(w._pages.el);y=w.get("navigation");if(p.isString(y.prev)||p.isArray(y.prev)){if(p.isString(y.prev)){y.prev=[y.prev];}for(u in y.prev){if(y.prev.hasOwnProperty(u)){w._navBtns.prev.push(e.get(y.prev[u]));}}}else{t=W("SPAN",{className:r.BUTTON+r.FIRST_NAV});e.setStyle(t,"visibility","visible");u=e.generateId();t.innerHTML='";v.appendChild(t);u=e.get(u);w._navBtns.prev=[u];s={prev:[t]};}if(p.isString(y.next)||p.isArray(y.next)){if(p.isString(y.next)){y.next=[y.next];}for(u in y.next){if(y.next.hasOwnProperty(u)){w._navBtns.next.push(e.get(y.next[u]));}}}else{x=W("SPAN",{className:r.BUTTON+r.NEXT_NAV});e.setStyle(x,"visibility","visible");u=e.generateId();x.innerHTML='";v.appendChild(x);u=e.get(u);w._navBtns.next=[u];if(s){s.next=[x];}else{s={next:[x]};}}if(s){w.set("navigation",s);}return v;},_setClipContainerSize:function(r,t){var z=this,x=z.get("isVertical"),AB=z._rows,v=z._cols,y=z.get("revealAmount"),s=O.call(z,"height"),u=O.call(z,"width"),AA,w;r=r||z._clipEl;if(AB){AA=s*AB;w=u*v;}else{t=t||z.get("numVisible");if(x){AA=s*t;}else{w=u*t;}}z._recomputeSize=(AA===0);if(z._recomputeSize){z._hasRendered=false;return;}y=N.call(z);if(x){AA+=(y*2);}else{w+=(y*2);}if(x){AA+=M(z._carouselEl,"height");e.setStyle(r,"height",AA+"px");if(v){w+=M(z._carouselEl,"width");e.setStyle(r,"width",w+(0)+"px");}}else{w+=M(z._carouselEl,"width");e.setStyle(r,"width",w+"px");if(AB){AA+=M(z._carouselEl,"height");e.setStyle(r,"height",AA+"px");}}z._setContainerSize(r);},_setContainerSize:function(s,t){var w=this,r=w.CONFIG,z=w.CLASSES,v,y,u,x;v=w.get("isVertical");y=w._rows;u=w._cols;s=s||w._clipEl;t=t||(v?"height":"width");x=parseFloat(e.getStyle(s,t),10);x=p.isNumber(x)?x:0;if(v){x+=M(w._carouselEl,"height")+d(w._navEl,"height");}else{x+=M(w._carouselEl,"width");}if(!v){if(x=0&&st){s.removeItem(r-1);r--;}}return t;},_setOrientation:function(t){var s=this,r=s.CLASSES;if(t){s.replaceClass(r.HORIZONTAL,r.VERTICAL);}else{s.replaceClass(r.VERTICAL,r.HORIZONTAL);}this._itemAttrCache={};return t;},_setRevealAmount:function(s){var r=this;if(s>=0&&s<=100){s=parseInt(s,10);s=p.isNumber(s)?s:0;r._setClipContainerSize();}else{s=r.get("revealAmount");}return s;},_setSelectedItem:function(r){this._selectedItem=r;},_getNumPages:function(){return Math.ceil(parseInt(this.get("numItems"),10)/parseInt(this.get("numVisible"),10));},_getLastVisible:function(){var r=this;return r.get("currentPage")+1==r.get("numPages")?r.get("numItems")-1:r.get("firstVisible")+r.get("numVisible")-1;},_syncUiForItemAdd:function(u){var v,AA=this,x=AA._carouselEl,r,AB,t=AA._itemsTable,s,w,y,z;w=p.isUndefined(u.pos)?u.newPos||t.numItems-1:u.pos;if(!s){AB=t.items[w]||{};r=AA._createCarouselItem({className:AB.className,styles:AB.styles,content:AB.item,id:AB.id,pos:w});if(p.isUndefined(u.pos)){if(!p.isUndefined(t.loading[w])){s=t.loading[w];}if(s){x.replaceChild(r,s);delete t.loading[w];}else{x.appendChild(r);}}else{if(!p.isUndefined(t.items[u.pos+1])){y=e.get(t.items[u.pos+1].id);}if(y){x.insertBefore(r,y);}else{}}}else{if(p.isUndefined(u.pos)){if(!e.isAncestor(AA._carouselEl,s)){x.appendChild(s);}}else{if(!e.isAncestor(x,s)){if(!p.isUndefined(t.items[u.pos+1])){x.insertBefore(s,e.get(t.items[u.pos+1].id));}}}}if(!AA._hasRendered){AA._refreshUi();}if(AA.get("selectedItem")<0){AA.set("selectedItem",AA.get("firstVisible"));}AA._syncUiItems();},_syncUiForItemReplace:function(x){var w=this,t=w._carouselEl,r=w._itemsTable,y=x.pos,v=x.newItem,s=x.oldItem,u;
+u=w._createCarouselItem({className:v.className,styles:v.styles,content:v.item,id:v.id,pos:y});if(u&&s){c.purgeElement(s,true);t.replaceChild(u,e.get(s.id));if(!p.isUndefined(r.loading[y])){r.numItems++;delete r.loading[y];}}if(!w._hasRendered){w._refreshUi();}w._syncUiItems();},_syncUiForItemRemove:function(w){var v=this,r=v._carouselEl,t,u,s,x;s=v.get("numItems");u=w.item;x=w.pos;if(u&&(t=e.get(u.id))){if(t&&e.isAncestor(r,t)){c.purgeElement(t,true);r.removeChild(t);}if(v.get("selectedItem")==x){x=x>=s?s-1:x;}}else{}v._syncUiItems();},_syncUiForLazyLoading:function(v){var z=this,x=z._carouselEl,t=z._itemsTable,w=t.items.length,y=t.items[v.last+1],r,s;if(!y&&v.last'+z.STRINGS.PAGER_PREFIX_TEXT+" "+(u+1)+"";r.innerHTML=w;t.appendChild(r);}e.setStyle(t,"visibility","visible");},_updatePagerMenu:function(){var z=this,x=z.CLASSES,y=z._pages.cur,s,v,AA,t=z.get("numVisible"),w=z._pages.num,u=z._pages.el,r;if(w===0){return;}r=document.createElement("SELECT");if(!r){return;}e.setStyle(u,"visibility","hidden");while(u.firstChild){u.removeChild(u.firstChild);}for(v=0;v=0&&t=0);},_validateNumVisible:function(r){var s=false;if(p.isNumber(r)){s=r>0&&r<=this.get("numItems");}else{if(p.isArray(r)){if(p.isNumber(r[0])&&p.isNumber(r[1])){s=r[0]*r[1]>0&&r.length==2;}}}return s;},_validateRevealAmount:function(r){var s=false;if(p.isNumber(r)){s=r>=0&&r<100;}return s;},_validateScrollIncrement:function(r){var s=false;if(p.isNumber(r)){s=(r>0&&rElement.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ afterScrollEvent = "afterScroll",
+
+ /**
+ * @event allItemsRemovedEvent
+ * @description Fires when all items have been removed from the Carousel.
+ * See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ allItemsRemovedEvent = "allItemsRemoved",
+
+ /**
+ * @event beforeHide
+ * @description Fires before the Carousel is hidden. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ beforeHideEvent = "beforeHide",
+
+ /**
+ * @event beforePageChange
+ * @description Fires when the Carousel is about to scroll to the previous
+ * or next page. Passes back the page number of the current page. Note
+ * that the first page number is zero. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ beforePageChangeEvent = "beforePageChange",
+
+ /**
+ * @event beforeScroll
+ * @description Fires when the Carousel is about to scroll to the previous
+ * or next page. Passes back the index of the first and last visible items
+ * in the Carousel and the direction (backward/forward) of the scroll. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ beforeScrollEvent = "beforeScroll",
+
+ /**
+ * @event beforeShow
+ * @description Fires when the Carousel is about to be shown. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ beforeShowEvent = "beforeShow",
+
+ /**
+ * @event blur
+ * @description Fires when the Carousel loses focus. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ blurEvent = "blur",
+
+ /**
+ * @event focus
+ * @description Fires when the Carousel gains focus. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ focusEvent = "focus",
+
+ /**
+ * @event hide
+ * @description Fires when the Carousel is hidden. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ hideEvent = "hide",
+
+ /**
+ * @event itemAdded
+ * @description Fires when an item has been added to the Carousel. Passes
+ * back the content of the item that would be added, the index at which the
+ * item would be added, and the event itself. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ itemAddedEvent = "itemAdded",
+
+ /**
+ * @event itemRemoved
+ * @description Fires when an item has been removed from the Carousel.
+ * Passes back the content of the item that would be removed, the index
+ * from which the item would be removed, and the event itself. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ itemRemovedEvent = "itemRemoved",
+
+ /**
+ * @event itemReplaced
+ * @description Fires when an item has been replaced in the Carousel.
+ * Passes back the content of the item that was replaced, the content
+ * of the new item, the index where the replacement occurred, and the event
+ * itself. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ itemReplacedEvent = "itemReplaced",
+
+ /**
+ * @event itemSelected
+ * @description Fires when an item has been selected in the Carousel.
+ * Passes back the index of the selected item in the Carousel. Note, that
+ * the index begins from zero. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ itemSelectedEvent = "itemSelected",
+
+ /**
+ * @event loadItems
+ * @description Fires when the Carousel needs more items to be loaded for
+ * displaying them. Passes back the first and last visible items in the
+ * Carousel, and the number of items needed to be loaded. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ loadItemsEvent = "loadItems",
+
+ /**
+ * @event navigationStateChange
+ * @description Fires when the state of either one of the navigation
+ * buttons are changed from enabled to disabled or vice versa. Passes back
+ * the state (true/false) of the previous and next buttons. The value true
+ * signifies the button is enabled, false signifies disabled. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ navigationStateChangeEvent = "navigationStateChange",
+
+ /**
+ * @event pageChange
+ * @description Fires after the Carousel has scrolled to the previous or
+ * next page. Passes back the page number of the current page. Note
+ * that the first page number is zero. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ pageChangeEvent = "pageChange",
+
+ /*
+ * Internal event.
+ * @event render
+ * @description Fires when the Carousel is rendered. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ renderEvent = "render",
+
+ /**
+ * @event show
+ * @description Fires when the Carousel is shown. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ showEvent = "show",
+
+ /**
+ * @event startAutoPlay
+ * @description Fires when the auto play has started in the Carousel. See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ startAutoPlayEvent = "startAutoPlay",
+
+ /**
+ * @event stopAutoPlay
+ * @description Fires when the auto play has been stopped in the Carousel.
+ * See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ stopAutoPlayEvent = "stopAutoPlay",
+
+ /*
+ * Internal event.
+ * @event uiUpdateEvent
+ * @description Fires when the UI has been updated.
+ * See
+ * Element.addListener
+ * for more information on listening for this event.
+ * @type YAHOO.util.CustomEvent
+ */
+ uiUpdateEvent = "uiUpdate";
+
+ /*
+ * Private helper functions used by the Carousel component
+ */
+
+ /**
+ * Set multiple styles on one element.
+ * @method setStyles
+ * @param el {HTMLElement} The element to set styles on
+ * @param style {Object} top:"10px", left:"0px", etc.
+ * @private
+ */
+ function setStyles(el, styles) {
+ var which;
+
+ for (which in styles) {
+ if (styles.hasOwnProperty(which)) {
+ Dom.setStyle(el, which, styles[which]);
+ }
+ }
+ }
+
+ /**
+ * Create an element, set its class name and optionally install the element
+ * to its parent.
+ * @method createElement
+ * @param el {String} The element to be created
+ * @param attrs {Object} Configuration of parent, class and id attributes.
+ * If the content is specified, it is inserted after creation of the
+ * element. The content can also be an HTML element in which case it would
+ * be appended as a child node of the created element.
+ * @private
+ */
+ function createElement(el, attrs) {
+ var newEl = document.createElement(el);
+
+ attrs = attrs || {};
+ if (attrs.className) {
+ Dom.addClass(newEl, attrs.className);
+ }
+
+ if (attrs.styles) {
+ setStyles(newEl, attrs.styles);
+ }
+
+ if (attrs.parent) {
+ attrs.parent.appendChild(newEl);
+ }
+
+ if (attrs.id) {
+ newEl.setAttribute("id", attrs.id);
+ }
+
+ if (attrs.content) {
+ if (attrs.content.nodeName) {
+ newEl.appendChild(attrs.content);
+ } else {
+ newEl.innerHTML = attrs.content;
+ }
+ }
+
+ return newEl;
+ }
+
+ /**
+ * Get the computed style of an element.
+ *
+ * @method getStyle
+ * @param el {HTMLElement} The element for which the style needs to be
+ * returned.
+ * @param style {String} The style attribute
+ * @param type {String} "int", "float", etc. (defaults to int)
+ * @private
+ */
+ function getStyle(el, style, type) {
+ var value;
+
+ if (!el) {
+ return 0;
+ }
+
+ function getStyleIntVal(el, style) {
+ var val;
+
+ /*
+ * XXX: Safari calculates incorrect marginRight for an element
+ * which has its parent element style set to overflow: hidden
+ * https://bugs.webkit.org/show_bug.cgi?id=13343
+ * Let us assume marginLeft == marginRight
+ */
+ if (style == "marginRight" && YAHOO.env.ua.webkit) {
+ val = parseInt(Dom.getStyle(el, "marginLeft"), 10);
+ } else {
+ val = parseInt(Dom.getStyle(el, style), 10);
+ }
+
+ return JS.isNumber(val) ? val : 0;
+ }
+
+ function getStyleFloatVal(el, style) {
+ var val;
+
+ /*
+ * XXX: Safari calculates incorrect marginRight for an element
+ * which has its parent element style set to overflow: hidden
+ * https://bugs.webkit.org/show_bug.cgi?id=13343
+ * Let us assume marginLeft == marginRight
+ */
+ if (style == "marginRight" && YAHOO.env.ua.webkit) {
+ val = parseFloat(Dom.getStyle(el, "marginLeft"));
+ } else {
+ val = parseFloat(Dom.getStyle(el, style));
+ }
+
+ return JS.isNumber(val) ? val : 0;
+ }
+
+ if (typeof type == "undefined") {
+ type = "int";
+ }
+
+ switch (style) {
+ case "height":
+ value = el.offsetHeight;
+ if (value > 0) {
+ value += getStyleIntVal(el, "marginTop") +
+ getStyleIntVal(el, "marginBottom");
+ } else {
+ value = getStyleFloatVal(el, "height") +
+ getStyleIntVal(el, "marginTop") +
+ getStyleIntVal(el, "marginBottom") +
+ getStyleIntVal(el, "borderTopWidth") +
+ getStyleIntVal(el, "borderBottomWidth") +
+ getStyleIntVal(el, "paddingTop") +
+ getStyleIntVal(el, "paddingBottom");
+ }
+ break;
+ case "width":
+ value = el.offsetWidth;
+ if (value > 0) {
+ value += getStyleIntVal(el, "marginLeft") +
+ getStyleIntVal(el, "marginRight");
+ } else {
+ value = getStyleFloatVal(el, "width") +
+ getStyleIntVal(el, "marginLeft") +
+ getStyleIntVal(el, "marginRight") +
+ getStyleIntVal(el, "borderLeftWidth") +
+ getStyleIntVal(el, "borderRightWidth") +
+ getStyleIntVal(el, "paddingLeft") +
+ getStyleIntVal(el, "paddingRight");
+ }
+ break;
+ default:
+ if (type == "int") {
+ value = getStyleIntVal(el, style);
+ } else if (type == "float") {
+ value = getStyleFloatVal(el, style);
+ } else {
+ value = Dom.getStyle(el, style);
+ }
+ break;
+ }
+
+ return value;
+ }
+
+ /**
+ * Compute and return the height or width of a single Carousel item
+ * depending upon the orientation.
+ *
+ * @method getCarouselItemSize
+ * @param which {String} "height" or "width" to be returned. If this is
+ * passed explicitly, the calculated size is not cached.
+ * @private
+ */
+ function getCarouselItemSize(which) {
+ var carousel = this,
+ child,
+ item,
+ size = 0,
+ first = carousel.get("firstVisible"),
+ vertical = false;
+
+ if (carousel._itemsTable.numItems === 0) {
+ return 0;
+ }
+
+ item = carousel._itemsTable.items[first] ||
+ carousel._itemsTable.loading[first];
+
+ if (JS.isUndefined(item)) {
+ return 0;
+ }
+
+ child = Dom.get(item.id);
+
+ if (typeof which == "undefined") {
+ vertical = carousel.get("isVertical");
+ } else {
+ vertical = which == "height";
+ }
+
+ if (this._itemAttrCache[which]) {
+ return this._itemAttrCache[which];
+ }
+
+ if (vertical) {
+ size = getStyle(child, "height");
+ } else {
+ size = getStyle(child, "width");
+ }
+
+ this._itemAttrCache[which] = size;
+
+ return size;
+ }
+
+ /**
+ * Return the size of a part of the item (reveal).
+ *
+ * @method getRevealSize
+ * @private
+ */
+ function getRevealSize() {
+ var carousel = this, isVertical, sz;
+
+ isVertical = carousel.get("isVertical");
+ sz = getCarouselItemSize.call(carousel,
+ isVertical ? "height" : "width");
+ return (sz * carousel.get("revealAmount") / 100);
+ }
+
+ /**
+ * Compute and return the position of a Carousel item based on its
+ * position.
+ *
+ * @method getCarouselItemPosition
+ * @param position {Number} The position of the Carousel item.
+ * @private
+ */
+ function getCarouselItemPosition(pos) {
+ var carousel = this,
+ itemsPerRow = carousel._cols,
+ itemsPerCol = carousel._rows,
+ page,
+ sz,
+ isVertical,
+ itemsCol,
+ itemsRow,
+ sentinel,
+ delta = 0,
+ top,
+ left,
+ rsz,
+ styles = {},
+ index = 0,
+ itemsTable = carousel._itemsTable,
+ items = itemsTable.items,
+ loading = itemsTable.loading;
+
+ isVertical = carousel.get("isVertical");
+ sz = getCarouselItemSize.call(carousel,
+ isVertical ? "height" : "width");
+ rsz = getRevealSize.call(carousel);
+
+ // adjust for items not yet loaded
+ while (index < pos) {
+ if (!items[index] && !loading[index]) {
+ delta++;
+ }
+ index++;
+ }
+ pos -= delta;
+
+ if (itemsPerCol) {
+ page = this.getPageForItem(pos);
+ if (isVertical) {
+ itemsRow = Math.floor(pos/itemsPerRow);
+ delta = itemsRow;
+ top = delta * sz;
+ styles.top = (top + rsz) + "px";
+
+ sz = getCarouselItemSize.call(carousel, "width");
+
+ itemsCol = pos % itemsPerRow;
+ delta = itemsCol;
+ left = delta * sz;
+ styles.left = left + "px";
+ } else {
+ itemsCol = pos % itemsPerRow;
+ sentinel = (page - 1) * itemsPerRow;
+ delta = itemsCol + sentinel;
+ left = delta * sz;
+ styles.left = (left + rsz) + "px";
+
+ sz = getCarouselItemSize.call(carousel, "height");
+
+ itemsRow = Math.floor(pos/itemsPerRow);
+ sentinel = (page - 1) * itemsPerCol;
+ delta = itemsRow - sentinel;
+ top = delta * sz;
+
+ styles.top = top + "px";
+ }
+ } else {
+ if (isVertical) {
+ styles.left = 0;
+ styles.top = ((pos * sz) + rsz) + "px";
+ } else {
+ styles.top = 0;
+ styles.left = ((pos * sz) + rsz) + "px";
+ }
+ }
+
+ return styles;
+ }
+
+ /**
+ * Return the index of the first item in the view port for displaying item
+ * in "pos".
+ *
+ * @method getFirstVisibleForPosition
+ * @param pos {Number} The position of the item to be displayed
+ * @private
+ */
+ function getFirstVisibleForPosition(pos) {
+ var num = this.get("numVisible");
+ return Math.floor(pos / num) * num;
+ }
+
+ /**
+ * Return the scrolling offset size given the number of elements to
+ * scroll.
+ *
+ * @method getScrollOffset
+ * @param delta {Number} The delta number of elements to scroll by.
+ * @private
+ */
+ function getScrollOffset(delta) {
+ var itemSize = 0,
+ size = 0;
+
+ itemSize = getCarouselItemSize.call(this);
+ size = itemSize * delta;
+
+ return size;
+ }
+
+ /**
+ * Scroll the Carousel by a page backward.
+ *
+ * @method scrollPageBackward
+ * @param {Event} ev The event object
+ * @param {Object} obj The context object
+ * @private
+ */
+ function scrollPageBackward(ev, obj) {
+ obj.scrollPageBackward();
+ Event.preventDefault(ev);
+ }
+
+ /**
+ * Scroll the Carousel by a page forward.
+ *
+ * @method scrollPageForward
+ * @param {Event} ev The event object
+ * @param {Object} obj The context object
+ * @private
+ */
+ function scrollPageForward(ev, obj) {
+ obj.scrollPageForward();
+ Event.preventDefault(ev);
+ }
+
+ /**
+ * Set the selected item.
+ *
+ * @method setItemSelection
+ * @param {Number} newpos The index of the new position
+ * @param {Number} oldpos The index of the previous position
+ * @private
+ */
+ function setItemSelection(newpos, oldpos) {
+ var carousel = this,
+ cssClass = carousel.CLASSES,
+ el,
+ firstItem = carousel._firstItem,
+ isCircular = carousel.get("isCircular"),
+ numItems = carousel.get("numItems"),
+ numVisible = carousel.get("numVisible"),
+ position = oldpos,
+ sentinel = firstItem + numVisible - 1;
+
+ if (position >= 0 && position < numItems) {
+ if (!JS.isUndefined(carousel._itemsTable.items[position])) {
+ el = Dom.get(carousel._itemsTable.items[position].id);
+ if (el) {
+ Dom.removeClass(el, cssClass.SELECTED_ITEM);
+ }
+ }
+ }
+
+ if (JS.isNumber(newpos)) {
+ newpos = parseInt(newpos, 10);
+ newpos = JS.isNumber(newpos) ? newpos : 0;
+ } else {
+ newpos = firstItem;
+ }
+
+ if (JS.isUndefined(carousel._itemsTable.items[newpos])) {
+ newpos = getFirstVisibleForPosition.call(carousel, newpos);
+ carousel.scrollTo(newpos); // still loading the item
+ }
+
+ if (!JS.isUndefined(carousel._itemsTable.items[newpos])) {
+ el = Dom.get(carousel._itemsTable.items[newpos].id);
+ if (el) {
+ Dom.addClass(el, cssClass.SELECTED_ITEM);
+ }
+ }
+
+ if (newpos < firstItem || newpos > sentinel) { // out of focus
+ newpos = getFirstVisibleForPosition.call(carousel, newpos);
+ carousel.scrollTo(newpos);
+ }
+ }
+
+ /**
+ * Fire custom events for enabling/disabling navigation elements.
+ *
+ * @method syncNavigation
+ * @private
+ */
+ function syncNavigation() {
+ var attach = false,
+ carousel = this,
+ cssClass = carousel.CLASSES,
+ i,
+ navigation,
+ sentinel;
+
+ // Don't do anything if the Carousel is not rendered
+ if (!carousel._hasRendered) {
+ return;
+ }
+
+ navigation = carousel.get("navigation");
+ sentinel = carousel._firstItem + carousel.get("numVisible");
+
+ if (navigation.prev) {
+ if (carousel.get("numItems") === 0 || carousel._firstItem === 0) {
+ if (carousel.get("numItems") === 0 ||
+ !carousel.get("isCircular")) {
+ Event.removeListener(navigation.prev, "click",
+ scrollPageBackward);
+ Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
+ for (i = 0; i < carousel._navBtns.prev.length; i++) {
+ carousel._navBtns.prev[i].setAttribute("disabled",
+ "true");
+ }
+ carousel._prevEnabled = false;
+ } else {
+ attach = !carousel._prevEnabled;
+ }
+ } else {
+ attach = !carousel._prevEnabled;
+ }
+
+ if (attach) {
+ Event.on(navigation.prev, "click", scrollPageBackward,
+ carousel);
+ Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
+ for (i = 0; i < carousel._navBtns.prev.length; i++) {
+ carousel._navBtns.prev[i].removeAttribute("disabled");
+ }
+ carousel._prevEnabled = true;
+ }
+ }
+
+ attach = false;
+ if (navigation.next) {
+ if (sentinel >= carousel.get("numItems")) {
+ if (!carousel.get("isCircular")) {
+ Event.removeListener(navigation.next, "click",
+ scrollPageForward);
+ Dom.addClass(navigation.next, cssClass.DISABLED);
+ for (i = 0; i < carousel._navBtns.next.length; i++) {
+ carousel._navBtns.next[i].setAttribute("disabled",
+ "true");
+ }
+ carousel._nextEnabled = false;
+ } else {
+ attach = !carousel._nextEnabled;
+ }
+ } else {
+ attach = !carousel._nextEnabled;
+ }
+
+ if (attach) {
+ Event.on(navigation.next, "click", scrollPageForward,
+ carousel);
+ Dom.removeClass(navigation.next, cssClass.DISABLED);
+ for (i = 0; i < carousel._navBtns.next.length; i++) {
+ carousel._navBtns.next[i].removeAttribute("disabled");
+ }
+ carousel._nextEnabled = true;
+ }
+ }
+
+ carousel.fireEvent(navigationStateChangeEvent,
+ { next: carousel._nextEnabled, prev: carousel._prevEnabled });
+ }
+
+ /**
+ * Synchronize and redraw the Pager UI if necessary.
+ *
+ * @method syncPagerUi
+ * @private
+ */
+ function syncPagerUi(page) {
+ var carousel = this, numPages, numVisible;
+
+ // Don't do anything if the Carousel is not rendered
+ if (!carousel._hasRendered) {
+ return;
+ }
+
+ numVisible = carousel.get("numVisible");
+
+ if (!JS.isNumber(page)) {
+ page = Math.floor(carousel.get("selectedItem") / numVisible);
+ }
+
+ numPages = Math.ceil(carousel.get("numItems") / numVisible);
+
+ carousel._pages.num = numPages;
+ carousel._pages.cur = page;
+
+ if (numPages > carousel.CONFIG.MAX_PAGER_BUTTONS) {
+ carousel._updatePagerMenu();
+ } else {
+ carousel._updatePagerButtons();
+ }
+ }
+
+ /**
+ * Get full dimensions of an element.
+ *
+ * @method getDimensions
+ * @param {Object} el The element to get the dimensions of
+ * @param {String} which Get the height or width of an element
+ * @private
+ */
+ function getDimensions(el, which) {
+ switch (which) {
+ case 'height':
+ return getStyle(el, "marginTop") +
+ getStyle(el, "marginBottom") +
+ getStyle(el, "paddingTop") +
+ getStyle(el, "paddingBottom") +
+ getStyle(el, "borderTopWidth") +
+ getStyle(el, "borderBottomWidth");
+ case 'width':
+ return getStyle(el, "marginLeft") +
+ getStyle(el, "marginRight") +
+ getStyle(el, "paddingLeft") +
+ getStyle(el, "paddingRight") +
+ getStyle(el, "borderLeftWidth") +
+ getStyle(el, "borderRightWidth");
+ default:
+ break;
+ }
+
+ return getStyle(el, which);
+ }
+
+ /**
+ * Handle UI update.
+ * Call the appropriate methods on events fired when an item is added, or
+ * removed for synchronizing the DOM.
+ *
+ * @method syncUi
+ * @param {Object} o The item that needs to be added or removed
+ * @private
+ */
+ function syncUi(o) {
+ var carousel = this;
+
+ if (!JS.isObject(o)) {
+ return;
+ }
+
+ switch (o.ev) {
+ case itemAddedEvent:
+ carousel._syncUiForItemAdd(o);
+ break;
+ case itemRemovedEvent:
+ carousel._syncUiForItemRemove(o);
+ break;
+ case itemReplacedEvent:
+ carousel._syncUiForItemReplace(o);
+ break;
+ case loadItemsEvent:
+ carousel._syncUiForLazyLoading(o);
+ break;
+ }
+
+ carousel.fireEvent(uiUpdateEvent);
+ }
+
+ /**
+ * Update the state variables after scrolling the Carousel view port.
+ *
+ * @method updateStateAfterScroll
+ * @param {Integer} item The index to which the Carousel has scrolled to.
+ * @param {Integer} sentinel The last element in the view port.
+ * @private
+ */
+ function updateStateAfterScroll(item, sentinel) {
+ var carousel = this,
+ page = carousel.get("currentPage"),
+ newPage,
+ numPerPage = carousel.get("numVisible");
+
+ newPage = parseInt(carousel._firstItem / numPerPage, 10);
+ if (newPage != page) {
+ carousel.setAttributeConfig("currentPage", { value: newPage });
+ carousel.fireEvent(pageChangeEvent, newPage);
+ }
+
+ if (carousel.get("selectOnScroll")) {
+ if (carousel.get("selectedItem") != carousel._selectedItem) {
+ carousel.set("selectedItem", carousel._selectedItem);
+ }
+ }
+
+ clearTimeout(carousel._autoPlayTimer);
+ delete carousel._autoPlayTimer;
+ if (carousel.isAutoPlayOn()) {
+ carousel.startAutoPlay();
+ }
+
+ carousel.fireEvent(afterScrollEvent,
+ { first: carousel._firstItem,
+ last: sentinel },
+ carousel);
+ }
+
+ /*
+ * Static members and methods of the Carousel component
+ */
+
+ /**
+ * Return the appropriate Carousel object based on the id associated with
+ * the Carousel element or false if none match.
+ * @method getById
+ * @public
+ * @static
+ */
+ Carousel.getById = function (id) {
+ return instances[id] ? instances[id].object : false;
+ };
+
+ YAHOO.extend(Carousel, YAHOO.util.Element, {
+
+ /*
+ * Internal variables used within the Carousel component
+ */
+
+ /**
+ * Number of rows for a multirow carousel.
+ *
+ * @property _rows
+ * @private
+ */
+ _rows: null,
+
+ /**
+ * Number of cols for a multirow carousel.
+ *
+ * @property _cols
+ * @private
+ */
+ _cols: null,
+
+ /**
+ * The Animation object.
+ *
+ * @property _animObj
+ * @private
+ */
+ _animObj: null,
+
+ /**
+ * The Carousel element.
+ *
+ * @property _carouselEl
+ * @private
+ */
+ _carouselEl: null,
+
+ /**
+ * The Carousel clipping container element.
+ *
+ * @property _clipEl
+ * @private
+ */
+ _clipEl: null,
+
+ /**
+ * The current first index of the Carousel.
+ *
+ * @property _firstItem
+ * @private
+ */
+ _firstItem: 0,
+
+ /**
+ * Does the Carousel element have focus?
+ *
+ * @property _hasFocus
+ * @private
+ */
+ _hasFocus: false,
+
+ /**
+ * Is the Carousel rendered already?
+ *
+ * @property _hasRendered
+ * @private
+ */
+ _hasRendered: false,
+
+ /**
+ * Is the animation still in progress?
+ *
+ * @property _isAnimationInProgress
+ * @private
+ */
+ _isAnimationInProgress: false,
+
+ /**
+ * Is the auto-scrolling of Carousel in progress?
+ *
+ * @property _isAutoPlayInProgress
+ * @private
+ */
+ _isAutoPlayInProgress: false,
+
+ /**
+ * The table of items in the Carousel.
+ * The numItems is the number of items in the Carousel, items being the
+ * array of items in the Carousel. The size is the size of a single
+ * item in the Carousel. It is cached here for efficiency (to avoid
+ * computing the size multiple times).
+ *
+ * @property _itemsTable
+ * @private
+ */
+ _itemsTable: null,
+
+ /**
+ * The Carousel navigation buttons.
+ *
+ * @property _navBtns
+ * @private
+ */
+ _navBtns: null,
+
+ /**
+ * The Carousel navigation.
+ *
+ * @property _navEl
+ * @private
+ */
+ _navEl: null,
+
+ /**
+ * Status of the next navigation item.
+ *
+ * @property _nextEnabled
+ * @private
+ */
+ _nextEnabled: true,
+
+ /**
+ * The Carousel pages structure.
+ * This is an object of the total number of pages and the current page.
+ *
+ * @property _pages
+ * @private
+ */
+ _pages: null,
+
+ /**
+ * The Carousel pagination structure.
+ *
+ * @property _pagination
+ * @private
+ */
+ _pagination: {},
+
+ /**
+ * Status of the previous navigation item.
+ *
+ * @property _prevEnabled
+ * @private
+ */
+ _prevEnabled: true,
+
+ /**
+ * Whether the Carousel size needs to be recomputed or not?
+ *
+ * @property _recomputeSize
+ * @private
+ */
+ _recomputeSize: true,
+
+ /**
+ * Cache the Carousel item attributes.
+ *
+ * @property _itemAttrCache
+ * @private
+ */
+ _itemAttrCache: {},
+
+ /*
+ * CSS classes used by the Carousel component
+ */
+
+ CLASSES: {
+
+ /**
+ * The class name of the Carousel navigation buttons.
+ *
+ * @property BUTTON
+ * @default "yui-carousel-button"
+ */
+ BUTTON: "yui-carousel-button",
+
+ /**
+ * The class name of the Carousel element.
+ *
+ * @property CAROUSEL
+ * @default "yui-carousel"
+ */
+ CAROUSEL: "yui-carousel",
+
+ /**
+ * The class name of the container of the items in the Carousel.
+ *
+ * @property CAROUSEL_EL
+ * @default "yui-carousel-element"
+ */
+ CAROUSEL_EL: "yui-carousel-element",
+
+ /**
+ * The class name of the Carousel's container element.
+ *
+ * @property CONTAINER
+ * @default "yui-carousel-container"
+ */
+ CONTAINER: "yui-carousel-container",
+
+ /**
+ * The class name of the Carousel's container element.
+ *
+ * @property CONTENT
+ * @default "yui-carousel-content"
+ */
+ CONTENT: "yui-carousel-content",
+
+ /**
+ * The class name of a disabled navigation button.
+ *
+ * @property DISABLED
+ * @default "yui-carousel-button-disabled"
+ */
+ DISABLED: "yui-carousel-button-disabled",
+
+ /**
+ * The class name of the first Carousel navigation button.
+ *
+ * @property FIRST_NAV
+ * @default " yui-carousel-first-button"
+ */
+ FIRST_NAV: " yui-carousel-first-button",
+
+ /**
+ * The class name of a first disabled navigation button.
+ *
+ * @property FIRST_NAV_DISABLED
+ * @default "yui-carousel-first-button-disabled"
+ */
+ FIRST_NAV_DISABLED: "yui-carousel-first-button-disabled",
+
+ /**
+ * The class name of a first page element.
+ *
+ * @property FIRST_PAGE
+ * @default "yui-carousel-nav-first-page"
+ */
+ FIRST_PAGE: "yui-carousel-nav-first-page",
+
+ /**
+ * The class name of the Carousel navigation button that has focus.
+ *
+ * @property FOCUSSED_BUTTON
+ * @default "yui-carousel-button-focus"
+ */
+ FOCUSSED_BUTTON: "yui-carousel-button-focus",
+
+ /**
+ * The class name of a horizontally oriented Carousel.
+ *
+ * @property HORIZONTAL
+ * @default "yui-carousel-horizontal"
+ */
+ HORIZONTAL: "yui-carousel-horizontal",
+
+ /**
+ * The element to be used as the progress indicator when the item
+ * is still being loaded.
+ *
+ * @property ITEM_LOADING
+ * @default The progress indicator (spinner) image CSS class
+ */
+ ITEM_LOADING: "yui-carousel-item-loading",
+
+ /**
+ * The class name that will be set if the Carousel adjusts itself
+ * for a minimum width.
+ *
+ * @property MIN_WIDTH
+ * @default "yui-carousel-min-width"
+ */
+ MIN_WIDTH: "yui-carousel-min-width",
+
+ /**
+ * The navigation element container class name.
+ *
+ * @property NAVIGATION
+ * @default "yui-carousel-nav"
+ */
+ NAVIGATION: "yui-carousel-nav",
+
+ /**
+ * The class name of the next Carousel navigation button.
+ *
+ * @property NEXT_NAV
+ * @default " yui-carousel-next-button"
+ */
+ NEXT_NAV: " yui-carousel-next-button",
+
+ /**
+ * The class name of the next navigation link. This variable is
+ * not only used for styling, but also for identifying the link
+ * within the Carousel container.
+ *
+ * @property NEXT_PAGE
+ * @default "yui-carousel-next"
+ */
+ NEXT_PAGE: "yui-carousel-next",
+
+ /**
+ * The class name for the navigation container for prev/next.
+ *
+ * @property NAV_CONTAINER
+ * @default "yui-carousel-buttons"
+ */
+ NAV_CONTAINER: "yui-carousel-buttons",
+
+ /**
+ * The class name for an item in the pager UL or dropdown menu.
+ *
+ * @property PAGER_ITEM
+ * @default "yui-carousel-pager-item"
+ */
+ PAGER_ITEM: "yui-carousel-pager-item",
+
+ /**
+ * The class name for the pagination container
+ *
+ * @property PAGINATION
+ * @default "yui-carousel-pagination"
+ */
+ PAGINATION: "yui-carousel-pagination",
+
+ /**
+ * The class name of the focussed page navigation. This class is
+ * specifically used for the ugly focus handling in Opera.
+ *
+ * @property PAGE_FOCUS
+ * @default "yui-carousel-nav-page-focus"
+ */
+ PAGE_FOCUS: "yui-carousel-nav-page-focus",
+
+ /**
+ * The class name of the previous navigation link. This variable
+ * is not only used for styling, but also for identifying the link
+ * within the Carousel container.
+ *
+ * @property PREV_PAGE
+ * @default "yui-carousel-prev"
+ */
+ PREV_PAGE: "yui-carousel-prev",
+
+ /**
+ * The class name of the selected item.
+ *
+ * @property SELECTED_ITEM
+ * @default "yui-carousel-item-selected"
+ */
+ SELECTED_ITEM: "yui-carousel-item-selected",
+
+ /**
+ * The class name of the selected paging navigation.
+ *
+ * @property SELECTED_NAV
+ * @default "yui-carousel-nav-page-selected"
+ */
+ SELECTED_NAV: "yui-carousel-nav-page-selected",
+
+ /**
+ * The class name of a vertically oriented Carousel.
+ *
+ * @property VERTICAL
+ * @default "yui-carousel-vertical"
+ */
+ VERTICAL: "yui-carousel-vertical",
+
+ /**
+ * The class name of a multirow Carousel.
+ *
+ * @property MULTI_ROW
+ * @default "yui-carousel-multi-row"
+ */
+ MULTI_ROW: "yui-carousel-multi-row",
+
+ /**
+ * The class name of a row in a multirow Carousel.
+ *
+ * @property ROW
+ * @default "yui-carousel-new-row"
+ */
+ ROW: "yui-carousel-row",
+
+ /**
+ * The class name of a vertical Carousel's container element.
+ *
+ * @property VERTICAL_CONTAINER
+ * @default "yui-carousel-vertical-container"
+ */
+ VERTICAL_CONTAINER: "yui-carousel-vertical-container",
+
+ /**
+ * The class name of a visible Carousel.
+ *
+ * @property VISIBLE
+ * @default "yui-carousel-visible"
+ */
+ VISIBLE: "yui-carousel-visible"
+
+ },
+
+ /*
+ * Configuration attributes for configuring the Carousel component
+ */
+
+ CONFIG: {
+
+ /**
+ * The offset of the first visible item in the Carousel.
+ *
+ * @property FIRST_VISIBLE
+ * @default 0
+ */
+ FIRST_VISIBLE: 0,
+
+ /**
+ * The minimum width of the horizontal Carousel container to support
+ * the navigation buttons.
+ *
+ * @property HORZ_MIN_WIDTH
+ * @default 180
+ */
+ HORZ_MIN_WIDTH: 180,
+
+ /**
+ * The maximum number of pager buttons allowed beyond which the UI
+ * of the pager would be a drop-down of pages instead of buttons.
+ *
+ * @property MAX_PAGER_BUTTONS
+ * @default 5
+ */
+ MAX_PAGER_BUTTONS: 5,
+
+ /**
+ * The minimum width of the vertical Carousel container to support
+ * the navigation buttons.
+ *
+ * @property VERT_MIN_WIDTH
+ * @default 155
+ */
+ VERT_MIN_WIDTH: 115,
+
+ /**
+ * The number of visible items in the Carousel.
+ *
+ * @property NUM_VISIBLE
+ * @default 3
+ */
+ NUM_VISIBLE: 3
+
+ },
+
+ /*
+ * Internationalizable strings in the Carousel component
+ */
+
+ STRINGS: {
+
+ /**
+ * The content to be used as the progress indicator when the item
+ * is still being loaded.
+ *
+ * @property ITEM_LOADING_CONTENT
+ * @default "Loading"
+ */
+ ITEM_LOADING_CONTENT: "Loading",
+
+ /**
+ * The next navigation button name/text.
+ *
+ * @property NEXT_BUTTON_TEXT
+ * @default "Next Page"
+ */
+ NEXT_BUTTON_TEXT: "Next Page",
+
+ /**
+ * The prefix text for the pager in case the UI is a drop-down.
+ *
+ * @property PAGER_PREFIX_TEXT
+ * @default "Go to page "
+ */
+ PAGER_PREFIX_TEXT: "Go to page ",
+
+ /**
+ * The previous navigation button name/text.
+ *
+ * @property PREVIOUS_BUTTON_TEXT
+ * @default "Previous Page"
+ */
+ PREVIOUS_BUTTON_TEXT: "Previous Page"
+
+ },
+
+ /*
+ * Public methods of the Carousel component
+ */
+
+ /**
+ * Insert or append an item to the Carousel.
+ * E.g. if Object: ({content:"Your Content", id:"", className:""}, index)
+ *
+ * @method addItem
+ * @public
+ * @param item {String | Object | HTMLElement} The item to be appended
+ * to the Carousel. If the parameter is a string, it is assumed to be
+ * the content of the newly created item. If the parameter is an
+ * object, it is assumed to supply the content and an optional class
+ * and an optional id of the newly created item.
+ * @param index {Number} optional The position to where in the list
+ * (starts from zero).
+ * @return {Boolean} Return true on success, false otherwise
+ */
+ addItem: function (item, index) {
+ var carousel = this,
+ className,
+ content,
+ elId,
+ replaceItems = 0,
+ newIndex, // Add newIndex as workaround for undefined pos
+ numItems = carousel.get("numItems");
+
+ if (!item) {
+ return false;
+ }
+
+ if (JS.isString(item) || item.nodeName) {
+ content = item.nodeName ? item.innerHTML : item;
+ } else if (JS.isObject(item)) {
+ content = item.content;
+ } else {
+ return false;
+ }
+
+ className = item.className || "";
+ elId = item.id ? item.id : Dom.generateId();
+
+ if (JS.isUndefined(index)) {
+ carousel._itemsTable.items.push({
+ item : content,
+ className : className,
+ id : elId
+ });
+ // Add newIndex as workaround for undefined pos
+ newIndex = carousel._itemsTable.items.length-1;
+ } else {
+ if (index < 0 || index > numItems) {
+ return false;
+ }
+
+ // make sure we splice into the correct position
+ if(!carousel._itemsTable.items[index]){
+ carousel._itemsTable.items[index] = undefined;
+ replaceItems = 1;
+ }
+
+ carousel._itemsTable.items.splice(index, replaceItems, {
+ item : content,
+ className : className,
+ id : elId
+ });
+ }
+ carousel._itemsTable.numItems++;
+
+ if (numItems < carousel._itemsTable.items.length) {
+ carousel.set("numItems", carousel._itemsTable.items.length);
+ }
+
+ // Add newPos as workaround for undefined pos
+ carousel.fireEvent(itemAddedEvent, { pos: index, ev: itemAddedEvent, newPos:newIndex });
+
+ return true;
+ },
+
+ /**
+ * Insert or append multiple items to the Carousel.
+ *
+ * @method addItems
+ * @public
+ * @param items {Array} An array containing an array of new items each linked to the
+ * index where the insertion should take place.
+ * E.g. [[{content:''}, index1], [{content:''}, index2]]
+ * NOTE: An item at index must already exist.
+ * @return {Boolean} Return true on success, false otherwise
+ */
+ addItems: function (items) {
+ var i, n, rv = true;
+
+ if (!JS.isArray(items)) {
+ return false;
+ }
+
+ for (i = 0, n = items.length; i < n; i++) {
+ if (this.addItem(items[i][0], items[i][1]) === false) {
+ rv = false;
+ }
+ }
+
+ return rv;
+ },
+
+ /**
+ * Remove focus from the Carousel.
+ *
+ * @method blur
+ * @public
+ */
+ blur: function () {
+ this._carouselEl.blur();
+ this.fireEvent(blurEvent);
+ },
+
+ /**
+ * Clears the items from Carousel.
+ *
+ * @method clearItems
+ * @public
+ */
+ clearItems: function () {
+ var carousel = this, n = carousel.get("numItems");
+
+ while (n > 0) {
+ if (!carousel.removeItem(0)) {
+ }
+ /*
+ For dynamic loading, the numItems may be much larger than
+ the actual number of items in the table. So, set the
+ numItems to zero, and break out of the loop if the table
+ is already empty.
+ */
+ if (carousel._itemsTable.numItems === 0) {
+ carousel.set("numItems", 0);
+ break;
+ }
+ n--;
+ }
+
+ carousel.fireEvent(allItemsRemovedEvent);
+ },
+
+ /**
+ * Set focus on the Carousel.
+ *
+ * @method focus
+ * @public
+ */
+ focus: function () {
+ var carousel = this,
+ first,
+ focusEl,
+ isSelectionInvisible,
+ itemsTable,
+ last,
+ numVisible,
+ selectOnScroll,
+ selected,
+ selItem;
+
+ // Don't do anything if the Carousel is not rendered
+ if (!carousel._hasRendered) {
+ return;
+ }
+
+ if (carousel.isAnimating()) {
+ // this messes up real bad!
+ return;
+ }
+
+ selItem = carousel.get("selectedItem");
+ numVisible = carousel.get("numVisible");
+ selectOnScroll = carousel.get("selectOnScroll");
+ selected = (selItem >= 0) ?
+ carousel.getItem(selItem) : null;
+ first = carousel.get("firstVisible");
+ last = first + numVisible - 1;
+ isSelectionInvisible = (selItem < first || selItem > last);
+ focusEl = (selected && selected.id) ?
+ Dom.get(selected.id) : null;
+ itemsTable = carousel._itemsTable;
+
+ if (!selectOnScroll && isSelectionInvisible) {
+ focusEl = (itemsTable && itemsTable.items &&
+ itemsTable.items[first]) ?
+ Dom.get(itemsTable.items[first].id) : null;
+ }
+
+ if (focusEl) {
+ try {
+ focusEl.focus();
+ } catch (ex) {
+ // ignore focus errors
+ }
+ }
+
+ carousel.fireEvent(focusEvent);
+ },
+
+ /**
+ * Hide the Carousel.
+ *
+ * @method hide
+ * @public
+ */
+ hide: function () {
+ var carousel = this;
+
+ if (carousel.fireEvent(beforeHideEvent) !== false) {
+ carousel.removeClass(carousel.CLASSES.VISIBLE);
+ carousel.fireEvent(hideEvent);
+ }
+ },
+
+ /**
+ * Initialize the Carousel.
+ *
+ * @method init
+ * @public
+ * @param el {HTMLElement | String} The html element that represents
+ * the Carousel container.
+ * @param attrs {Object} The set of configuration attributes for
+ * creating the Carousel.
+ */
+ init: function (el, attrs) {
+ var carousel = this,
+ elId = el, // save for a rainy day
+ parse = false,
+ selected;
+
+ if (!el) {
+ return;
+ }
+
+ carousel._hasRendered = false;
+ carousel._navBtns = { prev: [], next: [] };
+ carousel._pages = { el: null, num: 0, cur: 0 };
+ carousel._pagination = {};
+ carousel._itemAttrCache = {};
+
+ carousel._itemsTable = { loading: {}, numItems: 0,
+ items: [], size: 0 };
+
+
+ if (JS.isString(el)) {
+ el = Dom.get(el);
+ } else if (!el.nodeName) {
+ return;
+ }
+
+ Carousel.superclass.init.call(carousel, el, attrs);
+
+ // check if we're starting somewhere in the middle
+ selected = carousel.get("selectedItem");
+ if(selected > 0){
+ carousel.set("firstVisible",getFirstVisibleForPosition.call(carousel,selected));
+ }
+
+ if (el) {
+ if (!el.id) { // in case the HTML element is passed
+ el.setAttribute("id", Dom.generateId());
+ }
+ parse = carousel._parseCarousel(el);
+ if (!parse) {
+ carousel._createCarousel(elId);
+ }
+ } else {
+ el = carousel._createCarousel(elId);
+ }
+ elId = el.id;
+
+ carousel.initEvents();
+
+ if (parse) {
+ carousel._parseCarouselItems();
+ }
+
+ // add the selected class
+ if(selected > 0){
+ setItemSelection.call(carousel,selected,0);
+ }
+
+ if (!attrs || typeof attrs.isVertical == "undefined") {
+ carousel.set("isVertical", false);
+ }
+
+ carousel._parseCarouselNavigation(el);
+ carousel._navEl = carousel._setupCarouselNavigation();
+
+ instances[elId] = { object: carousel };
+ carousel._loadItems(Math.min(carousel.get("firstVisible")+carousel.get("numVisible"),carousel.get("numItems"))-1);
+ },
+
+ /**
+ * Initialize the configuration attributes used to create the Carousel.
+ *
+ * @method initAttributes
+ * @public
+ * @param attrs {Object} The set of configuration attributes for
+ * creating the Carousel.
+ */
+ initAttributes: function (attrs) {
+ var carousel = this;
+
+ attrs = attrs || {};
+ Carousel.superclass.initAttributes.call(carousel, attrs);
+
+ /**
+ * @attribute carouselEl
+ * @description The type of the Carousel element.
+ * @default OL
+ * @type Boolean
+ */
+ carousel.setAttributeConfig("carouselEl", {
+ validator : JS.isString,
+ value : attrs.carouselEl || "OL"
+ });
+
+ /**
+ * @attribute carouselItemEl
+ * @description The type of the list of items within the Carousel.
+ * @default LI
+ * @type Boolean
+ */
+ carousel.setAttributeConfig("carouselItemEl", {
+ validator : JS.isString,
+ value : attrs.carouselItemEl || "LI"
+ });
+
+ /**
+ * @attribute currentPage
+ * @description The current page number (read-only.)
+ * @type Number
+ */
+ carousel.setAttributeConfig("currentPage", {
+ readOnly : true,
+ value : 0
+ });
+
+ /**
+ * @attribute firstVisible
+ * @description The index to start the Carousel from (indexes begin
+ * from zero)
+ * @default 0
+ * @type Number
+ */
+ carousel.setAttributeConfig("firstVisible", {
+ method : carousel._setFirstVisible,
+ validator : carousel._validateFirstVisible,
+ value :
+ attrs.firstVisible || carousel.CONFIG.FIRST_VISIBLE
+ });
+
+ /**
+ * @attribute selectOnScroll
+ * @description Set this to true to automatically set focus to
+ * follow scrolling in the Carousel.
+ * @default true
+ * @type Boolean
+ */
+ carousel.setAttributeConfig("selectOnScroll", {
+ validator : JS.isBoolean,
+ value : attrs.selectOnScroll || true
+ });
+
+ /**
+ * @attribute numVisible
+ * @description The number of visible items in the Carousel's
+ * viewport.
+ * @default 3
+ * @type Number
+ */
+ carousel.setAttributeConfig("numVisible", {
+ setter : carousel._numVisibleSetter,
+ method : carousel._setNumVisible,
+ validator : carousel._validateNumVisible,
+ value : attrs.numVisible || carousel.CONFIG.NUM_VISIBLE
+ });
+
+ /**
+ * @attribute numItems
+ * @description The number of items in the Carousel.
+ * @type Number
+ */
+ carousel.setAttributeConfig("numItems", {
+ method : carousel._setNumItems,
+ validator : carousel._validateNumItems,
+ value : carousel._itemsTable.numItems
+ });
+
+ /**
+ * @attribute scrollIncrement
+ * @description The number of items to scroll by for arrow keys.
+ * @default 1
+ * @type Number
+ */
+ carousel.setAttributeConfig("scrollIncrement", {
+ validator : carousel._validateScrollIncrement,
+ value : attrs.scrollIncrement || 1
+ });
+
+ /**
+ * @attribute selectedItem
+ * @description The index of the selected item.
+ * @type Number
+ */
+ carousel.setAttributeConfig("selectedItem", {
+ setter : carousel._selectedItemSetter,
+ method : carousel._setSelectedItem,
+ validator : JS.isNumber,
+ value : -1
+ });
+
+ /**
+ * @attribute revealAmount
+ * @description The percentage of the item to be revealed on each
+ * side of the Carousel (before and after the first and last item
+ * in the Carousel's viewport.)
+ * @default 0
+ * @type Number
+ */
+ carousel.setAttributeConfig("revealAmount", {
+ method : carousel._setRevealAmount,
+ validator : carousel._validateRevealAmount,
+ value : attrs.revealAmount || 0
+ });
+
+ /**
+ * @attribute isCircular
+ * @description Set this to true to wrap scrolling of the contents
+ * in the Carousel.
+ * @default false
+ * @type Boolean
+ */
+ carousel.setAttributeConfig("isCircular", {
+ validator : JS.isBoolean,
+ value : attrs.isCircular || false
+ });
+
+ /**
+ * @attribute isVertical
+ * @description True if the orientation of the Carousel is vertical
+ * @default false
+ * @type Boolean
+ */
+ carousel.setAttributeConfig("isVertical", {
+ method : carousel._setOrientation,
+ validator : JS.isBoolean,
+ value : attrs.isVertical || false
+ });
+
+ /**
+ * @attribute navigation
+ * @description The set of navigation controls for Carousel
+ * @default
+ * { prev: null, // the previous navigation element
+ * next: null } // the next navigation element
+ * @type Object
+ */
+ carousel.setAttributeConfig("navigation", {
+ method : carousel._setNavigation,
+ validator : carousel._validateNavigation,
+ value :
+ attrs.navigation || {prev: null,next: null,page: null}
+ });
+
+ /**
+ * @attribute animation
+ * @description The optional animation attributes for the Carousel.
+ * @default
+ * { speed: 0, // the animation speed (in seconds)
+ * effect: null } // the animation effect (like
+ * YAHOO.util.Easing.easeOut)
+ * @type Object
+ */
+ carousel.setAttributeConfig("animation", {
+ validator : carousel._validateAnimation,
+ value : attrs.animation || { speed: 0, effect: null }
+ });
+
+ /**
+ * @attribute autoPlay
+ * @description Set this to time in milli-seconds to have the
+ * Carousel automatically scroll the contents.
+ * @type Number
+ * @deprecated Use autoPlayInterval instead.
+ */
+ carousel.setAttributeConfig("autoPlay", {
+ validator : JS.isNumber,
+ value : attrs.autoPlay || 0
+ });
+
+ /**
+ * @attribute autoPlayInterval
+ * @description The delay in milli-seconds for scrolling the
+ * Carousel during auto-play.
+ * Note: The startAutoPlay() method needs to be invoked to trigger
+ * automatic scrolling of Carousel.
+ * @type Number
+ */
+ carousel.setAttributeConfig("autoPlayInterval", {
+ validator : JS.isNumber,
+ value : attrs.autoPlayInterval || 0
+ });
+
+ /**
+ * @attribute numPages
+ * @description The number of pages in the carousel.
+ * @type Number
+ */
+ carousel.setAttributeConfig("numPages", {
+ readOnly : true,
+ getter : carousel._getNumPages
+ });
+
+ /**
+ * @attribute lastVisible
+ * @description The last item visible in the carousel.
+ * @type Number
+ */
+ carousel.setAttributeConfig("lastVisible", {
+ readOnly : true,
+ getter : carousel._getLastVisible
+ });
+ },
+
+ /**
+ * Initialize and bind the event handlers.
+ *
+ * @method initEvents
+ * @public
+ */
+ initEvents: function () {
+ var carousel = this,
+ cssClass = carousel.CLASSES,
+ focussedLi;
+
+ carousel.on("keydown", carousel._keyboardEventHandler);
+
+ carousel.on(afterScrollEvent, syncNavigation);
+
+ carousel.on(itemAddedEvent, syncUi);
+
+ carousel.on(itemRemovedEvent, syncUi);
+
+ carousel.on(itemReplacedEvent, syncUi);
+
+ carousel.on(itemSelectedEvent, function () {
+ if (carousel._hasFocus) {
+ carousel.focus();
+ }
+ });
+
+ carousel.on(loadItemsEvent, syncUi);
+
+ carousel.on(allItemsRemovedEvent, function (ev) {
+ carousel.scrollTo(0);
+ syncNavigation.call(carousel);
+ syncPagerUi.call(carousel);
+ });
+
+ carousel.on(pageChangeEvent, syncPagerUi, carousel);
+
+ carousel.on(renderEvent, function (ev) {
+ if (carousel.get("selectedItem") === null ||
+ carousel.get("selectedItem") <= 0) { //in either case
+ carousel.set("selectedItem", carousel.get("firstVisible"));
+ }
+ syncNavigation.call(carousel, ev);
+ syncPagerUi.call(carousel, ev);
+ carousel._setClipContainerSize();
+ carousel.show();
+ });
+
+ carousel.on("selectedItemChange", function (ev) {
+ setItemSelection.call(carousel, ev.newValue, ev.prevValue);
+ if (ev.newValue >= 0) {
+ carousel._updateTabIndex(
+ carousel.getElementForItem(ev.newValue));
+ }
+ carousel.fireEvent(itemSelectedEvent, ev.newValue);
+ });
+
+ carousel.on(uiUpdateEvent, function (ev) {
+ syncNavigation.call(carousel, ev);
+ syncPagerUi.call(carousel, ev);
+ });
+
+ carousel.on("firstVisibleChange", function (ev) {
+ if (!carousel.get("selectOnScroll")) {
+ if (ev.newValue >= 0) {
+ carousel._updateTabIndex(
+ carousel.getElementForItem(ev.newValue));
+ }
+ }
+ });
+
+ // Handle item selection on mouse click
+ carousel.on("click", function (ev) {
+ if (carousel.isAutoPlayOn()) {
+ carousel.stopAutoPlay();
+ }
+ carousel._itemClickHandler(ev);
+ carousel._pagerClickHandler(ev);
+ });
+
+ // Restore the focus on the navigation buttons
+
+ Event.onFocus(carousel.get("element"), function (ev, obj) {
+ var target = Event.getTarget(ev);
+
+ if (target && target.nodeName.toUpperCase() == "A" &&
+ Dom.getAncestorByClassName(target, cssClass.NAVIGATION)) {
+ if (focussedLi) {
+ Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
+ }
+ focussedLi = target.parentNode;
+ Dom.addClass(focussedLi, cssClass.PAGE_FOCUS);
+ } else {
+ if (focussedLi) {
+ Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
+ }
+ }
+
+ obj._hasFocus = true;
+ obj._updateNavButtons(Event.getTarget(ev), true);
+ }, carousel);
+
+ Event.onBlur(carousel.get("element"), function (ev, obj) {
+ obj._hasFocus = false;
+ obj._updateNavButtons(Event.getTarget(ev), false);
+ }, carousel);
+ },
+
+ /**
+ * Return true if the Carousel is still animating, or false otherwise.
+ *
+ * @method isAnimating
+ * @return {Boolean} Return true if animation is still in progress, or
+ * false otherwise.
+ * @public
+ */
+ isAnimating: function () {
+ return this._isAnimationInProgress;
+ },
+
+ /**
+ * Return true if the auto-scrolling of Carousel is "on", or false
+ * otherwise.
+ *
+ * @method isAutoPlayOn
+ * @return {Boolean} Return true if autoPlay is "on", or false
+ * otherwise.
+ * @public
+ */
+ isAutoPlayOn: function () {
+ return this._isAutoPlayInProgress;
+ },
+
+ /**
+ * Return the carouselItemEl at index or null if the index is not
+ * found.
+ *
+ * @method getElementForItem
+ * @param index {Number} The index of the item to be returned
+ * @return {Element} Return the item at index or null if not found
+ * @public
+ */
+ getElementForItem: function (index) {
+ var carousel = this;
+
+ if (index < 0 || index >= carousel.get("numItems")) {
+ return null;
+ }
+
+ if (carousel._itemsTable.items[index]) {
+ return Dom.get(carousel._itemsTable.items[index].id);
+ }
+
+ return null;
+ },
+
+ /**
+ * Return the carouselItemEl for all items in the Carousel.
+ *
+ * @method getElementForItems
+ * @return {Array} Return all the items
+ * @public
+ */
+ getElementForItems: function () {
+ var carousel = this, els = [], i;
+
+ for (i = 0; i < carousel._itemsTable.numItems; i++) {
+ els.push(carousel.getElementForItem(i));
+ }
+
+ return els;
+ },
+
+ /**
+ * Return the item at index or null if the index is not found.
+ *
+ * @method getItem
+ * @param index {Number} The index of the item to be returned
+ * @return {Object} Return the item at index or null if not found
+ * @public
+ */
+ getItem: function (index) {
+ var carousel = this;
+
+ if (index < 0 || index >= carousel.get("numItems")) {
+ return null;
+ }
+
+ if (carousel._itemsTable.numItems > index) {
+ if (!JS.isUndefined(carousel._itemsTable.items[index])) {
+ return carousel._itemsTable.items[index];
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Return all items as an array.
+ *
+ * @method getItems
+ * @return {Array} Return all items in the Carousel
+ * @public
+ */
+ getItems: function () {
+ return this._itemsTable.items;
+ },
+
+ /**
+ * Return all loading items as an array.
+ *
+ * @method getLoadingItems
+ * @return {Array} Return all items that are loading in the Carousel.
+ * @public
+ */
+ getLoadingItems: function () {
+ return this._itemsTable.loading;
+ },
+
+ /**
+ * For a multirow carousel, return the number of rows specified by user.
+ *
+ * @method getItems
+ * @return {Number} Number of rows
+ * @public
+ */
+ getRows: function () {
+ return this._rows;
+ },
+
+ /**
+ * For a multirow carousel, return the number of cols specified by user.
+ *
+ * @method getItems
+ * @return {Array} Return all items in the Carousel
+ * @public
+ */
+ getCols: function () {
+ return this._cols;
+ },
+
+ /**
+ * Return the position of the Carousel item that has the id "id", or -1
+ * if the id is not found.
+ *
+ * @method getItemPositionById
+ * @param index {Number} The index of the item to be returned
+ * @public
+ */
+ getItemPositionById: function (id) {
+ var carousel = this,
+ n = carousel.get("numItems"),
+ i = 0,
+ items = carousel._itemsTable.items,
+ item;
+
+ while (i < n) {
+ item = items[i] || {};
+ if(item.id == id) {
+ return i;
+ }
+ i++;
+ }
+
+ return -1;
+ },
+
+ /**
+ * Return all visible items as an array.
+ *
+ * @method getVisibleItems
+ * @return {Array} The array of visible items
+ * @public
+ */
+ getVisibleItems: function () {
+ var carousel = this,
+ i = carousel.get("firstVisible"),
+ n = i + carousel.get("numVisible"),
+ r = [];
+
+ while (i < n) {
+ r.push(carousel.getElementForItem(i));
+ i++;
+ }
+
+ return r;
+ },
+
+ /**
+ * Remove an item at index from the Carousel.
+ *
+ * @method removeItem
+ * @public
+ * @param index {Number} The position to where in the list (starts from
+ * zero).
+ * @return {Boolean} Return true on success, false otherwise
+ */
+ removeItem: function (index) {
+ var carousel = this,
+ item,
+ num = carousel.get("numItems");
+
+ if (index < 0 || index >= num) {
+ return false;
+ }
+
+ item = carousel._itemsTable.items.splice(index, 1);
+ if (item && item.length == 1) {
+ carousel._itemsTable.numItems--;
+ carousel.set("numItems", num - 1);
+
+ carousel.fireEvent(itemRemovedEvent,
+ { item: item[0], pos: index, ev: itemRemovedEvent });
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Replace an item at index witin Carousel.
+ *
+ * @method replaceItem
+ * @public
+ * @param item {String | Object | HTMLElement} The item to be appended
+ * to the Carousel. If the parameter is a string, it is assumed to be
+ * the content of the newly created item. If the parameter is an
+ * object, it is assumed to supply the content and an optional class
+ * and an optional id of the newly created item.
+ * @param index {Number} The position to where in the list (starts from
+ * zero).
+ * @return {Boolean} Return true on success, false otherwise
+ */
+ replaceItem: function (item, index) {
+ var carousel = this,
+ className,
+ content,
+ elId,
+ numItems = carousel.get("numItems"),
+ oel,
+ el = item;
+
+ if (!item) {
+ return false;
+ }
+
+ if (JS.isString(item) || item.nodeName) {
+ content = item.nodeName ? item.innerHTML : item;
+ } else if (JS.isObject(item)) {
+ content = item.content;
+ } else {
+ return false;
+ }
+
+ if (JS.isUndefined(index)) {
+ return false;
+ } else {
+ if (index < 0 || index >= numItems) {
+ return false;
+ }
+
+ oel = carousel._itemsTable.items[index];
+ if(!oel){
+ oel = carousel._itemsTable.loading[index];
+ carousel._itemsTable.items[index] = undefined;
+ }
+
+ carousel._itemsTable.items.splice(index, 1, {
+ item : content,
+ className : item.className || "",
+ id : Dom.generateId()
+ });
+
+ el = carousel._itemsTable.items[index];
+ }
+ carousel.fireEvent(itemReplacedEvent,
+ { newItem: el, oldItem: oel, pos: index, ev: itemReplacedEvent });
+
+ return true;
+ },
+
+ /**
+ * Replace multiple items at specified indexes.
+ * NOTE: item at index must already exist.
+ *
+ * @method replaceItems
+ * @public
+ * @param items {Array} An array containing an array of replacement items each linked to the
+ * index where the substitution should take place.
+ * E.g. [[{content:''}, index1], [{content:''}, index2]]
+ * @return {Boolean} Return true on success, false otherwise
+ */
+ replaceItems: function (items) {
+ var i, n, rv = true;
+
+ if (!JS.isArray(items)) {
+ return false;
+ }
+
+ for (i = 0, n = items.length; i < n; i++) {
+ if (this.replaceItem(items[i][0], items[i][1]) === false) {
+ rv = false;
+ }
+ }
+
+ return rv;
+ },
+
+ /**
+ * Render the Carousel.
+ *
+ * @method render
+ * @public
+ * @param appendTo {HTMLElement | String} The element to which the
+ * Carousel should be appended prior to rendering.
+ * @return {Boolean} Status of the operation
+ */
+ render: function (appendTo) {
+ var carousel = this,
+ cssClass = carousel.CLASSES,
+ rows = carousel._rows;
+
+ carousel.addClass(cssClass.CAROUSEL);
+
+ if (!carousel._clipEl) {
+ carousel._clipEl = carousel._createCarouselClip();
+ carousel._clipEl.appendChild(carousel._carouselEl);
+ }
+
+ if (appendTo) {
+ carousel.appendChild(carousel._clipEl);
+ carousel.appendTo(appendTo);
+ } else {
+ if (!Dom.inDocument(carousel.get("element"))) {
+ return false;
+ }
+ carousel.appendChild(carousel._clipEl);
+ }
+
+ if (rows) {
+ Dom.addClass(carousel._clipEl, cssClass.MULTI_ROW);
+ }
+
+ if (carousel.get("isVertical")) {
+ carousel.addClass(cssClass.VERTICAL);
+ } else {
+ carousel.addClass(cssClass.HORIZONTAL);
+ }
+
+ if (carousel.get("numItems") < 1) {
+ return false;
+ }
+
+ carousel._refreshUi();
+
+ return true;
+ },
+
+ /**
+ * Scroll the Carousel by an item backward.
+ *
+ * @method scrollBackward
+ * @public
+ */
+ scrollBackward: function () {
+ var carousel = this;
+ carousel.scrollTo(carousel._firstItem -
+ carousel.get("scrollIncrement"));
+ },
+
+ /**
+ * Scroll the Carousel by an item forward.
+ *
+ * @method scrollForward
+ * @public
+ */
+ scrollForward: function () {
+ var carousel = this;
+ carousel.scrollTo(carousel._firstItem +
+ carousel.get("scrollIncrement"));
+ },
+
+ /**
+ * Scroll the Carousel by a page backward.
+ *
+ * @method scrollPageBackward
+ * @public
+ */
+ scrollPageBackward: function () {
+ var carousel = this,
+ isVertical = carousel.get("isVertical"),
+ cols = carousel._cols,
+ item = carousel._firstItem - carousel.get("numVisible");
+
+ if (item < 0) { // only account for multi-row when scrolling backwards from item 0
+ if (cols) {
+ item = carousel._firstItem - cols;
+ }
+ }
+
+ if (carousel.get("selectOnScroll")) {
+ carousel._selectedItem = carousel._getSelectedItem(item);
+ }
+
+ carousel.scrollTo(item);
+ },
+
+ /**
+ * Scroll the Carousel by a page forward.
+ *
+ * @method scrollPageForward
+ * @public
+ */
+ scrollPageForward: function () {
+ var carousel = this,
+ item = carousel._firstItem + carousel.get("numVisible");
+
+ if (item > carousel.get("numItems")) {
+ item = 0;
+ }
+
+ if (carousel.get("selectOnScroll")) {
+ carousel._selectedItem = carousel._getSelectedItem(item);
+ }
+
+ carousel.scrollTo(item);
+ },
+
+ /**
+ * Scroll the Carousel to make the item the first visible item.
+ *
+ * @method scrollTo
+ * @public
+ * @param item Number The index of the element to position at.
+ * @param dontSelect Boolean True if select should be avoided
+ */
+ scrollTo: function (item, dontSelect) {
+ var carousel = this, animate, animCfg, isCircular, isVertical,
+ rows, delta, direction, firstItem, lastItem, itemsPerRow,
+ itemsPerCol, numItems, numPerPage, offset, page, rv, sentinel,
+ index, stopAutoScroll,
+ itemsTable = carousel._itemsTable,
+ items = itemsTable.items,
+ loading = itemsTable.loading;
+
+ if (JS.isUndefined(item) || item == carousel._firstItem ||
+ carousel.isAnimating()) {
+ return; // nothing to do!
+ }
+
+ animCfg = carousel.get("animation");
+ isCircular = carousel.get("isCircular");
+ isVertical = carousel.get("isVertical");
+ itemsPerRow = carousel._cols;
+ itemsPerCol = carousel._rows;
+ firstItem = carousel._firstItem;
+ numItems = carousel.get("numItems");
+ numPerPage = carousel.get("numVisible");
+ page = carousel.get("currentPage");
+
+ stopAutoScroll = function () {
+ if (carousel.isAutoPlayOn()) {
+ carousel.stopAutoPlay();
+ }
+ };
+
+ if (item < 0) {
+ if (isCircular) {
+ item = numItems + item;
+ } else {
+ stopAutoScroll.call(carousel);
+ return;
+ }
+ } else if (numItems > 0 && item > numItems - 1) {
+
+ if (carousel.get("isCircular")) {
+ item = numItems - item;
+ } else {
+ stopAutoScroll.call(carousel);
+ return;
+ }
+ }
+
+ if (isNaN(item)) {
+ return;
+ }
+
+ direction = (carousel._firstItem > item) ? "backward" : "forward";
+
+ sentinel = firstItem + numPerPage;
+ sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
+ rv = carousel.fireEvent(beforeScrollEvent,
+ { dir: direction, first: firstItem, last: sentinel });
+ if (rv === false) { // scrolling is prevented
+ return;
+ }
+
+ carousel.fireEvent(beforePageChangeEvent, { page: page });
+
+ // call loaditems to check if we have all the items to display
+ lastItem = item + numPerPage - 1;
+ carousel._loadItems(lastItem > numItems-1 ? numItems-1 : lastItem);
+
+ // Calculate the delta relative to the first item, the delta is
+ // always negative.
+ delta = 0 - item;
+
+ if (itemsPerCol) {
+ // offset calculations for multirow Carousel
+ if (isVertical) {
+ delta = parseInt(delta / itemsPerRow, 10);
+ } else {
+ delta = parseInt(delta / itemsPerCol, 10);
+ }
+ }
+
+ // adjust for items not yet loaded
+ index = 0;
+ while (delta < 0 && index < item+numPerPage-1 && index < numItems) {
+ if (!items[index] && !loading[index]) {
+ delta++;
+ }
+ index += itemsPerCol ? itemsPerCol : 1;
+ }
+
+ carousel._firstItem = item;
+ carousel.set("firstVisible", item);
+
+
+ sentinel = item + numPerPage;
+ sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
+
+ offset = getScrollOffset.call(carousel, delta);
+
+ animate = animCfg.speed > 0;
+
+ if (animate) {
+ carousel._animateAndSetCarouselOffset(offset, item, sentinel,
+ dontSelect);
+ } else {
+ carousel._setCarouselOffset(offset);
+ updateStateAfterScroll.call(carousel, item, sentinel);
+ }
+ },
+
+ /**
+ * Get the page an item is on within carousel.
+ *
+ * @method getPageForItem
+ * @public
+ * @param index {Number} Index of item
+ * @return {Number} Page item is on
+ */
+ getPageForItem : function(item) {
+ return Math.ceil(
+ (item+1) / parseInt(this.get("numVisible"),10)
+ );
+ },
+
+ /**
+ * Get the first visible item's index on any given page.
+ *
+ * @method getFirstVisibleOnpage
+ * @public
+ * @param page {Number} Page
+ * @return {Number} First item's index
+ */
+ getFirstVisibleOnPage : function(page) {
+ return (page - 1) * this.get("numVisible");
+ },
+
+ /**
+ * Select the previous item in the Carousel.
+ *
+ * @method selectPreviousItem
+ * @public
+ */
+ selectPreviousItem: function () {
+ var carousel = this,
+ newpos = 0,
+ selected = carousel.get("selectedItem");
+
+ if (selected == this._firstItem) {
+ newpos = selected - carousel.get("numVisible");
+ carousel._selectedItem = carousel._getSelectedItem(selected-1);
+ carousel.scrollTo(newpos);
+ } else {
+ newpos = carousel.get("selectedItem") -
+ carousel.get("scrollIncrement");
+ carousel.set("selectedItem",carousel._getSelectedItem(newpos));
+ }
+ },
+
+ /**
+ * Select the next item in the Carousel.
+ *
+ * @method selectNextItem
+ * @public
+ */
+ selectNextItem: function () {
+ var carousel = this, newpos = 0;
+
+ newpos = carousel.get("selectedItem") +
+ carousel.get("scrollIncrement");
+ carousel.set("selectedItem", carousel._getSelectedItem(newpos));
+ },
+
+ /**
+ * Display the Carousel.
+ *
+ * @method show
+ * @public
+ */
+ show: function () {
+ var carousel = this,
+ cssClass = carousel.CLASSES;
+
+ if (carousel.fireEvent(beforeShowEvent) !== false) {
+ carousel.addClass(cssClass.VISIBLE);
+ carousel.fireEvent(showEvent);
+ }
+ },
+
+ /**
+ * Start auto-playing the Carousel.
+ *
+ * @method startAutoPlay
+ * @public
+ */
+ startAutoPlay: function () {
+ var carousel = this, timer;
+
+ if (JS.isUndefined(carousel._autoPlayTimer)) {
+ timer = carousel.get("autoPlayInterval");
+ if (timer <= 0) {
+ return;
+ }
+ carousel._isAutoPlayInProgress = true;
+ carousel.fireEvent(startAutoPlayEvent);
+ carousel._autoPlayTimer = setTimeout(function () {
+ carousel._autoScroll();
+ }, timer);
+ }
+ },
+
+ /**
+ * Stop auto-playing the Carousel.
+ *
+ * @method stopAutoPlay
+ * @public
+ */
+ stopAutoPlay: function () {
+ var carousel = this;
+
+ if (!JS.isUndefined(carousel._autoPlayTimer)) {
+ clearTimeout(carousel._autoPlayTimer);
+ delete carousel._autoPlayTimer;
+ carousel._isAutoPlayInProgress = false;
+ carousel.fireEvent(stopAutoPlayEvent);
+ }
+ },
+
+ /**
+ * Update interface's pagination data within a registered template.
+ *
+ * @method updatePagination
+ * @public
+ */
+ updatePagination: function () {
+ var carousel = this,
+ pagination = carousel._pagination;
+ if(!pagination.el){ return false; }
+
+ var numItems = carousel.get('numItems'),
+ numVisible = carousel.get('numVisible'),
+ firstVisible = carousel.get('firstVisible')+1,
+ currentPage = carousel.get('currentPage')+1,
+ numPages = carousel.get('numPages'),
+ replacements = {
+ 'numVisible' : numVisible,
+ 'numPages' : numPages,
+ 'numItems' : numItems,
+ 'selectedItem' : carousel.get('selectedItem')+1,
+ 'currentPage' : currentPage,
+ 'firstVisible' : firstVisible,
+ 'lastVisible' : carousel.get("lastVisible")+1
+ },
+ cb = pagination.callback || {},
+ scope = cb.scope && cb.obj ? cb.obj : carousel;
+
+ pagination.el.innerHTML = JS.isFunction(cb.fn) ? cb.fn.apply(scope, [pagination.template, replacements]) : YAHOO.lang.substitute(pagination.template, replacements);
+ },
+
+ /**
+ * Register carousels pagination template, append to interface, and populate.
+ *
+ * @method registerPagination
+ * @param template {String} Pagination template as passed to lang.substitute
+ * @public
+ */
+ registerPagination: function (tpl, pos, cb) {
+ var carousel = this;
+
+ carousel._pagination.template = tpl;
+ carousel._pagination.callback = cb || {};
+
+ if(!carousel._pagination.el){
+ carousel._pagination.el = createElement('DIV', {className:carousel.CLASSES.PAGINATION});
+
+ if(pos == "before"){
+ carousel._navEl.insertBefore(carousel._pagination.el, carousel._navEl.firstChild);
+ } else {
+ carousel._navEl.appendChild(carousel._pagination.el);
+ }
+
+ carousel.on('itemSelected', carousel.updatePagination);
+ carousel.on('pageChange', carousel.updatePagination);
+ }
+
+ carousel.updatePagination();
+ },
+
+ /**
+ * Return the string representation of the Carousel.
+ *
+ * @method toString
+ * @public
+ * @return {String}
+ */
+ toString: function () {
+ return WidgetName + (this.get ? " (#" + this.get("id") + ")" : "");
+ },
+
+ /*
+ * Protected methods of the Carousel component
+ */
+
+ /**
+ * Set the Carousel offset to the passed offset after animating.
+ *
+ * @method _animateAndSetCarouselOffset
+ * @param {Integer} offset The offset to which the Carousel has to be
+ * scrolled to.
+ * @param {Integer} item The index to which the Carousel will scroll.
+ * @param {Integer} sentinel The last element in the view port.
+ * @protected
+ */
+ _animateAndSetCarouselOffset: function (offset, item, sentinel) {
+ var carousel = this,
+ animCfg = carousel.get("animation"),
+ animObj = null;
+
+ if (carousel.get("isVertical")) {
+ animObj = new YAHOO.util.Motion(carousel._carouselEl,
+ { top: { to: offset } },
+ animCfg.speed, animCfg.effect);
+ } else {
+ animObj = new YAHOO.util.Motion(carousel._carouselEl,
+ { left: { to: offset } },
+ animCfg.speed, animCfg.effect);
+ }
+
+ carousel._isAnimationInProgress = true;
+ animObj.onComplete.subscribe(carousel._animationCompleteHandler,
+ { scope: carousel, item: item,
+ last: sentinel });
+ animObj.animate();
+ },
+
+ /**
+ * Handle the animation complete event.
+ *
+ * @method _animationCompleteHandler
+ * @param {Event} ev The event.
+ * @param {Array} p The event parameters.
+ * @param {Object} o The object that has the state of the Carousel
+ * @protected
+ */
+ _animationCompleteHandler: function (ev, p, o) {
+ o.scope._isAnimationInProgress = false;
+ updateStateAfterScroll.call(o.scope, o.item, o.last);
+ },
+
+ /**
+ * Automatically scroll the contents of the Carousel.
+ * @method _autoScroll
+ * @protected
+ */
+ _autoScroll: function() {
+ var carousel = this,
+ currIndex = carousel._firstItem,
+ index;
+
+ if (currIndex >= carousel.get("numItems") - 1) {
+ if (carousel.get("isCircular")) {
+ index = 0;
+ } else {
+ carousel.stopAutoPlay();
+ }
+ } else {
+ index = currIndex + carousel.get("numVisible");
+ }
+
+ carousel._selectedItem = carousel._getSelectedItem(index);
+ carousel.scrollTo.call(carousel, index);
+ },
+
+ /**
+ * Create the Carousel.
+ *
+ * @method createCarousel
+ * @param elId {String} The id of the element to be created
+ * @protected
+ */
+ _createCarousel: function (elId) {
+ var carousel = this,
+ cssClass = carousel.CLASSES,
+ el = Dom.get(elId);
+
+ if (!el) {
+ el = createElement("DIV", {
+ className : cssClass.CAROUSEL,
+ id : elId
+ });
+ }
+
+ if (!carousel._carouselEl) {
+ carousel._carouselEl=createElement(carousel.get("carouselEl"),
+ { className: cssClass.CAROUSEL_EL });
+ }
+
+ return el;
+ },
+
+ /**
+ * Create the Carousel clip container.
+ *
+ * @method createCarouselClip
+ * @protected
+ */
+ _createCarouselClip: function () {
+ return createElement("DIV", { className: this.CLASSES.CONTENT });
+ },
+
+ /**
+ * Create the Carousel item.
+ *
+ * @method createCarouselItem
+ * @param obj {Object} The attributes of the element to be created
+ * @protected
+ */
+ _createCarouselItem: function (obj) {
+ var attr, carousel = this,
+ styles = getCarouselItemPosition.call(carousel, obj.pos);
+
+ return createElement(carousel.get("carouselItemEl"), {
+ className : obj.className,
+ styles : obj.styles,
+ content : obj.content,
+ id : obj.id
+ });
+ },
+
+ /**
+ * Return a valid item for a possibly out of bounds index considering
+ * the isCircular property.
+ *
+ * @method _getValidIndex
+ * @param index {Number} The index of the item to be returned
+ * @return {Object} Return a valid item index
+ * @protected
+ */
+ _getValidIndex: function (index) {
+ var carousel = this,
+ isCircular = carousel.get("isCircular"),
+ numItems = carousel.get("numItems"),
+ numVisible = carousel.get("numVisible"),
+ sentinel = numItems - 1;
+
+ if (index < 0) {
+ index = isCircular ?
+ Math.ceil(numItems/numVisible)*numVisible + index : 0;
+ } else if (index > sentinel) {
+ index = isCircular ? 0 : sentinel;
+ }
+
+ return index;
+ },
+
+ /**
+ * Get the value for the selected item.
+ *
+ * @method _getSelectedItem
+ * @param val {Number} The new value for "selected" item
+ * @return {Number} The new value that would be set
+ * @protected
+ */
+ _getSelectedItem: function (val) {
+ var carousel = this,
+ isCircular = carousel.get("isCircular"),
+ numItems = carousel.get("numItems"),
+ sentinel = numItems - 1;
+
+ if (val < 0) {
+ if (isCircular) {
+ val = numItems + val;
+ } else {
+ val = carousel.get("selectedItem");
+ }
+ } else if (val > sentinel) {
+ if (isCircular) {
+ val = val - numItems;
+ } else {
+ val = carousel.get("selectedItem");
+ }
+ }
+ return val;
+ },
+
+ /**
+ * The "click" handler for the item.
+ *
+ * @method _itemClickHandler
+ * @param {Event} ev The event object
+ * @protected
+ */
+ _itemClickHandler: function (ev) {
+ var carousel = this,
+ carouselItem = carousel.get("carouselItemEl"),
+ container = carousel.get("element"),
+ el,
+ item,
+ target = Event.getTarget(ev),
+ tag = target.tagName.toUpperCase();
+
+ if(tag === "INPUT" ||
+ tag === "SELECT" ||
+ tag === "TEXTAREA") {
+ return;
+ }
+
+ while (target && target != container &&
+ target.id != carousel._carouselEl) {
+ el = target.nodeName;
+ if (el.toUpperCase() == carouselItem) {
+ break;
+ }
+ target = target.parentNode;
+ }
+
+ if ((item = carousel.getItemPositionById(target.id)) >= 0) {
+ carousel.set("selectedItem", carousel._getSelectedItem(item));
+ carousel.focus();
+ }
+ },
+
+ /**
+ * The keyboard event handler for Carousel.
+ *
+ * @method _keyboardEventHandler
+ * @param ev {Event} The event that is being handled.
+ * @protected
+ */
+ _keyboardEventHandler: function (ev) {
+ var carousel = this,
+ key = Event.getCharCode(ev),
+ target = Event.getTarget(ev),
+ prevent = false;
+
+ // do not mess while animation is in progress or naving via select
+ if (carousel.isAnimating() || target.tagName.toUpperCase() === "SELECT") {
+ return;
+ }
+
+ switch (key) {
+ case 0x25: // left arrow
+ case 0x26: // up arrow
+ carousel.selectPreviousItem();
+ prevent = true;
+ break;
+ case 0x27: // right arrow
+ case 0x28: // down arrow
+ carousel.selectNextItem();
+ prevent = true;
+ break;
+ case 0x21: // page-up
+ carousel.scrollPageBackward();
+ prevent = true;
+ break;
+ case 0x22: // page-down
+ carousel.scrollPageForward();
+ prevent = true;
+ break;
+ }
+
+ if (prevent) {
+ if (carousel.isAutoPlayOn()) {
+ carousel.stopAutoPlay();
+ }
+ Event.preventDefault(ev);
+ }
+ },
+
+ /**
+ * The load the required set of items that are needed for display.
+ *
+ * @method _loadItems
+ * @protected
+ */
+ _loadItems: function(last) {
+ var carousel = this,
+ numItems = carousel.get("numItems"),
+ numVisible = carousel.get("numVisible"),
+ reveal = carousel.get("revealAmount"),
+ first = carousel._itemsTable.items.length,
+ lastVisible = carousel.get("lastVisible");
+
+ // adjust if going backwards
+ if(first > last && last+1 >= numVisible){
+ // need to get first a bit differently for the last page
+ first = last % numVisible || last == lastVisible ? last - last % numVisible : last - numVisible + 1;
+ }
+
+ if(reveal && last < numItems - 1){ last++; }
+
+ if (last >= first && (!carousel.getItem(first) || !carousel.getItem(last))) {
+ carousel.fireEvent(loadItemsEvent, {
+ ev: loadItemsEvent, first: first, last: last,
+ num: last - first + 1
+ });
+ }
+
+ },
+
+ /**
+ * The "onchange" handler for select box pagination.
+ *
+ * @method _pagerChangeHandler
+ * @param {Event} ev The event object
+ * @protected
+ */
+ _pagerChangeHandler: function (ev) {
+ var carousel = this,
+ target = Event.getTarget(ev),
+ page = target.value,
+ item;
+
+ if (page) {
+ item = carousel.getFirstVisibleOnPage(page);
+ carousel._selectedItem = item;
+ carousel.scrollTo(item);
+ carousel.focus();
+ }
+ },
+ /**
+ * The "click" handler for anchor pagination.
+ *
+ * @method _pagerClickHandler
+ * @param {Event} ev The event object
+ * @protected
+ */
+ _pagerClickHandler: function (ev) {
+ var carousel = this,
+ css = carousel.CLASSES,
+ target = Event.getTarget(ev),
+ elNode = target.nodeName.toUpperCase(),
+ val,
+ stringIndex,
+ page,
+ item;
+
+ if (Dom.hasClass(target, css.PAGER_ITEM) || Dom.hasClass(target.parentNode, css.PAGER_ITEM)) {
+ if (elNode == "EM") {
+ target = target.parentNode;// item is an em and not an anchor (when text is visible)
+ }
+ val = target.href;
+ stringIndex = val.lastIndexOf("#");
+ page = parseInt(val.substring(stringIndex+1), 10);
+ if (page != -1) {
+ item = carousel.getFirstVisibleOnPage(page);
+ carousel._selectedItem = item;
+ carousel.scrollTo(item);
+ carousel.focus();
+ }
+ Event.preventDefault(ev);
+ }
+ },
+
+ /**
+ * Find the Carousel within a container. The Carousel is identified by
+ * the first element that matches the carousel element tag or the
+ * element that has the Carousel class.
+ *
+ * @method parseCarousel
+ * @param parent {HTMLElement} The parent element to look under
+ * @return {Boolean} True if Carousel is found, false otherwise
+ * @protected
+ */
+ _parseCarousel: function (parent) {
+ var carousel = this, child, cssClass, domEl, found, node;
+
+ cssClass = carousel.CLASSES;
+ domEl = carousel.get("carouselEl");
+ found = false;
+
+ for (child = parent.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType == 1) {
+ node = child.nodeName;
+ if (node.toUpperCase() == domEl) {
+ carousel._carouselEl = child;
+ Dom.addClass(carousel._carouselEl,
+ carousel.CLASSES.CAROUSEL_EL);
+ found = true;
+ }
+ }
+ }
+
+ return found;
+ },
+
+ /**
+ * Find the items within the Carousel and add them to the items table.
+ * A Carousel item is identified by elements that matches the carousel
+ * item element tag.
+ *
+ * @method parseCarouselItems
+ * @protected
+ */
+ _parseCarouselItems: function () {
+ var carousel = this,
+ cssClass = carousel.CLASSES,
+ i=0,
+ rows,
+ child,
+ domItemEl,
+ elId,
+ node,
+ index = carousel.get("firstVisible"),
+ parent = carousel._carouselEl;
+
+ rows = carousel._rows;
+ domItemEl = carousel.get("carouselItemEl");
+
+ for (child = parent.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType == 1) {
+ node = child.nodeName;
+ if (node.toUpperCase() == domItemEl) {
+ if (child.id) {
+ elId = child.id;
+ } else {
+ elId = Dom.generateId();
+ child.setAttribute("id", elId);
+ }
+ carousel.addItem(child,index);
+ index++;
+ }
+ }
+ }
+ },
+
+ /**
+ * Find the Carousel navigation within a container. The navigation
+ * elements need to match the carousel navigation class names.
+ *
+ * @method parseCarouselNavigation
+ * @param parent {HTMLElement} The parent element to look under
+ * @return {Boolean} True if at least one is found, false otherwise
+ * @protected
+ */
+ _parseCarouselNavigation: function (parent) {
+ var carousel = this,
+ cfg,
+ cssClass = carousel.CLASSES,
+ el,
+ i,
+ j,
+ nav,
+ rv = false;
+
+ nav = Dom.getElementsByClassName(cssClass.PREV_PAGE, "*", parent);
+ if (nav.length > 0) {
+ for (i in nav) {
+ if (nav.hasOwnProperty(i)) {
+ el = nav[i];
+ if (el.nodeName == "INPUT" ||
+ el.nodeName == "BUTTON" ||
+ el.nodeName == "A") {// Anchor support in Nav (for SEO)
+ carousel._navBtns.prev.push(el);
+ } else {
+ j = el.getElementsByTagName("INPUT");
+ if (JS.isArray(j) && j.length > 0) {
+ carousel._navBtns.prev.push(j[0]);
+ } else {
+ j = el.getElementsByTagName("BUTTON");
+ if (JS.isArray(j) && j.length > 0) {
+ carousel._navBtns.prev.push(j[0]);
+ }
+ }
+ }
+ }
+ }
+ cfg = { prev: nav };
+ }
+
+ nav = Dom.getElementsByClassName(cssClass.NEXT_PAGE, "*", parent);
+ if (nav.length > 0) {
+ for (i in nav) {
+ if (nav.hasOwnProperty(i)) {
+ el = nav[i];
+ if (el.nodeName == "INPUT" ||
+ el.nodeName == "BUTTON" ||
+ el.nodeName == "A") {// Anchor support in Nav (for SEO)
+ carousel._navBtns.next.push(el);
+ } else {
+ j = el.getElementsByTagName("INPUT");
+ if (JS.isArray(j) && j.length > 0) {
+ carousel._navBtns.next.push(j[0]);
+ } else {
+ j = el.getElementsByTagName("BUTTON");
+ if (JS.isArray(j) && j.length > 0) {
+ carousel._navBtns.next.push(j[0]);
+ }
+ }
+ }
+ }
+ }
+ if (cfg) {
+ cfg.next = nav;
+ } else {
+ cfg = { next: nav };
+ }
+ }
+
+ if (cfg) {
+ carousel.set("navigation", cfg);
+ rv = true;
+ }
+
+ return rv;
+ },
+
+ /**
+ * Refresh the widget UI if it is not already rendered, on first item
+ * addition.
+ *
+ * @method _refreshUi
+ * @protected
+ */
+ _refreshUi: function () {
+ var carousel = this, i, isVertical = carousel.get("isVertical"), firstVisible = carousel.get("firstVisible"), item, n, rsz, sz;
+
+ if (carousel._itemsTable.numItems < 1) {
+ return;
+ }
+
+ sz = getCarouselItemSize.call(carousel,
+ isVertical ? "height" : "width");
+ // This fixes the widget to auto-adjust height/width for absolute
+ // positioned children.
+ item = carousel._itemsTable.items[firstVisible].id;
+
+ sz = isVertical ? getStyle(item, "width") :
+ getStyle(item, "height");
+
+ Dom.setStyle(carousel._carouselEl,
+ isVertical ? "width" : "height", sz + "px");
+
+ // Set the rendered state appropriately.
+ carousel._hasRendered = true;
+ carousel.fireEvent(renderEvent);
+ },
+
+ /**
+ * Set the Carousel offset to the passed offset.
+ *
+ * @method _setCarouselOffset
+ * @protected
+ */
+ _setCarouselOffset: function (offset) {
+ var carousel = this, which;
+
+ which = carousel.get("isVertical") ? "top" : "left";
+ Dom.setStyle(carousel._carouselEl, which, offset + "px");
+ },
+
+ /**
+ * Setup/Create the Carousel navigation element (if needed).
+ *
+ * @method _setupCarouselNavigation
+ * @protected
+ */
+ _setupCarouselNavigation: function () {
+ var carousel = this,
+ btn, cfg, cssClass, nav, navContainer, nextButton, prevButton;
+
+ cssClass = carousel.CLASSES;
+
+ // TODO: can the _navBtns be tested against instead?
+ navContainer = Dom.getElementsByClassName(cssClass.NAVIGATION,
+ "DIV", carousel.get("element"));
+
+ if (navContainer.length === 0) {
+ navContainer = createElement("DIV",
+ { className: cssClass.NAVIGATION });
+ carousel.insertBefore(navContainer,
+ Dom.getFirstChild(carousel.get("element")));
+ } else {
+ navContainer = navContainer[0];
+ }
+
+ carousel._pages.el = createElement("UL");
+ navContainer.appendChild(carousel._pages.el);
+
+ nav = carousel.get("navigation");
+ if (JS.isString(nav.prev) || JS.isArray(nav.prev)) {
+ if (JS.isString(nav.prev)) {
+ nav.prev = [nav.prev];
+ }
+ for (btn in nav.prev) {
+ if (nav.prev.hasOwnProperty(btn)) {
+ carousel._navBtns.prev.push(Dom.get(nav.prev[btn]));
+ }
+ }
+ } else {
+ // TODO: separate method for creating a navigation button
+ prevButton = createElement("SPAN",
+ { className: cssClass.BUTTON + cssClass.FIRST_NAV });
+ // XXX: for IE 6.x
+ Dom.setStyle(prevButton, "visibility", "visible");
+ btn = Dom.generateId();
+ prevButton.innerHTML = "";
+ navContainer.appendChild(prevButton);
+ btn = Dom.get(btn);
+ carousel._navBtns.prev = [btn];
+ cfg = { prev: [prevButton] };
+ }
+
+ if (JS.isString(nav.next) || JS.isArray(nav.next)) {
+ if (JS.isString(nav.next)) {
+ nav.next = [nav.next];
+ }
+ for (btn in nav.next) {
+ if (nav.next.hasOwnProperty(btn)) {
+ carousel._navBtns.next.push(Dom.get(nav.next[btn]));
+ }
+ }
+ } else {
+ // TODO: separate method for creating a navigation button
+ nextButton = createElement("SPAN",
+ { className: cssClass.BUTTON + cssClass.NEXT_NAV });
+ // XXX: for IE 6.x
+ Dom.setStyle(nextButton, "visibility", "visible");
+ btn = Dom.generateId();
+ nextButton.innerHTML = "";
+ navContainer.appendChild(nextButton);
+ btn = Dom.get(btn);
+ carousel._navBtns.next = [btn];
+ if (cfg) {
+ cfg.next = [nextButton];
+ } else {
+ cfg = { next: [nextButton] };
+ }
+ }
+
+ if (cfg) {
+ carousel.set("navigation", cfg);
+ }
+
+ return navContainer;
+ },
+
+ /**
+ * Set the clip container size (based on the new numVisible value).
+ *
+ * @method _setClipContainerSize
+ * @param clip {HTMLElement} The clip container element.
+ * @param num {Number} optional The number of items per page.
+ * @protected
+ */
+ _setClipContainerSize: function (clip, num) {
+ var carousel = this,
+ isVertical = carousel.get("isVertical"),
+ rows = carousel._rows,
+ cols = carousel._cols,
+ reveal = carousel.get("revealAmount"),
+ itemHeight = getCarouselItemSize.call(carousel, "height"),
+ itemWidth = getCarouselItemSize.call(carousel, "width"),
+ containerHeight,
+ containerWidth;
+
+ clip = clip || carousel._clipEl;
+
+ if (rows) {
+ containerHeight = itemHeight * rows;
+ containerWidth = itemWidth * cols;
+ } else {
+ num = num || carousel.get("numVisible");
+ if (isVertical) {
+ containerHeight = itemHeight * num;
+ } else {
+ containerWidth = itemWidth * num;
+ }
+ }
+
+ // TODO: try to re-use the _hasRendered indicator
+
+ carousel._recomputeSize = (containerHeight === 0); // bleh!
+ if (carousel._recomputeSize) {
+ carousel._hasRendered = false;
+ return; // no use going further, bail out!
+ }
+
+ reveal = getRevealSize.call(carousel);
+ if (isVertical) {
+ containerHeight += (reveal * 2);
+ } else {
+ containerWidth += (reveal * 2);
+ }
+
+ if (isVertical) {
+ containerHeight += getDimensions(carousel._carouselEl,"height");
+ Dom.setStyle(clip, "height", containerHeight + "px");
+ // For multi-row Carousel
+ if (cols) {
+ containerWidth += getDimensions(carousel._carouselEl,
+ "width");
+ Dom.setStyle(clip, "width", containerWidth + (0) + "px");
+ }
+ } else {
+ containerWidth += getDimensions(carousel._carouselEl, "width");
+ Dom.setStyle(clip, "width", containerWidth + "px");
+ // For multi-row Carousel
+ if (rows) {
+ containerHeight += getDimensions(carousel._carouselEl,
+ "height");
+ Dom.setStyle(clip, "height", containerHeight + "px");
+ }
+ }
+
+ carousel._setContainerSize(clip); // adjust the container size too
+ },
+
+ /**
+ * Set the container size.
+ *
+ * @method _setContainerSize
+ * @param clip {HTMLElement} The clip container element.
+ * @param attr {String} Either set the height or width.
+ * @protected
+ */
+ _setContainerSize: function (clip, attr) {
+ var carousel = this,
+ config = carousel.CONFIG,
+ cssClass = carousel.CLASSES,
+ isVertical,
+ rows,
+ cols,
+ size;
+
+ isVertical = carousel.get("isVertical");
+ rows = carousel._rows;
+ cols = carousel._cols;
+ clip = clip || carousel._clipEl;
+ attr = attr || (isVertical ? "height" : "width");
+ size = parseFloat(Dom.getStyle(clip, attr), 10);
+
+ size = JS.isNumber(size) ? size : 0;
+
+ if (isVertical) {
+ size += getDimensions(carousel._carouselEl, "height") +
+ getStyle(carousel._navEl, "height");
+ } else {
+ size += getDimensions(carousel._carouselEl, "width");
+ }
+
+ if (!isVertical) {
+ if (size < config.HORZ_MIN_WIDTH) {
+ size = config.HORZ_MIN_WIDTH;
+ carousel.addClass(cssClass.MIN_WIDTH);
+ }
+ }
+ carousel.setStyle(attr, size + "px");
+
+ // Additionally the width of the container should be set for
+ // the vertical Carousel
+ if (isVertical) {
+ size = getCarouselItemSize.call(carousel, "width");
+ if(cols) {
+ size = size * cols;
+ }
+ Dom.setStyle(carousel._carouselEl, "width", size + "px");// Bug fix for vertical carousel (goes in conjunction with .yui-carousel-element {... 3200px removed from styles), and allows for multirows in IEs).
+ if (size < config.VERT_MIN_WIDTH) {
+ size = config.VERT_MIN_WIDTH;
+ carousel.addClass(cssClass.MIN_WIDTH);// set a min width on vertical carousel, don't see why this shouldn't always be set...
+ }
+ carousel.setStyle("width", size + "px");
+ } else {
+ if(rows) {
+ size = getCarouselItemSize.call(carousel, "height");
+ size = size * rows;
+ Dom.setStyle(carousel._carouselEl, "height", size + "px");
+ }
+ }
+ },
+
+ /**
+ * Set the value for the Carousel's first visible item.
+ *
+ * @method _setFirstVisible
+ * @param val {Number} The new value for firstVisible
+ * @return {Number} The new value that would be set
+ * @protected
+ */
+ _setFirstVisible: function (val) {
+ var carousel = this;
+
+ if (val >= 0 && val < carousel.get("numItems")) {
+ carousel.scrollTo(val);
+ } else {
+ val = carousel.get("firstVisible");
+ }
+ return val;
+ },
+
+ /**
+ * Set the value for the Carousel's navigation.
+ *
+ * @method _setNavigation
+ * @param cfg {Object} The navigation configuration
+ * @return {Object} The new value that would be set
+ * @protected
+ */
+ _setNavigation: function (cfg) {
+ var carousel = this;
+
+ if (cfg.prev) {
+ Event.on(cfg.prev, "click", scrollPageBackward, carousel);
+ }
+ if (cfg.next) {
+ Event.on(cfg.next, "click", scrollPageForward, carousel);
+ }
+ },
+
+ /**
+ * Clip the container size every time numVisible is set.
+ *
+ * @method _setNumVisible
+ * @param val {Number} The new value for numVisible
+ * @return {Number} The new value that would be set
+ * @protected
+ */
+ _setNumVisible: function (val) { // TODO: _setNumVisible should just be reserved for setting numVisible.
+ var carousel = this;
+
+ carousel._setClipContainerSize(carousel._clipEl, val);
+ },
+
+ /**
+ * Set the value for the number of visible items in the Carousel.
+ *
+ * @method _numVisibleSetter
+ * @param val {Number} The new value for numVisible
+ * @return {Number} The new value that would be set
+ * @protected
+ */
+ _numVisibleSetter: function (val) {
+ var carousel = this,
+ numVisible = val;
+
+ if(JS.isArray(val)) {
+ carousel._cols = val[0];
+ carousel._rows = val[1];
+ numVisible = val[0] * val[1];
+ }
+ return numVisible;
+ },
+
+ /**
+ * Set the value for selectedItem.
+ *
+ * @method _selectedItemSetter
+ * @param val {Number} The new value for selectedItem
+ * @return {Number} The new value that would be set
+ * @protected
+ */
+ _selectedItemSetter: function (val) {
+ var carousel = this;
+ return (val < carousel.get("numItems")) ? val : 0;
+ },
+
+ /**
+ * Set the number of items in the Carousel.
+ * Warning: Setting this to a lower number than the current removes
+ * items from the end.
+ *
+ * @method _setNumItems
+ * @param val {Number} The new value for numItems
+ * @return {Number} The new value that would be set
+ * @protected
+ */
+ _setNumItems: function (val) {
+ var carousel = this,
+ num = carousel._itemsTable.numItems;
+
+ if (JS.isArray(carousel._itemsTable.items)) {
+ if (carousel._itemsTable.items.length != num) { // out of sync
+ num = carousel._itemsTable.items.length;
+ carousel._itemsTable.numItems = num;
+ }
+ }
+
+ if (val < num) {
+ while (num > val) {
+ carousel.removeItem(num - 1);
+ num--;
+ }
+ }
+
+ return val;
+ },
+
+ /**
+ * Set the orientation of the Carousel.
+ *
+ * @method _setOrientation
+ * @param val {Boolean} The new value for isVertical
+ * @return {Boolean} The new value that would be set
+ * @protected
+ */
+ _setOrientation: function (val) {
+ var carousel = this,
+ cssClass = carousel.CLASSES;
+
+ if (val) {
+ carousel.replaceClass(cssClass.HORIZONTAL, cssClass.VERTICAL);
+ } else {
+ carousel.replaceClass(cssClass.VERTICAL, cssClass.HORIZONTAL);
+ }
+ this._itemAttrCache = {}; // force recomputed next time
+
+ return val;
+ },
+
+ /**
+ * Set the value for the reveal amount percentage in the Carousel.
+ *
+ * @method _setRevealAmount
+ * @param val {Number} The new value for revealAmount
+ * @return {Number} The new value that would be set
+ * @protected
+ */
+ _setRevealAmount: function (val) {
+ var carousel = this;
+
+ if (val >= 0 && val <= 100) {
+ val = parseInt(val, 10);
+ val = JS.isNumber(val) ? val : 0;
+ carousel._setClipContainerSize();
+ } else {
+ val = carousel.get("revealAmount");
+ }
+ return val;
+ },
+
+ /**
+ * Set the value for the selected item.
+ *
+ * @method _setSelectedItem
+ * @param val {Number} The new value for "selected" item
+ * @protected
+ */
+ _setSelectedItem: function (val) {
+ this._selectedItem = val;
+ },
+
+ /**
+ * Get the total number of pages.
+ *
+ * @method _getNumPages
+ * @protected
+ */
+ _getNumPages: function () {
+ return Math.ceil(
+ parseInt(this.get("numItems"),10) / parseInt(this.get("numVisible"),10)
+ );
+ },
+
+ /**
+ * Get the index of the last visible item
+ *
+ * @method _getLastVisible
+ * @protected
+ */
+ _getLastVisible: function () {
+ var carousel = this;
+ return carousel.get("currentPage") + 1 == carousel.get("numPages") ?
+ carousel.get("numItems") - 1:
+ carousel.get("firstVisible") + carousel.get("numVisible") - 1;
+ },
+
+ /**
+ * Synchronize and redraw the UI after an item is added.
+ *
+ * @method _syncUiForItemAdd
+ * @protected
+ */
+ _syncUiForItemAdd: function (obj) {
+ var attr,
+ carousel = this,
+ carouselEl = carousel._carouselEl,
+ el,
+ item,
+ itemsTable = carousel._itemsTable,
+ oel,
+ pos,
+ sibling,
+ styles;
+
+ pos = JS.isUndefined(obj.pos) ?
+ obj.newPos || itemsTable.numItems - 1 : obj.pos;
+
+ if (!oel) {
+ item = itemsTable.items[pos] || {};
+ el = carousel._createCarouselItem({
+ className : item.className,
+ styles : item.styles,
+ content : item.item,
+ id : item.id,
+ pos : pos
+ });
+ if (JS.isUndefined(obj.pos)) {
+ if (!JS.isUndefined(itemsTable.loading[pos])) {
+ oel = itemsTable.loading[pos];
+ // if oel is null, it is a problem ...
+ }
+ if (oel) {
+ // replace the node
+ carouselEl.replaceChild(el, oel);
+ // ... and remove the item from the data structure
+ delete itemsTable.loading[pos];
+ } else {
+ carouselEl.appendChild(el);
+ }
+ } else {
+ if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
+ sibling = Dom.get(itemsTable.items[obj.pos + 1].id);
+ }
+ if (sibling) {
+ carouselEl.insertBefore(el, sibling);
+ } else {
+ }
+ }
+ } else {
+ if (JS.isUndefined(obj.pos)) {
+ if (!Dom.isAncestor(carousel._carouselEl, oel)) {
+ carouselEl.appendChild(oel);
+ }
+ } else {
+ if (!Dom.isAncestor(carouselEl, oel)) {
+ if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
+ carouselEl.insertBefore(oel,
+ Dom.get(itemsTable.items[obj.pos + 1].id));
+ }
+ }
+ }
+ }
+
+ if (!carousel._hasRendered) {
+ carousel._refreshUi();
+ }
+
+ if (carousel.get("selectedItem") < 0) {
+ carousel.set("selectedItem", carousel.get("firstVisible"));
+ }
+
+ carousel._syncUiItems();
+ },
+
+ /**
+ * Synchronize and redraw the UI after an item is replaced.
+ *
+ * @method _syncUiForItemReplace
+ * @protected
+ */
+ _syncUiForItemReplace: function (o) {
+ var carousel = this,
+ carouselEl = carousel._carouselEl,
+ itemsTable = carousel._itemsTable,
+ pos = o.pos,
+ item = o.newItem,
+ oel = o.oldItem,
+ el;
+
+ el = carousel._createCarouselItem({
+ className : item.className,
+ styles : item.styles,
+ content : item.item,
+ id : item.id,
+ pos : pos
+ });
+
+ if(el && oel) {
+ Event.purgeElement(oel, true);
+ carouselEl.replaceChild(el, Dom.get(oel.id));
+ if (!JS.isUndefined(itemsTable.loading[pos])) {
+ itemsTable.numItems++;
+ delete itemsTable.loading[pos];
+ }
+ }
+ // TODO: should we add the item if oel is undefined?
+
+ if (!carousel._hasRendered) {
+ carousel._refreshUi();
+ }
+
+ carousel._syncUiItems();
+ },
+
+ /**
+ * Synchronize and redraw the UI after an item is removed.
+ *
+ * @method _syncUiForItemAdd
+ * @protected
+ */
+ _syncUiForItemRemove: function (obj) {
+ var carousel = this,
+ carouselEl = carousel._carouselEl,
+ el, item, num, pos;
+
+ num = carousel.get("numItems");
+ item = obj.item;
+ pos = obj.pos;
+
+ if (item && (el = Dom.get(item.id))) {
+ if (el && Dom.isAncestor(carouselEl, el)) {
+ Event.purgeElement(el, true);
+ carouselEl.removeChild(el);
+ }
+
+ if (carousel.get("selectedItem") == pos) {
+ pos = pos >= num ? num - 1 : pos;
+ }
+ } else {
+ }
+
+ carousel._syncUiItems();
+ },
+
+ /**
+ * Synchronize and redraw the UI for lazy loading.
+ *
+ * @method _syncUiForLazyLoading
+ * @protected
+ */
+ _syncUiForLazyLoading: function (obj) {
+ var carousel = this,
+ carouselEl = carousel._carouselEl,
+ itemsTable = carousel._itemsTable,
+ len = itemsTable.items.length,
+ sibling = itemsTable.items[obj.last + 1],
+ el,
+ j;
+
+ // attempt to find the next closest sibling
+ if(!sibling && obj.last < len){
+ j = obj.first;
+ do {
+ sibling = itemsTable.items[j];
+ j++;
+ } while (j" +
+ carousel.STRINGS.PAGER_PREFIX_TEXT + " " + (i+1) +
+ "";
+ el.innerHTML = html;
+
+ pager.appendChild(el);
+ }
+
+ // Show the pager now
+ Dom.setStyle(pager, "visibility", "visible");
+ },
+
+ /**
+ * Update the UI for the pager menu based on the current page and
+ * the number of pages. If the number of pages is greater than
+ * MAX_PAGER_BUTTONS, then the selection of pages is provided by a drop
+ * down menu instead of a set of buttons.
+ *
+ * @method _updatePagerMenu
+ * @protected
+ */
+ _updatePagerMenu: function () {
+ var carousel = this,
+ css = carousel.CLASSES,
+ cur = carousel._pages.cur, // current page
+ el,
+ i,
+ item,
+ n = carousel.get("numVisible"),
+ num = carousel._pages.num, // total pages
+ pager = carousel._pages.el, // the pager container element
+ sel;
+
+ if (num === 0) {
+ return;// don't do anything if number of pages is 0
+ }
+
+ sel = document.createElement("SELECT");
+
+
+ if (!sel) {
+ return;
+ }
+
+ // Hide the pager before redrawing it
+ Dom.setStyle(pager, "visibility", "hidden");
+
+ // Remove all nodes from the pager
+ while (pager.firstChild) {
+ pager.removeChild(pager.firstChild);
+ }
+
+ for (i = 0; i < num; i++) {
+
+ el = document.createElement("OPTION");
+ el.value = i+1;
+ el.innerHTML = carousel.STRINGS.PAGER_PREFIX_TEXT+" "+(i+1);
+
+ if (i == cur) {
+ el.setAttribute("selected", "selected");
+ }
+
+ sel.appendChild(el);
+ }
+
+ el = document.createElement("FORM");
+ if (!el) {
+ } else {
+ el.appendChild(sel);
+ pager.appendChild(el);
+ }
+
+ // Show the pager now
+ Event.addListener(sel, "change", carousel._pagerChangeHandler, this, true);
+ Dom.setStyle(pager, "visibility", "visible");
+ },
+
+ /**
+ * Set the correct tab index for the Carousel items.
+ *
+ * @method _updateTabIndex
+ * @param el {Object} The element to be focussed
+ * @protected
+ */
+ _updateTabIndex: function (el) {
+ var carousel = this;
+
+ if (el) {
+ if (carousel._focusableItemEl) {
+ carousel._focusableItemEl.tabIndex = -1;
+ }
+ carousel._focusableItemEl = el;
+ el.tabIndex = 0;
+ }
+ },
+
+ /**
+ * Validate animation parameters.
+ *
+ * @method _validateAnimation
+ * @param cfg {Object} The animation configuration
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateAnimation: function (cfg) {
+ var rv = true;
+
+ if (JS.isObject(cfg)) {
+ if (cfg.speed) {
+ rv = rv && JS.isNumber(cfg.speed);
+ }
+ if (cfg.effect) {
+ rv = rv && JS.isFunction(cfg.effect);
+ } else if (!JS.isUndefined(YAHOO.util.Easing)) {
+ cfg.effect = YAHOO.util.Easing.easeOut;
+ }
+ } else {
+ rv = false;
+ }
+
+ return rv;
+ },
+
+ /**
+ * Validate the firstVisible value.
+ *
+ * @method _validateFirstVisible
+ * @param val {Number} The first visible value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateFirstVisible: function (val) {
+ var carousel = this, numItems = carousel.get("numItems");
+
+ if (JS.isNumber(val)) {
+ if (numItems === 0 && val == numItems) {
+ return true;
+ } else {
+ return (val >= 0 && val < numItems);
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Validate and navigation parameters.
+ *
+ * @method _validateNavigation
+ * @param cfg {Object} The navigation configuration
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateNavigation : function (cfg) {
+ var i;
+
+ if (!JS.isObject(cfg)) {
+ return false;
+ }
+
+ if (cfg.prev) {
+ if (!JS.isArray(cfg.prev)) {
+ return false;
+ }
+ for (i in cfg.prev) {
+ if (cfg.prev.hasOwnProperty(i)) {
+ if (!JS.isString(cfg.prev[i].nodeName)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ if (cfg.next) {
+ if (!JS.isArray(cfg.next)) {
+ return false;
+ }
+ for (i in cfg.next) {
+ if (cfg.next.hasOwnProperty(i)) {
+ if (!JS.isString(cfg.next[i].nodeName)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Validate the numItems value.
+ *
+ * @method _validateNumItems
+ * @param val {Number} The numItems value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateNumItems: function (val) {
+ return JS.isNumber(val) && (val >= 0);
+ },
+
+ /**
+ * Validate the numVisible value.
+ *
+ * @method _validateNumVisible
+ * @param val {Number} The numVisible value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateNumVisible: function (val) {
+ var rv = false;
+
+ if (JS.isNumber(val)) {
+ rv = val > 0 && val <= this.get("numItems");
+ } else if (JS.isArray(val)) {
+ if (JS.isNumber(val[0]) && JS.isNumber(val[1])) {
+ rv = val[0] * val[1] > 0 && val.length == 2;
+ }
+ }
+
+ return rv;
+ },
+
+ /**
+ * Validate the revealAmount value.
+ *
+ * @method _validateRevealAmount
+ * @param val {Number} The revealAmount value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateRevealAmount: function (val) {
+ var rv = false;
+
+ if (JS.isNumber(val)) {
+ rv = val >= 0 && val < 100;
+ }
+
+ return rv;
+ },
+
+ /**
+ * Validate the scrollIncrement value.
+ *
+ * @method _validateScrollIncrement
+ * @param val {Number} The scrollIncrement value
+ * @return {Boolean} The status of the validation
+ * @protected
+ */
+ _validateScrollIncrement: function (val) {
+ var rv = false;
+
+ if (JS.isNumber(val)) {
+ rv = (val > 0 && val < this.get("numItems"));
+ }
+
+ return rv;
+ }
+
+ });
+
+})();
+/*
+;; Local variables: **
+;; mode: js2 **
+;; indent-tabs-mode: nil **
+;; End: **
+*/
+YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.8.1", build: "19"});
diff --git a/lib/yui/2.8.1/build/charts/assets/charts.swf b/lib/yui/2.8.1/build/charts/assets/charts.swf
new file mode 100644
index 00000000000..e8c1e53318b
Binary files /dev/null and b/lib/yui/2.8.1/build/charts/assets/charts.swf differ
diff --git a/lib/yui/2.8.1/build/charts/charts-debug.js b/lib/yui/2.8.1/build/charts/charts-debug.js
new file mode 100644
index 00000000000..a7587e4aa54
--- /dev/null
+++ b/lib/yui/2.8.1/build/charts/charts-debug.js
@@ -0,0 +1,2061 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
+/**
+ * The Charts widget provides a Flash control for displaying data
+ * graphically by series across A-grade browsers with Flash Player installed.
+ *
+ * @module charts
+ * @requires yahoo, dom, event, datasource
+ * @title Charts Widget
+ * @experimental
+ */
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * Chart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class Chart
+ * @uses YAHOO.util.AttributeProvider
+ * @constructor
+ * @param type {String} The char type. May be "line", "column", "bar", or "pie"
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.Chart = function(type, containerId, dataSource, configurationAttributes)
+{
+ this._type = type;
+ this._dataSource = dataSource;
+
+ var possibleParams = {align:"", allowNetworking:"", allowScriptAccess:"", base:"", bgcolor:"", menu:"", name:"", quality:"", salign:"", scale:"", tabindex:"", wmode:""};
+ var attributes = {fixedAttributes:{allowScriptAccess:"always"}, flashVars:{allowedDomain : document.location.hostname}, backgroundColor:"#ffffff", host:this, version:9.045};
+
+ for(var i in configurationAttributes)
+ {
+ if(possibleParams.hasOwnProperty(i))
+ {
+ attributes.fixedAttributes[i] = configurationAttributes[i];
+ }
+ else
+ {
+ attributes[i] = configurationAttributes[i];
+ }
+ }
+
+ this._id = attributes.id = attributes.id || YAHOO.util.Dom.generateId(null, "yuigen");
+
+ if(attributes.version && attributes.version != null && attributes.version != undefined && attributes.version != "undefined")
+ {
+ var version = (/\w*.\w*/.exec(((attributes.version).toString()).replace(/.0./g, "."))).toString();
+ var verSplit = version.split(".");
+ version = verSplit[0] + ".";
+ switch((verSplit[1].toString()).length)
+ {
+ case 1:
+ version += "00";
+ break;
+ case 2:
+ version += "0";
+ break;
+ }
+ version += verSplit[1];
+ attributes.version = parseFloat(version);
+ }
+
+ this._swfURL = YAHOO.widget.Chart.SWFURL;
+ this._containerID = containerId;
+
+ this._attributes = attributes
+ this._swfEmbed = new YAHOO.widget.SWF(containerId, YAHOO.widget.Chart.SWFURL, attributes);
+
+ this._swf = this._swfEmbed.swf;
+ this._swfEmbed.subscribe("swfReady", this._eventHandler, this, true);
+
+ /**
+ * Fires when the SWF is initialized and communication is possible.
+ * @event contentReady
+ */
+ //Fix for iframe cross-domain issue with FF2x
+ try
+ {
+ this.createEvent("contentReady");
+ }
+ catch(e){}
+
+ /**
+ * Fires when the user moves the mouse over the bounds of an item renderer in the chart.
+ *
+ * @event itemMouseOverEvent
+ * @param event.type {String} The event type
+ * @param event.item {Object} The data displayed by the renderer
+ * @param event.index {Number} The position within the series that the item appears.
+ * @param event.seriesIndex {Number} The position within the series definition that the series appears.
+ * @param event.x {Number} The horizontal position of the mouse, relative to the SWF.
+ * @param event.y {Number} The vertical position of the mouse, relative to the SWF.
+ */
+ this.createEvent("itemMouseOverEvent");
+
+ /**
+ * Fires when the user moves the mouse out of the bounds of an item renderer in the chart.
+ *
+ * @event itemMouseOutEvent
+ * @param event.type {String} The event type
+ * @param event.item {Object} The data displayed by the renderer
+ * @param event.index {Number} The position within the series that the item appears.
+ * @param event.seriesIndex {Number} The position within the series definition that the series appears.
+ * @param event.x {Number} The horizontal position of the mouse, relative to the SWF.
+ * @param event.y {Number} The vertical position of the mouse, relative to the SWF.
+ */
+ this.createEvent("itemMouseOutEvent");
+
+ /**
+ * Fires when the user clicks an item renderer in the chart with the mouse.
+ *
+ * @event itemClickEvent
+ * @param event.type {String} The event type
+ * @param event.item {Object} The data displayed by the renderer
+ * @param event.index {Number} The position within the series that the item appears.
+ * @param event.seriesIndex {Number} The position within the series definition that the series appears.
+ * @param event.x {Number} The horizontal position of the mouse, relative to the SWF.
+ * @param event.y {Number} The vertical position of the mouse, relative to the SWF.
+ */
+ this.createEvent("itemClickEvent");
+
+ /**
+ * Fires when the user double-clicks an item renderer in the chart with the mouse.
+ *
+ * @event itemDoubleClickEvent
+ * @param event.type {String} The event type
+ * @param event.item {Object} The data displayed by the renderer
+ * @param event.index {Number} The position within the series that the item appears.
+ * @param event.seriesIndex {Number} The position within the series definition that the series appears.
+ * @param event.x {Number} The horizontal position of the mouse, relative to the SWF.
+ * @param event.y {Number} The vertical position of the mouse, relative to the SWF.
+ */
+ this.createEvent("itemDoubleClickEvent");
+
+ /**
+ * Fires when the user presses the mouse down on an item to initiate a drag action.
+ *
+ * @event itemDragStartEvent
+ * @param event.type {String} The event type
+ * @param event.item {Object} The data displayed by the renderer
+ * @param event.index {Number} The position within the series that the item appears.
+ * @param event.seriesIndex {Number} The position within the series definition that the series appears.
+ * @param event.x {Number} The horizontal position of the mouse, relative to the SWF.
+ * @param event.y {Number} The vertical position of the mouse, relative to the SWF.
+ */
+ this.createEvent("itemDragStartEvent");
+
+ /**
+ * Fires when the user moves the mouse during a drag action.
+ *
+ * @event itemDragEvent
+ * @param event.type {String} The event type
+ * @param event.item {Object} The data displayed by the renderer
+ * @param event.index {Number} The position within the series that the item appears.
+ * @param event.seriesIndex {Number} The position within the series definition that the series appears.
+ * @param event.x {Number} The horizontal position of the mouse, relative to the SWF.
+ * @param event.y {Number} The vertical position of the mouse, relative to the SWF.
+ */
+ this.createEvent("itemDragEvent");
+
+ /**
+ * Fires when the user releases the mouse during a drag action.
+ *
+ * @event itemDragEndEvent
+ * @param event.type {String} The event type
+ * @param event.item {Object} The data displayed by the renderer
+ * @param event.index {Number} The position within the series that the item appears.
+ * @param event.seriesIndex {Number} The position within the series definition that the series appears.
+ * @param event.x {Number} The horizontal position of the mouse, relative to the SWF.
+ * @param event.y {Number} The vertical position of the mouse, relative to the SWF.
+ */
+ this.createEvent("itemDragEndEvent");
+};
+
+YAHOO.extend(YAHOO.widget.Chart, YAHOO.util.AttributeProvider,
+{
+ /**
+ * The type of this chart instance.
+ * @property _type
+ * @type String
+ * @private
+ */
+ _type: null,
+
+ /**
+ * The id returned from the DataSource's setInterval function.
+ * @property _pollingID
+ * @type Number
+ * @private
+ */
+ _pollingID: null,
+
+ /**
+ * The time, in ms, between requests for data.
+ * @property _pollingInterval
+ * @type Number
+ * @private
+ */
+ _pollingInterval: null,
+
+ /**
+ * Stores a reference to the dataTipFunction created by
+ * YAHOO.widget.Chart.createProxyFunction()
+ * @property _dataTipFunction
+ * @type String
+ * @private
+ */
+ _dataTipFunction: null,
+
+ /**
+ * Stores a reference to the legendLabelFunction created by
+ * YAHOO.widget.Chart.createProxyFunction()
+ * @property _legendLabelFunction
+ * @type String
+ * @private
+ */
+ _legendLabelFunction: null,
+
+ /**
+ * Stores references to series function values created by
+ * YAHOO.widget.Chart.createProxyFunction()
+ * @property _seriesFunctions
+ * @type Array
+ * @private
+ */
+ _seriesFunctions: null,
+
+ /**
+ * Public accessor to the unique name of the Chart instance.
+ *
+ * @method toString
+ * @return {String} Unique name of the Chart instance.
+ */
+ toString: function()
+ {
+ return "Chart " + this._id;
+ },
+
+ /**
+ * Sets a single style value on the Chart instance.
+ *
+ * @method setStyle
+ * @param name {String} Name of the Chart style value to change.
+ * @param value {Object} New value to pass to the Chart style.
+ */
+ setStyle: function(name, value)
+ {
+ //we must jsonify this because Flash Player versions below 9.0.60 don't handle
+ //complex ExternalInterface parsing correctly
+ value = YAHOO.lang.JSON.stringify(value);
+ this._swf.setStyle(name, value);
+ },
+
+ /**
+ * Resets all styles on the Chart instance.
+ *
+ * @method setStyles
+ * @param styles {Object} Initializer for all Chart styles.
+ */
+ setStyles: function(styles)
+ {
+ //we must jsonify this because Flash Player versions below 9.0.60 don't handle
+ //complex ExternalInterface parsing correctly
+ styles = YAHOO.lang.JSON.stringify(styles);
+ this._swf.setStyles(styles);
+ },
+
+ /**
+ * Sets the styles on all series in the Chart.
+ *
+ * @method setSeriesStyles
+ * @param styles {Array} Initializer for all Chart series styles.
+ */
+ setSeriesStyles: function(styles)
+ {
+ //we must jsonify this because Flash Player versions below 9.0.60 don't handle
+ //complex ExternalInterface parsing correctly
+ for(var i = 0; i < styles.length; i++)
+ {
+ styles[i] = YAHOO.lang.JSON.stringify(styles[i]);
+ }
+ this._swf.setSeriesStyles(styles);
+ },
+
+ destroy: function()
+ {
+ //stop polling if needed
+ if(this._dataSource !== null)
+ {
+ if(this._pollingID !== null)
+ {
+ this._dataSource.clearInterval(this._pollingID);
+ this._pollingID = null;
+ }
+ }
+
+ //remove proxy functions
+ if(this._dataTipFunction)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(this._dataTipFunction);
+ }
+
+ if(this._legendLabelFunction)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(this._legendLabelFunction);
+ }
+
+ //kill the Flash Player instance
+ if(this._swf)
+ {
+ var container = YAHOO.util.Dom.get(this._containerID);
+ container.removeChild(this._swf);
+ }
+
+ var instanceName = this._id;
+
+ //null out properties
+ for(var prop in this)
+ {
+ if(YAHOO.lang.hasOwnProperty(this, prop))
+ {
+ this[prop] = null;
+ }
+ }
+ },
+
+ /**
+ * Initializes the attributes.
+ *
+ * @method _initAttributes
+ * @private
+ */
+ _initAttributes: function(attributes)
+ {
+ //YAHOO.widget.Chart.superclass._initAttributes.call(this, attributes);
+ /**
+ * @attribute wmode
+ * @description Sets the window mode of the Flash Player control. May be
+ * "window", "opaque", or "transparent". Only available in the constructor
+ * because it may not be set after Flash Player has been embedded in the page.
+ * @type String
+ */
+
+ /**
+ * @attribute expressInstall
+ * @description URL pointing to a SWF file that handles Flash Player's express
+ * install feature. Only available in the constructor because it may not be
+ * set after Flash Player has been embedded in the page.
+ * @type String
+ */
+
+ /**
+ * @attribute version
+ * @description Minimum required version for the SWF file. Only available in the constructor because it may not be
+ * set after Flash Player has been embedded in the page.
+ * @type String
+ */
+
+ /**
+ * @attribute backgroundColor
+ * @description The background color of the SWF. Only available in the constructor because it may not be
+ * set after Flash Player has been embedded in the page.
+ * @type String
+ */
+
+ /**
+ * @attribute altText
+ * @description The alternative text to provide for screen readers and other assistive technology.
+ * @type String
+ */
+ this.setAttributeConfig("altText",
+ {
+ method: this._setAltText,
+ getter: this._getAltText
+
+ });
+
+ /**
+ * @attribute swfURL
+ * @description Absolute or relative URL to the SWF displayed by the Chart. Only available in the constructor because it may not be
+ * set after Flash Player has been embedded in the page.
+ * @type String
+ */
+ this.setAttributeConfig("swfURL",
+ {
+ getter: this._getSWFURL
+ });
+
+ /**
+ * @attribute request
+ * @description Request to be sent to the Chart's DataSource.
+ * @type String
+ */
+ this.setAttributeConfig("request",
+ {
+ method: this._setRequest,
+ getter: this._getRequest
+ });
+
+ /**
+ * @attribute dataSource
+ * @description The DataSource instance to display in the Chart.
+ * @type DataSource
+ */
+ this.setAttributeConfig("dataSource",
+ {
+ method: this._setDataSource,
+ getter: this._getDataSource
+ });
+
+ /**
+ * @attribute series
+ * @description Defines the series to be displayed by the Chart.
+ * @type Array
+ */
+ this.setAttributeConfig("series",
+ {
+ method: this._setSeriesDefs,
+ getter: this._getSeriesDefs
+ });
+
+ /**
+ * @attribute categoryNames
+ * @description Defines the names of the categories to be displayed in the Chart..
+ * @type Array
+ */
+ this.setAttributeConfig("categoryNames",
+ {
+ validator: YAHOO.lang.isArray,
+ method: this._setCategoryNames,
+ getter: this._getCategoryNames
+ });
+
+ /**
+ * @attribute dataTipFunction
+ * @description The string representation of a globally-accessible function
+ * that may be called by the SWF to generate the datatip text for a Chart's item.
+ * @type String
+ */
+ this.setAttributeConfig("dataTipFunction",
+ {
+ method: this._setDataTipFunction,
+ getter: this._getDataTipFunction
+ });
+
+ /**
+ * @attribute legendLabelFunction
+ * @description The string representation of a globally-accessible function
+ * that may be called by the SWF to format the labels of a Chart's legend.
+ * @type String
+ */
+ this.setAttributeConfig("legendLabelFunction",
+ {
+ method: this._setLegendLabelFunction,
+ getter: this._legendLabelFunction
+ });
+
+ /**
+ * @attribute polling
+ * @description A numeric value indicating the number of milliseconds between
+ * polling requests to the DataSource.
+ * @type Number
+ */
+ this.setAttributeConfig("polling",
+ {
+ method: this._setPolling,
+ getter: this._getPolling
+ });
+ },
+
+ /**
+ * Handles swfReady event from SWF.
+ *
+ * @method _eventHandler
+ * @private
+ */
+ _eventHandler: function(event)
+ {
+ if(event.type == "swfReady")
+ {
+ this._swf = this._swfEmbed._swf;
+ this._loadHandler();
+ this.fireEvent("contentReady");
+ }
+ },
+
+ /**
+ * Called when the SWF is ready for communication. Sets the type, initializes
+ * the styles, and sets the DataSource.
+ *
+ * @method _loadHandler
+ * @private
+ */
+ _loadHandler: function()
+ {
+ //the type is set separately because it must be first!
+ if(!this._swf || !this._swf.setType) return;
+ this._swf.setType(this._type);
+
+
+ //set initial styles
+ if(this._attributes.style)
+ {
+ var style = this._attributes.style;
+ this.setStyles(style);
+ }
+
+ this._initialized = false;
+
+ this._initAttributes(this._attributes);
+ this.setAttributes(this._attributes, true);
+
+ this._initialized = true;
+ if(this._dataSource)
+ {
+ this.set("dataSource", this._dataSource);
+ }
+ },
+
+ /**
+ * Sends (or resends) the request to the DataSource.
+ *
+ * @method refreshData
+ */
+ refreshData: function()
+ {
+ if(!this._initialized)
+ {
+ return;
+ }
+
+ if(this._dataSource !== null)
+ {
+ if(this._pollingID !== null)
+ {
+ this._dataSource.clearInterval(this._pollingID);
+ this._pollingID = null;
+ }
+
+ if(this._pollingInterval > 0)
+ {
+ this._pollingID = this._dataSource.setInterval(this._pollingInterval, this._request, this._loadDataHandler, this);
+ }
+ this._dataSource.sendRequest(this._request, this._loadDataHandler, this);
+ }
+ },
+
+ /**
+ * Called when the DataSource receives new data. The series definitions are used
+ * to build a data provider for the SWF chart.
+ *
+ * @method _loadDataHandler
+ * @private
+ */
+ _loadDataHandler: function(request, response, error)
+ {
+ if(this._swf)
+ {
+ if(error)
+ {
+ YAHOO.log("Unable to load data.", "error");
+ }
+ else
+ {
+ var i;
+ if(this._seriesFunctions)
+ {
+ var count = this._seriesFunctions.length;
+ for(i = 0; i < count; i++)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(this._seriesFunctions[i]);
+ }
+ this._seriesFunctions = null;
+ }
+ this._seriesFunctions = [];
+
+ //make a copy of the series definitions so that we aren't
+ //editing them directly.
+ var dataProvider = [];
+ var seriesCount = 0;
+ var currentSeries = null;
+ if(this._seriesDefs !== null)
+ {
+ seriesCount = this._seriesDefs.length;
+ for(i = 0; i < seriesCount; i++)
+ {
+ currentSeries = this._seriesDefs[i];
+ var clonedSeries = {};
+ for(var prop in currentSeries)
+ {
+ if(YAHOO.lang.hasOwnProperty(currentSeries, prop))
+ {
+ if(prop == "style")
+ {
+ if(currentSeries.style !== null)
+ {
+ clonedSeries.style = YAHOO.lang.JSON.stringify(currentSeries.style);
+ }
+ }
+
+ else if(prop == "labelFunction")
+ {
+ if(currentSeries.labelFunction !== null)
+ {
+ clonedSeries.labelFunction = YAHOO.widget.Chart.getFunctionReference(currentSeries.labelFunction);
+ this._seriesFunctions.push(clonedSeries.labelFunction);
+ }
+ }
+
+ else if(prop == "dataTipFunction")
+ {
+ if(currentSeries.dataTipFunction !== null)
+ {
+ clonedSeries.dataTipFunction = YAHOO.widget.Chart.getFunctionReference(currentSeries.dataTipFunction);
+ this._seriesFunctions.push(clonedSeries.dataTipFunction);
+ }
+ }
+
+ else if(prop == "legendLabelFunction")
+ {
+ if(currentSeries.legendLabelFunction !== null)
+ {
+ clonedSeries.legendLabelFunction = YAHOO.widget.Chart.getFunctionReference(currentSeries.legendLabelFunction);
+ this._seriesFunctions.push(clonedSeries.legendLabelFunction);
+ }
+ }
+
+ else
+ {
+ clonedSeries[prop] = currentSeries[prop];
+ }
+ }
+ }
+ dataProvider.push(clonedSeries);
+ }
+ }
+
+ if(seriesCount > 0)
+ {
+ for(i = 0; i < seriesCount; i++)
+ {
+ currentSeries = dataProvider[i];
+ if(!currentSeries.type)
+ {
+ currentSeries.type = this._type;
+ }
+ currentSeries.dataProvider = response.results;
+ }
+ }
+ else
+ {
+ var series = {type: this._type, dataProvider: response.results};
+ dataProvider.push(series);
+ }
+ try
+ {
+ if(this._swf.setDataProvider) this._swf.setDataProvider(dataProvider);
+ }
+ catch(e)
+ {
+ this._swf.setDataProvider(dataProvider);
+ }
+ }
+ }
+ },
+
+ /**
+ * Storage for the request attribute.
+ *
+ * @property _request
+ * @private
+ */
+ _request: "",
+
+ /**
+ * Getter for the request attribute.
+ *
+ * @method _getRequest
+ * @private
+ */
+ _getRequest: function()
+ {
+ return this._request;
+ },
+
+ /**
+ * Setter for the request attribute.
+ *
+ * @method _setRequest
+ * @private
+ */
+ _setRequest: function(value)
+ {
+ this._request = value;
+ this.refreshData();
+ },
+
+ /**
+ * Storage for the dataSource attribute.
+ *
+ * @property _dataSource
+ * @private
+ */
+ _dataSource: null,
+
+ /**
+ * Getter for the dataSource attribute.
+ *
+ * @method _getDataSource
+ * @private
+ */
+ _getDataSource: function()
+ {
+ return this._dataSource;
+ },
+
+ /**
+ * Setter for the dataSource attribute.
+ *
+ * @method _setDataSource
+ * @private
+ */
+ _setDataSource: function(value)
+ {
+ this._dataSource = value;
+ this.refreshData();
+ },
+
+ /**
+ * Storage for the series attribute.
+ *
+ * @property _seriesDefs
+ * @private
+ */
+ _seriesDefs: null,
+
+ /**
+ * Getter for the series attribute.
+ *
+ * @method _getSeriesDefs
+ * @private
+ */
+ _getSeriesDefs: function()
+ {
+ return this._seriesDefs;
+ },
+
+ /**
+ * Setter for the series attribute.
+ *
+ * @method _setSeriesDefs
+ * @private
+ */
+ _setSeriesDefs: function(value)
+ {
+ this._seriesDefs = value;
+ this.refreshData();
+ },
+
+ /**
+ * Getter for the categoryNames attribute.
+ *
+ * @method _getCategoryNames
+ * @private
+ */
+ _getCategoryNames: function()
+ {
+ return this._swf.getCategoryNames();
+ },
+
+ /**
+ * Setter for the categoryNames attribute.
+ *
+ * @method _setCategoryNames
+ * @private
+ */
+ _setCategoryNames: function(value)
+ {
+ this._swf.setCategoryNames(value);
+ },
+
+ /**
+ * Setter for the dataTipFunction attribute.
+ *
+ * @method _setDataTipFunction
+ * @private
+ */
+ _setDataTipFunction: function(value)
+ {
+ if(this._dataTipFunction)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(this._dataTipFunction);
+ }
+
+ if(value)
+ {
+ this._dataTipFunction = value = YAHOO.widget.Chart.getFunctionReference(value);
+ }
+ this._swf.setDataTipFunction(value);
+ },
+
+ /**
+ * Setter for the legendLabelFunction attribute.
+ *
+ * @method _setLegendLabelFunction
+ * @private
+ */
+ _setLegendLabelFunction: function(value)
+ {
+ if(this._legendLabelFunction)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(this._legendLabelFunction);
+ }
+
+ if(value)
+ {
+ this._legendLabelFunction = value = YAHOO.widget.Chart.getFunctionReference(value);
+ }
+ this._swf.setLegendLabelFunction(value);
+ },
+
+ /**
+ * Getter for the polling attribute.
+ *
+ * @method _getPolling
+ * @private
+ */
+ _getPolling: function()
+ {
+ return this._pollingInterval;
+ },
+
+ /**
+ * Setter for the polling attribute.
+ *
+ * @method _setPolling
+ * @private
+ */
+ _setPolling: function(value)
+ {
+ this._pollingInterval = value;
+ this.refreshData();
+ },
+
+ /**
+ * The javascript wrapper for the swf object
+ *
+ * @property _swfEmbed
+ * @type swf
+ * @private
+ */
+ _swfEmbed: null,
+
+ /**
+ * The URL of the SWF file.
+ * @property _swfURL
+ * @type String
+ * @private
+ */
+ _swfURL: null,
+
+ /**
+ * The ID of the containing DIV.
+ * @property _containerID
+ * @type String
+ * @private
+ */
+ _containerID: null,
+
+ /**
+ * A reference to the embedded SWF file.
+ * @property _swf
+ * @private
+ */
+ _swf: null,
+
+ /**
+ * The id of this instance.
+ * @property _id
+ * @type String
+ * @private
+ */
+ _id: null,
+
+ /**
+ * Indicates whether the SWF has been initialized and is ready
+ * to communicate with JavaScript
+ * @property _initialized
+ * @type Boolean
+ * @private
+ */
+ _initialized: false,
+
+ /**
+ * The initializing attributes are stored here until the SWF is ready.
+ * @property _attributes
+ * @type Object
+ * @private
+ */
+ _attributes: null, //the intializing attributes
+
+ set: function(name, value)
+ {
+ //save all the attributes in case the swf reloads
+ //so that we can pass them in again
+ this._attributes[name] = value;
+ YAHOO.widget.Chart.superclass.set.call(this, name, value);
+ },
+
+ /**
+ * Getter for swfURL attribute.
+ *
+ * @method _getSWFURL
+ * @private
+ */
+ _getSWFURL: function()
+ {
+ return this._swfURL;
+ },
+
+ /**
+ * Getter for altText attribute.
+ *
+ * @method _getAltText
+ * @private
+ */
+ _getAltText: function()
+ {
+ return this._swf.getAltText();
+ },
+
+ /**
+ * Setter for altText attribute.
+ *
+ * @method _setAltText
+ * @private
+ */
+ _setAltText: function(value)
+ {
+ this._swf.setAltText(value);
+ }
+});
+
+/**
+ * The number of proxy functions that have been created.
+ * @static
+ * @private
+ */
+YAHOO.widget.Chart.proxyFunctionCount = 0;
+
+/**
+ * Creates a globally accessible function that wraps a function reference.
+ * Returns the proxy function's name as a string for use by the SWF through
+ * ExternalInterface.
+ *
+ * @method YAHOO.widget.Chart.createProxyFunction
+ * @static
+ * @private
+ */
+YAHOO.widget.Chart.createProxyFunction = function(func, scope)
+{
+ var scope = scope || null;
+ var index = YAHOO.widget.Chart.proxyFunctionCount;
+ YAHOO.widget.Chart["proxyFunction" + index] = function()
+ {
+ return func.apply(scope, arguments);
+ };
+ YAHOO.widget.Chart.proxyFunctionCount++;
+ return "YAHOO.widget.Chart.proxyFunction" + index.toString();
+};
+
+/**
+ * Uses YAHOO.widget.Chart.createProxyFunction to return string
+ * reference to a function.
+ *
+ * @method YAHOO.widget.Chart.getFunctionReference
+ * @static
+ * @private
+ */
+YAHOO.widget.Chart.getFunctionReference = function(value)
+{
+ if(typeof value == "function")
+ {
+ value = YAHOO.widget.Chart.createProxyFunction(value);
+ }
+ else if(value.func && typeof value.func == "function")
+ {
+ var args = [value.func];
+ if(value.scope && typeof value.scope == "object")
+ {
+ args.push(value.scope);
+ }
+ value = YAHOO.widget.Chart.createProxyFunction.apply(this, args);
+ }
+ return value;
+}
+
+/**
+ * Removes a function created with createProxyFunction()
+ *
+ * @method YAHOO.widget.Chart.removeProxyFunction
+ * @static
+ * @private
+ */
+YAHOO.widget.Chart.removeProxyFunction = function(funcName)
+{
+ //quick error check
+ if(!funcName || funcName.indexOf("YAHOO.widget.Chart.proxyFunction") < 0)
+ {
+ return;
+ }
+
+ funcName = funcName.substr(26);
+ YAHOO.widget.Chart[funcName] = null;
+};
+
+/**
+ * Storage for the dataTipFunction attribute.
+ *
+ * @property Chart.SWFURL
+ * @private
+ * @static
+ * @final
+ * @default "assets/charts.swf"
+ */
+YAHOO.widget.Chart.SWFURL = "assets/charts.swf";
+/**
+ * PieChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class PieChart
+ * @uses YAHOO.widget.Chart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.PieChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.PieChart.superclass.constructor.call(this, "pie", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.PieChart, YAHOO.widget.Chart,
+{
+ /**
+ * Initializes the attributes.
+ *
+ * @method _initAttributes
+ * @private
+ */
+ _initAttributes: function(attributes)
+ {
+ YAHOO.widget.PieChart.superclass._initAttributes.call(this, attributes);
+
+ /**
+ * @attribute dataField
+ * @description The field in each item that corresponds to the data value.
+ * @type String
+ */
+ this.setAttributeConfig("dataField",
+ {
+ validator: YAHOO.lang.isString,
+ method: this._setDataField,
+ getter: this._getDataField
+ });
+
+ /**
+ * @attribute categoryField
+ * @description The field in each item that corresponds to the category value.
+ * @type String
+ */
+ this.setAttributeConfig("categoryField",
+ {
+ validator: YAHOO.lang.isString,
+ method: this._setCategoryField,
+ getter: this._getCategoryField
+ });
+ },
+
+ /**
+ * Getter for the dataField attribute.
+ *
+ * @method _getDataField
+ * @private
+ */
+ _getDataField: function()
+ {
+ return this._swf.getDataField();
+ },
+
+ /**
+ * Setter for the dataField attribute.
+ *
+ * @method _setDataField
+ * @private
+ */
+ _setDataField: function(value)
+ {
+ this._swf.setDataField(value);
+ },
+
+ /**
+ * Getter for the categoryField attribute.
+ *
+ * @method _getCategoryField
+ * @private
+ */
+ _getCategoryField: function()
+ {
+ return this._swf.getCategoryField();
+ },
+
+ /**
+ * Setter for the categoryField attribute.
+ *
+ * @method _setCategoryField
+ * @private
+ */
+ _setCategoryField: function(value)
+ {
+ this._swf.setCategoryField(value);
+ }
+});
+/**
+ * CartesianChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class CartesianChart
+ * @uses YAHOO.widget.Chart
+ * @constructor
+ * @param type {String} The char type. May be "line", "column", or "bar"
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+ YAHOO.widget.CartesianChart = function(type, containerId, dataSource, attributes)
+{
+ YAHOO.widget.CartesianChart.superclass.constructor.call(this, type, containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.CartesianChart, YAHOO.widget.Chart,
+{
+ /**
+ * Stores a reference to the xAxis labelFunction created by
+ * YAHOO.widget.Chart.createProxyFunction()
+ * @property _xAxisLabelFunctions
+ * @type String
+ * @private
+ */
+ _xAxisLabelFunctions: [],
+
+ /**
+ * Stores a reference to the yAxis labelFunctions created by
+ * YAHOO.widget.Chart.createProxyFunction()
+ * @property _yAxisLabelFunctions
+ * @type Array
+ * @private
+ */
+ _yAxisLabelFunctions: [],
+
+ destroy: function()
+ {
+ //remove proxy functions
+ this._removeAxisFunctions(this._xAxisLabelFunctions);
+ this._removeAxisFunctions(this._yAxisLabelFunctions);
+
+ //call last
+ YAHOO.widget.CartesianChart.superclass.destroy.call(this);
+ },
+
+ /**
+ * Initializes the attributes.
+ *
+ * @method _initAttributes
+ * @private
+ */
+ _initAttributes: function(attributes)
+ {
+ YAHOO.widget.CartesianChart.superclass._initAttributes.call(this, attributes);
+
+ /**
+ * @attribute xField
+ * @description The field in each item that corresponds to a value on the x axis.
+ * @type String
+ */
+ this.setAttributeConfig("xField",
+ {
+ validator: YAHOO.lang.isString,
+ method: this._setXField,
+ getter: this._getXField
+ });
+
+ /**
+ * @attribute yField
+ * @description The field in each item that corresponds to a value on the x axis.
+ * @type String
+ */
+ this.setAttributeConfig("yField",
+ {
+ validator: YAHOO.lang.isString,
+ method: this._setYField,
+ getter: this._getYField
+ });
+
+ /**
+ * @attribute xAxis
+ * @description A custom configuration for the horizontal x axis.
+ * @type Axis
+ */
+ this.setAttributeConfig("xAxis",
+ {
+ method: this._setXAxis
+ });
+
+ /**
+ * @attribute xAxes
+ * @description Custom configurations for the horizontal x axes.
+ * @type Array
+ */
+ this.setAttributeConfig("xAxes",
+ {
+ method: this._setXAxes
+ });
+
+ /**
+ * @attribute yAxis
+ * @description A custom configuration for the vertical y axis.
+ * @type Axis
+ */
+ this.setAttributeConfig("yAxis",
+ {
+ method: this._setYAxis
+ });
+
+ /**
+ * @attribute yAxes
+ * @description Custom configurations for the vertical y axes.
+ * @type Array
+ */
+ this.setAttributeConfig("yAxes",
+ {
+ method: this._setYAxes
+ });
+
+ /**
+ * @attribute constrainViewport
+ * @description Determines whether the viewport is constrained to prevent series data from overflow.
+ * @type Boolean
+ */
+ this.setAttributeConfig("constrainViewport",
+ {
+ method: this._setConstrainViewport
+ });
+ },
+
+ /**
+ * Getter for the xField attribute.
+ *
+ * @method _getXField
+ * @private
+ */
+ _getXField: function()
+ {
+ return this._swf.getHorizontalField();
+ },
+
+ /**
+ * Setter for the xField attribute.
+ *
+ * @method _setXField
+ * @private
+ */
+ _setXField: function(value)
+ {
+ this._swf.setHorizontalField(value);
+ },
+
+ /**
+ * Getter for the yField attribute.
+ *
+ * @method _getYField
+ * @private
+ */
+ _getYField: function()
+ {
+ return this._swf.getVerticalField();
+ },
+
+ /**
+ * Setter for the yField attribute.
+ *
+ * @method _setYField
+ * @private
+ */
+ _setYField: function(value)
+ {
+ this._swf.setVerticalField(value);
+ },
+
+ /**
+ * Receives an axis object, creates a proxy function for
+ * the labelFunction and returns the updated object.
+ *
+ * @method _getClonedAxis
+ * @private
+ */
+ _getClonedAxis: function(value)
+ {
+ var clonedAxis = {};
+ for(var prop in value)
+ {
+ if(prop == "labelFunction")
+ {
+ if(value.labelFunction && value.labelFunction !== null)
+ {
+ clonedAxis.labelFunction = YAHOO.widget.Chart.getFunctionReference(value.labelFunction);
+ }
+ }
+ else
+ {
+ clonedAxis[prop] = value[prop];
+ }
+ }
+ return clonedAxis;
+ },
+
+ /**
+ * Removes axis functions contained in an array
+ *
+ * @method _removeAxisFunctions
+ * @private
+ */
+ _removeAxisFunctions: function(axisFunctions)
+ {
+ if(axisFunctions && axisFunctions.length > 0)
+ {
+ var len = axisFunctions.length;
+ for(var i = 0; i < len; i++)
+ {
+ if(axisFunctions[i] !== null)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(axisFunctions[i]);
+ }
+ }
+ axisFunctions = [];
+ }
+ },
+
+ /**
+ * Setter for the xAxis attribute.
+ *
+ * @method _setXAxis
+ * @private
+ */
+ _setXAxis: function(value)
+ {
+ if(value.position != "bottom" && value.position != "top") value.position = "bottom";
+ this._removeAxisFunctions(this._xAxisLabelFunctions);
+ value = this._getClonedAxis(value);
+ this._xAxisLabelFunctions.push(value.labelFunction);
+ this._swf.setHorizontalAxis(value);
+ },
+
+ /**
+ * Setter for the xAxes attribute
+ *
+ * @method _setXAxes
+ * @private
+ */
+ _setXAxes: function(value)
+ {
+ this._removeAxisFunctions(this._xAxisLabelFunctions);
+ var len = value.length;
+ for(var i = 0; i < len; i++)
+ {
+ if(value[i].position == "left") value[i].position = "bottom";
+ value[i] = this._getClonedAxis(value[i]);
+ if(value[i].labelFunction) this._xAxisLabelFunctions.push(value[i].labelFunction);
+ this._swf.setHorizontalAxis(value[i]);
+ }
+ },
+
+ /**
+ * Setter for the yAxis attribute.
+ *
+ * @method _setYAxis
+ * @private
+ */
+ _setYAxis: function(value)
+ {
+ this._removeAxisFunctions(this._yAxisLabelFunctions);
+ value = this._getClonedAxis(value);
+ this._yAxisLabelFunctions.push(value.labelFunction);
+ this._swf.setVerticalAxis(value);
+ },
+
+ /**
+ * Setter for the yAxes attribute.
+ *
+ * @method _setYAxes
+ * @private
+ */
+ _setYAxes: function(value)
+ {
+ this._removeAxisFunctions(this._yAxisLabelFunctions);
+ var len = value.length;
+ for(var i = 0; i < len; i++)
+ {
+ value[i] = this._getClonedAxis(value[i]);
+ if(value[i].labelFunction) this._yAxisLabelFunctions.push(value[i].labelFunction);
+ this._swf.setVerticalAxis(value[i]);
+ }
+ },
+
+ /**
+ * Setter for the constrainViewport attribute
+ *
+ * @method _setConstrainViewport
+ * @private
+ */
+ _setConstrainViewport: function(value)
+ {
+ this._swf.setConstrainViewport(value);
+ },
+
+ /**
+ * Sets the style object for a single series based on its index
+ *
+ * @method setSeriesStylesByIndex
+ * @param index {Number} The position within the series definition to apply the style
+ * @param style {object} Style object to be applied to the selected series
+ */
+ setSeriesStylesByIndex:function(index, style)
+ {
+ style = YAHOO.lang.JSON.stringify(style);
+ if(this._swf && this._swf.setSeriesStylesByIndex) this._swf.setSeriesStylesByIndex(index, style);
+ }
+});
+/**
+ * LineChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class LineChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.LineChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.LineChart.superclass.constructor.call(this, "line", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.LineChart, YAHOO.widget.CartesianChart);
+
+/**
+ * ColumnChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class ColumnChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.ColumnChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.ColumnChart.superclass.constructor.call(this, "column", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.ColumnChart, YAHOO.widget.CartesianChart);
+
+/**
+ * BarChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class BarChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.BarChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.BarChart.superclass.constructor.call(this, "bar", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.BarChart, YAHOO.widget.CartesianChart);
+
+/**
+ * StackedColumnChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedColumnChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.StackedColumnChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.StackedColumnChart.superclass.constructor.call(this, "stackcolumn", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedColumnChart, YAHOO.widget.CartesianChart);
+
+/**
+ * StackedBarChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedBarChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.StackedBarChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.StackedBarChart.superclass.constructor.call(this, "stackbar", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedBarChart, YAHOO.widget.CartesianChart);
+/**
+ * Defines a CartesianChart's vertical or horizontal axis.
+ *
+ * @namespace YAHOO.widget
+ * @class Axis
+ * @constructor
+ */
+YAHOO.widget.Axis = function()
+{
+};
+
+YAHOO.widget.Axis.prototype =
+{
+ /**
+ * The type of axis.
+ *
+ * @property type
+ * @type String
+ */
+ type: null,
+
+ /**
+ * If true, the items on the axis will be drawn in opposite direction.
+ *
+ * @property reverse
+ * @type Boolean
+ */
+ reverse: false,
+
+ /**
+ * A string reference to the globally-accessible function that may be called to
+ * determine each of the label values for this axis. Also accepts function references.
+ *
+ * @property labelFunction
+ * @type String
+ */
+ labelFunction: null,
+
+ /**
+ * The space, in pixels, between labels on an axis.
+ *
+ * @property labelSpacing
+ * @type Number
+ */
+ labelSpacing: 2,
+
+ /**
+ * The text that will appear next to the axis to indicate information about the data that it displays.
+ *
+ * @property title
+ * @type String
+ */
+ title: null
+};
+/**
+ * A type of axis whose units are measured in numeric values.
+ *
+ * @namespace YAHOO.widget
+ * @class NumericAxis
+ * @extends YAHOO.widget.Axis
+ * @constructor
+ */
+YAHOO.widget.NumericAxis = function()
+{
+ YAHOO.widget.NumericAxis.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.NumericAxis, YAHOO.widget.Axis,
+{
+ type: "numeric",
+
+ /**
+ * The minimum value drawn by the axis. If not set explicitly, the axis minimum
+ * will be calculated automatically.
+ *
+ * @property minimum
+ * @type Number
+ */
+ minimum: NaN,
+
+ /**
+ * The maximum value drawn by the axis. If not set explicitly, the axis maximum
+ * will be calculated automatically.
+ *
+ * @property maximum
+ * @type Number
+ */
+ maximum: NaN,
+
+ /**
+ * The spacing between major intervals on this axis.
+ *
+ * @property majorUnit
+ * @type Number
+ */
+ majorUnit: NaN,
+
+ /**
+ * The spacing between minor intervals on this axis.
+ *
+ * @property minorUnit
+ * @type Number
+ */
+ minorUnit: NaN,
+
+ /**
+ * If true, the labels, ticks, gridlines, and other objects will snap to
+ * the nearest major or minor unit. If false, their position will be based
+ * on the minimum value.
+ *
+ * @property snapToUnits
+ * @type Boolean
+ */
+ snapToUnits: true,
+
+ /**
+ * Series that are stackable will only stack when this value is set to true.
+ *
+ * @property stackingEnabled
+ * @type Boolean
+ */
+ stackingEnabled: false,
+
+ /**
+ * If true, and the bounds are calculated automatically, either the minimum or
+ * maximum will be set to zero.
+ *
+ * @property alwaysShowZero
+ * @type Boolean
+ */
+ alwaysShowZero: true,
+
+ /**
+ * The scaling algorithm to use on this axis. May be "linear" or "logarithmic".
+ *
+ * @property scale
+ * @type String
+ */
+ scale: "linear",
+
+ /**
+ * Indicates whether to round the major unit.
+ *
+ * @property roundMajorUnit
+ * @type Boolean
+ */
+ roundMajorUnit: true,
+
+ /**
+ * Indicates whether to factor in the size of the labels when calculating a major unit.
+ *
+ * @property calculateByLabelSize
+ * @type Boolean
+ */
+ calculateByLabelSize: true,
+
+ /**
+ * Indicates the position of the axis relative to the chart
+ *
+ * @property position
+ * @type String
+ */
+ position:"left",
+
+ /**
+ * Indicates whether to extend maximum beyond data's maximum to the nearest
+ * majorUnit.
+ *
+ * @property adjustMaximumByMajorUnit
+ * @type Boolean
+ */
+ adjustMaximumByMajorUnit:true,
+
+ /**
+ * Indicates whether to extend the minimum beyond data's minimum to the nearest
+ * majorUnit.
+ *
+ * @property adjustMinimumByMajorUnit
+ * @type Boolean
+ */
+ adjustMinimumByMajorUnit:true
+});
+/**
+ * A type of axis whose units are measured in time-based values.
+ *
+ * @namespace YAHOO.widget
+ * @class TimeAxis
+ * @constructor
+ */
+YAHOO.widget.TimeAxis = function()
+{
+ YAHOO.widget.TimeAxis.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.TimeAxis, YAHOO.widget.Axis,
+{
+ type: "time",
+
+ /**
+ * The minimum value drawn by the axis. If not set explicitly, the axis minimum
+ * will be calculated automatically.
+ *
+ * @property minimum
+ * @type Date
+ */
+ minimum: null,
+
+ /**
+ * The maximum value drawn by the axis. If not set explicitly, the axis maximum
+ * will be calculated automatically.
+ *
+ * @property maximum
+ * @type Number
+ */
+ maximum: null,
+
+ /**
+ * The spacing between major intervals on this axis.
+ *
+ * @property majorUnit
+ * @type Number
+ */
+ majorUnit: NaN,
+
+ /**
+ * The time unit used by the majorUnit.
+ *
+ * @property majorTimeUnit
+ * @type String
+ */
+ majorTimeUnit: null,
+
+ /**
+ * The spacing between minor intervals on this axis.
+ *
+ * @property majorUnit
+ * @type Number
+ */
+ minorUnit: NaN,
+
+ /**
+ * The time unit used by the minorUnit.
+ *
+ * @property majorTimeUnit
+ * @type String
+ */
+ minorTimeUnit: null,
+
+ /**
+ * If true, the labels, ticks, gridlines, and other objects will snap to
+ * the nearest major or minor unit. If false, their position will be based
+ * on the minimum value.
+ *
+ * @property snapToUnits
+ * @type Boolean
+ */
+ snapToUnits: true,
+
+ /**
+ * Series that are stackable will only stack when this value is set to true.
+ *
+ * @property stackingEnabled
+ * @type Boolean
+ */
+ stackingEnabled: false,
+
+ /**
+ * Indicates whether to factor in the size of the labels when calculating a major unit.
+ *
+ * @property calculateByLabelSize
+ * @type Boolean
+ */
+ calculateByLabelSize: true
+});
+/**
+ * A type of axis that displays items in categories.
+ *
+ * @namespace YAHOO.widget
+ * @class CategoryAxis
+ * @constructor
+ */
+YAHOO.widget.CategoryAxis = function()
+{
+ YAHOO.widget.CategoryAxis.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.CategoryAxis, YAHOO.widget.Axis,
+{
+ type: "category",
+
+ /**
+ * A list of category names to display along this axis.
+ *
+ * @property categoryNames
+ * @type Array
+ */
+ categoryNames: null,
+
+ /**
+ * Indicates whether or not to calculate the number of categories (ticks and labels)
+ * when there is not enough room to display all labels on the axis. If set to true, the axis
+ * will determine the number of categories to plot. If not, all categories will be plotted.
+ *
+ * @property calculateCategoryCount
+ * @type Boolean
+ */
+ calculateCategoryCount: false
+});
+ /**
+ * Functionality common to most series. Generally, a Series
+ * object shouldn't be instantiated directly. Instead, a subclass with a
+ * concrete implementation should be used.
+ *
+ * @namespace YAHOO.widget
+ * @class Series
+ * @constructor
+ */
+YAHOO.widget.Series = function() {};
+
+YAHOO.widget.Series.prototype =
+{
+ /**
+ * The type of series.
+ *
+ * @property type
+ * @type String
+ */
+ type: null,
+
+ /**
+ * The human-readable name of the series.
+ *
+ * @property displayName
+ * @type String
+ */
+ displayName: null
+};
+
+/**
+ * Functionality common to most series appearing in cartesian charts.
+ * Generally, a CartesianSeries object shouldn't be
+ * instantiated directly. Instead, a subclass with a concrete implementation
+ * should be used.
+ *
+ * @namespace YAHOO.widget
+ * @class CartesianSeries
+ * @uses YAHOO.widget.Series
+ * @constructor
+ */
+YAHOO.widget.CartesianSeries = function()
+{
+ YAHOO.widget.CartesianSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.CartesianSeries, YAHOO.widget.Series,
+{
+ /**
+ * The field used to access the x-axis value from the items from the data source.
+ *
+ * @property xField
+ * @type String
+ */
+ xField: null,
+
+ /**
+ * The field used to access the y-axis value from the items from the data source.
+ *
+ * @property yField
+ * @type String
+ */
+ yField: null,
+
+ /**
+ * Indicates which axis the series will bind to
+ *
+ * @property axis
+ * @type String
+ */
+ axis: "primary",
+
+ /**
+ * When a Legend is present, indicates whether the series will show in the legend.
+ *
+ * @property showInLegend
+ * @type Boolean
+ */
+ showInLegend: true
+});
+
+/**
+ * ColumnSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class ColumnSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.ColumnSeries = function()
+{
+ YAHOO.widget.ColumnSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.ColumnSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "column"
+});
+
+/**
+ * LineSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class LineSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.LineSeries = function()
+{
+ YAHOO.widget.LineSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.LineSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "line"
+});
+
+
+/**
+ * BarSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class BarSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.BarSeries = function()
+{
+ YAHOO.widget.BarSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.BarSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "bar"
+});
+
+
+/**
+ * PieSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class PieSeries
+ * @uses YAHOO.widget.Series
+ * @constructor
+ */
+YAHOO.widget.PieSeries = function()
+{
+ YAHOO.widget.PieSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.PieSeries, YAHOO.widget.Series,
+{
+ type: "pie",
+
+ /**
+ * The field used to access the data value from the items from the data source.
+ *
+ * @property dataField
+ * @type String
+ */
+ dataField: null,
+
+ /**
+ * The field used to access the category value from the items from the data source.
+ *
+ * @property categoryField
+ * @type String
+ */
+ categoryField: null,
+
+ /**
+ * A string reference to the globally-accessible function that may be called to
+ * determine each of the label values for this series. Also accepts function references.
+ *
+ * @property labelFunction
+ * @type String
+ */
+ labelFunction: null
+});
+
+/**
+ * StackedBarSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedBarSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.StackedBarSeries = function()
+{
+ YAHOO.widget.StackedBarSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedBarSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "stackbar"
+});
+
+/**
+ * StackedColumnSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedColumnSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.StackedColumnSeries = function()
+{
+ YAHOO.widget.StackedColumnSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedColumnSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "stackcolumn"
+});
+YAHOO.register("charts", YAHOO.widget.Chart, {version: "2.8.1", build: "19"});
diff --git a/lib/yui/2.8.1/build/charts/charts-min.js b/lib/yui/2.8.1/build/charts/charts-min.js
new file mode 100644
index 00000000000..3ecdd588735
--- /dev/null
+++ b/lib/yui/2.8.1/build/charts/charts-min.js
@@ -0,0 +1,9 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
+YAHOO.widget.Chart=function(I,F,A,J){this._type=I;this._dataSource=A;var B={align:"",allowNetworking:"",allowScriptAccess:"",base:"",bgcolor:"",menu:"",name:"",quality:"",salign:"",scale:"",tabindex:"",wmode:""};var D={fixedAttributes:{allowScriptAccess:"always"},flashVars:{allowedDomain:document.location.hostname},backgroundColor:"#ffffff",host:this,version:9.045};for(var E in J){if(B.hasOwnProperty(E)){D.fixedAttributes[E]=J[E];}else{D[E]=J[E];}}this._id=D.id=D.id||YAHOO.util.Dom.generateId(null,"yuigen");if(D.version&&D.version!=null&&D.version!=undefined&&D.version!="undefined"){var H=(/\w*.\w*/.exec(((D.version).toString()).replace(/.0./g,"."))).toString();var C=H.split(".");H=C[0]+".";switch((C[1].toString()).length){case 1:H+="00";break;case 2:H+="0";break;}H+=C[1];D.version=parseFloat(H);}this._swfURL=YAHOO.widget.Chart.SWFURL;this._containerID=F;this._attributes=D;this._swfEmbed=new YAHOO.widget.SWF(F,YAHOO.widget.Chart.SWFURL,D);this._swf=this._swfEmbed.swf;this._swfEmbed.subscribe("swfReady",this._eventHandler,this,true);try{this.createEvent("contentReady");}catch(G){}this.createEvent("itemMouseOverEvent");this.createEvent("itemMouseOutEvent");this.createEvent("itemClickEvent");this.createEvent("itemDoubleClickEvent");this.createEvent("itemDragStartEvent");this.createEvent("itemDragEvent");this.createEvent("itemDragEndEvent");};YAHOO.extend(YAHOO.widget.Chart,YAHOO.util.AttributeProvider,{_type:null,_pollingID:null,_pollingInterval:null,_dataTipFunction:null,_legendLabelFunction:null,_seriesFunctions:null,toString:function(){return"Chart "+this._id;},setStyle:function(A,B){B=YAHOO.lang.JSON.stringify(B);this._swf.setStyle(A,B);},setStyles:function(A){A=YAHOO.lang.JSON.stringify(A);this._swf.setStyles(A);},setSeriesStyles:function(B){for(var A=0;A0){this._pollingID=this._dataSource.setInterval(this._pollingInterval,this._request,this._loadDataHandler,this);}this._dataSource.sendRequest(this._request,this._loadDataHandler,this);}},_loadDataHandler:function(D,C,K){if(this._swf){if(K){}else{var H;if(this._seriesFunctions){var I=this._seriesFunctions.length;for(H=0;H0){for(H=0;H0){var A=C.length;for(var B=0;B 0)
+ {
+ this._pollingID = this._dataSource.setInterval(this._pollingInterval, this._request, this._loadDataHandler, this);
+ }
+ this._dataSource.sendRequest(this._request, this._loadDataHandler, this);
+ }
+ },
+
+ /**
+ * Called when the DataSource receives new data. The series definitions are used
+ * to build a data provider for the SWF chart.
+ *
+ * @method _loadDataHandler
+ * @private
+ */
+ _loadDataHandler: function(request, response, error)
+ {
+ if(this._swf)
+ {
+ if(error)
+ {
+ }
+ else
+ {
+ var i;
+ if(this._seriesFunctions)
+ {
+ var count = this._seriesFunctions.length;
+ for(i = 0; i < count; i++)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(this._seriesFunctions[i]);
+ }
+ this._seriesFunctions = null;
+ }
+ this._seriesFunctions = [];
+
+ //make a copy of the series definitions so that we aren't
+ //editing them directly.
+ var dataProvider = [];
+ var seriesCount = 0;
+ var currentSeries = null;
+ if(this._seriesDefs !== null)
+ {
+ seriesCount = this._seriesDefs.length;
+ for(i = 0; i < seriesCount; i++)
+ {
+ currentSeries = this._seriesDefs[i];
+ var clonedSeries = {};
+ for(var prop in currentSeries)
+ {
+ if(YAHOO.lang.hasOwnProperty(currentSeries, prop))
+ {
+ if(prop == "style")
+ {
+ if(currentSeries.style !== null)
+ {
+ clonedSeries.style = YAHOO.lang.JSON.stringify(currentSeries.style);
+ }
+ }
+
+ else if(prop == "labelFunction")
+ {
+ if(currentSeries.labelFunction !== null)
+ {
+ clonedSeries.labelFunction = YAHOO.widget.Chart.getFunctionReference(currentSeries.labelFunction);
+ this._seriesFunctions.push(clonedSeries.labelFunction);
+ }
+ }
+
+ else if(prop == "dataTipFunction")
+ {
+ if(currentSeries.dataTipFunction !== null)
+ {
+ clonedSeries.dataTipFunction = YAHOO.widget.Chart.getFunctionReference(currentSeries.dataTipFunction);
+ this._seriesFunctions.push(clonedSeries.dataTipFunction);
+ }
+ }
+
+ else if(prop == "legendLabelFunction")
+ {
+ if(currentSeries.legendLabelFunction !== null)
+ {
+ clonedSeries.legendLabelFunction = YAHOO.widget.Chart.getFunctionReference(currentSeries.legendLabelFunction);
+ this._seriesFunctions.push(clonedSeries.legendLabelFunction);
+ }
+ }
+
+ else
+ {
+ clonedSeries[prop] = currentSeries[prop];
+ }
+ }
+ }
+ dataProvider.push(clonedSeries);
+ }
+ }
+
+ if(seriesCount > 0)
+ {
+ for(i = 0; i < seriesCount; i++)
+ {
+ currentSeries = dataProvider[i];
+ if(!currentSeries.type)
+ {
+ currentSeries.type = this._type;
+ }
+ currentSeries.dataProvider = response.results;
+ }
+ }
+ else
+ {
+ var series = {type: this._type, dataProvider: response.results};
+ dataProvider.push(series);
+ }
+ try
+ {
+ if(this._swf.setDataProvider) this._swf.setDataProvider(dataProvider);
+ }
+ catch(e)
+ {
+ this._swf.setDataProvider(dataProvider);
+ }
+ }
+ }
+ },
+
+ /**
+ * Storage for the request attribute.
+ *
+ * @property _request
+ * @private
+ */
+ _request: "",
+
+ /**
+ * Getter for the request attribute.
+ *
+ * @method _getRequest
+ * @private
+ */
+ _getRequest: function()
+ {
+ return this._request;
+ },
+
+ /**
+ * Setter for the request attribute.
+ *
+ * @method _setRequest
+ * @private
+ */
+ _setRequest: function(value)
+ {
+ this._request = value;
+ this.refreshData();
+ },
+
+ /**
+ * Storage for the dataSource attribute.
+ *
+ * @property _dataSource
+ * @private
+ */
+ _dataSource: null,
+
+ /**
+ * Getter for the dataSource attribute.
+ *
+ * @method _getDataSource
+ * @private
+ */
+ _getDataSource: function()
+ {
+ return this._dataSource;
+ },
+
+ /**
+ * Setter for the dataSource attribute.
+ *
+ * @method _setDataSource
+ * @private
+ */
+ _setDataSource: function(value)
+ {
+ this._dataSource = value;
+ this.refreshData();
+ },
+
+ /**
+ * Storage for the series attribute.
+ *
+ * @property _seriesDefs
+ * @private
+ */
+ _seriesDefs: null,
+
+ /**
+ * Getter for the series attribute.
+ *
+ * @method _getSeriesDefs
+ * @private
+ */
+ _getSeriesDefs: function()
+ {
+ return this._seriesDefs;
+ },
+
+ /**
+ * Setter for the series attribute.
+ *
+ * @method _setSeriesDefs
+ * @private
+ */
+ _setSeriesDefs: function(value)
+ {
+ this._seriesDefs = value;
+ this.refreshData();
+ },
+
+ /**
+ * Getter for the categoryNames attribute.
+ *
+ * @method _getCategoryNames
+ * @private
+ */
+ _getCategoryNames: function()
+ {
+ return this._swf.getCategoryNames();
+ },
+
+ /**
+ * Setter for the categoryNames attribute.
+ *
+ * @method _setCategoryNames
+ * @private
+ */
+ _setCategoryNames: function(value)
+ {
+ this._swf.setCategoryNames(value);
+ },
+
+ /**
+ * Setter for the dataTipFunction attribute.
+ *
+ * @method _setDataTipFunction
+ * @private
+ */
+ _setDataTipFunction: function(value)
+ {
+ if(this._dataTipFunction)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(this._dataTipFunction);
+ }
+
+ if(value)
+ {
+ this._dataTipFunction = value = YAHOO.widget.Chart.getFunctionReference(value);
+ }
+ this._swf.setDataTipFunction(value);
+ },
+
+ /**
+ * Setter for the legendLabelFunction attribute.
+ *
+ * @method _setLegendLabelFunction
+ * @private
+ */
+ _setLegendLabelFunction: function(value)
+ {
+ if(this._legendLabelFunction)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(this._legendLabelFunction);
+ }
+
+ if(value)
+ {
+ this._legendLabelFunction = value = YAHOO.widget.Chart.getFunctionReference(value);
+ }
+ this._swf.setLegendLabelFunction(value);
+ },
+
+ /**
+ * Getter for the polling attribute.
+ *
+ * @method _getPolling
+ * @private
+ */
+ _getPolling: function()
+ {
+ return this._pollingInterval;
+ },
+
+ /**
+ * Setter for the polling attribute.
+ *
+ * @method _setPolling
+ * @private
+ */
+ _setPolling: function(value)
+ {
+ this._pollingInterval = value;
+ this.refreshData();
+ },
+
+ /**
+ * The javascript wrapper for the swf object
+ *
+ * @property _swfEmbed
+ * @type swf
+ * @private
+ */
+ _swfEmbed: null,
+
+ /**
+ * The URL of the SWF file.
+ * @property _swfURL
+ * @type String
+ * @private
+ */
+ _swfURL: null,
+
+ /**
+ * The ID of the containing DIV.
+ * @property _containerID
+ * @type String
+ * @private
+ */
+ _containerID: null,
+
+ /**
+ * A reference to the embedded SWF file.
+ * @property _swf
+ * @private
+ */
+ _swf: null,
+
+ /**
+ * The id of this instance.
+ * @property _id
+ * @type String
+ * @private
+ */
+ _id: null,
+
+ /**
+ * Indicates whether the SWF has been initialized and is ready
+ * to communicate with JavaScript
+ * @property _initialized
+ * @type Boolean
+ * @private
+ */
+ _initialized: false,
+
+ /**
+ * The initializing attributes are stored here until the SWF is ready.
+ * @property _attributes
+ * @type Object
+ * @private
+ */
+ _attributes: null, //the intializing attributes
+
+ set: function(name, value)
+ {
+ //save all the attributes in case the swf reloads
+ //so that we can pass them in again
+ this._attributes[name] = value;
+ YAHOO.widget.Chart.superclass.set.call(this, name, value);
+ },
+
+ /**
+ * Getter for swfURL attribute.
+ *
+ * @method _getSWFURL
+ * @private
+ */
+ _getSWFURL: function()
+ {
+ return this._swfURL;
+ },
+
+ /**
+ * Getter for altText attribute.
+ *
+ * @method _getAltText
+ * @private
+ */
+ _getAltText: function()
+ {
+ return this._swf.getAltText();
+ },
+
+ /**
+ * Setter for altText attribute.
+ *
+ * @method _setAltText
+ * @private
+ */
+ _setAltText: function(value)
+ {
+ this._swf.setAltText(value);
+ }
+});
+
+/**
+ * The number of proxy functions that have been created.
+ * @static
+ * @private
+ */
+YAHOO.widget.Chart.proxyFunctionCount = 0;
+
+/**
+ * Creates a globally accessible function that wraps a function reference.
+ * Returns the proxy function's name as a string for use by the SWF through
+ * ExternalInterface.
+ *
+ * @method YAHOO.widget.Chart.createProxyFunction
+ * @static
+ * @private
+ */
+YAHOO.widget.Chart.createProxyFunction = function(func, scope)
+{
+ var scope = scope || null;
+ var index = YAHOO.widget.Chart.proxyFunctionCount;
+ YAHOO.widget.Chart["proxyFunction" + index] = function()
+ {
+ return func.apply(scope, arguments);
+ };
+ YAHOO.widget.Chart.proxyFunctionCount++;
+ return "YAHOO.widget.Chart.proxyFunction" + index.toString();
+};
+
+/**
+ * Uses YAHOO.widget.Chart.createProxyFunction to return string
+ * reference to a function.
+ *
+ * @method YAHOO.widget.Chart.getFunctionReference
+ * @static
+ * @private
+ */
+YAHOO.widget.Chart.getFunctionReference = function(value)
+{
+ if(typeof value == "function")
+ {
+ value = YAHOO.widget.Chart.createProxyFunction(value);
+ }
+ else if(value.func && typeof value.func == "function")
+ {
+ var args = [value.func];
+ if(value.scope && typeof value.scope == "object")
+ {
+ args.push(value.scope);
+ }
+ value = YAHOO.widget.Chart.createProxyFunction.apply(this, args);
+ }
+ return value;
+}
+
+/**
+ * Removes a function created with createProxyFunction()
+ *
+ * @method YAHOO.widget.Chart.removeProxyFunction
+ * @static
+ * @private
+ */
+YAHOO.widget.Chart.removeProxyFunction = function(funcName)
+{
+ //quick error check
+ if(!funcName || funcName.indexOf("YAHOO.widget.Chart.proxyFunction") < 0)
+ {
+ return;
+ }
+
+ funcName = funcName.substr(26);
+ YAHOO.widget.Chart[funcName] = null;
+};
+
+/**
+ * Storage for the dataTipFunction attribute.
+ *
+ * @property Chart.SWFURL
+ * @private
+ * @static
+ * @final
+ * @default "assets/charts.swf"
+ */
+YAHOO.widget.Chart.SWFURL = "assets/charts.swf";
+/**
+ * PieChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class PieChart
+ * @uses YAHOO.widget.Chart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.PieChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.PieChart.superclass.constructor.call(this, "pie", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.PieChart, YAHOO.widget.Chart,
+{
+ /**
+ * Initializes the attributes.
+ *
+ * @method _initAttributes
+ * @private
+ */
+ _initAttributes: function(attributes)
+ {
+ YAHOO.widget.PieChart.superclass._initAttributes.call(this, attributes);
+
+ /**
+ * @attribute dataField
+ * @description The field in each item that corresponds to the data value.
+ * @type String
+ */
+ this.setAttributeConfig("dataField",
+ {
+ validator: YAHOO.lang.isString,
+ method: this._setDataField,
+ getter: this._getDataField
+ });
+
+ /**
+ * @attribute categoryField
+ * @description The field in each item that corresponds to the category value.
+ * @type String
+ */
+ this.setAttributeConfig("categoryField",
+ {
+ validator: YAHOO.lang.isString,
+ method: this._setCategoryField,
+ getter: this._getCategoryField
+ });
+ },
+
+ /**
+ * Getter for the dataField attribute.
+ *
+ * @method _getDataField
+ * @private
+ */
+ _getDataField: function()
+ {
+ return this._swf.getDataField();
+ },
+
+ /**
+ * Setter for the dataField attribute.
+ *
+ * @method _setDataField
+ * @private
+ */
+ _setDataField: function(value)
+ {
+ this._swf.setDataField(value);
+ },
+
+ /**
+ * Getter for the categoryField attribute.
+ *
+ * @method _getCategoryField
+ * @private
+ */
+ _getCategoryField: function()
+ {
+ return this._swf.getCategoryField();
+ },
+
+ /**
+ * Setter for the categoryField attribute.
+ *
+ * @method _setCategoryField
+ * @private
+ */
+ _setCategoryField: function(value)
+ {
+ this._swf.setCategoryField(value);
+ }
+});
+/**
+ * CartesianChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class CartesianChart
+ * @uses YAHOO.widget.Chart
+ * @constructor
+ * @param type {String} The char type. May be "line", "column", or "bar"
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+ YAHOO.widget.CartesianChart = function(type, containerId, dataSource, attributes)
+{
+ YAHOO.widget.CartesianChart.superclass.constructor.call(this, type, containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.CartesianChart, YAHOO.widget.Chart,
+{
+ /**
+ * Stores a reference to the xAxis labelFunction created by
+ * YAHOO.widget.Chart.createProxyFunction()
+ * @property _xAxisLabelFunctions
+ * @type String
+ * @private
+ */
+ _xAxisLabelFunctions: [],
+
+ /**
+ * Stores a reference to the yAxis labelFunctions created by
+ * YAHOO.widget.Chart.createProxyFunction()
+ * @property _yAxisLabelFunctions
+ * @type Array
+ * @private
+ */
+ _yAxisLabelFunctions: [],
+
+ destroy: function()
+ {
+ //remove proxy functions
+ this._removeAxisFunctions(this._xAxisLabelFunctions);
+ this._removeAxisFunctions(this._yAxisLabelFunctions);
+
+ //call last
+ YAHOO.widget.CartesianChart.superclass.destroy.call(this);
+ },
+
+ /**
+ * Initializes the attributes.
+ *
+ * @method _initAttributes
+ * @private
+ */
+ _initAttributes: function(attributes)
+ {
+ YAHOO.widget.CartesianChart.superclass._initAttributes.call(this, attributes);
+
+ /**
+ * @attribute xField
+ * @description The field in each item that corresponds to a value on the x axis.
+ * @type String
+ */
+ this.setAttributeConfig("xField",
+ {
+ validator: YAHOO.lang.isString,
+ method: this._setXField,
+ getter: this._getXField
+ });
+
+ /**
+ * @attribute yField
+ * @description The field in each item that corresponds to a value on the x axis.
+ * @type String
+ */
+ this.setAttributeConfig("yField",
+ {
+ validator: YAHOO.lang.isString,
+ method: this._setYField,
+ getter: this._getYField
+ });
+
+ /**
+ * @attribute xAxis
+ * @description A custom configuration for the horizontal x axis.
+ * @type Axis
+ */
+ this.setAttributeConfig("xAxis",
+ {
+ method: this._setXAxis
+ });
+
+ /**
+ * @attribute xAxes
+ * @description Custom configurations for the horizontal x axes.
+ * @type Array
+ */
+ this.setAttributeConfig("xAxes",
+ {
+ method: this._setXAxes
+ });
+
+ /**
+ * @attribute yAxis
+ * @description A custom configuration for the vertical y axis.
+ * @type Axis
+ */
+ this.setAttributeConfig("yAxis",
+ {
+ method: this._setYAxis
+ });
+
+ /**
+ * @attribute yAxes
+ * @description Custom configurations for the vertical y axes.
+ * @type Array
+ */
+ this.setAttributeConfig("yAxes",
+ {
+ method: this._setYAxes
+ });
+
+ /**
+ * @attribute constrainViewport
+ * @description Determines whether the viewport is constrained to prevent series data from overflow.
+ * @type Boolean
+ */
+ this.setAttributeConfig("constrainViewport",
+ {
+ method: this._setConstrainViewport
+ });
+ },
+
+ /**
+ * Getter for the xField attribute.
+ *
+ * @method _getXField
+ * @private
+ */
+ _getXField: function()
+ {
+ return this._swf.getHorizontalField();
+ },
+
+ /**
+ * Setter for the xField attribute.
+ *
+ * @method _setXField
+ * @private
+ */
+ _setXField: function(value)
+ {
+ this._swf.setHorizontalField(value);
+ },
+
+ /**
+ * Getter for the yField attribute.
+ *
+ * @method _getYField
+ * @private
+ */
+ _getYField: function()
+ {
+ return this._swf.getVerticalField();
+ },
+
+ /**
+ * Setter for the yField attribute.
+ *
+ * @method _setYField
+ * @private
+ */
+ _setYField: function(value)
+ {
+ this._swf.setVerticalField(value);
+ },
+
+ /**
+ * Receives an axis object, creates a proxy function for
+ * the labelFunction and returns the updated object.
+ *
+ * @method _getClonedAxis
+ * @private
+ */
+ _getClonedAxis: function(value)
+ {
+ var clonedAxis = {};
+ for(var prop in value)
+ {
+ if(prop == "labelFunction")
+ {
+ if(value.labelFunction && value.labelFunction !== null)
+ {
+ clonedAxis.labelFunction = YAHOO.widget.Chart.getFunctionReference(value.labelFunction);
+ }
+ }
+ else
+ {
+ clonedAxis[prop] = value[prop];
+ }
+ }
+ return clonedAxis;
+ },
+
+ /**
+ * Removes axis functions contained in an array
+ *
+ * @method _removeAxisFunctions
+ * @private
+ */
+ _removeAxisFunctions: function(axisFunctions)
+ {
+ if(axisFunctions && axisFunctions.length > 0)
+ {
+ var len = axisFunctions.length;
+ for(var i = 0; i < len; i++)
+ {
+ if(axisFunctions[i] !== null)
+ {
+ YAHOO.widget.Chart.removeProxyFunction(axisFunctions[i]);
+ }
+ }
+ axisFunctions = [];
+ }
+ },
+
+ /**
+ * Setter for the xAxis attribute.
+ *
+ * @method _setXAxis
+ * @private
+ */
+ _setXAxis: function(value)
+ {
+ if(value.position != "bottom" && value.position != "top") value.position = "bottom";
+ this._removeAxisFunctions(this._xAxisLabelFunctions);
+ value = this._getClonedAxis(value);
+ this._xAxisLabelFunctions.push(value.labelFunction);
+ this._swf.setHorizontalAxis(value);
+ },
+
+ /**
+ * Setter for the xAxes attribute
+ *
+ * @method _setXAxes
+ * @private
+ */
+ _setXAxes: function(value)
+ {
+ this._removeAxisFunctions(this._xAxisLabelFunctions);
+ var len = value.length;
+ for(var i = 0; i < len; i++)
+ {
+ if(value[i].position == "left") value[i].position = "bottom";
+ value[i] = this._getClonedAxis(value[i]);
+ if(value[i].labelFunction) this._xAxisLabelFunctions.push(value[i].labelFunction);
+ this._swf.setHorizontalAxis(value[i]);
+ }
+ },
+
+ /**
+ * Setter for the yAxis attribute.
+ *
+ * @method _setYAxis
+ * @private
+ */
+ _setYAxis: function(value)
+ {
+ this._removeAxisFunctions(this._yAxisLabelFunctions);
+ value = this._getClonedAxis(value);
+ this._yAxisLabelFunctions.push(value.labelFunction);
+ this._swf.setVerticalAxis(value);
+ },
+
+ /**
+ * Setter for the yAxes attribute.
+ *
+ * @method _setYAxes
+ * @private
+ */
+ _setYAxes: function(value)
+ {
+ this._removeAxisFunctions(this._yAxisLabelFunctions);
+ var len = value.length;
+ for(var i = 0; i < len; i++)
+ {
+ value[i] = this._getClonedAxis(value[i]);
+ if(value[i].labelFunction) this._yAxisLabelFunctions.push(value[i].labelFunction);
+ this._swf.setVerticalAxis(value[i]);
+ }
+ },
+
+ /**
+ * Setter for the constrainViewport attribute
+ *
+ * @method _setConstrainViewport
+ * @private
+ */
+ _setConstrainViewport: function(value)
+ {
+ this._swf.setConstrainViewport(value);
+ },
+
+ /**
+ * Sets the style object for a single series based on its index
+ *
+ * @method setSeriesStylesByIndex
+ * @param index {Number} The position within the series definition to apply the style
+ * @param style {object} Style object to be applied to the selected series
+ */
+ setSeriesStylesByIndex:function(index, style)
+ {
+ style = YAHOO.lang.JSON.stringify(style);
+ if(this._swf && this._swf.setSeriesStylesByIndex) this._swf.setSeriesStylesByIndex(index, style);
+ }
+});
+/**
+ * LineChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class LineChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.LineChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.LineChart.superclass.constructor.call(this, "line", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.LineChart, YAHOO.widget.CartesianChart);
+
+/**
+ * ColumnChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class ColumnChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.ColumnChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.ColumnChart.superclass.constructor.call(this, "column", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.ColumnChart, YAHOO.widget.CartesianChart);
+
+/**
+ * BarChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class BarChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.BarChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.BarChart.superclass.constructor.call(this, "bar", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.BarChart, YAHOO.widget.CartesianChart);
+
+/**
+ * StackedColumnChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedColumnChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.StackedColumnChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.StackedColumnChart.superclass.constructor.call(this, "stackcolumn", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedColumnChart, YAHOO.widget.CartesianChart);
+
+/**
+ * StackedBarChart class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedBarChart
+ * @uses YAHOO.widget.CartesianChart
+ * @constructor
+ * @param containerId {HTMLElement} Container element for the Flash Player instance.
+ * @param dataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param attributes {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.StackedBarChart = function(containerId, dataSource, attributes)
+{
+ YAHOO.widget.StackedBarChart.superclass.constructor.call(this, "stackbar", containerId, dataSource, attributes);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedBarChart, YAHOO.widget.CartesianChart);
+/**
+ * Defines a CartesianChart's vertical or horizontal axis.
+ *
+ * @namespace YAHOO.widget
+ * @class Axis
+ * @constructor
+ */
+YAHOO.widget.Axis = function()
+{
+};
+
+YAHOO.widget.Axis.prototype =
+{
+ /**
+ * The type of axis.
+ *
+ * @property type
+ * @type String
+ */
+ type: null,
+
+ /**
+ * If true, the items on the axis will be drawn in opposite direction.
+ *
+ * @property reverse
+ * @type Boolean
+ */
+ reverse: false,
+
+ /**
+ * A string reference to the globally-accessible function that may be called to
+ * determine each of the label values for this axis. Also accepts function references.
+ *
+ * @property labelFunction
+ * @type String
+ */
+ labelFunction: null,
+
+ /**
+ * The space, in pixels, between labels on an axis.
+ *
+ * @property labelSpacing
+ * @type Number
+ */
+ labelSpacing: 2,
+
+ /**
+ * The text that will appear next to the axis to indicate information about the data that it displays.
+ *
+ * @property title
+ * @type String
+ */
+ title: null
+};
+/**
+ * A type of axis whose units are measured in numeric values.
+ *
+ * @namespace YAHOO.widget
+ * @class NumericAxis
+ * @extends YAHOO.widget.Axis
+ * @constructor
+ */
+YAHOO.widget.NumericAxis = function()
+{
+ YAHOO.widget.NumericAxis.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.NumericAxis, YAHOO.widget.Axis,
+{
+ type: "numeric",
+
+ /**
+ * The minimum value drawn by the axis. If not set explicitly, the axis minimum
+ * will be calculated automatically.
+ *
+ * @property minimum
+ * @type Number
+ */
+ minimum: NaN,
+
+ /**
+ * The maximum value drawn by the axis. If not set explicitly, the axis maximum
+ * will be calculated automatically.
+ *
+ * @property maximum
+ * @type Number
+ */
+ maximum: NaN,
+
+ /**
+ * The spacing between major intervals on this axis.
+ *
+ * @property majorUnit
+ * @type Number
+ */
+ majorUnit: NaN,
+
+ /**
+ * The spacing between minor intervals on this axis.
+ *
+ * @property minorUnit
+ * @type Number
+ */
+ minorUnit: NaN,
+
+ /**
+ * If true, the labels, ticks, gridlines, and other objects will snap to
+ * the nearest major or minor unit. If false, their position will be based
+ * on the minimum value.
+ *
+ * @property snapToUnits
+ * @type Boolean
+ */
+ snapToUnits: true,
+
+ /**
+ * Series that are stackable will only stack when this value is set to true.
+ *
+ * @property stackingEnabled
+ * @type Boolean
+ */
+ stackingEnabled: false,
+
+ /**
+ * If true, and the bounds are calculated automatically, either the minimum or
+ * maximum will be set to zero.
+ *
+ * @property alwaysShowZero
+ * @type Boolean
+ */
+ alwaysShowZero: true,
+
+ /**
+ * The scaling algorithm to use on this axis. May be "linear" or "logarithmic".
+ *
+ * @property scale
+ * @type String
+ */
+ scale: "linear",
+
+ /**
+ * Indicates whether to round the major unit.
+ *
+ * @property roundMajorUnit
+ * @type Boolean
+ */
+ roundMajorUnit: true,
+
+ /**
+ * Indicates whether to factor in the size of the labels when calculating a major unit.
+ *
+ * @property calculateByLabelSize
+ * @type Boolean
+ */
+ calculateByLabelSize: true,
+
+ /**
+ * Indicates the position of the axis relative to the chart
+ *
+ * @property position
+ * @type String
+ */
+ position:"left",
+
+ /**
+ * Indicates whether to extend maximum beyond data's maximum to the nearest
+ * majorUnit.
+ *
+ * @property adjustMaximumByMajorUnit
+ * @type Boolean
+ */
+ adjustMaximumByMajorUnit:true,
+
+ /**
+ * Indicates whether to extend the minimum beyond data's minimum to the nearest
+ * majorUnit.
+ *
+ * @property adjustMinimumByMajorUnit
+ * @type Boolean
+ */
+ adjustMinimumByMajorUnit:true
+});
+/**
+ * A type of axis whose units are measured in time-based values.
+ *
+ * @namespace YAHOO.widget
+ * @class TimeAxis
+ * @constructor
+ */
+YAHOO.widget.TimeAxis = function()
+{
+ YAHOO.widget.TimeAxis.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.TimeAxis, YAHOO.widget.Axis,
+{
+ type: "time",
+
+ /**
+ * The minimum value drawn by the axis. If not set explicitly, the axis minimum
+ * will be calculated automatically.
+ *
+ * @property minimum
+ * @type Date
+ */
+ minimum: null,
+
+ /**
+ * The maximum value drawn by the axis. If not set explicitly, the axis maximum
+ * will be calculated automatically.
+ *
+ * @property maximum
+ * @type Number
+ */
+ maximum: null,
+
+ /**
+ * The spacing between major intervals on this axis.
+ *
+ * @property majorUnit
+ * @type Number
+ */
+ majorUnit: NaN,
+
+ /**
+ * The time unit used by the majorUnit.
+ *
+ * @property majorTimeUnit
+ * @type String
+ */
+ majorTimeUnit: null,
+
+ /**
+ * The spacing between minor intervals on this axis.
+ *
+ * @property majorUnit
+ * @type Number
+ */
+ minorUnit: NaN,
+
+ /**
+ * The time unit used by the minorUnit.
+ *
+ * @property majorTimeUnit
+ * @type String
+ */
+ minorTimeUnit: null,
+
+ /**
+ * If true, the labels, ticks, gridlines, and other objects will snap to
+ * the nearest major or minor unit. If false, their position will be based
+ * on the minimum value.
+ *
+ * @property snapToUnits
+ * @type Boolean
+ */
+ snapToUnits: true,
+
+ /**
+ * Series that are stackable will only stack when this value is set to true.
+ *
+ * @property stackingEnabled
+ * @type Boolean
+ */
+ stackingEnabled: false,
+
+ /**
+ * Indicates whether to factor in the size of the labels when calculating a major unit.
+ *
+ * @property calculateByLabelSize
+ * @type Boolean
+ */
+ calculateByLabelSize: true
+});
+/**
+ * A type of axis that displays items in categories.
+ *
+ * @namespace YAHOO.widget
+ * @class CategoryAxis
+ * @constructor
+ */
+YAHOO.widget.CategoryAxis = function()
+{
+ YAHOO.widget.CategoryAxis.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.CategoryAxis, YAHOO.widget.Axis,
+{
+ type: "category",
+
+ /**
+ * A list of category names to display along this axis.
+ *
+ * @property categoryNames
+ * @type Array
+ */
+ categoryNames: null,
+
+ /**
+ * Indicates whether or not to calculate the number of categories (ticks and labels)
+ * when there is not enough room to display all labels on the axis. If set to true, the axis
+ * will determine the number of categories to plot. If not, all categories will be plotted.
+ *
+ * @property calculateCategoryCount
+ * @type Boolean
+ */
+ calculateCategoryCount: false
+});
+ /**
+ * Functionality common to most series. Generally, a Series
+ * object shouldn't be instantiated directly. Instead, a subclass with a
+ * concrete implementation should be used.
+ *
+ * @namespace YAHOO.widget
+ * @class Series
+ * @constructor
+ */
+YAHOO.widget.Series = function() {};
+
+YAHOO.widget.Series.prototype =
+{
+ /**
+ * The type of series.
+ *
+ * @property type
+ * @type String
+ */
+ type: null,
+
+ /**
+ * The human-readable name of the series.
+ *
+ * @property displayName
+ * @type String
+ */
+ displayName: null
+};
+
+/**
+ * Functionality common to most series appearing in cartesian charts.
+ * Generally, a CartesianSeries object shouldn't be
+ * instantiated directly. Instead, a subclass with a concrete implementation
+ * should be used.
+ *
+ * @namespace YAHOO.widget
+ * @class CartesianSeries
+ * @uses YAHOO.widget.Series
+ * @constructor
+ */
+YAHOO.widget.CartesianSeries = function()
+{
+ YAHOO.widget.CartesianSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.CartesianSeries, YAHOO.widget.Series,
+{
+ /**
+ * The field used to access the x-axis value from the items from the data source.
+ *
+ * @property xField
+ * @type String
+ */
+ xField: null,
+
+ /**
+ * The field used to access the y-axis value from the items from the data source.
+ *
+ * @property yField
+ * @type String
+ */
+ yField: null,
+
+ /**
+ * Indicates which axis the series will bind to
+ *
+ * @property axis
+ * @type String
+ */
+ axis: "primary",
+
+ /**
+ * When a Legend is present, indicates whether the series will show in the legend.
+ *
+ * @property showInLegend
+ * @type Boolean
+ */
+ showInLegend: true
+});
+
+/**
+ * ColumnSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class ColumnSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.ColumnSeries = function()
+{
+ YAHOO.widget.ColumnSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.ColumnSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "column"
+});
+
+/**
+ * LineSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class LineSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.LineSeries = function()
+{
+ YAHOO.widget.LineSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.LineSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "line"
+});
+
+
+/**
+ * BarSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class BarSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.BarSeries = function()
+{
+ YAHOO.widget.BarSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.BarSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "bar"
+});
+
+
+/**
+ * PieSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class PieSeries
+ * @uses YAHOO.widget.Series
+ * @constructor
+ */
+YAHOO.widget.PieSeries = function()
+{
+ YAHOO.widget.PieSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.PieSeries, YAHOO.widget.Series,
+{
+ type: "pie",
+
+ /**
+ * The field used to access the data value from the items from the data source.
+ *
+ * @property dataField
+ * @type String
+ */
+ dataField: null,
+
+ /**
+ * The field used to access the category value from the items from the data source.
+ *
+ * @property categoryField
+ * @type String
+ */
+ categoryField: null,
+
+ /**
+ * A string reference to the globally-accessible function that may be called to
+ * determine each of the label values for this series. Also accepts function references.
+ *
+ * @property labelFunction
+ * @type String
+ */
+ labelFunction: null
+});
+
+/**
+ * StackedBarSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedBarSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.StackedBarSeries = function()
+{
+ YAHOO.widget.StackedBarSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedBarSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "stackbar"
+});
+
+/**
+ * StackedColumnSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedColumnSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.StackedColumnSeries = function()
+{
+ YAHOO.widget.StackedColumnSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedColumnSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "stackcolumn"
+});
+YAHOO.register("charts", YAHOO.widget.Chart, {version: "2.8.1", build: "19"});
diff --git a/lib/yui/2.8.1/build/colorpicker/assets/colorpicker-core.css b/lib/yui/2.8.1/build/colorpicker/assets/colorpicker-core.css
new file mode 100644
index 00000000000..357a8273699
--- /dev/null
+++ b/lib/yui/2.8.1/build/colorpicker/assets/colorpicker-core.css
@@ -0,0 +1,6 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
diff --git a/lib/yui/2.8.1/build/colorpicker/assets/hue_thumb.png b/lib/yui/2.8.1/build/colorpicker/assets/hue_thumb.png
new file mode 100644
index 00000000000..14d5db48624
Binary files /dev/null and b/lib/yui/2.8.1/build/colorpicker/assets/hue_thumb.png differ
diff --git a/lib/yui/2.8.1/build/colorpicker/assets/picker_mask.png b/lib/yui/2.8.1/build/colorpicker/assets/picker_mask.png
new file mode 100644
index 00000000000..f8d91932b37
Binary files /dev/null and b/lib/yui/2.8.1/build/colorpicker/assets/picker_mask.png differ
diff --git a/lib/yui/2.8.1/build/colorpicker/assets/picker_thumb.png b/lib/yui/2.8.1/build/colorpicker/assets/picker_thumb.png
new file mode 100644
index 00000000000..78445a2fe06
Binary files /dev/null and b/lib/yui/2.8.1/build/colorpicker/assets/picker_thumb.png differ
diff --git a/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/colorpicker-skin.css b/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/colorpicker-skin.css
new file mode 100644
index 00000000000..a5e2440bece
--- /dev/null
+++ b/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/colorpicker-skin.css
@@ -0,0 +1,105 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
+
+.yui-picker-panel {
+ background: #e3e3e3;
+ border-color: #888;
+}
+
+.yui-picker-panel .hd {
+ background-color:#ccc;
+ font-size:100%;
+ line-height:100%;
+ border:1px solid #e3e3e3;
+ font-weight:bold;
+ overflow:hidden;
+ padding: 6px;
+ color: #000;
+}
+
+.yui-picker-panel .bd {
+ background: #e8e8e8;
+ margin: 1px;
+ height: 200px;
+}
+
+.yui-picker-panel .ft {
+ background: #e8e8e8;
+ margin: 1px;
+ padding: 1px;
+ /*
+ text-align: center;
+ */
+}
+
+.yui-picker {
+ position: relative;
+}
+
+.yui-picker-hue-thumb { cursor:default; width:18px; height:18px;
+top: -8px;
+left: -2px;
+ z-index: 9; position:absolute; }
+.yui-picker-hue-bg {-moz-outline: none; outline:0px none;
+ position:absolute; left:200px; height:183px; width:14px;
+ background:url(hue_bg.png) no-repeat;
+ top:4px;
+}
+
+.yui-picker-bg {
+ -moz-outline: none;
+ outline:0px none;
+ position:absolute;
+ top:4px;
+ left:4px;
+ height:182px;
+ width:182px;
+ background-color:#F00;
+ background-image: url(picker_mask.png);
+}
+
+*html .yui-picker-bg {
+ background-image: none;
+ filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../../build/colorpicker/assets/picker_mask.png', sizingMethod='scale');
+}
+
+
+.yui-picker-mask { position:absolute; z-index: 1; top:0px; left:0px; }
+
+.yui-picker-thumb { cursor:default; width:11px; height:11px; z-index: 9; position:absolute;
+ top:-4px; left:-4px; }
+
+.yui-picker-swatch { position:absolute; left:240px; top:4px; height:60px;
+ width:55px; border:1px solid #888; }
+.yui-picker-websafe-swatch { position:absolute; left:304px; top:4px;
+ height:24px; width:24px; border:1px solid #888; }
+
+.yui-picker-controls { position:absolute; top: 72px; left:226px; font:1em monospace;}
+.yui-picker-controls .hd { background: transparent; border-width: 0px !important;}
+.yui-picker-controls .bd { height: 100px; border-width: 0px !important;}
+.yui-picker-controls ul {float:left;padding:0 2px 0 0;margin:0}
+.yui-picker-controls li {padding:2px;list-style:none;margin:0}
+.yui-picker-controls input {
+ font-size: 0.85em;
+ width: 2.4em;
+}
+.yui-picker-hex-controls {
+ clear: both;
+ padding: 2px;
+}
+.yui-picker-hex-controls input {
+ width: 4.6em;
+}
+
+.yui-picker-controls a {
+ font: 1em arial,helvetica,clean,sans-serif;
+ display:block;
+ *display:inline-block; /* IE */
+ padding: 0;
+ color: #000;
+
+}
diff --git a/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/colorpicker.css b/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/colorpicker.css
new file mode 100644
index 00000000000..f930c63922d
--- /dev/null
+++ b/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/colorpicker.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
+.yui-picker-panel{background:#e3e3e3;border-color:#888;}.yui-picker-panel .hd{background-color:#ccc;font-size:100%;line-height:100%;border:1px solid #e3e3e3;font-weight:bold;overflow:hidden;padding:6px;color:#000;}.yui-picker-panel .bd{background:#e8e8e8;margin:1px;height:200px;}.yui-picker-panel .ft{background:#e8e8e8;margin:1px;padding:1px;}.yui-picker{position:relative;}.yui-picker-hue-thumb{cursor:default;width:18px;height:18px;top:-8px;left:-2px;z-index:9;position:absolute;}.yui-picker-hue-bg{-moz-outline:none;outline:0 none;position:absolute;left:200px;height:183px;width:14px;background:url(hue_bg.png) no-repeat;top:4px;}.yui-picker-bg{-moz-outline:none;outline:0 none;position:absolute;top:4px;left:4px;height:182px;width:182px;background-color:#F00;background-image:url(picker_mask.png);}*html .yui-picker-bg{background-image:none;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../../build/colorpicker/assets/picker_mask.png',sizingMethod='scale');}.yui-picker-mask{position:absolute;z-index:1;top:0;left:0;}.yui-picker-thumb{cursor:default;width:11px;height:11px;z-index:9;position:absolute;top:-4px;left:-4px;}.yui-picker-swatch{position:absolute;left:240px;top:4px;height:60px;width:55px;border:1px solid #888;}.yui-picker-websafe-swatch{position:absolute;left:304px;top:4px;height:24px;width:24px;border:1px solid #888;}.yui-picker-controls{position:absolute;top:72px;left:226px;font:1em monospace;}.yui-picker-controls .hd{background:transparent;border-width:0!important;}.yui-picker-controls .bd{height:100px;border-width:0!important;}.yui-picker-controls ul{float:left;padding:0 2px 0 0;margin:0;}.yui-picker-controls li{padding:2px;list-style:none;margin:0;}.yui-picker-controls input{font-size:.85em;width:2.4em;}.yui-picker-hex-controls{clear:both;padding:2px;}.yui-picker-hex-controls input{width:4.6em;}.yui-picker-controls a{font:1em arial,helvetica,clean,sans-serif;display:block;*display:inline-block;padding:0;color:#000;}
diff --git a/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/hue_bg.png b/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/hue_bg.png
new file mode 100644
index 00000000000..d9bcdeb5c49
Binary files /dev/null and b/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/hue_bg.png differ
diff --git a/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/picker_mask.png b/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/picker_mask.png
new file mode 100644
index 00000000000..f8d91932b37
Binary files /dev/null and b/lib/yui/2.8.1/build/colorpicker/assets/skins/sam/picker_mask.png differ
diff --git a/lib/yui/2.8.1/build/colorpicker/colorpicker-debug.js b/lib/yui/2.8.1/build/colorpicker/colorpicker-debug.js
new file mode 100644
index 00000000000..c4315679758
--- /dev/null
+++ b/lib/yui/2.8.1/build/colorpicker/colorpicker-debug.js
@@ -0,0 +1,1783 @@
+/*
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.1
+*/
+/**
+ * Provides color conversion and validation utils
+ * @class YAHOO.util.Color
+ * @namespace YAHOO.util
+ */
+YAHOO.util.Color = function() {
+
+ var ZERO = "0",
+ isArray = YAHOO.lang.isArray,
+ isNumber = YAHOO.lang.isNumber;
+
+ return {
+
+ /**
+ * Converts 0-1 to 0-255
+ * @method real2dec
+ * @param n {float} the number to convert
+ * @return {int} a number 0-255
+ */
+ real2dec: function(n) {
+ return Math.min(255, Math.round(n*256));
+ },
+
+ /**
+ * Converts HSV (h[0-360], s[0-1]), v[0-1] to RGB [255,255,255]
+ * @method hsv2rgb
+ * @param h {int|[int, float, float]} the hue, or an
+ * array containing all three parameters
+ * @param s {float} the saturation
+ * @param v {float} the value/brightness
+ * @return {[int, int, int]} the red, green, blue values in
+ * decimal.
+ */
+ hsv2rgb: function(h, s, v) {
+
+ if (isArray(h)) {
+ return this.hsv2rgb.call(this, h[0], h[1], h[2]);
+ }
+
+ var r, g, b,
+ i = Math.floor((h/60)%6),
+ f = (h/60)-i,
+ p = v*(1-s),
+ q = v*(1-f*s),
+ t = v*(1-(1-f)*s),
+ fn;
+
+ switch (i) {
+ case 0: r=v; g=t; b=p; break;
+ case 1: r=q; g=v; b=p; break;
+ case 2: r=p; g=v; b=t; break;
+ case 3: r=p; g=q; b=v; break;
+ case 4: r=t; g=p; b=v; break;
+ case 5: r=v; g=p; b=q; break;
+ }
+
+ fn=this.real2dec;
+
+ return [fn(r), fn(g), fn(b)];
+ },
+
+ /**
+ * Converts to RGB [255,255,255] to HSV (h[0-360], s[0-1]), v[0-1]
+ * @method rgb2hsv
+ * @param r {int|[int, int, int]} the red value, or an
+ * array containing all three parameters
+ * @param g {int} the green value
+ * @param b {int} the blue value
+ * @return {[int, float, float]} the value converted to hsv
+ */
+ rgb2hsv: function(r, g, b) {
+
+ if (isArray(r)) {
+ return this.rgb2hsv.apply(this, r);
+ }
+
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ var h,s,
+ min = Math.min(Math.min(r,g),b),
+ max = Math.max(Math.max(r,g),b),
+ delta = max-min,
+ hsv;
+
+ switch (max) {
+ case min: h=0; break;
+ case r: h=60*(g-b)/delta;
+ if (g FFFFFF
+ * @method rgb2hex
+ * @param r {int|[int, int, int]} the red value, or an
+ * array containing all three parameters
+ * @param g {int} the green value
+ * @param b {int} the blue value
+ * @return {string} the hex string
+ */
+ rgb2hex: function(r, g, b) {
+ if (isArray(r)) {
+ return this.rgb2hex.apply(this, r);
+ }
+
+ var f=this.dec2hex;
+ return f(r) + f(g) + f(b);
+ },
+
+ /**
+ * Converts an int 0...255 to hex pair 00...FF
+ * @method dec2hex
+ * @param n {int} the number to convert
+ * @return {string} the hex equivalent
+ */
+ dec2hex: function(n) {
+ n = parseInt(n,10)|0;
+ n = (n > 255 || n < 0) ? 0 : n;
+
+ return (ZERO+n.toString(16)).slice(-2).toUpperCase();
+ },
+
+ /**
+ * Converts a hex pair 00...FF to an int 0...255
+ * @method hex2dec
+ * @param str {string} the hex pair to convert
+ * @return {int} the decimal
+ */
+ hex2dec: function(str) {
+ return parseInt(str,16);
+ },
+
+ /**
+ * Converts a hex string to rgb
+ * @method hex2rgb
+ * @param str {string} the hex string
+ * @return {[int, int, int]} an array containing the rgb values
+ */
+ hex2rgb: function(s) {
+ var f = this.hex2dec;
+ return [f(s.slice(0, 2)), f(s.slice(2, 4)), f(s.slice(4, 6))];
+ },
+
+ /**
+ * Returns the closest websafe color to the supplied rgb value.
+ * @method websafe
+ * @param r {int|[int, int, int]} the red value, or an
+ * array containing all three parameters
+ * @param g {int} the green value
+ * @param b {int} the blue value
+ * @return {[int, int, int]} an array containing the closes
+ * websafe rgb colors.
+ */
+ websafe: function(r, g, b) {
+
+ if (isArray(r)) {
+ return this.websafe.apply(this, r);
+ }
+
+ // returns the closest match [0, 51, 102, 153, 204, 255]
+ var f = function(v) {
+ if (isNumber(v)) {
+ v = Math.min(Math.max(0, v), 255);
+ var i, next;
+ for (i=0; i<256; i=i+51) {
+ next = i+51;
+ if (v >= i && v <= next) {
+ return (v-i > 25) ? next : i;
+ }
+ }
+ YAHOO.log("Error calculating the websafe value for " + v, "warn");
+ }
+
+ return v;
+ };
+
+ return [f(r), f(g), f(b)];
+ }
+ };
+}();
+
+
+/**
+ * The colorpicker module provides a widget for selecting colors
+ * @module colorpicker
+ * @requires yahoo, dom, event, element, slider
+ */
+(function() {
+
+ var _pickercount = 0,
+ util = YAHOO.util,
+ lang = YAHOO.lang,
+ Slider = YAHOO.widget.Slider,
+ Color = util.Color,
+ Dom = util.Dom,
+ Event = util.Event,
+ sub = lang.substitute,
+
+ b = "yui-picker";
+
+
+ /**
+ * A widget to select colors
+ * @namespace YAHOO.widget
+ * @class YAHOO.widget.ColorPicker
+ * @extends YAHOO.util.Element
+ * @constructor
+ * @param {HTMLElement | String | Object} el(optional) The html
+ * element that represents the colorpicker, or the attribute object to use.
+ * An element will be created if none provided.
+ * @param {Object} attr (optional) A key map of the colorpicker's
+ * initial attributes. Ignored if first arg is attributes object.
+ */
+ function ColorPicker(el, attr) {
+ _pickercount = _pickercount + 1;
+ this.logger = new YAHOO.widget.LogWriter("ColorPicker");
+ attr = attr || {};
+ if (arguments.length === 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
+ attr = el; // treat first arg as attr object
+ el = attr.element || null;
+ }
+
+ if (!el && !attr.element) { // create if we dont have one
+ this.logger.log("creating host element");
+ el = this._createHostElement(attr);
+ }
+
+ ColorPicker.superclass.constructor.call(this, el, attr);
+
+ this.initPicker();
+ }
+
+ YAHOO.extend(ColorPicker, YAHOO.util.Element, {
+
+ /**
+ * The element ids used by this control
+ * @property ID
+ * @final
+ */
+ ID : {
+
+ /**
+ * The id for the "red" form field
+ * @property ID.R
+ * @type String
+ * @final
+ * @default yui-picker-r
+ */
+ R: b + "-r",
+
+ /**
+ * The id for the "red" hex pair output
+ * @property ID.R_HEX
+ * @type String
+ * @final
+ * @default yui-picker-rhex
+ */
+ R_HEX: b + "-rhex",
+
+ /**
+ * The id for the "green" form field
+ * @property ID.G
+ * @type String
+ * @final
+ * @default yui-picker-g
+ */
+ G: b + "-g",
+
+ /**
+ * The id for the "green" hex pair output
+ * @property ID.G_HEX
+ * @type String
+ * @final
+ * @default yui-picker-ghex
+ */
+ G_HEX: b + "-ghex",
+
+
+ /**
+ * The id for the "blue" form field
+ * @property ID.B
+ * @type String
+ * @final
+ * @default yui-picker-b
+ */
+ B: b + "-b",
+
+ /**
+ * The id for the "blue" hex pair output
+ * @property ID.B_HEX
+ * @type String
+ * @final
+ * @default yui-picker-bhex
+ */
+ B_HEX: b + "-bhex",
+
+ /**
+ * The id for the "hue" form field
+ * @property ID.H
+ * @type String
+ * @final
+ * @default yui-picker-h
+ */
+ H: b + "-h",
+
+ /**
+ * The id for the "saturation" form field
+ * @property ID.S
+ * @type String
+ * @final
+ * @default yui-picker-s
+ */
+ S: b + "-s",
+
+ /**
+ * The id for the "value" form field
+ * @property ID.V
+ * @type String
+ * @final
+ * @default yui-picker-v
+ */
+ V: b + "-v",
+
+ /**
+ * The id for the picker region slider
+ * @property ID.PICKER_BG
+ * @type String
+ * @final
+ * @default yui-picker-bg
+ */
+ PICKER_BG: b + "-bg",
+
+ /**
+ * The id for the picker region thumb
+ * @property ID.PICKER_THUMB
+ * @type String
+ * @final
+ * @default yui-picker-thumb
+ */
+ PICKER_THUMB: b + "-thumb",
+
+ /**
+ * The id for the hue slider
+ * @property ID.HUE_BG
+ * @type String
+ * @final
+ * @default yui-picker-hue-bg
+ */
+ HUE_BG: b + "-hue-bg",
+
+ /**
+ * The id for the hue thumb
+ * @property ID.HUE_THUMB
+ * @type String
+ * @final
+ * @default yui-picker-hue-thumb
+ */
+ HUE_THUMB: b + "-hue-thumb",
+
+ /**
+ * The id for the hex value form field
+ * @property ID.HEX
+ * @type String
+ * @final
+ * @default yui-picker-hex
+ */
+ HEX: b + "-hex",
+
+ /**
+ * The id for the color swatch
+ * @property ID.SWATCH
+ * @type String
+ * @final
+ * @default yui-picker-swatch
+ */
+ SWATCH: b + "-swatch",
+
+ /**
+ * The id for the websafe color swatch
+ * @property ID.WEBSAFE_SWATCH
+ * @type String
+ * @final
+ * @default yui-picker-websafe-swatch
+ */
+ WEBSAFE_SWATCH: b + "-websafe-swatch",
+
+ /**
+ * The id for the control details
+ * @property ID.CONTROLS
+ * @final
+ * @default yui-picker-controls
+ */
+ CONTROLS: b + "-controls",
+
+ /**
+ * The id for the rgb controls
+ * @property ID.RGB_CONTROLS
+ * @final
+ * @default yui-picker-rgb-controls
+ */
+ RGB_CONTROLS: b + "-rgb-controls",
+
+ /**
+ * The id for the hsv controls
+ * @property ID.HSV_CONTROLS
+ * @final
+ * @default yui-picker-hsv-controls
+ */
+ HSV_CONTROLS: b + "-hsv-controls",
+
+ /**
+ * The id for the hsv controls
+ * @property ID.HEX_CONTROLS
+ * @final
+ * @default yui-picker-hex-controls
+ */
+ HEX_CONTROLS: b + "-hex-controls",
+
+ /**
+ * The id for the hex summary
+ * @property ID.HEX_SUMMARY
+ * @final
+ * @default yui-picker-hex-summary
+ */
+ HEX_SUMMARY: b + "-hex-summary",
+
+ /**
+ * The id for the controls section header
+ * @property ID.CONTROLS_LABEL
+ * @final
+ * @default yui-picker-controls-label
+ */
+ CONTROLS_LABEL: b + "-controls-label"
+ },
+
+ /**
+ * Constants for any script-generated messages. The values here
+ * are the default messages. They can be updated by providing
+ * the complete list to the constructor for the "txt" attribute.
+ * @property TXT
+ * @final
+ */
+ TXT : {
+ ILLEGAL_HEX: "Illegal hex value entered",
+ SHOW_CONTROLS: "Show color details",
+ HIDE_CONTROLS: "Hide color details",
+ CURRENT_COLOR: "Currently selected color: {rgb}",
+ CLOSEST_WEBSAFE: "Closest websafe color: {rgb}. Click to select.",
+ R: "R",
+ G: "G",
+ B: "B",
+ H: "H",
+ S: "S",
+ V: "V",
+ HEX: "#",
+ DEG: "\u00B0",
+ PERCENT: "%"
+ },
+
+ /**
+ * Constants for the default image locations for img tags that are
+ * generated by the control. They can be modified by passing the
+ * complete list to the contructor for the "images" attribute
+ * @property IMAGE
+ * @final
+ */
+ IMAGE : {
+ PICKER_THUMB: "../../build/colorpicker/assets/picker_thumb.png",
+ HUE_THUMB: "../../build/colorpicker/assets/hue_thumb.png"
+ },
+
+ /**
+ * Constants for the control's default default values
+ * @property DEFAULT
+ * @final
+ */
+ DEFAULT : {
+ PICKER_SIZE: 180
+ },
+
+ /**
+ * Constants for the control's configuration attributes
+ * @property OPT
+ * @final
+ */
+ OPT : {
+ HUE : "hue",
+ SATURATION : "saturation",
+ VALUE : "value",
+ RED : "red",
+ GREEN : "green",
+ BLUE : "blue",
+ HSV : "hsv",
+ RGB : "rgb",
+ WEBSAFE : "websafe",
+ HEX : "hex",
+ PICKER_SIZE : "pickersize",
+ SHOW_CONTROLS : "showcontrols",
+ SHOW_RGB_CONTROLS : "showrgbcontrols",
+ SHOW_HSV_CONTROLS : "showhsvcontrols",
+ SHOW_HEX_CONTROLS : "showhexcontrols",
+ SHOW_HEX_SUMMARY : "showhexsummary",
+ SHOW_WEBSAFE : "showwebsafe",
+ CONTAINER : "container",
+ IDS : "ids",
+ ELEMENTS : "elements",
+ TXT : "txt",
+ IMAGES : "images",
+ ANIMATE : "animate"
+ },
+
+ /**
+ * Flag to allow individual UI updates to forego animation if available.
+ * True during construction for initial thumb placement. Set to false
+ * after that.
+ *
+ * @property skipAnim
+ * @type Boolean
+ * @default true
+ */
+ skipAnim : true,
+
+ /**
+ * Creates the host element if it doesn't exist
+ * @method _createHostElement
+ * @protected
+ */
+ _createHostElement : function () {
+ var el = document.createElement('div');
+
+ if (this.CSS.BASE) {
+ el.className = this.CSS.BASE;
+ }
+
+ return el;
+ },
+
+ /**
+ * Moves the hue slider into the position dictated by the current state
+ * of the control
+ * @method _updateHueSlider
+ * @protected
+ */
+ _updateHueSlider : function() {
+ var size = this.get(this.OPT.PICKER_SIZE),
+ h = this.get(this.OPT.HUE);
+
+ h = size - Math.round(h / 360 * size);
+
+ // 0 is at the top and bottom of the hue slider. Always go to
+ // the top so we don't end up sending the thumb to the bottom
+ // when the value didn't actually change (e.g., a conversion
+ // produced 360 instead of 0 and the value was already 0).
+ if (h === size) {
+ h = 0;
+ }
+ this.logger.log("Hue slider is being set to " + h);
+
+ this.hueSlider.setValue(h, this.skipAnim);
+ },
+
+ /**
+ * Moves the picker slider into the position dictated by the current state
+ * of the control
+ * @method _updatePickerSlider
+ * @protected
+ */
+ _updatePickerSlider : function() {
+ var size = this.get(this.OPT.PICKER_SIZE),
+ s = this.get(this.OPT.SATURATION),
+ v = this.get(this.OPT.VALUE);
+
+ s = Math.round(s * size / 100);
+ v = Math.round(size - (v * size / 100));
+
+ this.logger.log("Setting picker slider to " + [s, v]);
+
+ this.pickerSlider.setRegionValue(s, v, this.skipAnim);
+ },
+
+ /**
+ * Moves the sliders into the position dictated by the current state
+ * of the control
+ * @method _updateSliders
+ * @protected
+ */
+ _updateSliders : function() {
+ this._updateHueSlider();
+ this._updatePickerSlider();
+ },
+
+ /**
+ * Sets the control to the specified rgb value and
+ * moves the sliders to the proper positions
+ * @method setValue
+ * @param rgb {[int, int, int]} the rgb value
+ * @param silent {boolean} whether or not to fire the change event
+ */
+ setValue : function(rgb, silent) {
+ silent = (silent) || false;
+ this.set(this.OPT.RGB, rgb, silent);
+ this._updateSliders();
+ },
+
+ /**
+ * The hue slider
+ * @property hueSlider
+ * @type YAHOO.widget.Slider
+ */
+ hueSlider : null,
+
+ /**
+ * The picker region
+ * @property pickerSlider
+ * @type YAHOO.widget.Slider
+ */
+ pickerSlider : null,
+
+ /**
+ * Translates the slider value into hue, int[0,359]
+ * @method _getH
+ * @protected
+ * @return {int} the hue from 0 to 359
+ */
+ _getH : function() {
+ var size = this.get(this.OPT.PICKER_SIZE),
+ h = (size - this.hueSlider.getValue()) / size;
+ h = Math.round(h*360);
+ return (h === 360) ? 0 : h;
+ },
+
+ /**
+ * Translates the slider value into saturation, int[0,1], left to right
+ * @method _getS
+ * @protected
+ * @return {int} the saturation from 0 to 1
+ */
+ _getS : function() {
+ return this.pickerSlider.getXValue() / this.get(this.OPT.PICKER_SIZE);
+ },
+
+ /**
+ * Translates the slider value into value/brightness, int[0,1], top
+ * to bottom
+ * @method _getV
+ * @protected
+ * @return {int} the value from 0 to 1
+ */
+ _getV : function() {
+ var size = this.get(this.OPT.PICKER_SIZE);
+ return (size - this.pickerSlider.getYValue()) / size;
+ },
+
+ /**
+ * Updates the background of the swatch with the current rbg value.
+ * Also updates the websafe swatch to the closest websafe color
+ * @method _updateSwatch
+ * @protected
+ */
+ _updateSwatch : function() {
+ var rgb = this.get(this.OPT.RGB),
+ websafe = this.get(this.OPT.WEBSAFE),
+ el = this.getElement(this.ID.SWATCH),
+ color = rgb.join(","),
+ txt = this.get(this.OPT.TXT);
+
+ Dom.setStyle(el, "background-color", "rgb(" + color + ")");
+ el.title = sub(txt.CURRENT_COLOR, {
+ "rgb": "#" + this.get(this.OPT.HEX)
+ });
+
+
+ el = this.getElement(this.ID.WEBSAFE_SWATCH);
+ color = websafe.join(",");
+
+ Dom.setStyle(el, "background-color", "rgb(" + color + ")");
+ el.title = sub(txt.CLOSEST_WEBSAFE, {
+ "rgb": "#" + Color.rgb2hex(websafe)
+ });
+
+ },
+
+ /**
+ * Reads the sliders and converts the values to RGB, updating the
+ * internal state for all the individual form fields
+ * @method _getValuesFromSliders
+ * @protected
+ */
+ _getValuesFromSliders : function() {
+ this.logger.log("hsv " + [this._getH(),this._getS(),this._getV()]);
+ this.set(this.OPT.RGB, Color.hsv2rgb(this._getH(), this._getS(), this._getV()));
+ },
+
+ /**
+ * Updates the form field controls with the state data contained
+ * in the control.
+ * @method _updateFormFields
+ * @protected
+ */
+ _updateFormFields : function() {
+ this.getElement(this.ID.H).value = this.get(this.OPT.HUE);
+ this.getElement(this.ID.S).value = this.get(this.OPT.SATURATION);
+ this.getElement(this.ID.V).value = this.get(this.OPT.VALUE);
+ this.getElement(this.ID.R).value = this.get(this.OPT.RED);
+ this.getElement(this.ID.R_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.RED));
+ this.getElement(this.ID.G).value = this.get(this.OPT.GREEN);
+ this.getElement(this.ID.G_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.GREEN));
+ this.getElement(this.ID.B).value = this.get(this.OPT.BLUE);
+ this.getElement(this.ID.B_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.BLUE));
+ this.getElement(this.ID.HEX).value = this.get(this.OPT.HEX);
+ },
+
+ /**
+ * Event handler for the hue slider.
+ * @method _onHueSliderChange
+ * @param newOffset {int} pixels from the start position
+ * @protected
+ */
+ _onHueSliderChange : function(newOffset) {
+ this.logger.log("hue update: " + newOffset , "warn");
+
+ var h = this._getH(),
+ rgb = Color.hsv2rgb(h, 1, 1),
+ styleDef = "rgb(" + rgb.join(",") + ")";
+
+ this.set(this.OPT.HUE, h, true);
+
+ // set picker background to the hue
+ Dom.setStyle(this.getElement(this.ID.PICKER_BG), "background-color", styleDef);
+
+ if (this.hueSlider.valueChangeSource !== Slider.SOURCE_SET_VALUE) {
+ this._getValuesFromSliders();
+ }
+
+ this._updateFormFields();
+ this._updateSwatch();
+ },
+
+ /**
+ * Event handler for the picker slider, which controls the
+ * saturation and value/brightness.
+ * @method _onPickerSliderChange
+ * @param newOffset {{x: int, y: int}} x/y pixels from the start position
+ * @protected
+ */
+ _onPickerSliderChange : function(newOffset) {
+ this.logger.log(sub("picker update [{x}, {y}]", newOffset));
+
+ var s=this._getS(), v=this._getV();
+ this.set(this.OPT.SATURATION, Math.round(s*100), true);
+ this.set(this.OPT.VALUE, Math.round(v*100), true);
+
+ if (this.pickerSlider.valueChangeSource !== Slider.SOURCE_SET_VALUE) {
+ this._getValuesFromSliders();
+ }
+
+ this._updateFormFields();
+ this._updateSwatch();
+ },
+
+
+ /**
+ * Key map to well-known commands for txt field input
+ * @method _getCommand
+ * @param e {Event} the keypress or keydown event
+ * @return {int} a command code
+ *