/*
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.
 * <p>Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);</p>
 * @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.
 * <p>Usage: <code>var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut);</code> Color values can be specified with either 112233, #112233, 
 * [255,255,255], or rgb(255,255,255)</p>
 * @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.
 * <p>Usage: <code>var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p>
 * @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.
 * <p>Usage: <code>var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p>
 * @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"});
/*
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
*/


/**
* @module menu
* @description <p>The Menu family of components features a collection of 
* controls that make it easy to add menus to your website or web application.  
* With the Menu Controls you can create website fly-out menus, customized 
* context menus, or application-style menu bars with just a small amount of 
* scripting.</p><p>The Menu family of controls features:</p>
* <ul>
*    <li>Keyboard and mouse navigation.</li>
*    <li>A rich event model that provides access to all of a menu's 
*    interesting moments.</li>
*    <li>Support for 
*    <a href="http://en.wikipedia.org/wiki/Progressive_Enhancement">Progressive
*    Enhancement</a>; Menus can be created from simple, 
*    semantic markup on the page or purely through JavaScript.</li>
* </ul>
* @title Menu
* @namespace YAHOO.widget
* @requires Event, Dom, Container
*/
(function () {

    var UA = YAHOO.env.ua,
		Dom = YAHOO.util.Dom,
	    Event = YAHOO.util.Event,
	    Lang = YAHOO.lang,

		_DIV = "DIV",
    	_HD = "hd",
    	_BD = "bd",
    	_FT = "ft",
    	_LI = "LI",
    	_DISABLED = "disabled",
		_MOUSEOVER = "mouseover",
		_MOUSEOUT = "mouseout",
		_MOUSEDOWN = "mousedown",
		_MOUSEUP = "mouseup",
		_CLICK = "click",
		_KEYDOWN = "keydown",
		_KEYUP = "keyup",
		_KEYPRESS = "keypress",
		_CLICK_TO_HIDE = "clicktohide",
		_POSITION = "position", 
		_DYNAMIC = "dynamic",
		_SHOW_DELAY = "showdelay",
		_SELECTED = "selected",
		_VISIBLE = "visible",
		_UL = "UL",
		_MENUMANAGER = "MenuManager";


    /**
    * Singleton that manages a collection of all menus and menu items.  Listens 
    * for DOM events at the document level and dispatches the events to the 
    * corresponding menu or menu item.
    *
    * @namespace YAHOO.widget
    * @class MenuManager
    * @static
    */
    YAHOO.widget.MenuManager = function () {
    
        // Private member variables
    
    
        // Flag indicating if the DOM event handlers have been attached
    
        var m_bInitializedEventHandlers = false,
    
    
        // Collection of menus

        m_oMenus = {},


        // Collection of visible menus
    
        m_oVisibleMenus = {},
    
    
        //  Collection of menu items 

        m_oItems = {},


        // Map of DOM event types to their equivalent CustomEvent types
        
        m_oEventTypes = {
            "click": "clickEvent",
            "mousedown": "mouseDownEvent",
            "mouseup": "mouseUpEvent",
            "mouseover": "mouseOverEvent",
            "mouseout": "mouseOutEvent",
            "keydown": "keyDownEvent",
            "keyup": "keyUpEvent",
            "keypress": "keyPressEvent",
            "focus": "focusEvent",
            "focusin": "focusEvent",
            "blur": "blurEvent",
            "focusout": "blurEvent"
        },
    
    
        m_oFocusedMenuItem = null;
    
    
    
        // Private methods
    
    
        /**
        * @method getMenuRootElement
        * @description Finds the root DIV node of a menu or the root LI node of 
        * a menu item.
        * @private
        * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-58190037">HTMLElement</a>} p_oElement Object 
        * specifying an HTML element.
        */
        function getMenuRootElement(p_oElement) {
        
            var oParentNode,
            	returnVal;
    
            if (p_oElement && p_oElement.tagName) {
            
                switch (p_oElement.tagName.toUpperCase()) {
                        
                case _DIV:
    
                    oParentNode = p_oElement.parentNode;
    
                    // Check if the DIV is the inner "body" node of a menu

                    if ((
                            Dom.hasClass(p_oElement, _HD) ||
                            Dom.hasClass(p_oElement, _BD) ||
                            Dom.hasClass(p_oElement, _FT)
                        ) && 
                        oParentNode && 
                        oParentNode.tagName && 
                        oParentNode.tagName.toUpperCase() == _DIV) {
                    
                        returnVal = oParentNode;
                    
                    }
                    else {
                    
                        returnVal = p_oElement;
                    
                    }
                
                    break;

                case _LI:
    
                    returnVal = p_oElement;
                    
                    break;

                default:
    
                    oParentNode = p_oElement.parentNode;
    
                    if (oParentNode) {
                    
                        returnVal = getMenuRootElement(oParentNode);
                    
                    }
                
                    break;
                
                }
    
            }
            
            return returnVal;
            
        }
    
    
    
        // Private event handlers
    
    
        /**
        * @method onDOMEvent
        * @description Generic, global event handler for all of a menu's 
        * DOM-based events.  This listens for events against the document 
        * object.  If the target of a given event is a member of a menu or 
        * menu item's DOM, the instance's corresponding Custom Event is fired.
        * @private
        * @param {Event} p_oEvent Object representing the DOM event object  
        * passed back by the event utility (YAHOO.util.Event).
        */
        function onDOMEvent(p_oEvent) {
    
            // Get the target node of the DOM event
        
            var oTarget = Event.getTarget(p_oEvent),
                
            // See if the target of the event was a menu, or a menu item
    
            oElement = getMenuRootElement(oTarget),
			bFireEvent = true,
			sEventType = p_oEvent.type,
            sCustomEventType,
            sTagName,
            sId,
            oMenuItem,
            oMenu; 
    
    
            if (oElement) {
    
                sTagName = oElement.tagName.toUpperCase();
        
                if (sTagName == _LI) {
            
                    sId = oElement.id;
            
                    if (sId && m_oItems[sId]) {
            
                        oMenuItem = m_oItems[sId];
                        oMenu = oMenuItem.parent;
            
                    }
                
                }
                else if (sTagName == _DIV) {
                
                    if (oElement.id) {
                    
                        oMenu = m_oMenus[oElement.id];
                    
                    }
                
                }
    
            }
    
    
            if (oMenu) {
    
                sCustomEventType = m_oEventTypes[sEventType];

				/*
					There is an inconsistency between Firefox for Mac OS X and 
					Firefox Windows & Linux regarding the triggering of the 
					display of the browser's context menu and the subsequent 
					firing of the "click" event. In Firefox for Windows & Linux, 
					when the user triggers the display of the browser's context 
					menu the "click" event also fires for the document object, 
					even though the "click" event did not fire for the element 
					that was the original target of the "contextmenu" event. 
					This is unique to Firefox on Windows & Linux.  For all 
					other A-Grade browsers, including Firefox for Mac OS X, the 
					"click" event doesn't fire for the document object. 

					This bug in Firefox for Windows affects Menu, as Menu 
					instances listen for events at the document level and 
					dispatches Custom Events of the same name.  Therefore users
					of Menu will get an unwanted firing of the "click" 
					custom event.  The following line fixes this bug.
				*/
				


				if (sEventType == "click" && 
					(UA.gecko && oMenu.platform != "mac") && 
					p_oEvent.button > 0) {

					bFireEvent = false;

				}
    
                // Fire the Custom Event that corresponds the current DOM event    
        
                if (bFireEvent && oMenuItem && !oMenuItem.cfg.getProperty(_DISABLED)) {
                    oMenuItem[sCustomEventType].fire(p_oEvent);                   
                }
        
				if (bFireEvent) {
                	oMenu[sCustomEventType].fire(p_oEvent, oMenuItem);
				}
            
            }
            else if (sEventType == _MOUSEDOWN) {
    
                /*
                    If the target of the event wasn't a menu, hide all 
                    dynamically positioned menus
                */
                
                for (var i in m_oVisibleMenus) {
        
                    if (Lang.hasOwnProperty(m_oVisibleMenus, i)) {
        
                        oMenu = m_oVisibleMenus[i];

                        if (oMenu.cfg.getProperty(_CLICK_TO_HIDE) && 
                            !(oMenu instanceof YAHOO.widget.MenuBar) && 
                            oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) {

                            oMenu.hide();

							//	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();
							}
        
                        }
                        else {
                            
							if (oMenu.cfg.getProperty(_SHOW_DELAY) > 0) {
							
								oMenu._cancelShowDelay();
							
							}


							if (oMenu.activeItem) {
						
								oMenu.activeItem.blur();
								oMenu.activeItem.cfg.setProperty(_SELECTED, false);
						
								oMenu.activeItem = null;            
						
							}
        
                        }
        
                    }
        
                } 
    
            }
            
        }
    
    
        /**
        * @method onMenuDestroy
        * @description "destroy" event handler for a 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.
        * @param {YAHOO.widget.Menu} p_oMenu The menu that fired the event.
        */
        function onMenuDestroy(p_sType, p_aArgs, p_oMenu) {
    
            if (m_oMenus[p_oMenu.id]) {
    
                this.removeMenu(p_oMenu);
    
            }
    
        }
    
    
        /**
        * @method onMenuFocus
        * @description "focus" event handler for a MenuItem instance.
        * @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.
        */
        function onMenuFocus(p_sType, p_aArgs) {
    
            var oItem = p_aArgs[1];
    
            if (oItem) {
    
                m_oFocusedMenuItem = oItem;
            
            }
    
        }
    
    
        /**
        * @method onMenuBlur
        * @description "blur" event handler for a MenuItem instance.
        * @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.
        */
        function onMenuBlur(p_sType, p_aArgs) {
    
            m_oFocusedMenuItem = null;
    
        }

    
        /**
        * @method onMenuVisibleConfigChange
        * @description Event handler for when the "visible" configuration  
        * property of a Menu instance changes.
        * @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.
        */
        function onMenuVisibleConfigChange(p_sType, p_aArgs) {
    
            var bVisible = p_aArgs[0],
                sId = this.id;
            
            if (bVisible) {
    
                m_oVisibleMenus[sId] = this;
                
            
            }
            else if (m_oVisibleMenus[sId]) {
            
                delete m_oVisibleMenus[sId];
                
            
            }
        
        }
    
    
        /**
        * @method onItemDestroy
        * @description "destroy" event handler for a MenuItem instance.
        * @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.
        */
        function onItemDestroy(p_sType, p_aArgs) {
    
            removeItem(this);
    
        }


        /**
        * @method removeItem
        * @description Removes a MenuItem instance from the MenuManager's collection of MenuItems.
        * @private
        * @param {MenuItem} p_oMenuItem The MenuItem instance to be removed.
        */    
        function removeItem(p_oMenuItem) {

            var sId = p_oMenuItem.id;
    
            if (sId && m_oItems[sId]) {
    
                if (m_oFocusedMenuItem == p_oMenuItem) {
    
                    m_oFocusedMenuItem = null;
    
                }
    
                delete m_oItems[sId];
                
                p_oMenuItem.destroyEvent.unsubscribe(onItemDestroy);
    
    
            }

        }
    
    
        /**
        * @method onItemAdded
        * @description "itemadded" event handler for a Menu instance.
        * @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.
        */
        function onItemAdded(p_sType, p_aArgs) {
    
            var oItem = p_aArgs[0],
                sId;
    
            if (oItem instanceof YAHOO.widget.MenuItem) { 
    
                sId = oItem.id;
        
                if (!m_oItems[sId]) {
            
                    m_oItems[sId] = oItem;
        
                    oItem.destroyEvent.subscribe(onItemDestroy);
        
        
                }
    
            }
        
        }
    
    
        return {
    
            // Privileged methods
    
    
            /**
            * @method addMenu
            * @description Adds a menu to the collection of known menus.
            * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu  
            * instance to be added.
            */
            addMenu: function (p_oMenu) {
    
                var oDoc;
    
                if (p_oMenu instanceof YAHOO.widget.Menu && p_oMenu.id && 
                    !m_oMenus[p_oMenu.id]) {
        
                    m_oMenus[p_oMenu.id] = p_oMenu;
                
            
                    if (!m_bInitializedEventHandlers) {
            
                        oDoc = document;
                
                        Event.on(oDoc, _MOUSEOVER, onDOMEvent, this, true);
                        Event.on(oDoc, _MOUSEOUT, onDOMEvent, this, true);
                        Event.on(oDoc, _MOUSEDOWN, onDOMEvent, this, true);
                        Event.on(oDoc, _MOUSEUP, onDOMEvent, this, true);
                        Event.on(oDoc, _CLICK, onDOMEvent, this, true);
                        Event.on(oDoc, _KEYDOWN, onDOMEvent, this, true);
                        Event.on(oDoc, _KEYUP, onDOMEvent, this, true);
                        Event.on(oDoc, _KEYPRESS, onDOMEvent, this, true);
    
						Event.onFocus(oDoc, onDOMEvent, this, true);
						Event.onBlur(oDoc, onDOMEvent, this, true);						
    
                        m_bInitializedEventHandlers = true;
                        
            
                    }
            
                    p_oMenu.cfg.subscribeToConfigEvent(_VISIBLE, onMenuVisibleConfigChange);
                    p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this);
                    p_oMenu.itemAddedEvent.subscribe(onItemAdded);
                    p_oMenu.focusEvent.subscribe(onMenuFocus);
                    p_oMenu.blurEvent.subscribe(onMenuBlur);
        
        
                }
        
            },
    
        
            /**
            * @method removeMenu
            * @description Removes a menu from the collection of known menus.
            * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu  
            * instance to be removed.
            */
            removeMenu: function (p_oMenu) {
    
                var sId,
                    aItems,
                    i;
        
                if (p_oMenu) {
    
                    sId = p_oMenu.id;
        
                    if ((sId in m_oMenus) && (m_oMenus[sId] == p_oMenu)) {

                        // Unregister each menu item

                        aItems = p_oMenu.getItems();

                        if (aItems && aItems.length > 0) {

                            i = aItems.length - 1;

                            do {

                                removeItem(aItems[i]);

                            }
                            while (i--);

                        }


                        // Unregister the menu

                        delete m_oMenus[sId];
            
        

                        /*
                             Unregister the menu from the collection of 
                             visible menus
                        */

                        if ((sId in m_oVisibleMenus) && (m_oVisibleMenus[sId] == p_oMenu)) {
            
                            delete m_oVisibleMenus[sId];
                            
       
                        }


                        // Unsubscribe event listeners

                        if (p_oMenu.cfg) {

                            p_oMenu.cfg.unsubscribeFromConfigEvent(_VISIBLE, 
                                onMenuVisibleConfigChange);
                            
                        }

                        p_oMenu.destroyEvent.unsubscribe(onMenuDestroy, 
                            p_oMenu);
                
                        p_oMenu.itemAddedEvent.unsubscribe(onItemAdded);
                        p_oMenu.focusEvent.unsubscribe(onMenuFocus);
                        p_oMenu.blurEvent.unsubscribe(onMenuBlur);

                    }
                
                }
    
            },
        
        
            /**
            * @method hideVisible
            * @description Hides all visible, dynamically positioned menus 
            * (excluding instances of YAHOO.widget.MenuBar).
            */
            hideVisible: function () {
        
                var oMenu;
        
                for (var i in m_oVisibleMenus) {
        
                    if (Lang.hasOwnProperty(m_oVisibleMenus, i)) {
        
                        oMenu = m_oVisibleMenus[i];
        
                        if (!(oMenu instanceof YAHOO.widget.MenuBar) && 
                            oMenu.cfg.getProperty(_POSITION) == _DYNAMIC) {
        
                            oMenu.hide();
        
                        }
        
                    }
        
                }        
    
            },


            /**
            * @method getVisible
            * @description Returns a collection of all visible menus registered
            * with the menu manger.
            * @return {Object}
            */
            getVisible: function () {
            
                return m_oVisibleMenus;
            
            },

    
            /**
            * @method getMenus
            * @description Returns a collection of all menus registered with the 
            * menu manger.
            * @return {Object}
            */
            getMenus: function () {
    
                return m_oMenus;
            
            },
    
    
            /**
            * @method getMenu
            * @description Returns a menu with the specified id.
            * @param {String} p_sId String specifying the id of the 
            * <code>&#60;div&#62;</code> element representing the menu to
            * be retrieved.
            * @return {YAHOO.widget.Menu}
            */
            getMenu: function (p_sId) {
                
                var returnVal;
                
                if (p_sId in m_oMenus) {
                
					returnVal = m_oMenus[p_sId];
				
				}
            
            	return returnVal;
            
            },
    
    
            /**
            * @method getMenuItem
            * @description Returns a menu item with the specified id.
            * @param {String} p_sId String specifying the id of the 
            * <code>&#60;li&#62;</code> element representing the menu item to
            * be retrieved.
            * @return {YAHOO.widget.MenuItem}
            */
            getMenuItem: function (p_sId) {
    
    			var returnVal;
    
    			if (p_sId in m_oItems) {
    
					returnVal = m_oItems[p_sId];
				
				}
				
				return returnVal;
            
            },


            /**
            * @method getMenuItemGroup
            * @description Returns an array of menu item instances whose 
            * corresponding <code>&#60;li&#62;</code> elements are child 
            * nodes of the <code>&#60;ul&#62;</code> element with the 
            * specified id.
            * @param {String} p_sId String specifying the id of the 
            * <code>&#60;ul&#62;</code> element representing the group of 
            * menu items to be retrieved.
            * @return {Array}
            */
            getMenuItemGroup: function (p_sId) {

                var oUL = Dom.get(p_sId),
                    aItems,
                    oNode,
                    oItem,
                    sId,
                    returnVal;
    

                if (oUL && oUL.tagName && oUL.tagName.toUpperCase() == _UL) {

                    oNode = oUL.firstChild;

                    if (oNode) {

                        aItems = [];
                        
                        do {

                            sId = oNode.id;

                            if (sId) {
                            
                                oItem = this.getMenuItem(sId);
                                
                                if (oItem) {
                                
                                    aItems[aItems.length] = oItem;
                                
                                }
                            
                            }
                        
                        }
                        while ((oNode = oNode.nextSibling));


                        if (aItems.length > 0) {

                            returnVal = aItems;
                        
                        }

                    }
                
                }

				return returnVal;
            
            },

    
            /**
            * @method getFocusedMenuItem
            * @description Returns a reference to the menu item that currently 
            * has focus.
            * @return {YAHOO.widget.MenuItem}
            */
            getFocusedMenuItem: function () {
    
                return m_oFocusedMenuItem;
    
            },
    
    
            /**
            * @method getFocusedMenu
            * @description Returns a reference to the menu that currently 
            * has focus.
            * @return {YAHOO.widget.Menu}
            */
            getFocusedMenu: function () {

				var returnVal;
    
                if (m_oFocusedMenuItem) {
    
                    returnVal = m_oFocusedMenuItem.parent.getRoot();
                
                }
    
    			return returnVal;
    
            },
    
        
            /**
            * @method toString
            * @description Returns a string representing the menu manager.
            * @return {String}
            */
            toString: function () {
            
                return _MENUMANAGER;
            
            }
    
        };
    
    }();

})();



(function () {

	var Lang = YAHOO.lang,

	// String constants
	
		_MENU = "Menu",
		_DIV_UPPERCASE = "DIV",
		_DIV_LOWERCASE = "div",
		_ID = "id",
		_SELECT = "SELECT",
		_XY = "xy",
		_Y = "y",
		_UL_UPPERCASE = "UL",
		_UL_LOWERCASE = "ul",
		_FIRST_OF_TYPE = "first-of-type",
		_LI = "LI",
		_OPTGROUP = "OPTGROUP",
		_OPTION = "OPTION",
		_DISABLED = "disabled",
		_NONE = "none",
		_SELECTED = "selected",
		_GROUP_INDEX = "groupindex",
		_INDEX = "index",
		_SUBMENU = "submenu",
		_VISIBLE = "visible",
		_HIDE_DELAY = "hidedelay",
		_POSITION = "position",
		_DYNAMIC = "dynamic",
		_STATIC = "static",
		_DYNAMIC_STATIC = _DYNAMIC + "," + _STATIC,
		_URL = "url",
		_HASH = "#",
		_TARGET = "target",
		_MAX_HEIGHT = "maxheight",
        _TOP_SCROLLBAR = "topscrollbar",
        _BOTTOM_SCROLLBAR = "bottomscrollbar",
        _UNDERSCORE = "_",
		_TOP_SCROLLBAR_DISABLED = _TOP_SCROLLBAR + _UNDERSCORE + _DISABLED,
		_BOTTOM_SCROLLBAR_DISABLED = _BOTTOM_SCROLLBAR + _UNDERSCORE + _DISABLED,
		_MOUSEMOVE = "mousemove",
		_SHOW_DELAY = "showdelay",
		_SUBMENU_HIDE_DELAY = "submenuhidedelay",
		_IFRAME = "iframe",
		_CONSTRAIN_TO_VIEWPORT = "constraintoviewport",
		_PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap",
		_SUBMENU_ALIGNMENT = "submenualignment",
		_AUTO_SUBMENU_DISPLAY = "autosubmenudisplay",
		_CLICK_TO_HIDE = "clicktohide",
		_CONTAINER = "container",
		_SCROLL_INCREMENT = "scrollincrement",
		_MIN_SCROLL_HEIGHT = "minscrollheight",
		_CLASSNAME = "classname",
		_SHADOW = "shadow",
		_KEEP_OPEN = "keepopen",
		_HD = "hd",
		_HAS_TITLE = "hastitle",
		_CONTEXT = "context",
		_EMPTY_STRING = "",
		_MOUSEDOWN = "mousedown",
		_KEYDOWN = "keydown",
		_HEIGHT = "height",
		_WIDTH = "width",
		_PX = "px",
		_EFFECT = "effect",
		_MONITOR_RESIZE = "monitorresize",
		_DISPLAY = "display",
		_BLOCK = "block",
		_VISIBILITY = "visibility",
		_ABSOLUTE = "absolute",
		_ZINDEX = "zindex",
		_YUI_MENU_BODY_SCROLLED = "yui-menu-body-scrolled",
		_NON_BREAKING_SPACE = "&#32;",
		_SPACE = " ",
		_MOUSEOVER = "mouseover",
		_MOUSEOUT = "mouseout",
        _ITEM_ADDED = "itemAdded",
        _ITEM_REMOVED = "itemRemoved",
        _HIDDEN = "hidden",
        _YUI_MENU_SHADOW = "yui-menu-shadow",
        _YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + "-visible",
        _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE = _YUI_MENU_SHADOW + _SPACE + _YUI_MENU_SHADOW_VISIBLE;


/**
* The Menu class creates a container that holds a vertical list representing 
* a set of options or commands.  Menu is the base class for all 
* menu containers. 
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;div&#62;</code> element of the menu.
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;select&#62;</code> element to be used as the data source 
* for the menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 
* specifying the <code>&#60;div&#62;</code> element of the menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement 
* Object specifying the <code>&#60;select&#62;</code> element to be used as 
* the data source for the menu.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the menu. See configuration class documentation for 
* more details.
* @namespace YAHOO.widget
* @class Menu
* @constructor
* @extends YAHOO.widget.Overlay
*/
YAHOO.widget.Menu = function (p_oElement, p_oConfig) {

    if (p_oConfig) {

        this.parent = p_oConfig.parent;
        this.lazyLoad = p_oConfig.lazyLoad || p_oConfig.lazyload;
        this.itemData = p_oConfig.itemData || p_oConfig.itemdata;

    }


    YAHOO.widget.Menu.superclass.constructor.call(this, p_oElement, p_oConfig);

};



/**
* @method checkPosition
* @description Checks to make sure that the value of the "position" property 
* is one of the supported strings. Returns true if the position is supported.
* @private
* @param {Object} p_sPosition String specifying the position of the menu.
* @return {Boolean}
*/
function checkPosition(p_sPosition) {

	var returnVal = false;

    if (Lang.isString(p_sPosition)) {

        returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1);

    }

	return returnVal;

}


var Dom = YAHOO.util.Dom,
    Event = YAHOO.util.Event,
    Module = YAHOO.widget.Module,
    Overlay = YAHOO.widget.Overlay,
    Menu = YAHOO.widget.Menu,
    MenuManager = YAHOO.widget.MenuManager,
    CustomEvent = YAHOO.util.CustomEvent,
    UA = YAHOO.env.ua,
    
    m_oShadowTemplate,

	bFocusListenerInitialized = false,

	oFocusedElement,

	EVENT_TYPES = [
    
		["mouseOverEvent", _MOUSEOVER],
		["mouseOutEvent", _MOUSEOUT],
		["mouseDownEvent", _MOUSEDOWN],
		["mouseUpEvent", "mouseup"],
		["clickEvent", "click"],
		["keyPressEvent", "keypress"],
		["keyDownEvent", _KEYDOWN],
		["keyUpEvent", "keyup"],
		["focusEvent", "focus"],
		["blurEvent", "blur"],
		["itemAddedEvent", _ITEM_ADDED],
		["itemRemovedEvent", _ITEM_REMOVED]

	],

	VISIBLE_CONFIG =  { 
		key: _VISIBLE, 
		value: false, 
		validator: Lang.isBoolean
	}, 

	CONSTRAIN_TO_VIEWPORT_CONFIG =  {
		key: _CONSTRAIN_TO_VIEWPORT, 
		value: true, 
		validator: Lang.isBoolean, 
		supercedes: [_IFRAME,"x",_Y,_XY]
	}, 

	PREVENT_CONTEXT_OVERLAP_CONFIG =  {
		key: _PREVENT_CONTEXT_OVERLAP,
		value: true,
		validator: Lang.isBoolean,  
		supercedes: [_CONSTRAIN_TO_VIEWPORT]
	},

	POSITION_CONFIG =  { 
		key: _POSITION, 
		value: _DYNAMIC, 
		validator: checkPosition, 
		supercedes: [_VISIBLE, _IFRAME]
	}, 

	SUBMENU_ALIGNMENT_CONFIG =  { 
		key: _SUBMENU_ALIGNMENT, 
		value: ["tl","tr"]
	},

	AUTO_SUBMENU_DISPLAY_CONFIG =  { 
		key: _AUTO_SUBMENU_DISPLAY, 
		value: true, 
		validator: Lang.isBoolean,
		suppressEvent: true
	}, 

	SHOW_DELAY_CONFIG =  { 
		key: _SHOW_DELAY, 
		value: 250, 
		validator: Lang.isNumber, 
		suppressEvent: true
	}, 

	HIDE_DELAY_CONFIG =  { 
		key: _HIDE_DELAY, 
		value: 0, 
		validator: Lang.isNumber, 
		suppressEvent: true
	}, 

	SUBMENU_HIDE_DELAY_CONFIG =  { 
		key: _SUBMENU_HIDE_DELAY, 
		value: 250, 
		validator: Lang.isNumber,
		suppressEvent: true
	}, 

	CLICK_TO_HIDE_CONFIG =  { 
		key: _CLICK_TO_HIDE, 
		value: true, 
		validator: Lang.isBoolean,
		suppressEvent: true
	},

	CONTAINER_CONFIG =  { 
		key: _CONTAINER,
		suppressEvent: true
	}, 

	SCROLL_INCREMENT_CONFIG =  { 
		key: _SCROLL_INCREMENT, 
		value: 1, 
		validator: Lang.isNumber,
		supercedes: [_MAX_HEIGHT],
		suppressEvent: true
	},

	MIN_SCROLL_HEIGHT_CONFIG =  { 
		key: _MIN_SCROLL_HEIGHT, 
		value: 90, 
		validator: Lang.isNumber,
		supercedes: [_MAX_HEIGHT],
		suppressEvent: true
	},    

	MAX_HEIGHT_CONFIG =  { 
		key: _MAX_HEIGHT, 
		value: 0, 
		validator: Lang.isNumber,
		supercedes: [_IFRAME],
		suppressEvent: true
	}, 

	CLASS_NAME_CONFIG =  { 
		key: _CLASSNAME, 
		value: null, 
		validator: Lang.isString,
		suppressEvent: true
	}, 

	DISABLED_CONFIG =  { 
		key: _DISABLED, 
		value: false, 
		validator: Lang.isBoolean,
		suppressEvent: true
	},
	
	SHADOW_CONFIG =  { 
		key: _SHADOW, 
		value: true, 
		validator: Lang.isBoolean,
		suppressEvent: true,
		supercedes: [_VISIBLE]
	},
	
	KEEP_OPEN_CONFIG = {
		key: _KEEP_OPEN, 
		value: false, 
		validator: Lang.isBoolean
	};


function onDocFocus(event) {

	oFocusedElement = Event.getTarget(event);

}



YAHOO.lang.extend(Menu, Overlay, {


// Constants


/**
* @property CSS_CLASS_NAME
* @description String representing the CSS class(es) to be applied to the 
* menu's <code>&#60;div&#62;</code> element.
* @default "yuimenu"
* @final
* @type String
*/
CSS_CLASS_NAME: "yuimenu",


/**
* @property ITEM_TYPE
* @description Object representing the type of menu item to instantiate and 
* add when parsing the child nodes (either <code>&#60;li&#62;</code> element, 
* <code>&#60;optgroup&#62;</code> element or <code>&#60;option&#62;</code>) 
* of the menu's source HTML element.
* @default YAHOO.widget.MenuItem
* @final
* @type YAHOO.widget.MenuItem
*/
ITEM_TYPE: null,


/**
* @property GROUP_TITLE_TAG_NAME
* @description String representing the tagname of the HTML element used to 
* title the menu's item groups.
* @default H6
* @final
* @type String
*/
GROUP_TITLE_TAG_NAME: "h6",


/**
* @property OFF_SCREEN_POSITION
* @description Array representing the default x and y position that a menu 
* should have when it is positioned outside the viewport by the 
* "poistionOffScreen" method.
* @default "-999em"
* @final
* @type String
*/
OFF_SCREEN_POSITION: "-999em",


// Private properties


/** 
* @property _useHideDelay
* @description Boolean indicating if the "mouseover" and "mouseout" event 
* handlers used for hiding the menu via a call to "YAHOO.lang.later" have 
* already been assigned.
* @default false
* @private
* @type Boolean
*/
_useHideDelay: false,


/**
* @property _bHandledMouseOverEvent
* @description Boolean indicating the current state of the menu's 
* "mouseover" event.
* @default false
* @private
* @type Boolean
*/
_bHandledMouseOverEvent: false,


/**
* @property _bHandledMouseOutEvent
* @description Boolean indicating the current state of the menu's
* "mouseout" event.
* @default false
* @private
* @type Boolean
*/
_bHandledMouseOutEvent: false,


/**
* @property _aGroupTitleElements
* @description Array of HTML element used to title groups of menu items.
* @default []
* @private
* @type Array
*/
_aGroupTitleElements: null,


/**
* @property _aItemGroups
* @description Multi-dimensional Array representing the menu items as they
* are grouped in the menu.
* @default []
* @private
* @type Array
*/
_aItemGroups: null,


/**
* @property _aListElements
* @description Array of <code>&#60;ul&#62;</code> elements, each of which is 
* the parent node for each item's <code>&#60;li&#62;</code> element.
* @default []
* @private
* @type Array
*/
_aListElements: null,


/**
* @property _nCurrentMouseX
* @description The current x coordinate of the mouse inside the area of 
* the menu.
* @default 0
* @private
* @type Number
*/
_nCurrentMouseX: 0,


/**
* @property _bStopMouseEventHandlers
* @description Stops "mouseover," "mouseout," and "mousemove" event handlers 
* from executing.
* @default false
* @private
* @type Boolean
*/
_bStopMouseEventHandlers: false,


/**
* @property _sClassName
* @description The current value of the "classname" configuration attribute.
* @default null
* @private
* @type String
*/
_sClassName: null,



// Public properties


/**
* @property lazyLoad
* @description Boolean indicating if the menu's "lazy load" feature is 
* enabled.  If set to "true," initialization and rendering of the menu's 
* items will be deferred until the first time it is made visible.  This 
* property should be set via the constructor using the configuration 
* object literal.
* @default false
* @type Boolean
*/
lazyLoad: false,


/**
* @property itemData
* @description Array of items to be added to the menu.  The array can contain 
* strings representing the text for each item to be created, object literals 
* representing the menu item configuration properties, or MenuItem instances.  
* This property should be set via the constructor using the configuration 
* object literal.
* @default null
* @type Array
*/
itemData: null,


/**
* @property activeItem
* @description Object reference to the item in the menu that has is selected.
* @default null
* @type YAHOO.widget.MenuItem
*/
activeItem: null,


/**
* @property parent
* @description Object reference to the menu's parent menu or menu item.  
* This property can be set via the constructor using the configuration 
* object literal.
* @default null
* @type YAHOO.widget.MenuItem
*/
parent: null,


/**
* @property srcElement
* @description Object reference to the HTML element (either 
* <code>&#60;select&#62;</code> or <code>&#60;div&#62;</code>) used to 
* create the menu.
* @default null
* @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-94282980">HTMLSelectElement</a>|<a 
* href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.
* html#ID-22445964">HTMLDivElement</a>
*/
srcElement: null,



// Events


/**
* @event mouseOverEvent
* @description Fires when the mouse has entered the menu.  Passes back 
* the DOM Event object as an argument.
*/


/**
* @event mouseOutEvent
* @description Fires when the mouse has left the menu.  Passes back the DOM 
* Event object as an argument.
* @type YAHOO.util.CustomEvent
*/


/**
* @event mouseDownEvent
* @description Fires when the user mouses down on the menu.  Passes back the 
* DOM Event object as an argument.
* @type YAHOO.util.CustomEvent
*/


/**
* @event mouseUpEvent
* @description Fires when the user releases a mouse button while the mouse is 
* over the menu.  Passes back the DOM Event object as an argument.
* @type YAHOO.util.CustomEvent
*/


/**
* @event clickEvent
* @description Fires when the user clicks the on the menu.  Passes back the 
* DOM Event object as an argument.
* @type YAHOO.util.CustomEvent
*/


/**
* @event keyPressEvent
* @description Fires when the user presses an alphanumeric key when one of the
* menu's items has focus.  Passes back the DOM Event object as an argument.
* @type YAHOO.util.CustomEvent
*/


/**
* @event keyDownEvent
* @description Fires when the user presses a key when one of the menu's items 
* has focus.  Passes back the DOM Event object as an argument.
* @type YAHOO.util.CustomEvent
*/


/**
* @event keyUpEvent
* @description Fires when the user releases a key when one of the menu's items 
* has focus.  Passes back the DOM Event object as an argument.
* @type YAHOO.util.CustomEvent
*/


/**
* @event itemAddedEvent
* @description Fires when an item is added to the menu.
* @type YAHOO.util.CustomEvent
*/


/**
* @event itemRemovedEvent
* @description Fires when an item is removed to the menu.
* @type YAHOO.util.CustomEvent
*/


/**
* @method init
* @description The Menu class's initialization method. This method is 
* automatically called by the constructor, and sets up all DOM references 
* for pre-existing markup, and creates required markup if it is not 
* already present.
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;div&#62;</code> element of the menu.
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;select&#62;</code> element to be used as the data source 
* for the menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 
* specifying the <code>&#60;div&#62;</code> element of the menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
* level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement 
* Object specifying the <code>&#60;select&#62;</code> element to be used as 
* the data source for the menu.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the menu. See configuration class documentation for 
* more details.
*/
init: function (p_oElement, p_oConfig) {

    this._aItemGroups = [];
    this._aListElements = [];
    this._aGroupTitleElements = [];

    if (!this.ITEM_TYPE) {

        this.ITEM_TYPE = YAHOO.widget.MenuItem;

    }


    var oElement;

    if (Lang.isString(p_oElement)) {

        oElement = Dom.get(p_oElement);

    }
    else if (p_oElement.tagName) {

        oElement = p_oElement;

    }


    if (oElement && oElement.tagName) {

        switch(oElement.tagName.toUpperCase()) {
    
            case _DIV_UPPERCASE:

                this.srcElement = oElement;

                if (!oElement.id) {

                    oElement.setAttribute(_ID, Dom.generateId());

                }


                /* 
                    Note: we don't pass the user config in here yet 
                    because we only want it executed once, at the lowest 
                    subclass level.
                */ 
            
                Menu.superclass.init.call(this, oElement);

                this.beforeInitEvent.fire(Menu);

    
            break;
    
            case _SELECT:
    
                this.srcElement = oElement;

    
                /*
                    The source element is not something that we can use 
                    outright, so we need to create a new Overlay

                    Note: we don't pass the user config in here yet 
                    because we only want it executed once, at the lowest 
                    subclass level.
                */ 

                Menu.superclass.init.call(this, Dom.generateId());

                this.beforeInitEvent.fire(Menu);


            break;

        }

    }
    else {

        /* 
            Note: we don't pass the user config in here yet 
            because we only want it executed once, at the lowest 
            subclass level.
        */ 
    
        Menu.superclass.init.call(this, p_oElement);

        this.beforeInitEvent.fire(Menu);


    }


    if (this.element) {

        Dom.addClass(this.element, this.CSS_CLASS_NAME);


        // Subscribe to Custom Events

        this.initEvent.subscribe(this._onInit);
        this.beforeRenderEvent.subscribe(this._onBeforeRender);
        this.renderEvent.subscribe(this._onRender);
        this.beforeShowEvent.subscribe(this._onBeforeShow);
		this.hideEvent.subscribe(this._onHide);
        this.showEvent.subscribe(this._onShow);
		this.beforeHideEvent.subscribe(this._onBeforeHide);
        this.mouseOverEvent.subscribe(this._onMouseOver);
        this.mouseOutEvent.subscribe(this._onMouseOut);
        this.clickEvent.subscribe(this._onClick);
        this.keyDownEvent.subscribe(this._onKeyDown);
        this.keyPressEvent.subscribe(this._onKeyPress);
        this.blurEvent.subscribe(this._onBlur);


		if (!bFocusListenerInitialized) {
			Event.onFocus(document, onDocFocus);
			bFocusListenerInitialized = true;
		}


		//	Fixes an issue in Firefox 2 and Webkit where Dom's "getX" and "getY" 
		//	methods return values that don't take scrollTop into consideration 

        if ((UA.gecko && UA.gecko < 1.9) || UA.webkit) {

            this.cfg.subscribeToConfigEvent(_Y, this._onYChange);

        }


        if (p_oConfig) {
    
            this.cfg.applyConfig(p_oConfig, true);
    
        }


        // Register the Menu instance with the MenuManager

        MenuManager.addMenu(this);


        this.initEvent.fire(Menu);

    }

},



// Private methods


/**
* @method _initSubTree
* @description Iterates the childNodes of the source element to find nodes 
* used to instantiate menu and menu items.
* @private
*/
_initSubTree: function () {

    var oSrcElement = this.srcElement,
        sSrcElementTagName,
        nGroup,
        sGroupTitleTagName,
        oNode,
        aListElements,
        nListElements,
        i;


    if (oSrcElement) {
    
        sSrcElementTagName = 
            (oSrcElement.tagName && oSrcElement.tagName.toUpperCase());


        if (sSrcElementTagName == _DIV_UPPERCASE) {
    
            //  Populate the collection of item groups and item group titles
    
            oNode = this.body.firstChild;
    

            if (oNode) {
    
                nGroup = 0;
                sGroupTitleTagName = this.GROUP_TITLE_TAG_NAME.toUpperCase();
        
                do {
        

                    if (oNode && oNode.tagName) {
        
                        switch (oNode.tagName.toUpperCase()) {
        
                            case sGroupTitleTagName:
                            
                                this._aGroupTitleElements[nGroup] = oNode;
        
                            break;
        
                            case _UL_UPPERCASE:
        
                                this._aListElements[nGroup] = oNode;
                                this._aItemGroups[nGroup] = [];
                                nGroup++;
        
                            break;
        
                        }
                    
                    }
        
                }
                while ((oNode = oNode.nextSibling));
        
        
                /*
                    Apply the "first-of-type" class to the first UL to mimic 
                    the ":first-of-type" CSS3 psuedo class.
                */
        
                if (this._aListElements[0]) {
        
                    Dom.addClass(this._aListElements[0], _FIRST_OF_TYPE);
        
                }
            
            }
    
        }
    
    
        oNode = null;
    
    

        if (sSrcElementTagName) {
    
            switch (sSrcElementTagName) {
        
                case _DIV_UPPERCASE:

                    aListElements = this._aListElements;
                    nListElements = aListElements.length;
        
                    if (nListElements > 0) {
        
        
                        i = nListElements - 1;
        
                        do {
        
                            oNode = aListElements[i].firstChild;
            
                            if (oNode) {

            
                                do {
                
                                    if (oNode && oNode.tagName && 
                                        oNode.tagName.toUpperCase() == _LI) {
                
        
                                        this.addItem(new this.ITEM_TYPE(oNode, 
                                                    { parent: this }), i);
            
                                    }
                        
                                }
                                while ((oNode = oNode.nextSibling));
                            
                            }
                    
                        }
                        while (i--);
        
                    }
        
                break;
        
                case _SELECT:
        
        
                    oNode = oSrcElement.firstChild;
        
                    do {
        
                        if (oNode && oNode.tagName) {
                        
                            switch (oNode.tagName.toUpperCase()) {
            
                                case _OPTGROUP:
                                case _OPTION:
            
            
                                    this.addItem(
                                            new this.ITEM_TYPE(
                                                    oNode, 
                                                    { parent: this }
                                                )
                                            );
            
                                break;
            
                            }
    
                        }
        
                    }
                    while ((oNode = oNode.nextSibling));
        
                break;
        
            }
    
        }    
    
    }

},


/**
* @method _getFirstEnabledItem
* @description Returns the first enabled item in the menu.
* @return {YAHOO.widget.MenuItem}
* @private
*/
_getFirstEnabledItem: function () {

    var aItems = this.getItems(),
        nItems = aItems.length,
        oItem,
        returnVal;
    

    for(var i=0; i<nItems; i++) {

        oItem = aItems[i];

        if (oItem && !oItem.cfg.getProperty(_DISABLED) && oItem.element.style.display != _NONE) {

            returnVal = oItem;
            break;

        }
    
    }
    
    return returnVal;
    
},


/**
* @method _addItemToGroup
* @description Adds a menu item to a group.
* @private
* @param {Number} p_nGroupIndex Number indicating the group to which the 
* item belongs.
* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
* instance to be added to the menu.
* @param {String} p_oItem String specifying the text of the item to be added 
* to the menu.
* @param {Object} p_oItem Object literal containing a set of menu item 
* configuration properties.
* @param {Number} p_nItemIndex Optional. Number indicating the index at 
* which the menu item should be added.
* @return {YAHOO.widget.MenuItem}
*/
_addItemToGroup: function (p_nGroupIndex, p_oItem, p_nItemIndex) {

    var oItem,
        nGroupIndex,
        aGroup,
        oGroupItem,
        bAppend,
        oNextItemSibling,
        nItemIndex,
        returnVal;


    function getNextItemSibling(p_aArray, p_nStartIndex) {

        return (p_aArray[p_nStartIndex] || getNextItemSibling(p_aArray, (p_nStartIndex+1)));

    }


    if (p_oItem instanceof this.ITEM_TYPE) {

        oItem = p_oItem;
        oItem.parent = this;

    }
    else if (Lang.isString(p_oItem)) {

        oItem = new this.ITEM_TYPE(p_oItem, { parent: this });
    
    }
    else if (Lang.isObject(p_oItem)) {

        p_oItem.parent = this;

        oItem = new this.ITEM_TYPE(p_oItem.text, p_oItem);

    }


    if (oItem) {

        if (oItem.cfg.getProperty(_SELECTED)) {

            this.activeItem = oItem;
        
        }


        nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0;
        aGroup = this._getItemGroup(nGroupIndex);



        if (!aGroup) {

            aGroup = this._createItemGroup(nGroupIndex);

        }


        if (Lang.isNumber(p_nItemIndex)) {

            bAppend = (p_nItemIndex >= aGroup.length);            


            if (aGroup[p_nItemIndex]) {
    
                aGroup.splice(p_nItemIndex, 0, oItem);
    
            }
            else {
    
                aGroup[p_nItemIndex] = oItem;
    
            }


            oGroupItem = aGroup[p_nItemIndex];

            if (oGroupItem) {

                if (bAppend && (!oGroupItem.element.parentNode || 
                        oGroupItem.element.parentNode.nodeType == 11)) {
        
                    this._aListElements[nGroupIndex].appendChild(oGroupItem.element);
    
                }
                else {
    
                    oNextItemSibling = getNextItemSibling(aGroup, (p_nItemIndex+1));
    
                    if (oNextItemSibling && (!oGroupItem.element.parentNode || 
                            oGroupItem.element.parentNode.nodeType == 11)) {
            
                        this._aListElements[nGroupIndex].insertBefore(
                                oGroupItem.element, oNextItemSibling.element);
        
                    }
    
                }
    

                oGroupItem.parent = this;
        
                this._subscribeToItemEvents(oGroupItem);
    
                this._configureSubmenu(oGroupItem);
                
                this._updateItemProperties(nGroupIndex);
        

                this.itemAddedEvent.fire(oGroupItem);
                this.changeContentEvent.fire();

                returnVal = oGroupItem;
    
            }

        }
        else {
    
            nItemIndex = aGroup.length;
    
            aGroup[nItemIndex] = oItem;

            oGroupItem = aGroup[nItemIndex];
    

            if (oGroupItem) {
    
                if (!Dom.isAncestor(this._aListElements[nGroupIndex], oGroupItem.element)) {
    
                    this._aListElements[nGroupIndex].appendChild(oGroupItem.element);
    
                }
    
                oGroupItem.element.setAttribute(_GROUP_INDEX, nGroupIndex);
                oGroupItem.element.setAttribute(_INDEX, nItemIndex);
        
                oGroupItem.parent = this;
    
                oGroupItem.index = nItemIndex;
                oGroupItem.groupIndex = nGroupIndex;
        
                this._subscribeToItemEvents(oGroupItem);
    
                this._configureSubmenu(oGroupItem);
    
                if (nItemIndex === 0) {
        
                    Dom.addClass(oGroupItem.element, _FIRST_OF_TYPE);
        
                }

        

                this.itemAddedEvent.fire(oGroupItem);
                this.changeContentEvent.fire();

                returnVal = oGroupItem;
    
            }
    
        }

    }
    
    return returnVal;
    
},


/**
* @method _removeItemFromGroupByIndex
* @description Removes a menu item from a group by index.  Returns the menu 
* item that was removed.
* @private
* @param {Number} p_nGroupIndex Number indicating the group to which the menu 
* item belongs.
* @param {Number} p_nItemIndex Number indicating the index of the menu item 
* to be removed.
* @return {YAHOO.widget.MenuItem}
*/
_removeItemFromGroupByIndex: function (p_nGroupIndex, p_nItemIndex) {

    var nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0,
        aGroup = this._getItemGroup(nGroupIndex),
        aArray,
        oItem,
        oUL;

    if (aGroup) {

        aArray = aGroup.splice(p_nItemIndex, 1);
        oItem = aArray[0];
    
        if (oItem) {
    
            // Update the index and className properties of each member        
            
            this._updateItemProperties(nGroupIndex);
    
            if (aGroup.length === 0) {
    
                // Remove the UL
    
                oUL = this._aListElements[nGroupIndex];
    
                if (this.body && oUL) {
    
                    this.body.removeChild(oUL);
    
                }
    
                // Remove the group from the array of items
    
                this._aItemGroups.splice(nGroupIndex, 1);
    
    
                // Remove the UL from the array of ULs
    
                this._aListElements.splice(nGroupIndex, 1);
    
    
                /*
                     Assign the "first-of-type" class to the new first UL 
                     in the collection
                */
    
                oUL = this._aListElements[0];
    
                if (oUL) {
    
                    Dom.addClass(oUL, _FIRST_OF_TYPE);
    
                }            
    
            }
    

            this.itemRemovedEvent.fire(oItem);
            this.changeContentEvent.fire();
    
        }

    }

	// Return a reference to the item that was removed

	return oItem;
    
},


/**
* @method _removeItemFromGroupByValue
* @description Removes a menu item from a group by reference.  Returns the 
* menu item that was removed.
* @private
* @param {Number} p_nGroupIndex Number indicating the group to which the
* menu item belongs.
* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
* instance to be removed.
* @return {YAHOO.widget.MenuItem}
*/    
_removeItemFromGroupByValue: function (p_nGroupIndex, p_oItem) {

    var aGroup = this._getItemGroup(p_nGroupIndex),
        nItems,
        nItemIndex,
        returnVal,
        i;

    if (aGroup) {

        nItems = aGroup.length;
        nItemIndex = -1;
    
        if (nItems > 0) {
    
            i = nItems-1;
        
            do {
        
                if (aGroup[i] == p_oItem) {
        
                    nItemIndex = i;
                    break;    
        
                }
        
            }
            while (i--);
        
            if (nItemIndex > -1) {
        
                returnVal = this._removeItemFromGroupByIndex(p_nGroupIndex, nItemIndex);
        
            }
    
        }
    
    }
    
    return returnVal;

},


/**
* @method _updateItemProperties
* @description Updates the "index," "groupindex," and "className" properties 
* of the menu items in the specified group. 
* @private
* @param {Number} p_nGroupIndex Number indicating the group of items to update.
*/
_updateItemProperties: function (p_nGroupIndex) {

    var aGroup = this._getItemGroup(p_nGroupIndex),
        nItems = aGroup.length,
        oItem,
        oLI,
        i;


    if (nItems > 0) {

        i = nItems - 1;

        // Update the index and className properties of each member
    
        do {

            oItem = aGroup[i];

            if (oItem) {
    
                oLI = oItem.element;

                oItem.index = i;
                oItem.groupIndex = p_nGroupIndex;

                oLI.setAttribute(_GROUP_INDEX, p_nGroupIndex);
                oLI.setAttribute(_INDEX, i);

                Dom.removeClass(oLI, _FIRST_OF_TYPE);

            }
    
        }
        while (i--);


        if (oLI) {

            Dom.addClass(oLI, _FIRST_OF_TYPE);

        }

    }

},


/**
* @method _createItemGroup
* @description Creates a new menu item group (array) and its associated 
* <code>&#60;ul&#62;</code> element. Returns an aray of menu item groups.
* @private
* @param {Number} p_nIndex Number indicating the group to create.
* @return {Array}
*/
_createItemGroup: function (p_nIndex) {

    var oUL,
    	returnVal;

    if (!this._aItemGroups[p_nIndex]) {

        this._aItemGroups[p_nIndex] = [];

        oUL = document.createElement(_UL_LOWERCASE);

        this._aListElements[p_nIndex] = oUL;

        returnVal = this._aItemGroups[p_nIndex];

    }
    
    return returnVal;

},


/**
* @method _getItemGroup
* @description Returns the menu item group at the specified index.
* @private
* @param {Number} p_nIndex Number indicating the index of the menu item group 
* to be retrieved.
* @return {Array}
*/
_getItemGroup: function (p_nIndex) {

    var nIndex = Lang.isNumber(p_nIndex) ? p_nIndex : 0,
    	aGroups = this._aItemGroups,
    	returnVal;

	if (nIndex in aGroups) {

	    returnVal = aGroups[nIndex];

	}
	
	return returnVal;

},


/**
* @method _configureSubmenu
* @description Subscribes the menu item's submenu to its parent menu's events.
* @private
* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
* instance with the submenu to be configured.
*/
_configureSubmenu: function (p_oItem) {

    var oSubmenu = p_oItem.cfg.getProperty(_SUBMENU);

    if (oSubmenu) {
            
        /*
            Listen for configuration changes to the parent menu 
            so they they can be applied to the submenu.
        */

        this.cfg.configChangedEvent.subscribe(this._onParentMenuConfigChange, oSubmenu, true);

        this.renderEvent.subscribe(this._onParentMenuRender, oSubmenu, true);

    }

},




/**
* @method _subscribeToItemEvents
* @description Subscribes a menu to a menu item's event.
* @private
* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
* instance whose events should be subscribed to.
*/
_subscribeToItemEvents: function (p_oItem) {

    p_oItem.destroyEvent.subscribe(this._onMenuItemDestroy, p_oItem, this);
    p_oItem.cfg.configChangedEvent.subscribe(this._onMenuItemConfigChange, p_oItem, this);

},


/**
* @method _onVisibleChange
* @description Change event handler for the the menu's "visible" configuration
* property.
* @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.
*/
_onVisibleChange: function (p_sType, p_aArgs) {

    var bVisible = p_aArgs[0];
    
    if (bVisible) {

        Dom.addClass(this.element, _VISIBLE);

    }
    else {

        Dom.removeClass(this.element, _VISIBLE);

    }

},


/**
* @method _cancelHideDelay
* @description Cancels the call to "hideMenu."
* @private
*/
_cancelHideDelay: function () {

    var oTimer = this.getRoot()._hideDelayTimer;

    if (oTimer) {

		oTimer.cancel();

    }

},


/**
* @method _execHideDelay
* @description Hides the menu after the number of milliseconds specified by 
* the "hidedelay" configuration property.
* @private
*/
_execHideDelay: function () {

    this._cancelHideDelay();

    var oRoot = this.getRoot();
        
	oRoot._hideDelayTimer = Lang.later(oRoot.cfg.getProperty(_HIDE_DELAY), this, function () {
    
        if (oRoot.activeItem) {

			if (oRoot.hasFocus()) {

				oRoot.activeItem.focus();
			
			}
			
            oRoot.clearActiveItem();

        }

        if (oRoot == this && !(this instanceof YAHOO.widget.MenuBar) && 
            this.cfg.getProperty(_POSITION) == _DYNAMIC) {

            this.hide();
        
        }
    
    });

},


/**
* @method _cancelShowDelay
* @description Cancels the call to the "showMenu."
* @private
*/
_cancelShowDelay: function () {

    var oTimer = this.getRoot()._showDelayTimer;

    if (oTimer) {

        oTimer.cancel();

    }

},


/**
* @method _execSubmenuHideDelay
* @description Hides a submenu after the number of milliseconds specified by 
* the "submenuhidedelay" configuration property have ellapsed.
* @private
* @param {YAHOO.widget.Menu} p_oSubmenu Object specifying the submenu that  
* should be hidden.
* @param {Number} p_nMouseX The x coordinate of the mouse when it left 
* the specified submenu's parent menu item.
* @param {Number} p_nHideDelay The number of milliseconds that should ellapse
* before the submenu is hidden.
*/
_execSubmenuHideDelay: function (p_oSubmenu, p_nMouseX, p_nHideDelay) {

	p_oSubmenu._submenuHideDelayTimer = Lang.later(50, this, function () {

        if (this._nCurrentMouseX > (p_nMouseX + 10)) {

            p_oSubmenu._submenuHideDelayTimer = Lang.later(p_nHideDelay, p_oSubmenu, function () {
        
                this.hide();

            });

        }
        else {

            p_oSubmenu.hide();
        
        }
	
	});

},



// Protected methods


/**
* @method _disableScrollHeader
* @description Disables the header used for scrolling the body of the menu.
* @protected
*/
_disableScrollHeader: function () {

    if (!this._bHeaderDisabled) {

        Dom.addClass(this.header, _TOP_SCROLLBAR_DISABLED);
        this._bHeaderDisabled = true;

    }

},


/**
* @method _disableScrollFooter
* @description Disables the footer used for scrolling the body of the menu.
* @protected
*/
_disableScrollFooter: function () {

    if (!this._bFooterDisabled) {

        Dom.addClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED);
        this._bFooterDisabled = true;

    }

},


/**
* @method _enableScrollHeader
* @description Enables the header used for scrolling the body of the menu.
* @protected
*/
_enableScrollHeader: function () {

    if (this._bHeaderDisabled) {

        Dom.removeClass(this.header, _TOP_SCROLLBAR_DISABLED);
        this._bHeaderDisabled = false;

    }

},


/**
* @method _enableScrollFooter
* @description Enables the footer used for scrolling the body of the menu.
* @protected
*/
_enableScrollFooter: function () {

    if (this._bFooterDisabled) {

        Dom.removeClass(this.footer, _BOTTOM_SCROLLBAR_DISABLED);
        this._bFooterDisabled = false;

    }

},


/**
* @method _onMouseOver
* @description "mouseover" event handler for the menu.
* @protected
* @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.
*/
_onMouseOver: function (p_sType, p_aArgs) {

    var oEvent = p_aArgs[0],
        oItem = p_aArgs[1],
        oTarget = Event.getTarget(oEvent),
        oRoot = this.getRoot(),
        oSubmenuHideDelayTimer = this._submenuHideDelayTimer,
        oParentMenu,
        nShowDelay,
        bShowDelay,
        oActiveItem,
        oItemCfg,
        oSubmenu;


    var showSubmenu = function () {

        if (this.parent.cfg.getProperty(_SELECTED)) {

            this.show();

        }

    };


    if (!this._bStopMouseEventHandlers) {
    
		if (!this._bHandledMouseOverEvent && (oTarget == this.element || 
				Dom.isAncestor(this.element, oTarget))) {
	
			// Menu mouseover logic

	        if (this._useHideDelay) {
	        	this._cancelHideDelay();
	        }
	
			this._nCurrentMouseX = 0;
	
			Event.on(this.element, _MOUSEMOVE, this._onMouseMove, this, true);


			/*
				If the mouse is moving from the submenu back to its corresponding menu item, 
				don't hide the submenu or clear the active MenuItem.
			*/

			if (!(oItem && Dom.isAncestor(oItem.element, Event.getRelatedTarget(oEvent)))) {

				this.clearActiveItem();

			}
	

			if (this.parent && oSubmenuHideDelayTimer) {
	
				oSubmenuHideDelayTimer.cancel();
	
				this.parent.cfg.setProperty(_SELECTED, true);
	
				oParentMenu = this.parent.parent;
	
				oParentMenu._bHandledMouseOutEvent = true;
				oParentMenu._bHandledMouseOverEvent = false;
	
			}
	
	
			this._bHandledMouseOverEvent = true;
			this._bHandledMouseOutEvent = false;
		
		}
	
	
		if (oItem && !oItem.handledMouseOverEvent && !oItem.cfg.getProperty(_DISABLED) && 
			(oTarget == oItem.element || Dom.isAncestor(oItem.element, oTarget))) {
	
			// Menu Item mouseover logic
	
			nShowDelay = this.cfg.getProperty(_SHOW_DELAY);
			bShowDelay = (nShowDelay > 0);
	
	
			if (bShowDelay) {
			
				this._cancelShowDelay();
			
			}
	
	
			oActiveItem = this.activeItem;
		
			if (oActiveItem) {
		
				oActiveItem.cfg.setProperty(_SELECTED, false);
		
			}
	
	
			oItemCfg = oItem.cfg;
		
			// Select and focus the current menu item
		
			oItemCfg.setProperty(_SELECTED, true);
	
	
			if (this.hasFocus() || oRoot._hasFocus) {
			
				oItem.focus();
				
				oRoot._hasFocus = false;
			
			}
	
	
			if (this.cfg.getProperty(_AUTO_SUBMENU_DISPLAY)) {
	
				// Show the submenu this menu item
	
				oSubmenu = oItemCfg.getProperty(_SUBMENU);
			
				if (oSubmenu) {
			
					if (bShowDelay) {
	
						oRoot._showDelayTimer = 
							Lang.later(oRoot.cfg.getProperty(_SHOW_DELAY), oSubmenu, showSubmenu);
			
					}
					else {
	
						oSubmenu.show();
	
					}
	
				}
	
			}                        
	
			oItem.handledMouseOverEvent = true;
			oItem.handledMouseOutEvent = false;
	
		}
    
    }

},


/**
* @method _onMouseOut
* @description "mouseout" event handler for the menu.
* @protected
* @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.
*/
_onMouseOut: function (p_sType, p_aArgs) {

    var oEvent = p_aArgs[0],
        oItem = p_aArgs[1],
        oRelatedTarget = Event.getRelatedTarget(oEvent),
        bMovingToSubmenu = false,
        oItemCfg,
        oSubmenu,
        nSubmenuHideDelay,
        nShowDelay;


    if (!this._bStopMouseEventHandlers) {
    
		if (oItem && !oItem.cfg.getProperty(_DISABLED)) {
	
			oItemCfg = oItem.cfg;
			oSubmenu = oItemCfg.getProperty(_SUBMENU);
	
	
			if (oSubmenu && (oRelatedTarget == oSubmenu.element ||
					Dom.isAncestor(oSubmenu.element, oRelatedTarget))) {
	
				bMovingToSubmenu = true;
	
			}
	
	
			if (!oItem.handledMouseOutEvent && ((oRelatedTarget != oItem.element &&  
				!Dom.isAncestor(oItem.element, oRelatedTarget)) || bMovingToSubmenu)) {
	
				// Menu Item mouseout logic
	
				if (!bMovingToSubmenu) {
	
					oItem.cfg.setProperty(_SELECTED, false);
	
	
					if (oSubmenu) {
	
						nSubmenuHideDelay = this.cfg.getProperty(_SUBMENU_HIDE_DELAY);
	
						nShowDelay = this.cfg.getProperty(_SHOW_DELAY);
	
						if (!(this instanceof YAHOO.widget.MenuBar) && nSubmenuHideDelay > 0 && 
							nShowDelay >= nSubmenuHideDelay) {
	
							this._execSubmenuHideDelay(oSubmenu, Event.getPageX(oEvent),
									nSubmenuHideDelay);
	
						}
						else {
	
							oSubmenu.hide();
	
						}
	
					}
	
				}
	
	
				oItem.handledMouseOutEvent = true;
				oItem.handledMouseOverEvent = false;
		
			}
	
		}


		if (!this._bHandledMouseOutEvent && ((oRelatedTarget != this.element &&  
			!Dom.isAncestor(this.element, oRelatedTarget)) || bMovingToSubmenu)) {
	
			// Menu mouseout logic

	        if (this._useHideDelay) {
	        	this._execHideDelay();
	        }

			Event.removeListener(this.element, _MOUSEMOVE, this._onMouseMove);
	
			this._nCurrentMouseX = Event.getPageX(oEvent);
	
			this._bHandledMouseOutEvent = true;
			this._bHandledMouseOverEvent = false;
	
		}
    
    }

},


/**
* @method _onMouseMove
* @description "click" event handler for the menu.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object passed 
* back by the event utility (YAHOO.util.Event).
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
* fired the event.
*/
_onMouseMove: function (p_oEvent, p_oMenu) {

    if (!this._bStopMouseEventHandlers) {
    
	    this._nCurrentMouseX = Event.getPageX(p_oEvent);
    
    }

},


/**
* @method _onClick
* @description "click" event handler for the menu.
* @protected
* @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.
*/
_onClick: function (p_sType, p_aArgs) {

	var oEvent = p_aArgs[0],
		oItem = p_aArgs[1],
		bInMenuAnchor = false,
		oSubmenu,
		oMenu,
		oRoot,
		sId,
		sURL,
		nHashPos,
		nLen;


	var hide = function () {
		
		oRoot = this.getRoot();

		if (oRoot instanceof YAHOO.widget.MenuBar || 
			oRoot.cfg.getProperty(_POSITION) == _STATIC) {

			oRoot.clearActiveItem();

		}
		else {

			oRoot.hide();
		
		}
	
	};


	if (oItem) {
	
		if (oItem.cfg.getProperty(_DISABLED)) {
		
			Event.preventDefault(oEvent);

			hide.call(this);

		}
		else {

			oSubmenu = oItem.cfg.getProperty(_SUBMENU);
	
			
			/*
				 Check if the URL of the anchor is pointing to an element that is 
				 a child of the menu.
			*/
			
			sURL = oItem.cfg.getProperty(_URL);

		
			if (sURL) {
	
				nHashPos = sURL.indexOf(_HASH);
	
				nLen = sURL.length;
	
	
				if (nHashPos != -1) {
	
					sURL = sURL.substr(nHashPos, nLen);
		
					nLen = sURL.length;
	
	
					if (nLen > 1) {
	
						sId = sURL.substr(1, nLen);
	
						oMenu = YAHOO.widget.MenuManager.getMenu(sId);
						
						if (oMenu) {

							bInMenuAnchor = 
								(this.getRoot() === oMenu.getRoot());

						}
						
					}
					else if (nLen === 1) {
	
						bInMenuAnchor = true;
					
					}
	
				}
			
			}

	
			if (bInMenuAnchor && !oItem.cfg.getProperty(_TARGET)) {
	
				Event.preventDefault(oEvent);
				

				if (UA.webkit) {
				
					oItem.focus();
				
				}
				else {

					oItem.focusEvent.fire();
				
				}
			
			}
	
	
			if (!oSubmenu && !this.cfg.getProperty(_KEEP_OPEN)) {
	
				hide.call(this);
	
			}
			
		}
	
	}

},


/**
* @method _onKeyDown
* @description "keydown" event handler for the menu.
* @protected
* @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.
*/
_onKeyDown: function (p_sType, p_aArgs) {

    var oEvent = p_aArgs[0],
        oItem = p_aArgs[1],
        oSubmenu,
        oItemCfg,
        oParentItem,
        oRoot,
        oNextItem,
        oBody,
        nBodyScrollTop,
        nBodyOffsetHeight,
        aItems,
        nItems,
        nNextItemOffsetTop,
        nScrollTarget,
        oParentMenu,
		oFocusedEl;


	if (this._useHideDelay) {
		this._cancelHideDelay();
	}


    /*
        This function is called to prevent a bug in Firefox.  In Firefox,
        moving a DOM element into a stationary mouse pointer will cause the 
        browser to fire mouse events.  This can result in the menu mouse
        event handlers being called uncessarily, especially when menus are 
        moved into a stationary mouse pointer as a result of a 
        key event handler.
    */
    function stopMouseEventHandlers() {

        this._bStopMouseEventHandlers = true;
        
        Lang.later(10, this, function () {

            this._bStopMouseEventHandlers = false;
        
        });

    }


    if (oItem && !oItem.cfg.getProperty(_DISABLED)) {

        oItemCfg = oItem.cfg;
        oParentItem = this.parent;

        switch(oEvent.keyCode) {
    
            case 38:    // Up arrow
            case 40:    // Down arrow
    
                oNextItem = (oEvent.keyCode == 38) ? 
                    oItem.getPreviousEnabledSibling() : 
                    oItem.getNextEnabledSibling();
        
                if (oNextItem) {

                    this.clearActiveItem();

                    oNextItem.cfg.setProperty(_SELECTED, true);
                    oNextItem.focus();


                    if (this.cfg.getProperty(_MAX_HEIGHT) > 0) {

                        oBody = this.body;
                        nBodyScrollTop = oBody.scrollTop;
                        nBodyOffsetHeight = oBody.offsetHeight;
                        aItems = this.getItems();
                        nItems = aItems.length - 1;
                        nNextItemOffsetTop = oNextItem.element.offsetTop;


                        if (oEvent.keyCode == 40 ) {    // Down
                       
                            if (nNextItemOffsetTop >= (nBodyOffsetHeight + nBodyScrollTop)) {

                                oBody.scrollTop = nNextItemOffsetTop - nBodyOffsetHeight;

                            }
                            else if (nNextItemOffsetTop <= nBodyScrollTop) {
                            
                                oBody.scrollTop = 0;
                            
                            }


                            if (oNextItem == aItems[nItems]) {

                                oBody.scrollTop = oNextItem.element.offsetTop;

                            }

                        }
                        else {  // Up

                            if (nNextItemOffsetTop <= nBodyScrollTop) {

                                oBody.scrollTop = nNextItemOffsetTop - oNextItem.element.offsetHeight;
                            
                            }
                            else if (nNextItemOffsetTop >= (nBodyScrollTop + nBodyOffsetHeight)) {
                            
                                oBody.scrollTop = nNextItemOffsetTop;
                            
                            }


                            if (oNextItem == aItems[0]) {
                            
                                oBody.scrollTop = 0;
                            
                            }

                        }


                        nBodyScrollTop = oBody.scrollTop;
                        nScrollTarget = oBody.scrollHeight - oBody.offsetHeight;

                        if (nBodyScrollTop === 0) {

                            this._disableScrollHeader();
                            this._enableScrollFooter();

                        }
                        else if (nBodyScrollTop == nScrollTarget) {

                             this._enableScrollHeader();
                             this._disableScrollFooter();

                        }
                        else {

                            this._enableScrollHeader();
                            this._enableScrollFooter();

                        }

                    }

                }

    
                Event.preventDefault(oEvent);

                stopMouseEventHandlers();
    
            break;
            
    
            case 39:    // Right arrow
    
                oSubmenu = oItemCfg.getProperty(_SUBMENU);
    
                if (oSubmenu) {
    
                    if (!oItemCfg.getProperty(_SELECTED)) {
        
                        oItemCfg.setProperty(_SELECTED, true);
        
                    }
    
                    oSubmenu.show();
                    oSubmenu.setInitialFocus();
                    oSubmenu.setInitialSelection();
    
                }
                else {
    
                    oRoot = this.getRoot();
                    
                    if (oRoot instanceof YAHOO.widget.MenuBar) {
    
                        oNextItem = oRoot.activeItem.getNextEnabledSibling();
    
                        if (oNextItem) {
                        
                            oRoot.clearActiveItem();
    
                            oNextItem.cfg.setProperty(_SELECTED, true);
    
                            oSubmenu = oNextItem.cfg.getProperty(_SUBMENU);
    
                            if (oSubmenu) {
    
                                oSubmenu.show();
                                oSubmenu.setInitialFocus();
                            
                            }
                            else {
    
                            	oNextItem.focus();
                            
                            }
                        
                        }
                    
                    }
                
                }
    
    
                Event.preventDefault(oEvent);

                stopMouseEventHandlers();

            break;
    
    
            case 37:    // Left arrow
    
                if (oParentItem) {
    
                    oParentMenu = oParentItem.parent;
    
                    if (oParentMenu instanceof YAHOO.widget.MenuBar) {
    
                        oNextItem = 
                            oParentMenu.activeItem.getPreviousEnabledSibling();
    
                        if (oNextItem) {
                        
                            oParentMenu.clearActiveItem();
    
                            oNextItem.cfg.setProperty(_SELECTED, true);
    
                            oSubmenu = oNextItem.cfg.getProperty(_SUBMENU);
    
                            if (oSubmenu) {
                            
                                oSubmenu.show();
								oSubmenu.setInitialFocus();                                
                            
                            }
                            else {
    
                            	oNextItem.focus();
                            
                            }
                        
                        } 
                    
                    }
                    else {
    
                        this.hide();
    
                        oParentItem.focus();
                    
                    }
    
                }
    
                Event.preventDefault(oEvent);

                stopMouseEventHandlers();

            break;        
    
        }


    }


    if (oEvent.keyCode == 27) { // Esc key

        if (this.cfg.getProperty(_POSITION) == _DYNAMIC) {
        
            this.hide();

            if (this.parent) {

                this.parent.focus();
            
            }
			else {
				// Focus the element that previously had focus

				oFocusedEl = this._focusedElement;

				if (oFocusedEl && oFocusedEl.focus) {

					try {
						oFocusedEl.focus();
					}
					catch(ex) {
					}

				}
				
			}

        }
        else if (this.activeItem) {

            oSubmenu = this.activeItem.cfg.getProperty(_SUBMENU);

            if (oSubmenu && oSubmenu.cfg.getProperty(_VISIBLE)) {
            
                oSubmenu.hide();
                this.activeItem.focus();
            
            }
            else {

                this.activeItem.blur();
                this.activeItem.cfg.setProperty(_SELECTED, false);
        
            }
        
        }


        Event.preventDefault(oEvent);
    
    }
    
},


/**
* @method _onKeyPress
* @description "keypress" event handler for a Menu instance.
* @protected
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
*/
_onKeyPress: function (p_sType, p_aArgs) {
    
    var oEvent = p_aArgs[0];


    if (oEvent.keyCode == 40 || oEvent.keyCode == 38) {

        Event.preventDefault(oEvent);

    }

},


/**
* @method _onBlur
* @description "blur" event handler for a Menu instance.
* @protected
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
*/
_onBlur: function (p_sType, p_aArgs) {
        
	if (this._hasFocus) {
		this._hasFocus = false;
	}

},

/**
* @method _onYChange
* @description "y" event handler for a Menu instance.
* @protected
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
*/
_onYChange: function (p_sType, p_aArgs) {

    var oParent = this.parent,
        nScrollTop,
        oIFrame,
        nY;


    if (oParent) {

        nScrollTop = oParent.parent.body.scrollTop;


        if (nScrollTop > 0) {
    
            nY = (this.cfg.getProperty(_Y) - nScrollTop);
            
            Dom.setY(this.element, nY);

            oIFrame = this.iframe;            
    

            if (oIFrame) {
    
                Dom.setY(oIFrame, nY);
    
            }
            
            this.cfg.setProperty(_Y, nY, true);
        
        }
    
    }

},


/**
* @method _onScrollTargetMouseOver
* @description "mouseover" event handler for the menu's "header" and "footer" 
* elements.  Used to scroll the body of the menu up and down when the 
* menu's "maxheight" configuration property is set to a value greater than 0.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object passed 
* back by the event utility (YAHOO.util.Event).
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
* fired the event.
*/
_onScrollTargetMouseOver: function (p_oEvent, p_oMenu) {

	var oBodyScrollTimer = this._bodyScrollTimer;


	if (oBodyScrollTimer) {

		oBodyScrollTimer.cancel();

	}


	this._cancelHideDelay();


    var oTarget = Event.getTarget(p_oEvent),
        oBody = this.body,
        nScrollIncrement = this.cfg.getProperty(_SCROLL_INCREMENT),
        nScrollTarget,
        fnScrollFunction;


    function scrollBodyDown() {

        var nScrollTop = oBody.scrollTop;


        if (nScrollTop < nScrollTarget) {

            oBody.scrollTop = (nScrollTop + nScrollIncrement);

            this._enableScrollHeader();

        }
        else {

            oBody.scrollTop = nScrollTarget;

            this._bodyScrollTimer.cancel();

            this._disableScrollFooter();

        }

    }


    function scrollBodyUp() {

        var nScrollTop = oBody.scrollTop;


        if (nScrollTop > 0) {

            oBody.scrollTop = (nScrollTop - nScrollIncrement);

            this._enableScrollFooter();

        }
        else {

            oBody.scrollTop = 0;

			this._bodyScrollTimer.cancel();

            this._disableScrollHeader();

        }

    }

    
    if (Dom.hasClass(oTarget, _HD)) {

        fnScrollFunction = scrollBodyUp;
    
    }
    else {

        nScrollTarget = oBody.scrollHeight - oBody.offsetHeight;

        fnScrollFunction = scrollBodyDown;
    
    }
    

    this._bodyScrollTimer = Lang.later(10, this, fnScrollFunction, null, true);

},


/**
* @method _onScrollTargetMouseOut
* @description "mouseout" event handler for the menu's "header" and "footer" 
* elements.  Used to stop scrolling the body of the menu up and down when the 
* menu's "maxheight" configuration property is set to a value greater than 0.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object passed 
* back by the event utility (YAHOO.util.Event).
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
* fired the event.
*/
_onScrollTargetMouseOut: function (p_oEvent, p_oMenu) {

	var oBodyScrollTimer = this._bodyScrollTimer;

	if (oBodyScrollTimer) {

		oBodyScrollTimer.cancel();

	}
	
    this._cancelHideDelay();

},



// Private methods


/**
* @method _onInit
* @description "init" event handler for the 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.
*/
_onInit: function (p_sType, p_aArgs) {

    this.cfg.subscribeToConfigEvent(_VISIBLE, this._onVisibleChange);

    var bRootMenu = !this.parent,
        bLazyLoad = this.lazyLoad;


    /*
        Automatically initialize a menu's subtree if:

        1) This is the root menu and lazyload is off
        
        2) This is the root menu, lazyload is on, but the menu is 
           already visible

        3) This menu is a submenu and lazyload is off
    */



    if (((bRootMenu && !bLazyLoad) || 
        (bRootMenu && (this.cfg.getProperty(_VISIBLE) || 
        this.cfg.getProperty(_POSITION) == _STATIC)) || 
        (!bRootMenu && !bLazyLoad)) && this.getItemGroups().length === 0) {

        if (this.srcElement) {

            this._initSubTree();
        
        }


        if (this.itemData) {

            this.addItems(this.itemData);

        }
    
    }
    else if (bLazyLoad) {

        this.cfg.fireQueue();
    
    }

},


/**
* @method _onBeforeRender
* @description "beforerender" event handler for the menu.  Appends all of the 
* <code>&#60;ul&#62;</code>, <code>&#60;li&#62;</code> and their accompanying 
* title elements to the body element of the 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.
*/
_onBeforeRender: function (p_sType, p_aArgs) {

    var oEl = this.element,
        nListElements = this._aListElements.length,
        bFirstList = true,
        i = 0,
        oUL,
        oGroupTitle;

    if (nListElements > 0) {

        do {

            oUL = this._aListElements[i];

            if (oUL) {

                if (bFirstList) {
        
                    Dom.addClass(oUL, _FIRST_OF_TYPE);
                    bFirstList = false;
        
                }


                if (!Dom.isAncestor(oEl, oUL)) {

                    this.appendToBody(oUL);

                }


                oGroupTitle = this._aGroupTitleElements[i];

                if (oGroupTitle) {

                    if (!Dom.isAncestor(oEl, oGroupTitle)) {

                        oUL.parentNode.insertBefore(oGroupTitle, oUL);

                    }


                    Dom.addClass(oUL, _HAS_TITLE);

                }

            }

            i++;

        }
        while (i < nListElements);

    }

},


/**
* @method _onRender
* @description "render" event handler for the 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.
*/
_onRender: function (p_sType, p_aArgs) {

    if (this.cfg.getProperty(_POSITION) == _DYNAMIC) { 

        if (!this.cfg.getProperty(_VISIBLE)) {

            this.positionOffScreen();

        }
    
    }

},





/**
* @method _onBeforeShow
* @description "beforeshow" event handler for the 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.
*/
_onBeforeShow: function (p_sType, p_aArgs) {

    var nOptions,
        n,
        oSrcElement,
        oContainer = this.cfg.getProperty(_CONTAINER);


    if (this.lazyLoad && this.getItemGroups().length === 0) {

        if (this.srcElement) {
        
            this._initSubTree();

        }


        if (this.itemData) {

            if (this.parent && this.parent.parent && 
                this.parent.parent.srcElement && 
                this.parent.parent.srcElement.tagName.toUpperCase() == 
                _SELECT) {

                nOptions = this.itemData.length;
    
                for(n=0; n<nOptions; n++) {

                    if (this.itemData[n].tagName) {

                        this.addItem((new this.ITEM_TYPE(this.itemData[n])));
    
                    }
    
                }
            
            }
            else {

                this.addItems(this.itemData);
            
            }
        
        }


        oSrcElement = this.srcElement;

        if (oSrcElement) {

            if (oSrcElement.tagName.toUpperCase() == _SELECT) {

                if (Dom.inDocument(oSrcElement)) {

                    this.render(oSrcElement.parentNode);
                
                }
                else {
                
                    this.render(oContainer);
                
                }

            }
            else {

                this.render();

            }

        }
        else {

            if (this.parent) {

                this.render(this.parent.element);     

            }
            else {

                this.render(oContainer);

            }                

        }

    }



    var oParent = this.parent,
		aAlignment;


    if (!oParent && this.cfg.getProperty(_POSITION) == _DYNAMIC) {

        this.cfg.refireEvent(_XY);
   
    }


	if (oParent) {

		aAlignment = oParent.parent.cfg.getProperty(_SUBMENU_ALIGNMENT);
		
		this.cfg.setProperty(_CONTEXT, [oParent.element, aAlignment[0], aAlignment[1]]);
		this.align();
	
	}

},


getConstrainedY: function (y) {

	var oMenu = this,
	
		aContext = oMenu.cfg.getProperty(_CONTEXT),
		nInitialMaxHeight = oMenu.cfg.getProperty(_MAX_HEIGHT),

		nMaxHeight,

		oOverlapPositions = {

			"trbr": true,
			"tlbl": true,
			"bltl": true,
			"brtr": true

		},

		bPotentialContextOverlap = (aContext && oOverlapPositions[aContext[1] + aContext[2]]),
	
		oMenuEl = oMenu.element,
		nMenuOffsetHeight = oMenuEl.offsetHeight,
	
		nViewportOffset = Overlay.VIEWPORT_OFFSET,
		viewPortHeight = Dom.getViewportHeight(),
		scrollY = Dom.getDocumentScrollTop(),

		bCanConstrain = 
			(oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT) + nViewportOffset < viewPortHeight),

		nAvailableHeight,

		oContextEl,
		nContextElY,
		nContextElHeight,

		bFlipped = false,

		nTopRegionHeight,
		nBottomRegionHeight,

		topConstraint = scrollY + nViewportOffset,
		bottomConstraint = scrollY + viewPortHeight - nMenuOffsetHeight - nViewportOffset,

		yNew = y;
		

	var flipVertical = function () {

		var nNewY;
	
		// The Menu is below the context element, flip it above
		if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { 
			nNewY = (nContextElY - nMenuOffsetHeight);
		}
		else {	// The Menu is above the context element, flip it below
			nNewY = (nContextElY + nContextElHeight);
		}

		oMenu.cfg.setProperty(_Y, (nNewY + scrollY), true);
		
		return nNewY;
	
	};


	/*
		 Uses the context element's position to calculate the availble height 
		 above and below it to display its corresponding Menu.
	*/

	var getDisplayRegionHeight = function () {

		// The Menu is below the context element
		if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) {
			return (nBottomRegionHeight - nViewportOffset);				
		}
		else {	// The Menu is above the context element
			return (nTopRegionHeight - nViewportOffset);				
		}

	};


	/*
		Sets the Menu's "y" configuration property to the correct value based on its
		current orientation.
	*/ 

	var alignY = function () {

		var nNewY;

		if ((oMenu.cfg.getProperty(_Y) - scrollY) > nContextElY) { 
			nNewY = (nContextElY + nContextElHeight);
		}
		else {	
			nNewY = (nContextElY - oMenuEl.offsetHeight);
		}

		oMenu.cfg.setProperty(_Y, (nNewY + scrollY), true);
	
	};


	//	Resets the maxheight of the Menu to the value set by the user

	var resetMaxHeight = function () {

		oMenu._setScrollHeight(this.cfg.getProperty(_MAX_HEIGHT));

		oMenu.hideEvent.unsubscribe(resetMaxHeight);
	
	};


	/*
		Trys to place the Menu in the best possible position (either above or 
		below its corresponding context element).
	*/

	var setVerticalPosition = function () {

		var nDisplayRegionHeight = getDisplayRegionHeight(),
			bMenuHasItems = (oMenu.getItems().length > 0),
			nMenuMinScrollHeight,
			fnReturnVal;


		if (nMenuOffsetHeight > nDisplayRegionHeight) {

			nMenuMinScrollHeight = 
				bMenuHasItems ? oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT) : nMenuOffsetHeight;


			if ((nDisplayRegionHeight > nMenuMinScrollHeight) && bMenuHasItems) {
				nMaxHeight = nDisplayRegionHeight;
			}
			else {
				nMaxHeight = nInitialMaxHeight;
			}


			oMenu._setScrollHeight(nMaxHeight);
			oMenu.hideEvent.subscribe(resetMaxHeight);
			

			// Re-align the Menu since its height has just changed
			// as a result of the setting of the maxheight property.

			alignY();
			

			if (nDisplayRegionHeight < nMenuMinScrollHeight) {

				if (bFlipped) {
	
					/*
						 All possible positions and values for the "maxheight" 
						 configuration property have been tried, but none were 
						 successful, so fall back to the original size and position.
					*/

					flipVertical();
					
				}
				else {
	
					flipVertical();

					bFlipped = true;
	
					fnReturnVal = setVerticalPosition();
	
				}
				
			}
		
		}
		else if (nMaxHeight && (nMaxHeight !== nInitialMaxHeight)) {
		
			oMenu._setScrollHeight(nInitialMaxHeight);
			oMenu.hideEvent.subscribe(resetMaxHeight);

			// Re-align the Menu since its height has just changed
			// as a result of the setting of the maxheight property.

			alignY();
		
		}

		return fnReturnVal;

	};


	// Determine if the current value for the Menu's "y" configuration property will
	// result in the Menu being positioned outside the boundaries of the viewport

	if (y < topConstraint || y  > bottomConstraint) {

		// The current value for the Menu's "y" configuration property WILL
		// result in the Menu being positioned outside the boundaries of the viewport

		if (bCanConstrain) {

			if (oMenu.cfg.getProperty(_PREVENT_CONTEXT_OVERLAP) && bPotentialContextOverlap) {
		
				//	SOLUTION #1:
				//	If the "preventcontextoverlap" configuration property is set to "true", 
				//	try to flip and/or scroll the Menu to both keep it inside the boundaries of the 
				//	viewport AND from overlaping its context element (MenuItem or MenuBarItem).

				oContextEl = aContext[0];
				nContextElHeight = oContextEl.offsetHeight;
				nContextElY = (Dom.getY(oContextEl) - scrollY);
	
				nTopRegionHeight = nContextElY;
				nBottomRegionHeight = (viewPortHeight - (nContextElY + nContextElHeight));
	
				setVerticalPosition();
				
				yNew = oMenu.cfg.getProperty(_Y);
		
			}
			else if (!(oMenu instanceof YAHOO.widget.MenuBar) && 
				nMenuOffsetHeight >= viewPortHeight) {

				//	SOLUTION #2:
				//	If the Menu exceeds the height of the viewport, introduce scroll bars
				//	to keep the Menu inside the boundaries of the viewport

				nAvailableHeight = (viewPortHeight - (nViewportOffset * 2));
		
				if (nAvailableHeight > oMenu.cfg.getProperty(_MIN_SCROLL_HEIGHT)) {
		
					oMenu._setScrollHeight(nAvailableHeight);
					oMenu.hideEvent.subscribe(resetMaxHeight);
		
					alignY();
					
					yNew = oMenu.cfg.getProperty(_Y);
				
				}
		
			}	
			else {

				//	SOLUTION #3:
			
				if (y < topConstraint) {
					yNew  = topConstraint;
				} else if (y  > bottomConstraint) {
					yNew  = bottomConstraint;
				}				
			
			}

		}
		else {
			//	The "y" configuration property cannot be set to a value that will keep
			//	entire Menu inside the boundary of the viewport.  Therefore, set  
			//	the "y" configuration property to scrollY to keep as much of the 
			//	Menu inside the viewport as possible.
			yNew = nViewportOffset + scrollY;
		}	

	}

	return yNew;

},


/**
* @method _onHide
* @description "hide" event handler for the 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.
*/
_onHide: function (p_sType, p_aArgs) {

	if (this.cfg.getProperty(_POSITION) === _DYNAMIC) {
	
		this.positionOffScreen();
	
	}

},


/**
* @method _onShow
* @description "show" event handler for the 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.
*/
_onShow: function (p_sType, p_aArgs) {

    var oParent = this.parent,
        oParentMenu,
		oElement,
		nOffsetWidth,
		sWidth;        


    function disableAutoSubmenuDisplay(p_oEvent) {

        var oTarget;

        if (p_oEvent.type == _MOUSEDOWN || (p_oEvent.type == _KEYDOWN && p_oEvent.keyCode == 27)) {

            /*  
                Set the "autosubmenudisplay" to "false" if the user
                clicks outside the menu bar.
            */

            oTarget = Event.getTarget(p_oEvent);

            if (oTarget != oParentMenu.element || !Dom.isAncestor(oParentMenu.element, oTarget)) {

                oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false);

                Event.removeListener(document, _MOUSEDOWN, disableAutoSubmenuDisplay);
                Event.removeListener(document, _KEYDOWN, disableAutoSubmenuDisplay);

            }
        
        }

    }


	function onSubmenuHide(p_sType, p_aArgs, p_sWidth) {
	
		this.cfg.setProperty(_WIDTH, _EMPTY_STRING);
		this.hideEvent.unsubscribe(onSubmenuHide, p_sWidth);
	
	}


    if (oParent) {

        oParentMenu = oParent.parent;


        if (!oParentMenu.cfg.getProperty(_AUTO_SUBMENU_DISPLAY) && 
            (oParentMenu instanceof YAHOO.widget.MenuBar || 
            oParentMenu.cfg.getProperty(_POSITION) == _STATIC)) {

            oParentMenu.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, true);

            Event.on(document, _MOUSEDOWN, disableAutoSubmenuDisplay);                             
            Event.on(document, _KEYDOWN, disableAutoSubmenuDisplay);

        }


		//	The following fixes an issue with the selected state of a MenuItem 
		//	not rendering correctly when a submenu is aligned to the left of
		//	its parent Menu instance.

		if ((this.cfg.getProperty("x") < oParentMenu.cfg.getProperty("x")) && 
			(UA.gecko && UA.gecko < 1.9) && !this.cfg.getProperty(_WIDTH)) {

			oElement = this.element;
			nOffsetWidth = oElement.offsetWidth;
			
			/*
				Measuring the difference of the offsetWidth before and after
				setting the "width" style attribute allows us to compute the 
				about of padding and borders applied to the element, which in 
				turn allows us to set the "width" property correctly.
			*/
			
			oElement.style.width = nOffsetWidth + _PX;
			
			sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX;
			
			this.cfg.setProperty(_WIDTH, sWidth);
		
			this.hideEvent.subscribe(onSubmenuHide, sWidth);
		
		}

    }


	/*
		Dynamically positioned, root Menus focus themselves when visible, and 
		will then, when hidden, restore focus to the UI control that had focus 
		before the Menu was made visible.
	*/ 

	if (this === this.getRoot() && this.cfg.getProperty(_POSITION) === _DYNAMIC) {

		this._focusedElement = oFocusedElement;
		
		this.focus();
	
	}


},


/**
* @method _onBeforeHide
* @description "beforehide" event handler for the 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.
*/
_onBeforeHide: function (p_sType, p_aArgs) {

    var oActiveItem = this.activeItem,
        oRoot = this.getRoot(),
        oConfig,
        oSubmenu;


    if (oActiveItem) {

        oConfig = oActiveItem.cfg;

        oConfig.setProperty(_SELECTED, false);

        oSubmenu = oConfig.getProperty(_SUBMENU);

        if (oSubmenu) {

            oSubmenu.hide();

        }

    }


	/*
		Focus can get lost in IE when the mouse is moving from a submenu back to its parent Menu.  
		For this reason, it is necessary to maintain the focused state in a private property 
		so that the _onMouseOver event handler is able to determined whether or not to set focus
		to MenuItems as the user is moving the mouse.
	*/ 

	if (UA.ie && this.cfg.getProperty(_POSITION) === _DYNAMIC && this.parent) {

		oRoot._hasFocus = this.hasFocus();
	
	}


    if (oRoot == this) {

        oRoot.blur();
    
    }

},


/**
* @method _onParentMenuConfigChange
* @description "configchange" event handler for a submenu.
* @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.
* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that 
* subscribed to the event.
*/
_onParentMenuConfigChange: function (p_sType, p_aArgs, p_oSubmenu) {
    
    var sPropertyName = p_aArgs[0][0],
        oPropertyValue = p_aArgs[0][1];

    switch(sPropertyName) {

        case _IFRAME:
        case _CONSTRAIN_TO_VIEWPORT:
        case _HIDE_DELAY:
        case _SHOW_DELAY:
        case _SUBMENU_HIDE_DELAY:
        case _CLICK_TO_HIDE:
        case _EFFECT:
        case _CLASSNAME:
        case _SCROLL_INCREMENT:
        case _MAX_HEIGHT:
        case _MIN_SCROLL_HEIGHT:
        case _MONITOR_RESIZE:
        case _SHADOW:
        case _PREVENT_CONTEXT_OVERLAP:
		case _KEEP_OPEN:

            p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue);
                
        break;
        
        case _SUBMENU_ALIGNMENT:

			if (!(this.parent.parent instanceof YAHOO.widget.MenuBar)) {
		
				p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue);
		
			}
        
        break;
        
    }
    
},


/**
* @method _onParentMenuRender
* @description "render" event handler for a submenu.  Renders a  
* submenu in response to the firing of its parent's "render" event.
* @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.
* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that 
* subscribed to the event.
*/
_onParentMenuRender: function (p_sType, p_aArgs, p_oSubmenu) {

    var oParentMenu = p_oSubmenu.parent.parent,
    	oParentCfg = oParentMenu.cfg,

        oConfig = {

            constraintoviewport: oParentCfg.getProperty(_CONSTRAIN_TO_VIEWPORT),

            xy: [0,0],

            clicktohide: oParentCfg.getProperty(_CLICK_TO_HIDE),
                
            effect: oParentCfg.getProperty(_EFFECT),

            showdelay: oParentCfg.getProperty(_SHOW_DELAY),
            
            hidedelay: oParentCfg.getProperty(_HIDE_DELAY),

            submenuhidedelay: oParentCfg.getProperty(_SUBMENU_HIDE_DELAY),

            classname: oParentCfg.getProperty(_CLASSNAME),
            
            scrollincrement: oParentCfg.getProperty(_SCROLL_INCREMENT),
            
			maxheight: oParentCfg.getProperty(_MAX_HEIGHT),

            minscrollheight: oParentCfg.getProperty(_MIN_SCROLL_HEIGHT),
            
            iframe: oParentCfg.getProperty(_IFRAME),
            
            shadow: oParentCfg.getProperty(_SHADOW),

			preventcontextoverlap: oParentCfg.getProperty(_PREVENT_CONTEXT_OVERLAP),
            
            monitorresize: oParentCfg.getProperty(_MONITOR_RESIZE),

			keepopen: oParentCfg.getProperty(_KEEP_OPEN)

        },
        
        oLI;


	
	if (!(oParentMenu instanceof YAHOO.widget.MenuBar)) {

		oConfig[_SUBMENU_ALIGNMENT] = oParentCfg.getProperty(_SUBMENU_ALIGNMENT);

	}


    p_oSubmenu.cfg.applyConfig(oConfig);


    if (!this.lazyLoad) {

        oLI = this.parent.element;

        if (this.element.parentNode == oLI) {
    
            this.render();
    
        }
        else {

            this.render(oLI);
    
        }

    }
    
},


/**
* @method _onMenuItemDestroy
* @description "destroy" event handler for the menu's items.
* @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.
* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 
* that fired the event.
*/
_onMenuItemDestroy: function (p_sType, p_aArgs, p_oItem) {

    this._removeItemFromGroupByValue(p_oItem.groupIndex, p_oItem);

},


/**
* @method _onMenuItemConfigChange
* @description "configchange" event handler for the menu's items.
* @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.
* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item 
* that fired the event.
*/
_onMenuItemConfigChange: function (p_sType, p_aArgs, p_oItem) {

    var sPropertyName = p_aArgs[0][0],
        oPropertyValue = p_aArgs[0][1],
        oSubmenu;


    switch(sPropertyName) {

        case _SELECTED:

            if (oPropertyValue === true) {

                this.activeItem = p_oItem;
            
            }

        break;

        case _SUBMENU:

            oSubmenu = p_aArgs[0][1];

            if (oSubmenu) {

                this._configureSubmenu(p_oItem);

            }

        break;

    }

},



// Public event handlers for configuration properties


/**
* @method configVisible
* @description Event handler for when the "visible" configuration property 
* the menu changes.
* @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.
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
* fired the event.
*/
configVisible: function (p_sType, p_aArgs, p_oMenu) {

    var bVisible,
        sDisplay;

    if (this.cfg.getProperty(_POSITION) == _DYNAMIC) {

        Menu.superclass.configVisible.call(this, p_sType, p_aArgs, p_oMenu);

    }
    else {

        bVisible = p_aArgs[0];
        sDisplay = Dom.getStyle(this.element, _DISPLAY);

        Dom.setStyle(this.element, _VISIBILITY, _VISIBLE);

        if (bVisible) {

            if (sDisplay != _BLOCK) {
                this.beforeShowEvent.fire();
                Dom.setStyle(this.element, _DISPLAY, _BLOCK);
                this.showEvent.fire();
            }
        
        }
        else {

			if (sDisplay == _BLOCK) {
				this.beforeHideEvent.fire();
				Dom.setStyle(this.element, _DISPLAY, _NONE);
				this.hideEvent.fire();
			}
        
        }

    }

},


/**
* @method configPosition
* @description Event handler for when the "position" configuration property 
* of the menu changes.
* @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.
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
* fired the event.
*/
configPosition: function (p_sType, p_aArgs, p_oMenu) {

    var oElement = this.element,
        sCSSPosition = p_aArgs[0] == _STATIC ? _STATIC : _ABSOLUTE,
        oCfg = this.cfg,
        nZIndex;


    Dom.setStyle(oElement, _POSITION, sCSSPosition);


    if (sCSSPosition == _STATIC) {

        // Statically positioned menus are visible by default
        
        Dom.setStyle(oElement, _DISPLAY, _BLOCK);

        oCfg.setProperty(_VISIBLE, true);

    }
    else {

        /*
            Even though the "visible" property is queued to 
            "false" by default, we need to set the "visibility" property to 
            "hidden" since Overlay's "configVisible" implementation checks the 
            element's "visibility" style property before deciding whether 
            or not to show an Overlay instance.
        */

        Dom.setStyle(oElement, _VISIBILITY, _HIDDEN);
    
    }

  	 
     if (sCSSPosition == _ABSOLUTE) { 	 
  	 
         nZIndex = oCfg.getProperty(_ZINDEX);
  	 
         if (!nZIndex || nZIndex === 0) { 	 
  	 
             oCfg.setProperty(_ZINDEX, 1); 	 
  	 
         } 	 
  	 
     }

},


/**
* @method configIframe
* @description Event handler for when the "iframe" configuration property of 
* the menu changes.
* @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.
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
* fired the event.
*/
configIframe: function (p_sType, p_aArgs, p_oMenu) {    

    if (this.cfg.getProperty(_POSITION) == _DYNAMIC) {

        Menu.superclass.configIframe.call(this, p_sType, p_aArgs, p_oMenu);

    }

},


/**
* @method configHideDelay
* @description Event handler for when the "hidedelay" configuration property 
* of the menu changes.
* @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.
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
* fired the event.
*/
configHideDelay: function (p_sType, p_aArgs, p_oMenu) {

    var nHideDelay = p_aArgs[0];

	this._useHideDelay = (nHideDelay > 0);

},


/**
* @method configContainer
* @description Event handler for when the "container" configuration property 
* of the menu changes.
* @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.
* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that 
* fired the event.
*/
configContainer: function (p_sType, p_aArgs, p_oMenu) {

	var oElement = p_aArgs[0];

	if (Lang.isString(oElement)) {

        this.cfg.setProperty(_CONTAINER, Dom.get(oElement), true);

	}

},


/**
* @method _clearSetWidthFlag
* @description Change event listener for the "width" configuration property.  This listener is 
* added when a Menu's "width" configuration property is set by the "_setScrollHeight" method, and 
* is used to set the "_widthSetForScroll" property to "false" if the "width" configuration property 
* is changed after it was set by the "_setScrollHeight" method.  If the "_widthSetForScroll" 
* property is set to "false", and the "_setScrollHeight" method is in the process of tearing down 
* scrolling functionality, it will maintain the Menu's new width rather than reseting it.
* @private
*/
_clearSetWidthFlag: function () {

	this._widthSetForScroll = false;
	
	this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag);

},


/**
* @method _setScrollHeight
* @description 
* @param {String} p_nScrollHeight Number representing the scrolling height of the Menu.
* @private
*/
_setScrollHeight: function (p_nScrollHeight) {

    var nScrollHeight = p_nScrollHeight,
		bRefireIFrameAndShadow = false,
		bSetWidth = false,
        oElement,
        oBody,
        oHeader,
        oFooter,
        fnMouseOver,
        fnMouseOut,
        nMinScrollHeight,
        nHeight,
        nOffsetWidth,
        sWidth;


	if (this.getItems().length > 0) {
	
        oElement = this.element;
        oBody = this.body;
        oHeader = this.header;
        oFooter = this.footer;
        fnMouseOver = this._onScrollTargetMouseOver;
        fnMouseOut = this._onScrollTargetMouseOut;
        nMinScrollHeight = this.cfg.getProperty(_MIN_SCROLL_HEIGHT);


		if (nScrollHeight > 0 && nScrollHeight < nMinScrollHeight) {
		
			nScrollHeight = nMinScrollHeight;
		
		}


		Dom.setStyle(oBody, _HEIGHT, _EMPTY_STRING);
		Dom.removeClass(oBody, _YUI_MENU_BODY_SCROLLED);
		oBody.scrollTop = 0;


		//	Need to set a width for the Menu to fix the following problems in 
		//	Firefox 2 and IE:

		//	#1) Scrolled Menus will render at 1px wide in Firefox 2

		//	#2) There is a bug in gecko-based browsers where an element whose 
		//	"position" property is set to "absolute" and "overflow" property is 
		//	set to "hidden" will not render at the correct width when its 
		//	offsetParent's "position" property is also set to "absolute."  It is 
		//	possible to work around this bug by specifying a value for the width 
		//	property in addition to overflow.

		//	#3) In IE it is necessary to give the Menu a width before the 
		//	scrollbars are rendered to prevent the Menu from rendering with a 
		//	width that is 100% of the browser viewport.
	
		bSetWidth = ((UA.gecko && UA.gecko < 1.9) || UA.ie);

		if (nScrollHeight > 0 && bSetWidth && !this.cfg.getProperty(_WIDTH)) {

			nOffsetWidth = oElement.offsetWidth;
	
			/*
				Measuring the difference of the offsetWidth before and after
				setting the "width" style attribute allows us to compute the 
				about of padding and borders applied to the element, which in 
				turn allows us to set the "width" property correctly.
			*/
			
			oElement.style.width = nOffsetWidth + _PX;
	
			sWidth = (nOffsetWidth - (oElement.offsetWidth - nOffsetWidth)) + _PX;


			this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag);


			this.cfg.setProperty(_WIDTH, sWidth);


			/*
				Set a flag (_widthSetForScroll) to maintain some history regarding how the 
				"width" configuration property was set.  If the "width" configuration property 
				is set by something other than the "_setScrollHeight" method, it will be 
				necessary to maintain that new value and not clear the width if scrolling 
				is turned off.
			*/

			this._widthSetForScroll = true;

			this.cfg.subscribeToConfigEvent(_WIDTH, this._clearSetWidthFlag);
	
		}
	
	
		if (nScrollHeight > 0 && (!oHeader && !oFooter)) {
	
	
			this.setHeader(_NON_BREAKING_SPACE);
			this.setFooter(_NON_BREAKING_SPACE);
	
			oHeader = this.header;
			oFooter = this.footer;
	
			Dom.addClass(oHeader, _TOP_SCROLLBAR);
			Dom.addClass(oFooter, _BOTTOM_SCROLLBAR);
			
			oElement.insertBefore(oHeader, oBody);
			oElement.appendChild(oFooter);
		
		}
	
	
		nHeight = nScrollHeight;
	
	
		if (oHeader && oFooter) {
			nHeight = (nHeight - (oHeader.offsetHeight + oFooter.offsetHeight));
		}
	
	
		if ((nHeight > 0) && (oBody.offsetHeight > nScrollHeight)) {

	
			Dom.addClass(oBody, _YUI_MENU_BODY_SCROLLED);
			Dom.setStyle(oBody, _HEIGHT, (nHeight + _PX));

			if (!this._hasScrollEventHandlers) {
	
				Event.on(oHeader, _MOUSEOVER, fnMouseOver, this, true);
				Event.on(oHeader, _MOUSEOUT, fnMouseOut, this, true);
				Event.on(oFooter, _MOUSEOVER, fnMouseOver, this, true);
				Event.on(oFooter, _MOUSEOUT, fnMouseOut, this, true);
	
				this._hasScrollEventHandlers = true;
	
			}
	
			this._disableScrollHeader();
			this._enableScrollFooter();
			
			bRefireIFrameAndShadow = true;			
	
		}
		else if (oHeader && oFooter) {

	

			/*
				Only clear the the "width" configuration property if it was set the 
				"_setScrollHeight" method and wasn't changed by some other means after it was set.
			*/	
	
			if (this._widthSetForScroll) {
	

				this._widthSetForScroll = false;

				this.cfg.unsubscribeFromConfigEvent(_WIDTH, this._clearSetWidthFlag);
	
				this.cfg.setProperty(_WIDTH, _EMPTY_STRING);
			
			}
	
	
			this._enableScrollHeader();
			this._enableScrollFooter();
	
			if (this._hasScrollEventHandlers) {
	
				Event.removeListener(oHeader, _MOUSEOVER, fnMouseOver);
				Event.removeListener(oHeader, _MOUSEOUT, fnMouseOut);
				Event.removeListener(oFooter, _MOUSEOVER, fnMouseOver);
				Event.removeListener(oFooter, _MOUSEOUT, fnMouseOut);

				this._hasScrollEventHandlers = false;
	
			}

			oElement.removeChild(oHeader);
			oElement.removeChild(oFooter);
	
			this.header = null;
			this.footer = null;
			
			bRefireIFrameAndShadow = true;
		
		}


		if (bRefireIFrameAndShadow) {
	
			this.cfg.refireEvent(_IFRAME);
			this.cfg.refireEvent(_SHADOW);
		
		}
	
	}

},


/**
* @method _setMaxHeight
* @description "renderEvent" handler used to defer the setting of the 
* "maxheight" configuration property until the menu is rendered in lazy 
* load scenarios.
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {Number} p_nMaxHeight Number representing the value to set for the 
* "maxheight" configuration property.
* @private
*/
_setMaxHeight: function (p_sType, p_aArgs, p_nMaxHeight) {

    this._setScrollHeight(p_nMaxHeight);
    this.renderEvent.unsubscribe(this._setMaxHeight);

},


/**
* @method configMaxHeight
* @description Event handler for when the "maxheight" configuration property of 
* a Menu changes.
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired
* the event.
*/
configMaxHeight: function (p_sType, p_aArgs, p_oMenu) {

	var nMaxHeight = p_aArgs[0];

	if (this.lazyLoad && !this.body && nMaxHeight > 0) {
	
		this.renderEvent.subscribe(this._setMaxHeight, nMaxHeight, this);

	}
	else {

		this._setScrollHeight(nMaxHeight);
	
	}

},


/**
* @method configClassName
* @description Event handler for when the "classname" configuration property of 
* a menu changes.
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event was fired.
* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event.
*/
configClassName: function (p_sType, p_aArgs, p_oMenu) {

    var sClassName = p_aArgs[0];

    if (this._sClassName) {

        Dom.removeClass(this.element, this._sClassName);

    }

    Dom.addClass(this.element, sClassName);
    this._sClassName = sClassName;

},


/**
* @method _onItemAdded
* @description "itemadded" event handler for a Menu instance.
* @private
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event 
* was fired.
*/
_onItemAdded: function (p_sType, p_aArgs) {

    var oItem = p_aArgs[0];
    
    if (oItem) {

        oItem.cfg.setProperty(_DISABLED, true);
    
    }

},


/**
* @method configDisabled
* @description Event handler for when the "disabled" configuration property of 
* a menu changes.
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event was fired.
* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event.
*/
configDisabled: function (p_sType, p_aArgs, p_oMenu) {

    var bDisabled = p_aArgs[0],
        aItems = this.getItems(),
        nItems,
        i;

    if (Lang.isArray(aItems)) {

        nItems = aItems.length;
    
        if (nItems > 0) {
        
            i = nItems - 1;
    
            do {
    
                aItems[i].cfg.setProperty(_DISABLED, bDisabled);
            
            }
            while (i--);
        
        }


        if (bDisabled) {

            this.clearActiveItem(true);

            Dom.addClass(this.element, _DISABLED);

            this.itemAddedEvent.subscribe(this._onItemAdded);

        }
        else {

            Dom.removeClass(this.element, _DISABLED);

            this.itemAddedEvent.unsubscribe(this._onItemAdded);

        }
        
    }

},


/**
* @method configShadow
* @description Event handler for when the "shadow" configuration property of 
* a menu changes.
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event was fired.
* @param {YAHOO.widget.Menu} p_oMenu The Menu instance fired the event.
*/
configShadow: function (p_sType, p_aArgs, p_oMenu) {

    var sizeShadow = function () {

        var oElement = this.element,
            oShadow = this._shadow;
    
        if (oShadow && oElement) {

			// Clear the previous width

			if (oShadow.style.width && oShadow.style.height) {
			
				oShadow.style.width = _EMPTY_STRING;
				oShadow.style.height = _EMPTY_STRING;
			
			}

            oShadow.style.width = (oElement.offsetWidth + 6) + _PX;
            oShadow.style.height = (oElement.offsetHeight + 1) + _PX;
            
        }
    
    };


    var replaceShadow = function () {

        this.element.appendChild(this._shadow);

    };


    var addShadowVisibleClass = function () {
    
        Dom.addClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE);
    
    };
    

    var removeShadowVisibleClass = function () {

        Dom.removeClass(this._shadow, _YUI_MENU_SHADOW_VISIBLE);
    
    };


    var createShadow = function () {

        var oShadow = this._shadow,
            oElement;

        if (!oShadow) {

            oElement = this.element;


            if (!m_oShadowTemplate) {

                m_oShadowTemplate = document.createElement(_DIV_LOWERCASE);
                m_oShadowTemplate.className = _YUI_MENU_SHADOW_YUI_MENU_SHADOW_VISIBLE;
            
            }

            oShadow = m_oShadowTemplate.cloneNode(false);

            oElement.appendChild(oShadow);
            
            this._shadow = oShadow;

            this.beforeShowEvent.subscribe(addShadowVisibleClass);
            this.beforeHideEvent.subscribe(removeShadowVisibleClass);


            if (UA.ie) {
        
                /*
                     Need to call sizeShadow & syncIframe via setTimeout for 
                     IE 7 Quirks Mode and IE 6 Standards Mode and Quirks Mode 
                     or the shadow and iframe shim will not be sized and 
                     positioned properly.
                */
        
				Lang.later(0, this, function () {

                    sizeShadow.call(this); 
                    this.syncIframe();
				
				});


                this.cfg.subscribeToConfigEvent(_WIDTH, sizeShadow);
                this.cfg.subscribeToConfigEvent(_HEIGHT, sizeShadow);
                this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, sizeShadow);
                this.changeContentEvent.subscribe(sizeShadow);

                Module.textResizeEvent.subscribe(sizeShadow, this, true);
                
                this.destroyEvent.subscribe(function () {
                
                    Module.textResizeEvent.unsubscribe(sizeShadow, this);
                
                });
        
            }

            this.cfg.subscribeToConfigEvent(_MAX_HEIGHT, replaceShadow);

        }

    };


    var onBeforeShow = function () {

    	if (this._shadow) {

			// If called because the "shadow" event was refired - just append again and resize
			
			replaceShadow.call(this);
			
			if (UA.ie) {
				sizeShadow.call(this);
			}
    	
    	}
    	else {
    
        	createShadow.call(this);
        
        }

        this.beforeShowEvent.unsubscribe(onBeforeShow);
    
    };


	var bShadow = p_aArgs[0];


    if (bShadow && this.cfg.getProperty(_POSITION) == _DYNAMIC) {

        if (this.cfg.getProperty(_VISIBLE)) {

			if (this._shadow) {

				// If the "shadow" event was refired - just append again and resize
				
				replaceShadow.call(this);
				
				if (UA.ie) {
					sizeShadow.call(this);
				}
				
			} 
			else {
            	createShadow.call(this);
            }
        
        }
        else {

            this.beforeShowEvent.subscribe(onBeforeShow);
        
        }
    
    }
    
},



// Public methods


/**
* @method initEvents
* @description Initializes the custom events for the menu.
*/
initEvents: function () {

	Menu.superclass.initEvents.call(this);

    // Create custom events

	var i = EVENT_TYPES.length - 1,
		aEventData,
		oCustomEvent;


	do {

		aEventData = EVENT_TYPES[i];

		oCustomEvent = this.createEvent(aEventData[1]);
		oCustomEvent.signature = CustomEvent.LIST;
		
		this[aEventData[0]] = oCustomEvent;

	}
	while (i--);

},


/**
* @method positionOffScreen
* @description Positions the menu outside of the boundaries of the browser's 
* viewport.  Called automatically when a menu is hidden to ensure that 
* it doesn't force the browser to render uncessary scrollbars.
*/
positionOffScreen: function () {

    var oIFrame = this.iframe,
    	oElement = this.element,
        sPos = this.OFF_SCREEN_POSITION;
    
    oElement.style.top = _EMPTY_STRING;
    oElement.style.left = _EMPTY_STRING;
    
    if (oIFrame) {

		oIFrame.style.top = sPos;
		oIFrame.style.left = sPos;
    
    }

},


/**
* @method getRoot
* @description Finds the menu's root menu.
*/
getRoot: function () {

    var oItem = this.parent,
        oParentMenu,
        returnVal;

    if (oItem) {

        oParentMenu = oItem.parent;

        returnVal = oParentMenu ? oParentMenu.getRoot() : this;

    }
    else {
    
        returnVal = this;
    
    }
    
    return returnVal;

},


/**
* @method toString
* @description Returns a string representing the menu.
* @return {String}
*/
toString: function () {

    var sReturnVal = _MENU,
        sId = this.id;

    if (sId) {

        sReturnVal += (_SPACE + sId);
    
    }

    return sReturnVal;

},


/**
* @method setItemGroupTitle
* @description Sets the title of a group of menu items.
* @param {String} p_sGroupTitle String specifying the title of the group.
* @param {Number} p_nGroupIndex Optional. Number specifying the group to which
* the title belongs.
*/
setItemGroupTitle: function (p_sGroupTitle, p_nGroupIndex) {

    var nGroupIndex,
        oTitle,
        i,
        nFirstIndex;
        
    if (Lang.isString(p_sGroupTitle) && p_sGroupTitle.length > 0) {

        nGroupIndex = Lang.isNumber(p_nGroupIndex) ? p_nGroupIndex : 0;
        oTitle = this._aGroupTitleElements[nGroupIndex];


        if (oTitle) {

            oTitle.innerHTML = p_sGroupTitle;
            
        }
        else {

            oTitle = document.createElement(this.GROUP_TITLE_TAG_NAME);
                    
            oTitle.innerHTML = p_sGroupTitle;

            this._aGroupTitleElements[nGroupIndex] = oTitle;

        }


        i = this._aGroupTitleElements.length - 1;

        do {

            if (this._aGroupTitleElements[i]) {

                Dom.removeClass(this._aGroupTitleElements[i], _FIRST_OF_TYPE);

                nFirstIndex = i;

            }

        }
        while (i--);


        if (nFirstIndex !== null) {

            Dom.addClass(this._aGroupTitleElements[nFirstIndex], 
                _FIRST_OF_TYPE);

        }

        this.changeContentEvent.fire();

    }

},



/**
* @method addItem
* @description Appends an item to the menu.
* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
* instance to be added to the menu.
* @param {String} p_oItem String specifying the text of the item to be added 
* to the menu.
* @param {Object} p_oItem Object literal containing a set of menu item 
* configuration properties.
* @param {Number} p_nGroupIndex Optional. Number indicating the group to
* which the item belongs.
* @return {YAHOO.widget.MenuItem}
*/
addItem: function (p_oItem, p_nGroupIndex) {

	return this._addItemToGroup(p_nGroupIndex, p_oItem);

},


/**
* @method addItems
* @description Adds an array of items to the menu.
* @param {Array} p_aItems Array of items to be added to the menu.  The array 
* can contain strings specifying the text for each item to be created, object
* literals specifying each of the menu item configuration properties, 
* or MenuItem instances.
* @param {Number} p_nGroupIndex Optional. Number specifying the group to 
* which the items belongs.
* @return {Array}
*/
addItems: function (p_aItems, p_nGroupIndex) {

    var nItems,
        aItems,
        oItem,
        i,
        returnVal;


    if (Lang.isArray(p_aItems)) {

        nItems = p_aItems.length;
        aItems = [];

        for(i=0; i<nItems; i++) {

            oItem = p_aItems[i];

            if (oItem) {

                if (Lang.isArray(oItem)) {
    
                    aItems[aItems.length] = this.addItems(oItem, i);
    
                }
                else {
    
                    aItems[aItems.length] = this._addItemToGroup(p_nGroupIndex, oItem);
                
                }

            }
    
        }


        if (aItems.length) {
        
            returnVal = aItems;
        
        }

    }

	return returnVal;

},


/**
* @method insertItem
* @description Inserts an item into the menu at the specified index.
* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem 
* instance to be added to the menu.
* @param {String} p_oItem String specifying the text of the item to be added 
* to the menu.
* @param {Object} p_oItem Object literal containing a set of menu item 
* configuration properties.
* @param {Number} p_nItemIndex Number indicating the ordinal position at which
* the item should be added.
* @param {Number} p_nGroupIndex Optional. Number indicating the group to which 
* the item belongs.
* @return {YAHOO.widget.MenuItem}
*/
insertItem: function (p_oItem, p_nItemIndex, p_nGroupIndex) {
    
	return this._addItemToGroup(p_nGroupIndex, p_oItem, p_nItemIndex);

},


/**
* @method removeItem
* @description Removes the specified item from the menu.
* @param {YAHOO.widget.MenuItem} p_oObject Object reference for the MenuItem 
* instance to be removed from the menu.
* @param {Number} p_oObject Number specifying the index of the item 
* to be removed.
* @param {Number} p_nGroupIndex Optional. Number specifying the group to 
* which the item belongs.
* @return {YAHOO.widget.MenuItem}
*/
removeItem: function (p_oObject, p_nGroupIndex) {

    var oItem,
    	returnVal;
    
    if (!Lang.isUndefined(p_oObject)) {

        if (p_oObject instanceof YAHOO.widget.MenuItem) {

            oItem = this._removeItemFromGroupByValue(p_nGroupIndex, p_oObject);           

        }
        else if (Lang.isNumber(p_oObject)) {

            oItem = this._removeItemFromGroupByIndex(p_nGroupIndex, p_oObject);

        }

        if (oItem) {

            oItem.destroy();


            returnVal = oItem;

        }

    }

	return returnVal;

},


/**
* @method getItems
* @description Returns an array of all of the items in the menu.
* @return {Array}
*/
getItems: function () {

    var aGroups = this._aItemGroups,
        nGroups,
        returnVal,
        aItems = [];


    if (Lang.isArray(aGroups)) {

        nGroups = aGroups.length;

        returnVal = ((nGroups == 1) ? aGroups[0] : (Array.prototype.concat.apply(aItems, aGroups)));

    }

	return returnVal;

},


/**
* @method getItemGroups
* @description Multi-dimensional Array representing the menu items as they 
* are grouped in the menu.
* @return {Array}
*/        
getItemGroups: function () {

    return this._aItemGroups;

},


/**
* @method getItem
* @description Returns the item at the specified index.
* @param {Number} p_nItemIndex Number indicating the ordinal position of the 
* item to be retrieved.
* @param {Number} p_nGroupIndex Optional. Number indicating the group to which 
* the item belongs.
* @return {YAHOO.widget.MenuItem}
*/
getItem: function (p_nItemIndex, p_nGroupIndex) {
    
    var aGroup,
    	returnVal;
    
    if (Lang.isNumber(p_nItemIndex)) {

        aGroup = this._getItemGroup(p_nGroupIndex);

        if (aGroup) {

            returnVal = aGroup[p_nItemIndex];
        
        }

    }
    
    return returnVal;
    
},


/**
* @method getSubmenus
* @description Returns an array of all of the submenus that are immediate 
* children of the menu.
* @return {Array}
*/
getSubmenus: function () {

    var aItems = this.getItems(),
        nItems = aItems.length,
        aSubmenus,
        oSubmenu,
        oItem,
        i;


    if (nItems > 0) {
        
        aSubmenus = [];

        for(i=0; i<nItems; i++) {

            oItem = aItems[i];
            
            if (oItem) {

                oSubmenu = oItem.cfg.getProperty(_SUBMENU);
                
                if (oSubmenu) {

                    aSubmenus[aSubmenus.length] = oSubmenu;

                }
            
            }
        
        }
    
    }

    return aSubmenus;

},


/**
* @method clearContent
* @description Removes all of the content from the menu, including the menu 
* items, group titles, header and footer.
*/
clearContent: function () {

    var aItems = this.getItems(),
        nItems = aItems.length,
        oElement = this.element,
        oBody = this.body,
        oHeader = this.header,
        oFooter = this.footer,
        oItem,
        oSubmenu,
        i;


    if (nItems > 0) {

        i = nItems - 1;

        do {

            oItem = aItems[i];

            if (oItem) {

                oSubmenu = oItem.cfg.getProperty(_SUBMENU);

                if (oSubmenu) {

                    this.cfg.configChangedEvent.unsubscribe(
                        this._onParentMenuConfigChange, oSubmenu);

                    this.renderEvent.unsubscribe(this._onParentMenuRender, 
                        oSubmenu);

                }
                
                this.removeItem(oItem, oItem.groupIndex);

            }
        
        }
        while (i--);

    }


    if (oHeader) {

        Event.purgeElement(oHeader);
        oElement.removeChild(oHeader);

    }
    

    if (oFooter) {

        Event.purgeElement(oFooter);
        oElement.removeChild(oFooter);
    }


    if (oBody) {

        Event.purgeElement(oBody);

        oBody.innerHTML = _EMPTY_STRING;

    }

    this.activeItem = null;

    this._aItemGroups = [];
    this._aListElements = [];
    this._aGroupTitleElements = [];

    this.cfg.setProperty(_WIDTH, null);

},


/**
* @method destroy
* @description Removes the menu's <code>&#60;div&#62;</code> element 
* (and accompanying child nodes) from the document.
*/
destroy: function () {

    // Remove all items

    this.clearContent();

    this._aItemGroups = null;
    this._aListElements = null;
    this._aGroupTitleElements = null;


    // Continue with the superclass implementation of this method

    Menu.superclass.destroy.call(this);
    

},


/**
* @method setInitialFocus
* @description Sets focus to the menu's first enabled item.
*/
setInitialFocus: function () {

    var oItem = this._getFirstEnabledItem();
    
    if (oItem) {

        oItem.focus();

    }
    
},


/**
* @method setInitialSelection
* @description Sets the "selected" configuration property of the menu's first 
* enabled item to "true."
*/
setInitialSelection: function () {

    var oItem = this._getFirstEnabledItem();
    
    if (oItem) {
    
        oItem.cfg.setProperty(_SELECTED, true);
    }        

},


/**
* @method clearActiveItem
* @description Sets the "selected" configuration property of the menu's active
* item to "false" and hides the item's submenu.
* @param {Boolean} p_bBlur Boolean indicating if the menu's active item 
* should be blurred.  
*/
clearActiveItem: function (p_bBlur) {

    if (this.cfg.getProperty(_SHOW_DELAY) > 0) {
    
        this._cancelShowDelay();
    
    }


    var oActiveItem = this.activeItem,
        oConfig,
        oSubmenu;

    if (oActiveItem) {

        oConfig = oActiveItem.cfg;

        if (p_bBlur) {

            oActiveItem.blur();
            
            this.getRoot()._hasFocus = true;
        
        }

        oConfig.setProperty(_SELECTED, false);

        oSubmenu = oConfig.getProperty(_SUBMENU);


        if (oSubmenu) {

            oSubmenu.hide();

        }

        this.activeItem = null;  

    }

},


/**
* @method focus
* @description Causes the menu to receive focus and fires the "focus" event.
*/
focus: function () {

    if (!this.hasFocus()) {

        this.setInitialFocus();
    
    }

},


/**
* @method blur
* @description Causes the menu to lose focus and fires the "blur" event.
*/    
blur: function () {

    var oItem;

    if (this.hasFocus()) {
    
        oItem = MenuManager.getFocusedMenuItem();
        
        if (oItem) {

            oItem.blur();

        }

    }

},


/**
* @method hasFocus
* @description Returns a boolean indicating whether or not the menu has focus.
* @return {Boolean}
*/
hasFocus: function () {

    return (MenuManager.getFocusedMenu() == this.getRoot());

},


_doItemSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) {

    var oItem = p_aArgs[0],
        oSubmenu = oItem.cfg.getProperty(_SUBMENU);

    if (oSubmenu) {
        oSubmenu.subscribe.apply(oSubmenu, p_oObject);
    }

},


_doSubmenuSubscribe: function (p_sType, p_aArgs, p_oObject) { 

    var oSubmenu = this.cfg.getProperty(_SUBMENU);
    
    if (oSubmenu) {
        oSubmenu.subscribe.apply(oSubmenu, p_oObject);
    }

},


/**
* Adds the specified CustomEvent subscriber to the menu and each of 
* its submenus.
* @method subscribe
* @param p_type     {string}   the type, or name of the event
* @param p_fn       {function} the function to exectute when the event fires
* @param p_obj      {Object}   An object to be passed along when the event 
*                              fires
* @param p_override {boolean}  If true, the obj passed in becomes the 
*                              execution scope of the listener
*/
subscribe: function () {

	//	Subscribe to the event for this Menu instance
    Menu.superclass.subscribe.apply(this, arguments);

	//	Subscribe to the "itemAdded" event so that all future submenus
	//	also subscribe to this event
    Menu.superclass.subscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, arguments);


    var aItems = this.getItems(),
        nItems,
        oItem,
        oSubmenu,
        i;
        

    if (aItems) {

        nItems = aItems.length;
        
        if (nItems > 0) {
        
            i = nItems - 1;
            
            do {

                oItem = aItems[i];
                oSubmenu = oItem.cfg.getProperty(_SUBMENU);
                
                if (oSubmenu) {
                    oSubmenu.subscribe.apply(oSubmenu, arguments);
                }
                else {
                    oItem.cfg.subscribeToConfigEvent(_SUBMENU, this._doSubmenuSubscribe, arguments);
                }

            }
            while (i--);
        
        }

    }

},


unsubscribe: function () {

	//	Remove the event for this Menu instance
    Menu.superclass.unsubscribe.apply(this, arguments);

	//	Remove the "itemAdded" event so that all future submenus don't have 
	//	the event handler
    Menu.superclass.unsubscribe.call(this, _ITEM_ADDED, this._doItemSubmenuSubscribe, arguments);


    var aItems = this.getItems(),
        nItems,
        oItem,
        oSubmenu,
        i;
        

    if (aItems) {

        nItems = aItems.length;
        
        if (nItems > 0) {
        
            i = nItems - 1;
            
            do {

                oItem = aItems[i];
                oSubmenu = oItem.cfg.getProperty(_SUBMENU);
                
                if (oSubmenu) {
                    oSubmenu.unsubscribe.apply(oSubmenu, arguments);
                }
                else {
                    oItem.cfg.unsubscribeFromConfigEvent(_SUBMENU, this._doSubmenuSubscribe, arguments);
                }

            }
            while (i--);
        
        }

    }

},


/**
* @description Initializes the class's configurable properties which can be
* changed using the menu's Config object ("cfg").
* @method initDefaultConfig
*/
initDefaultConfig: function () {

    Menu.superclass.initDefaultConfig.call(this);

    var oConfig = this.cfg;


    // Module documentation overrides

    /**
    * @config effect
    * @description Object or array of objects representing the ContainerEffect 
    * classes that are active for animating the container.  When set this 
    * property is automatically applied to all submenus.
    * @type Object
    * @default null
    */

    // Overlay documentation overrides


    /**
    * @config x
    * @description Number representing the absolute x-coordinate position of 
    * the Menu.  This property is only applied when the "position" 
    * configuration property is set to dynamic.
    * @type Number
    * @default null
    */
    

    /**
    * @config y
    * @description Number representing the absolute y-coordinate position of 
    * the Menu.  This property is only applied when the "position" 
    * configuration property is set to dynamic.
    * @type Number
    * @default null
    */


    /**
    * @description Array of the absolute x and y positions of the Menu.  This 
    * property is only applied when the "position" configuration property is 
    * set to dynamic.
    * @config xy
    * @type Number[]
    * @default null
    */
    

    /**
    * @config context
    * @description Array of context arguments for context-sensitive positioning.  
    * The format is: [id or element, element corner, context corner]. 
    * For example, setting this property to ["img1", "tl", "bl"] would 
    * align the Menu's top left corner to the context element's 
    * bottom left corner.  This property is only applied when the "position" 
    * configuration property is set to dynamic.
    * @type Array
    * @default null
    */
    
    
    /**
    * @config fixedcenter
    * @description Boolean indicating if the Menu should be anchored to the 
    * center of the viewport.  This property is only applied when the 
    * "position" configuration property is set to dynamic.
    * @type Boolean
    * @default false
    */
    
    
    /**
    * @config iframe
    * @description Boolean indicating whether or not the Menu should 
    * have an IFRAME shim; used to prevent SELECT elements from 
    * poking through an Overlay instance in IE6.  When set to "true", 
    * the iframe shim is created when the Menu instance is intially
    * made visible.  This property is only applied when the "position" 
    * configuration property is set to dynamic and is automatically applied 
    * to all submenus.
    * @type Boolean
    * @default true for IE6 and below, false for all other browsers.
    */


	// Add configuration attributes

    /*
        Change the default value for the "visible" configuration 
        property to "false" by re-adding the property.
    */

    /**
    * @config visible
    * @description Boolean indicating whether or not the menu is visible.  If 
    * the menu's "position" configuration property is set to "dynamic" (the 
    * default), this property toggles the menu's <code>&#60;div&#62;</code> 
    * element's "visibility" style property between "visible" (true) or 
    * "hidden" (false).  If the menu's "position" configuration property is 
    * set to "static" this property toggles the menu's 
    * <code>&#60;div&#62;</code> element's "display" style property 
    * between "block" (true) or "none" (false).
    * @default false
    * @type Boolean
    */
    oConfig.addProperty(
        VISIBLE_CONFIG.key, 
        {
            handler: this.configVisible, 
            value: VISIBLE_CONFIG.value, 
            validator: VISIBLE_CONFIG.validator
        }
     );


    /*
        Change the default value for the "constraintoviewport" configuration 
        property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property.
    */

    /**
    * @config constraintoviewport
    * @description Boolean indicating if the menu will try to remain inside 
    * the boundaries of the size of viewport.  This property is only applied 
    * when the "position" configuration property is set to dynamic and is 
    * automatically applied to all submenus.
    * @default true
    * @type Boolean
    */
    oConfig.addProperty(
        CONSTRAIN_TO_VIEWPORT_CONFIG.key, 
        {
            handler: this.configConstrainToViewport, 
            value: CONSTRAIN_TO_VIEWPORT_CONFIG.value, 
            validator: CONSTRAIN_TO_VIEWPORT_CONFIG.validator, 
            supercedes: CONSTRAIN_TO_VIEWPORT_CONFIG.supercedes 
        } 
    );


    /*
        Change the default value for the "preventcontextoverlap" configuration 
        property (inherited by YAHOO.widget.Overlay) to "true" by re-adding the property.
    */

	/**
	* @config preventcontextoverlap
	* @description Boolean indicating whether or not a submenu should overlap its parent MenuItem 
	* when the "constraintoviewport" configuration property is set to "true".
	* @type Boolean
	* @default true
	*/
	oConfig.addProperty(PREVENT_CONTEXT_OVERLAP_CONFIG.key, {

		value: PREVENT_CONTEXT_OVERLAP_CONFIG.value, 
		validator: PREVENT_CONTEXT_OVERLAP_CONFIG.validator, 
		supercedes: PREVENT_CONTEXT_OVERLAP_CONFIG.supercedes

	});


    /**
    * @config position
    * @description String indicating how a menu should be positioned on the 
    * screen.  Possible values are "static" and "dynamic."  Static menus are 
    * visible by default and reside in the normal flow of the document 
    * (CSS position: static).  Dynamic menus are hidden by default, reside 
    * out of the normal flow of the document (CSS position: absolute), and 
    * can overlay other elements on the screen.
    * @default dynamic
    * @type String
    */
    oConfig.addProperty(
        POSITION_CONFIG.key, 
        {
            handler: this.configPosition,
            value: POSITION_CONFIG.value, 
            validator: POSITION_CONFIG.validator,
            supercedes: POSITION_CONFIG.supercedes
        }
    );


    /**
    * @config submenualignment
    * @description Array defining how submenus should be aligned to their 
    * parent menu item. The format is: [itemCorner, submenuCorner]. By default
    * a submenu's top left corner is aligned to its parent menu item's top 
    * right corner.
    * @default ["tl","tr"]
    * @type Array
    */
    oConfig.addProperty(
        SUBMENU_ALIGNMENT_CONFIG.key, 
        { 
            value: SUBMENU_ALIGNMENT_CONFIG.value,
            suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent
        }
    );


    /**
    * @config autosubmenudisplay
    * @description Boolean indicating if submenus are automatically made 
    * visible when the user mouses over the menu's items.
    * @default true
    * @type Boolean
    */
	oConfig.addProperty(
	   AUTO_SUBMENU_DISPLAY_CONFIG.key, 
	   { 
	       value: AUTO_SUBMENU_DISPLAY_CONFIG.value, 
	       validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator,
	       suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent
       } 
    );


    /**
    * @config showdelay
    * @description Number indicating the time (in milliseconds) that should 
    * expire before a submenu is made visible when the user mouses over 
    * the menu's items.  This property is only applied when the "position" 
    * configuration property is set to dynamic and is automatically applied 
    * to all submenus.
    * @default 250
    * @type Number
    */
	oConfig.addProperty(
	   SHOW_DELAY_CONFIG.key, 
	   { 
	       value: SHOW_DELAY_CONFIG.value, 
	       validator: SHOW_DELAY_CONFIG.validator,
	       suppressEvent: SHOW_DELAY_CONFIG.suppressEvent
       } 
    );


    /**
    * @config hidedelay
    * @description Number indicating the time (in milliseconds) that should 
    * expire before the menu is hidden.  This property is only applied when 
    * the "position" configuration property is set to dynamic and is 
    * automatically applied to all submenus.
    * @default 0
    * @type Number
    */
	oConfig.addProperty(
	   HIDE_DELAY_CONFIG.key, 
	   { 
	       handler: this.configHideDelay,
	       value: HIDE_DELAY_CONFIG.value, 
	       validator: HIDE_DELAY_CONFIG.validator, 
	       suppressEvent: HIDE_DELAY_CONFIG.suppressEvent
       } 
    );


    /**
    * @config submenuhidedelay
    * @description Number indicating the time (in milliseconds) that should 
    * expire before a submenu is hidden when the user mouses out of a menu item 
    * heading in the direction of a submenu.  The value must be greater than or 
    * equal to the value specified for the "showdelay" configuration property.
    * This property is only applied when the "position" configuration property 
    * is set to dynamic and is automatically applied to all submenus.
    * @default 250
    * @type Number
    */
	oConfig.addProperty(
	   SUBMENU_HIDE_DELAY_CONFIG.key, 
	   { 
	       value: SUBMENU_HIDE_DELAY_CONFIG.value, 
	       validator: SUBMENU_HIDE_DELAY_CONFIG.validator,
	       suppressEvent: SUBMENU_HIDE_DELAY_CONFIG.suppressEvent
       } 
    );


    /**
    * @config clicktohide
    * @description Boolean indicating if the menu will automatically be 
    * hidden if the user clicks outside of it.  This property is only 
    * applied when the "position" configuration property is set to dynamic 
    * and is automatically applied to all submenus.
    * @default true
    * @type Boolean
    */
    oConfig.addProperty(
        CLICK_TO_HIDE_CONFIG.key,
        {
            value: CLICK_TO_HIDE_CONFIG.value,
            validator: CLICK_TO_HIDE_CONFIG.validator,
            suppressEvent: CLICK_TO_HIDE_CONFIG.suppressEvent
        }
    );


	/**
	* @config container
	* @description HTML element reference or string specifying the id 
	* attribute of the HTML element that the menu's markup should be 
	* rendered into.
	* @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
	* level-one-html.html#ID-58190037">HTMLElement</a>|String
	* @default document.body
	*/
	oConfig.addProperty(
	   CONTAINER_CONFIG.key, 
	   { 
	       handler: this.configContainer,
	       value: document.body,
           suppressEvent: CONTAINER_CONFIG.suppressEvent
       } 
   );


    /**
    * @config scrollincrement
    * @description Number used to control the scroll speed of a menu.  Used to 
    * increment the "scrollTop" property of the menu's body by when a menu's 
    * content is scrolling.  When set this property is automatically applied 
    * to all submenus.
    * @default 1
    * @type Number
    */
    oConfig.addProperty(
        SCROLL_INCREMENT_CONFIG.key, 
        { 
            value: SCROLL_INCREMENT_CONFIG.value, 
            validator: SCROLL_INCREMENT_CONFIG.validator,
            supercedes: SCROLL_INCREMENT_CONFIG.supercedes,
            suppressEvent: SCROLL_INCREMENT_CONFIG.suppressEvent
        }
    );


    /**
    * @config minscrollheight
    * @description Number defining the minimum threshold for the "maxheight" 
    * configuration property.  When set this property is automatically applied 
    * to all submenus.
    * @default 90
    * @type Number
    */
    oConfig.addProperty(
        MIN_SCROLL_HEIGHT_CONFIG.key, 
        { 
            value: MIN_SCROLL_HEIGHT_CONFIG.value, 
            validator: MIN_SCROLL_HEIGHT_CONFIG.validator,
            supercedes: MIN_SCROLL_HEIGHT_CONFIG.supercedes,
            suppressEvent: MIN_SCROLL_HEIGHT_CONFIG.suppressEvent
        }
    );


    /**
    * @config maxheight
    * @description Number defining the maximum height (in pixels) for a menu's 
    * body element (<code>&#60;div class="bd"&#62;</code>).  Once a menu's body 
    * exceeds this height, the contents of the body are scrolled to maintain 
    * this value.  This value cannot be set lower than the value of the 
    * "minscrollheight" configuration property.
    * @default 0
    * @type Number
    */
    oConfig.addProperty(
       MAX_HEIGHT_CONFIG.key, 
       {
            handler: this.configMaxHeight,
            value: MAX_HEIGHT_CONFIG.value,
            validator: MAX_HEIGHT_CONFIG.validator,
            suppressEvent: MAX_HEIGHT_CONFIG.suppressEvent,
            supercedes: MAX_HEIGHT_CONFIG.supercedes            
       } 
    );


    /**
    * @config classname
    * @description String representing the CSS class to be applied to the 
    * menu's root <code>&#60;div&#62;</code> element.  The specified class(es)  
    * are appended in addition to the default class as specified by the menu's
    * CSS_CLASS_NAME constant. When set this property is automatically 
    * applied to all submenus.
    * @default null
    * @type String
    */
    oConfig.addProperty(
        CLASS_NAME_CONFIG.key, 
        { 
            handler: this.configClassName,
            value: CLASS_NAME_CONFIG.value, 
            validator: CLASS_NAME_CONFIG.validator,
            supercedes: CLASS_NAME_CONFIG.supercedes      
        }
    );


    /**
    * @config disabled
    * @description Boolean indicating if the menu should be disabled.  
    * Disabling a menu disables each of its items.  (Disabled menu items are 
    * dimmed and will not respond to user input or fire events.)  Disabled
    * menus have a corresponding "disabled" CSS class applied to their root
    * <code>&#60;div&#62;</code> element.
    * @default false
    * @type Boolean
    */
    oConfig.addProperty(
        DISABLED_CONFIG.key, 
        { 
            handler: this.configDisabled,
            value: DISABLED_CONFIG.value, 
            validator: DISABLED_CONFIG.validator,
            suppressEvent: DISABLED_CONFIG.suppressEvent
        }
    );


    /**
    * @config shadow
    * @description Boolean indicating if the menu should have a shadow.
    * @default true
    * @type Boolean
    */
    oConfig.addProperty(
        SHADOW_CONFIG.key, 
        { 
            handler: this.configShadow,
            value: SHADOW_CONFIG.value, 
            validator: SHADOW_CONFIG.validator
        }
    );


    /**
    * @config keepopen
    * @description Boolean indicating if the menu should remain open when clicked.
    * @default false
    * @type Boolean
    */
    oConfig.addProperty(
        KEEP_OPEN_CONFIG.key, 
        { 
            value: KEEP_OPEN_CONFIG.value, 
            validator: KEEP_OPEN_CONFIG.validator
        }
    );

}

}); // END YAHOO.lang.extend

})();



(function () {

/**
* Creates an item for a menu.
* 
* @param {String} p_oObject String specifying the text of the menu item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying 
* the <code>&#60;li&#62;</code> element of the menu item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
* specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object 
* specifying the <code>&#60;option&#62;</code> element of the menu item.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the menu item. See configuration class documentation 
* for more details.
* @class MenuItem
* @constructor
*/
YAHOO.widget.MenuItem = function (p_oObject, p_oConfig) {

    if (p_oObject) {

        if (p_oConfig) {
    
            this.parent = p_oConfig.parent;
            this.value = p_oConfig.value;
            this.id = p_oConfig.id;

        }

        this.init(p_oObject, p_oConfig);

    }

};


var Dom = YAHOO.util.Dom,
    Module = YAHOO.widget.Module,
    Menu = YAHOO.widget.Menu,
    MenuItem = YAHOO.widget.MenuItem,
    CustomEvent = YAHOO.util.CustomEvent,
    UA = YAHOO.env.ua,
    Lang = YAHOO.lang,

	// Private string constants

	_TEXT = "text",
	_HASH = "#",
	_HYPHEN = "-",
	_HELP_TEXT = "helptext",
	_URL = "url",
	_TARGET = "target",
	_EMPHASIS = "emphasis",
	_STRONG_EMPHASIS = "strongemphasis",
	_CHECKED = "checked",
	_SUBMENU = "submenu",
	_DISABLED = "disabled",
	_SELECTED = "selected",
	_HAS_SUBMENU = "hassubmenu",
	_CHECKED_DISABLED = "checked-disabled",
	_HAS_SUBMENU_DISABLED = "hassubmenu-disabled",
	_HAS_SUBMENU_SELECTED = "hassubmenu-selected",
	_CHECKED_SELECTED = "checked-selected",
	_ONCLICK = "onclick",
	_CLASSNAME = "classname",
	_EMPTY_STRING = "",
	_OPTION = "OPTION",
	_OPTGROUP = "OPTGROUP",
	_LI_UPPERCASE = "LI",
	_HREF = "href",
	_SELECT = "SELECT",
	_DIV = "DIV",
	_START_HELP_TEXT = "<em class=\"helptext\">",
	_START_EM = "<em>",
	_END_EM = "</em>",
	_START_STRONG = "<strong>",
	_END_STRONG = "</strong>",
	_PREVENT_CONTEXT_OVERLAP = "preventcontextoverlap",
	_OBJ = "obj",
	_SCOPE = "scope",
	_NONE = "none",
	_VISIBLE = "visible",
	_SPACE = " ",
	_MENUITEM = "MenuItem",
	_CLICK = "click",
	_SHOW = "show",
	_HIDE = "hide",
	_LI_LOWERCASE = "li",
	_ANCHOR_TEMPLATE = "<a href=\"#\"></a>",

    EVENT_TYPES = [
    
        ["mouseOverEvent", "mouseover"],
        ["mouseOutEvent", "mouseout"],
        ["mouseDownEvent", "mousedown"],
        ["mouseUpEvent", "mouseup"],
        ["clickEvent", _CLICK],
        ["keyPressEvent", "keypress"],
        ["keyDownEvent", "keydown"],
        ["keyUpEvent", "keyup"],
        ["focusEvent", "focus"],
        ["blurEvent", "blur"],
        ["destroyEvent", "destroy"]
    
    ],

	TEXT_CONFIG = { 
		key: _TEXT, 
		value: _EMPTY_STRING, 
		validator: Lang.isString, 
		suppressEvent: true 
	}, 

	HELP_TEXT_CONFIG = { 
		key: _HELP_TEXT,
		supercedes: [_TEXT], 
		suppressEvent: true 
	},

	URL_CONFIG = { 
		key: _URL, 
		value: _HASH, 
		suppressEvent: true 
	}, 

	TARGET_CONFIG = { 
		key: _TARGET, 
		suppressEvent: true 
	}, 

	EMPHASIS_CONFIG = { 
		key: _EMPHASIS, 
		value: false, 
		validator: Lang.isBoolean, 
		suppressEvent: true, 
		supercedes: [_TEXT]
	}, 

	STRONG_EMPHASIS_CONFIG = { 
		key: _STRONG_EMPHASIS, 
		value: false, 
		validator: Lang.isBoolean, 
		suppressEvent: true,
		supercedes: [_TEXT]
	},

	CHECKED_CONFIG = { 
		key: _CHECKED, 
		value: false, 
		validator: Lang.isBoolean, 
		suppressEvent: true, 
		supercedes: [_DISABLED, _SELECTED]
	}, 

	SUBMENU_CONFIG = { 
		key: _SUBMENU,
		suppressEvent: true,
		supercedes: [_DISABLED, _SELECTED]
	},

	DISABLED_CONFIG = { 
		key: _DISABLED, 
		value: false, 
		validator: Lang.isBoolean, 
		suppressEvent: true,
		supercedes: [_TEXT, _SELECTED]
	},

	SELECTED_CONFIG = { 
		key: _SELECTED, 
		value: false, 
		validator: Lang.isBoolean, 
		suppressEvent: true
	},

	ONCLICK_CONFIG = { 
		key: _ONCLICK,
		suppressEvent: true
	},

	CLASS_NAME_CONFIG = { 
		key: _CLASSNAME, 
		value: null, 
		validator: Lang.isString,
		suppressEvent: true
	},
    
	KEY_LISTENER_CONFIG = {
		key: "keylistener", 
		value: null, 
		suppressEvent: true
	},

	m_oMenuItemTemplate = null,

    CLASS_NAMES = {};


/**
* @method getClassNameForState
* @description Returns a class name for the specified prefix and state.  If the class name does not 
* yet exist, it is created and stored in the CLASS_NAMES object to increase performance.
* @private
* @param {String} prefix String representing the prefix for the class name
* @param {String} state String representing a state - "disabled," "checked," etc.
*/  
var getClassNameForState = function (prefix, state) {

	var oClassNames = CLASS_NAMES[prefix];
	
	if (!oClassNames) {
		CLASS_NAMES[prefix] = {};
		oClassNames = CLASS_NAMES[prefix];
	}


	var sClassName = oClassNames[state];

	if (!sClassName) {
		sClassName = prefix + _HYPHEN + state;
		oClassNames[state] = sClassName;
	}

	return sClassName;
	
};


/**
* @method addClassNameForState
* @description Applies a class name to a MenuItem instance's &#60;LI&#62; and &#60;A&#62; elements
* that represents a MenuItem's state - "disabled," "checked," etc.
* @private
* @param {String} state String representing a state - "disabled," "checked," etc.
*/  
var addClassNameForState = function (state) {

	Dom.addClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state));
	Dom.addClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state));

};

/**
* @method removeClassNameForState
* @description Removes a class name from a MenuItem instance's &#60;LI&#62; and &#60;A&#62; elements
* that represents a MenuItem's state - "disabled," "checked," etc.
* @private
* @param {String} state String representing a state - "disabled," "checked," etc.
*/  
var removeClassNameForState = function (state) {

	Dom.removeClass(this.element, getClassNameForState(this.CSS_CLASS_NAME, state));
	Dom.removeClass(this._oAnchor, getClassNameForState(this.CSS_LABEL_CLASS_NAME, state));

};


MenuItem.prototype = {

    /**
    * @property CSS_CLASS_NAME
    * @description String representing the CSS class(es) to be applied to the 
    * <code>&#60;li&#62;</code> element of the menu item.
    * @default "yuimenuitem"
    * @final
    * @type String
    */
    CSS_CLASS_NAME: "yuimenuitem",


    /**
    * @property CSS_LABEL_CLASS_NAME
    * @description String representing the CSS class(es) to be applied to the 
    * menu item's <code>&#60;a&#62;</code> element.
    * @default "yuimenuitemlabel"
    * @final
    * @type String
    */
    CSS_LABEL_CLASS_NAME: "yuimenuitemlabel",


    /**
    * @property SUBMENU_TYPE
    * @description Object representing the type of menu to instantiate and 
    * add when parsing the child nodes of the menu item's source HTML element.
    * @final
    * @type YAHOO.widget.Menu
    */
    SUBMENU_TYPE: null,



    // Private member variables
    

    /**
    * @property _oAnchor
    * @description Object reference to the menu item's 
    * <code>&#60;a&#62;</code> element.
    * @default null 
    * @private
    * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-48250443">HTMLAnchorElement</a>
    */
    _oAnchor: null,
    
    
    /**
    * @property _oHelpTextEM
    * @description Object reference to the menu item's help text 
    * <code>&#60;em&#62;</code> element.
    * @default null
    * @private
    * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-58190037">HTMLElement</a>
    */
    _oHelpTextEM: null,
    
    
    /**
    * @property _oSubmenu
    * @description Object reference to the menu item's submenu.
    * @default null
    * @private
    * @type YAHOO.widget.Menu
    */
    _oSubmenu: null,


    /** 
    * @property _oOnclickAttributeValue
    * @description Object reference to the menu item's current value for the 
    * "onclick" configuration attribute.
    * @default null
    * @private
    * @type Object
    */
    _oOnclickAttributeValue: null,


    /**
    * @property _sClassName
    * @description The current value of the "classname" configuration attribute.
    * @default null
    * @private
    * @type String
    */
    _sClassName: null,



    // Public properties


	/**
    * @property constructor
	* @description Object reference to the menu item's constructor function.
    * @default YAHOO.widget.MenuItem
	* @type YAHOO.widget.MenuItem
	*/
	constructor: MenuItem,


    /**
    * @property index
    * @description Number indicating the ordinal position of the menu item in 
    * its group.
    * @default null
    * @type Number
    */
    index: null,


    /**
    * @property groupIndex
    * @description Number indicating the index of the group to which the menu 
    * item belongs.
    * @default null
    * @type Number
    */
    groupIndex: null,


    /**
    * @property parent
    * @description Object reference to the menu item's parent menu.
    * @default null
    * @type YAHOO.widget.Menu
    */
    parent: null,


    /**
    * @property element
    * @description Object reference to the menu item's 
    * <code>&#60;li&#62;</code> element.
    * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level
    * -one-html.html#ID-74680021">HTMLLIElement</a>
    * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-74680021">HTMLLIElement</a>
    */
    element: null,


    /**
    * @property srcElement
    * @description Object reference to the HTML element (either 
    * <code>&#60;li&#62;</code>, <code>&#60;optgroup&#62;</code> or 
    * <code>&#60;option&#62;</code>) used create the menu item.
    * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
    * level-one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.
    * w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247"
    * >HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
    * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
    * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.w3.
    * org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247">
    * HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
    * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
    */
    srcElement: null,


    /**
    * @property value
    * @description Object reference to the menu item's value.
    * @default null
    * @type Object
    */
    value: null,


	/**
    * @property browser
    * @deprecated Use YAHOO.env.ua
	* @description String representing the browser.
	* @type String
	*/
	browser: Module.prototype.browser,


    /**
    * @property id
    * @description Id of the menu item's root <code>&#60;li&#62;</code> 
    * element.  This property should be set via the constructor using the 
    * configuration object literal.  If an id is not specified, then one will 
    * be created using the "generateId" method of the Dom utility.
    * @default null
    * @type String
    */
    id: null,



    // Events


    /**
    * @event destroyEvent
    * @description Fires when the menu item's <code>&#60;li&#62;</code> 
    * element is removed from its parent <code>&#60;ul&#62;</code> element.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event mouseOverEvent
    * @description Fires when the mouse has entered the menu item.  Passes 
    * back the DOM Event object as an argument.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event mouseOutEvent
    * @description Fires when the mouse has left the menu item.  Passes back 
    * the DOM Event object as an argument.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event mouseDownEvent
    * @description Fires when the user mouses down on the menu item.  Passes 
    * back the DOM Event object as an argument.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event mouseUpEvent
    * @description Fires when the user releases a mouse button while the mouse 
    * is over the menu item.  Passes back the DOM Event object as an argument.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event clickEvent
    * @description Fires when the user clicks the on the menu item.  Passes 
    * back the DOM Event object as an argument.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event keyPressEvent
    * @description Fires when the user presses an alphanumeric key when the 
    * menu item has focus.  Passes back the DOM Event object as an argument.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event keyDownEvent
    * @description Fires when the user presses a key when the menu item has 
    * focus.  Passes back the DOM Event object as an argument.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event keyUpEvent
    * @description Fires when the user releases a key when the menu item has 
    * focus.  Passes back the DOM Event object as an argument.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event focusEvent
    * @description Fires when the menu item receives focus.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @event blurEvent
    * @description Fires when the menu item loses the input focus.
    * @type YAHOO.util.CustomEvent
    */


    /**
    * @method init
    * @description The MenuItem class's initialization method. This method is 
    * automatically called by the constructor, and sets up all DOM references 
    * for pre-existing markup, and creates required markup if it is not 
    * already present.
    * @param {String} p_oObject String specifying the text of the menu item.
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying 
    * the <code>&#60;li&#62;</code> element of the menu item.
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
    * specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object 
    * specifying the <code>&#60;option&#62;</code> element of the menu item.
    * @param {Object} p_oConfig Optional. Object literal specifying the 
    * configuration for the menu item. See configuration class documentation 
    * for more details.
    */
    init: function (p_oObject, p_oConfig) {


        if (!this.SUBMENU_TYPE) {
    
            this.SUBMENU_TYPE = Menu;
    
        }


        // Create the config object

        this.cfg = new YAHOO.util.Config(this);

        this.initDefaultConfig();

        var oConfig = this.cfg,
            sURL = _HASH,
            oCustomEvent,
			aEventData,
            oAnchor,
            sTarget,
            sText,
            sId,
            i;


        if (Lang.isString(p_oObject)) {

            this._createRootNodeStructure();

            oConfig.queueProperty(_TEXT, p_oObject);

        }
        else if (p_oObject && p_oObject.tagName) {

            switch(p_oObject.tagName.toUpperCase()) {

                case _OPTION:

                    this._createRootNodeStructure();

                    oConfig.queueProperty(_TEXT, p_oObject.text);
                    oConfig.queueProperty(_DISABLED, p_oObject.disabled);

                    this.value = p_oObject.value;

                    this.srcElement = p_oObject;

                break;

                case _OPTGROUP:

                    this._createRootNodeStructure();

                    oConfig.queueProperty(_TEXT, p_oObject.label);
                    oConfig.queueProperty(_DISABLED, p_oObject.disabled);

                    this.srcElement = p_oObject;

                    this._initSubTree();

                break;

                case _LI_UPPERCASE:

                    // Get the anchor node (if it exists)
                    
                    oAnchor = Dom.getFirstChild(p_oObject);


                    // Capture the "text" and/or the "URL"

                    if (oAnchor) {

                        sURL = oAnchor.getAttribute(_HREF, 2);
                        sTarget = oAnchor.getAttribute(_TARGET);

                        sText = oAnchor.innerHTML;

                    }

                    this.srcElement = p_oObject;
                    this.element = p_oObject;
                    this._oAnchor = oAnchor;

                    /*
                        Set these properties silently to sync up the 
                        configuration object without making changes to the 
                        element's DOM
                    */ 

                    oConfig.setProperty(_TEXT, sText, true);
                    oConfig.setProperty(_URL, sURL, true);
                    oConfig.setProperty(_TARGET, sTarget, true);

                    this._initSubTree();

                break;

            }            

        }


        if (this.element) {

            sId = (this.srcElement || this.element).id;

            if (!sId) {

                sId = this.id || Dom.generateId();

                this.element.id = sId;

            }

            this.id = sId;


            Dom.addClass(this.element, this.CSS_CLASS_NAME);
            Dom.addClass(this._oAnchor, this.CSS_LABEL_CLASS_NAME);


			i = EVENT_TYPES.length - 1;

			do {

				aEventData = EVENT_TYPES[i];

				oCustomEvent = this.createEvent(aEventData[1]);
				oCustomEvent.signature = CustomEvent.LIST;
				
				this[aEventData[0]] = oCustomEvent;

			}
			while (i--);


            if (p_oConfig) {
    
                oConfig.applyConfig(p_oConfig);
    
            }        

            oConfig.fireQueue();

        }

    },



    // Private methods

    /**
    * @method _createRootNodeStructure
    * @description Creates the core DOM structure for the menu item.
    * @private
    */
    _createRootNodeStructure: function () {

        var oElement,
            oAnchor;

        if (!m_oMenuItemTemplate) {

            m_oMenuItemTemplate = document.createElement(_LI_LOWERCASE);
            m_oMenuItemTemplate.innerHTML = _ANCHOR_TEMPLATE;

        }

        oElement = m_oMenuItemTemplate.cloneNode(true);
        oElement.className = this.CSS_CLASS_NAME;

        oAnchor = oElement.firstChild;
        oAnchor.className = this.CSS_LABEL_CLASS_NAME;

        this.element = oElement;
        this._oAnchor = oAnchor;

    },


    /**
    * @method _initSubTree
    * @description Iterates the source element's childNodes collection and uses 
    * the child nodes to instantiate other menus.
    * @private
    */
    _initSubTree: function () {

        var oSrcEl = this.srcElement,
            oConfig = this.cfg,
            oNode,
            aOptions,
            nOptions,
            oMenu,
            n;


        if (oSrcEl.childNodes.length > 0) {

            if (this.parent.lazyLoad && this.parent.srcElement && 
                this.parent.srcElement.tagName.toUpperCase() == _SELECT) {

                oConfig.setProperty(
                        _SUBMENU, 
                        { id: Dom.generateId(), itemdata: oSrcEl.childNodes }
                    );

            }
            else {

                oNode = oSrcEl.firstChild;
                aOptions = [];
    
                do {
    
                    if (oNode && oNode.tagName) {
    
                        switch(oNode.tagName.toUpperCase()) {
                
                            case _DIV:
                
                                oConfig.setProperty(_SUBMENU, oNode);
                
                            break;
         
                            case _OPTION:
        
                                aOptions[aOptions.length] = oNode;
        
                            break;
               
                        }
                    
                    }
                
                }        
                while((oNode = oNode.nextSibling));
    
    
                nOptions = aOptions.length;
    
                if (nOptions > 0) {
    
                    oMenu = new this.SUBMENU_TYPE(Dom.generateId());
                    
                    oConfig.setProperty(_SUBMENU, oMenu);
    
                    for(n=0; n<nOptions; n++) {
        
                        oMenu.addItem((new oMenu.ITEM_TYPE(aOptions[n])));
        
                    }
        
                }
            
            }

        }

    },



    // Event handlers for configuration properties


    /**
    * @method configText
    * @description Event handler for when the "text" configuration property of 
    * the menu item changes.
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */
    configText: function (p_sType, p_aArgs, p_oItem) {

        var sText = p_aArgs[0],
            oConfig = this.cfg,
            oAnchor = this._oAnchor,
            sHelpText = oConfig.getProperty(_HELP_TEXT),
            sHelpTextHTML = _EMPTY_STRING,
            sEmphasisStartTag = _EMPTY_STRING,
            sEmphasisEndTag = _EMPTY_STRING;


        if (sText) {


            if (sHelpText) {
                    
                sHelpTextHTML = _START_HELP_TEXT + sHelpText + _END_EM;
            
            }


            if (oConfig.getProperty(_EMPHASIS)) {

                sEmphasisStartTag = _START_EM;
                sEmphasisEndTag = _END_EM;

            }


            if (oConfig.getProperty(_STRONG_EMPHASIS)) {

                sEmphasisStartTag = _START_STRONG;
                sEmphasisEndTag = _END_STRONG;
            
            }


            oAnchor.innerHTML = (sEmphasisStartTag + sText + sEmphasisEndTag + sHelpTextHTML);

        }

    },


    /**
    * @method configHelpText
    * @description Event handler for when the "helptext" configuration property 
    * of the menu item changes.
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */    
    configHelpText: function (p_sType, p_aArgs, p_oItem) {

        this.cfg.refireEvent(_TEXT);

    },


    /**
    * @method configURL
    * @description Event handler for when the "url" configuration property of 
    * the menu item changes.
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */    
    configURL: function (p_sType, p_aArgs, p_oItem) {

        var sURL = p_aArgs[0];

        if (!sURL) {

            sURL = _HASH;

        }

        var oAnchor = this._oAnchor;

        if (UA.opera) {

            oAnchor.removeAttribute(_HREF);
        
        }

        oAnchor.setAttribute(_HREF, sURL);

    },


    /**
    * @method configTarget
    * @description Event handler for when the "target" configuration property 
    * of the menu item changes.  
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */    
    configTarget: function (p_sType, p_aArgs, p_oItem) {

        var sTarget = p_aArgs[0],
            oAnchor = this._oAnchor;

        if (sTarget && sTarget.length > 0) {

            oAnchor.setAttribute(_TARGET, sTarget);

        }
        else {

            oAnchor.removeAttribute(_TARGET);
        
        }

    },


    /**
    * @method configEmphasis
    * @description Event handler for when the "emphasis" configuration property
    * of the menu item changes.
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */    
    configEmphasis: function (p_sType, p_aArgs, p_oItem) {

        var bEmphasis = p_aArgs[0],
            oConfig = this.cfg;


        if (bEmphasis && oConfig.getProperty(_STRONG_EMPHASIS)) {

            oConfig.setProperty(_STRONG_EMPHASIS, false);

        }


        oConfig.refireEvent(_TEXT);

    },


    /**
    * @method configStrongEmphasis
    * @description Event handler for when the "strongemphasis" configuration 
    * property of the menu item changes.
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */    
    configStrongEmphasis: function (p_sType, p_aArgs, p_oItem) {

        var bStrongEmphasis = p_aArgs[0],
            oConfig = this.cfg;


        if (bStrongEmphasis && oConfig.getProperty(_EMPHASIS)) {

            oConfig.setProperty(_EMPHASIS, false);

        }

        oConfig.refireEvent(_TEXT);

    },


    /**
    * @method configChecked
    * @description Event handler for when the "checked" configuration property 
    * of the menu item changes. 
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */    
    configChecked: function (p_sType, p_aArgs, p_oItem) {

        var bChecked = p_aArgs[0],
            oConfig = this.cfg;


        if (bChecked) {

            addClassNameForState.call(this, _CHECKED);

        }
        else {

            removeClassNameForState.call(this, _CHECKED);
        }


        oConfig.refireEvent(_TEXT);


        if (oConfig.getProperty(_DISABLED)) {

            oConfig.refireEvent(_DISABLED);

        }


        if (oConfig.getProperty(_SELECTED)) {

            oConfig.refireEvent(_SELECTED);

        }

    },



    /**
    * @method configDisabled
    * @description Event handler for when the "disabled" configuration property 
    * of the menu item changes. 
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */    
    configDisabled: function (p_sType, p_aArgs, p_oItem) {

        var bDisabled = p_aArgs[0],
            oConfig = this.cfg,
            oSubmenu = oConfig.getProperty(_SUBMENU),
            bChecked = oConfig.getProperty(_CHECKED);


        if (bDisabled) {

            if (oConfig.getProperty(_SELECTED)) {

                oConfig.setProperty(_SELECTED, false);

            }


			addClassNameForState.call(this, _DISABLED);


            if (oSubmenu) {

				addClassNameForState.call(this, _HAS_SUBMENU_DISABLED);
            
            }
            

            if (bChecked) {

				addClassNameForState.call(this, _CHECKED_DISABLED);

            }

        }
        else {

			removeClassNameForState.call(this, _DISABLED);


            if (oSubmenu) {

				removeClassNameForState.call(this, _HAS_SUBMENU_DISABLED);
            
            }
            

            if (bChecked) {

				removeClassNameForState.call(this, _CHECKED_DISABLED);

            }

        }

    },


    /**
    * @method configSelected
    * @description Event handler for when the "selected" configuration property 
    * of the menu item changes. 
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */    
    configSelected: function (p_sType, p_aArgs, p_oItem) {

        var oConfig = this.cfg,
        	oAnchor = this._oAnchor,
        	
            bSelected = p_aArgs[0],
            bChecked = oConfig.getProperty(_CHECKED),
            oSubmenu = oConfig.getProperty(_SUBMENU);


        if (UA.opera) {

            oAnchor.blur();
        
        }


        if (bSelected && !oConfig.getProperty(_DISABLED)) {

			addClassNameForState.call(this, _SELECTED);


            if (oSubmenu) {

				addClassNameForState.call(this, _HAS_SUBMENU_SELECTED);
            
            }


            if (bChecked) {

				addClassNameForState.call(this, _CHECKED_SELECTED);

            }

        }
        else {

			removeClassNameForState.call(this, _SELECTED);


            if (oSubmenu) {

				removeClassNameForState.call(this, _HAS_SUBMENU_SELECTED);
            
            }


            if (bChecked) {

				removeClassNameForState.call(this, _CHECKED_SELECTED);

            }

        }


        if (this.hasFocus() && UA.opera) {
        
            oAnchor.focus();
        
        }

    },


    /**
    * @method _onSubmenuBeforeHide
    * @description "beforehide" Custom Event handler for a submenu.
    * @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.
    */
    _onSubmenuBeforeHide: function (p_sType, p_aArgs) {

        var oItem = this.parent,
            oMenu;

        function onHide() {

            oItem._oAnchor.blur();
            oMenu.beforeHideEvent.unsubscribe(onHide);
        
        }


        if (oItem.hasFocus()) {

            oMenu = oItem.parent;

            oMenu.beforeHideEvent.subscribe(onHide);
        
        }
    
    },


    /**
    * @method configSubmenu
    * @description Event handler for when the "submenu" configuration property 
    * of the menu item changes. 
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */
    configSubmenu: function (p_sType, p_aArgs, p_oItem) {

        var oSubmenu = p_aArgs[0],
            oConfig = this.cfg,
            bLazyLoad = this.parent && this.parent.lazyLoad,
            oMenu,
            sSubmenuId,
            oSubmenuConfig;


        if (oSubmenu) {

            if (oSubmenu instanceof Menu) {

                oMenu = oSubmenu;
                oMenu.parent = this;
                oMenu.lazyLoad = bLazyLoad;

            }
            else if (Lang.isObject(oSubmenu) && oSubmenu.id && !oSubmenu.nodeType) {

                sSubmenuId = oSubmenu.id;
                oSubmenuConfig = oSubmenu;

                oSubmenuConfig.lazyload = bLazyLoad;
                oSubmenuConfig.parent = this;

                oMenu = new this.SUBMENU_TYPE(sSubmenuId, oSubmenuConfig);


                // Set the value of the property to the Menu instance

                oConfig.setProperty(_SUBMENU, oMenu, true);

            }
            else {

                oMenu = new this.SUBMENU_TYPE(oSubmenu, { lazyload: bLazyLoad, parent: this });


                // Set the value of the property to the Menu instance
                
                oConfig.setProperty(_SUBMENU, oMenu, true);

            }


            if (oMenu) {

				oMenu.cfg.setProperty(_PREVENT_CONTEXT_OVERLAP, true);

                addClassNameForState.call(this, _HAS_SUBMENU);


				if (oConfig.getProperty(_URL) === _HASH) {
				
					oConfig.setProperty(_URL, (_HASH + oMenu.id));
				
				}


                this._oSubmenu = oMenu;


                if (UA.opera) {
                
                    oMenu.beforeHideEvent.subscribe(this._onSubmenuBeforeHide);               
                
                }
            
            }

        }
        else {

			removeClassNameForState.call(this, _HAS_SUBMENU);

            if (this._oSubmenu) {

                this._oSubmenu.destroy();

            }

        }


        if (oConfig.getProperty(_DISABLED)) {

            oConfig.refireEvent(_DISABLED);

        }


        if (oConfig.getProperty(_SELECTED)) {

            oConfig.refireEvent(_SELECTED);

        }

    },


    /**
    * @method configOnClick
    * @description Event handler for when the "onclick" configuration property 
    * of the menu item changes. 
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */
    configOnClick: function (p_sType, p_aArgs, p_oItem) {

        var oObject = p_aArgs[0];

        /*
            Remove any existing listeners if a "click" event handler has 
            already been specified.
        */

        if (this._oOnclickAttributeValue && (this._oOnclickAttributeValue != oObject)) {

            this.clickEvent.unsubscribe(this._oOnclickAttributeValue.fn, 
                                this._oOnclickAttributeValue.obj);

            this._oOnclickAttributeValue = null;

        }


        if (!this._oOnclickAttributeValue && Lang.isObject(oObject) && 
            Lang.isFunction(oObject.fn)) {
            
            this.clickEvent.subscribe(oObject.fn, 
                ((_OBJ in oObject) ? oObject.obj : this), 
                ((_SCOPE in oObject) ? oObject.scope : null) );

            this._oOnclickAttributeValue = oObject;

        }
    
    },


    /**
    * @method configClassName
    * @description Event handler for when the "classname" configuration 
    * property of a menu item changes.
    * @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.
    * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
    * that fired the event.
    */
    configClassName: function (p_sType, p_aArgs, p_oItem) {
    
        var sClassName = p_aArgs[0];
    
        if (this._sClassName) {
    
            Dom.removeClass(this.element, this._sClassName);
    
        }
    
        Dom.addClass(this.element, sClassName);
        this._sClassName = sClassName;
    
    },


    /**
    * @method _dispatchClickEvent
    * @description Dispatches a DOM "click" event to the anchor element of a 
	* MenuItem instance.
	* @private	
    */
	_dispatchClickEvent: function () {

		var oMenuItem = this,
			oAnchor,
			oEvent;

		if (!oMenuItem.cfg.getProperty(_DISABLED)) {

			oAnchor = Dom.getFirstChild(oMenuItem.element);

			//	Dispatch a "click" event to the MenuItem's anchor so that its
			//	"click" event handlers will get called in response to the user 
			//	pressing the keyboard shortcut defined by the "keylistener"
			//	configuration property.

			if (UA.ie) {
				oAnchor.fireEvent(_ONCLICK);
			}
			else {

				if ((UA.gecko && UA.gecko >= 1.9) || UA.opera || UA.webkit) {

					oEvent = document.createEvent("HTMLEvents");
					oEvent.initEvent(_CLICK, true, true);

				}
				else {

					oEvent = document.createEvent("MouseEvents");
					oEvent.initMouseEvent(_CLICK, true, true, window, 0, 0, 0, 
						0, 0, false, false, false, false, 0, null);

				}

				oAnchor.dispatchEvent(oEvent);

			}

		}

	},


    /**
    * @method _createKeyListener
    * @description "show" event handler for a Menu instance - responsible for 
	* setting up the KeyListener instance for a MenuItem.
	* @private	
    * @param {String} type String representing the name of the event that 
    * was fired.
    * @param {Array} args Array of arguments sent when the event was fired.
    * @param {Array} keyData Array of arguments sent when the event was fired.
    */
	_createKeyListener: function (type, args, keyData) {

		var oMenuItem = this,
			oMenu = oMenuItem.parent;

		var oKeyListener = new YAHOO.util.KeyListener(
										oMenu.element.ownerDocument, 
										keyData, 
										{
											fn: oMenuItem._dispatchClickEvent, 
											scope: oMenuItem, 
											correctScope: true });


		if (oMenu.cfg.getProperty(_VISIBLE)) {
			oKeyListener.enable();
		}


		oMenu.subscribe(_SHOW, oKeyListener.enable, null, oKeyListener);
		oMenu.subscribe(_HIDE, oKeyListener.disable, null, oKeyListener);
		
		oMenuItem._keyListener = oKeyListener;
		
		oMenu.unsubscribe(_SHOW, oMenuItem._createKeyListener, keyData);
		
	},


    /**
    * @method configKeyListener
    * @description Event handler for when the "keylistener" configuration 
    * property of a menu item changes.
    * @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.
    */
    configKeyListener: function (p_sType, p_aArgs) {

		var oKeyData = p_aArgs[0],
			oMenuItem = this,
			oMenu = oMenuItem.parent;

		if (oMenuItem._keyData) {

			//	Unsubscribe from the "show" event in case the keylistener 
			//	config was changed before the Menu was ever made visible.

			oMenu.unsubscribe(_SHOW, 
					oMenuItem._createKeyListener, oMenuItem._keyData);

			oMenuItem._keyData = null;					
					
		}


		//	Tear down for the previous value of the "keylistener" property

		if (oMenuItem._keyListener) {

			oMenu.unsubscribe(_SHOW, oMenuItem._keyListener.enable);
			oMenu.unsubscribe(_HIDE, oMenuItem._keyListener.disable);

			oMenuItem._keyListener.disable();
			oMenuItem._keyListener = null;

		}


    	if (oKeyData) {
	
			oMenuItem._keyData = oKeyData;

			//	Defer the creation of the KeyListener instance until the 
			//	parent Menu is visible.  This is necessary since the 
			//	KeyListener instance needs to be bound to the document the 
			//	Menu has been rendered into.  Deferring creation of the 
			//	KeyListener instance also improves performance.

			oMenu.subscribe(_SHOW, oMenuItem._createKeyListener, 
				oKeyData, oMenuItem);
		}
    
    },


    // Public methods


	/**
    * @method initDefaultConfig
	* @description Initializes an item's configurable properties.
	*/
	initDefaultConfig : function () {

        var oConfig = this.cfg;


        // Define the configuration attributes

        /**
        * @config text
        * @description String specifying the text label for the menu item.  
        * When building a menu from existing HTML the value of this property
        * will be interpreted from the menu's markup.
        * @default ""
        * @type String
        */
        oConfig.addProperty(
            TEXT_CONFIG.key, 
            { 
                handler: this.configText, 
                value: TEXT_CONFIG.value, 
                validator: TEXT_CONFIG.validator, 
                suppressEvent: TEXT_CONFIG.suppressEvent 
            }
        );
        

        /**
        * @config helptext
        * @description String specifying additional instructional text to 
        * accompany the text for the menu item.
        * @deprecated Use "text" configuration property to add help text markup.  
        * For example: <code>oMenuItem.cfg.setProperty("text", "Copy &#60;em 
        * class=\"helptext\"&#62;Ctrl + C&#60;/em&#62;");</code>
        * @default null
        * @type String|<a href="http://www.w3.org/TR/
        * 2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
        * HTMLElement</a>
        */
        oConfig.addProperty(
            HELP_TEXT_CONFIG.key,
            {
                handler: this.configHelpText, 
                supercedes: HELP_TEXT_CONFIG.supercedes,
                suppressEvent: HELP_TEXT_CONFIG.suppressEvent 
            }
        );


        /**
        * @config url
        * @description String specifying the URL for the menu item's anchor's 
        * "href" attribute.  When building a menu from existing HTML the value 
        * of this property will be interpreted from the menu's markup.
        * @default "#"
        * @type String
        */        
        oConfig.addProperty(
            URL_CONFIG.key, 
            {
                handler: this.configURL, 
                value: URL_CONFIG.value, 
                suppressEvent: URL_CONFIG.suppressEvent
            }
        );


        /**
        * @config target
        * @description String specifying the value for the "target" attribute 
        * of the menu item's anchor element. <strong>Specifying a target will 
        * require the user to click directly on the menu item's anchor node in
        * order to cause the browser to navigate to the specified URL.</strong> 
        * When building a menu from existing HTML the value of this property 
        * will be interpreted from the menu's markup.
        * @default null
        * @type String
        */        
        oConfig.addProperty(
            TARGET_CONFIG.key, 
            {
                handler: this.configTarget, 
                suppressEvent: TARGET_CONFIG.suppressEvent
            }
        );


        /**
        * @config emphasis
        * @description Boolean indicating if the text of the menu item will be 
        * rendered with emphasis.
        * @deprecated Use the "text" configuration property to add emphasis.  
        * For example: <code>oMenuItem.cfg.setProperty("text", "&#60;em&#62;Some 
        * Text&#60;/em&#62;");</code>
        * @default false
        * @type Boolean
        */
        oConfig.addProperty(
            EMPHASIS_CONFIG.key, 
            { 
                handler: this.configEmphasis, 
                value: EMPHASIS_CONFIG.value, 
                validator: EMPHASIS_CONFIG.validator, 
                suppressEvent: EMPHASIS_CONFIG.suppressEvent,
                supercedes: EMPHASIS_CONFIG.supercedes
            }
        );


        /**
        * @config strongemphasis
        * @description Boolean indicating if the text of the menu item will be 
        * rendered with strong emphasis.
        * @deprecated Use the "text" configuration property to add strong emphasis.  
        * For example: <code>oMenuItem.cfg.setProperty("text", "&#60;strong&#62; 
        * Some Text&#60;/strong&#62;");</code>
        * @default false
        * @type Boolean
        */
        oConfig.addProperty(
            STRONG_EMPHASIS_CONFIG.key,
            {
                handler: this.configStrongEmphasis,
                value: STRONG_EMPHASIS_CONFIG.value,
                validator: STRONG_EMPHASIS_CONFIG.validator,
                suppressEvent: STRONG_EMPHASIS_CONFIG.suppressEvent,
                supercedes: STRONG_EMPHASIS_CONFIG.supercedes
            }
        );


        /**
        * @config checked
        * @description Boolean indicating if the menu item should be rendered 
        * with a checkmark.
        * @default false
        * @type Boolean
        */
        oConfig.addProperty(
            CHECKED_CONFIG.key, 
            {
                handler: this.configChecked, 
                value: CHECKED_CONFIG.value, 
                validator: CHECKED_CONFIG.validator, 
                suppressEvent: CHECKED_CONFIG.suppressEvent,
                supercedes: CHECKED_CONFIG.supercedes
            } 
        );


        /**
        * @config disabled
        * @description Boolean indicating if the menu item should be disabled.  
        * (Disabled menu items are  dimmed and will not respond to user input 
        * or fire events.)
        * @default false
        * @type Boolean
        */
        oConfig.addProperty(
            DISABLED_CONFIG.key,
            {
                handler: this.configDisabled,
                value: DISABLED_CONFIG.value,
                validator: DISABLED_CONFIG.validator,
                suppressEvent: DISABLED_CONFIG.suppressEvent
            }
        );


        /**
        * @config selected
        * @description Boolean indicating if the menu item should 
        * be highlighted.
        * @default false
        * @type Boolean
        */
        oConfig.addProperty(
            SELECTED_CONFIG.key,
            {
                handler: this.configSelected,
                value: SELECTED_CONFIG.value,
                validator: SELECTED_CONFIG.validator,
                suppressEvent: SELECTED_CONFIG.suppressEvent
            }
        );


        /**
        * @config submenu
        * @description Object specifying the submenu to be appended to the 
        * menu item.  The value can be one of the following: <ul><li>Object 
        * specifying a Menu instance.</li><li>Object literal specifying the
        * menu to be created.  Format: <code>{ id: [menu id], itemdata: 
        * [<a href="YAHOO.widget.Menu.html#itemData">array of values for 
        * items</a>] }</code>.</li><li>String specifying the id attribute 
        * of the <code>&#60;div&#62;</code> element of the menu.</li><li>
        * Object specifying the <code>&#60;div&#62;</code> element of the 
        * menu.</li></ul>
        * @default null
        * @type Menu|String|Object|<a href="http://www.w3.org/TR/2000/
        * WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
        * HTMLElement</a>
        */
        oConfig.addProperty(
            SUBMENU_CONFIG.key, 
            {
                handler: this.configSubmenu, 
                supercedes: SUBMENU_CONFIG.supercedes,
                suppressEvent: SUBMENU_CONFIG.suppressEvent
            }
        );


        /**
        * @config onclick
        * @description Object literal representing the code to be executed when 
        * the item is clicked.  Format:<br> <code> {<br> 
        * <strong>fn:</strong> Function,   &#47;&#47; The handler to call when 
        * the event fires.<br> <strong>obj:</strong> Object, &#47;&#47; An 
        * object to  pass back to the handler.<br> <strong>scope:</strong> 
        * Object &#47;&#47; The object to use for the scope of the handler.
        * <br> } </code>
        * @type Object
        * @default null
        */
        oConfig.addProperty(
            ONCLICK_CONFIG.key, 
            {
                handler: this.configOnClick, 
                suppressEvent: ONCLICK_CONFIG.suppressEvent 
            }
        );


        /**
        * @config classname
        * @description CSS class to be applied to the menu item's root 
        * <code>&#60;li&#62;</code> element.  The specified class(es) are 
        * appended in addition to the default class as specified by the menu 
        * item's CSS_CLASS_NAME constant.
        * @default null
        * @type String
        */
        oConfig.addProperty(
            CLASS_NAME_CONFIG.key, 
            { 
                handler: this.configClassName,
                value: CLASS_NAME_CONFIG.value, 
                validator: CLASS_NAME_CONFIG.validator,
                suppressEvent: CLASS_NAME_CONFIG.suppressEvent 
            }
        );


        /**
        * @config keylistener
        * @description Object literal representing the key(s) that can be used 
 		* to trigger the MenuItem's "click" event.  Possible attributes are 
		* shift (boolean), alt (boolean), ctrl (boolean) and keys (either an int 
		* or an array of ints representing keycodes).
        * @default null
        * @type Object
        */
        oConfig.addProperty(
            KEY_LISTENER_CONFIG.key, 
            { 
                handler: this.configKeyListener,
                value: KEY_LISTENER_CONFIG.value, 
                suppressEvent: KEY_LISTENER_CONFIG.suppressEvent 
            }
        );

	},

    /**
    * @method getNextSibling
    * @description Finds the menu item's next sibling.
    * @return YAHOO.widget.MenuItem
    */
	getNextSibling: function () {
	
		var isUL = function (el) {
				return (el.nodeName.toLowerCase() === "ul");
			},
	
			menuitemEl = this.element,
			next = Dom.getNextSibling(menuitemEl),
			parent,
			sibling,
			list;
		
		if (!next) {
			
			parent = menuitemEl.parentNode;
			sibling = Dom.getNextSiblingBy(parent, isUL);
			
			if (sibling) {
				list = sibling;
			}
			else {
				list = Dom.getFirstChildBy(parent.parentNode, isUL);
			}
			
			next = Dom.getFirstChild(list);
			
		}

		return YAHOO.widget.MenuManager.getMenuItem(next.id);

	},

    /**
    * @method getNextEnabledSibling
    * @description Finds the menu item's next enabled sibling.
    * @return YAHOO.widget.MenuItem
    */
	getNextEnabledSibling: function () {
		
		var next = this.getNextSibling();
		
        return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getNextEnabledSibling() : next;
		
	},


    /**
    * @method getPreviousSibling
    * @description Finds the menu item's previous sibling.
    * @return {YAHOO.widget.MenuItem}
    */	
	getPreviousSibling: function () {

		var isUL = function (el) {
				return (el.nodeName.toLowerCase() === "ul");
			},

			menuitemEl = this.element,
			next = Dom.getPreviousSibling(menuitemEl),
			parent,
			sibling,
			list;
		
		if (!next) {
			
			parent = menuitemEl.parentNode;
			sibling = Dom.getPreviousSiblingBy(parent, isUL);
			
			if (sibling) {
				list = sibling;
			}
			else {
				list = Dom.getLastChildBy(parent.parentNode, isUL);
			}
			
			next = Dom.getLastChild(list);
			
		}

		return YAHOO.widget.MenuManager.getMenuItem(next.id);
		
	},


    /**
    * @method getPreviousEnabledSibling
    * @description Finds the menu item's previous enabled sibling.
    * @return {YAHOO.widget.MenuItem}
    */
	getPreviousEnabledSibling: function () {
		
		var next = this.getPreviousSibling();
		
        return (next.cfg.getProperty(_DISABLED) || next.element.style.display == _NONE) ? next.getPreviousEnabledSibling() : next;
		
	},


    /**
    * @method focus
    * @description Causes the menu item to receive the focus and fires the 
    * focus event.
    */
    focus: function () {

        var oParent = this.parent,
            oAnchor = this._oAnchor,
            oActiveItem = oParent.activeItem;


        function setFocus() {

            try {

                if (!(UA.ie && !document.hasFocus())) {
                
					if (oActiveItem) {
		
						oActiveItem.blurEvent.fire();
		
					}
	
					oAnchor.focus();
					
					this.focusEvent.fire();
                
                }

            }
            catch(e) {
            
            }

        }


        if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE) && 
            this.element.style.display != _NONE) {


            /*
                Setting focus via a timer fixes a race condition in Firefox, IE 
                and Opera where the browser viewport jumps as it trys to 
                position and focus the menu.
            */

            Lang.later(0, this, setFocus);

        }

    },


    /**
    * @method blur
    * @description Causes the menu item to lose focus and fires the 
    * blur event.
    */    
    blur: function () {

        var oParent = this.parent;

        if (!this.cfg.getProperty(_DISABLED) && oParent && oParent.cfg.getProperty(_VISIBLE)) {

            Lang.later(0, this, function () {

                try {
    
                    this._oAnchor.blur();
                    this.blurEvent.fire();    

                } 
                catch (e) {
                
                }
                
            }, 0);

        }

    },


    /**
    * @method hasFocus
    * @description Returns a boolean indicating whether or not the menu item
    * has focus.
    * @return {Boolean}
    */
    hasFocus: function () {
    
        return (YAHOO.widget.MenuManager.getFocusedMenuItem() == this);
    
    },


	/**
    * @method destroy
	* @description Removes the menu item's <code>&#60;li&#62;</code> element 
	* from its parent <code>&#60;ul&#62;</code> element.
	*/
    destroy: function () {

        var oEl = this.element,
            oSubmenu,
            oParentNode,
            aEventData,
            i;


        if (oEl) {


            // If the item has a submenu, destroy it first

            oSubmenu = this.cfg.getProperty(_SUBMENU);

            if (oSubmenu) {
            
                oSubmenu.destroy();
            
            }


            // Remove the element from the parent node

            oParentNode = oEl.parentNode;

            if (oParentNode) {

                oParentNode.removeChild(oEl);

                this.destroyEvent.fire();

            }


            // Remove CustomEvent listeners

			i = EVENT_TYPES.length - 1;

			do {

				aEventData = EVENT_TYPES[i];
				
				this[aEventData[0]].unsubscribeAll();

			}
			while (i--);
            
            
            this.cfg.configChangedEvent.unsubscribeAll();

        }

    },


    /**
    * @method toString
    * @description Returns a string representing the menu item.
    * @return {String}
    */
    toString: function () {

        var sReturnVal = _MENUITEM,
            sId = this.id;

        if (sId) {
    
            sReturnVal += (_SPACE + sId);
        
        }

        return sReturnVal;
    
    }

};

Lang.augmentProto(MenuItem, YAHOO.util.EventProvider);

})();
(function () {

	var _XY = "xy",
		_MOUSEDOWN = "mousedown",
		_CONTEXTMENU = "ContextMenu",
		_SPACE = " ";

/**
* Creates a list of options or commands which are made visible in response to 
* an HTML element's "contextmenu" event ("mousedown" for Opera).
*
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;div&#62;</code> element of the context menu.
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;select&#62;</code> element to be used as the data source for the 
* context menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
* html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the 
* <code>&#60;div&#62;</code> element of the context menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
* html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying 
* the <code>&#60;select&#62;</code> element to be used as the data source for 
* the context menu.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the context menu. See configuration class documentation 
* for more details.
* @class ContextMenu
* @constructor
* @extends YAHOO.widget.Menu
* @namespace YAHOO.widget
*/
YAHOO.widget.ContextMenu = function(p_oElement, p_oConfig) {

    YAHOO.widget.ContextMenu.superclass.constructor.call(this, p_oElement, p_oConfig);

};


var Event = YAHOO.util.Event,
	UA = YAHOO.env.ua,
    ContextMenu = YAHOO.widget.ContextMenu,



    /**
    * Constant representing the name of the ContextMenu's events
    * @property EVENT_TYPES
    * @private
    * @final
    * @type Object
    */
    EVENT_TYPES = {

        "TRIGGER_CONTEXT_MENU": "triggerContextMenu",
        "CONTEXT_MENU": (UA.opera ? _MOUSEDOWN : "contextmenu"),
        "CLICK": "click"

    },
    
    
    /**
    * Constant representing the ContextMenu's configuration properties
    * @property DEFAULT_CONFIG
    * @private
    * @final
    * @type Object
    */
    TRIGGER_CONFIG = { 
		key: "trigger",
		suppressEvent: true
    };


/**
* @method position
* @description "beforeShow" event handler used to position the contextmenu.
* @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.
* @param {Array} p_aPos Array representing the xy position for the context menu.
*/
function position(p_sType, p_aArgs, p_aPos) {

    this.cfg.setProperty(_XY, p_aPos);
    
    this.beforeShowEvent.unsubscribe(position, p_aPos);

}


YAHOO.lang.extend(ContextMenu, YAHOO.widget.Menu, {



// Private properties


/**
* @property _oTrigger
* @description Object reference to the current value of the "trigger" 
* configuration property.
* @default null
* @private
* @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/leve
* l-one-html.html#ID-58190037">HTMLElement</a>|Array
*/
_oTrigger: null,


/**
* @property _bCancelled
* @description Boolean indicating if the display of the context menu should 
* be cancelled.
* @default false
* @private
* @type Boolean
*/
_bCancelled: false,



// Public properties


/**
* @property contextEventTarget
* @description Object reference for the HTML element that was the target of the
* "contextmenu" DOM event ("mousedown" for Opera) that triggered the display of 
* the context menu.
* @default null
* @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
* html.html#ID-58190037">HTMLElement</a>
*/
contextEventTarget: null,



// Events


/**
* @event triggerContextMenuEvent
* @description Custom Event wrapper for the "contextmenu" DOM event 
* ("mousedown" for Opera) fired by the element(s) that trigger the display of 
* the context menu.
*/
triggerContextMenuEvent: null,



/**
* @method init
* @description The ContextMenu class's initialization method. This method is 
* automatically called by the constructor, and sets up all DOM references for 
* pre-existing markup, and creates required markup if it is not already present.
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;div&#62;</code> element of the context menu.
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;select&#62;</code> element to be used as the data source for 
* the context menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
* html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the 
* <code>&#60;div&#62;</code> element of the context menu.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
* html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying 
* the <code>&#60;select&#62;</code> element to be used as the data source for 
* the context menu.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the context menu. See configuration class documentation 
* for more details.
*/
init: function(p_oElement, p_oConfig) {


    // Call the init of the superclass (YAHOO.widget.Menu)

    ContextMenu.superclass.init.call(this, p_oElement);


    this.beforeInitEvent.fire(ContextMenu);


    if (p_oConfig) {

        this.cfg.applyConfig(p_oConfig, true);

    }
    
    this.initEvent.fire(ContextMenu);
    
},


/**
* @method initEvents
* @description Initializes the custom events for the context menu.
*/
initEvents: function() {

	ContextMenu.superclass.initEvents.call(this);

    // Create custom events

    this.triggerContextMenuEvent = this.createEvent(EVENT_TYPES.TRIGGER_CONTEXT_MENU);

    this.triggerContextMenuEvent.signature = YAHOO.util.CustomEvent.LIST;

},


/**
* @method cancel
* @description Cancels the display of the context menu.
*/
cancel: function() {

    this._bCancelled = true;

},



// Private methods


/**
* @method _removeEventHandlers
* @description Removes all of the DOM event handlers from the HTML element(s) 
* whose "context menu" event ("click" for Opera) trigger the display of 
* the context menu.
* @private
*/
_removeEventHandlers: function() {

    var oTrigger = this._oTrigger;


    // Remove the event handlers from the trigger(s)

    if (oTrigger) {

        Event.removeListener(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu);    
        
        if (UA.opera) {
        
            Event.removeListener(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick);
    
        }

    }

},



// Private event handlers



/**
* @method _onTriggerClick
* @description "click" event handler for the HTML element(s) identified as the 
* "trigger" for the context menu.  Used to cancel default behaviors in Opera.
* @private
* @param {Event} p_oEvent Object representing the DOM event object passed back 
* by the event utility (YAHOO.util.Event).
* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 
* menu that is handling the event.
*/
_onTriggerClick: function(p_oEvent, p_oMenu) {

    if (p_oEvent.ctrlKey) {
    
        Event.stopEvent(p_oEvent);

    }
    
},


/**
* @method _onTriggerContextMenu
* @description "contextmenu" event handler ("mousedown" for Opera) for the HTML 
* element(s) that trigger the display of the context menu.
* @private
* @param {Event} p_oEvent Object representing the DOM event object passed back 
* by the event utility (YAHOO.util.Event).
* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 
* menu that is handling the event.
*/
_onTriggerContextMenu: function(p_oEvent, p_oMenu) {

    var aXY;

    if (!(p_oEvent.type == _MOUSEDOWN && !p_oEvent.ctrlKey)) {
	
		this.contextEventTarget = Event.getTarget(p_oEvent);
	
		this.triggerContextMenuEvent.fire(p_oEvent);
		
	
		if (!this._bCancelled) {

			/*
				Prevent the browser's default context menu from appearing and 
				stop the propagation of the "contextmenu" event so that 
				other ContextMenu instances are not displayed.
			*/

			Event.stopEvent(p_oEvent);


			// Hide any other Menu instances that might be visible

			YAHOO.widget.MenuManager.hideVisible();
			
	

			// Position and display the context menu
	
			aXY = Event.getXY(p_oEvent);
	
	
			if (!YAHOO.util.Dom.inDocument(this.element)) {
	
				this.beforeShowEvent.subscribe(position, aXY);
	
			}
			else {
	
				this.cfg.setProperty(_XY, aXY);
			
			}
	
	
			this.show();
	
		}
	
		this._bCancelled = false;

    }

},



// Public methods


/**
* @method toString
* @description Returns a string representing the context menu.
* @return {String}
*/
toString: function() {

    var sReturnVal = _CONTEXTMENU,
        sId = this.id;

    if (sId) {

        sReturnVal += (_SPACE + sId);
    
    }

    return sReturnVal;

},


/**
* @method initDefaultConfig
* @description Initializes the class's configurable properties which can be 
* changed using the context menu's Config object ("cfg").
*/
initDefaultConfig: function() {

    ContextMenu.superclass.initDefaultConfig.call(this);

    /**
    * @config trigger
    * @description The HTML element(s) whose "contextmenu" event ("mousedown" 
    * for Opera) trigger the display of the context menu.  Can be a string 
    * representing the id attribute of the HTML element, an object reference 
    * for the HTML element, or an array of strings or HTML element references.
    * @default null
    * @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
    * level-one-html.html#ID-58190037">HTMLElement</a>|Array
    */
    this.cfg.addProperty(TRIGGER_CONFIG.key, 
        {
            handler: this.configTrigger, 
            suppressEvent: TRIGGER_CONFIG.suppressEvent 
        }
    );

},


/**
* @method destroy
* @description Removes the context menu's <code>&#60;div&#62;</code> element 
* (and accompanying child nodes) from the document.
*/
destroy: function() {

    // Remove the DOM event handlers from the current trigger(s)

    this._removeEventHandlers();


    // Continue with the superclass implementation of this method

    ContextMenu.superclass.destroy.call(this);

},



// Public event handlers for configuration properties


/**
* @method configTrigger
* @description Event handler for when the value of the "trigger" configuration 
* property changes. 
* @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.
* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context 
* menu that fired the event.
*/
configTrigger: function(p_sType, p_aArgs, p_oMenu) {
    
    var oTrigger = p_aArgs[0];

    if (oTrigger) {

        /*
            If there is a current "trigger" - remove the event handlers 
            from that element(s) before assigning new ones
        */

        if (this._oTrigger) {
        
            this._removeEventHandlers();

        }

        this._oTrigger = oTrigger;


        /*
            Listen for the "mousedown" event in Opera b/c it does not 
            support the "contextmenu" event
        */ 
  
        Event.on(oTrigger, EVENT_TYPES.CONTEXT_MENU, this._onTriggerContextMenu, this, true);


        /*
            Assign a "click" event handler to the trigger element(s) for
            Opera to prevent default browser behaviors.
        */

        if (UA.opera) {
        
            Event.on(oTrigger, EVENT_TYPES.CLICK, this._onTriggerClick, this, true);

        }

    }
    else {
   
        this._removeEventHandlers();
    
    }
    
}

}); // END YAHOO.lang.extend

}());



/**
* Creates an item for a context menu.
* 
* @param {String} p_oObject String specifying the text of the context menu item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 
* <code>&#60;li&#62;</code> element of the context menu item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
* specifying the <code>&#60;optgroup&#62;</code> element of the context 
* menu item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 
* the <code>&#60;option&#62;</code> element of the context menu item.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the context menu item. See configuration class 
* documentation for more details.
* @class ContextMenuItem
* @constructor
* @extends YAHOO.widget.MenuItem
* @deprecated As of version 2.4.0 items for YAHOO.widget.ContextMenu instances
* are of type YAHOO.widget.MenuItem.
*/
YAHOO.widget.ContextMenuItem = YAHOO.widget.MenuItem;
(function () {

	var Lang = YAHOO.lang,

		// String constants
	
		_STATIC = "static",
		_DYNAMIC_STATIC = "dynamic," + _STATIC,
		_DISABLED = "disabled",
		_SELECTED = "selected",
		_AUTO_SUBMENU_DISPLAY = "autosubmenudisplay",
		_SUBMENU = "submenu",
		_VISIBLE = "visible",
		_SPACE = " ",
		_SUBMENU_TOGGLE_REGION = "submenutoggleregion",
		_MENUBAR = "MenuBar";

/**
* Horizontal collection of items, each of which can contain a submenu.
* 
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;div&#62;</code> element of the menu bar.
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;select&#62;</code> element to be used as the data source for the 
* menu bar.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying 
* the <code>&#60;div&#62;</code> element of the menu bar.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object 
* specifying the <code>&#60;select&#62;</code> element to be used as the data 
* source for the menu bar.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the menu bar. See configuration class documentation for
* more details.
* @class MenuBar
* @constructor
* @extends YAHOO.widget.Menu
* @namespace YAHOO.widget
*/
YAHOO.widget.MenuBar = function(p_oElement, p_oConfig) {

    YAHOO.widget.MenuBar.superclass.constructor.call(this, p_oElement, p_oConfig);

};


/**
* @method checkPosition
* @description Checks to make sure that the value of the "position" property 
* is one of the supported strings. Returns true if the position is supported.
* @private
* @param {Object} p_sPosition String specifying the position of the menu.
* @return {Boolean}
*/
function checkPosition(p_sPosition) {

	var returnVal = false;

    if (Lang.isString(p_sPosition)) {

        returnVal = (_DYNAMIC_STATIC.indexOf((p_sPosition.toLowerCase())) != -1);

    }
    
    return returnVal;

}


var Event = YAHOO.util.Event,
    MenuBar = YAHOO.widget.MenuBar,

    POSITION_CONFIG =  { 
		key: "position", 
		value: _STATIC, 
		validator: checkPosition, 
		supercedes: [_VISIBLE] 
	}, 

	SUBMENU_ALIGNMENT_CONFIG =  { 
		key: "submenualignment", 
		value: ["tl","bl"]
	},

	AUTO_SUBMENU_DISPLAY_CONFIG =  { 
		key: _AUTO_SUBMENU_DISPLAY, 
		value: false, 
		validator: Lang.isBoolean,
		suppressEvent: true
	},
	
	SUBMENU_TOGGLE_REGION_CONFIG = {
		key: _SUBMENU_TOGGLE_REGION, 
		value: false, 
		validator: Lang.isBoolean
	};



Lang.extend(MenuBar, YAHOO.widget.Menu, {

/**
* @method init
* @description The MenuBar class's initialization method. This method is 
* automatically called by the constructor, and sets up all DOM references for 
* pre-existing markup, and creates required markup if it is not already present.
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;div&#62;</code> element of the menu bar.
* @param {String} p_oElement String specifying the id attribute of the 
* <code>&#60;select&#62;</code> element to be used as the data source for the 
* menu bar.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying 
* the <code>&#60;div&#62;</code> element of the menu bar.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object 
* specifying the <code>&#60;select&#62;</code> element to be used as the data 
* source for the menu bar.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the menu bar. See configuration class documentation for
* more details.
*/
init: function(p_oElement, p_oConfig) {

    if(!this.ITEM_TYPE) {

        this.ITEM_TYPE = YAHOO.widget.MenuBarItem;

    }


    // Call the init of the superclass (YAHOO.widget.Menu)

    MenuBar.superclass.init.call(this, p_oElement);


    this.beforeInitEvent.fire(MenuBar);


    if(p_oConfig) {

        this.cfg.applyConfig(p_oConfig, true);

    }

    this.initEvent.fire(MenuBar);

},



// Constants


/**
* @property CSS_CLASS_NAME
* @description String representing the CSS class(es) to be applied to the menu 
* bar's <code>&#60;div&#62;</code> element.
* @default "yuimenubar"
* @final
* @type String
*/
CSS_CLASS_NAME: "yuimenubar",


/**
* @property SUBMENU_TOGGLE_REGION_WIDTH
* @description Width (in pixels) of the area of a MenuBarItem that, when pressed, will toggle the
* display of the MenuBarItem's submenu.
* @default 20
* @final
* @type Number
*/
SUBMENU_TOGGLE_REGION_WIDTH: 20,


// Protected event handlers


/**
* @method _onKeyDown
* @description "keydown" Custom Event handler for the menu bar.
* @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.
* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar 
* that fired the event.
*/
_onKeyDown: function(p_sType, p_aArgs, p_oMenuBar) {

    var oEvent = p_aArgs[0],
        oItem = p_aArgs[1],
        oSubmenu,
        oItemCfg,
        oNextItem;


    if(oItem && !oItem.cfg.getProperty(_DISABLED)) {

        oItemCfg = oItem.cfg;

        switch(oEvent.keyCode) {
    
            case 37:    // Left arrow
            case 39:    // Right arrow
    
                if(oItem == this.activeItem && !oItemCfg.getProperty(_SELECTED)) {
    
                    oItemCfg.setProperty(_SELECTED, true);
    
                }
                else {
    
                    oNextItem = (oEvent.keyCode == 37) ? 
                        oItem.getPreviousEnabledSibling() : 
                        oItem.getNextEnabledSibling();
            
                    if(oNextItem) {
    
                        this.clearActiveItem();
    
                        oNextItem.cfg.setProperty(_SELECTED, true);
                        
						oSubmenu = oNextItem.cfg.getProperty(_SUBMENU);
						
						if(oSubmenu) {
					
							oSubmenu.show();
							oSubmenu.setInitialFocus();
						
						}
						else {
							oNextItem.focus();  
						}
    
                    }
    
                }
    
                Event.preventDefault(oEvent);
    
            break;
    
            case 40:    // Down arrow
    
                if(this.activeItem != oItem) {
    
                    this.clearActiveItem();
    
                    oItemCfg.setProperty(_SELECTED, true);
                    oItem.focus();
                
                }
    
                oSubmenu = oItemCfg.getProperty(_SUBMENU);
    
                if(oSubmenu) {
    
                    if(oSubmenu.cfg.getProperty(_VISIBLE)) {
    
                        oSubmenu.setInitialSelection();
                        oSubmenu.setInitialFocus();
                    
                    }
                    else {
    
                        oSubmenu.show();
                        oSubmenu.setInitialFocus();
                    
                    }
    
                }
    
                Event.preventDefault(oEvent);
    
            break;
    
        }

    }


    if(oEvent.keyCode == 27 && this.activeItem) { // Esc key

        oSubmenu = this.activeItem.cfg.getProperty(_SUBMENU);

        if(oSubmenu && oSubmenu.cfg.getProperty(_VISIBLE)) {
        
            oSubmenu.hide();
            this.activeItem.focus();
        
        }
        else {

            this.activeItem.cfg.setProperty(_SELECTED, false);
            this.activeItem.blur();
    
        }

        Event.preventDefault(oEvent);
    
    }

},


/**
* @method _onClick
* @description "click" event handler for the menu bar.
* @protected
* @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.
* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar 
* that fired the event.
*/
_onClick: function(p_sType, p_aArgs, p_oMenuBar) {

    MenuBar.superclass._onClick.call(this, p_sType, p_aArgs, p_oMenuBar);

    var oItem = p_aArgs[1],
        bReturnVal = true,
    	oItemEl,
        oEvent,
        oTarget,
        oActiveItem,
        oConfig,
        oSubmenu,
        nMenuItemX,
        nToggleRegion;


	var toggleSubmenuDisplay = function () {

		if(oSubmenu.cfg.getProperty(_VISIBLE)) {
		
			oSubmenu.hide();
		
		}
		else {
		
			oSubmenu.show();                    
		
		}
	
	};
    

    if(oItem && !oItem.cfg.getProperty(_DISABLED)) {

        oEvent = p_aArgs[0];
        oTarget = Event.getTarget(oEvent);
        oActiveItem = this.activeItem;
        oConfig = this.cfg;


        // Hide any other submenus that might be visible
    
        if(oActiveItem && oActiveItem != oItem) {
    
            this.clearActiveItem();
    
        }

    
        oItem.cfg.setProperty(_SELECTED, true);
    

        // Show the submenu for the item
    
        oSubmenu = oItem.cfg.getProperty(_SUBMENU);


        if(oSubmenu) {

			oItemEl = oItem.element;
			nMenuItemX = YAHOO.util.Dom.getX(oItemEl);
			nToggleRegion = nMenuItemX + (oItemEl.offsetWidth - this.SUBMENU_TOGGLE_REGION_WIDTH);

			if (oConfig.getProperty(_SUBMENU_TOGGLE_REGION)) {

				if (Event.getPageX(oEvent) > nToggleRegion) {

					toggleSubmenuDisplay();

					Event.preventDefault(oEvent);

					/*
						 Return false so that other click event handlers are not called when the 
						 user clicks inside the toggle region.
					*/
					bReturnVal = false;
				
				}
        
        	}
			else {

				toggleSubmenuDisplay();
            
            }
        
        }
    
    }


	return bReturnVal;

},



// Public methods

/**
* @method configSubmenuToggle
* @description Event handler for when the "submenutoggleregion" configuration property of 
* a MenuBar changes.
* @param {String} p_sType The name of the event that was fired.
* @param {Array} p_aArgs Collection of arguments sent when the event was fired.
*/
configSubmenuToggle: function (p_sType, p_aArgs) {

	var bSubmenuToggle = p_aArgs[0];
	
	if (bSubmenuToggle) {
	
		this.cfg.setProperty(_AUTO_SUBMENU_DISPLAY, false);
	
	}

},


/**
* @method toString
* @description Returns a string representing the menu bar.
* @return {String}
*/
toString: function() {

    var sReturnVal = _MENUBAR,
        sId = this.id;

    if(sId) {

        sReturnVal += (_SPACE + sId);
    
    }

    return sReturnVal;

},


/**
* @description Initializes the class's configurable properties which can be
* changed using the menu bar's Config object ("cfg").
* @method initDefaultConfig
*/
initDefaultConfig: function() {

    MenuBar.superclass.initDefaultConfig.call(this);

    var oConfig = this.cfg;

	// Add configuration properties


    /*
        Set the default value for the "position" configuration property
        to "static" by re-adding the property.
    */


    /**
    * @config position
    * @description String indicating how a menu bar should be positioned on the 
    * screen.  Possible values are "static" and "dynamic."  Static menu bars 
    * are visible by default and reside in the normal flow of the document 
    * (CSS position: static).  Dynamic menu bars are hidden by default, reside
    * out of the normal flow of the document (CSS position: absolute), and can 
    * overlay other elements on the screen.
    * @default static
    * @type String
    */
    oConfig.addProperty(
        POSITION_CONFIG.key, 
        {
            handler: this.configPosition, 
            value: POSITION_CONFIG.value, 
            validator: POSITION_CONFIG.validator,
            supercedes: POSITION_CONFIG.supercedes
        }
    );


    /*
        Set the default value for the "submenualignment" configuration property
        to ["tl","bl"] by re-adding the property.
    */

    /**
    * @config submenualignment
    * @description Array defining how submenus should be aligned to their 
    * parent menu bar item. The format is: [itemCorner, submenuCorner].
    * @default ["tl","bl"]
    * @type Array
    */
    oConfig.addProperty(
        SUBMENU_ALIGNMENT_CONFIG.key, 
        {
            value: SUBMENU_ALIGNMENT_CONFIG.value,
            suppressEvent: SUBMENU_ALIGNMENT_CONFIG.suppressEvent
        }
    );


    /*
        Change the default value for the "autosubmenudisplay" configuration 
        property to "false" by re-adding the property.
    */

    /**
    * @config autosubmenudisplay
    * @description Boolean indicating if submenus are automatically made 
    * visible when the user mouses over the menu bar's items.
    * @default false
    * @type Boolean
    */
	oConfig.addProperty(
	   AUTO_SUBMENU_DISPLAY_CONFIG.key, 
	   {
	       value: AUTO_SUBMENU_DISPLAY_CONFIG.value, 
	       validator: AUTO_SUBMENU_DISPLAY_CONFIG.validator,
	       suppressEvent: AUTO_SUBMENU_DISPLAY_CONFIG.suppressEvent
       } 
    );


    /**
    * @config submenutoggleregion
    * @description Boolean indicating if only a specific region of a MenuBarItem should toggle the 
    * display of a submenu.  The default width of the region is determined by the value of the
    * SUBMENU_TOGGLE_REGION_WIDTH property.  If set to true, the autosubmenudisplay 
    * configuration property will be set to false, and any click event listeners will not be 
    * called when the user clicks inside the submenu toggle region of a MenuBarItem.  If the 
    * user clicks outside of the submenu toggle region, the MenuBarItem will maintain its 
    * standard behavior.
    * @default false
    * @type Boolean
    */
	oConfig.addProperty(
	   SUBMENU_TOGGLE_REGION_CONFIG.key, 
	   {
	       value: SUBMENU_TOGGLE_REGION_CONFIG.value, 
	       validator: SUBMENU_TOGGLE_REGION_CONFIG.validator,
	       handler: this.configSubmenuToggle
       } 
    );

}
 
}); // END YAHOO.lang.extend

}());



/**
* Creates an item for a menu bar.
* 
* @param {String} p_oObject String specifying the text of the menu bar item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 
* <code>&#60;li&#62;</code> element of the menu bar item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
* specifying the <code>&#60;optgroup&#62;</code> element of the menu bar item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 
* the <code>&#60;option&#62;</code> element of the menu bar item.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the menu bar item. See configuration class documentation 
* for more details.
* @class MenuBarItem
* @constructor
* @extends YAHOO.widget.MenuItem
*/
YAHOO.widget.MenuBarItem = function(p_oObject, p_oConfig) {

    YAHOO.widget.MenuBarItem.superclass.constructor.call(this, p_oObject, p_oConfig);

};

YAHOO.lang.extend(YAHOO.widget.MenuBarItem, YAHOO.widget.MenuItem, {



/**
* @method init
* @description The MenuBarItem class's initialization method. This method is 
* automatically called by the constructor, and sets up all DOM references for 
* pre-existing markup, and creates required markup if it is not already present.
* @param {String} p_oObject String specifying the text of the menu bar item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the 
* <code>&#60;li&#62;</code> element of the menu bar item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object 
* specifying the <code>&#60;optgroup&#62;</code> element of the menu bar item.
* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying 
* the <code>&#60;option&#62;</code> element of the menu bar item.
* @param {Object} p_oConfig Optional. Object literal specifying the 
* configuration for the menu bar item. See configuration class documentation 
* for more details.
*/
init: function(p_oObject, p_oConfig) {

    if(!this.SUBMENU_TYPE) {

        this.SUBMENU_TYPE = YAHOO.widget.Menu;

    }


    /* 
        Call the init of the superclass (YAHOO.widget.MenuItem)
        Note: We don't pass the user config in here yet 
        because we only want it executed once, at the lowest 
        subclass level.
    */ 

    YAHOO.widget.MenuBarItem.superclass.init.call(this, p_oObject);  


    var oConfig = this.cfg;

    if(p_oConfig) {

        oConfig.applyConfig(p_oConfig, true);

    }

    oConfig.fireQueue();

},



// Constants


/**
* @property CSS_CLASS_NAME
* @description String representing the CSS class(es) to be applied to the 
* <code>&#60;li&#62;</code> element of the menu bar item.
* @default "yuimenubaritem"
* @final
* @type String
*/
CSS_CLASS_NAME: "yuimenubaritem",


/**
* @property CSS_LABEL_CLASS_NAME
* @description String representing the CSS class(es) to be applied to the 
* menu bar item's <code>&#60;a&#62;</code> element.
* @default "yuimenubaritemlabel"
* @final
* @type String
*/
CSS_LABEL_CLASS_NAME: "yuimenubaritemlabel",



// Public methods


/**
* @method toString
* @description Returns a string representing the menu bar item.
* @return {String}
*/
toString: function() {

    var sReturnVal = "MenuBarItem";

    if(this.cfg && this.cfg.getProperty("text")) {

        sReturnVal += (": " + this.cfg.getProperty("text"));

    }

    return sReturnVal;

}
    
}); // END YAHOO.lang.extend
YAHOO.register("menu", YAHOO.widget.Menu, {version: "2.8.1", build: "19"});
/*
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 Attribute configurations.
 * @namespace YAHOO.util
 * @class Attribute
 * @constructor
 * @param hash {Object} The intial Attribute.
 * @param {YAHOO.util.AttributeProvider} The owner of the Attribute instance.
 */

YAHOO.util.Attribute = function(hash, owner) {
    if (owner) { 
        this.owner = owner;
        this.configure(hash, true);
    }
};

YAHOO.util.Attribute.prototype = {
    /**
     * The name of the attribute.
     * @property name
     * @type String
     */
    name: undefined,
    
    /**
     * The value of the attribute.
     * @property value
     * @type String
     */
    value: null,
    
    /**
     * The owner of the attribute.
     * @property owner
     * @type YAHOO.util.AttributeProvider
     */
    owner: null,
    
    /**
     * Whether or not the attribute is read only.
     * @property readOnly
     * @type Boolean
     */
    readOnly: false,
    
    /**
     * Whether or not the attribute can only be written once.
     * @property writeOnce
     * @type Boolean
     */
    writeOnce: false,

    /**
     * The attribute's initial configuration.
     * @private
     * @property _initialConfig
     * @type Object
     */
    _initialConfig: null,
    
    /**
     * Whether or not the attribute's value has been set.
     * @private
     * @property _written
     * @type Boolean
     */
    _written: false,
    
    /**
     * A function to call when setting the attribute's value.
     * The method receives the new value as the first arg and the attribute name as the 2nd
     * @property method
     * @type Function
     */
    method: null,
    
    /**
     * The function to use when setting the attribute's value.
     * The setter receives the new value as the first arg and the attribute name as the 2nd
     * The return value of the setter replaces the value passed to set(). 
     * @property setter
     * @type Function
     */
    setter: null,
    
    /**
     * The function to use when getting the attribute's value.
     * The getter receives the new value as the first arg and the attribute name as the 2nd
     * The return value of the getter will be used as the return from get().
     * @property getter
     * @type Function
     */
    getter: null,

    /**
     * The validator to use when setting the attribute's value.
     * @property validator
     * @type Function
     * @return Boolean
     */
    validator: null,
    
    /**
     * Retrieves the current value of the attribute.
     * @method getValue
     * @return {any} The current value of the attribute.
     */
    getValue: function() {
        var val = this.value;

        if (this.getter) {
            val = this.getter.call(this.owner, this.name, val);
        }

        return val;
    },
    
    /**
     * Sets the value of the attribute and fires beforeChange and change events.
     * @method setValue
     * @param {Any} value The value to apply to the attribute.
     * @param {Boolean} silent If true the change events will not be fired.
     * @return {Boolean} Whether or not the value was set.
     */
    setValue: function(value, silent) {
        var beforeRetVal,
            owner = this.owner,
            name = this.name;
        
        var event = {
            type: name, 
            prevValue: this.getValue(),
            newValue: value
        };
        
        if (this.readOnly || ( this.writeOnce && this._written) ) {
            return false; // write not allowed
        }
        
        if (this.validator && !this.validator.call(owner, value) ) {
            return false; // invalid value
        }

        if (!silent) {
            beforeRetVal = owner.fireBeforeChangeEvent(event);
            if (beforeRetVal === false) {
                return false;
            }
        }

        if (this.setter) {
            value = this.setter.call(owner, value, this.name);
            if (value === undefined) {
            }
        }
        
        if (this.method) {
            this.method.call(owner, value, this.name);
        }
        
        this.value = value; // TODO: set before calling setter/method?
        this._written = true;
        
        event.type = name;
        
        if (!silent) {
            this.owner.fireChangeEvent(event);
        }
        
        return true;
    },
    
    /**
     * Allows for configuring the Attribute's properties.
     * @method configure
     * @param {Object} map A key-value map of Attribute properties.
     * @param {Boolean} init Whether or not this should become the initial config.
     */
    configure: function(map, init) {
        map = map || {};

        if (init) {
            this._written = false; // reset writeOnce
        }

        this._initialConfig = this._initialConfig || {};
        
        for (var key in map) {
            if ( map.hasOwnProperty(key) ) {
                this[key] = map[key];
                if (init) {
                    this._initialConfig[key] = map[key];
                }
            }
        }
    },
    
    /**
     * Resets the value to the initial config value.
     * @method resetValue
     * @return {Boolean} Whether or not the value was set.
     */
    resetValue: function() {
        return this.setValue(this._initialConfig.value);
    },
    
    /**
     * Resets the attribute config to the initial config state.
     * @method resetConfig
     */
    resetConfig: function() {
        this.configure(this._initialConfig, true);
    },
    
    /**
     * Resets the value to the current value.
     * Useful when values may have gotten out of sync with actual properties.
     * @method refresh
     * @return {Boolean} Whether or not the value was set.
     */
    refresh: function(silent) {
        this.setValue(this.value, silent);
    }
};

(function() {
    var Lang = YAHOO.util.Lang;

    /*
    Copyright (c) 2006, Yahoo! Inc. All rights reserved.
    Code licensed under the BSD License:
    http://developer.yahoo.net/yui/license.txt
    */
    
    /**
     * Provides and manages YAHOO.util.Attribute instances
     * @namespace YAHOO.util
     * @class AttributeProvider
     * @uses YAHOO.util.EventProvider
     */
    YAHOO.util.AttributeProvider = function() {};

    YAHOO.util.AttributeProvider.prototype = {
        
        /**
         * A key-value map of Attribute configurations
         * @property _configs
         * @protected (may be used by subclasses and augmentors)
         * @private
         * @type {Object}
         */
        _configs: null,
        /**
         * Returns the current value of the attribute.
         * @method get
         * @param {String} key The attribute whose value will be returned.
         * @return {Any} The current value of the attribute.
         */
        get: function(key){
            this._configs = this._configs || {};
            var config = this._configs[key];
            
            if (!config || !this._configs.hasOwnProperty(key)) {
                return null;
            }
            
            return config.getValue();
        },
        
        /**
         * Sets the value of a config.
         * @method set
         * @param {String} key The name of the attribute
         * @param {Any} value The value to apply to the attribute
         * @param {Boolean} silent Whether or not to suppress change events
         * @return {Boolean} Whether or not the value was set.
         */
        set: function(key, value, silent){
            this._configs = this._configs || {};
            var config = this._configs[key];
            
            if (!config) {
                return false;
            }
            
            return config.setValue(value, silent);
        },
    
        /**
         * Returns an array of attribute names.
         * @method getAttributeKeys
         * @return {Array} An array of attribute names.
         */
        getAttributeKeys: function(){
            this._configs = this._configs;
            var keys = [], key;

            for (key in this._configs) {
                if ( Lang.hasOwnProperty(this._configs, key) && 
                        !Lang.isUndefined(this._configs[key]) ) {
                    keys[keys.length] = key;
                }
            }
            
            return keys;
        },
        
        /**
         * Sets multiple attribute values.
         * @method setAttributes
         * @param {Object} map  A key-value map of attributes
         * @param {Boolean} silent Whether or not to suppress change events
         */
        setAttributes: function(map, silent){
            for (var key in map) {
                if ( Lang.hasOwnProperty(map, key) ) {
                    this.set(key, map[key], silent);
                }
            }
        },
    
        /**
         * Resets the specified attribute's value to its initial value.
         * @method resetValue
         * @param {String} key The name of the attribute
         * @param {Boolean} silent Whether or not to suppress change events
         * @return {Boolean} Whether or not the value was set
         */
        resetValue: function(key, silent){
            this._configs = this._configs || {};
            if (this._configs[key]) {
                this.set(key, this._configs[key]._initialConfig.value, silent);
                return true;
            }
            return false;
        },
    
        /**
         * Sets the attribute's value to its current value.
         * @method refresh
         * @param {String | Array} key The attribute(s) to refresh
         * @param {Boolean} silent Whether or not to suppress change events
         */
        refresh: function(key, silent) {
            this._configs = this._configs || {};
            var configs = this._configs;
            
            key = ( ( Lang.isString(key) ) ? [key] : key ) || 
                    this.getAttributeKeys();
            
            for (var i = 0, len = key.length; i < len; ++i) { 
                if (configs.hasOwnProperty(key[i])) {
                    this._configs[key[i]].refresh(silent);
                }
            }
        },
    
        /**
         * Adds an Attribute to the AttributeProvider instance. 
         * @method register
         * @param {String} key The attribute's name
         * @param {Object} map A key-value map containing the
         * attribute's properties.
         * @deprecated Use setAttributeConfig
         */
        register: function(key, map) {
            this.setAttributeConfig(key, map);
        },
        
        
        /**
         * Returns the attribute's properties.
         * @method getAttributeConfig
         * @param {String} key The attribute's name
         * @private
         * @return {object} A key-value map containing all of the
         * attribute's properties.
         */
        getAttributeConfig: function(key) {
            this._configs = this._configs || {};
            var config = this._configs[key] || {};
            var map = {}; // returning a copy to prevent overrides
            
            for (key in config) {
                if ( Lang.hasOwnProperty(config, key) ) {
                    map[key] = config[key];
                }
            }
    
            return map;
        },
        
        /**
         * Sets or updates an Attribute instance's properties. 
         * @method setAttributeConfig
         * @param {String} key The attribute's name.
         * @param {Object} map A key-value map of attribute properties
         * @param {Boolean} init Whether or not this should become the intial config.
         */
        setAttributeConfig: function(key, map, init) {
            this._configs = this._configs || {};
            map = map || {};
            if (!this._configs[key]) {
                map.name = key;
                this._configs[key] = this.createAttribute(map);
            } else {
                this._configs[key].configure(map, init);
            }
        },
        
        /**
         * Sets or updates an Attribute instance's properties. 
         * @method configureAttribute
         * @param {String} key The attribute's name.
         * @param {Object} map A key-value map of attribute properties
         * @param {Boolean} init Whether or not this should become the intial config.
         * @deprecated Use setAttributeConfig
         */
        configureAttribute: function(key, map, init) {
            this.setAttributeConfig(key, map, init);
        },
        
        /**
         * Resets an attribute to its intial configuration. 
         * @method resetAttributeConfig
         * @param {String} key The attribute's name.
         * @private
         */
        resetAttributeConfig: function(key){
            this._configs = this._configs || {};
            this._configs[key].resetConfig();
        },
        
        // wrapper for EventProvider.subscribe
        // to create events on the fly
        subscribe: function(type, callback) {
            this._events = this._events || {};

            if ( !(type in this._events) ) {
                this._events[type] = this.createEvent(type);
            }

            YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments);
        },

        on: function() {
            this.subscribe.apply(this, arguments);
        },

        addListener: function() {
            this.subscribe.apply(this, arguments);
        },

        /**
         * Fires the attribute's beforeChange event. 
         * @method fireBeforeChangeEvent
         * @param {String} key The attribute's name.
         * @param {Obj} e The event object to pass to handlers.
         */
        fireBeforeChangeEvent: function(e) {
            var type = 'before';
            type += e.type.charAt(0).toUpperCase() + e.type.substr(1) + 'Change';
            e.type = type;
            return this.fireEvent(e.type, e);
        },
        
        /**
         * Fires the attribute's change event. 
         * @method fireChangeEvent
         * @param {String} key The attribute's name.
         * @param {Obj} e The event object to pass to the handlers.
         */
        fireChangeEvent: function(e) {
            e.type += 'Change';
            return this.fireEvent(e.type, e);
        },

        createAttribute: function(map) {
            return new YAHOO.util.Attribute(map, this);
        }
    };
    
    YAHOO.augment(YAHOO.util.AttributeProvider, YAHOO.util.EventProvider);
})();

(function() {
// internal shorthand
var Dom = YAHOO.util.Dom,
    AttributeProvider = YAHOO.util.AttributeProvider,
	specialTypes = {
		mouseenter: true,
		mouseleave: true
	};

/**
 * Element provides an wrapper object to simplify adding
 * event listeners, using dom methods, and managing attributes. 
 * @module element
 * @namespace YAHOO.util
 * @requires yahoo, dom, event
 */

/**
 * Element provides an wrapper object to simplify adding
 * event listeners, using dom methods, and managing attributes. 
 * @class Element
 * @uses YAHOO.util.AttributeProvider
 * @constructor
 * @param el {HTMLElement | String} The html element that 
 * represents the Element.
 * @param {Object} map A key-value map of initial config names and values
 */
var Element = function(el, map) {
    this.init.apply(this, arguments);
};

Element.DOM_EVENTS = {
    'click': true,
    'dblclick': true,
    'keydown': true,
    'keypress': true,
    'keyup': true,
    'mousedown': true,
    'mousemove': true,
    'mouseout': true, 
    'mouseover': true, 
    'mouseup': true,
    'mouseenter': true, 
    'mouseleave': true,
    'focus': true,
    'blur': true,
    'submit': true,
    'change': true
};

Element.prototype = {
    /**
     * Dom events supported by the Element instance.
     * @property DOM_EVENTS
     * @type Object
     */
    DOM_EVENTS: null,

    DEFAULT_HTML_SETTER: function(value, key) {
        var el = this.get('element');
        
        if (el) {
            el[key] = value;
        }

		return value;

    },

    DEFAULT_HTML_GETTER: function(key) {
        var el = this.get('element'),
            val;

        if (el) {
            val = el[key];
        }

        return val;
    },

    /**
     * Wrapper for HTMLElement method.
     * @method appendChild
     * @param {YAHOO.util.Element || HTMLElement} child The element to append. 
     * @return {HTMLElement} The appended DOM element. 
     */
    appendChild: function(child) {
        child = child.get ? child.get('element') : child;
        return this.get('element').appendChild(child);
    },
    
    /**
     * Wrapper for HTMLElement method.
     * @method getElementsByTagName
     * @param {String} tag The tagName to collect
     * @return {HTMLCollection} A collection of DOM elements. 
     */
    getElementsByTagName: function(tag) {
        return this.get('element').getElementsByTagName(tag);
    },
    
    /**
     * Wrapper for HTMLElement method.
     * @method hasChildNodes
     * @return {Boolean} Whether or not the element has childNodes
     */
    hasChildNodes: function() {
        return this.get('element').hasChildNodes();
    },
    
    /**
     * Wrapper for HTMLElement method.
     * @method insertBefore
     * @param {HTMLElement} element The HTMLElement to insert
     * @param {HTMLElement} before The HTMLElement to insert
     * the element before.
     * @return {HTMLElement} The inserted DOM element. 
     */
    insertBefore: function(element, before) {
        element = element.get ? element.get('element') : element;
        before = (before && before.get) ? before.get('element') : before;
        
        return this.get('element').insertBefore(element, before);
    },
    
    /**
     * Wrapper for HTMLElement method.
     * @method removeChild
     * @param {HTMLElement} child The HTMLElement to remove
     * @return {HTMLElement} The removed DOM element. 
     */
    removeChild: function(child) {
        child = child.get ? child.get('element') : child;
        return this.get('element').removeChild(child);
    },
    
    /**
     * Wrapper for HTMLElement method.
     * @method replaceChild
     * @param {HTMLElement} newNode The HTMLElement to insert
     * @param {HTMLElement} oldNode The HTMLElement to replace
     * @return {HTMLElement} The replaced DOM element. 
     */
    replaceChild: function(newNode, oldNode) {
        newNode = newNode.get ? newNode.get('element') : newNode;
        oldNode = oldNode.get ? oldNode.get('element') : oldNode;
        return this.get('element').replaceChild(newNode, oldNode);
    },

    
    /**
     * Registers Element specific attributes.
     * @method initAttributes
     * @param {Object} map A key-value map of initial attribute configs
     */
    initAttributes: function(map) {
    },

    /**
     * Adds a listener for the given event.  These may be DOM or 
     * customEvent listeners.  Any event that is fired via fireEvent
     * can be listened for.  All handlers receive an event object. 
     * @method addListener
     * @param {String} type The name of the event to listen for
     * @param {Function} fn The handler to call when the event fires
     * @param {Any} obj A variable to pass to the handler
     * @param {Object} scope The object to use for the scope of the handler 
     */
    addListener: function(type, fn, obj, scope) {

        scope = scope || this;

        var Event = YAHOO.util.Event,
			el = this.get('element') || this.get('id'),
        	self = this;


		if (specialTypes[type] && !Event._createMouseDelegate) {
	        return false;	
		}


        if (!this._events[type]) { // create on the fly

            if (el && this.DOM_EVENTS[type]) {
				Event.on(el, type, function(e, matchedEl) {

					// Supplement IE with target, currentTarget relatedTarget

	                if (e.srcElement && !e.target) { 
	                    e.target = e.srcElement;
	                }

					if ((e.toElement && !e.relatedTarget) || (e.fromElement && !e.relatedTarget)) {
						e.relatedTarget = Event.getRelatedTarget(e);
					}
					
					if (!e.currentTarget) {
						e.currentTarget = el;
					}

					//	Note: matchedEl el is passed back for delegated listeners
		            self.fireEvent(type, e, matchedEl);

		        }, obj, scope);
            }
            this.createEvent(type, {scope: this});
        }
        
        return YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments); // notify via customEvent
    },


    /**
     * Alias for addListener
     * @method on
     * @param {String} type The name of the event to listen for
     * @param {Function} fn The function call when the event fires
     * @param {Any} obj A variable to pass to the handler
     * @param {Object} scope The object to use for the scope of the handler 
     */
    on: function() {
        return this.addListener.apply(this, arguments);
    },
    
    /**
     * Alias for addListener
     * @method subscribe
     * @param {String} type The name of the event to listen for
     * @param {Function} fn The function call when the event fires
     * @param {Any} obj A variable to pass to the handler
     * @param {Object} scope The object to use for the scope of the handler 
     */
    subscribe: function() {
        return this.addListener.apply(this, arguments);
    },
    
    /**
     * Remove an event listener
     * @method removeListener
     * @param {String} type The name of the event to listen for
     * @param {Function} fn The function call when the event fires
     */
    removeListener: function(type, fn) {
        return this.unsubscribe.apply(this, arguments);
    },
    
    /**
     * Wrapper for Dom method.
     * @method addClass
     * @param {String} className The className to add
     */
    addClass: function(className) {
        Dom.addClass(this.get('element'), className);
    },
    
    /**
     * Wrapper for Dom method.
     * @method getElementsByClassName
     * @param {String} className The className to collect
     * @param {String} tag (optional) The tag to use in
     * conjunction with class name
     * @return {Array} Array of HTMLElements
     */
    getElementsByClassName: function(className, tag) {
        return Dom.getElementsByClassName(className, tag,
                this.get('element') );
    },
    
    /**
     * Wrapper for Dom method.
     * @method hasClass
     * @param {String} className The className to add
     * @return {Boolean} Whether or not the element has the class name
     */
    hasClass: function(className) {
        return Dom.hasClass(this.get('element'), className); 
    },
    
    /**
     * Wrapper for Dom method.
     * @method removeClass
     * @param {String} className The className to remove
     */
    removeClass: function(className) {
        return Dom.removeClass(this.get('element'), className);
    },
    
    /**
     * Wrapper for Dom method.
     * @method replaceClass
     * @param {String} oldClassName The className to replace
     * @param {String} newClassName The className to add
     */
    replaceClass: function(oldClassName, newClassName) {
        return Dom.replaceClass(this.get('element'), 
                oldClassName, newClassName);
    },
    
    /**
     * Wrapper for Dom method.
     * @method setStyle
     * @param {String} property The style property to set
     * @param {String} value The value to apply to the style property
     */
    setStyle: function(property, value) {
        return Dom.setStyle(this.get('element'),  property, value); // TODO: always queuing?
    },
    
    /**
     * Wrapper for Dom method.
     * @method getStyle
     * @param {String} property The style property to retrieve
     * @return {String} The current value of the property
     */
    getStyle: function(property) {
        return Dom.getStyle(this.get('element'),  property);
    },
    
    /**
     * Apply any queued set calls.
     * @method fireQueue
     */
    fireQueue: function() {
        var queue = this._queue;
        for (var i = 0, len = queue.length; i < len; ++i) {
            this[queue[i][0]].apply(this, queue[i][1]);
        }
    },
    
    /**
     * Appends the HTMLElement into either the supplied parentNode.
     * @method appendTo
     * @param {HTMLElement | Element} parentNode The node to append to
     * @param {HTMLElement | Element} before An optional node to insert before
     * @return {HTMLElement} The appended DOM element. 
     */
    appendTo: function(parent, before) {
        parent = (parent.get) ?  parent.get('element') : Dom.get(parent);
        
        this.fireEvent('beforeAppendTo', {
            type: 'beforeAppendTo',
            target: parent
        });
        
        
        before = (before && before.get) ? 
                before.get('element') : Dom.get(before);
        var element = this.get('element');
        
        if (!element) {
            return false;
        }
        
        if (!parent) {
            return false;
        }
        
        if (element.parent != parent) {
            if (before) {
                parent.insertBefore(element, before);
            } else {
                parent.appendChild(element);
            }
        }
        
        
        this.fireEvent('appendTo', {
            type: 'appendTo',
            target: parent
        });

        return element;
    },
    
    get: function(key) {
        var configs = this._configs || {},
            el = configs.element; // avoid loop due to 'element'

        if (el && !configs[key] && !YAHOO.lang.isUndefined(el.value[key]) ) {
            this._setHTMLAttrConfig(key);
        }

        return AttributeProvider.prototype.get.call(this, key);
    },

    setAttributes: function(map, silent) {
        // set based on configOrder
        var done = {},
            configOrder = this._configOrder;

        // set based on configOrder
        for (var i = 0, len = configOrder.length; i < len; ++i) {
            if (map[configOrder[i]] !== undefined) {
                done[configOrder[i]] = true;
                this.set(configOrder[i], map[configOrder[i]], silent);
            }
        }

        // unconfigured (e.g. Dom attributes)
        for (var att in map) {
            if (map.hasOwnProperty(att) && !done[att]) {
                this.set(att, map[att], silent);
            }
        }
    },

    set: function(key, value, silent) {
        var el = this.get('element');
        if (!el) {
            this._queue[this._queue.length] = ['set', arguments];
            if (this._configs[key]) {
                this._configs[key].value = value; // so "get" works while queueing
            
            }
            return;
        }
        
        // set it on the element if not configured and is an HTML attribute
        if ( !this._configs[key] && !YAHOO.lang.isUndefined(el[key]) ) {
            this._setHTMLAttrConfig(key);
        }

        return AttributeProvider.prototype.set.apply(this, arguments);
    },
    
    setAttributeConfig: function(key, map, init) {
        this._configOrder.push(key);
        AttributeProvider.prototype.setAttributeConfig.apply(this, arguments);
    },

    createEvent: function(type, config) {
        this._events[type] = true;
        return AttributeProvider.prototype.createEvent.apply(this, arguments);
    },
    
    init: function(el, attr) {
        this._initElement(el, attr); 
    },

    destroy: function() {
        var el = this.get('element');
        YAHOO.util.Event.purgeElement(el, true); // purge DOM listeners recursively
        this.unsubscribeAll(); // unsubscribe all custom events

        if (el && el.parentNode) {
            el.parentNode.removeChild(el); // pull from the DOM
        }

        // revert initial configs
        this._queue = [];
        this._events = {};
        this._configs = {};
        this._configOrder = []; 
    },

    _initElement: function(el, attr) {
        this._queue = this._queue || [];
        this._events = this._events || {};
        this._configs = this._configs || {};
        this._configOrder = []; 
        attr = attr || {};
        attr.element = attr.element || el || null;

        var isReady = false;  // to determine when to init HTMLElement and content

        var DOM_EVENTS = Element.DOM_EVENTS;
        this.DOM_EVENTS = this.DOM_EVENTS || {};

        for (var event in DOM_EVENTS) {
            if (DOM_EVENTS.hasOwnProperty(event)) {
                this.DOM_EVENTS[event] = DOM_EVENTS[event];
            }
        }

        if (typeof attr.element === 'string') { // register ID for get() access
            this._setHTMLAttrConfig('id', { value: attr.element });
        }

        if (Dom.get(attr.element)) {
            isReady = true;
            this._initHTMLElement(attr);
            this._initContent(attr);
        }

        YAHOO.util.Event.onAvailable(attr.element, function() {
            if (!isReady) { // otherwise already done
                this._initHTMLElement(attr);
            }

            this.fireEvent('available', { type: 'available', target: Dom.get(attr.element) });  
        }, this, true);
        
        YAHOO.util.Event.onContentReady(attr.element, function() {
            if (!isReady) { // otherwise already done
                this._initContent(attr);
            }
            this.fireEvent('contentReady', { type: 'contentReady', target: Dom.get(attr.element) });  
        }, this, true);
    },

    _initHTMLElement: function(attr) {
        /**
         * The HTMLElement the Element instance refers to.
         * @attribute element
         * @type HTMLElement
         */
        this.setAttributeConfig('element', {
            value: Dom.get(attr.element),
            readOnly: true
         });
    },

    _initContent: function(attr) {
        this.initAttributes(attr);
        this.setAttributes(attr, true);
        this.fireQueue();

    },

    /**
     * Sets the value of the property and fires beforeChange and change events.
     * @private
     * @method _setHTMLAttrConfig
     * @param {YAHOO.util.Element} element The Element instance to
     * register the config to.
     * @param {String} key The name of the config to register
     * @param {Object} map A key-value map of the config's params
     */
    _setHTMLAttrConfig: function(key, map) {
        var el = this.get('element');
        map = map || {};
        map.name = key;

        map.setter = map.setter || this.DEFAULT_HTML_SETTER;
        map.getter = map.getter || this.DEFAULT_HTML_GETTER;

        map.value = map.value || el[key];
        this._configs[key] = new YAHOO.util.Attribute(map, this);
    }
};

/**
 * Fires when the Element's HTMLElement can be retrieved by Id.
 * <p>See: <a href="#addListener">Element.addListener</a></p>
 * <p><strong>Event fields:</strong><br>
 * <code>&lt;String&gt; type</code> available<br>
 * <code>&lt;HTMLElement&gt;
 * target</code> the HTMLElement bound to this Element instance<br>
 * <p><strong>Usage:</strong><br>
 * <code>var handler = function(e) {var target = e.target};<br>
 * myTabs.addListener('available', handler);</code></p>
 * @event available
 */
 
/**
 * Fires when the Element's HTMLElement subtree is rendered.
 * <p>See: <a href="#addListener">Element.addListener</a></p>
 * <p><strong>Event fields:</strong><br>
 * <code>&lt;String&gt; type</code> contentReady<br>
 * <code>&lt;HTMLElement&gt;
 * target</code> the HTMLElement bound to this Element instance<br>
 * <p><strong>Usage:</strong><br>
 * <code>var handler = function(e) {var target = e.target};<br>
 * myTabs.addListener('contentReady', handler);</code></p>
 * @event contentReady
 */

/**
 * Fires before the Element is appended to another Element.
 * <p>See: <a href="#addListener">Element.addListener</a></p>
 * <p><strong>Event fields:</strong><br>
 * <code>&lt;String&gt; type</code> beforeAppendTo<br>
 * <code>&lt;HTMLElement/Element&gt;
 * target</code> the HTMLElement/Element being appended to 
 * <p><strong>Usage:</strong><br>
 * <code>var handler = function(e) {var target = e.target};<br>
 * myTabs.addListener('beforeAppendTo', handler);</code></p>
 * @event beforeAppendTo
 */

/**
 * Fires after the Element is appended to another Element.
 * <p>See: <a href="#addListener">Element.addListener</a></p>
 * <p><strong>Event fields:</strong><br>
 * <code>&lt;String&gt; type</code> appendTo<br>
 * <code>&lt;HTMLElement/Element&gt;
 * target</code> the HTMLElement/Element being appended to 
 * <p><strong>Usage:</strong><br>
 * <code>var handler = function(e) {var target = e.target};<br>
 * myTabs.addListener('appendTo', handler);</code></p>
 * @event appendTo
 */

YAHOO.augment(Element, AttributeProvider);
YAHOO.util.Element = Element;
})();

YAHOO.register("element", YAHOO.util.Element, {version: "2.8.1", build: "19"});
/*
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
*/
/**
* @module button
* @description <p>The Button Control enables the creation of rich, graphical 
* buttons that function like traditional HTML form buttons.  <em>Unlike</em> 
* traditional HTML form buttons, buttons created with the Button Control can have 
* a label that is different from its value.  With the inclusion of the optional 
* <a href="module_menu.html">Menu Control</a>, the Button Control can also be
* used to create menu buttons and split buttons, controls that are not 
* available natively in HTML.  The Button Control can also be thought of as a 
* way to create more visually engaging implementations of the browser's 
* default radio-button and check-box controls.</p>
* <p>The Button Control supports the following types:</p>
* <dl>
* <dt>push</dt>
* <dd>Basic push button that can execute a user-specified command when 
* pressed.</dd>
* <dt>link</dt>
* <dd>Navigates to a specified url when pressed.</dd>
* <dt>submit</dt>
* <dd>Submits the parent form when pressed.</dd>
* <dt>reset</dt>
* <dd>Resets the parent form when pressed.</dd>
* <dt>checkbox</dt>
* <dd>Maintains a "checked" state that can be toggled on and off.</dd>
* <dt>radio</dt>
* <dd>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.</dd>
* <dt>menu</dt>
* <dd>When pressed will show/hide a menu.</dd>
* <dt>split</dt>
* <dd>Can execute a user-specified command or display a menu when pressed.</dd>
* </dl>
* @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 
    * <code>&#60;input&#62;</code>, <code>&#60;button&#62;</code>,
    * <code>&#60;a&#62;</code>, or <code>&#60;span&#62;</code> element to 
    * be used to create the button.
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-6043025">HTMLInputElement</a>|<a href="http://www.w3.org
    * /TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-34812697">
    * HTMLButtonElement</a>|<a href="
    * http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#
    * ID-33759296">HTMLElement</a>} p_oElement Object reference for the 
    * <code>&#60;input&#62;</code>, <code>&#60;button&#62;</code>, 
    * <code>&#60;a&#62;</code>, or <code>&#60;span&#62;</code> 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 <code>&#60;input&#62;</code> element of the 
    * specified type.
    * @private
    * @param {String} p_sType String specifying the type of 
    * <code>&#60;input&#62;</code> element to create.
    * @param {String} p_sName String specifying the name of 
    * <code>&#60;input&#62;</code> element to create.
    * @param {String} p_sValue String specifying the value of 
    * <code>&#60;input&#62;</code> element to create.
    * @param {String} p_bChecked Boolean specifying if the  
    * <code>&#60;input&#62;</code> element is to be checked.
    * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-6043025">HTMLInputElement</a>}
    */
    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 = "<input type=\"" + p_sType + "\" name=\"" + 
                    p_sName + "\"";
        
                if (p_bChecked) {
        
                    sInput += " checked";
                
                }
                
                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 <code>&#60;input&#62;</code> or <code>&#60;a&#62;</code>) that 
    * map to Button configuration attributes and sets them into a collection 
    * that is passed to the Button constructor.
    * @private
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-6043025">HTMLInputElement</a>|<a href="http://www.w3.org/
    * TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-
    * 48250443">HTMLAnchorElement</a>} p_oElement Object reference to the HTML 
    * element (either <code>&#60;input&#62;</code> or <code>&#60;span&#62;
    * </code>) 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 <code>&#60;button&#62;</code> for the value attribute  
                    rather than the value of the "value" attribute.
                */
        
                oAttribute = p_oElement.getAttributeNode(p_sAttribute);
        
    
                if (oAttribute && ("value" in oAttribute)) {
    
    
                    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();
    
    
            }
    
    
            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;
                    
                    }
    
                
                
                    oConfig.attributes.srcelement = oElement;
                
                    initConfig.call(this, oConfig);
                
                
                    if (!oConfig.element) {
                
                
                        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();
        
        
                    }
    
                }
    
    
    
                oConfig.attributes.srcelement = p_oElement;
        
                initConfig.call(this, oConfig);
        
        
                if (!oConfig.element) {
    
            
                    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 
        * <code>&#60;a&#62;</code> or <code>&#60;button&#62;</code> element.
        * @default null
        * @protected
        * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-48250443">HTMLAnchorElement</a>|<a href="
        * http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html
        * #ID-34812697">HTMLButtonElement</a>
        */
        _button: null,
        
        
        /** 
        * @property _menu
        * @description Object reference to the button's menu.
        * @default null
        * @protected
        * @type {<a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>|
        * <a href="YAHOO.widget.Menu.html">YAHOO.widget.Menu</a>}
        */
        _menu: null,
        
        
        /** 
        * @property _hiddenFields
        * @description Object reference to the <code>&#60;input&#62;</code>  
        * element, or array of HTML form elements used to represent the button
        *  when its parent form is submitted.
        * @default null
        * @protected
        * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-6043025">HTMLInputElement</a>|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 <option> of the Menu's source 
			//	<select> element to be selected, sync the selectedIndex with 
			//	the "selectedMenuItem" Attribute.

			if (oSrcElement && 
					oSrcElement.nodeName.toLowerCase() === "select" && 
					oSrcElement.value) {
				
				
				oItem = oMenu.getItem(oSrcElement.selectedIndex);
				
				//	Set the value of the "selectedMenuItem" attribute
				//	silently since this is the initial set--synchronizing 
				//	the value of the source <SELECT> element in the DOM with 
				//	its corresponding Menu instance.

				this.set("selectedMenuItem", oItem, true);
				
				//	Call the "_onSelectedMenuItemChange" method since the 
				//	attribute was set silently.

				this._onSelectedMenuItemChange({ newValue: oItem });
				
			}

        },

        
        
        /**
        * @method _onMenuClick
        * @description "click" 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.
        */
        _onMenuClick: function (p_sType, p_aArgs) {

            var oItem = p_aArgs[1],
                oSrcElement;
        
            if (oItem) {
        
				this.set("selectedMenuItem", oItem);

                oSrcElement = this.get("srcelement");
            
                if (oSrcElement && oSrcElement.type == "submit") {
        
                    this.submitForm();
            
                }
            
                this._hideMenu();
            
            }
        
        },


        /**
        * @method _onSelectedMenuItemChange
        * @description "selectedMenuItemChange" event handler for the Button's
		* "selectedMenuItem" attribute.
        * @param {Event} event Object representing the DOM event object  
        * passed back by the event utility (YAHOO.util.Event).
        */
		_onSelectedMenuItemChange: function (event) {
		
			var oSelected = event.prevValue,
				oItem = event.newValue,
				sPrefix = this.CLASS_NAME_PREFIX;

			if (oSelected) {
				Dom.removeClass(oSelected.element, (sPrefix + "button-selectedmenuitem"));
			}
			
			if (oItem) {
				Dom.addClass(oItem.element, (sPrefix + "button-selectedmenuitem"));
			}
			
		},        
        

        /**
        * @method _onLabelClick
        * @description "click" event handler for the Button's
		* <code>&#60;label&#62;</code> element.
        * @param {Event} event Object representing the DOM event object  
        * passed back by the event utility (YAHOO.util.Event).
        */
		_onLabelClick: function (event) {

			this.focus();

			var sType = this.get("type");

			if (sType == "radio" || sType == "checkbox") {
				this.set("checked", (!this.get("checked")));						
			}
			
		},

        
        // Public methods
        
        
        /**
        * @method createButtonElement
        * @description Creates the button's HTML elements.
        * @param {String} p_sType String indicating the type of element 
        * to create.
        * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-58190037">HTMLElement</a>}
        */
        createButtonElement: function (p_sType) {
        
            var sNodeName = this.NODE_NAME,
                oElement = document.createElement(sNodeName);
        
            oElement.innerHTML =  "<" + sNodeName + " class=\"first-child\">" + 
                (p_sType == "link" ? "<a></a>" : 
                "<button type=\"button\"></button>") + "</" + sNodeName + ">";
        
            return oElement;
        
        },

        
        /**
        * @method addStateCSSClasses
        * @description Appends state-specific CSS classes to the button's root 
        * DOM element.
        */
        addStateCSSClasses: function (p_sState) {
        
            var sType = this.get("type"),
				sPrefix = this.CLASS_NAME_PREFIX;
        
            if (Lang.isString(p_sState)) {
        
                if (p_sState != "activeoption" && p_sState != "hoveroption") {
        
                    this.addClass(sPrefix + this.CSS_CLASS_NAME + ("-" + p_sState));
        
                }
        
                this.addClass(sPrefix + sType + ("-button-" + p_sState));
            
            }
        
        },
        
        
        /**
        * @method removeStateCSSClasses
        * @description Removes state-specific CSS classes to the button's root 
        * DOM element.
        */
        removeStateCSSClasses: function (p_sState) {
        
            var sType = this.get("type"),
				sPrefix = this.CLASS_NAME_PREFIX;
        
            if (Lang.isString(p_sState)) {
        
                this.removeClass(sPrefix + this.CSS_CLASS_NAME + ("-" + p_sState));
                this.removeClass(sPrefix + sType + ("-button-" + p_sState));
            
            }
        
        },
        
        
        /**
        * @method createHiddenFields
        * @description Creates the button's hidden form field and appends it 
        * to its parent form.
        * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-6043025">HTMLInputElement</a>|Array}
        */
        createHiddenFields: function () {
        
            this.removeHiddenFields();
        
            var oForm = this.getForm(),
                oButtonField,
                sType,
                bCheckable,
                oMenu,
                oMenuItem,
                sButtonName,
                oValue,
                oMenuField,
                oReturnVal,
				sMenuFieldName,
				oMenuSrcElement,
				bMenuSrcElementIsSelect = false;
        
        
            if (oForm && !this.get("disabled")) {
        
                sType = this.get("type");
                bCheckable = (sType == "checkbox" || sType == "radio");
        
        
                if ((bCheckable && this.get("checked")) || (m_oSubmitTrigger == this)) {
                
        
                    oButtonField = createInputElement((bCheckable ? sType : "hidden"),
                                    this.get("name"), this.get("value"), this.get("checked"));
            
            
                    if (oButtonField) {
            
                        if (bCheckable) {
            
                            oButtonField.style.display = "none";
            
                        }
            
                        oForm.appendChild(oButtonField);
            
                    }
        
                }
                    
        
                oMenu = this._menu;
            
            
                if (Menu && oMenu && (oMenu instanceof Menu)) {
        
        
                    oMenuItem = this.get("selectedMenuItem");
					oMenuSrcElement = oMenu.srcElement;
					bMenuSrcElementIsSelect = (oMenuSrcElement && 
												oMenuSrcElement.nodeName.toUpperCase() == "SELECT");

                    if (oMenuItem) {

						oValue = (oMenuItem.value === null || oMenuItem.value === "") ? 
									oMenuItem.cfg.getProperty("text") : oMenuItem.value;

						sButtonName = this.get("name");


						if (bMenuSrcElementIsSelect) {
						
							sMenuFieldName = oMenuSrcElement.name;
						
						}
						else if (sButtonName) {

							sMenuFieldName = (sButtonName + "_options");
						
						}
						

						if (oValue && sMenuFieldName) {
		
							oMenuField = createInputElement("hidden", sMenuFieldName, oValue);
							oForm.appendChild(oMenuField);
		
						}
                    
                    }
                    else if (bMenuSrcElementIsSelect) {
					
						oMenuField = oForm.appendChild(oMenuSrcElement);
                    
                    }
        
                }
            
            
                if (oButtonField && oMenuField) {
        
                    this._hiddenFields = [oButtonField, oMenuField];
        
                }
                else if (!oButtonField && oMenuField) {
        
                    this._hiddenFields = oMenuField;
                
                }
                else if (oButtonField && !oMenuField) {
        
                    this._hiddenFields = oButtonField;
                
                }
        
        		oReturnVal = this._hiddenFields;
        
            }

			return oReturnVal;
        
        },
        
        
        /**
        * @method removeHiddenFields
        * @description Removes the button's hidden form field(s) from its 
        * parent form.
        */
        removeHiddenFields: function () {
        
            var oField = this._hiddenFields,
                nFields,
                i;
        
            function removeChild(p_oElement) {
        
                if (Dom.inDocument(p_oElement)) {
        
                    p_oElement.parentNode.removeChild(p_oElement);

                }
                
            }
            
        
            if (oField) {
        
                if (Lang.isArray(oField)) {
        
                    nFields = oField.length;
                    
                    if (nFields > 0) {
                    
                        i = nFields - 1;
                        
                        do {
        
                            removeChild(oField[i]);
        
                        }
                        while (i--);
                    
                    }
                
                }
                else {
        
                    removeChild(oField);
        
                }
        
                this._hiddenFields = null;
            
            }
        
        },
        
        
        /**
        * @method submitForm
        * @description Submits the form to which the button belongs.  Returns  
        * true if the form was submitted successfully, false if the submission 
        * was cancelled.
        * @protected
        * @return {Boolean}
        */
        submitForm: function () {
        
            var oForm = this.getForm(),
        
                oSrcElement = this.get("srcelement"),
        
                /*
                    Boolean indicating if the event fired successfully 
                    (was not cancelled by any handlers)
                */
        
                bSubmitForm = false,
                
                oEvent;
        
        
            if (oForm) {
        
                if (this.get("type") == "submit" || (oSrcElement && oSrcElement.type == "submit")) {
        
                    m_oSubmitTrigger = this;
                    
                }
        
        
                if (UA.ie) {
        
                    bSubmitForm = oForm.fireEvent("onsubmit");
        
                }
                else {  // Gecko, Opera, and Safari
        
                    oEvent = document.createEvent("HTMLEvents");
                    oEvent.initEvent("submit", true, true);
        
                    bSubmitForm = oForm.dispatchEvent(oEvent);
        
                }
        
        
                /*
                    In IE and Safari, dispatching a "submit" event to a form 
                    WILL cause the form's "submit" event to fire, but WILL NOT 
                    submit the form.  Therefore, we need to call the "submit" 
                    method as well.
                */
              
                if ((UA.ie || UA.webkit) && bSubmitForm) {
        
                    oForm.submit();
                
                }
            
            }
        
            return bSubmitForm;
            
        },
        
        
        /**
        * @method init
        * @description The Button class's initialization method.
        * @param {String} p_oElement String specifying the id attribute of the 
        * <code>&#60;input&#62;</code>, <code>&#60;button&#62;</code>,
        * <code>&#60;a&#62;</code>, or <code>&#60;span&#62;</code> element to 
        * be used to create the button.
        * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-6043025">HTMLInputElement</a>|<a href="http://
        * www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html
        * #ID-34812697">HTMLButtonElement</a>|<a href="http://www.w3.org/TR
        * /2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-33759296">
        * HTMLElement</a>} p_oElement Object reference for the 
        * <code>&#60;input&#62;</code>, <code>&#60;button&#62;</code>, 
        * <code>&#60;a&#62;</code>, or <code>&#60;span&#62;</code> 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.
        */
        init: function (p_oElement, p_oAttributes) {
        
            var sNodeName = p_oAttributes.type == "link" ? "a" : "button",
                oSrcElement = p_oAttributes.srcelement,
                oButton = p_oElement.getElementsByTagName(sNodeName)[0],
                oInput;


            if (!oButton) {

                oInput = p_oElement.getElementsByTagName("input")[0];


                if (oInput) {

                    oButton = document.createElement("button");
                    oButton.setAttribute("type", "button");

                    oInput.parentNode.replaceChild(oButton, oInput);
                
                }

            }

            this._button = oButton;


            YAHOO.widget.Button.superclass.init.call(this, p_oElement, p_oAttributes);


			var sId = this.get("id"),
				sButtonId = sId + "-button";


        	oButton.id = sButtonId;


			var aLabels,
				oLabel;


        	var hasLabel = function (element) {
        	
				return (element.htmlFor === sId);

        	};


			var setLabel = function () {

				oLabel.setAttribute((UA.ie ? "htmlFor" : "for"), sButtonId);
			
			};


			if (oSrcElement && this.get("type") != "link") {

				aLabels = Dom.getElementsBy(hasLabel, "label");

				if (Lang.isArray(aLabels) && aLabels.length > 0) {
				
					oLabel = aLabels[0];
				
				}

			}
        

            m_oButtons[sId] = this;

        	var sPrefix = this.CLASS_NAME_PREFIX;

            this.addClass(sPrefix + this.CSS_CLASS_NAME);
            this.addClass(sPrefix + this.get("type") + "-button");
        
            Event.on(this._button, "focus", this._onFocus, null, this);
            this.on("mouseover", this._onMouseOver);
			this.on("mousedown", this._onMouseDown);
			this.on("mouseup", this._onMouseUp);
            this.on("click", this._onClick);

			//	Need to reset the value of the "onclick" Attribute so that any
			//	handlers registered via the "onclick" Attribute are fired after 
			//	Button's default "_onClick" listener.

			var fnOnClick = this.get("onclick");

			this.set("onclick", null);
			this.set("onclick", fnOnClick);

            this.on("dblclick", this._onDblClick);


			var oParentNode;

            if (oLabel) {
            
				if (this.get("replaceLabel")) {

					this.set("label", oLabel.innerHTML);
					
					oParentNode = oLabel.parentNode;
					
					oParentNode.removeChild(oLabel);
					
				}
				else {

					this.on("appendTo", setLabel); 

					Event.on(oLabel, "click", this._onLabelClick, null, this);

					this._label = oLabel;
					
				}
            
            }
            
            this.on("appendTo", this._onAppendTo);
       
        

            var oContainer = this.get("container"),
                oElement = this.get("element"),
                bElInDoc = Dom.inDocument(oElement);


            if (oContainer) {
        
                if (oSrcElement && oSrcElement != oElement) {
                
                    oParentNode = oSrcElement.parentNode;

                    if (oParentNode) {
                    
                        oParentNode.removeChild(oSrcElement);
                    
                    }

                }
        
                if (Lang.isString(oContainer)) {
        
                    Event.onContentReady(oContainer, this.appendTo, oContainer, this);
        
                }
                else {
        
        			this.on("init", function () {
        			
        				Lang.later(0, this, this.appendTo, oContainer);
        			
        			});
        
                }
        
            }
            else if (!bElInDoc && oSrcElement && oSrcElement != oElement) {

                oParentNode = oSrcElement.parentNode;
        
                if (oParentNode) {
        
                    this.fireEvent("beforeAppendTo", {
                        type: "beforeAppendTo",
                        target: oParentNode
                    });
            
                    oParentNode.replaceChild(oElement, oSrcElement);
            
                    this.fireEvent("appendTo", {
                        type: "appendTo",
                        target: oParentNode
                    });
                
                }
        
            }
            else if (this.get("type") != "link" && bElInDoc && oSrcElement && 
                oSrcElement == oElement) {
        
                this._addListenersToForm();
        
            }
        
        

			this.fireEvent("init", {
				type: "init",
				target: this
			});        
        
        },
        
        
        /**
        * @method initAttributes
        * @description Initializes all of the configuration attributes used to  
        * create the button.
        * @param {Object} p_oAttributes Object literal specifying a set of 
        * configuration attributes used to create the button.
        */
        initAttributes: function (p_oAttributes) {
        
            var oAttributes = p_oAttributes || {};
        
            YAHOO.widget.Button.superclass.initAttributes.call(this, 
                oAttributes);
        
        
            /**
            * @attribute type
            * @description String specifying the button's type.  Possible 
            * values are: "push," "link," "submit," "reset," "checkbox," 
            * "radio," "menu," and "split."
            * @default "push"
            * @type String
			* @writeonce
            */
            this.setAttributeConfig("type", {
        
                value: (oAttributes.type || "push"),
                validator: Lang.isString,
                writeOnce: true,
                method: this._setType

            });
        
        
            /**
            * @attribute label
            * @description String specifying the button's text label 
            * or innerHTML.
            * @default null
            * @type String
            */
            this.setAttributeConfig("label", {
        
                value: oAttributes.label,
                validator: Lang.isString,
                method: this._setLabel
        
            });
        
        
            /**
            * @attribute value
            * @description Object specifying the value for the button.
            * @default null
            * @type Object
            */
            this.setAttributeConfig("value", {
        
                value: oAttributes.value
        
            });
        
        
            /**
            * @attribute name
            * @description String specifying the name for the button.
            * @default null
            * @type String
            */
            this.setAttributeConfig("name", {
        
                value: oAttributes.name,
                validator: Lang.isString
        
            });
        
        
            /**
            * @attribute tabindex
            * @description Number specifying the tabindex for the button.
            * @default null
            * @type Number
            */
            this.setAttributeConfig("tabindex", {
        
                value: oAttributes.tabindex,
                validator: Lang.isNumber,
                method: this._setTabIndex
        
            });
        
        
            /**
            * @attribute title
            * @description String specifying the title for the button.
            * @default null
            * @type String
            */
            this.configureAttribute("title", {
        
                value: oAttributes.title,
                validator: Lang.isString,
                method: this._setTitle
        
            });
        
        
            /**
            * @attribute disabled
            * @description Boolean indicating if the button should be disabled.  
            * (Disabled buttons are dimmed and will not respond to user input 
            * or fire events.  Does not apply to button's of type "link.")
            * @default false
            * @type Boolean
            */
            this.setAttributeConfig("disabled", {
        
                value: (oAttributes.disabled || false),
                validator: Lang.isBoolean,
                method: this._setDisabled
        
            });
        
        
            /**
            * @attribute href
            * @description String specifying the href for the button.  Applies
            * only to buttons of type "link."
            * @type String
            */
            this.setAttributeConfig("href", {
        
                value: oAttributes.href,
                validator: Lang.isString,
                method: this._setHref
        
            });
        
        
            /**
            * @attribute target
            * @description String specifying the target for the button.  
            * Applies only to buttons of type "link."
            * @type String
            */
            this.setAttributeConfig("target", {
        
                value: oAttributes.target,
                validator: Lang.isString,
                method: this._setTarget
        
            });
        
        
            /**
            * @attribute checked
            * @description Boolean indicating if the button is checked. 
            * Applies only to buttons of type "radio" and "checkbox."
            * @default false
            * @type Boolean
            */
            this.setAttributeConfig("checked", {
        
                value: (oAttributes.checked || false),
                validator: Lang.isBoolean,
                method: this._setChecked
        
            });
        
        
            /**
            * @attribute container
            * @description HTML element reference or string specifying the id 
            * attribute of the HTML element that the button's markup should be 
            * rendered into.
            * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
            * level-one-html.html#ID-58190037">HTMLElement</a>|String
            * @default null
			* @writeonce
            */
            this.setAttributeConfig("container", {
        
                value: oAttributes.container,
                writeOnce: true
        
            });
        
        
            /**
            * @attribute srcelement
            * @description Object reference to the HTML element (either 
            * <code>&#60;input&#62;</code> or <code>&#60;span&#62;</code>) 
            * used to create the button.
            * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
            * level-one-html.html#ID-58190037">HTMLElement</a>|String
            * @default null
			* @writeonce
            */
            this.setAttributeConfig("srcelement", {
        
                value: oAttributes.srcelement,
                writeOnce: true
        
            });
        
        
            /**
            * @attribute menu
            * @description Object specifying the menu for the button.  
            * The value can be one of the following:
            * <ul>
            * <li>Object specifying a rendered <a href="YAHOO.widget.Menu.html">
            * YAHOO.widget.Menu</a> instance.</li>
            * <li>Object specifying a rendered <a href="YAHOO.widget.Overlay.html">
            * YAHOO.widget.Overlay</a> instance.</li>
            * <li>String specifying the id attribute of the <code>&#60;div&#62;
            * </code> element used to create the menu.  By default the menu 
            * will be created as an instance of 
            * <a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>.  
            * If the <a href="YAHOO.widget.Menu.html#CSS_CLASS_NAME">
            * default CSS class name for YAHOO.widget.Menu</a> is applied to 
            * the <code>&#60;div&#62;</code> element, it will be created as an
            * instance of <a href="YAHOO.widget.Menu.html">YAHOO.widget.Menu
            * </a>.</li><li>String specifying the id attribute of the 
            * <code>&#60;select&#62;</code> element used to create the menu.
            * </li><li>Object specifying the <code>&#60;div&#62;</code> element
            * used to create the menu.</li>
            * <li>Object specifying the <code>&#60;select&#62;</code> element
            * used to create the menu.</li>
            * <li>Array of object literals, each representing a set of 
            * <a href="YAHOO.widget.MenuItem.html">YAHOO.widget.MenuItem</a> 
            * configuration attributes.</li>
            * <li>Array of strings representing the text labels for each menu 
            * item in the menu.</li>
            * </ul>
            * @type <a href="YAHOO.widget.Menu.html">YAHOO.widget.Menu</a>|<a 
            * href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>|<a 
            * href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
            * one-html.html#ID-58190037">HTMLElement</a>|String|Array
            * @default null
			* @writeonce
            */
            this.setAttributeConfig("menu", {
        
                value: null,
                method: this._setMenu,
                writeOnce: true
            
            });
        
        
            /**
            * @attribute lazyloadmenu
            * @description Boolean indicating the value to set for the 
            * <a href="YAHOO.widget.Menu.html#lazyLoad">"lazyload"</a>
            * configuration property of the button's menu.  Setting 
            * "lazyloadmenu" to <code>true </code> will defer rendering of 
            * the button's menu until the first time it is made visible.  
            * If "lazyloadmenu" is set to <code>false</code>, the button's 
            * menu will be rendered immediately if the button is in the 
            * document, or in response to the button's "appendTo" event if 
            * the button is not yet in the document.  In either case, the 
            * menu is rendered into the button's parent HTML element.  
            * <em>This attribute does not apply if a 
            * <a href="YAHOO.widget.Menu.html">YAHOO.widget.Menu</a> or 
            * <a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a> 
            * instance is passed as the value of the button's "menu" 
            * configuration attribute. <a href="YAHOO.widget.Menu.html">
            * YAHOO.widget.Menu</a> or <a href="YAHOO.widget.Overlay.html">
            * YAHOO.widget.Overlay</a> instances should be rendered before 
            * being set as the value for the "menu" configuration 
            * attribute.</em>
            * @default true
            * @type Boolean
			* @writeonce
            */
            this.setAttributeConfig("lazyloadmenu", {
        
                value: (oAttributes.lazyloadmenu === false ? false : true),
                validator: Lang.isBoolean,
                writeOnce: true
        
            });


            /**
            * @attribute menuclassname
            * @description String representing the CSS class name to be 
            * applied to the root element of the button's menu.
            * @type String
            * @default "yui-button-menu"
			* @writeonce
            */
            this.setAttributeConfig("menuclassname", {
        
                value: (oAttributes.menuclassname || (this.CLASS_NAME_PREFIX + "button-menu")),
                validator: Lang.isString,
                method: this._setMenuClassName,
                writeOnce: true
        
            });        


			/**
			* @attribute menuminscrollheight
			* @description Number defining the minimum threshold for the "menumaxheight" 
			* configuration attribute.  When set this attribute is automatically applied 
			* to all submenus.
			* @default 90
			* @type Number
			*/
            this.setAttributeConfig("menuminscrollheight", {
        
                value: (oAttributes.menuminscrollheight || 90),
                validator: Lang.isNumber
        
            });


            /**
            * @attribute menumaxheight
			* @description Number defining the maximum height (in pixels) for a menu's 
			* body element (<code>&#60;div class="bd"&#60;</code>).  Once a menu's body 
			* exceeds this height, the contents of the body are scrolled to maintain 
			* this value.  This value cannot be set lower than the value of the 
			* "minscrollheight" configuration property.
            * @type Number
            * @default 0
            */
            this.setAttributeConfig("menumaxheight", {
        
                value: (oAttributes.menumaxheight || 0),
                validator: Lang.isNumber
        
            });


            /**
            * @attribute menualignment
			* @description Array defining how the Button's Menu is aligned to the Button.  
            * The default value of ["tl", "bl"] aligns the Menu's top left corner to the Button's 
            * bottom left corner.
            * @type Array
            * @default ["tl", "bl"]
            */
            this.setAttributeConfig("menualignment", {
        
                value: (oAttributes.menualignment || ["tl", "bl"]),
                validator: Lang.isArray
        
            });
            

            /**
            * @attribute selectedMenuItem
            * @description Object representing the item in the button's menu 
            * that is currently selected.
            * @type YAHOO.widget.MenuItem
            * @default null
            */
            this.setAttributeConfig("selectedMenuItem", {
        
                value: null
        
            });
        
        
            /**
            * @attribute onclick
            * @description Object literal representing the code to be executed  
            * when the button is clicked.  Format:<br> <code> {<br> 
            * <strong>fn:</strong> Function,   &#47;&#47; The handler to call 
            * when the event fires.<br> <strong>obj:</strong> Object, 
            * &#47;&#47; An object to pass back to the handler.<br> 
            * <strong>scope:</strong> Object &#47;&#47;  The object to use 
            * for the scope of the handler.<br> } </code>
            * @type Object
            * @default null
            */
            this.setAttributeConfig("onclick", {
        
                value: oAttributes.onclick,
                method: this._setOnClick
            
            });


            /**
            * @attribute focusmenu
            * @description Boolean indicating whether or not the button's menu 
            * should be focused when it is made visible.
            * @type Boolean
            * @default true
            */
            this.setAttributeConfig("focusmenu", {
        
                value: (oAttributes.focusmenu === false ? false : true),
                validator: Lang.isBoolean
        
            });


            /**
            * @attribute replaceLabel
            * @description Boolean indicating whether or not the text of the 
			* button's <code>&#60;label&#62;</code> element should be used as
			* the source for the button's label configuration attribute and 
			* removed from the DOM.
            * @type Boolean
            * @default false
            */
            this.setAttributeConfig("replaceLabel", {
        
                value: false,
                validator: Lang.isBoolean,
                writeOnce: true
        
            });

        },
        
        
        /**
        * @method focus
        * @description Causes the button to receive the focus and fires the 
        * button's "focus" event.
        */
        focus: function () {
        
            if (!this.get("disabled")) {
        
                this._button.focus();
            
            }
        
        },
        
        
        /**
        * @method blur
        * @description Causes the button to lose focus and fires the button's
        * "blur" event.
        */
        blur: function () {
        
            if (!this.get("disabled")) {
        
                this._button.blur();
        
            }
        
        },
        
        
        /**
        * @method hasFocus
        * @description Returns a boolean indicating whether or not the button 
        * has focus.
        * @return {Boolean}
        */
        hasFocus: function () {
        
            return (m_oFocusedButton == this);
        
        },
        
        
        /**
        * @method isActive
        * @description Returns a boolean indicating whether or not the button 
        * is active.
        * @return {Boolean}
        */
        isActive: function () {
        
            return this.hasClass(this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME + "-active");
        
        },
        
        
        /**
        * @method getMenu
        * @description Returns a reference to the button's menu.
        * @return {<a href="YAHOO.widget.Overlay.html">
        * YAHOO.widget.Overlay</a>|<a 
        * href="YAHOO.widget.Menu.html">YAHOO.widget.Menu</a>}
        */
        getMenu: function () {
        
            return this._menu;
        
        },
        
        
        /**
        * @method getForm
        * @description Returns a reference to the button's parent form.
        * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-
        * 20000929/level-one-html.html#ID-40002357">HTMLFormElement</a>}
        */
        getForm: function () {
        
        	var oButton = this._button,
        		oForm;
        
            if (oButton) {
            
            	oForm = oButton.form;
            
            }
        
        	return oForm;
        
        },
        
        
        /** 
        * @method getHiddenFields
        * @description Returns an <code>&#60;input&#62;</code> element or 
        * array of form elements used to represent the button when its parent 
        * form is submitted.  
        * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-6043025">HTMLInputElement</a>|Array}
        */
        getHiddenFields: function () {
        
            return this._hiddenFields;
        
        },
        
        
        /**
        * @method destroy
        * @description Removes the button's element from its parent element and 
        * removes all event handlers.
        */
        destroy: function () {
        
        
            var oElement = this.get("element"),
                oMenu = this._menu,
				oLabel = this._label,
                oParentNode,
                aButtons;
        
            if (oMenu) {
        

                if (m_oOverlayManager && m_oOverlayManager.find(oMenu)) {

                    m_oOverlayManager.remove(oMenu);

                }
        
                oMenu.destroy();
        
            }
        
        
            Event.purgeElement(oElement);
            Event.purgeElement(this._button);
            Event.removeListener(document, "mouseup", this._onDocumentMouseUp);
            Event.removeListener(document, "keyup", this._onDocumentKeyUp);
            Event.removeListener(document, "mousedown", this._onDocumentMouseDown);


			if (oLabel) {

            	Event.removeListener(oLabel, "click", this._onLabelClick);
				
				oParentNode = oLabel.parentNode;
				oParentNode.removeChild(oLabel);
				
			}
        
        
            var oForm = this.getForm();
            
            if (oForm) {
        
                Event.removeListener(oForm, "reset", this._onFormReset);
                Event.removeListener(oForm, "submit", this._onFormSubmit);
        
            }


            this.unsubscribeAll();

			oParentNode = oElement.parentNode;

            if (oParentNode) {

                oParentNode.removeChild(oElement);
            
            }
        
        
            delete m_oButtons[this.get("id")];

			var sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME);

            aButtons = Dom.getElementsByClassName(sClass, 
                                this.NODE_NAME, oForm); 

            if (Lang.isArray(aButtons) && aButtons.length === 0) {

                Event.removeListener(oForm, "keypress", 
                        YAHOO.widget.Button.onFormKeyPress);

            }

        
        },
        
        
        fireEvent: function (p_sType , p_aArgs) {
        
			var sType = arguments[0];
		
			//  Disabled buttons should not respond to DOM events
		
			if (this.DOM_EVENTS[sType] && this.get("disabled")) {
		
				return false;
		
			}
		
			return YAHOO.widget.Button.superclass.fireEvent.apply(this, arguments);
        
        },
        
        
        /**
        * @method toString
        * @description Returns a string representing the button.
        * @return {String}
        */
        toString: function () {
        
            return ("Button " + this.get("id"));
        
        }
    
    });
    
    
    /**
    * @method YAHOO.widget.Button.onFormKeyPress
    * @description "keypress" event handler for the button's form.
    * @param {Event} p_oEvent Object representing the DOM event object passed 
    * back by the event utility (YAHOO.util.Event).
    */
    YAHOO.widget.Button.onFormKeyPress = function (p_oEvent) {
    
        var oTarget = Event.getTarget(p_oEvent),
            nCharCode = Event.getCharCode(p_oEvent),
            sNodeName = oTarget.nodeName && oTarget.nodeName.toUpperCase(),
            sType = oTarget.type,
    
            /*
                Boolean indicating if the form contains any enabled or 
                disabled YUI submit buttons
            */
    
            bFormContainsYUIButtons = false,
    
            oButton,
    
            oYUISubmitButton,   // The form's first, enabled YUI submit button
    
            /*
                 The form's first, enabled HTML submit button that precedes any 
                 YUI submit button
            */
    
            oPrecedingSubmitButton,
            
            oEvent; 
    
    
        function isSubmitButton(p_oElement) {
    
            var sId,
                oSrcElement;
    
            switch (p_oElement.nodeName.toUpperCase()) {
    
            case "INPUT":
            case "BUTTON":
            
                if (p_oElement.type == "submit" && !p_oElement.disabled) {
                    
                    if (!bFormContainsYUIButtons && !oPrecedingSubmitButton) {

                        oPrecedingSubmitButton = p_oElement;

                    }
                
                }

                break;
            

            default:
            
                sId = p_oElement.id;
    
                if (sId) {
    
                    oButton = m_oButtons[sId];
        
                    if (oButton) {

                        bFormContainsYUIButtons = true;
        
                        if (!oButton.get("disabled")) {

                            oSrcElement = oButton.get("srcelement");
    
                            if (!oYUISubmitButton && (oButton.get("type") == "submit" || 
                                (oSrcElement && oSrcElement.type == "submit"))) {

                                oYUISubmitButton = oButton;
                            
                            }
                        
                        }
                        
                    }
                
                }

                break;
    
            }
    
        }
    
    
        if (nCharCode == 13 && ((sNodeName == "INPUT" && (sType == "text" || 
            sType == "password" || sType == "checkbox" || sType == "radio" || 
            sType == "file")) || sNodeName == "SELECT")) {
    
            Dom.getElementsBy(isSubmitButton, "*", this);
    
    
            if (oPrecedingSubmitButton) {
    
                /*
                     Need to set focus to the first enabled submit button
                     to make sure that IE includes its name and value 
                     in the form's data set.
                */
    
                oPrecedingSubmitButton.focus();
            
            }
            else if (!oPrecedingSubmitButton && oYUISubmitButton) {
    
				/*
					Need to call "preventDefault" to ensure that the form doesn't end up getting
					submitted twice.
				*/
    
    			Event.preventDefault(p_oEvent);


				if (UA.ie) {
				
					oYUISubmitButton.get("element").fireEvent("onclick");
				
				}
				else {

					oEvent = document.createEvent("HTMLEvents");
					oEvent.initEvent("click", true, true);
			

					if (UA.gecko < 1.9) {
					
						oYUISubmitButton.fireEvent("click", oEvent);
					
					}
					else {

						oYUISubmitButton.get("element").dispatchEvent(oEvent);
					
					}
  
                }

            }
            
        }
    
    };
    
    
    /**
    * @method YAHOO.widget.Button.addHiddenFieldsToForm
    * @description Searches the specified form and adds hidden fields for  
    * instances of YAHOO.widget.Button that are of type "radio," "checkbox," 
    * "menu," and "split."
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
    * one-html.html#ID-40002357">HTMLFormElement</a>} p_oForm Object reference 
    * for the form to search.
    */
    YAHOO.widget.Button.addHiddenFieldsToForm = function (p_oForm) {
    
        var proto = YAHOO.widget.Button.prototype,
			aButtons = Dom.getElementsByClassName(
							(proto.CLASS_NAME_PREFIX + proto.CSS_CLASS_NAME), 
                            "*", 
                            p_oForm),
    
            nButtons = aButtons.length,
            oButton,
            sId,
            i;
    
        if (nButtons > 0) {
    
    
            for (i = 0; i < nButtons; i++) {
    
                sId = aButtons[i].id;
    
                if (sId) {
    
                    oButton = m_oButtons[sId];
        
                    if (oButton) {
           
                        oButton.createHiddenFields();
                        
                    }
                
                }
            
            }
    
        }
    
    };
    

    /**
    * @method YAHOO.widget.Button.getButton
    * @description Returns a button with the specified id.
    * @param {String} p_sId String specifying the id of the root node of the 
    * HTML element representing the button to be retrieved.
    * @return {YAHOO.widget.Button}
    */
    YAHOO.widget.Button.getButton = function (p_sId) {

		return m_oButtons[p_sId];

    };
    
    
    // Events
    
    
    /**
    * @event focus
    * @description Fires when the menu item receives focus.  Passes back a  
    * single object representing the original DOM event object passed back by 
    * the event utility (YAHOO.util.Event) when the event was fired.  See 
    * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 
    * for more information on listening for this event.
    * @type YAHOO.util.CustomEvent
    */
    
    
    /**
    * @event blur
    * @description Fires when the menu item loses the input focus.  Passes back  
    * a single object representing the original DOM event object passed back by 
    * the event utility (YAHOO.util.Event) when the event was fired.  See 
    * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for  
    * more information on listening for this event.
    * @type YAHOO.util.CustomEvent
    */
    
    
    /**
    * @event option
    * @description Fires when the user invokes the button's option.  Passes 
    * back a single object representing the original DOM event (either 
    * "mousedown" or "keydown") that caused the "option" event to fire.  See 
    * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> 
    * for more information on listening for this event.
    * @type YAHOO.util.CustomEvent
    */

})();
(function () {

    // Shorthard for utilities
    
    var Dom = YAHOO.util.Dom,
        Event = YAHOO.util.Event,
        Lang = YAHOO.lang,
        Button = YAHOO.widget.Button,  
    
        // Private collection of radio buttons
    
        m_oButtons = {};



    /**
    * The ButtonGroup class creates a set of buttons that are mutually 
    * exclusive; checking one button in the set will uncheck all others in the 
    * button group.
    * @param {String} p_oElement String specifying the id attribute of the 
    * <code>&#60;div&#62;</code> element of the button group.
    * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
    * level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 
    * specifying the <code>&#60;div&#62;</code> element of the button group.
    * @param {Object} p_oElement Object literal specifying a set of 
    * configuration attributes used to create the button group.
    * @param {Object} p_oAttributes Optional. Object literal specifying a set 
    * of configuration attributes used to create the button group.
    * @namespace YAHOO.widget
    * @class ButtonGroup
    * @constructor
    * @extends YAHOO.util.Element
    */
    YAHOO.widget.ButtonGroup = function (p_oElement, p_oAttributes) {
    
        var fnSuperClass = YAHOO.widget.ButtonGroup.superclass.constructor,
            sNodeName,
            oElement,
            sId;
    
        if (arguments.length == 1 && !Lang.isString(p_oElement) && 
            !p_oElement.nodeName) {
    
            if (!p_oElement.id) {
    
                sId = Dom.generateId();
    
                p_oElement.id = sId;
    
    
            }
    
    
    
            fnSuperClass.call(this, (this._createGroupElement()), p_oElement);
    
        }
        else if (Lang.isString(p_oElement)) {
    
            oElement = Dom.get(p_oElement);
    
            if (oElement) {
            
                if (oElement.nodeName.toUpperCase() == this.NODE_NAME) {
    
            
                    fnSuperClass.call(this, oElement, p_oAttributes);
    
                }
    
            }
        
        }
        else {
    
            sNodeName = p_oElement.nodeName.toUpperCase();
    
            if (sNodeName && sNodeName == this.NODE_NAME) {
        
                if (!p_oElement.id) {
        
                    p_oElement.id = Dom.generateId();
        
        
                }
        
        
                fnSuperClass.call(this, p_oElement, p_oAttributes);
    
            }
    
        }
    
    };
    
    
    YAHOO.extend(YAHOO.widget.ButtonGroup, YAHOO.util.Element, {
    
    
        // Protected properties
        
        
        /** 
        * @property _buttons
        * @description Array of buttons in the button group.
        * @default null
        * @protected
        * @type Array
        */
        _buttons: null,
        
        
        
        // Constants
        
        
        /**
        * @property NODE_NAME
        * @description The name of the tag to be used for the button 
        * group's element. 
        * @default "DIV"
        * @final
        * @type String
        */
        NODE_NAME: "DIV",


        /**
        * @property CLASS_NAME_PREFIX
        * @description Prefix used for all class names applied to a ButtonGroup.
        * @default "yui-"
        * @final
        * @type String
        */
        CLASS_NAME_PREFIX: "yui-",
        
        
        /**
        * @property CSS_CLASS_NAME
        * @description String representing the CSS class(es) to be applied  
        * to the button group's element.
        * @default "buttongroup"
        * @final
        * @type String
        */
        CSS_CLASS_NAME: "buttongroup",
    
    
    
        // Protected methods
        
        
        /**
        * @method _createGroupElement
        * @description Creates the button group's element.
        * @protected
        * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-22445964">HTMLDivElement</a>}
        */
        _createGroupElement: function () {
        
            var oElement = document.createElement(this.NODE_NAME);
        
            return oElement;
        
        },
        
        
        
        // Protected attribute setter methods
        
        
        /**
        * @method _setDisabled
        * @description Sets the value of the button groups's 
        * "disabled" attribute.
        * @protected
        * @param {Boolean} p_bDisabled Boolean indicating the value for
        * the button group's "disabled" attribute.
        */
        _setDisabled: function (p_bDisabled) {
        
            var nButtons = this.getCount(),
                i;
        
            if (nButtons > 0) {
        
                i = nButtons - 1;
                
                do {
        
                    this._buttons[i].set("disabled", p_bDisabled);
                
                }
                while (i--);
        
            }
        
        },
        
        
        
        // Protected event handlers
        
        
        /**
        * @method _onKeyDown
        * @description "keydown" event handler for the button group.
        * @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 oTarget = Event.getTarget(p_oEvent),
                nCharCode = Event.getCharCode(p_oEvent),
                sId = oTarget.parentNode.parentNode.id,
                oButton = m_oButtons[sId],
                nIndex = -1;
        
        
            if (nCharCode == 37 || nCharCode == 38) {
        
                nIndex = (oButton.index === 0) ? 
                            (this._buttons.length - 1) : (oButton.index - 1);
            
            }
            else if (nCharCode == 39 || nCharCode == 40) {
        
                nIndex = (oButton.index === (this._buttons.length - 1)) ? 
                            0 : (oButton.index + 1);
        
            }
        
        
            if (nIndex > -1) {
        
                this.check(nIndex);
                this.getButton(nIndex).focus();
            
            }        
        
        },
        
        
        /**
        * @method _onAppendTo
        * @description "appendTo" event handler for the button group.
        * @protected
        * @param {Event} p_oEvent Object representing the event that was fired.
        */
        _onAppendTo: function (p_oEvent) {
        
            var aButtons = this._buttons,
                nButtons = aButtons.length,
                i;
        
            for (i = 0; i < nButtons; i++) {
        
                aButtons[i].appendTo(this.get("element"));
        
            }
        
        },
        
        
        /**
        * @method _onButtonCheckedChange
        * @description "checkedChange" event handler for each button in the 
        * button group.
        * @protected
        * @param {Event} p_oEvent Object representing the event that was fired.
        * @param {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>}  
        * p_oButton Object representing the button that fired the event.
        */
        _onButtonCheckedChange: function (p_oEvent, p_oButton) {
        
            var bChecked = p_oEvent.newValue,
                oCheckedButton = this.get("checkedButton");
        
            if (bChecked && oCheckedButton != p_oButton) {
        
                if (oCheckedButton) {
        
                    oCheckedButton.set("checked", false, true);
        
                }
        
                this.set("checkedButton", p_oButton);
                this.set("value", p_oButton.get("value"));
        
            }
            else if (oCheckedButton && !oCheckedButton.set("checked")) {
        
                oCheckedButton.set("checked", true, true);
        
            }
           
        },
        
        
        
        // Public methods
        
        
        /**
        * @method init
        * @description The ButtonGroup class's initialization method.
        * @param {String} p_oElement String specifying the id attribute of the 
        * <code>&#60;div&#62;</code> element of the button group.
        * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object 
        * specifying the <code>&#60;div&#62;</code> element of the button group.
        * @param {Object} p_oElement Object literal specifying a set of  
        * configuration attributes used to create the button group.
        * @param {Object} p_oAttributes Optional. Object literal specifying a
        * set of configuration attributes used to create the button group.
        */
        init: function (p_oElement, p_oAttributes) {
        
            this._buttons = [];
        
            YAHOO.widget.ButtonGroup.superclass.init.call(this, p_oElement, 
                    p_oAttributes);
        
            this.addClass(this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME);

        
            var sClass = (YAHOO.widget.Button.prototype.CLASS_NAME_PREFIX + "radio-button"),
				aButtons = this.getElementsByClassName(sClass);

        
        
            if (aButtons.length > 0) {
        
        
                this.addButtons(aButtons);
        
            }
        
        
        
            function isRadioButton(p_oElement) {
        
                return (p_oElement.type == "radio");
        
            }
        
            aButtons = 
                Dom.getElementsBy(isRadioButton, "input", this.get("element"));
        
        
            if (aButtons.length > 0) {
        
        
                this.addButtons(aButtons);
        
            }
        
            this.on("keydown", this._onKeyDown);
            this.on("appendTo", this._onAppendTo);
        

            var oContainer = this.get("container");

            if (oContainer) {
        
                if (Lang.isString(oContainer)) {
        
                    Event.onContentReady(oContainer, function () {
        
                        this.appendTo(oContainer);            
                    
                    }, null, this);
        
                }
                else {
        
                    this.appendTo(oContainer);
        
                }
        
            }
        
        
        
        },
        
        
        /**
        * @method initAttributes
        * @description Initializes all of the configuration attributes used to  
        * create the button group.
        * @param {Object} p_oAttributes Object literal specifying a set of 
        * configuration attributes used to create the button group.
        */
        initAttributes: function (p_oAttributes) {
        
            var oAttributes = p_oAttributes || {};
        
            YAHOO.widget.ButtonGroup.superclass.initAttributes.call(
                this, oAttributes);
        
        
            /**
            * @attribute name
            * @description String specifying the name for the button group.  
            * This name will be applied to each button in the button group.
            * @default null
            * @type String
            */
            this.setAttributeConfig("name", {
        
                value: oAttributes.name,
                validator: Lang.isString
        
            });
        
        
            /**
            * @attribute disabled
            * @description Boolean indicating if the button group should be 
            * disabled.  Disabling the button group will disable each button 
            * in the button group.  Disabled buttons are dimmed and will not 
            * respond to user input or fire events.
            * @default false
            * @type Boolean
            */
            this.setAttributeConfig("disabled", {
        
                value: (oAttributes.disabled || false),
                validator: Lang.isBoolean,
                method: this._setDisabled
        
            });
        
        
            /**
            * @attribute value
            * @description Object specifying the value for the button group.
            * @default null
            * @type Object
            */
            this.setAttributeConfig("value", {
        
                value: oAttributes.value
        
            });
        
        
            /**
            * @attribute container
            * @description HTML element reference or string specifying the id 
            * attribute of the HTML element that the button group's markup
            * should be rendered into.
            * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
            * level-one-html.html#ID-58190037">HTMLElement</a>|String
            * @default null
			* @writeonce
            */
            this.setAttributeConfig("container", {
        
                value: oAttributes.container,
                writeOnce: true
        
            });
        
        
            /**
            * @attribute checkedButton
            * @description Reference for the button in the button group that 
            * is checked.
            * @type {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>}
            * @default null
            */
            this.setAttributeConfig("checkedButton", {
        
                value: null
        
            });
        
        },
        
        
        /**
        * @method addButton
        * @description Adds the button to the button group.
        * @param {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>}  
        * p_oButton Object reference for the <a href="YAHOO.widget.Button.html">
        * YAHOO.widget.Button</a> instance to be added to the button group.
        * @param {String} p_oButton String specifying the id attribute of the 
        * <code>&#60;input&#62;</code> or <code>&#60;span&#62;</code> element 
        * to be used to create the button to be added to the button group.
        * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
        * level-one-html.html#ID-6043025">HTMLInputElement</a>|<a href="
        * http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#
        * ID-33759296">HTMLElement</a>} p_oButton Object reference for the 
        * <code>&#60;input&#62;</code> or <code>&#60;span&#62;</code> element 
        * to be used to create the button to be added to the button group.
        * @param {Object} p_oButton Object literal specifying a set of 
        * <a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a> 
        * configuration attributes used to configure the button to be added to 
        * the button group.
        * @return {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>} 
        */
        addButton: function (p_oButton) {
        
            var oButton,
                oButtonElement,
                oGroupElement,
                nIndex,
                sButtonName,
                sGroupName;
        
        
            if (p_oButton instanceof Button && 
                p_oButton.get("type") == "radio") {
        
                oButton = p_oButton;
        
            }
            else if (!Lang.isString(p_oButton) && !p_oButton.nodeName) {
        
                p_oButton.type = "radio";
        
                oButton = new Button(p_oButton);

            }
            else {
        
                oButton = new Button(p_oButton, { type: "radio" });
        
            }
        
        
            if (oButton) {
        
                nIndex = this._buttons.length;
                sButtonName = oButton.get("name");
                sGroupName = this.get("name");
        
                oButton.index = nIndex;
        
                this._buttons[nIndex] = oButton;
                m_oButtons[oButton.get("id")] = oButton;
        
        
                if (sButtonName != sGroupName) {
        
                    oButton.set("name", sGroupName);
                
                }
        
        
                if (this.get("disabled")) {
        
                    oButton.set("disabled", true);
        
                }
        
        
                if (oButton.get("checked")) {
        
                    this.set("checkedButton", oButton);
        
                }

                
                oButtonElement = oButton.get("element");
                oGroupElement = this.get("element");
                
                if (oButtonElement.parentNode != oGroupElement) {
                
                    oGroupElement.appendChild(oButtonElement);
                
                }
        
                
                oButton.on("checkedChange", 
                    this._onButtonCheckedChange, oButton, this);
        
        
            }

			return oButton;
        
        },
        
        
        /**
        * @method addButtons
        * @description Adds the array of buttons to the button group.
        * @param {Array} p_aButtons Array of <a href="YAHOO.widget.Button.html">
        * YAHOO.widget.Button</a> instances to be added 
        * to the button group.
        * @param {Array} p_aButtons Array of strings specifying the id 
        * attribute of the <code>&#60;input&#62;</code> or <code>&#60;span&#62;
        * </code> elements to be used to create the buttons to be added to the 
        * button group.
        * @param {Array} p_aButtons Array of object references for the 
        * <code>&#60;input&#62;</code> or <code>&#60;span&#62;</code> elements 
        * to be used to create the buttons to be added to the button group.
        * @param {Array} p_aButtons Array of object literals, each containing
        * a set of <a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>  
        * configuration attributes used to configure each button to be added 
        * to the button group.
        * @return {Array}
        */
        addButtons: function (p_aButtons) {
    
            var nButtons,
                oButton,
                aButtons,
                i;
        
            if (Lang.isArray(p_aButtons)) {
            
                nButtons = p_aButtons.length;
                aButtons = [];
        
                if (nButtons > 0) {
        
                    for (i = 0; i < nButtons; i++) {
        
                        oButton = this.addButton(p_aButtons[i]);
                        
                        if (oButton) {
        
                            aButtons[aButtons.length] = oButton;
        
                        }
                    
                    }
                
                }
        
            }

			return aButtons;
        
        },
        
        
        /**
        * @method removeButton
        * @description Removes the button at the specified index from the 
        * button group.
        * @param {Number} p_nIndex Number specifying the index of the button 
        * to be removed from the button group.
        */
        removeButton: function (p_nIndex) {
        
            var oButton = this.getButton(p_nIndex),
                nButtons,
                i;
            
            if (oButton) {
        
        
                this._buttons.splice(p_nIndex, 1);
                delete m_oButtons[oButton.get("id")];
        
                oButton.removeListener("checkedChange", 
                    this._onButtonCheckedChange);

                oButton.destroy();
        
        
                nButtons = this._buttons.length;
                
                if (nButtons > 0) {
        
                    i = this._buttons.length - 1;
                    
                    do {
        
                        this._buttons[i].index = i;
        
                    }
                    while (i--);
                
                }
        
        
            }
        
        },
        
        
        /**
        * @method getButton
        * @description Returns the button at the specified index.
        * @param {Number} p_nIndex The index of the button to retrieve from the 
        * button group.
        * @return {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>}
        */
        getButton: function (p_nIndex) {
        
            return this._buttons[p_nIndex];
        
        },
        
        
        /**
        * @method getButtons
        * @description Returns an array of the buttons in the button group.
        * @return {Array}
        */
        getButtons: function () {
        
            return this._buttons;
        
        },
        
        
        /**
        * @method getCount
        * @description Returns the number of buttons in the button group.
        * @return {Number}
        */
        getCount: function () {
        
            return this._buttons.length;
        
        },
        
        
        /**
        * @method focus
        * @description Sets focus to the button at the specified index.
        * @param {Number} p_nIndex Number indicating the index of the button 
        * to focus. 
        */
        focus: function (p_nIndex) {
        
            var oButton,
                nButtons,
                i;
        
            if (Lang.isNumber(p_nIndex)) {
        
                oButton = this._buttons[p_nIndex];
                
                if (oButton) {
        
                    oButton.focus();
        
                }
            
            }
            else {
        
                nButtons = this.getCount();
        
                for (i = 0; i < nButtons; i++) {
        
                    oButton = this._buttons[i];
        
                    if (!oButton.get("disabled")) {
        
                        oButton.focus();
                        break;
        
                    }
        
                }
        
            }
        
        },
        
        
        /**
        * @method check
        * @description Checks the button at the specified index.
        * @param {Number} p_nIndex Number indicating the index of the button 
        * to check. 
        */
        check: function (p_nIndex) {
        
            var oButton = this.getButton(p_nIndex);
            
            if (oButton) {
        
                oButton.set("checked", true);
            
            }
        
        },
        
        
        /**
        * @method destroy
        * @description Removes the button group's element from its parent 
        * element and removes all event handlers.
        */
        destroy: function () {
        
        
            var nButtons = this._buttons.length,
                oElement = this.get("element"),
                oParentNode = oElement.parentNode,
                i;
            
            if (nButtons > 0) {
        
                i = this._buttons.length - 1;
        
                do {
        
                    this._buttons[i].destroy();
        
                }
                while (i--);
            
            }
        
        
            Event.purgeElement(oElement);
            
        
            oParentNode.removeChild(oElement);
        
        },
        
        
        /**
        * @method toString
        * @description Returns a string representing the button group.
        * @return {String}
        */
        toString: function () {
        
            return ("ButtonGroup " + this.get("id"));
        
        }
    
    });

})();
YAHOO.register("button", YAHOO.widget.Button, {version: "2.8.1", build: "19"});
/*
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 Connection Manager provides a simplified interface to the XMLHttpRequest
 * object.  It handles cross-browser instantiantion of XMLHttpRequest, negotiates the
 * interactive states and server response, returning the results to a pre-defined
 * callback you create.
 *
 * @namespace YAHOO.util
 * @module connection
 * @requires yahoo
 * @requires event
 */

/**
 * The Connection Manager singleton provides methods for creating and managing
 * asynchronous transactions.
 *
 * @class Connect
 */

YAHOO.util.Connect =
{
  /**
   * @description Array of MSFT ActiveX ids for XMLHttpRequest.
   * @property _msxml_progid
   * @private
   * @static
   * @type array
   */
	_msxml_progid:[
		'Microsoft.XMLHTTP',
		'MSXML2.XMLHTTP.3.0',
		'MSXML2.XMLHTTP'
		],

  /**
   * @description Object literal of HTTP header(s)
   * @property _http_header
   * @private
   * @static
   * @type object
   */
	_http_headers:{},

  /**
   * @description Determines if HTTP headers are set.
   * @property _has_http_headers
   * @private
   * @static
   * @type boolean
   */
	_has_http_headers:false,

 /**
  * @description Determines if a default header of
  * Content-Type of 'application/x-www-form-urlencoded'
  * will be added to any client HTTP headers sent for POST
  * transactions.
  * @property _use_default_post_header
  * @private
  * @static
  * @type boolean
  */
    _use_default_post_header:true,

 /**
  * @description The default header used for POST transactions.
  * @property _default_post_header
  * @private
  * @static
  * @type boolean
  */
    _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',

 /**
  * @description The default header used for transactions involving the
  * use of HTML forms.
  * @property _default_form_header
  * @private
  * @static
  * @type boolean
  */
    _default_form_header:'application/x-www-form-urlencoded',

 /**
  * @description Determines if a default header of
  * 'X-Requested-With: XMLHttpRequest'
  * will be added to each transaction.
  * @property _use_default_xhr_header
  * @private
  * @static
  * @type boolean
  */
    _use_default_xhr_header:true,

 /**
  * @description The default header value for the label
  * "X-Requested-With".  This is sent with each
  * transaction, by default, to identify the
  * request as being made by YUI Connection Manager.
  * @property _default_xhr_header
  * @private
  * @static
  * @type boolean
  */
    _default_xhr_header:'XMLHttpRequest',

 /**
  * @description Determines if custom, default headers
  * are set for each transaction.
  * @property _has_default_header
  * @private
  * @static
  * @type boolean
  */
    _has_default_headers:true,

 /**
  * @description Determines if custom, default headers
  * are set for each transaction.
  * @property _has_default_header
  * @private
  * @static
  * @type boolean
  */
    _default_headers:{},

 /**
  * @description Collection of polling references to the polling mechanism in handleReadyState.
  * @property _poll
  * @private
  * @static
  * @type object
  */
    _poll:{},

 /**
  * @description Queue of timeout values for each transaction callback with a defined timeout value.
  * @property _timeOut
  * @private
  * @static
  * @type object
  */
    _timeOut:{},

  /**
   * @description The polling frequency, in milliseconds, for HandleReadyState.
   * when attempting to determine a transaction's XHR readyState.
   * The default is 50 milliseconds.
   * @property _polling_interval
   * @private
   * @static
   * @type int
   */
     _polling_interval:50,

  /**
   * @description A transaction counter that increments the transaction id for each transaction.
   * @property _transaction_id
   * @private
   * @static
   * @type int
   */
     _transaction_id:0,

  /**
   * @description Custom event that fires at the start of a transaction
   * @property startEvent
   * @private
   * @static
   * @type CustomEvent
   */
	startEvent: new YAHOO.util.CustomEvent('start'),

  /**
   * @description Custom event that fires when a transaction response has completed.
   * @property completeEvent
   * @private
   * @static
   * @type CustomEvent
   */
	completeEvent: new YAHOO.util.CustomEvent('complete'),

  /**
   * @description Custom event that fires when handleTransactionResponse() determines a
   * response in the HTTP 2xx range.
   * @property successEvent
   * @private
   * @static
   * @type CustomEvent
   */
	successEvent: new YAHOO.util.CustomEvent('success'),

  /**
   * @description Custom event that fires when handleTransactionResponse() determines a
   * response in the HTTP 4xx/5xx range.
   * @property failureEvent
   * @private
   * @static
   * @type CustomEvent
   */
	failureEvent: new YAHOO.util.CustomEvent('failure'),

  /**
   * @description Custom event that fires when a transaction is successfully aborted.
   * @property abortEvent
   * @private
   * @static
   * @type CustomEvent
   */
	abortEvent: new YAHOO.util.CustomEvent('abort'),

  /**
   * @description A reference table that maps callback custom events members to its specific
   * event name.
   * @property _customEvents
   * @private
   * @static
   * @type object
   */
	_customEvents:
	{
		onStart:['startEvent', 'start'],
		onComplete:['completeEvent', 'complete'],
		onSuccess:['successEvent', 'success'],
		onFailure:['failureEvent', 'failure'],
		onUpload:['uploadEvent', 'upload'],
		onAbort:['abortEvent', 'abort']
	},

  /**
   * @description Member to add an ActiveX id to the existing xml_progid array.
   * In the event(unlikely) a new ActiveX id is introduced, it can be added
   * without internal code modifications.
   * @method setProgId
   * @public
   * @static
   * @param {string} id The ActiveX id to be added to initialize the XHR object.
   * @return void
   */
	setProgId:function(id)
	{
		this._msxml_progid.unshift(id);
	},

  /**
   * @description Member to override the default POST header.
   * @method setDefaultPostHeader
   * @public
   * @static
   * @param {boolean} b Set and use default header - true or false .
   * @return void
   */
	setDefaultPostHeader:function(b)
	{
		if(typeof b == 'string'){
			this._default_post_header = b;
		}
		else if(typeof b == 'boolean'){
			this._use_default_post_header = b;
		}
	},

  /**
   * @description Member to override the default transaction header..
   * @method setDefaultXhrHeader
   * @public
   * @static
   * @param {boolean} b Set and use default header - true or false .
   * @return void
   */
	setDefaultXhrHeader:function(b)
	{
		if(typeof b == 'string'){
			this._default_xhr_header = b;
		}
		else{
			this._use_default_xhr_header = b;
		}
	},

  /**
   * @description Member to modify the default polling interval.
   * @method setPollingInterval
   * @public
   * @static
   * @param {int} i The polling interval in milliseconds.
   * @return void
   */
	setPollingInterval:function(i)
	{
		if(typeof i == 'number' && isFinite(i)){
			this._polling_interval = i;
		}
	},

  /**
   * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
   * the XMLHttpRequest instance and the transaction id.
   * @method createXhrObject
   * @private
   * @static
   * @param {int} transactionId Property containing the transaction id for this transaction.
   * @return object
   */
	createXhrObject:function(transactionId)
	{
		var obj,http,i;
		try
		{
			// Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
			http = new XMLHttpRequest();
			//  Object literal with http and tId properties
			obj = { conn:http, tId:transactionId, xhr: true };
		}
		catch(e)
		{
			for(i=0; i<this._msxml_progid.length; ++i){
				try
				{
					// Instantiates XMLHttpRequest for IE and assign to http
					http = new ActiveXObject(this._msxml_progid[i]);
					//  Object literal with conn and tId properties
					obj = { conn:http, tId:transactionId, xhr: true };
					break;
				}
				catch(e1){}
			}
		}
		finally
		{
			return obj;
		}
	},

  /**
   * @description This method is called by asyncRequest to create a
   * valid connection object for the transaction.  It also passes a
   * transaction id and increments the transaction id counter.
   * @method getConnectionObject
   * @private
   * @static
   * @return {object}
   */
	getConnectionObject:function(t)
	{
		var o, tId = this._transaction_id;

		try
		{
			if(!t){
				o = this.createXhrObject(tId);
			}
			else{
				o = {tId:tId};
				if(t==='xdr'){
					o.conn = this._transport;
					o.xdr = true;
				}
				else if(t==='upload'){
					o.upload = true;
				}
			}

			if(o){
				this._transaction_id++;
			}
		}
		catch(e){}
		return o;
	},

  /**
   * @description Method for initiating an asynchronous request via the XHR object.
   * @method asyncRequest
   * @public
   * @static
   * @param {string} method HTTP transaction method
   * @param {string} uri Fully qualified path of resource
   * @param {callback} callback User-defined callback function or object
   * @param {string} postData POST body
   * @return {object} Returns the connection object
   */
	asyncRequest:function(method, uri, callback, postData)
	{
		var o,t,args = (callback && callback.argument)?callback.argument:null;

		if(this._isFileUpload){
			t = 'upload';
		}
		else if(callback.xdr){
			t = 'xdr';
		}

		o = this.getConnectionObject(t);
		if(!o){
			return null;
		}
		else{

			// Intialize any transaction-specific custom events, if provided.
			if(callback && callback.customevents){
				this.initCustomEvents(o, callback);
			}

			if(this._isFormSubmit){
				if(this._isFileUpload){
					this.uploadFile(o, callback, uri, postData);
					return o;
				}

				// If the specified HTTP method is GET, setForm() will return an
				// encoded string that is concatenated to the uri to
				// create a querystring.
				if(method.toUpperCase() == 'GET'){
					if(this._sFormData.length !== 0){
						// If the URI already contains a querystring, append an ampersand
						// and then concatenate _sFormData to the URI.
						uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
					}
				}
				else if(method.toUpperCase() == 'POST'){
					// If POST data exist in addition to the HTML form data,
					// it will be concatenated to the form data.
					postData = postData?this._sFormData + "&" + postData:this._sFormData;
				}
			}

			if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){
				// If callback.cache is defined and set to false, a
				// timestamp value will be added to the querystring.
				uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString();
			}

			// Each transaction will automatically include a custom header of
			// "X-Requested-With: XMLHttpRequest" to identify the request as
			// having originated from Connection Manager.
			if(this._use_default_xhr_header){
				if(!this._default_headers['X-Requested-With']){
					this.initHeader('X-Requested-With', this._default_xhr_header, true);
				}
			}

			//If the transaction method is POST and the POST header value is set to true
			//or a custom value, initalize the Content-Type header to this value.
			if((method.toUpperCase() === 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
				this.initHeader('Content-Type', this._default_post_header);
			}

			if(o.xdr){
				this.xdr(o, method, uri, callback, postData);
				return o;
			}

			o.conn.open(method, uri, true);
			//Initialize all default and custom HTTP headers,
			if(this._has_default_headers || this._has_http_headers){
				this.setHeader(o);
			}

			this.handleReadyState(o, callback);
			o.conn.send(postData || '');

			// Reset the HTML form data and state properties as
			// soon as the data are submitted.
			if(this._isFormSubmit === true){
				this.resetFormState();
			}

			// Fire global custom event -- startEvent
			this.startEvent.fire(o, args);

			if(o.startEvent){
				// Fire transaction custom event -- startEvent
				o.startEvent.fire(o, args);
			}

			return o;
		}
	},

  /**
   * @description This method creates and subscribes custom events,
   * specific to each transaction
   * @method initCustomEvents
   * @private
   * @static
   * @param {object} o The connection object
   * @param {callback} callback The user-defined callback object
   * @return {void}
   */
	initCustomEvents:function(o, callback)
	{
		var prop;
		// Enumerate through callback.customevents members and bind/subscribe
		// events that match in the _customEvents table.
		for(prop in callback.customevents){
			if(this._customEvents[prop][0]){
				// Create the custom event
				o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);

				// Subscribe the custom event
				o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
			}
		}
	},

  /**
   * @description This method serves as a timer that polls the XHR object's readyState
   * property during a transaction, instead of binding a callback to the
   * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
   * will process the response, and the timer will be cleared.
   * @method handleReadyState
   * @private
   * @static
   * @param {object} o The connection object
   * @param {callback} callback The user-defined callback object
   * @return {void}
   */

    handleReadyState:function(o, callback)

    {
		var oConn = this,
			args = (callback && callback.argument)?callback.argument:null;

		if(callback && callback.timeout){
			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
		}

		this._poll[o.tId] = window.setInterval(
			function(){
				if(o.conn && o.conn.readyState === 4){

					// Clear the polling interval for the transaction
					// and remove the reference from _poll.
					window.clearInterval(oConn._poll[o.tId]);
					delete oConn._poll[o.tId];

					if(callback && callback.timeout){
						window.clearTimeout(oConn._timeOut[o.tId]);
						delete oConn._timeOut[o.tId];
					}

					// Fire global custom event -- completeEvent
					oConn.completeEvent.fire(o, args);

					if(o.completeEvent){
						// Fire transaction custom event -- completeEvent
						o.completeEvent.fire(o, args);
					}

					oConn.handleTransactionResponse(o, callback);
				}
			}
		,this._polling_interval);
    },

  /**
   * @description This method attempts to interpret the server response and
   * determine whether the transaction was successful, or if an error or
   * exception was encountered.
   * @method handleTransactionResponse
   * @private
   * @static
   * @param {object} o The connection object
   * @param {object} callback The user-defined callback object
   * @param {boolean} isAbort Determines if the transaction was terminated via abort().
   * @return {void}
   */
    handleTransactionResponse:function(o, callback, isAbort)
    {
		var httpStatus, responseObject,
			args = (callback && callback.argument)?callback.argument:null,
			xdrS = (o.r && o.r.statusText === 'xdr:success')?true:false,
			xdrF = (o.r && o.r.statusText === 'xdr:failure')?true:false,
			xdrA = isAbort;

		try
		{
			if((o.conn.status !== undefined && o.conn.status !== 0) || xdrS){
				// XDR requests will not have HTTP status defined. The
				// statusText property will define the response status
				// set by the Flash transport.
				httpStatus = o.conn.status;
			}
			else if(xdrF && !xdrA){
				// Set XDR transaction failure to a status of 0, which
				// resolves as an HTTP failure, instead of an exception.
				httpStatus = 0;
			}
			else{
				httpStatus = 13030;
			}
		}
		catch(e){

			 // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
			 // when the XHR object's status and statusText properties are
			 // unavailable, and a query attempt throws an exception.
			httpStatus = 13030;
		}

		if((httpStatus >= 200 && httpStatus < 300) || httpStatus === 1223 || xdrS){
			responseObject = o.xdr ? o.r : this.createResponseObject(o, args);
			if(callback && callback.success){
				if(!callback.scope){
					callback.success(responseObject);
				}
				else{
					// If a scope property is defined, the callback will be fired from
					// the context of the object.
					callback.success.apply(callback.scope, [responseObject]);
				}
			}

			// Fire global custom event -- successEvent
			this.successEvent.fire(responseObject);

			if(o.successEvent){
				// Fire transaction custom event -- successEvent
				o.successEvent.fire(responseObject);
			}
		}
		else{
			switch(httpStatus){
				// The following cases are wininet.dll error codes that may be encountered.
				case 12002: // Server timeout
				case 12029: // 12029 to 12031 correspond to dropped connections.
				case 12030:
				case 12031:
				case 12152: // Connection closed by server.
				case 13030: // See above comments for variable status.
					// XDR transactions will not resolve to this case, since the
					// response object is already built in the xdr response.
					responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
					if(callback && callback.failure){
						if(!callback.scope){
							callback.failure(responseObject);
						}
						else{
							callback.failure.apply(callback.scope, [responseObject]);
						}
					}

					break;
				default:
					responseObject = (o.xdr) ? o.response : this.createResponseObject(o, args);
					if(callback && callback.failure){
						if(!callback.scope){
							callback.failure(responseObject);
						}
						else{
							callback.failure.apply(callback.scope, [responseObject]);
						}
					}
			}

			// Fire global custom event -- failureEvent
			this.failureEvent.fire(responseObject);

			if(o.failureEvent){
				// Fire transaction custom event -- failureEvent
				o.failureEvent.fire(responseObject);
			}

		}

		this.releaseObject(o);
		responseObject = null;
    },

  /**
   * @description This method evaluates the server response, creates and returns the results via
   * its properties.  Success and failure cases will differ in the response
   * object's property values.
   * @method createResponseObject
   * @private
   * @static
   * @param {object} o The connection object
   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
   * @return {object}
   */
    createResponseObject:function(o, callbackArg)
    {
		var obj = {}, headerObj = {},
			i, headerStr, header, delimitPos;

		try
		{
			headerStr = o.conn.getAllResponseHeaders();
			header = headerStr.split('\n');
			for(i=0; i<header.length; i++){
				delimitPos = header[i].indexOf(':');
				if(delimitPos != -1){
					headerObj[header[i].substring(0,delimitPos)] = YAHOO.lang.trim(header[i].substring(delimitPos+2));
				}
			}
		}
		catch(e){}

		obj.tId = o.tId;
		// Normalize IE's response to HTTP 204 when Win error 1223.
		obj.status = (o.conn.status == 1223)?204:o.conn.status;
		// Normalize IE's statusText to "No Content" instead of "Unknown".
		obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
		obj.getResponseHeader = headerObj;
		obj.getAllResponseHeaders = headerStr;
		obj.responseText = o.conn.responseText;
		obj.responseXML = o.conn.responseXML;

		if(callbackArg){
			obj.argument = callbackArg;
		}

		return obj;
    },

  /**
   * @description If a transaction cannot be completed due to dropped or closed connections,
   * there may be not be enough information to build a full response object.
   * The failure callback will be fired and this specific condition can be identified
   * by a status property value of 0.
   *
   * If an abort was successful, the status property will report a value of -1.
   *
   * @method createExceptionObject
   * @private
   * @static
   * @param {int} tId The Transaction Id
   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
   * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
   * @return {object}
   */
    createExceptionObject:function(tId, callbackArg, isAbort)
    {
		var COMM_CODE = 0,
			COMM_ERROR = 'communication failure',
			ABORT_CODE = -1,
			ABORT_ERROR = 'transaction aborted',
			obj = {};

		obj.tId = tId;
		if(isAbort){
			obj.status = ABORT_CODE;
			obj.statusText = ABORT_ERROR;
		}
		else{
			obj.status = COMM_CODE;
			obj.statusText = COMM_ERROR;
		}

		if(callbackArg){
			obj.argument = callbackArg;
		}

		return obj;
    },

  /**
   * @description Method that initializes the custom HTTP headers for the each transaction.
   * @method initHeader
   * @public
   * @static
   * @param {string} label The HTTP header label
   * @param {string} value The HTTP header value
   * @param {string} isDefault Determines if the specific header is a default header
   * automatically sent with each transaction.
   * @return {void}
   */
	initHeader:function(label, value, isDefault)
	{
		var headerObj = (isDefault)?this._default_headers:this._http_headers;

		headerObj[label] = value;
		if(isDefault){
			this._has_default_headers = true;
		}
		else{
			this._has_http_headers = true;
		}
	},


  /**
   * @description Accessor that sets the HTTP headers for each transaction.
   * @method setHeader
   * @private
   * @static
   * @param {object} o The connection object for the transaction.
   * @return {void}
   */
	setHeader:function(o)
	{
		var prop;
		if(this._has_default_headers){
			for(prop in this._default_headers){
				if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
					o.conn.setRequestHeader(prop, this._default_headers[prop]);
				}
			}
		}

		if(this._has_http_headers){
			for(prop in this._http_headers){
				if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
					o.conn.setRequestHeader(prop, this._http_headers[prop]);
				}
			}

			this._http_headers = {};
			this._has_http_headers = false;
		}
	},

  /**
   * @description Resets the default HTTP headers object
   * @method resetDefaultHeaders
   * @public
   * @static
   * @return {void}
   */
	resetDefaultHeaders:function(){
		this._default_headers = {};
		this._has_default_headers = false;
	},

  /**
   * @description Method to terminate a transaction, if it has not reached readyState 4.
   * @method abort
   * @public
   * @static
   * @param {object} o The connection object returned by asyncRequest.
   * @param {object} callback  User-defined callback object.
   * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
   * @return {boolean}
   */
	abort:function(o, callback, isTimeout)
	{
		var abortStatus,
			args = (callback && callback.argument)?callback.argument:null;
			o = o || {};

		if(o.conn){
			if(o.xhr){
				if(this.isCallInProgress(o)){
					// Issue abort request
					o.conn.abort();

					window.clearInterval(this._poll[o.tId]);
					delete this._poll[o.tId];

					if(isTimeout){
						window.clearTimeout(this._timeOut[o.tId]);
						delete this._timeOut[o.tId];
					}

					abortStatus = true;
				}
			}
			else if(o.xdr){
				o.conn.abort(o.tId);
				abortStatus = true;
			}
		}
		else if(o.upload){
			var frameId = 'yuiIO' + o.tId;
			var io = document.getElementById(frameId);

			if(io){
				// Remove all listeners on the iframe prior to
				// its destruction.
				YAHOO.util.Event.removeListener(io, "load");
				// Destroy the iframe facilitating the transaction.
				document.body.removeChild(io);

				if(isTimeout){
					window.clearTimeout(this._timeOut[o.tId]);
					delete this._timeOut[o.tId];
				}

				abortStatus = true;
			}
		}
		else{
			abortStatus = false;
		}

		if(abortStatus === true){
			// Fire global custom event -- abortEvent
			this.abortEvent.fire(o, args);

			if(o.abortEvent){
				// Fire transaction custom event -- abortEvent
				o.abortEvent.fire(o, args);
			}

			this.handleTransactionResponse(o, callback, true);
		}

		return abortStatus;
	},

  /**
   * @description Determines if the transaction is still being processed.
   * @method isCallInProgress
   * @public
   * @static
   * @param {object} o The connection object returned by asyncRequest
   * @return {boolean}
   */
	isCallInProgress:function(o)
	{
		o = o || {};
		// if the XHR object assigned to the transaction has not been dereferenced,
		// then check its readyState status.  Otherwise, return false.
		if(o.xhr && o.conn){
			return o.conn.readyState !== 4 && o.conn.readyState !== 0;
		}
		else if(o.xdr && o.conn){
			return o.conn.isCallInProgress(o.tId);
		}
		else if(o.upload === true){
			return document.getElementById('yuiIO' + o.tId)?true:false;
		}
		else{
			return false;
		}
	},

  /**
   * @description Dereference the XHR instance and the connection object after the transaction is completed.
   * @method releaseObject
   * @private
   * @static
   * @param {object} o The connection object
   * @return {void}
   */
	releaseObject:function(o)
	{
		if(o && o.conn){
			//dereference the XHR instance.
			o.conn = null;


			//dereference the connection object.
			o = null;
		}
	}
};

/**
  * @for Connect
  */
(function() {
	var YCM = YAHOO.util.Connect, _fn = {};

   /**
    * @description This method creates and instantiates the Flash transport.
    * @method _swf
    * @private
    * @static
    * @param {string} URI to connection.swf.
    * @return {void}
    */
	function _swf(uri) {
		var o = '<object id="YUIConnectionSwf" type="application/x-shockwave-flash" data="' +
		        uri + '" width="0" height="0">' +
		     	'<param name="movie" value="' + uri + '">' +
                '<param name="allowScriptAccess" value="always">' +
		    	'</object>',
		    c = document.createElement('div');

		document.body.appendChild(c);
		c.innerHTML = o;
	}

   /**
    * @description This method calls the public method on the
    * Flash transport to start the XDR transaction.  It is analogous
    * to Connection Manager's asyncRequest method.
    * @method xdr
    * @private
    * @static
    * @param {object} The transaction object.
    * @param {string} HTTP request method.
    * @param {string} URI for the transaction.
    * @param {object} The transaction's callback object.
    * @param {object} The JSON object used as HTTP POST data.
    * @return {void}
    */
	function _xdr(o, m, u, c, d) {
		_fn[parseInt(o.tId)] = { 'o':o, 'c':c };
		if (d) {
			c.method = m;
			c.data = d;
		}

		o.conn.send(u, c, o.tId);
	}

   /**
    * @description This method instantiates the Flash transport and
    * establishes a static reference to it, used for all XDR requests.
    * @method transport
    * @public
    * @static
    * @param {string} URI to connection.swf.
    * @return {void}
    */
	function _init(uri) {
		_swf(uri);
		YCM._transport = document.getElementById('YUIConnectionSwf');
	}

	function _xdrReady() {
		YCM.xdrReadyEvent.fire();
	}

   /**
    * @description This method fires the global and transaction start
    * events.
    * @method _xdrStart
    * @private
    * @static
    * @param {object} The transaction object.
    * @param {string} The transaction's callback object.
    * @return {void}
    */
	function _xdrStart(o, cb) {
		if (o) {
			// Fire global custom event -- startEvent
			YCM.startEvent.fire(o, cb.argument);

			if(o.startEvent){
				// Fire transaction custom event -- startEvent
				o.startEvent.fire(o, cb.argument);
			}
		}
	}

   /**
    * @description This method is the initial response handler
    * for XDR transactions.  The Flash transport calls this
    * function and sends the response payload.
    * @method handleXdrResponse
    * @private
    * @static
    * @param {object} The response object sent from the Flash transport.
    * @return {void}
    */
	function _handleXdrResponse(r) {
		var o = _fn[r.tId].o,
			cb = _fn[r.tId].c;

		if (r.statusText === 'xdr:start') {
			_xdrStart(o, cb);
			return;
		}

		r.responseText = decodeURI(r.responseText);
		o.r = r;
		if (cb.argument) {
			o.r.argument = cb.argument;
		}

		this.handleTransactionResponse(o, cb, r.statusText === 'xdr:abort' ? true : false);
		delete _fn[r.tId];
	}

	// Bind the functions to Connection Manager as static fields.
	YCM.xdr = _xdr;
	YCM.swf = _swf;
	YCM.transport = _init;
	YCM.xdrReadyEvent = new YAHOO.util.CustomEvent('xdrReady');
	YCM.xdrReady = _xdrReady;
	YCM.handleXdrResponse = _handleXdrResponse;
})();

/**
  * @for Connect
  */
(function(){
	var YCM = YAHOO.util.Connect,
		YE = YAHOO.util.Event;
   /**
	* @description Property modified by setForm() to determine if the data
	* should be submitted as an HTML form.
	* @property _isFormSubmit
	* @private
	* @static
	* @type boolean
	*/
	YCM._isFormSubmit = false;

   /**
	* @description Property modified by setForm() to determine if a file(s)
	* upload is expected.
	* @property _isFileUpload
	* @private
	* @static
	* @type boolean
	*/
	YCM._isFileUpload = false;

   /**
	* @description Property modified by setForm() to set a reference to the HTML
	* form node if the desired action is file upload.
	* @property _formNode
	* @private
	* @static
	* @type object
	*/
	YCM._formNode = null;

   /**
	* @description Property modified by setForm() to set the HTML form data
	* for each transaction.
	* @property _sFormData
	* @private
	* @static
	* @type string
	*/
	YCM._sFormData = null;

   /**
	* @description Tracks the name-value pair of the "clicked" submit button if multiple submit
	* buttons are present in an HTML form; and, if YAHOO.util.Event is available.
	* @property _submitElementValue
	* @private
	* @static
	* @type string
	*/
	YCM._submitElementValue = null;

   /**
    * @description Custom event that fires when handleTransactionResponse() determines a
    * response in the HTTP 4xx/5xx range.
    * @property failureEvent
    * @private
    * @static
    * @type CustomEvent
    */
	YCM.uploadEvent = new YAHOO.util.CustomEvent('upload'),

   /**
	* @description Determines whether YAHOO.util.Event is available and returns true or false.
	* If true, an event listener is bound at the document level to trap click events that
	* resolve to a target type of "Submit".  This listener will enable setForm() to determine
	* the clicked "Submit" value in a multi-Submit button, HTML form.
	* @property _hasSubmitListener
	* @private
	* @static
	*/
	YCM._hasSubmitListener = function() {
		if(YE){
			YE.addListener(
				document,
				'click',
				function(e){
					var obj = YE.getTarget(e),
						name = obj.nodeName.toLowerCase();

					if((name === 'input' || name === 'button') && (obj.type && obj.type.toLowerCase() == 'submit')){
						YCM._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
					}
				});
			return true;
		}
		return false;
	}();

  /**
   * @description This method assembles the form label and value pairs and
   * constructs an encoded string.
   * asyncRequest() will automatically initialize the transaction with a
   * a HTTP header Content-Type of application/x-www-form-urlencoded.
   * @method setForm
   * @public
   * @static
   * @param {string || object} form id or name attribute, or form object.
   * @param {boolean} optional enable file upload.
   * @param {boolean} optional enable file upload over SSL in IE only.
   * @return {string} string of the HTML form field name and value pairs..
   */
	function _setForm(formId, isUpload, secureUri)
	{
		var oForm, oElement, oName, oValue, oDisabled,
			hasSubmit = false,
			data = [], item = 0,
			i,len,j,jlen,opt;

		this.resetFormState();

		if(typeof formId == 'string'){
			// Determine if the argument is a form id or a form name.
			// Note form name usage is deprecated by supported
			// here for legacy reasons.
			oForm = (document.getElementById(formId) || document.forms[formId]);
		}
		else if(typeof formId == 'object'){
			// Treat argument as an HTML form object.
			oForm = formId;
		}
		else{
			return;
		}

		// If the isUpload argument is true, setForm will call createFrame to initialize
		// an iframe as the form target.
		//
		// The argument secureURI is also required by IE in SSL environments
		// where the secureURI string is a fully qualified HTTP path, used to set the source
		// of the iframe, to a stub resource in the same domain.
		if(isUpload){

			// Create iframe in preparation for file upload.
			this.createFrame(secureUri?secureUri:null);

			// Set form reference and file upload properties to true.
			this._isFormSubmit = true;
			this._isFileUpload = true;
			this._formNode = oForm;

			return;
		}

		// Iterate over the form elements collection to construct the
		// label-value pairs.
		for (i=0,len=oForm.elements.length; i<len; ++i){
			oElement  = oForm.elements[i];
			oDisabled = oElement.disabled;
			oName     = oElement.name;

			// Do not submit fields that are disabled or
			// do not have a name attribute value.
			if(!oDisabled && oName)
			{
				oName  = encodeURIComponent(oName)+'=';
				oValue = encodeURIComponent(oElement.value);

				switch(oElement.type)
				{
					// Safari, Opera, FF all default opt.value from .text if
					// value attribute not specified in markup
					case 'select-one':
						if (oElement.selectedIndex > -1) {
							opt = oElement.options[oElement.selectedIndex];
							data[item++] = oName + encodeURIComponent(
								(opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
						}
						break;
					case 'select-multiple':
						if (oElement.selectedIndex > -1) {
							for(j=oElement.selectedIndex, jlen=oElement.options.length; j<jlen; ++j){
								opt = oElement.options[j];
								if (opt.selected) {
									data[item++] = oName + encodeURIComponent(
										(opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
								}
							}
						}
						break;
					case 'radio':
					case 'checkbox':
						if(oElement.checked){
							data[item++] = oName + oValue;
						}
						break;
					case 'file':
						// stub case as XMLHttpRequest will only send the file path as a string.
					case undefined:
						// stub case for fieldset element which returns undefined.
					case 'reset':
						// stub case for input type reset button.
					case 'button':
						// stub case for input type button elements.
						break;
					case 'submit':
						if(hasSubmit === false){
							if(this._hasSubmitListener && this._submitElementValue){
								data[item++] = this._submitElementValue;
							}
							hasSubmit = true;
						}
						break;
					default:
						data[item++] = oName + oValue;
				}
			}
		}

		this._isFormSubmit = true;
		this._sFormData = data.join('&');


		this.initHeader('Content-Type', this._default_form_header);

		return this._sFormData;
	}

   /**
    * @description Resets HTML form properties when an HTML form or HTML form
    * with file upload transaction is sent.
    * @method resetFormState
    * @private
    * @static
    * @return {void}
    */
	function _resetFormState(){
		this._isFormSubmit = false;
		this._isFileUpload = false;
		this._formNode = null;
		this._sFormData = "";
	}


   /**
    * @description Creates an iframe to be used for form file uploads.  It is remove from the
    * document upon completion of the upload transaction.
    * @method createFrame
    * @private
    * @static
    * @param {string} optional qualified path of iframe resource for SSL in IE.
    * @return {void}
    */
	function _createFrame(secureUri){

		// IE does not allow the setting of id and name attributes as object
		// properties via createElement().  A different iframe creation
		// pattern is required for IE.
		var frameId = 'yuiIO' + this._transaction_id,
			io;
		if(YAHOO.env.ua.ie){
			io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');

			// IE will throw a security exception in an SSL environment if the
			// iframe source is undefined.
			if(typeof secureUri == 'boolean'){
				io.src = 'javascript:false';
			}
		}
		else{
			io = document.createElement('iframe');
			io.id = frameId;
			io.name = frameId;
		}

		io.style.position = 'absolute';
		io.style.top = '-1000px';
		io.style.left = '-1000px';

		document.body.appendChild(io);
	}

   /**
    * @description Parses the POST data and creates hidden form elements
    * for each key-value, and appends them to the HTML form object.
    * @method appendPostData
    * @private
    * @static
    * @param {string} postData The HTTP POST data
    * @return {array} formElements Collection of hidden fields.
    */
	function _appendPostData(postData){
		var formElements = [],
			postMessage = postData.split('&'),
			i, delimitPos;

		for(i=0; i < postMessage.length; i++){
			delimitPos = postMessage[i].indexOf('=');
			if(delimitPos != -1){
				formElements[i] = document.createElement('input');
				formElements[i].type = 'hidden';
				formElements[i].name = decodeURIComponent(postMessage[i].substring(0,delimitPos));
				formElements[i].value = decodeURIComponent(postMessage[i].substring(delimitPos+1));
				this._formNode.appendChild(formElements[i]);
			}
		}

		return formElements;
	}

   /**
    * @description Uploads HTML form, inclusive of files/attachments, using the
    * iframe created in createFrame to facilitate the transaction.
    * @method uploadFile
    * @private
    * @static
    * @param {int} id The transaction id.
    * @param {object} callback User-defined callback object.
    * @param {string} uri Fully qualified path of resource.
    * @param {string} postData POST data to be submitted in addition to HTML form.
    * @return {void}
    */
	function _uploadFile(o, callback, uri, postData){
		// Each iframe has an id prefix of "yuiIO" followed
		// by the unique transaction id.
		var frameId = 'yuiIO' + o.tId,
		    uploadEncoding = 'multipart/form-data',
		    io = document.getElementById(frameId),
		    ie8 = (document.documentMode && document.documentMode === 8) ? true : false,
		    oConn = this,
			args = (callback && callback.argument)?callback.argument:null,
            oElements,i,prop,obj, rawFormAttributes, uploadCallback;

		// Track original HTML form attribute values.
		rawFormAttributes = {
			action:this._formNode.getAttribute('action'),
			method:this._formNode.getAttribute('method'),
			target:this._formNode.getAttribute('target')
		};

		// Initialize the HTML form properties in case they are
		// not defined in the HTML form.
		this._formNode.setAttribute('action', uri);
		this._formNode.setAttribute('method', 'POST');
		this._formNode.setAttribute('target', frameId);

		if(YAHOO.env.ua.ie && !ie8){
			// IE does not respect property enctype for HTML forms.
			// Instead it uses the property - "encoding".
			this._formNode.setAttribute('encoding', uploadEncoding);
		}
		else{
			this._formNode.setAttribute('enctype', uploadEncoding);
		}

		if(postData){
			oElements = this.appendPostData(postData);
		}

		// Start file upload.
		this._formNode.submit();

		// Fire global custom event -- startEvent
		this.startEvent.fire(o, args);

		if(o.startEvent){
			// Fire transaction custom event -- startEvent
			o.startEvent.fire(o, args);
		}

		// Start polling if a callback is present and the timeout
		// property has been defined.
		if(callback && callback.timeout){
			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
		}

		// Remove HTML elements created by appendPostData
		if(oElements && oElements.length > 0){
			for(i=0; i < oElements.length; i++){
				this._formNode.removeChild(oElements[i]);
			}
		}

		// Restore HTML form attributes to their original
		// values prior to file upload.
		for(prop in rawFormAttributes){
			if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
				if(rawFormAttributes[prop]){
					this._formNode.setAttribute(prop, rawFormAttributes[prop]);
				}
				else{
					this._formNode.removeAttribute(prop);
				}
			}
		}

		// Reset HTML form state properties.
		this.resetFormState();

		// Create the upload callback handler that fires when the iframe
		// receives the load event.  Subsequently, the event handler is detached
		// and the iframe removed from the document.
		uploadCallback = function() {
			if(callback && callback.timeout){
				window.clearTimeout(oConn._timeOut[o.tId]);
				delete oConn._timeOut[o.tId];
			}

			// Fire global custom event -- completeEvent
			oConn.completeEvent.fire(o, args);

			if(o.completeEvent){
				// Fire transaction custom event -- completeEvent
				o.completeEvent.fire(o, args);
			}

			obj = {
			    tId : o.tId,
			    argument : callback.argument
            };

			try
			{
				// responseText and responseXML will be populated with the same data from the iframe.
				// Since the HTTP headers cannot be read from the iframe
				obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
				obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
			}
			catch(e){}

			if(callback && callback.upload){
				if(!callback.scope){
					callback.upload(obj);
				}
				else{
					callback.upload.apply(callback.scope, [obj]);
				}
			}

			// Fire global custom event -- uploadEvent
			oConn.uploadEvent.fire(obj);

			if(o.uploadEvent){
				// Fire transaction custom event -- uploadEvent
				o.uploadEvent.fire(obj);
			}

			YE.removeListener(io, "load", uploadCallback);

			setTimeout(
				function(){
					document.body.removeChild(io);
					oConn.releaseObject(o);
				}, 100);
		};

		// Bind the onload handler to the iframe to detect the file upload response.
		YE.addListener(io, "load", uploadCallback);
	}

	YCM.setForm = _setForm;
	YCM.resetFormState = _resetFormState;
	YCM.createFrame = _createFrame;
	YCM.appendPostData = _appendPostData;
	YCM.uploadFile = _uploadFile;
})();

YAHOO.register("connection", YAHOO.util.Connect, {version: "2.8.1", build: "19"});
SONET.featureProviders["plugin/mmaNavigationPlugin"] = {};
SONET.featureProviders["plugin/mmaNavigationPlugin"].translations = [];
SONET.featureProviders["plugin/mmaNavigationPlugin"].assetDirPattern = "%assettype%\/mmaNavigationPlugin";
SONET.featureProviders["plugin/mmaNavigationPlugin"].widgetClosures = {};
SONET.featureProviders["plugin/mmaNavigationPlugin"].decoratorClosures = {};
SONET.featureProviders["plugin/mmaNavigationPlugin"].widgetClosures["mmaHeader"] = function () {
    var _ = SONET.translatorForFeatureProvider("plugin/mmaNavigationPlugin");
    var widget = new ENGINE.Widget;
    SONET.uwa.use(function(UWA) {
var Dom = YAHOO.util.Dom,
    Event = YAHOO.util.Event,
    Lang = YAHOO.lang,
    MenuBar = YAHOO.widget.MenuBar,
    MenuBarItem = YAHOO.widget.MenuBarItem,
    Anim = YAHOO.util.Anim,
    Button = YAHOO.widget.Button,
    ua = YAHOO.env.ua,
	// search related
    _emptySearchString = true,
    _ENTER_KEY = 13,
    oMenu = null;
	
	

widget.onLoad = function() 
{
	
	elOnlineDropDownButton = UWA.extendElement(widget.body.getElementsByClassName('onlineDropButton')[0]);
	elOnlineDropDownDiv = UWA.extendElement(widget.body.getElementsByClassName('onlineModeDropdown')[0]);
	
	// Countdown
	initCountDown();
	
	// Search
	initSearch();
	
	// Online Dropdown
	OnlineDropdown.init();
	
	// activate Pulldown
	Pulldown.init();
}


function onPreorderButtonOver(){
	elPreorderButton.removeClassName('orderButton');
	elPreorderButton.addClassName('orderButtonOver');
}
function onPreorderButtonOut(){
	elPreorderButton.addClassName('orderButton');
	elPreorderButton.removeClassName('orderButtonOver');
}


/////////////////////////////////////////////////////////
// COUNTDOWN
/////////////////////////////////////////////////////////

function initCountDown(){
	
	// vars
	var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
	var strEventTitle = widget.getValue('eventtitle');
	var strEventLink = widget.getValue('eventlink');
	var strEventDate = widget.getValue('startdate');
	var strEventTime = widget.getValue('starttime');
	//var strDateET = widget.getValue('dateET');
	var oEventParam = {};
	
	// start Countdown
	if ((typeof(strEventTitle) != 'undefined' ) && (strEventTitle != null) && (strEventTitle != 'null')) {
		
		//alert(strDateET);
		
	   // check for timezone difference, disregarding daylight savings	
	   var rightNow = new Date();
	   var date1 = new Date(rightNow.getFullYear(), 0, 1, 0, 0, 0, 0);
	   var date2 = new Date(rightNow.getFullYear(), 6, 1, 0, 0, 0, 0);
	   var temp = date1.toGMTString();
	   var date3 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
	   var temp = date2.toGMTString();
	   var date4 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
	   var hoursDiffStdTime = (date1 - date3) / (1000 * 60 * 60);
	   var hoursDiffDaylightTime = (date2 - date4) / (1000 * 60 * 60);
	   //if (hoursDiffDaylightTime == hoursDiffStdTime) {
	   //   alert("Time zone is GMT " + hoursDiffStdTime + ".\nDaylight Saving Time is NOT observed here.");
	   //} else {
	   //   alert("Time zone is GMT " + hoursDiffStdTime + ".\nDaylight Saving Time is observed here.");
	   //}
		
		// create individual dates for different timezones (strEventDate and strEventTime are GMT date)
		var dateGMT = new Date(strEventDate + " " + strEventTime);
		var milliseconds = dateGMT.getTime();
		
		var isDayLightSaving = EASPORTSMMA.isDaylightSaving(dateGMT);
		
		var dateET = new Date(milliseconds);
		if(isDayLightSaving){
			dateET.setHours(dateET.getHours() - 4);
		}else{
			dateET.setHours(dateET.getHours() - 5);
		}
		
		var datePT = new Date(milliseconds);
		if(isDayLightSaving){
			datePT.setHours(datePT.getHours() - 7);
		}else{
			datePT.setHours(datePT.getHours() - 8);
		}	
		
		var dateLocal = new Date(milliseconds);
		dateLocal.setHours(dateLocal.getHours() - (dateLocal.getTimezoneOffset()/60) ); 	
		//dateLocal.setHours(dateLocal.getHours() - (dateLocal.getTimezoneOffset()/60) ); 	
		//dateLocal.setHours(dateLocal.getHours() + hoursDiffDaylightTime ); 	
		
		
		// propper ET formatting
		var eHourET = dateET.getHours();
		var eHourETText;
		if(eHourET < 12){
			eHourETText = "am";
		}else if (eHourET == 12){
			eHourETText = "pm";
		}else if (eHourET > 12){
			eHourET -= 12;
			eHourETText = "pm";
		}
		
		// propper PT formatting
		var eHourPT = datePT.getHours();
		var eHourPTText;
		if(eHourPT < 12){
			eHourPTText = "am";
		}else if (eHourPT == 12){
			eHourPTText = "pm";
		}else if (eHourPT > 12){
			eHourPT -= 12;
			eHourPTText = "pm";
		}
		
		// vars for flash countdown
		var eYear = dateLocal.getFullYear();
		var eMonth = dateLocal.getMonth() + 1;
		var eDay = dateLocal.getDate();
		var eHour = dateLocal.getHours();
		var eMinute = dateLocal.getMinutes();
		
		var eHourETFix = (eHourET <= 9)? "0" + eHourET : eHourET;
		var eHourPTFix = (eHourPT <= 9)? "0" + eHourPT : eHourPT;
		var eMinuteFix = (eMinute <= 9)? "0" + eMinute : eMinute;
		
		var currLink = window.location.href;
		var isCurrentlyWatching = "";
		if( currLink.indexOf(strEventLink) > -1){
			isCurrentlyWatching = "true";
		}else{
			isCurrentlyWatching = "false";
		}
		
		// flash parameters
		oEventParam = {
			eventtitle:strEventTitle,
			year:eYear,
			month:eMonth,
			day:eDay,
			hour:eHour,
			minute:eMinute,
			datetext: months[dateET.getMonth()] + " " + dateET.getDate() + ", " + dateET.getFullYear() + " " + eHourETFix + ":" + eMinuteFix + eHourETText + " ET" + " / " + eHourPTFix + ":" + eMinuteFix + eHourPTText + " PT",
			eventlink: strEventLink,
			isWatching: isCurrentlyWatching
		}
		
		
		var params = {
				   allowScriptAccess: "always",
				   allowFullScreen: "true",
				   allowNetworking:"all"
				 };
		
		// create flash
		var elFlashContainer = widget.body.getElementsByClassName('eventcountdown')[0];
		widget.setFlashContent('countdown.swf', oEventParam, params, 43, 460, elFlashContainer);
		
	}
	
	
}



/******************** SEARCH ********************/



function initSearch(){
	
	  var elSearchNav = UWA.extendElement(widget.body.getElementsByClassName('navSearch')[0]),
	  elSearchNavForm = UWA.extendElement(widget.body.getElementsByClassName('navSearchForm')[0]),
	  elSearchNavText = UWA.extendElement(widget.body.getElementsByClassName('navSearchText')[0]),
	  elSearchNavBtn = UWA.extendElement(widget.body.getElementsByClassName('navSearchButton')[0]);
	  
	  //Form submit fn
	  var formCheck = function() {
		  if ((elSearchNavText.value == "Search now ...") || (Lang.trim(elSearchNavText.value) == '')){
			  _emptySearchString = true;
		  }else{
			  _emptySearchString = false;
			  elSearchNavForm.submit();
		  }
	  }
	
	  // Input Keypress event
	  elSearchNavText.onkeypress = function(event){
		  
	    _emptySearchString = false;
	    event = event? event: window.event;
	    var key = event.keyCode || event.which;
	
	    if(key == _ENTER_KEY){
	      formCheck();
	      return false;
	    }
	  }
	
	  // Input OnClick fn
	  elSearchNavText.onclick = function(){
		  
	    if (_emptySearchString){
	      _emptySearchString = false;
	      elSearchNavText.value = '';
	    }
	    
	    elSearchNav.removeClassName('navSearch');
	    elSearchNav.addClassName('navSearchActive');
	  }
	
	  // Input OnLostFocus fn
	  elSearchNavText.onblur = function(){
		  
	    if (Lang.trim(elSearchNavText.value) == ''){
	      elSearchNavText.value = _('Search now ...');
	      _emptySearchString = true;
	    }
	    
		elSearchNav.removeClassName('navSearchActive');
	    elSearchNav.addClassName('navSearch');
	    
	  }
	
	  // button onclick fn
	  elSearchNavBtn.onclick = function(){
		  formCheck();
		  return false;
	  }
}

var Pulldown ={
	
	elPulldownMinimized:null,
	elPulldown:null,
	elPulldownBtn:null,
	elPulldownCloseBtn:null,
	elLoginEmail:null,
	elLoginPassword:null,
	elLoginMessage:null,
	elLoginBtn:null,
	elLogoutBtn:null,
	elPersonas:null,
	elPersonnaSelector:null,
	elPersonnaSelectorMenuContainer:null,
	oPersonnaSelectorMenu:null,
	
	init:function(){
    
    Pulldown.elPulldownMinimized =  UWA.extendElement(widget.body.getElementsByClassName('pulldown-min')[0]);
    Pulldown.elPulldown = UWA.extendElement(widget.body.getElementsByClassName('pulldown')[0]);
    Pulldown.elPulldownBtn = UWA.extendElement(Pulldown.elPulldownMinimized.getElementsByClassName('pulldownBtn')[0]);
    Pulldown.elPulldownCloseBtn = UWA.extendElement(Pulldown.elPulldown.getElementsByClassName('close-icon-white')[0]);
    Pulldown.elLoginEmail = Pulldown.elPulldown.getElementsByClassName('login-email')[0];
    Pulldown.elLoginPassword = Pulldown.elPulldown.getElementsByClassName('login-password')[0];
    Pulldown.elLoginMessage = UWA.extendElement(Pulldown.elPulldown.getElementsByClassName('loginMessage')[0]);
    Pulldown.elPersonnaSelector = UWA.extendElement(widget.body.getElementsByClassName('persona')[0]);
    Pulldown.elPersonnaSelectorMenuContainer = UWA.extendElement(widget.body.getElementsByClassName('personaSelectorMenu')[0]);
    
    
    if(typeof(Pulldown.elPersonnaSelectorMenuContainer) != 'undefined'){
	    Pulldown.oPersonnaSelectorMenu = new YAHOO.widget.Menu(Pulldown.elPersonnaSelectorMenuContainer, {
			fixedcenter: false,
			shadow: false
		});
	    Pulldown.positionPersonaSelector();
	    Pulldown.oPersonnaSelectorMenu.render();
    }
    
    // pulldown toggle
    
    if (!widget.getValue('showMiniNav')) 
    {   
      Event.on(Pulldown.elPulldownMinimized, 'click', Pulldown.togglePulldown);
      Event.on(Pulldown.elPulldownCloseBtn, 'click', Pulldown.togglePulldown);
    }
    
    // Login and Logout links
    if (widget.getValue('isAuthenticated'))
    {
      Pulldown.elLogoutBtn = UWA.extendElement(Pulldown.elPulldownMinimized.getElementsByClassName('logout-btn')[0]);
      Event.on(Pulldown.elLogoutBtn, 'click', Pulldown.onLogoutClick);
      
      elPersonas = Pulldown.elPulldownMinimized.getElementsByClassName('personaSelectorItem');
      for (var i=0; i<elPersonas.length; i++)
      {
    	  Event.on(elPersonas[i], 'click', Pulldown.onPersonaClick, elPersonas[i]);  
      }
    } 
    else 
    {
    	// disabled ajax login again
      //Pulldown.elLoginBtn = UWA.extendElement(Pulldown.elPulldown.getElementsByClassName('mmaGoldButton')[0]);
      //Event.on(Pulldown.elLoginBtn, 'click', Pulldown.onLoginClick);
    }
    
    // listen for messages to open the login box
    SONET.MessageCenter.subscribe('requireLogin', function(oParameter, sEventName) {
      //var sMessage = oParameter.message || '';
      //Pulldown.showSignIn(sMessage);
      Pulldown.expandPulldown();
      window.scroll(0,0);
    });    
    
    var elRegister = Pulldown.elPulldownMinimized.getElementsByClassName('register-btn')[0];
    Event.on(elRegister, 'click', function(ev) {
      //SONET.omniture.tagLink(elRegister, oOmnitureTags.links.register, oOmnitureTags.widget);
    });    
    
    
    // PERSONA SELECTOR
    if(typeof(Pulldown.elPersonnaSelectorMenuContainer) != 'undefined'){
    	Event.on(Pulldown.elPersonnaSelector, 'mouseover', Pulldown.onPersonaSelectorOver);
	    Event.on(Pulldown.elPersonnaSelector, 'mouseout', Pulldown.onPersonaSelectorOut);
	    Event.on(Pulldown.elPersonnaSelector, 'click', Pulldown.onPersonaSelectorClick);
    }
  },
  
  
  ///////////////////////////////////////////////////////////////
  // PERSONA SELECTOR
  ///////////////////////////////////////////////////////////////
  
  
  onPersonaSelectorOver:function(){
	  //Pulldown.elPersonnaSelectorDropdown.setAttribute('style', 'display:block;');
  },
  onPersonaSelectorOut:function(){
	  //Pulldown.elPersonnaSelectorDropdown.setAttribute('style', 'display:block;');
  },
  onPersonaSelectorClick:function(){
	  //Pulldown.elPersonnaSelectorDropdown.setAttribute('style', 'display:block;');
	  Pulldown.oPersonnaSelectorMenu.show();
	  Pulldown.positionPersonaSelector();
  },
  
  positionPersonaSelector:function(){
	  var newX = YAHOO.util.Dom.getX(Pulldown.elPersonnaSelector)+1;
	  var newY = YAHOO.util.Dom.getY(Pulldown.elPersonnaSelector);
	  YAHOO.util.Dom.setXY(Pulldown.elPersonnaSelectorMenuContainer, [newX, newY]);
  },
  
  
  
  
  
  ///////////////////////////////////////////////////////////////
  // PULLDOWN
  ///////////////////////////////////////////////////////////////
  
  
  // Toggle Rolldown and Rollup of Pulldown menu
  togglePulldown:function(ev)
  {
    var elTarget = Event.getTarget(ev);

    if(!Dom.hasClass(elTarget, 'noToggle'))
    {
      Event.preventDefault(ev);
      
      if (Pulldown.elPulldownBtn) 
      {
        if (Dom.hasClass(Pulldown.elPulldown, 'pulldown-hidden'))
        {
          Pulldown.expandPulldown();
          
          // if clicked 'sign-in' then focus on login field
          if (Dom.hasClass(elTarget, 'signIn'))
          {
            Pulldown.elLoginEmail.focus();
            //SONET.omniture.tagInteraction(oOmnitureTags.interaction.signin, oOmnitureTags.widget);
          } else {
            // omni tag that pulldown was opened
            //SONET.omniture.tagInteraction(oOmnitureTags.interaction.toggleDown, oOmnitureTags.widget);
          }
          
        } 
        else
        {
          Pulldown.closePulldown();
          //SONET.omniture.tagInteraction(oOmnitureTags.interaction.toggleUp, oOmnitureTags.widget);
        }
        
      }
    }
  },
  
  expandPulldown:function()
  {
    var nHeight = Dom.getRegion(Pulldown.elPulldown).height; 
    
    
    var oPulldownAnim = new Anim(Pulldown.elPulldownMinimized, 
        { top: { from: 0, to: nHeight } }, .5, YAHOO.util.Easing.easeBoth);
    oPulldownAnim.animate();
    
    var oPulldownAnim = new Anim(Pulldown.elPulldown, 
        { top: { from: -nHeight, to: 0 } }, .5, YAHOO.util.Easing.easeBoth);
    //oPulldownAnim.onTween.subscribe(Login.stepPulldownMinimized, nHeight);
    oPulldownAnim.animate();
    
    Pulldown.elPulldown.removeClassName('pulldown-hidden');
    Pulldown.elPulldownBtn.removeClassName('circle-down');
    Pulldown.elPulldownBtn.addClassName('circle-up');
  },
  
  closePulldown:function()
  {
    var nHeight = Dom.getRegion(Pulldown.elPulldown).height; 
    
    
    var oPulldownAnim = new Anim(Pulldown.elPulldownMinimized, 
        { top: { from: nHeight, to: 0 } }, .5, YAHOO.util.Easing.easeOut);
    oPulldownAnim.animate();
    
    
    var oPulldownAnim = new Anim(Pulldown.elPulldown, 
        { top: { from: 0, to: -nHeight } }, .5, YAHOO.util.Easing.easeOut);
    //oPulldownAnim.onTween.subscribe(Login.stepPulldownUpMinimized, nHeight);
    oPulldownAnim.animate();
    
    Pulldown.elPulldown.addClassName('pulldown-hidden');
    Pulldown.elPulldownBtn.removeClassName('circle-up');
    Pulldown.elPulldownBtn.addClassName('circle-down');
  },
  
  // show pulldown menu and set message to login
  showSignIn:function(sMessage) 
  {
     window.scroll(0,0);
     Pulldown.expandPulldown();
     //Pulldown.elLoginMessage.setContent(sMessage);
     //Pulldown.elLoginEmail.focus();
     //SONET.omniture.tagInteraction(oOmnitureTags.interaction.toggleDown, oOmnitureTags.widget);
  },

  // handle Login
  onLoginClick:function(ev)
  {
    //alert("test1");
    if ((Lang.trim(Pulldown.elLoginEmail.value) != '') || (Lang.trim(Pulldown.elLoginPassword.value)))
    {
      // Show Spinner
      Pulldown.elLoginBtn.hide();
      
      var elSpinner = UWA.extendElement(Pulldown.elPulldown.getElementsByClassName('login-spinner')[0]);
      elSpinner.setStyle('display','block'); 
      
      //SONET.omniture.tagInteraction(oOmnitureTags.interaction.login, oOmnitureTags.widget);
      
	  var form = document.getElementById( 'pulldownLoginForm' );
	  form.submit();
      
	  /**
	  // FI NEW LOGIN FUNCTIONALITY
	  //alert("test2");
	  var form = document.getElementById( 'pulldownLoginForm' );
	  var data = YAHOO.util.Connect.setForm( form );
	  var request = {
	  	    method: "post",
            service: "service",
            type: "xml",
			onComplete: processLoginResponse,
			postBody: data
        };
	  widget.doRequest( "authenticate/login", request);
	  //*/
    }
  },
  
  // handle logout
  onLogoutClick:function(ev)
  {
    Event.preventDefault(ev);
    
    var request = {
        method:'POST',
        service:'service',
        type:'xml',
        onComplete: Pulldown.onLogoutSuccess
    };

    widget.doRequest('authenticate/logout', request);
    
  },
  
  onLogoutSuccess:function(ev)
  {
    // Reload the page
    window.location.reload();
  },
  
  // handle logout
  onPersonaClick:function(event, target)
  {
	var id = target.id.substr(target.id.indexOf('_')+1);
	//alert(target + ' : ' + target.id + ' | ' + id);
	//return;
    Event.preventDefault(event);
    
    var request = {
        method:'post',
        service:'service',
        parameters: 'personaId='+id,
        onComplete: Pulldown.onSelectPersonaSuccess
    };

    widget.doRequest('user/selectPersona', request);
  },
  
  onSelectPersonaSuccess:function(result)
  {
	  //alert(result);
	  window.location.reload();
  }
	
}



function processLoginResponse (result){
	window.location.reload();
  	//alert( 'processLoginResponse ' + result );
}

var OnlineDropdown = {
		
		
		
		///////////////////////////////////////
		// VARS
		///////////////////////////////////////
			elButton:null,
			elDropdownDiv:null,
			isOpened:false,
			caughtFalseFirst:false,
			
		///////////////////////////////////////
		// CONSTRUCTOR
		///////////////////////////////////////
			
		init:function(){
			// elements
			OnlineDropdown.elButton = UWA.extendElement(widget.body.getElementsByClassName('onlineDropButton')[0]);
			OnlineDropdown.elDropdownDiv = UWA.extendElement(widget.body.getElementsByClassName('onlineModeDropdown')[0]);
			
			// add first event
			Event.on(OnlineDropdown.elButton, 'click', OnlineDropdown.onButtonClick);
		},
		
		
		/////////////////////////////////////
		// OPEN / CLASE
		///////////////////////////////////////
		openDropdown:function(){
			OnlineDropdown.elButton.addClassName('active');
			OnlineDropdown.caughtFalseFirst = false;
			OnlineDropdown.isOpened = true;
			OnlineDropdown.elDropdownDiv.addClassName('opened');
			Event.on(document, "click", OnlineDropdown.onDOMEvent, this, true);
		},
		
		closeDropdown:function(){
			OnlineDropdown.elButton.removeClassName('active');
			Event.removeListener(document, "click");
			OnlineDropdown.isOpened = false;
			OnlineDropdown.elDropdownDiv.removeClassName('opened');
			OnlineDropdown.caughtFalseFirst = false;
		},
		
		
		/////////////////////////////////////
		// EVENTS
		///////////////////////////////////////
		onDOMEvent: function(p_oEvent) {
			if(OnlineDropdown.isOpened){
				if(OnlineDropdown.caughtFalseFirst){
					// Get the target node of the DOM event
				    var oTarget = UWA.extendElement(Event.getTarget(p_oEvent));
				    var sEventType = p_oEvent.type;
				    var sTagName = p_oEvent.type;
				    var bWasDropDownAction;
		
				    // find out if event is coming from menu and if not close it
				    try{
				    	bWasDropDownAction = oTarget.hasClassName('onlineDropdownMenu');
				    	if(bWasDropDownAction == false){
				    		OnlineDropdown.closeDropdown();
				    	}
				    }catch(e){
				    	OnlineDropdown.closeDropdown();
				    }
				}else{
					OnlineDropdown.caughtFalseFirst = true;
				}
			}
		},
		
		onButtonClick:function(){
			OnlineDropdown.openDropdown();
		}
		
		
		
}
    });
    return widget;
};
/*
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
*/
/**
 * Utilities for cookie management
 * @namespace YAHOO.util
 * @module cookie
 */
YAHOO.namespace("util");

/**
 * Cookie utility.
 * @class Cookie
 * @static
 */
YAHOO.util.Cookie = {
    
    //-------------------------------------------------------------------------
    // Private Methods
    //-------------------------------------------------------------------------
    
    /**
     * Creates a cookie string that can be assigned into document.cookie.
     * @param {String} name The name of the cookie.
     * @param {String} value The value of the cookie.
     * @param {Boolean} encodeValue True to encode the value, false to leave as-is.
     * @param {Object} options (Optional) Options for the cookie.
     * @return {String} The formatted cookie string.
     * @method _createCookieString
     * @private
     * @static
     */
    _createCookieString : function (name /*:String*/, value /*:Variant*/, encodeValue /*:Boolean*/, options /*:Object*/) /*:String*/ {
        
        //shortcut
        var lang = YAHOO.lang,
            text = encodeURIComponent(name) + "=" + (encodeValue ? encodeURIComponent(value) : value);
        
        
        if (lang.isObject(options)){
            //expiration date
            if (options.expires instanceof Date){
                text += "; expires=" + options.expires.toUTCString();
            }
            
            //path
            if (lang.isString(options.path) && options.path !== ""){
                text += "; path=" + options.path;
            }
            
            //domain
            if (lang.isString(options.domain) && options.domain !== ""){
                text += "; domain=" + options.domain;
            }
            
            //secure
            if (options.secure === true){
                text += "; secure";
            }
        }
        
        return text;
    },
    
    /**
     * Formats a cookie value for an object containing multiple values.
     * @param {Object} hash An object of key-value pairs to create a string for.
     * @return {String} A string suitable for use as a cookie value.
     * @method _createCookieHashString
     * @private
     * @static
     */
    _createCookieHashString : function (hash /*:Object*/) /*:String*/ {
        
        //shortcuts
        var lang = YAHOO.lang;
        
        if (!lang.isObject(hash)){
            throw new TypeError("Cookie._createCookieHashString(): Argument must be an object.");
        }
        
        var text /*:Array*/ = [];
        
        for (var key in hash){
            if (lang.hasOwnProperty(hash, key) && !lang.isFunction(hash[key]) && !lang.isUndefined(hash[key])){
                text.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(hash[key])));
            }
        }
        
        return text.join("&");
    },
    
    /**
     * Parses a cookie hash string into an object.
     * @param {String} text The cookie hash string to parse. The string should already be URL-decoded.
     * @return {Object} An object containing entries for each cookie value.
     * @method _parseCookieHash
     * @private
     * @static
     */
    _parseCookieHash : function (text /*:String*/) /*:Object*/ {
        
        var hashParts /*:Array*/ = text.split("&"),
            hashPart /*:Array*/ = null,
            hash /*:Object*/ = {};
        
        if (text.length > 0){
            for (var i=0, len=hashParts.length; i < len; i++){
                hashPart = hashParts[i].split("=");
                hash[decodeURIComponent(hashPart[0])] = decodeURIComponent(hashPart[1]);
            }
        }
        
        return hash;
    },
    
    /**
     * Parses a cookie string into an object representing all accessible cookies.
     * @param {String} text The cookie string to parse.
     * @param {Boolean} decode (Optional) Indicates if the cookie values should be decoded or not. Default is true.
     * @return {Object} An object containing entries for each accessible cookie.
     * @method _parseCookieString
     * @private
     * @static
     */
    _parseCookieString : function (text /*:String*/, decode /*:Boolean*/) /*:Object*/ {
        
        var cookies /*:Object*/ = {};
        
        if (YAHOO.lang.isString(text) && text.length > 0) {
            
            var decodeValue = (decode === false ? function(s){return s;} : decodeURIComponent);
            
            //if (/[^=]+=[^=;]?(?:; [^=]+=[^=]?)?/.test(text)){
                var cookieParts /*:Array*/ = text.split(/;\s/g),
                    cookieName /*:String*/ = null,
                    cookieValue /*:String*/ = null,
                    cookieNameValue /*:Array*/ = null;
                
                for (var i=0, len=cookieParts.length; i < len; i++){
                    
                    //check for normally-formatted cookie (name-value)
                    cookieNameValue = cookieParts[i].match(/([^=]+)=/i);
                    if (cookieNameValue instanceof Array){
                        try {
                            cookieName = decodeURIComponent(cookieNameValue[1]);
                            cookieValue = decodeValue(cookieParts[i].substring(cookieNameValue[1].length+1));
                        } catch (ex){
                            //ignore the entire cookie - encoding is likely invalid
                        }
                    } else {
                        //means the cookie does not have an "=", so treat it as a boolean flag
                        cookieName = decodeURIComponent(cookieParts[i]);
                        cookieValue = "";
                    }
                    cookies[cookieName] = cookieValue;
                }
            //}
        }
        
        return cookies;
    },
    
    //-------------------------------------------------------------------------
    // Public Methods
    //-------------------------------------------------------------------------
    
    /**
     * Determines if the cookie with the given name exists. This is useful for
     * Boolean cookies (those that do not follow the name=value convention).
     * @param {String} name The name of the cookie to check.
     * @return {Boolean} True if the cookie exists, false if not.
     * @method exists
     * @static
     */
    exists: function(name) {

        if (!YAHOO.lang.isString(name) || name === ""){
            throw new TypeError("Cookie.exists(): Cookie name must be a non-empty string.");
        }

        var cookies /*:Object*/ = this._parseCookieString(document.cookie, true);
        
        return cookies.hasOwnProperty(name);
    },
    
    /**
     * Returns the cookie value for the given name.
     * @param {String} name The name of the cookie to retrieve.
     * @param {Object|Function} options (Optional) An object containing one or more
     *      cookie options: raw (true/false) and converter (a function).
     *      The converter function is run on the value before returning it. The
     *      function is not used if the cookie doesn't exist. The function can be
     *      passed instead of the options object for backwards compatibility.
     * @return {Variant} If no converter is specified, returns a string or null if
     *      the cookie doesn't exist. If the converter is specified, returns the value
     *      returned from the converter or null if the cookie doesn't exist.
     * @method get
     * @static
     */
    get : function (name /*:String*/, options /*:Variant*/) /*:Variant*/{
        
        var lang = YAHOO.lang,
            converter;
        
        if (lang.isFunction(options)) {
            converter = options;
            options = {};
        } else if (lang.isObject(options)) {
            converter = options.converter;
        } else {
            options = {};
        }
        
        var cookies /*:Object*/ = this._parseCookieString(document.cookie, !options.raw);
        
        if (!lang.isString(name) || name === ""){
            throw new TypeError("Cookie.get(): Cookie name must be a non-empty string.");
        }
        
        if (lang.isUndefined(cookies[name])) {
            return null;
        }
        
        if (!lang.isFunction(converter)){
            return cookies[name];
        } else {
            return converter(cookies[name]);
        }
    },
    
    /**
     * Returns the value of a subcookie.
     * @param {String} name The name of the cookie to retrieve.
     * @param {String} subName The name of the subcookie to retrieve.
     * @param {Function} converter (Optional) A function to run on the value before returning
     *      it. The function is not used if the cookie doesn't exist.
     * @return {Variant} If the cookie doesn't exist, null is returned. If the subcookie
     *      doesn't exist, null if also returned. If no converter is specified and the
     *      subcookie exists, a string is returned. If a converter is specified and the
     *      subcookie exists, the value returned from the converter is returned.
     * @method getSub
     * @static
     */
    getSub : function (name, subName, converter) {
        
        var lang = YAHOO.lang,
            hash = this.getSubs(name);
        
        if (hash !== null) {
            
            if (!lang.isString(subName) || subName === ""){
                throw new TypeError("Cookie.getSub(): Subcookie name must be a non-empty string.");
            }
            
            if (lang.isUndefined(hash[subName])){
                return null;
            }
            
            if (!lang.isFunction(converter)){
                return hash[subName];
            } else {
                return converter(hash[subName]);
            }
        } else {
            return null;
        }
    
    },
    
    /**
     * Returns an object containing name-value pairs stored in the cookie with the given name.
     * @param {String} name The name of the cookie to retrieve.
     * @return {Object} An object of name-value pairs if the cookie with the given name
     *      exists, null if it does not.
     * @method getSubs
     * @static
     */
    getSubs : function (name /*:String*/) /*:Object*/ {
    
        var isString = YAHOO.lang.isString;
        
        //check cookie name
        if (!isString(name) || name === ""){
            throw new TypeError("Cookie.getSubs(): Cookie name must be a non-empty string.");
        }
        
        var cookies = this._parseCookieString(document.cookie, false);
        if (isString(cookies[name])){
            return this._parseCookieHash(cookies[name]);
        }
        return null;
    },
    
    /**
     * Removes a cookie from the machine by setting its expiration date to
     * sometime in the past.
     * @param {String} name The name of the cookie to remove.
     * @param {Object} options (Optional) An object containing one or more
     *      cookie options: path (a string), domain (a string),
     *      and secure (true/false). The expires option will be overwritten
     *      by the method.
     * @return {String} The created cookie string.
     * @method remove
     * @static
     */
    remove : function (name /*:String*/, options /*:Object*/) /*:String*/ {
        
        //check cookie name
        if (!YAHOO.lang.isString(name) || name === ""){
            throw new TypeError("Cookie.remove(): Cookie name must be a non-empty string.");
        }
        
        //set options - clone options so the original isn't affected
        options = YAHOO.lang.merge(options || {}, {
            expires: new Date(0)
        });
        
        //set cookie
        return this.set(name, "", options);
    },
    
    /**
     * Removes a subcookie with a given name. Removing the last subcookie
     *      won't remove the entire cookie unless options.removeIfEmpty is true.
     * @param {String} name The name of the cookie in which the subcookie exists.
     * @param {String} subName The name of the subcookie to remove.
     * @param {Object} options (Optional) An object containing one or more
     *      cookie options: path (a string), domain (a string), expires (a Date object),
     *      removeIfEmpty (true/false), and secure (true/false). This must be the same
     *      settings as the original subcookie.
     * @return {String} The created cookie string.
     * @method removeSub
     * @static
     */
    removeSub : function(name /*:String*/, subName /*:String*/, options /*:Object*/) /*:String*/ {
        
        var lang = YAHOO.lang;
        
        options = options || {};
        
        //check cookie name
        if (!lang.isString(name) || name === ""){
            throw new TypeError("Cookie.removeSub(): Cookie name must be a non-empty string.");
        }
        
        //check subcookie name
        if (!lang.isString(subName) || subName === ""){
            throw new TypeError("Cookie.removeSub(): Subcookie name must be a non-empty string.");
        }
        
        //get all subcookies for this cookie
        var subs = this.getSubs(name);
        
        //delete the indicated subcookie
        if (lang.isObject(subs) && lang.hasOwnProperty(subs, subName)){
            delete subs[subName];

            if (!options.removeIfEmpty) {
                //reset the cookie

                return this.setSubs(name, subs, options);
            } else {
                //reset the cookie if there are subcookies left, else remove
                for (var key in subs){
                    if (lang.hasOwnProperty(subs, key) && !lang.isFunction(subs[key]) && !lang.isUndefined(subs[key])){
                        return this.setSubs(name, subs, options);
                    }
                }
                
                return this.remove(name, options);
            }
        } else {
            return "";
        }
        
    },
    
    /**
     * Sets a cookie with a given name and value.
     * @param {String} name The name of the cookie to set.
     * @param {Variant} value The value to set for the cookie.
     * @param {Object} options (Optional) An object containing one or more
     *      cookie options: path (a string), domain (a string), expires (a Date object),
     *      raw (true/false), and secure (true/false).
     * @return {String} The created cookie string.
     * @method set
     * @static
     */
    set : function (name /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/ {
        
        var lang = YAHOO.lang;
        
        options = options || {};
        
        if (!lang.isString(name)){
            throw new TypeError("Cookie.set(): Cookie name must be a string.");
        }
        
        if (lang.isUndefined(value)){
            throw new TypeError("Cookie.set(): Value cannot be undefined.");
        }
        
        var text /*:String*/ = this._createCookieString(name, value, !options.raw, options);
        document.cookie = text;
        return text;
    },
    
    /**
     * Sets a sub cookie with a given name to a particular value.
     * @param {String} name The name of the cookie to set.
     * @param {String} subName The name of the subcookie to set.
     * @param {Variant} value The value to set.
     * @param {Object} options (Optional) An object containing one or more
     *      cookie options: path (a string), domain (a string), expires (a Date object),
     *      and secure (true/false).
     * @return {String} The created cookie string.
     * @method setSub
     * @static
     */
    setSub : function (name /*:String*/, subName /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/ {
        
        var lang = YAHOO.lang;
        
        if (!lang.isString(name) || name === ""){
            throw new TypeError("Cookie.setSub(): Cookie name must be a non-empty string.");
        }
        
        if (!lang.isString(subName) || subName === ""){
            throw new TypeError("Cookie.setSub(): Subcookie name must be a non-empty string.");
        }
        
        if (lang.isUndefined(value)){
            throw new TypeError("Cookie.setSub(): Subcookie value cannot be undefined.");
        }
        
        var hash /*:Object*/ = this.getSubs(name);
        
        if (!lang.isObject(hash)){
            hash = {};
        }
        
        hash[subName] = value;
        
        return this.setSubs(name, hash, options);
        
    },
    
    /**
     * Sets a cookie with a given name to contain a hash of name-value pairs.
     * @param {String} name The name of the cookie to set.
     * @param {Object} value An object containing name-value pairs.
     * @param {Object} options (Optional) An object containing one or more
     *      cookie options: path (a string), domain (a string), expires (a Date object),
     *      and secure (true/false).
     * @return {String} The created cookie string.
     * @method setSubs
     * @static
     */
    setSubs : function (name /*:String*/, value /*:Object*/, options /*:Object*/) /*:String*/ {
        
        var lang = YAHOO.lang;
        
        if (!lang.isString(name)){
            throw new TypeError("Cookie.setSubs(): Cookie name must be a string.");
        }
        
        if (!lang.isObject(value)){
            throw new TypeError("Cookie.setSubs(): Cookie value must be an object.");
        }
        
        var text /*:String*/ = this._createCookieString(name, this._createCookieHashString(value), false, options);
        document.cookie = text;
        return text;
    }

};

YAHOO.register("cookie", YAHOO.util.Cookie, {version: "2.8.1", build: "19"});
SONET.featureProviders["plugin/mmaMarqueePlugin"] = {};
SONET.featureProviders["plugin/mmaMarqueePlugin"].translations = [];
SONET.featureProviders["plugin/mmaMarqueePlugin"].assetDirPattern = "%assettype%\/mmaMarqueePlugin";
SONET.featureProviders["plugin/mmaMarqueePlugin"].widgetClosures = {};
SONET.featureProviders["plugin/mmaMarqueePlugin"].decoratorClosures = {};
SONET.featureProviders["plugin/mmaMarqueePlugin"].widgetClosures["marquee"] = function () {
    var _ = SONET.translatorForFeatureProvider("plugin/mmaMarqueePlugin");
    var widget = new ENGINE.Widget;
    SONET.uwa.use(function(UWA) {
var Marquee = {

		/////////////////////////////////////
		// VARS
		/////////////////////////////////////
		
		oMarquees:null,
		aDays:null,
		aMonths:null,
		isReturningUser:null,
		
		init:function(){
			// check for timezone difference, disregarding daylight savings	
		   var rightNow = new Date();
		   var date1 = new Date(rightNow.getFullYear(), 0, 1, 0, 0, 0, 0);
		   var date2 = new Date(rightNow.getFullYear(), 6, 1, 0, 0, 0, 0);
		   var temp = date1.toGMTString();
		   var date3 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
		   var temp = date2.toGMTString();
		   var date4 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
		   var hoursDiffStdTime = (date1 - date3) / (1000 * 60 * 60);
		   var hoursDiffDaylightTime = (date2 - date4) / (1000 * 60 * 60);
	
			Marquee.oMarquees = widget.getValue('marquees');
			Marquee.aDays = [_("Sunday"), _("Monday"), _("Tuesday"), _("Wednesday"), _("Thursday"), _("Friday"), _("Saturday")];
			Marquee.aMonths = [_("January"), _("February"), _("March"), _("April"), _("May"), _("June"), _("July"), _("August"), _("September"), _("October"), _("November"), _("December")];
			Marquee.isReturningUser = YAHOO.util.Cookie.get("marqueeCookie");
			
			YAHOO.util.Cookie.set("marqueeCookie", "true", {
				expires: new Date("January 12, 2050")
			});
			
			if(typeof(console) != 'undefined')console.log(Marquee.oMarquees);
			
			var elFlashContainer = UWA.extendElement(widget.body.getElementsByClassName('flashContainer')[0]);
			var params = { version: 10,
					   useExpressInstall: true,
					   allowScriptAccess: "always",
					   allowFullScreen: "true",
					   allowNetworking: "all",
					   fixedAttributes:
					   {width:620,height:490}
					 };
			
			var flashVars = {
					localTimeZoneOffset: hoursDiffDaylightTime,
					isReturningUser: Marquee.isReturningUser
			};
			
			// create flash and set marquees
			widget.setFlashContent('marquee.swf', flashVars, params, 490, 620, elFlashContainer);
			
			// send marquee object to flash
			Marquee.setMarqueesInFlash();
		},
		
		setMarqueesInFlash:function(){
			try{
				var elFlashMovie = UWA.extendElement(widget.body.getElementsByTagName('object')[0]);
				elFlashMovie.setMarquees(Marquee.oMarquees, Marquee.aDays, Marquee.aMonths);
			
				if( typeof(console) != 'undefined' ){
					console.log("SET MARQUEE FUNCTION SUCCESS");
				}
			}catch(err){
				if( typeof(console) != 'undefined' ){
					console.log("SET MARQUEE FUNCTION FAIL");
				}
				var fnRef = EASPORTSMMA.globalAjaxCallback(Marquee.setMarqueesInFlashAgain, false);
				setTimeout(fnRef + "()",500);
			}
		},
		
		setMarqueesInFlashAgain:function(){
			Marquee.setMarqueesInFlash();
		}
		
		
		
}

widget.onLoad = function(){
	Marquee.init();
}


    });
    return widget;
};
SONET.featureProviders["plugin/mmaBasePlugin"] = {};
SONET.featureProviders["plugin/mmaBasePlugin"].translations = [];
SONET.featureProviders["plugin/mmaBasePlugin"].assetDirPattern = "%assettype%\/mmaBasePlugin";
SONET.featureProviders["plugin/mmaBasePlugin"].widgetClosures = {};
SONET.featureProviders["plugin/mmaBasePlugin"].decoratorClosures = {};
SONET.featureProviders["plugin/mmaBasePlugin"].decoratorClosures["mmaDecorator"] = function () {
    var _ = SONET.translatorForFeatureProvider("plugin/mmaBasePlugin");
    var decorator = new SONET.Decorator;
    SONET.uwa.use(function(UWA) {
decorator.setMoreLink = function (sMoreUrl){
	var elFooterLink = decorator.body.getElementsByClassName('moreLink')[0];
	if ( typeof( elFooterLink ) != "undefined")  elFooterLink.setAttribute('href', sMoreUrl);
} 	 

decorator.setMoreText = function (sMoreText){
	var elFooterLink = decorator.body.getElementsByClassName('moreLink')[0];
	if ( typeof( elFooterLink ) != "undefined") elFooterLink.innerHTML = sMoreText;
}

    });
    return decorator;
};
SONET.featureProviders["plugin/mmaBasePlugin"].widgetClosures["filler"] = function () {
    var _ = SONET.translatorForFeatureProvider("plugin/mmaBasePlugin");
    var widget = new ENGINE.Widget;
    SONET.uwa.use(function(UWA) {

var resizeFn = EASPORTSMMA.globalAjaxCallback(resizeFiller, true);
EASPORTSMMA.addFunctionToWindowLoad(resizeFn);

widget.onLoad = function(){
	SONET.MessageCenter.subscribe('fillerResize', function(oParameter, sEventName) {
      resizeFiller();
    });
}

function resizeFiller (){
	var Dom   = YAHOO.util.Dom;
	var Event = YAHOO.util.Event;

	var padding = 20;
	
	var elFiller = UWA.extendElement(widget.body.getElementsByClassName('filler')[0]);
	var elTargetDiv = document.getElementById('lowerContent');
	
	// check if "lowerContent" div is there OR of 0px height
	if ( (typeof(elTargetDiv)  == 'undefined') || (parseInt(Dom.getStyle(elTargetDiv, 'height'), 10) == 0)){
		elTargetDiv = document.getElementById('ft');
	}
	
	var thisY = Dom.getY(elFiller);
	var footerY = Dom.getY(elTargetDiv);
	
	var newHeight = Math.max(0, Math.min(999999, (footerY - thisY - padding) )) ; // clamp newHeight to 0 - 99999
	
	//if(newHeight > padding){
			elFiller.setAttribute('style', 'height:' + newHeight + 'px;');		
	//}else{
	//	elFiller.setAttribute('style', 'height:0px; margin-top:0px;');
	//}
	
}

    });
    return widget;
};
/*
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 methods to parse JSON strings and convert objects to JSON strings.
 *
 * @module json
 * @class JSON
 * @namespace YAHOO.lang
 * @static
 */
(function () {

var l = YAHOO.lang,
    isFunction = l.isFunction,
    isObject   = l.isObject,
    isArray    = l.isArray,
    _toStr     = Object.prototype.toString,
                 // 'this' is the global object.  window in browser env.  Keep
                 // the code env agnostic.  Caja requies window, unfortunately.
    Native     = (YAHOO.env.ua.caja ? window : this).JSON,

/* Variables used by parse */

    /**
     * Replace certain Unicode characters that JavaScript may handle incorrectly
     * during eval--either by deleting them or treating them as line
     * endings--with escape sequences.
     * IMPORTANT NOTE: This regex will be used to modify the input if a match is
     * found.
     *
     * @property _UNICODE_EXCEPTIONS
     * @type {RegExp}
     * @private
     */
    _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,

    /**
     * First step in the safety evaluation.  Regex used to replace all escape
     * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character).
     *
     * @property _ESCAPES
     * @type {RegExp}
     * @static
     * @private
     */
    _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,

    /**
     * Second step in the safety evaluation.  Regex used to replace all simple
     * values with ']' characters.
     *
     * @property _VALUES
     * @type {RegExp}
     * @static
     * @private
     */
    _VALUES  = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,

    /**
     * Third step in the safety evaluation.  Regex used to remove all open
     * square brackets following a colon, comma, or at the beginning of the
     * string.
     *
     * @property _BRACKETS
     * @type {RegExp}
     * @static
     * @private
     */
    _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,

    /**
     * Final step in the safety evaluation.  Regex used to test the string left
     * after all previous replacements for invalid characters.
     *
     * @property _UNSAFE
     * @type {RegExp}
     * @static
     * @private
     */
    _UNSAFE  = /^[\],:{}\s]*$/,


/* Variables used by stringify */

    /**
     * Regex used to replace special characters in strings for JSON
     * stringification.
     *
     * @property _SPECIAL_CHARS
     * @type {RegExp}
     * @static
     * @private
     */
    _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,

    /**
     * Character substitution map for common escapes and special characters.
     *
     * @property _CHARS
     * @type {Object}
     * @static
     * @private
     */
    _CHARS = {
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"' : '\\"',
        '\\': '\\\\'
    },
    
    UNDEFINED = 'undefined',
    OBJECT    = 'object',
    NULL      = 'null',
    STRING    = 'string',
    NUMBER    = 'number',
    BOOLEAN   = 'boolean',
    DATE      = 'date',
    _allowable = {
        'undefined'        : UNDEFINED,
        'string'           : STRING,
        '[object String]'  : STRING,
        'number'           : NUMBER,
        '[object Number]'  : NUMBER,
        'boolean'          : BOOLEAN,
        '[object Boolean]' : BOOLEAN,
        '[object Date]'    : DATE,
        '[object RegExp]'  : OBJECT
    },
    EMPTY     = '',
    OPEN_O    = '{',
    CLOSE_O   = '}',
    OPEN_A    = '[',
    CLOSE_A   = ']',
    COMMA     = ',',
    COMMA_CR  = ",\n",
    CR        = "\n",
    COLON     = ':',
    COLON_SP  = ': ',
    QUOTE     = '"';

// Only accept JSON objects that report a [[Class]] of JSON
Native = _toStr.call(Native) === '[object JSON]' && Native;

// Escapes a special character to a safe Unicode representation
function _char(c) {
    if (!_CHARS[c]) {
        _CHARS[c] =  '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
    }
    return _CHARS[c];
}


/* functions used by parse */

/**
 * Traverses nested objects, applying a filter or reviver function to
 * each value.  The value returned from the function will replace the
 * original value in the key:value pair.  If the value returned is
 * undefined, the key will be omitted from the returned object.
 *
 * @method _revive
 * @param data {MIXED} Any JavaScript data
 * @param reviver {Function} filter or mutation function
 * @return {MIXED} The results of the filtered/mutated data structure
 * @private
 */
function _revive(data, reviver) {
    var walk = function (o,key) {
        var k,v,value = o[key];
        if (value && typeof value === 'object') {
            for (k in value) {
                if (l.hasOwnProperty(value,k)) {
                    v = walk(value, k);
                    if (v === undefined) {
                        delete value[k];
                    } else {
                        value[k] = v;
                    }
                }
            }
        }
        return reviver.call(o,key,value);
    };

    return typeof reviver === 'function' ? walk({'':data},'') : data;
}

/**
 * Replace certain Unicode characters that may be handled incorrectly by
 * some browser implementations.
 *
 * @method _prepare
 * @param s {String} parse input
 * @return {String} sanitized JSON string ready to be validated/parsed
 * @private
 */
function _prepare(s) {
    return s.replace(_UNICODE_EXCEPTIONS, _char);
}

function _isSafe(str) {
    return l.isString(str) &&
            _UNSAFE.test(str.replace(_ESCAPES,'@').
                             replace(_VALUES,']').
                             replace(_BRACKETS,''));
}

function _parse(s,reviver) {
    // sanitize
    s = _prepare(s);

    // Ensure valid JSON
    if (_isSafe(s)) {
        // Eval the text into a JavaScript data structure, apply the
        // reviver function if provided, and return
        return _revive( eval('(' + s + ')'), reviver );
    }

    // The text is not valid JSON
    throw new SyntaxError('JSON.parse');
}



/* functions used by stringify */

// Utility function used to determine how to serialize a variable.
function _type(o) {
    var t = typeof o;
    return  _allowable[t] ||              // number, string, boolean, undefined
            _allowable[_toStr.call(o)] || // Number, String, Boolean, Date
            (t === OBJECT ?
                (o ? OBJECT : NULL) :     // object, array, null, misc natives
                UNDEFINED);               // function, unknown
}

// Enclose escaped strings in quotes
function _string(s) {
    return QUOTE + s.replace(_SPECIAL_CHARS, _char) + QUOTE;
}

// Adds the provided space to the beginning of every line in the input string
function _indent(s,space) {
    return s.replace(/^/gm, space);
}

// JavaScript implementation of stringify (see API declaration of stringify)
function _stringify(o,w,space) {
    if (o === undefined) {
        return undefined;
    }

    var replacer = isFunction(w) ? w : null,
        format   = _toStr.call(space).match(/String|Number/) || [],
        _date    = YAHOO.lang.JSON.dateToString,
        stack    = [],
        tmp,i,len;

    if (replacer || !isArray(w)) {
        w = undefined;
    }

    // Ensure whitelist keys are unique (bug 2110391)
    if (w) {
        tmp = {};
        for (i = 0, len = w.length; i < len; ++i) {
            tmp[w[i]] = true;
        }
        w = tmp;
    }

    // Per the spec, strings are truncated to 10 characters and numbers
    // are converted to that number of spaces (max 10)
    space = format[0] === 'Number' ?
                new Array(Math.min(Math.max(0,space),10)+1).join(" ") :
                (space || EMPTY).slice(0,10);

    function _serialize(h,key) {
        var value = h[key],
            t     = _type(value),
            a     = [],
            colon = space ? COLON_SP : COLON,
            arr, i, keys, k, v;

        // Per the ECMA 5 spec, toJSON is applied before the replacer is
        // called.  Also per the spec, Date.prototype.toJSON has been added, so
        // Date instances should be serialized prior to exposure to the
        // replacer.  I disagree with this decision, but the spec is the spec.
        if (isObject(value) && isFunction(value.toJSON)) {
            value = value.toJSON(key);
        } else if (t === DATE) {
            value = _date(value);
        }

        if (isFunction(replacer)) {
            value = replacer.call(h,key,value);
        }

        if (value !== h[key]) {
            t = _type(value);
        }

        switch (t) {
            case DATE    : // intentional fallthrough.  Pre-replacer Dates are
                           // serialized in the toJSON stage.  Dates here would
                           // have been produced by the replacer.
            case OBJECT  : break;
            case STRING  : return _string(value);
            case NUMBER  : return isFinite(value) ? value+EMPTY : NULL;
            case BOOLEAN : return value+EMPTY;
            case NULL    : return NULL;
            default      : return undefined;
        }

        // Check for cyclical references in nested objects
        for (i = stack.length - 1; i >= 0; --i) {
            if (stack[i] === value) {
                throw new Error("JSON.stringify. Cyclical reference");
            }
        }

        arr = isArray(value);

        // Add the object to the processing stack
        stack.push(value);

        if (arr) { // Array
            for (i = value.length - 1; i >= 0; --i) {
                a[i] = _serialize(value, i) || NULL;
            }
        } else {   // Object
            // If whitelist provided, take only those keys
            keys = w || value;
            i = 0;

            for (k in keys) {
                if (keys.hasOwnProperty(k)) {
                    v = _serialize(value, k);
                    if (v) {
                        a[i++] = _string(k) + colon + v;
                    }
                }
            }
        }

        // remove the array from the stack
        stack.pop();

        if (space && a.length) {
            return arr ?
                OPEN_A + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_A :
                OPEN_O + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_O;
        } else {
            return arr ?
                OPEN_A + a.join(COMMA) + CLOSE_A :
                OPEN_O + a.join(COMMA) + CLOSE_O;
        }
    }

    // process the input
    return _serialize({'':o},'');
}


/* Public API */
YAHOO.lang.JSON = {
    /**
     * Leverage native JSON parse if the browser has a native implementation.
     * In general, this is a good idea.  See the Known Issues section in the
     * JSON user guide for caveats.  The default value is true for browsers with
     * native JSON support.
     *
     * @property useNativeParse
     * @type Boolean
     * @default true
     * @static
     */
    useNativeParse : !!Native,

    /**
     * Leverage native JSON stringify if the browser has a native
     * implementation.  In general, this is a good idea.  See the Known Issues
     * section in the JSON user guide for caveats.  The default value is true
     * for browsers with native JSON support.
     *
     * @property useNativeStringify
     * @type Boolean
     * @default true
     * @static
     */
    useNativeStringify : !!Native,

    /**
     * Four step determination whether a string is safe to eval. In three steps,
     * escape sequences, safe values, and properly placed open square brackets
     * are replaced with placeholders or removed.  Then in the final step, the
     * result of all these replacements is checked for invalid characters.
     *
     * @method isSafe
     * @param str {String} JSON string to be tested
     * @return {boolean} is the string safe for eval?
     * @static
     */
    isSafe : function (s) {
        return _isSafe(_prepare(s));
    },

    /**
     * <p>Parse a JSON string, returning the native JavaScript
     * representation.</p>
     *
     * <p>When lang.JSON.useNativeParse is true, this will defer to the native
     * JSON.parse if the browser has a native implementation.  Otherwise, a
     * JavaScript implementation based on http://www.json.org/json2.js
     * is used.</p>
     *
     * @method parse
     * @param s {string} JSON string data
     * @param reviver {function} (optional) function(k,v) passed each key:value
     *          pair of object literals, allowing pruning or altering values
     * @return {MIXED} the native JavaScript representation of the JSON string
     * @throws SyntaxError
     * @static
     */
    parse : function (s,reviver) {
        return Native && YAHOO.lang.JSON.useNativeParse ?
            Native.parse(s,reviver) : _parse(s,reviver);
    },

    /**
     * <p>Converts an arbitrary value to a JSON string representation.</p>
     *
     * <p>Objects with cyclical references will trigger an exception.</p>
     *
     * <p>If a whitelist is provided, only matching object keys will be
     * included.  Alternately, a replacer function may be passed as the
     * second parameter.  This function is executed on every value in the
     * input, and its return value will be used in place of the original value.
     * This is useful to serialize specialized objects or class instances.</p>
     *
     * <p>If a positive integer or non-empty string is passed as the third
     * parameter, the output will be formatted with carriage returns and
     * indentation for readability.  If a String is passed (such as "\t") it
     * will be used once for each indentation level.  If a number is passed,
     * that number of spaces will be used.</p>
     *
     * <p>When lang.JSON.useNativeStringify is true, this will defer to the
     * native JSON.stringify if the browser has a native implementation.
     * Otherwise, a JavaScript implementation is used.</p>
     *
     * @method stringify
     * @param o {MIXED} any arbitrary object to convert to JSON string
     * @param w {Array|Function} (optional) whitelist of acceptable object keys
     *                  to include OR a function(value,key) to alter values
     *                  before serialization
     * @param space {Number|String} (optional) indentation character(s) or
     *                  depthy of spaces to format the output 
     * @return {string} JSON string representation of the input
     * @throws Error
     * @static
     */
    stringify : function (o,w,space) {
        return Native && YAHOO.lang.JSON.useNativeStringify ?
            Native.stringify(o,w,space) : _stringify(o,w,space);
    },

    /**
     * Serializes a Date instance as a UTC date string.  Used internally by
     * the JavaScript implementation of stringify.  If you need a different
     * Date serialization format, override this method.  If you change this,
     * you should also set useNativeStringify to false, since native JSON
     * implementations serialize Dates per the ECMAScript 5 spec.  You've been
     * warned.
     *
     * @method dateToString
     * @param d {Date} The Date to serialize
     * @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ
     * @static
     */
    dateToString : function (d) {
        function _zeroPad(v) {
            return v < 10 ? '0' + v : v;
        }

        return d.getUTCFullYear()         + '-' +
            _zeroPad(d.getUTCMonth() + 1) + '-' +
            _zeroPad(d.getUTCDate())      + 'T' +
            _zeroPad(d.getUTCHours())     + COLON +
            _zeroPad(d.getUTCMinutes())   + COLON +
            _zeroPad(d.getUTCSeconds())   + 'Z';
    },

    /**
     * Reconstitute Date instances from the default JSON UTC serialization.
     * Reference this from a reviver function to rebuild Dates during the
     * parse operation.
     *
     * @method stringToDate
     * @param str {String} String serialization of a Date
     * @return {Date}
     */
    stringToDate : function (str) {
        var m = str.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?Z$/);
        if (m) {
            var d = new Date();
            d.setUTCFullYear(m[1], m[2]-1, m[3]);
            d.setUTCHours(m[4], m[5], m[6], (m[7] || 0));
            return d;
        }
        return str;
    }
};

/**
 * <p>Four step determination whether a string is safe to eval. In three steps,
 * escape sequences, safe values, and properly placed open square brackets
 * are replaced with placeholders or removed.  Then in the final step, the
 * result of all these replacements is checked for invalid characters.</p>
 *
 * <p>This is an alias for isSafe.</p>
 *
 * @method isValid
 * @param str {String} JSON string to be tested
 * @return {boolean} is the string safe for eval?
 * @static
 * @deprecated use isSafe
 */
YAHOO.lang.JSON.isValid = YAHOO.lang.JSON.isSafe;

})();
YAHOO.register("json", YAHOO.lang.JSON, {version: "2.8.1", build: "19"});
/*
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 a mechanism to fetch remote resources and
 * insert them into a document
 * @module get
 * @requires yahoo
 */

/**
 * Fetches and inserts one or more script or link nodes into the document 
 * @namespace YAHOO.util
 * @class YAHOO.util.Get
 */
YAHOO.util.Get = function() {

    /**
     * hash of queues to manage multiple requests
     * @property queues
     * @private
     */
    var queues={}, 
        
    /**
     * queue index used to generate transaction ids
     * @property qidx
     * @type int
     * @private
     */
        qidx=0, 
        
    /**
     * node index used to generate unique node ids
     * @property nidx
     * @type int
     * @private
     */
        nidx=0, 

        // ridx=0,

        // sandboxFrame=null,

    /**
     * interal property used to prevent multiple simultaneous purge 
     * processes
     * @property purging
     * @type boolean
     * @private
     */
        purging=false,

        ua=YAHOO.env.ua, 
        
        lang=YAHOO.lang;
    
    /** 
     * Generates an HTML element, this is not appended to a document
     * @method _node
     * @param type {string} the type of element
     * @param attr {string} the attributes
     * @param win {Window} optional window to create the element in
     * @return {HTMLElement} the generated node
     * @private
     */
    var _node = function(type, attr, win) {
        var w = win || window, d=w.document, n=d.createElement(type);

        for (var i in attr) {
            if (attr[i] && YAHOO.lang.hasOwnProperty(attr, i)) {
                n.setAttribute(i, attr[i]);
            }
        }

        return n;
    };

    /**
     * Generates a link node
     * @method _linkNode
     * @param url {string} the url for the css file
     * @param win {Window} optional window to create the node in
     * @return {HTMLElement} the generated node
     * @private
     */
    var _linkNode = function(url, win, attributes) {

        var o = {
            id:   "yui__dyn_" + (nidx++),
            type: "text/css",
            rel:  "stylesheet",
            href: url
        };

        if (attributes) {
            lang.augmentObject(o, attributes);
        }

        return _node("link", o, win);
    };

    /**
     * Generates a script node
     * @method _scriptNode
     * @param url {string} the url for the script file
     * @param win {Window} optional window to create the node in
     * @return {HTMLElement} the generated node
     * @private
     */
    var _scriptNode = function(url, win, attributes) {
        var o = {
            id:   "yui__dyn_" + (nidx++),
            type: "text/javascript",
            src:  url
        };

        if (attributes) {
            lang.augmentObject(o, attributes);
        }

        return _node("script", o, win);
    };

    /**
     * Returns the data payload for callback functions
     * @method _returnData
     * @private
     */
    var _returnData = function(q, msg) {
        return {
                tId: q.tId,
                win: q.win,
                data: q.data,
                nodes: q.nodes,
                msg: msg,
                purge: function() {
                    _purge(this.tId);
                }
            };
    };

    var _get = function(nId, tId) {
        var q = queues[tId],
            n = (lang.isString(nId)) ? q.win.document.getElementById(nId) : nId;
        if (!n) {
            _fail(tId, "target node not found: " + nId);
        }

        return n;
    };

    /*
     * The request failed, execute fail handler with whatever
     * was accomplished.  There isn't a failure case at the
     * moment unless you count aborted transactions
     * @method _fail
     * @param id {string} the id of the request
     * @private
     */
    var _fail = function(id, msg) {
        var q = queues[id];
        // execute failure callback
        if (q.onFailure) {
            var sc=q.scope || q.win;
            q.onFailure.call(sc, _returnData(q, msg));
        }
    };

    /**
     * The request is complete, so executing the requester's callback
     * @method _finish
     * @param id {string} the id of the request
     * @private
     */
    var _finish = function(id) {
        var q = queues[id];
        q.finished = true;

        if (q.aborted) {
            var msg = "transaction " + id + " was aborted";
            _fail(id, msg);
            return;
        }

        // execute success callback
        if (q.onSuccess) {
            var sc=q.scope || q.win;
            q.onSuccess.call(sc, _returnData(q));
        }
    };

    /**
     * Timeout detected
     * @method _timeout
     * @param id {string} the id of the request
     * @private
     */
    var _timeout = function(id) {
        var q = queues[id];
        if (q.onTimeout) {
            var sc=q.scope || q;
            q.onTimeout.call(sc, _returnData(q));
        }
    };

    /**
     * Loads the next item for a given request
     * @method _next
     * @param id {string} the id of the request
     * @param loaded {string} the url that was just loaded, if any
     * @private
     */
    var _next = function(id, loaded) {
        var q = queues[id];

        if (q.timer) {
            // Y.log('cancel timer');
            q.timer.cancel();
        }

        if (q.aborted) {
            var msg = "transaction " + id + " was aborted";
            _fail(id, msg);
            return;
        }

        if (loaded) {
            q.url.shift(); 
            if (q.varName) {
                q.varName.shift(); 
            }
        } else {
            // This is the first pass: make sure the url is an array
            q.url = (lang.isString(q.url)) ? [q.url] : q.url;
            if (q.varName) {
                q.varName = (lang.isString(q.varName)) ? [q.varName] : q.varName;
            }
        }

        var w=q.win, d=w.document, h=d.getElementsByTagName("head")[0], n;

        if (q.url.length === 0) {
            // Safari 2.x workaround - There is no way to know when 
            // a script is ready in versions of Safari prior to 3.x.
            // Adding an extra node reduces the problem, but doesn't
            // eliminate it completely because the browser executes
            // them asynchronously. 
            if (q.type === "script" && ua.webkit && ua.webkit < 420 && 
                    !q.finalpass && !q.varName) {
                // Add another script node.  This does not guarantee that the
                // scripts will execute in order, but it does appear to fix the
                // problem on fast connections more effectively than using an
                // arbitrary timeout.  It is possible that the browser does
                // block subsequent script execution in this case for a limited
                // time.
                var extra = _scriptNode(null, q.win, q.attributes);
                extra.innerHTML='YAHOO.util.Get._finalize("' + id + '");';
                q.nodes.push(extra); h.appendChild(extra);

            } else {
                _finish(id);
            }

            return;
        } 


        var url = q.url[0];

        // if the url is undefined, this is probably a trailing comma problem in IE
        if (!url) {
            q.url.shift(); 
            return _next(id);
        }


        if (q.timeout) {
            // Y.log('create timer');
            q.timer = lang.later(q.timeout, q, _timeout, id);
        }

        if (q.type === "script") {
            n = _scriptNode(url, w, q.attributes);
        } else {
            n = _linkNode(url, w, q.attributes);
        }

        // track this node's load progress
        _track(q.type, n, id, url, w, q.url.length);

        // add the node to the queue so we can return it to the user supplied callback
        q.nodes.push(n);

        // add it to the head or insert it before 'insertBefore'
        if (q.insertBefore) {
            var s = _get(q.insertBefore, id);
            if (s) {
                s.parentNode.insertBefore(n, s);
            }
        } else {
            h.appendChild(n);
        }
        

        // FireFox does not support the onload event for link nodes, so there is
        // no way to make the css requests synchronous. This means that the css 
        // rules in multiple files could be applied out of order in this browser
        // if a later request returns before an earlier one.  Safari too.
        if ((ua.webkit || ua.gecko) && q.type === "css") {
            _next(id, url);
        }
    };

    /**
     * Removes processed queues and corresponding nodes
     * @method _autoPurge
     * @private
     */
    var _autoPurge = function() {

        if (purging) {
            return;
        }

        purging = true;
        for (var i in queues) {
            var q = queues[i];
            if (q.autopurge && q.finished) {
                _purge(q.tId);
                delete queues[i];
            }
        }

        purging = false;
    };

    /**
     * Removes the nodes for the specified queue
     * @method _purge
     * @private
     */
    var _purge = function(tId) {
        if (queues[tId]) {

            var q     = queues[tId],
                nodes = q.nodes, 
                l     = nodes.length, 
                d     = q.win.document, 
                h     = d.getElementsByTagName("head")[0],
                sib, i, node, attr;

            if (q.insertBefore) {
                sib = _get(q.insertBefore, tId);
                if (sib) {
                    h = sib.parentNode;
                }
            }

            for (i=0; i<l; i=i+1) {
                node = nodes[i];
                if (node.clearAttributes) {
                    node.clearAttributes();
                } else {
                    for (attr in node) {
                        delete node[attr];
                    }
                }

                h.removeChild(node);
            }

            q.nodes = [];
        }
    };

    /**
     * Saves the state for the request and begins loading
     * the requested urls
     * @method queue
     * @param type {string} the type of node to insert
     * @param url {string} the url to load
     * @param opts the hash of options for this request
     * @private
     */
    var _queue = function(type, url, opts) {

        var id = "q" + (qidx++);
        opts = opts || {};

        if (qidx % YAHOO.util.Get.PURGE_THRESH === 0) {
            _autoPurge();
        }

        queues[id] = lang.merge(opts, {
            tId: id,
            type: type,
            url: url,
            finished: false,
            aborted: false,
            nodes: []
        });

        var q = queues[id];
        q.win = q.win || window;
        q.scope = q.scope || q.win;
        q.autopurge = ("autopurge" in q) ? q.autopurge : 
                      (type === "script") ? true : false;

        if (opts.charset) {
            q.attributes = q.attributes || {};
            q.attributes.charset = opts.charset;
        }

        lang.later(0, q, _next, id);

        return {
            tId: id
        };
    };

    /**
     * Detects when a node has been loaded.  In the case of
     * script nodes, this does not guarantee that contained
     * script is ready to use.
     * @method _track
     * @param type {string} the type of node to track
     * @param n {HTMLElement} the node to track
     * @param id {string} the id of the request
     * @param url {string} the url that is being loaded
     * @param win {Window} the targeted window
     * @param qlength the number of remaining items in the queue,
     * including this one
     * @param trackfn {Function} function to execute when finished
     * the default is _next
     * @private
     */
    var _track = function(type, n, id, url, win, qlength, trackfn) {
        var f = trackfn || _next;

        // IE supports the readystatechange event for script and css nodes
        if (ua.ie) {
            n.onreadystatechange = function() {
                var rs = this.readyState;
                if ("loaded" === rs || "complete" === rs) {
                    n.onreadystatechange = null;
                    f(id, url);
                }
            };

        // webkit prior to 3.x is problemmatic
        } else if (ua.webkit) {

            if (type === "script") {

                // Safari 3.x supports the load event for script nodes (DOM2)
                if (ua.webkit >= 420) {

                    n.addEventListener("load", function() {
                        f(id, url);
                    });

                // Nothing can be done with Safari < 3.x except to pause and hope
                // for the best, particularly after last script is inserted. The
                // scripts will always execute in the order they arrive, not
                // necessarily the order in which they were inserted.  To support
                // script nodes with complete reliability in these browsers, script
                // nodes either need to invoke a function in the window once they
                // are loaded or the implementer needs to provide a well-known
                // property that the utility can poll for.
                } else {
                    // Poll for the existence of the named variable, if it
                    // was supplied.
                    var q = queues[id];
                    if (q.varName) {
                        var freq=YAHOO.util.Get.POLL_FREQ;
                        q.maxattempts = YAHOO.util.Get.TIMEOUT/freq;
                        q.attempts = 0;
                        q._cache = q.varName[0].split(".");
                        q.timer = lang.later(freq, q, function(o) {
                            var a=this._cache, l=a.length, w=this.win, i;
                            for (i=0; i<l; i=i+1) {
                                w = w[a[i]];
                                if (!w) {
                                    // if we have exausted our attempts, give up
                                    this.attempts++;
                                    if (this.attempts++ > this.maxattempts) {
                                        var msg = "Over retry limit, giving up";
                                        q.timer.cancel();
                                        _fail(id, msg);
                                    } else {
                                    }
                                    return;
                                }
                            }
                            

                            q.timer.cancel();
                            f(id, url);

                        }, null, true);
                    } else {
                        lang.later(YAHOO.util.Get.POLL_FREQ, null, f, [id, url]);
                    }
                }
            } 

        // FireFox and Opera support onload (but not DOM2 in FF) handlers for
        // script nodes.  Opera, but not FF, supports the onload event for link
        // nodes.
        } else { 
            n.onload = function() {
                f(id, url);
            };
        }
    };

    return {

        /**
         * The default poll freqency in ms, when needed
         * @property POLL_FREQ
         * @static
         * @type int
         * @default 10
         */
        POLL_FREQ: 10,

        /**
         * The number of request required before an automatic purge.
         * property PURGE_THRESH
         * @static
         * @type int
         * @default 20
         */
        PURGE_THRESH: 20,

        /**
         * The length time to poll for varName when loading a script in
         * Safari 2.x before the transaction fails.
         * property TIMEOUT
         * @static
         * @type int
         * @default 2000
         */
        TIMEOUT: 2000,
        
        /**
         * Called by the the helper for detecting script load in Safari
         * @method _finalize
         * @param id {string} the transaction id
         * @private
         */
        _finalize: function(id) {
            lang.later(0, null, _finish, id);
        },

        /**
         * Abort a transaction
         * @method abort
         * @param {string|object} either the tId or the object returned from
         * script() or css()
         */
        abort: function(o) {
            var id = (lang.isString(o)) ? o : o.tId;
            var q = queues[id];
            if (q) {
                q.aborted = true;
            }
        }, 

        /**
         * Fetches and inserts one or more script nodes into the head
         * of the current document or the document in a specified window.
         *
         * @method script
         * @static
         * @param url {string|string[]} the url or urls to the script(s)
         * @param opts {object} Options: 
         * <dl>
         * <dt>onSuccess</dt>
         * <dd>
         * callback to execute when the script(s) are finished loading
         * The callback receives an object back with the following
         * data:
         * <dl>
         * <dt>win</dt>
         * <dd>the window the script(s) were inserted into</dd>
         * <dt>data</dt>
         * <dd>the data object passed in when the request was made</dd>
         * <dt>nodes</dt>
         * <dd>An array containing references to the nodes that were
         * inserted</dd>
         * <dt>purge</dt>
         * <dd>A function that, when executed, will remove the nodes
         * that were inserted</dd>
         * <dt>
         * </dl>
         * </dd>
         * <dt>onFailure</dt>
         * <dd>
         * callback to execute when the script load operation fails
         * The callback receives an object back with the following
         * data:
         * <dl>
         * <dt>win</dt>
         * <dd>the window the script(s) were inserted into</dd>
         * <dt>data</dt>
         * <dd>the data object passed in when the request was made</dd>
         * <dt>nodes</dt>
         * <dd>An array containing references to the nodes that were
         * inserted successfully</dd>
         * <dt>purge</dt>
         * <dd>A function that, when executed, will remove any nodes
         * that were inserted</dd>
         * <dt>
         * </dl>
         * </dd>
         * <dt>onTimeout</dt>
         * <dd>
         * callback to execute when a timeout occurs.
         * The callback receives an object back with the following
         * data:
         * <dl>
         * <dt>win</dt>
         * <dd>the window the script(s) were inserted into</dd>
         * <dt>data</dt>
         * <dd>the data object passed in when the request was made</dd>
         * <dt>nodes</dt>
         * <dd>An array containing references to the nodes that were
         * inserted</dd>
         * <dt>purge</dt>
         * <dd>A function that, when executed, will remove the nodes
         * that were inserted</dd>
         * <dt>
         * </dl>
         * </dd>
         * <dt>scope</dt>
         * <dd>the execution context for the callbacks</dd>
         * <dt>win</dt>
         * <dd>a window other than the one the utility occupies</dd>
         * <dt>autopurge</dt>
         * <dd>
         * setting to true will let the utilities cleanup routine purge 
         * the script once loaded
         * </dd>
         * <dt>data</dt>
         * <dd>
         * data that is supplied to the callback when the script(s) are
         * loaded.
         * </dd>
         * <dt>varName</dt>
         * <dd>
         * variable that should be available when a script is finished
         * loading.  Used to help Safari 2.x and below with script load 
         * detection.  The type of this property should match what was
         * passed into the url parameter: if loading a single url, a
         * string can be supplied.  If loading multiple scripts, you
         * must supply an array that contains the variable name for
         * each script.
         * </dd>
         * <dt>insertBefore</dt>
         * <dd>node or node id that will become the new node's nextSibling</dd>
         * </dl>
         * <dt>charset</dt>
         * <dd>Node charset, deprecated, use 'attributes'</dd>
         * <dt>attributes</dt>
         * <dd>A hash of attributes to apply to dynamic nodes.</dd>
         * <dt>timeout</dt>
         * <dd>Number of milliseconds to wait before aborting and firing the timeout event</dd>
         * <pre>
         * // assumes yahoo, dom, and event are already on the page
         * &nbsp;&nbsp;YAHOO.util.Get.script(
         * &nbsp;&nbsp;["http://yui.yahooapis.com/2.7.0/build/dragdrop/dragdrop-min.js",
         * &nbsp;&nbsp;&nbsp;"http://yui.yahooapis.com/2.7.0/build/animation/animation-min.js"], &#123;
         * &nbsp;&nbsp;&nbsp;&nbsp;onSuccess: function(o) &#123;
         * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new YAHOO.util.DDProxy("dd1"); // also new o.reference("dd1"); would work
         * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.log("won't cause error because YAHOO is the scope");
         * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.log(o.nodes.length === 2) // true
         * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// o.purge(); // optionally remove the script nodes immediately
         * &nbsp;&nbsp;&nbsp;&nbsp;&#125;,
         * &nbsp;&nbsp;&nbsp;&nbsp;onFailure: function(o) &#123;
         * &nbsp;&nbsp;&nbsp;&nbsp;&#125;,
         * &nbsp;&nbsp;&nbsp;&nbsp;data: "foo",
         * &nbsp;&nbsp;&nbsp;&nbsp;timeout: 10000, // 10 second timeout
         * &nbsp;&nbsp;&nbsp;&nbsp;scope: YAHOO,
         * &nbsp;&nbsp;&nbsp;&nbsp;// win: otherframe // target another window/frame
         * &nbsp;&nbsp;&nbsp;&nbsp;autopurge: true // allow the utility to choose when to remove the nodes
         * &nbsp;&nbsp;&#125;);
         * </pre>
         * @return {tId: string} an object containing info about the transaction
         */
        script: function(url, opts) { return _queue("script", url, opts); },

        /**
         * Fetches and inserts one or more css link nodes into the 
         * head of the current document or the document in a specified
         * window.
         * @method css
         * @static
         * @param url {string} the url or urls to the css file(s)
         * @param opts Options: 
         * <dl>
         * <dt>onSuccess</dt>
         * <dd>
         * callback to execute when the css file(s) are finished loading
         * The callback receives an object back with the following
         * data:
         * <dl>win</dl>
         * <dd>the window the link nodes(s) were inserted into</dd>
         * <dt>data</dt>
         * <dd>the data object passed in when the request was made</dd>
         * <dt>nodes</dt>
         * <dd>An array containing references to the nodes that were
         * inserted</dd>
         * <dt>purge</dt>
         * <dd>A function that, when executed, will remove the nodes
         * that were inserted</dd>
         * <dt>
         * </dl>
         * </dd>
         * <dt>scope</dt>
         * <dd>the execution context for the callbacks</dd>
         * <dt>win</dt>
         * <dd>a window other than the one the utility occupies</dd>
         * <dt>data</dt>
         * <dd>
         * data that is supplied to the callbacks when the nodes(s) are
         * loaded.
         * </dd>
         * <dt>insertBefore</dt>
         * <dd>node or node id that will become the new node's nextSibling</dd>
         * <dt>charset</dt>
         * <dd>Node charset, deprecated, use 'attributes'</dd>
         * <dt>attributes</dt>
         * <dd>A hash of attributes to apply to dynamic nodes.</dd>
         * </dl>
         * <pre>
         *      YAHOO.util.Get.css("http://yui.yahooapis.com/2.7.0/build/menu/assets/skins/sam/menu.css");
         * </pre>
         * <pre>
         *      YAHOO.util.Get.css(["http://yui.yahooapis.com/2.7.0/build/menu/assets/skins/sam/menu.css",
         * </pre>
         * @return {tId: string} an object containing info about the transaction
         */
        css: function(url, opts) {
            return _queue("css", url, opts); 
        }
    };
}();

YAHOO.register("get", YAHOO.util.Get, {version: "2.8.1", build: "19"});
/*
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 CustomEvent class lets you define events for your application
 * that can be subscribed to by one or more independent component.
 *
 * @param {String}  type The type of event, which is passed to the callback
 *                  when the event fires
 * @param {Object}  context The context the event will fire from.  "this" will
 *                  refer to this object in the callback.  Default value: 
 *                  the window object.  The listener can override this.
 * @param {boolean} silent pass true to prevent the event from writing to
 *                  the debugsystem
 * @param {int}     signature the signature that the custom event subscriber
 *                  will receive. YAHOO.util.CustomEvent.LIST or 
 *                  YAHOO.util.CustomEvent.FLAT.  The default is
 *                  YAHOO.util.CustomEvent.LIST.
 * @param fireOnce {boolean} If configured to fire once, the custom event 
 * will only notify subscribers a single time regardless of how many times 
 * the event is fired.  In addition, new subscribers will be notified 
 * immediately if the event has already been fired.
 * @namespace YAHOO.util
 * @class CustomEvent
 * @constructor
 */
YAHOO.util.CustomEvent = function(type, context, silent, signature, fireOnce) {

    /**
     * The type of event, returned to subscribers when the event fires
     * @property type
     * @type string
     */
    this.type = type;

    /**
     * The context the event will fire from by default. Defaults to the window obj.
     * @property scope
     * @type object
     */
    this.scope = context || window;

    /**
     * By default all custom events are logged in the debug build. Set silent to true 
     * to disable debug output for this event.
     * @property silent
     * @type boolean
     */
    this.silent = silent;

    /**
     * If configured to fire once, the custom event will only notify subscribers
     * a single time regardless of how many times the event is fired.  In addition,
     * new subscribers will be notified immediately if the event has already been
     * fired.
     * @property fireOnce
     * @type boolean
     * @default false
     */
    this.fireOnce = fireOnce;

    /**
     * Indicates whether or not this event has ever been fired.
     * @property fired
     * @type boolean
     * @default false
     */
    this.fired = false;

    /**
     * For fireOnce events the arguments the event was fired with are stored
     * so that new subscribers get the proper payload.
     * @property firedWith
     * @type Array
     */
    this.firedWith = null;

    /**
     * Custom events support two styles of arguments provided to the event
     * subscribers.  
     * <ul>
     * <li>YAHOO.util.CustomEvent.LIST: 
     *   <ul>
     *   <li>param1: event name</li>
     *   <li>param2: array of arguments sent to fire</li>
     *   <li>param3: <optional> a custom object supplied by the subscriber</li>
     *   </ul>
     * </li>
     * <li>YAHOO.util.CustomEvent.FLAT
     *   <ul>
     *   <li>param1: the first argument passed to fire.  If you need to
     *           pass multiple parameters, use and array or object literal</li>
     *   <li>param2: <optional> a custom object supplied by the subscriber</li>
     *   </ul>
     * </li>
     * </ul>
     *   @property signature
     *   @type int
     */
    this.signature = signature || YAHOO.util.CustomEvent.LIST;

    /**
     * The subscribers to this event
     * @property subscribers
     * @type Subscriber[]
     */
    this.subscribers = [];

    if (!this.silent) {
    }

    var onsubscribeType = "_YUICEOnSubscribe";

    // Only add subscribe events for events that are not generated by 
    // CustomEvent
    if (type !== onsubscribeType) {

        /**
         * Custom events provide a custom event that fires whenever there is
         * a new subscriber to the event.  This provides an opportunity to
         * handle the case where there is a non-repeating event that has
         * already fired has a new subscriber.  
         *
         * @event subscribeEvent
         * @type YAHOO.util.CustomEvent
         * @param fn {Function} The function to execute
         * @param obj <Object> An object to be passed along when the event fires. 
         * Defaults to the custom event.
         * @param override <boolean|Object> If true, the obj passed in becomes the 
         * execution context of the listener. If an object, that object becomes 
         * the execution context. Defaults to the custom event.
         */
        this.subscribeEvent = 
                new YAHOO.util.CustomEvent(onsubscribeType, this, true);

    } 


    /**
     * In order to make it possible to execute the rest of the subscriber
     * stack when one thows an exception, the subscribers exceptions are
     * caught.  The most recent exception is stored in this property
     * @property lastError
     * @type Error
     */
    this.lastError = null;
};

/**
 * Subscriber listener sigature constant.  The LIST type returns three
 * parameters: the event type, the array of args passed to fire, and
 * the optional custom object
 * @property YAHOO.util.CustomEvent.LIST
 * @static
 * @type int
 */
YAHOO.util.CustomEvent.LIST = 0;

/**
 * Subscriber listener sigature constant.  The FLAT type returns two
 * parameters: the first argument passed to fire and the optional 
 * custom object
 * @property YAHOO.util.CustomEvent.FLAT
 * @static
 * @type int
 */
YAHOO.util.CustomEvent.FLAT = 1;

YAHOO.util.CustomEvent.prototype = {

    /**
     * Subscribes the caller to this event
     * @method subscribe
     * @param {Function} fn        The function to execute
     * @param {Object}   obj       An object to be passed along when the event fires.
     * overrideContext <boolean|Object> If true, the obj passed in becomes the execution 
     * context of the listener. If an object, that object becomes the execution context.
     */
    subscribe: function(fn, obj, overrideContext) {

        if (!fn) {
throw new Error("Invalid callback for subscriber to '" + this.type + "'");
        }

        if (this.subscribeEvent) {
            this.subscribeEvent.fire(fn, obj, overrideContext);
        }

        var s = new YAHOO.util.Subscriber(fn, obj, overrideContext);

        if (this.fireOnce && this.fired) {
            this.notify(s, this.firedWith);
        } else {
            this.subscribers.push(s);
        }
    },

    /**
     * Unsubscribes subscribers.
     * @method unsubscribe
     * @param {Function} fn  The subscribed function to remove, if not supplied
     *                       all will be removed
     * @param {Object}   obj  The custom object passed to subscribe.  This is
     *                        optional, but if supplied will be used to
     *                        disambiguate multiple listeners that are the same
     *                        (e.g., you subscribe many object using a function
     *                        that lives on the prototype)
     * @return {boolean} True if the subscriber was found and detached.
     */
    unsubscribe: function(fn, obj) {

        if (!fn) {
            return this.unsubscribeAll();
        }

        var found = false;
        for (var i=0, len=this.subscribers.length; i<len; ++i) {
            var s = this.subscribers[i];
            if (s && s.contains(fn, obj)) {
                this._delete(i);
                found = true;
            }
        }

        return found;
    },

    /**
     * Notifies the subscribers.  The callback functions will be executed
     * from the context specified when the event was created, and with the 
     * following parameters:
     *   <ul>
     *   <li>The type of event</li>
     *   <li>All of the arguments fire() was executed with as an array</li>
     *   <li>The custom object (if any) that was passed into the subscribe() 
     *       method</li>
     *   </ul>
     * @method fire 
     * @param {Object*} arguments an arbitrary set of parameters to pass to 
     *                            the handler.
     * @return {boolean} false if one of the subscribers returned false, 
     *                   true otherwise
     */
    fire: function() {

        this.lastError = null;

        var errors = [],
            len=this.subscribers.length;


        var args=[].slice.call(arguments, 0), ret=true, i, rebuild=false;

        if (this.fireOnce) {
            if (this.fired) {
                return true;
            } else {
                this.firedWith = args;
            }
        }

        this.fired = true;

        if (!len && this.silent) {
            return true;
        }

        if (!this.silent) {
        }

        // make a copy of the subscribers so that there are
        // no index problems if one subscriber removes another.
        var subs = this.subscribers.slice();

        for (i=0; i<len; ++i) {
            var s = subs[i];
            if (!s) {
                rebuild=true;
            } else {

                ret = this.notify(s, args);

                if (false === ret) {
                    if (!this.silent) {
                    }

                    break;
                }
            }
        }

        return (ret !== false);
    },

    notify: function(s, args) {

        var ret, param=null, scope = s.getScope(this.scope),
                 throwErrors = YAHOO.util.Event.throwErrors;

        if (!this.silent) {
        }

        if (this.signature == YAHOO.util.CustomEvent.FLAT) {

            if (args.length > 0) {
                param = args[0];
            }

            try {
                ret = s.fn.call(scope, param, s.obj);
            } catch(e) {
                this.lastError = e;
                // errors.push(e);
                if (throwErrors) {
                    throw e;
                }
            }
        } else {
            try {
                ret = s.fn.call(scope, this.type, args, s.obj);
            } catch(ex) {
                this.lastError = ex;
                if (throwErrors) {
                    throw ex;
                }
            }
        }

        return ret;
    },

    /**
     * Removes all listeners
     * @method unsubscribeAll
     * @return {int} The number of listeners unsubscribed
     */
    unsubscribeAll: function() {
        var l = this.subscribers.length, i;
        for (i=l-1; i>-1; i--) {
            this._delete(i);
        }

        this.subscribers=[];

        return l;
    },

    /**
     * @method _delete
     * @private
     */
    _delete: function(index) {
        var s = this.subscribers[index];
        if (s) {
            delete s.fn;
            delete s.obj;
        }

        // this.subscribers[index]=null;
        this.subscribers.splice(index, 1);
    },

    /**
     * @method toString
     */
    toString: function() {
         return "CustomEvent: " + "'" + this.type  + "', " + 
             "context: " + this.scope;

    }
};

/////////////////////////////////////////////////////////////////////

/**
 * Stores the subscriber information to be used when the event fires.
 * @param {Function} fn       The function to execute
 * @param {Object}   obj      An object to be passed along when the event fires
 * @param {boolean}  overrideContext If true, the obj passed in becomes the execution
 *                            context of the listener
 * @class Subscriber
 * @constructor
 */
YAHOO.util.Subscriber = function(fn, obj, overrideContext) {

    /**
     * The callback that will be execute when the event fires
     * @property fn
     * @type function
     */
    this.fn = fn;

    /**
     * An optional custom object that will passed to the callback when
     * the event fires
     * @property obj
     * @type object
     */
    this.obj = YAHOO.lang.isUndefined(obj) ? null : obj;

    /**
     * The default execution context for the event listener is defined when the
     * event is created (usually the object which contains the event).
     * By setting overrideContext to true, the execution context becomes the custom
     * object passed in by the subscriber.  If overrideContext is an object, that 
     * object becomes the context.
     * @property overrideContext
     * @type boolean|object
     */
    this.overrideContext = overrideContext;

};

/**
 * Returns the execution context for this listener.  If overrideContext was set to true
 * the custom obj will be the context.  If overrideContext is an object, that is the
 * context, otherwise the default context will be used.
 * @method getScope
 * @param {Object} defaultScope the context to use if this listener does not
 *                              override it.
 */
YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {
    if (this.overrideContext) {
        if (this.overrideContext === true) {
            return this.obj;
        } else {
            return this.overrideContext;
        }
    }
    return defaultScope;
};

/**
 * Returns true if the fn and obj match this objects properties.
 * Used by the unsubscribe method to match the right subscriber.
 *
 * @method contains
 * @param {Function} fn the function to execute
 * @param {Object} obj an object to be passed along when the event fires
 * @return {boolean} true if the supplied arguments match this 
 *                   subscriber's signature.
 */
YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
    if (obj) {
        return (this.fn == fn && this.obj == obj);
    } else {
        return (this.fn == fn);
    }
};

/**
 * @method toString
 */
YAHOO.util.Subscriber.prototype.toString = function() {
    return "Subscriber { obj: " + this.obj  + 
           ", overrideContext: " +  (this.overrideContext || "no") + " }";
};

/**
 * The Event Utility provides utilities for managing DOM Events and tools
 * for building event systems
 *
 * @module event
 * @title Event Utility
 * @namespace YAHOO.util
 * @requires yahoo
 */

// The first instance of Event will win if it is loaded more than once.
// @TODO this needs to be changed so that only the state data that needs to
// be preserved is kept, while methods are overwritten/added as needed.
// This means that the module pattern can't be used.
if (!YAHOO.util.Event) {

/**
 * The event utility provides functions to add and remove event listeners,
 * event cleansing.  It also tries to automatically remove listeners it
 * registers during the unload event.
 *
 * @class Event
 * @static
 */
    YAHOO.util.Event = function() {

        /**
         * True after the onload event has fired
         * @property loadComplete
         * @type boolean
         * @static
         * @private
         */
        var loadComplete =  false,

        /**
         * Cache of wrapped listeners
         * @property listeners
         * @type array
         * @static
         * @private
         */
        listeners = [],


        /**
         * User-defined unload function that will be fired before all events
         * are detached
         * @property unloadListeners
         * @type array
         * @static
         * @private
         */
        unloadListeners = [],

        /**
         * The number of times to poll after window.onload.  This number is
         * increased if additional late-bound handlers are requested after
         * the page load.
         * @property retryCount
         * @static
         * @private
         */
        retryCount = 0,

        /**
         * onAvailable listeners
         * @property onAvailStack
         * @static
         * @private
         */
        onAvailStack = [],

        /**
         * Counter for auto id generation
         * @property counter
         * @static
         * @private
         */
        counter = 0,
        
        /**
         * Normalized keycodes for webkit/safari
         * @property webkitKeymap
         * @type {int: int}
         * @private
         * @static
         * @final
         */
         webkitKeymap = {
            63232: 38, // up
            63233: 40, // down
            63234: 37, // left
            63235: 39, // right
            63276: 33, // page up
            63277: 34, // page down
            25: 9      // SHIFT-TAB (Safari provides a different key code in
                       // this case, even though the shiftKey modifier is set)
        },

		isIE = YAHOO.env.ua.ie,

        // String constants used by the addFocusListener and removeFocusListener methods
		
       	FOCUSIN = "focusin",
       	FOCUSOUT = "focusout";

        return {

            /**
             * The number of times we should look for elements that are not
             * in the DOM at the time the event is requested after the document
             * has been loaded.  The default is 500@amp;40 ms, so it will poll
             * for 20 seconds or until all outstanding handlers are bound
             * (whichever comes first).
             * @property POLL_RETRYS
             * @type int
             * @static
             * @final
             */
            POLL_RETRYS: 500,

            /**
             * The poll interval in milliseconds
             * @property POLL_INTERVAL
             * @type int
             * @static
             * @final
             */
            POLL_INTERVAL: 40,

            /**
             * Element to bind, int constant
             * @property EL
             * @type int
             * @static
             * @final
             */
            EL: 0,

            /**
             * Type of event, int constant
             * @property TYPE
             * @type int
             * @static
             * @final
             */
            TYPE: 1,

            /**
             * Function to execute, int constant
             * @property FN
             * @type int
             * @static
             * @final
             */
            FN: 2,

            /**
             * Function wrapped for context correction and cleanup, int constant
             * @property WFN
             * @type int
             * @static
             * @final
             */
            WFN: 3,

            /**
             * Object passed in by the user that will be returned as a 
             * parameter to the callback, int constant.  Specific to
             * unload listeners
             * @property OBJ
             * @type int
             * @static
             * @final
             */
            UNLOAD_OBJ: 3,

            /**
             * Adjusted context, either the element we are registering the event
             * on or the custom object passed in by the listener, int constant
             * @property ADJ_SCOPE
             * @type int
             * @static
             * @final
             */
            ADJ_SCOPE: 4,

            /**
             * The original obj passed into addListener
             * @property OBJ
             * @type int
             * @static
             * @final
             */
            OBJ: 5,

            /**
             * The original context parameter passed into addListener
             * @property OVERRIDE
             * @type int
             * @static
             * @final
             */
            OVERRIDE: 6,

            /**
             * The original capture parameter passed into addListener
             * @property CAPTURE
             * @type int
             * @static
             * @final
             */
			CAPTURE: 7,

            /**
             * addListener/removeListener can throw errors in unexpected scenarios.
             * These errors are suppressed, the method returns false, and this property
             * is set
             * @property lastError
             * @static
             * @type Error
             */
            lastError: null,

            /**
             * Safari detection
             * @property isSafari
             * @private
             * @static
             * @deprecated use YAHOO.env.ua.webkit
             */
            isSafari: YAHOO.env.ua.webkit,
            
            /**
             * webkit version
             * @property webkit
             * @type string
             * @private
             * @static
             * @deprecated use YAHOO.env.ua.webkit
             */
            webkit: YAHOO.env.ua.webkit,
            
            /**
             * IE detection 
             * @property isIE
             * @private
             * @static
             * @deprecated use YAHOO.env.ua.ie
             */
            isIE: isIE,

            /**
             * poll handle
             * @property _interval
             * @static
             * @private
             */
            _interval: null,

            /**
             * document readystate poll handle
             * @property _dri
             * @static
             * @private
             */
             _dri: null,


            /**
             * Map of special event types
             * @property _specialTypes
             * @static
             * @private
             */
			_specialTypes: {
				focusin: (isIE ? "focusin" : "focus"),
				focusout: (isIE ? "focusout" : "blur")
			},


            /**
             * True when the document is initially usable
             * @property DOMReady
             * @type boolean
             * @static
             */
            DOMReady: false,

            /**
             * Errors thrown by subscribers of custom events are caught
             * and the error message is written to the debug console.  If
             * this property is set to true, it will also re-throw the
             * error.
             * @property throwErrors
             * @type boolean
             * @default false
             */
            throwErrors: false,


            /**
             * @method startInterval
             * @static
             * @private
             */
            startInterval: function() {
                if (!this._interval) {
                    // var self = this;
                    // var callback = function() { self._tryPreloadAttach(); };
                    // this._interval = setInterval(callback, this.POLL_INTERVAL);
                    this._interval = YAHOO.lang.later(this.POLL_INTERVAL, this, this._tryPreloadAttach, null, true);
                }
            },

            /**
             * Executes the supplied callback when the item with the supplied
             * id is found.  This is meant to be used to execute behavior as
             * soon as possible as the page loads.  If you use this after the
             * initial page load it will poll for a fixed time for the element.
             * The number of times it will poll and the frequency are
             * configurable.  By default it will poll for 10 seconds.
             *
             * <p>The callback is executed with a single parameter:
             * the custom object parameter, if provided.</p>
             *
             * @method onAvailable
             *
             * @param {string||string[]}   id the id of the element, or an array
             * of ids to look for.
             * @param {function} fn what to execute when the element is found.
             * @param {object}   obj an optional object to be passed back as
             *                   a parameter to fn.
             * @param {boolean|object}  overrideContext If set to true, fn will execute
             *                   in the context of obj, if set to an object it
             *                   will execute in the context of that object
             * @param checkContent {boolean} check child node readiness (onContentReady)
             * @static
             */
            onAvailable: function(id, fn, obj, overrideContext, checkContent) {

                var a = (YAHOO.lang.isString(id)) ? [id] : id;

                for (var i=0; i<a.length; i=i+1) {
                    onAvailStack.push({id:         a[i], 
                                       fn:         fn, 
                                       obj:        obj, 
                                       overrideContext:   overrideContext, 
                                       checkReady: checkContent });
                }

                retryCount = this.POLL_RETRYS;

                this.startInterval();
            },

            /**
             * Works the same way as onAvailable, but additionally checks the
             * state of sibling elements to determine if the content of the
             * available element is safe to modify.
             *
             * <p>The callback is executed with a single parameter:
             * the custom object parameter, if provided.</p>
             *
             * @method onContentReady
             *
             * @param {string}   id the id of the element to look for.
             * @param {function} fn what to execute when the element is ready.
             * @param {object}   obj an optional object to be passed back as
             *                   a parameter to fn.
             * @param {boolean|object}  overrideContext If set to true, fn will execute
             *                   in the context of obj.  If an object, fn will
             *                   exectute in the context of that object
             *
             * @static
             */
            onContentReady: function(id, fn, obj, overrideContext) {
                this.onAvailable(id, fn, obj, overrideContext, true);
            },

            /**
             * Executes the supplied callback when the DOM is first usable.  This
             * will execute immediately if called after the DOMReady event has
             * fired.   @todo the DOMContentReady event does not fire when the
             * script is dynamically injected into the page.  This means the
             * DOMReady custom event will never fire in FireFox or Opera when the
             * library is injected.  It _will_ fire in Safari, and the IE 
             * implementation would allow for us to fire it if the defered script
             * is not available.  We want this to behave the same in all browsers.
             * Is there a way to identify when the script has been injected 
             * instead of included inline?  Is there a way to know whether the 
             * window onload event has fired without having had a listener attached 
             * to it when it did so?
             *
             * <p>The callback is a CustomEvent, so the signature is:</p>
             * <p>type &lt;string&gt;, args &lt;array&gt;, customobject &lt;object&gt;</p>
             * <p>For DOMReady events, there are no fire argments, so the
             * signature is:</p>
             * <p>"DOMReady", [], obj</p>
             *
             *
             * @method onDOMReady
             *
             * @param {function} fn what to execute when the element is found.
             * @param {object}   obj an optional object to be passed back as
             *                   a parameter to fn.
             * @param {boolean|object}  overrideContext If set to true, fn will execute
             *                   in the context of obj, if set to an object it
             *                   will execute in the context of that object
             *
             * @static
             */
            // onDOMReady: function(fn, obj, overrideContext) {
            onDOMReady: function() {
                this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent, arguments);
            },


            /**
             * Appends an event handler
             *
             * @method _addListener
             *
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
             *  reference, or a collection of ids and/or elements to assign the 
             *  listener to.
             * @param {String}   sType     The type of event to append
             * @param {Function} fn        The method the event invokes
             * @param {Object}   obj    An arbitrary object that will be 
             *                             passed as a parameter to the handler
             * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
             *                             the execution context of the listener. If an
             *                             object, this object becomes the execution
             *                             context.
             * @param {boolen}      capture capture or bubble phase
             * @return {Boolean} True if the action was successful or defered,
             *                        false if one or more of the elements 
             *                        could not have the listener attached,
             *                        or if the operation throws an exception.
             * @private
             * @static
             */
            _addListener: function(el, sType, fn, obj, overrideContext, bCapture) {

                if (!fn || !fn.call) {
                    return false;
                }

                // The el argument can be an array of elements or element ids.
                if ( this._isValidCollection(el)) {
                    var ok = true;
                    for (var i=0,len=el.length; i<len; ++i) {
                        ok = this.on(el[i], 
                                       sType, 
                                       fn, 
                                       obj, 
                                       overrideContext) && ok;
                    }
                    return ok;

                } else if (YAHOO.lang.isString(el)) {
                    var oEl = this.getEl(el);
                    // If the el argument is a string, we assume it is 
                    // actually the id of the element.  If the page is loaded
                    // we convert el to the actual element, otherwise we 
                    // defer attaching the event until onload event fires

                    // check to see if we need to delay hooking up the event 
                    // until after the page loads.
                    if (oEl) {
                        el = oEl;
                    } else {
                        // defer adding the event until the element is available
                        this.onAvailable(el, function() {
                           YAHOO.util.Event._addListener(el, sType, fn, obj, overrideContext, bCapture);
                        });

                        return true;
                    }
                }

                // Element should be an html element or an array if we get 
                // here.
                if (!el) {
                    return false;
                }

                // we need to make sure we fire registered unload events 
                // prior to automatically unhooking them.  So we hang on to 
                // these instead of attaching them to the window and fire the
                // handles explicitly during our one unload event.
                if ("unload" == sType && obj !== this) {
                    unloadListeners[unloadListeners.length] =
                            [el, sType, fn, obj, overrideContext];
                    return true;
                }


                // if the user chooses to override the context, we use the custom
                // object passed in, otherwise the executing context will be the
                // HTML element that the event is registered on
                var context = el;
                if (overrideContext) {
                    if (overrideContext === true) {
                        context = obj;
                    } else {
                        context = overrideContext;
                    }
                }

                // wrap the function so we can return the obj object when
                // the event fires;
                var wrappedFn = function(e) {
                        return fn.call(context, YAHOO.util.Event.getEvent(e, el), 
                                obj);
                    };

                var li = [el, sType, fn, wrappedFn, context, obj, overrideContext, bCapture];
                var index = listeners.length;
                // cache the listener so we can try to automatically unload
                listeners[index] = li;

                try {
                    this._simpleAdd(el, sType, wrappedFn, bCapture);
                } catch(ex) {
                    // handle an error trying to attach an event.  If it fails
                    // we need to clean up the cache
                    this.lastError = ex;
                    this.removeListener(el, sType, fn);
                    return false;
                }

                return true;
                
            },

            /**
             * Checks to see if the type requested is a special type 
			 * (as defined by the _specialTypes hash), and (if so) returns 
			 * the special type name.
             *
             * @method _getType
             *
             * @param {String}   sType     The type to look up
             * @private
             */
			_getType: function (type) {
			
				return this._specialTypes[type] || type;
				
			},


            /**
             * Appends an event handler
             *
             * @method addListener
             *
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
             *  reference, or a collection of ids and/or elements to assign the 
             *  listener to.
             * @param {String}   sType     The type of event to append
             * @param {Function} fn        The method the event invokes
             * @param {Object}   obj    An arbitrary object that will be 
             *                             passed as a parameter to the handler
             * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
             *                             the execution context of the listener. If an
             *                             object, this object becomes the execution
             *                             context.
             * @return {Boolean} True if the action was successful or defered,
             *                        false if one or more of the elements 
             *                        could not have the listener attached,
             *                        or if the operation throws an exception.
             * @static
             */
            addListener: function (el, sType, fn, obj, overrideContext) {

				var capture = ((sType == FOCUSIN || sType == FOCUSOUT) && !YAHOO.env.ua.ie) ? true : false;

                return this._addListener(el, this._getType(sType), fn, obj, overrideContext, capture);

        	},


            /**
             * Attaches a focusin event listener to the specified element for 
 			 * the purpose of listening for the focus event on the element's 
             * descendants.
             * @method addFocusListener
             *
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
             *  reference, or a collection of ids and/or elements to assign the 
             *  listener to.
             * @param {Function} fn        The method the event invokes
             * @param {Object}   obj    An arbitrary object that will be 
             *                             passed as a parameter to the handler
             * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
             *                             the execution context of the listener. If an
             *                             object, this object becomes the execution
             *                             context.
             * @return {Boolean} True if the action was successful or defered,
             *                        false if one or more of the elements 
             *                        could not have the listener attached,
             *                        or if the operation throws an exception.
             * @static
			* @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type.
             */
            addFocusListener: function (el, fn, obj, overrideContext) {
                return this.on(el, FOCUSIN, fn, obj, overrideContext);
            },          


            /**
             * Removes a focusin event listener to the specified element for 
			 * the purpose of listening for the focus event on the element's 
             * descendants.
             *
             * @method removeFocusListener
             *
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
             *  reference, or a collection of ids and/or elements to remove
             *  the listener from.
             * @param {Function} fn the method the event invokes.  If fn is
             *  undefined, then all event handlers for the type of event are 
             *  removed.
             * @return {boolean} true if the unbind was successful, false 
             *  otherwise.
             * @static
         	 * @deprecated use YAHOO.util.Event.removeListener and specify "focusin" as the event type.
             */
            removeFocusListener: function (el, fn) { 
                return this.removeListener(el, FOCUSIN, fn);
            },

            /**
             * Attaches a focusout event listener to the specified element for 
			 * the purpose of listening for the blur event on the element's 
			 * descendants.
             *
             * @method addBlurListener
             *
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
             *  reference, or a collection of ids and/or elements to assign the 
             *  listener to.
             * @param {Function} fn        The method the event invokes
             * @param {Object}   obj    An arbitrary object that will be 
             *                             passed as a parameter to the handler
             * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
             *                             the execution context of the listener. If an
             *                             object, this object becomes the execution
             *                             context.
             * @return {Boolean} True if the action was successful or defered,
             *                        false if one or more of the elements 
             *                        could not have the listener attached,
             *                        or if the operation throws an exception.
             * @static
         	 * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type.
             */
            addBlurListener: function (el, fn, obj, overrideContext) {
                return this.on(el, FOCUSOUT, fn, obj, overrideContext);
            },          

            /**
             * Removes a focusout event listener to the specified element for 
			 * the purpose of listening for the blur event on the element's 
			 * descendants.
             *
             * @method removeBlurListener
             *
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
             *  reference, or a collection of ids and/or elements to remove
             *  the listener from.
             * @param {Function} fn the method the event invokes.  If fn is
             *  undefined, then all event handlers for the type of event are 
             *  removed.
             * @return {boolean} true if the unbind was successful, false 
             *  otherwise.
             * @static
         	 * @deprecated use YAHOO.util.Event.removeListener and specify "focusout" as the event type.
             */
            removeBlurListener: function (el, fn) { 
                return this.removeListener(el, FOCUSOUT, fn);
            },

            /**
             * Removes an event listener
             *
             * @method removeListener
             *
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
             *  reference, or a collection of ids and/or elements to remove
             *  the listener from.
             * @param {String} sType the type of event to remove.
             * @param {Function} fn the method the event invokes.  If fn is
             *  undefined, then all event handlers for the type of event are 
             *  removed.
             * @return {boolean} true if the unbind was successful, false 
             *  otherwise.
             * @static
             */
            removeListener: function(el, sType, fn) {
                var i, len, li;

				sType = this._getType(sType);

                // The el argument can be a string
                if (typeof el == "string") {
                    el = this.getEl(el);
                // The el argument can be an array of elements or element ids.
                } else if ( this._isValidCollection(el)) {
                    var ok = true;
                    for (i=el.length-1; i>-1; i--) {
                        ok = ( this.removeListener(el[i], sType, fn) && ok );
                    }
                    return ok;
                }

                if (!fn || !fn.call) {
                    //return false;
                    return this.purgeElement(el, false, sType);
                }

                if ("unload" == sType) {

                    for (i=unloadListeners.length-1; i>-1; i--) {
                        li = unloadListeners[i];
                        if (li && 
                            li[0] == el && 
                            li[1] == sType && 
                            li[2] == fn) {
                                unloadListeners.splice(i, 1);
                                // unloadListeners[i]=null;
                                return true;
                        }
                    }

                    return false;
                }

                var cacheItem = null;

                // The index is a hidden parameter; needed to remove it from
                // the method signature because it was tempting users to
                // try and take advantage of it, which is not possible.
                var index = arguments[3];
  
                if ("undefined" === typeof index) {
                    index = this._getCacheIndex(listeners, el, sType, fn);
                }

                if (index >= 0) {
                    cacheItem = listeners[index];
                }

                if (!el || !cacheItem) {
                    return false;
                }


				var bCapture = cacheItem[this.CAPTURE] === true ? true : false;

                try {
                    this._simpleRemove(el, sType, cacheItem[this.WFN], bCapture);
                } catch(ex) {
                    this.lastError = ex;
                    return false;
                }

                // removed the wrapped handler
                delete listeners[index][this.WFN];
                delete listeners[index][this.FN];
                listeners.splice(index, 1);
                // listeners[index]=null;

                return true;

            },

            /**
             * Returns the event's target element.  Safari sometimes provides
             * a text node, and this is automatically resolved to the text
             * node's parent so that it behaves like other browsers.
             * @method getTarget
             * @param {Event} ev the event
             * @param {boolean} resolveTextNode when set to true the target's
             *                  parent will be returned if the target is a 
             *                  text node.  @deprecated, the text node is
             *                  now resolved automatically
             * @return {HTMLElement} the event's target
             * @static
             */
            getTarget: function(ev, resolveTextNode) {
                var t = ev.target || ev.srcElement;
                return this.resolveTextNode(t);
            },

            /**
             * In some cases, some browsers will return a text node inside
             * the actual element that was targeted.  This normalizes the
             * return value for getTarget and getRelatedTarget.
             * @method resolveTextNode
             * @param {HTMLElement} node node to resolve
             * @return {HTMLElement} the normized node
             * @static
             */
            resolveTextNode: function(n) {
                try {
                    if (n && 3 == n.nodeType) {
                        return n.parentNode;
                    }
                } catch(e) { }

                return n;
            },

            /**
             * Returns the event's pageX
             * @method getPageX
             * @param {Event} ev the event
             * @return {int} the event's pageX
             * @static
             */
            getPageX: function(ev) {
                var x = ev.pageX;
                if (!x && 0 !== x) {
                    x = ev.clientX || 0;

                    if ( this.isIE ) {
                        x += this._getScrollLeft();
                    }
                }

                return x;
            },

            /**
             * Returns the event's pageY
             * @method getPageY
             * @param {Event} ev the event
             * @return {int} the event's pageY
             * @static
             */
            getPageY: function(ev) {
                var y = ev.pageY;
                if (!y && 0 !== y) {
                    y = ev.clientY || 0;

                    if ( this.isIE ) {
                        y += this._getScrollTop();
                    }
                }


                return y;
            },

            /**
             * Returns the pageX and pageY properties as an indexed array.
             * @method getXY
             * @param {Event} ev the event
             * @return {[x, y]} the pageX and pageY properties of the event
             * @static
             */
            getXY: function(ev) {
                return [this.getPageX(ev), this.getPageY(ev)];
            },

            /**
             * Returns the event's related target 
             * @method getRelatedTarget
             * @param {Event} ev the event
             * @return {HTMLElement} the event's relatedTarget
             * @static
             */
            getRelatedTarget: function(ev) {
                var t = ev.relatedTarget;
                if (!t) {
                    if (ev.type == "mouseout") {
                        t = ev.toElement;
                    } else if (ev.type == "mouseover") {
                        t = ev.fromElement;
                    }
                }

                return this.resolveTextNode(t);
            },

            /**
             * Returns the time of the event.  If the time is not included, the
             * event is modified using the current time.
             * @method getTime
             * @param {Event} ev the event
             * @return {Date} the time of the event
             * @static
             */
            getTime: function(ev) {
                if (!ev.time) {
                    var t = new Date().getTime();
                    try {
                        ev.time = t;
                    } catch(ex) { 
                        this.lastError = ex;
                        return t;
                    }
                }

                return ev.time;
            },

            /**
             * Convenience method for stopPropagation + preventDefault
             * @method stopEvent
             * @param {Event} ev the event
             * @static
             */
            stopEvent: function(ev) {
                this.stopPropagation(ev);
                this.preventDefault(ev);
            },

            /**
             * Stops event propagation
             * @method stopPropagation
             * @param {Event} ev the event
             * @static
             */
            stopPropagation: function(ev) {
                if (ev.stopPropagation) {
                    ev.stopPropagation();
                } else {
                    ev.cancelBubble = true;
                }
            },

            /**
             * Prevents the default behavior of the event
             * @method preventDefault
             * @param {Event} ev the event
             * @static
             */
            preventDefault: function(ev) {
                if (ev.preventDefault) {
                    ev.preventDefault();
                } else {
                    ev.returnValue = false;
                }
            },
             
            /**
             * Finds the event in the window object, the caller's arguments, or
             * in the arguments of another method in the callstack.  This is
             * executed automatically for events registered through the event
             * manager, so the implementer should not normally need to execute
             * this function at all.
             * @method getEvent
             * @param {Event} e the event parameter from the handler
             * @param {HTMLElement} boundEl the element the listener is attached to
             * @return {Event} the event 
             * @static
             */
            getEvent: function(e, boundEl) {
                var ev = e || window.event;

                if (!ev) {
                    var c = this.getEvent.caller;
                    while (c) {
                        ev = c.arguments[0];
                        if (ev && Event == ev.constructor) {
                            break;
                        }
                        c = c.caller;
                    }
                }

                return ev;
            },

            /**
             * Returns the charcode for an event
             * @method getCharCode
             * @param {Event} ev the event
             * @return {int} the event's charCode
             * @static
             */
            getCharCode: function(ev) {
                var code = ev.keyCode || ev.charCode || 0;

                // webkit key normalization
                if (YAHOO.env.ua.webkit && (code in webkitKeymap)) {
                    code = webkitKeymap[code];
                }
                return code;
            },

            /**
             * Locating the saved event handler data by function ref
             *
             * @method _getCacheIndex
             * @static
             * @private
             */
            _getCacheIndex: function(a, el, sType, fn) {
                for (var i=0, l=a.length; i<l; i=i+1) {
                    var li = a[i];
                    if ( li                 && 
                         li[this.FN] == fn  && 
                         li[this.EL] == el  && 
                         li[this.TYPE] == sType ) {
                        return i;
                    }
                }

                return -1;
            },

            /**
             * Generates an unique ID for the element if it does not already 
             * have one.
             * @method generateId
             * @param el the element to create the id for
             * @return {string} the resulting id of the element
             * @static
             */
            generateId: function(el) {
                var id = el.id;

                if (!id) {
                    id = "yuievtautoid-" + counter;
                    ++counter;
                    el.id = id;
                }

                return id;
            },


            /**
             * We want to be able to use getElementsByTagName as a collection
             * to attach a group of events to.  Unfortunately, different 
             * browsers return different types of collections.  This function
             * tests to determine if the object is array-like.  It will also 
             * fail if the object is an array, but is empty.
             * @method _isValidCollection
             * @param o the object to test
             * @return {boolean} true if the object is array-like and populated
             * @static
             * @private
             */
            _isValidCollection: function(o) {
                try {
                    return ( o                     && // o is something
                             typeof o !== "string" && // o is not a string
                             o.length              && // o is indexed
                             !o.tagName            && // o is not an HTML element
                             !o.alert              && // o is not a window
                             typeof o[0] !== "undefined" );
                } catch(ex) {
                    return false;
                }

            },

            /**
             * @private
             * @property elCache
             * DOM element cache
             * @static
             * @deprecated Elements are not cached due to issues that arise when
             * elements are removed and re-added
             */
            elCache: {},

            /**
             * We cache elements bound by id because when the unload event 
             * fires, we can no longer use document.getElementById
             * @method getEl
             * @static
             * @private
             * @deprecated Elements are not cached any longer
             */
            getEl: function(id) {
                return (typeof id === "string") ? document.getElementById(id) : id;
            },

            /**
             * Clears the element cache
             * @deprecated Elements are not cached any longer
             * @method clearCache
             * @static
             * @private
             */
            clearCache: function() { },

            /**
             * Custom event the fires when the dom is initially usable
             * @event DOMReadyEvent
             */
            DOMReadyEvent: new YAHOO.util.CustomEvent("DOMReady", YAHOO, 0, 0, 1),

            /**
             * hook up any deferred listeners
             * @method _load
             * @static
             * @private
             */
            _load: function(e) {

                if (!loadComplete) {
                    loadComplete = true;
                    var EU = YAHOO.util.Event;

                    // Just in case DOMReady did not go off for some reason
                    EU._ready();

                    // Available elements may not have been detected before the
                    // window load event fires. Try to find them now so that the
                    // the user is more likely to get the onAvailable notifications
                    // before the window load notification
                    EU._tryPreloadAttach();

                }
            },

            /**
             * Fires the DOMReady event listeners the first time the document is
             * usable.
             * @method _ready
             * @static
             * @private
             */
            _ready: function(e) {
                var EU = YAHOO.util.Event;
                if (!EU.DOMReady) {
                    EU.DOMReady=true;

                    // Fire the content ready custom event
                    EU.DOMReadyEvent.fire();

                    // Remove the DOMContentLoaded (FF/Opera)
                    EU._simpleRemove(document, "DOMContentLoaded", EU._ready);
                }
            },

            /**
             * Polling function that runs before the onload event fires, 
             * attempting to attach to DOM Nodes as soon as they are 
             * available
             * @method _tryPreloadAttach
             * @static
             * @private
             */
            _tryPreloadAttach: function() {

                if (onAvailStack.length === 0) {
                    retryCount = 0;
                    if (this._interval) {
                        // clearInterval(this._interval);
                        this._interval.cancel();
                        this._interval = null;
                    } 
                    return;
                }

                if (this.locked) {
                    return;
                }

                if (this.isIE) {
                    // Hold off if DOMReady has not fired and check current
                    // readyState to protect against the IE operation aborted
                    // issue.
                    if (!this.DOMReady) {
                        this.startInterval();
                        return;
                    }
                }

                this.locked = true;


                // keep trying until after the page is loaded.  We need to 
                // check the page load state prior to trying to bind the 
                // elements so that we can be certain all elements have been 
                // tested appropriately
                var tryAgain = !loadComplete;
                if (!tryAgain) {
                    tryAgain = (retryCount > 0 && onAvailStack.length > 0);
                }

                // onAvailable
                var notAvail = [];

                var executeItem = function (el, item) {
                    var context = el;
                    if (item.overrideContext) {
                        if (item.overrideContext === true) {
                            context = item.obj;
                        } else {
                            context = item.overrideContext;
                        }
                    }
                    item.fn.call(context, item.obj);
                };

                var i, len, item, el, ready=[];

                // onAvailable onContentReady
                for (i=0, len=onAvailStack.length; i<len; i=i+1) {
                    item = onAvailStack[i];
                    if (item) {
                        el = this.getEl(item.id);
                        if (el) {
                            if (item.checkReady) {
                                if (loadComplete || el.nextSibling || !tryAgain) {
                                    ready.push(item);
                                    onAvailStack[i] = null;
                                }
                            } else {
                                executeItem(el, item);
                                onAvailStack[i] = null;
                            }
                        } else {
                            notAvail.push(item);
                        }
                    }
                }
                
                // make sure onContentReady fires after onAvailable
                for (i=0, len=ready.length; i<len; i=i+1) {
                    item = ready[i];
                    executeItem(this.getEl(item.id), item);
                }


                retryCount--;

                if (tryAgain) {
                    for (i=onAvailStack.length-1; i>-1; i--) {
                        item = onAvailStack[i];
                        if (!item || !item.id) {
                            onAvailStack.splice(i, 1);
                        }
                    }

                    this.startInterval();
                } else {
                    if (this._interval) {
                        // clearInterval(this._interval);
                        this._interval.cancel();
                        this._interval = null;
                    }
                }

                this.locked = false;

            },

            /**
             * Removes all listeners attached to the given element via addListener.
             * Optionally, the node's children can also be purged.
             * Optionally, you can specify a specific type of event to remove.
             * @method purgeElement
             * @param {HTMLElement} el the element to purge
             * @param {boolean} recurse recursively purge this element's children
             * as well.  Use with caution.
             * @param {string} sType optional type of listener to purge. If
             * left out, all listeners will be removed
             * @static
             */
            purgeElement: function(el, recurse, sType) {
                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
                var elListeners = this.getListeners(oEl, sType), i, len;
                if (elListeners) {
                    for (i=elListeners.length-1; i>-1; i--) {
                        var l = elListeners[i];
                        this.removeListener(oEl, l.type, l.fn);
                    }
                }

                if (recurse && oEl && oEl.childNodes) {
                    for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
                        this.purgeElement(oEl.childNodes[i], recurse, sType);
                    }
                }
            },

            /**
             * Returns all listeners attached to the given element via addListener.
             * Optionally, you can specify a specific type of event to return.
             * @method getListeners
             * @param el {HTMLElement|string} the element or element id to inspect 
             * @param sType {string} optional type of listener to return. If
             * left out, all listeners will be returned
             * @return {Object} the listener. Contains the following fields:
             * &nbsp;&nbsp;type:   (string)   the type of event
             * &nbsp;&nbsp;fn:     (function) the callback supplied to addListener
             * &nbsp;&nbsp;obj:    (object)   the custom object supplied to addListener
             * &nbsp;&nbsp;adjust: (boolean|object)  whether or not to adjust the default context
             * &nbsp;&nbsp;scope: (boolean)  the derived context based on the adjust parameter
             * &nbsp;&nbsp;index:  (int)      its position in the Event util listener cache
             * @static
             */           
            getListeners: function(el, sType) {
                var results=[], searchLists;
                if (!sType) {
                    searchLists = [listeners, unloadListeners];
                } else if (sType === "unload") {
                    searchLists = [unloadListeners];
                } else {
					sType = this._getType(sType);
                    searchLists = [listeners];
                }

                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;

                for (var j=0;j<searchLists.length; j=j+1) {
                    var searchList = searchLists[j];
                    if (searchList) {
                        for (var i=0,len=searchList.length; i<len ; ++i) {
                            var l = searchList[i];
                            if ( l  && l[this.EL] === oEl && 
                                    (!sType || sType === l[this.TYPE]) ) {
                                results.push({
                                    type:   l[this.TYPE],
                                    fn:     l[this.FN],
                                    obj:    l[this.OBJ],
                                    adjust: l[this.OVERRIDE],
                                    scope:  l[this.ADJ_SCOPE],
                                    index:  i
                                });
                            }
                        }
                    }
                }

                return (results.length) ? results : null;
            },

            /**
             * Removes all listeners registered by pe.event.  Called 
             * automatically during the unload event.
             * @method _unload
             * @static
             * @private
             */
            _unload: function(e) {

                var EU = YAHOO.util.Event, i, j, l, len, index,
                         ul = unloadListeners.slice(), context;

                // execute and clear stored unload listeners
                for (i=0, len=unloadListeners.length; i<len; ++i) {
                    l = ul[i];
                    if (l) {
                        context = window;
                        if (l[EU.ADJ_SCOPE]) {
                            if (l[EU.ADJ_SCOPE] === true) {
                                context = l[EU.UNLOAD_OBJ];
                            } else {
                                context = l[EU.ADJ_SCOPE];
                            }
                        }
                        l[EU.FN].call(context, EU.getEvent(e, l[EU.EL]), l[EU.UNLOAD_OBJ] );
                        ul[i] = null;
                    }
                }

                l = null;
                context = null;
                unloadListeners = null;

                // Remove listeners to handle IE memory leaks
                // 2.5.0 listeners are removed for all browsers again.  FireFox preserves
                // at least some listeners between page refreshes, potentially causing
                // errors during page load (mouseover listeners firing before they
                // should if the user moves the mouse at the correct moment).
                if (listeners) {
                    for (j=listeners.length-1; j>-1; j--) {
                        l = listeners[j];
                        if (l) {
                            EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], j);
                        } 
                    }
                    l=null;
                }

                EU._simpleRemove(window, "unload", EU._unload);

            },

            /**
             * Returns scrollLeft
             * @method _getScrollLeft
             * @static
             * @private
             */
            _getScrollLeft: function() {
                return this._getScroll()[1];
            },

            /**
             * Returns scrollTop
             * @method _getScrollTop
             * @static
             * @private
             */
            _getScrollTop: function() {
                return this._getScroll()[0];
            },

            /**
             * Returns the scrollTop and scrollLeft.  Used to calculate the 
             * pageX and pageY in Internet Explorer
             * @method _getScroll
             * @static
             * @private
             */
            _getScroll: function() {
                var dd = document.documentElement, db = document.body;
                if (dd && (dd.scrollTop || dd.scrollLeft)) {
                    return [dd.scrollTop, dd.scrollLeft];
                } else if (db) {
                    return [db.scrollTop, db.scrollLeft];
                } else {
                    return [0, 0];
                }
            },
            
            /**
             * Used by old versions of CustomEvent, restored for backwards
             * compatibility
             * @method regCE
             * @private
             * @static
             * @deprecated still here for backwards compatibility
             */
            regCE: function() {},

            /**
             * Adds a DOM event directly without the caching, cleanup, context adj, etc
             *
             * @method _simpleAdd
             * @param {HTMLElement} el      the element to bind the handler to
             * @param {string}      sType   the type of event handler
             * @param {function}    fn      the callback to invoke
             * @param {boolen}      capture capture or bubble phase
             * @static
             * @private
             */
            _simpleAdd: function () {
                if (window.addEventListener) {
                    return function(el, sType, fn, capture) {
                        el.addEventListener(sType, fn, (capture));
                    };
                } else if (window.attachEvent) {
                    return function(el, sType, fn, capture) {
                        el.attachEvent("on" + sType, fn);
                    };
                } else {
                    return function(){};
                }
            }(),

            /**
             * Basic remove listener
             *
             * @method _simpleRemove
             * @param {HTMLElement} el      the element to bind the handler to
             * @param {string}      sType   the type of event handler
             * @param {function}    fn      the callback to invoke
             * @param {boolen}      capture capture or bubble phase
             * @static
             * @private
             */
            _simpleRemove: function() {
                if (window.removeEventListener) {
                    return function (el, sType, fn, capture) {
                        el.removeEventListener(sType, fn, (capture));
                    };
                } else if (window.detachEvent) {
                    return function (el, sType, fn) {
                        el.detachEvent("on" + sType, fn);
                    };
                } else {
                    return function(){};
                }
            }()
        };

    }();

    (function() {
        var EU = YAHOO.util.Event;

        /**
         * YAHOO.util.Event.on is an alias for addListener
         * @method on
         * @see addListener
         * @static
         */
        EU.on = EU.addListener;

        /**
         * YAHOO.util.Event.onFocus is an alias for addFocusListener
         * @method onFocus
         * @see addFocusListener
         * @static
		 * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type.
         */
        EU.onFocus = EU.addFocusListener;

        /**
         * YAHOO.util.Event.onBlur is an alias for addBlurListener
         * @method onBlur
         * @see addBlurListener
         * @static
		 * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type.
         */     
        EU.onBlur = EU.addBlurListener;

/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */

        // Internet Explorer: use the readyState of a defered script.
        // This isolates what appears to be a safe moment to manipulate
        // the DOM prior to when the document's readyState suggests
        // it is safe to do so.
        if (EU.isIE) {
            if (self !== self.top) {
                document.onreadystatechange = function() {
                    if (document.readyState == 'complete') {
                        document.onreadystatechange = null;
                        EU._ready();
                    }
                };
            } else {

                // Process onAvailable/onContentReady items when the 
                // DOM is ready.
                YAHOO.util.Event.onDOMReady(
                        YAHOO.util.Event._tryPreloadAttach,
                        YAHOO.util.Event, true);
                
                var n = document.createElement('p');  

                EU._dri = setInterval(function() {
                    try {
                        // throws an error if doc is not ready
                        n.doScroll('left');
                        clearInterval(EU._dri);
                        EU._dri = null;
                        EU._ready();
                        n = null;
                    } catch (ex) { 
                    }
                }, EU.POLL_INTERVAL); 
            }

        // The document's readyState in Safari currently will
        // change to loaded/complete before images are loaded.
        } else if (EU.webkit && EU.webkit < 525) {

            EU._dri = setInterval(function() {
                var rs=document.readyState;
                if ("loaded" == rs || "complete" == rs) {
                    clearInterval(EU._dri);
                    EU._dri = null;
                    EU._ready();
                }
            }, EU.POLL_INTERVAL); 

        // FireFox and Opera: These browsers provide a event for this
        // moment.  The latest WebKit releases now support this event.
        } else {

            EU._simpleAdd(document, "DOMContentLoaded", EU._ready);

        }
        /////////////////////////////////////////////////////////////


        EU._simpleAdd(window, "load", EU._load);
        EU._simpleAdd(window, "unload", EU._unload);
        EU._tryPreloadAttach();
    })();

}
/**
 * EventProvider is designed to be used with YAHOO.augment to wrap 
 * CustomEvents in an interface that allows events to be subscribed to 
 * and fired by name.  This makes it possible for implementing code to
 * subscribe to an event that either has not been created yet, or will
 * not be created at all.
 *
 * @Class EventProvider
 */
YAHOO.util.EventProvider = function() { };

YAHOO.util.EventProvider.prototype = {

    /**
     * Private storage of custom events
     * @property __yui_events
     * @type Object[]
     * @private
     */
    __yui_events: null,

    /**
     * Private storage of custom event subscribers
     * @property __yui_subscribers
     * @type Object[]
     * @private
     */
    __yui_subscribers: null,
    
    /**
     * Subscribe to a CustomEvent by event type
     *
     * @method subscribe
     * @param p_type     {string}   the type, or name of the event
     * @param p_fn       {function} the function to exectute when the event fires
     * @param p_obj      {Object}   An object to be passed along when the event 
     *                              fires
     * @param overrideContext {boolean}  If true, the obj passed in becomes the 
     *                              execution scope of the listener
     */
    subscribe: function(p_type, p_fn, p_obj, overrideContext) {

        this.__yui_events = this.__yui_events || {};
        var ce = this.__yui_events[p_type];

        if (ce) {
            ce.subscribe(p_fn, p_obj, overrideContext);
        } else {
            this.__yui_subscribers = this.__yui_subscribers || {};
            var subs = this.__yui_subscribers;
            if (!subs[p_type]) {
                subs[p_type] = [];
            }
            subs[p_type].push(
                { fn: p_fn, obj: p_obj, overrideContext: overrideContext } );
        }
    },

    /**
     * Unsubscribes one or more listeners the from the specified event
     * @method unsubscribe
     * @param p_type {string}   The type, or name of the event.  If the type
     *                          is not specified, it will attempt to remove
     *                          the listener from all hosted events.
     * @param p_fn   {Function} The subscribed function to unsubscribe, if not
     *                          supplied, all subscribers will be removed.
     * @param p_obj  {Object}   The custom object passed to subscribe.  This is
     *                        optional, but if supplied will be used to
     *                        disambiguate multiple listeners that are the same
     *                        (e.g., you subscribe many object using a function
     *                        that lives on the prototype)
     * @return {boolean} true if the subscriber was found and detached.
     */
    unsubscribe: function(p_type, p_fn, p_obj) {
        this.__yui_events = this.__yui_events || {};
        var evts = this.__yui_events;
        if (p_type) {
            var ce = evts[p_type];
            if (ce) {
                return ce.unsubscribe(p_fn, p_obj);
            }
        } else {
            var ret = true;
            for (var i in evts) {
                if (YAHOO.lang.hasOwnProperty(evts, i)) {
                    ret = ret && evts[i].unsubscribe(p_fn, p_obj);
                }
            }
            return ret;
        }

        return false;
    },
    
    /**
     * Removes all listeners from the specified event.  If the event type
     * is not specified, all listeners from all hosted custom events will
     * be removed.
     * @method unsubscribeAll
     * @param p_type {string}   The type, or name of the event
     */
    unsubscribeAll: function(p_type) {
        return this.unsubscribe(p_type);
    },

    /**
     * Creates a new custom event of the specified type.  If a custom event
     * by that name already exists, it will not be re-created.  In either
     * case the custom event is returned. 
     *
     * @method createEvent
     *
     * @param p_type {string} the type, or name of the event
     * @param p_config {object} optional config params.  Valid properties are:
     *
     *  <ul>
     *    <li>
     *      scope: defines the default execution scope.  If not defined
     *      the default scope will be this instance.
     *    </li>
     *    <li>
     *      silent: if true, the custom event will not generate log messages.
     *      This is false by default.
     *    </li>
     *    <li>
     *      fireOnce: if true, the custom event will only notify subscribers
     *      once regardless of the number of times the event is fired.  In
     *      addition, new subscribers will be executed immediately if the
     *      event has already fired.
     *      This is false by default.
     *    </li>
     *    <li>
     *      onSubscribeCallback: specifies a callback to execute when the
     *      event has a new subscriber.  This will fire immediately for
     *      each queued subscriber if any exist prior to the creation of
     *      the event.
     *    </li>
     *  </ul>
     *
     *  @return {CustomEvent} the custom event
     *
     */
    createEvent: function(p_type, p_config) {

        this.__yui_events = this.__yui_events || {};
        var opts = p_config || {},
            events = this.__yui_events, ce;

        if (events[p_type]) {
        } else {

            ce = new YAHOO.util.CustomEvent(p_type, opts.scope || this, opts.silent,
                         YAHOO.util.CustomEvent.FLAT, opts.fireOnce);

            events[p_type] = ce;

            if (opts.onSubscribeCallback) {
                ce.subscribeEvent.subscribe(opts.onSubscribeCallback);
            }

            this.__yui_subscribers = this.__yui_subscribers || {};
            var qs = this.__yui_subscribers[p_type];

            if (qs) {
                for (var i=0; i<qs.length; ++i) {
                    ce.subscribe(qs[i].fn, qs[i].obj, qs[i].overrideContext);
                }
            }
        }

        return events[p_type];
    },


   /**
     * Fire a custom event by name.  The callback functions will be executed
     * from the scope specified when the event was created, and with the 
     * following parameters:
     *   <ul>
     *   <li>The first argument fire() was executed with</li>
     *   <li>The custom object (if any) that was passed into the subscribe() 
     *       method</li>
     *   </ul>
     * @method fireEvent
     * @param p_type    {string}  the type, or name of the event
     * @param arguments {Object*} an arbitrary set of parameters to pass to 
     *                            the handler.
     * @return {boolean} the return value from CustomEvent.fire
     *                   
     */
    fireEvent: function(p_type) {

        this.__yui_events = this.__yui_events || {};
        var ce = this.__yui_events[p_type];

        if (!ce) {
            return null;
        }

        var args = [];
        for (var i=1; i<arguments.length; ++i) {
            args.push(arguments[i]);
        }
        return ce.fire.apply(ce, args);
    },

    /**
     * Returns true if the custom event of the provided type has been created
     * with createEvent.
     * @method hasEvent
     * @param type {string} the type, or name of the event
     */
    hasEvent: function(type) {
        if (this.__yui_events) {
            if (this.__yui_events[type]) {
                return true;
            }
        }
        return false;
    }

};

(function() {

    var Event = YAHOO.util.Event, Lang = YAHOO.lang;

/**
* KeyListener is a utility that provides an easy interface for listening for
* keydown/keyup events fired against DOM elements.
* @namespace YAHOO.util
* @class KeyListener
* @constructor
* @param {HTMLElement} attachTo The element or element ID to which the key 
*                               event should be attached
* @param {String}      attachTo The element or element ID to which the key
*                               event should be attached
* @param {Object}      keyData  The object literal representing the key(s) 
*                               to detect. Possible attributes are 
*                               shift(boolean), alt(boolean), ctrl(boolean) 
*                               and keys(either an int or an array of ints 
*                               representing keycodes).
* @param {Function}    handler  The CustomEvent handler to fire when the 
*                               key event is detected
* @param {Object}      handler  An object literal representing the handler. 
* @param {String}      event    Optional. The event (keydown or keyup) to 
*                               listen for. Defaults automatically to keydown.
*
* @knownissue the "keypress" event is completely broken in Safari 2.x and below.
*             the workaround is use "keydown" for key listening.  However, if
*             it is desired to prevent the default behavior of the keystroke,
*             that can only be done on the keypress event.  This makes key
*             handling quite ugly.
* @knownissue keydown is also broken in Safari 2.x and below for the ESC key.
*             There currently is no workaround other than choosing another
*             key to listen for.
*/
YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) {
    if (!attachTo) {
    } else if (!keyData) {
    } else if (!handler) {
    } 
    
    if (!event) {
        event = YAHOO.util.KeyListener.KEYDOWN;
    }

    /**
    * The CustomEvent fired internally when a key is pressed
    * @event keyEvent
    * @private
    * @param {Object} keyData The object literal representing the key(s) to 
    *                         detect. Possible attributes are shift(boolean), 
    *                         alt(boolean), ctrl(boolean) and keys(either an 
    *                         int or an array of ints representing keycodes).
    */
    var keyEvent = new YAHOO.util.CustomEvent("keyPressed");
    
    /**
    * The CustomEvent fired when the KeyListener is enabled via the enable() 
    * function
    * @event enabledEvent
    * @param {Object} keyData The object literal representing the key(s) to 
    *                         detect. Possible attributes are shift(boolean), 
    *                         alt(boolean), ctrl(boolean) and keys(either an 
    *                         int or an array of ints representing keycodes).
    */
    this.enabledEvent = new YAHOO.util.CustomEvent("enabled");

    /**
    * The CustomEvent fired when the KeyListener is disabled via the 
    * disable() function
    * @event disabledEvent
    * @param {Object} keyData The object literal representing the key(s) to 
    *                         detect. Possible attributes are shift(boolean), 
    *                         alt(boolean), ctrl(boolean) and keys(either an 
    *                         int or an array of ints representing keycodes).
    */
    this.disabledEvent = new YAHOO.util.CustomEvent("disabled");

    if (Lang.isString(attachTo)) {
        attachTo = document.getElementById(attachTo); // No Dom util
    }

    if (Lang.isFunction(handler)) {
        keyEvent.subscribe(handler);
    } else {
        keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope);
    }

    /**
    * Handles the key event when a key is pressed.
    * @method handleKeyPress
    * @param {DOMEvent} e   The keypress DOM event
    * @param {Object}   obj The DOM event scope object
    * @private
    */
    function handleKeyPress(e, obj) {
        if (! keyData.shift) {  
            keyData.shift = false; 
        }
        if (! keyData.alt) {    
            keyData.alt = false;
        }
        if (! keyData.ctrl) {
            keyData.ctrl = false;
        }

        // check held down modifying keys first
        if (e.shiftKey == keyData.shift && 
            e.altKey   == keyData.alt &&
            e.ctrlKey  == keyData.ctrl) { // if we pass this, all modifiers match
            
            var dataItem, keys = keyData.keys, key;

            if (YAHOO.lang.isArray(keys)) {
                for (var i=0;i<keys.length;i++) {
                    dataItem = keys[i];
                    key = Event.getCharCode(e);

                    if (dataItem == key) {
                        keyEvent.fire(key, e);
                        break;
                    }
                }
            } else {
                key = Event.getCharCode(e);
                if (keys == key ) {
                    keyEvent.fire(key, e);
                }
            }
        }
    }

    /**
    * Enables the KeyListener by attaching the DOM event listeners to the 
    * target DOM element
    * @method enable
    */
    this.enable = function() {
        if (! this.enabled) {
            Event.on(attachTo, event, handleKeyPress);
            this.enabledEvent.fire(keyData);
        }
        /**
        * Boolean indicating the enabled/disabled state of the Tooltip
        * @property enabled
        * @type Boolean
        */
        this.enabled = true;
    };

    /**
    * Disables the KeyListener by removing the DOM event listeners from the 
    * target DOM element
    * @method disable
    */
    this.disable = function() {
        if (this.enabled) {
            Event.removeListener(attachTo, event, handleKeyPress);
            this.disabledEvent.fire(keyData);
        }
        this.enabled = false;
    };

    /**
    * Returns a String representation of the object.
    * @method toString
    * @return {String}  The string representation of the KeyListener
    */ 
    this.toString = function() {
        return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + 
                (attachTo.id ? "[" + attachTo.id + "]" : "");
    };

};

var KeyListener = YAHOO.util.KeyListener;

/**
 * Constant representing the DOM "keydown" event.
 * @property YAHOO.util.KeyListener.KEYDOWN
 * @static
 * @final
 * @type String
 */
KeyListener.KEYDOWN = "keydown";

/**
 * Constant representing the DOM "keyup" event.
 * @property YAHOO.util.KeyListener.KEYUP
 * @static
 * @final
 * @type String
 */
KeyListener.KEYUP = "keyup";

/**
 * keycode constants for a subset of the special keys
 * @property KEY
 * @static
 * @final
 */
KeyListener.KEY = {
    ALT          : 18,
    BACK_SPACE   : 8,
    CAPS_LOCK    : 20,
    CONTROL      : 17,
    DELETE       : 46,
    DOWN         : 40,
    END          : 35,
    ENTER        : 13,
    ESCAPE       : 27,
    HOME         : 36,
    LEFT         : 37,
    META         : 224,
    NUM_LOCK     : 144,
    PAGE_DOWN    : 34,
    PAGE_UP      : 33, 
    PAUSE        : 19,
    PRINTSCREEN  : 44,
    RIGHT        : 39,
    SCROLL_LOCK  : 145,
    SHIFT        : 16,
    SPACE        : 32,
    TAB          : 9,
    UP           : 38
};

})();
YAHOO.register("event", YAHOO.util.Event, {version: "2.8.1", build: "19"});
SONET.featureProviders["plugin/mmaTwitterPlugin"] = {};
SONET.featureProviders["plugin/mmaTwitterPlugin"].translations = [];
SONET.featureProviders["plugin/mmaTwitterPlugin"].assetDirPattern = "%assettype%\/mmaTwitterPlugin";
SONET.featureProviders["plugin/mmaTwitterPlugin"].widgetClosures = {};
SONET.featureProviders["plugin/mmaTwitterPlugin"].decoratorClosures = {};
SONET.featureProviders["plugin/mmaTwitterPlugin"].widgetClosures["twitterList"] = function () {
    var _ = SONET.translatorForFeatureProvider("plugin/mmaTwitterPlugin");
    var widget = new ENGINE.Widget;
    SONET.uwa.use(function(UWA) {
	// YUI vars
	var Dom = YAHOO.util.Dom;
    var Event = YAHOO.util.Event;
	var Lang = YAHOO.lang;
	
	// DOM Elements & vars to keep track of states
	var elLoginDiv;
	var elLoginTextField;
	var elLoginLeftBorder;
	var elLoginButton;
	var isEmptyString = true;
	var _ENTER_KEY = 13;


//////////////////////////////////////////
// START THE WIDGET
//////////////////////////////////////////

widget.onLoad = function(){
	// alert("isFeed:" + widget.getValue('isFeed') + " | hasCredentials:" + widget.getValue('hasCredentials') + " | isOwner:" + widget.getValue('isOwner') + " | twitterName: " + widget.getValue('twitterName'));
	// Figure out whether to show community feed, personal feed, or login
	if (widget.getValue('isFeed')){
		displayCommunityFeed();
	}else if (widget.getValue('hasCredentials')){
		displayPersonalFeed( widget.getValue('twitterName') );
	}else if (widget.getValue('isOwner')){
		displayLogin();
	}else{ 
		displayCommunityFeed(); // This should never occur. Safeguard to make sure we always show something.
	}
}

//////////////////////////////////////////
// HANDLE THE DIFFERENT DISPLAY STATES
//////////////////////////////////////////

function displayCommunityFeed(){
	widget.decorator.setMoreLink("http://twitter.com/" + 'easportsmma');
	search('fromUser', 'easportsmma');
	//widget.decorator.setMoreLink("http://twitter.com/#search?q=%23mma");
	//search('hashtag', 'mma');
}

function displayPersonalFeed( twitterName ){
	widget.decorator.setMoreLink("http://twitter.com/" + twitterName);
	search('fromUser', twitterName);
}

function displayLogin(){
	setupLogin();
}

//////////////////////////////////////////
// TWITTER FEED FUNCTIONALITY
//////////////////////////////////////////

/**
 * twitter/feedreader service sends the request to twitter API and processes the data
 */
function search(searchMode, searchTerm){
	widget.doRequest( "twitter/feedReader/" + searchMode + "/" + searchTerm,
        {
            service: "service",
            type: "text",
			onComplete:processData
        } );
};

function processData(responseData){
	// get container 
	var elWidgetContainer = UWA.extendElement(widget.body.getElementsByClassName('container')[0]);
	// empty widgetContainer
	elWidgetContainer.empty();
	// set twitterlist html content
	elWidgetContainer.setHTML(responseData);
}

//////////////////////////////////////////
// LOGIN FUNCTIONS
//////////////////////////////////////////

function setupLogin(){
	elLoginDiv = UWA.extendElement(widget.body.getElementsByClassName('mmaTextFieldArea')[0]);
	elLoginField = UWA.extendElement(widget.body.getElementsByClassName('mmaTextField')[0]);
	elLoginBorder = UWA.extendElement(widget.body.getElementsByClassName('leftBorder')[0]);
	elLoginButton = UWA.extendElement(widget.body.getElementsByClassName('mmaButton')[0]);
	
	Event.on(elLoginField, 'click', onTextFieldActivate); 
	Event.on(elLoginField, 'blur', onTextFieldDeactivate); 
	Event.on(elLoginField, 'keypress', onTextFieldKeyPress); 
	Event.on(elLoginButton, 'click', onTwitterSetupSubmit); 
}

function onTwitterSetupSubmit(){
	formCheck();
	//var form = widget.body.getElementById( 'twitter_login_form' );
	//var data = YAHOO.util.Connect.setForm( form );
	//var name = data.split('=')[1];
	//widget.setValue('twitterName', name);
}

function formCheck(){
	if (!isEmptyString){
		
		// Christoph's way
		var form = UWA.extendElement(widget.body.getElementsByClassName( 'twitter_login_form' )[0]);
		var data = YAHOO.util.Connect.setForm( form );
		var name = data.split('=')[1];
		widget.setValue('twitterName', name);
		
		//alert("setting twitter name to : " + name);
		//alert("for now, you gotta manually refresh the page to test... twitterName is now: " + widget.getValue('twitterName'));
		
		// jens' way
		// var dataInput = Lang.trim(elLoginField.value);
		// widget.setValue('twitterName', dataInput)
		// alert("setting twitter name to : " + dataInput);
		// alert("for now, you gotta manually refresh the page to test... twitterName is now: " + widget.getValue('twitterName'));
	}
}

function onTextFieldKeyPress(event){
	iEmptySearchString = false;
    event = event? event: window.event;
    var key = event.keyCode || event.which;

    if(key == _ENTER_KEY){
      formCheck();
      return false;
    }
}

function onTextFieldActivate(o){
    if (isEmptyString){
      isEmptyString = false;
      elLoginField.value = '';
    }
	elLoginDiv.removeClassName('mmaTextFieldArea');
    elLoginDiv.addClassName('mmaTextFieldAreaActive');
}

function onTextFieldDeactivate(o){
  	elLoginDiv.removeClassName('mmaTextFieldAreaActive');
    elLoginDiv.addClassName('mmaTextFieldArea');
   
    if (Lang.trim(elLoginField.value) == ''){
      elLoginField.value = _('Your Twitter Name');
      isEmptyString = true;
    }
}

	
    });
    return widget;
};

SONET.Injector.addCompiledBundleAssets("Javascripts", ["{STATIC_ASSET_PREFIX}js\/easSonetPlugin\/yui\/2\/animation\/animation.js","{STATIC_ASSET_PREFIX}js\/easSonetPlugin\/yui\/2\/menu\/menu.js","{STATIC_ASSET_PREFIX}js\/easSonetPlugin\/yui\/2\/element\/element.js","{STATIC_ASSET_PREFIX}js\/easSonetPlugin\/yui\/2\/button\/button.js","{STATIC_ASSET_PREFIX}js\/easSonetPlugin\/yui\/2\/connection\/connection.js","{RELATIVE_URL_ROOT}\/assets\/276\/en_US\/plugin\/mmaNavigationPlugin.js","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaNavigationPlugin\/widget\/mmaHeader.js","{STATIC_ASSET_PREFIX}js\/easSonetPlugin\/yui\/2\/cookie\/cookie.js","{RELATIVE_URL_ROOT}\/assets\/276\/en_US\/plugin\/mmaMarqueePlugin.js","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaMarqueePlugin\/widget\/marquee.js","{RELATIVE_URL_ROOT}\/assets\/276\/en_US\/plugin\/mmaBasePlugin.js","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaBasePlugin\/decorator\/mmaDecorator.js","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaBasePlugin\/widget\/filler.js","{STATIC_ASSET_PREFIX}js\/easSonetPlugin\/yui\/2\/json\/json.js","{STATIC_ASSET_PREFIX}js\/easSonetPlugin\/yui\/2\/get\/get.js","{STATIC_ASSET_PREFIX}js\/easSonetPlugin\/yui\/2\/event\/event.js","{RELATIVE_URL_ROOT}\/assets\/276\/en_US\/plugin\/mmaTwitterPlugin.js","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaTwitterPlugin\/widget\/twitterList.js"]);
SONET.Injector.addCompiledBundleAssets("Stylesheets", ["{STATIC_ASSET_PREFIX}css\/template\/themes\/common.css","{STATIC_ASSET_PREFIX}css\/template\/themes\/easportsmma\/main.css","{STATIC_ASSET_PREFIX}css\/template\/themes\/easportsmma\/nv-compat.css","{STATIC_ASSET_PREFIX}css\/template\/themes\/easportsmma\/easports-yui-paginator-skin.css","{STATIC_ASSET_PREFIX}css\/template\/themes\/easportsmma\/easports-yui-calendar-skin.css","{STATIC_ASSET_PREFIX}css\/template\/themes\/easportsmma\/easports-yui-tabview-skin.css","{STATIC_ASSET_PREFIX}css\/template\/themes\/easportsmma\/mmaWidgetDecorator.css","{STATIC_ASSET_PREFIX}css\/template\/themes\/easportsmma\/mmaDecorator.css","{STATIC_ASSET_PREFIX}css\/template\/layout-template.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaNavigationPlugin\/widget\/mmaHeader.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaMarqueePlugin\/widget\/marquee.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaBlogPlugin\/widget\/blogEntrySummaryList.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaBasePlugin\/decorator\/mmaDecorator.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaBasePlugin\/widget\/filler.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaBasePlugin\/decorator\/mmaDecoratorBlank.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaBasePlugin\/widget\/homePromo.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaBasePlugin\/widget\/communityLinks.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaTwitterPlugin\/widget\/twitterList.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaBasePlugin\/widget\/preOrder.css","{RELATIVE_URL_ROOT}\/assets\/276\/plugin\/mmaNavigationPlugin\/widget\/footer.css"]);

