/*Copyright (c) 2007, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txtversion: 2.4.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}  oScope 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. * @namespace YAHOO.util * @class CustomEvent * @constructor */YAHOO.util.CustomEvent = function(type, oScope, silent, signature) {    /**     * The type of event, returned to subscribers when the event fires     * @property type     * @type string     */    this.type = type;    /**     * The scope the the event will fire from by default.  Defaults to the window      * obj     * @property scope     * @type object     */    this.scope = oScope || window;    /**     * By default all custom events are logged in the debug build, set silent     * to true to disable debug outpu for this event.     * @property silent     * @type boolean     */    this.silent = silent;    /**     * 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 {Function} fn The function to execute         * @param {Object}   obj An object to be passed along when the event          *                       fires         * @param {boolean|Object}  override If true, the obj passed in becomes          *                                   the execution scope of the listener.         *                                   if an object, that object becomes the         *                                   the execution scope.         */        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     * @param {boolean|Object}  override If true, the obj passed in becomes      *                                   the execution scope of the listener.     *                                   if an object, that object becomes the     *                                   the execution scope.     */    subscribe: function(fn, obj, override) {        if (!fn) {throw new Error("Invalid callback for subscriber to '" + this.type + "'");        }        if (this.subscribeEvent) {            this.subscribeEvent.fire(fn, obj, override);        }        this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, override) );    },    /**     * 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 scope 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() {        var len=this.subscribers.length;        if (!len && this.silent) {            return true;        }        var args=[], ret=true, i, rebuild=false;        for (i=0; i<arguments.length; ++i) {            args.push(arguments[i]);        }        if (!this.silent) {        }        for (i=0; i<len; ++i) {            var s = this.subscribers[i];            if (!s) {                rebuild=true;            } else {                if (!this.silent) {                }                var scope = s.getScope(this.scope);                if (this.signature == YAHOO.util.CustomEvent.FLAT) {                    var param = null;                    if (args.length > 0) {                        param = args[0];                    }                    try {                        ret = s.fn.call(scope, param, s.obj);                    } catch(e) {                        this.lastError = e;                    }                } else {                    try {                        ret = s.fn.call(scope, this.type, args, s.obj);                    } catch(ex) {                        this.lastError = ex;                    }                }                if (false === ret) {                    if (!this.silent) {                    }                    //break;                    return false;                }            }        }        if (rebuild) {            var newlist=[],subs=this.subscribers;            for (i=0,len=subs.length; i<len; i=i+1) {                newlist.push(subs[i]);            }            this.subscribers=newlist;        }        return true;    },    /**     * Removes all listeners     * @method unsubscribeAll     * @return {int} The number of listeners unsubscribed     */    unsubscribeAll: function() {        for (var i=0, len=this.subscribers.length; i<len; ++i) {            this._delete(len - 1 - i);        }        this.subscribers=[];        return i;    },    /**     * @method _delete     * @private     */    _delete: function(index) {        var s = this.subscribers[index];        if (s) {            delete s.fn;            delete s.obj;        }        this.subscribers[index]=null;    },    /**     * @method toString     */    toString: function() {         return "CustomEvent: " + "'" + this.type  + "', " +              "scope: " + 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}  override If true, the obj passed in becomes the execution *                            scope of the listener * @class Subscriber * @constructor */YAHOO.util.Subscriber = function(fn, obj, override) {    /**     * 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 scope for the event listener is defined when the     * event is created (usually the object which contains the event).     * By setting override to true, the execution scope becomes the custom     * object passed in by the subscriber.  If override is an object, that      * object becomes the scope.     * @property override     * @type boolean|object     */    this.override = override;};/** * Returns the execution scope for this listener.  If override was set to true * the custom obj will be the scope.  If override is an object, that is the * scope, otherwise the default scope will be used. * @method getScope * @param {Object} defaultScope the scope to use if this listener does not *                              override it. */YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {    if (this.override) {        if (this.override === true) {            return this.obj;        } else {            return this.override;        }    }    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  +            ", override: " +  (this.override || "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         */        var listeners = [];        /**         * User-defined unload function that will be fired before all events         * are detached         * @property unloadListeners         * @type array         * @static         * @private         */        var unloadListeners = [];        /**         * Cache of DOM0 event handlers to work around issues with DOM2 events         * in Safari         * @property legacyEvents         * @static         * @private         */        var legacyEvents = [];        /**         * Listener stack for DOM0 events         * @property legacyHandlers         * @static         * @private         */        var legacyHandlers = [];        /**         * 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         */        var retryCount = 0;        /**         * onAvailable listeners         * @property onAvailStack         * @static         * @private         */        var onAvailStack = [];        /**         * Lookup table for legacy events         * @property legacyMap         * @static         * @private         */        var legacyMap = [];        /**         * Counter for auto id generation         * @property counter         * @static         * @private         */        var counter = 0;                /**         * Normalized keycodes for webkit/safari         * @property webkitKeymap         * @type {int: int}         * @private         * @static         * @final         */        var 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)        };        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 4000@amp;10 ms, so it will poll             * for 40 seconds or until all outstanding handlers are bound             * (whichever comes first).             * @property POLL_RETRYS             * @type int             * @static             * @final             */            POLL_RETRYS: 4000,            /**             * The poll interval in milliseconds             * @property POLL_INTERVAL             * @type int             * @static             * @final             */            POLL_INTERVAL: 10,            /**             * 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 scope 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 scope, 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 scope parameter passed into addListener             * @property OVERRIDE             * @type int             * @static             * @final             */            OVERRIDE: 6,            /**             * 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: YAHOO.env.ua.ie,            /**             * poll handle             * @property _interval             * @static             * @private             */            _interval: null,            /**             * document readystate poll handle             * @property _dri             * @static             * @private             */             _dri: null,            /**             * True when the document is initially usable             * @property DOMReady             * @type boolean             * @static             */            DOMReady: 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);                }            },            /**             * 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[]}   p_id the id of the element, or an array             * of ids to look for.             * @param {function} p_fn what to execute when the element is found.             * @param {object}   p_obj an optional object to be passed back as             *                   a parameter to p_fn.             * @param {boolean|object}  p_override If set to true, p_fn will execute             *                   in the scope of p_obj, if set to an object it             *                   will execute in the scope of that object             * @param checkContent {boolean} check child node readiness (onContentReady)             * @static             */            onAvailable: function(p_id, p_fn, p_obj, p_override, checkContent) {                var a = (YAHOO.lang.isString(p_id)) ? [p_id] : p_id;                for (var i=0; i<a.length; i=i+1) {                    onAvailStack.push({id:         a[i],                                        fn:         p_fn,                                        obj:        p_obj,                                        override:   p_override,                                        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}   p_id the id of the element to look for.             * @param {function} p_fn what to execute when the element is ready.             * @param {object}   p_obj an optional object to be passed back as             *                   a parameter to p_fn.             * @param {boolean|object}  p_override If set to true, p_fn will execute             *                   in the scope of p_obj.  If an object, p_fn will             *                   exectute in the scope of that object             *             * @static             */            onContentReady: function(p_id, p_fn, p_obj, p_override) {                this.onAvailable(p_id, p_fn, p_obj, p_override, 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} p_fn what to execute when the element is found.             * @param {object}   p_obj an optional object to be passed back as             *                   a parameter to p_fn.             * @param {boolean|object}  p_scope If set to true, p_fn will execute             *                   in the scope of p_obj, if set to an object it             *                   will execute in the scope of that object             *             * @static             */            onDOMReady: function(p_fn, p_obj, p_override) {                if (this.DOMReady) {                    setTimeout(function() {                        var s = window;                        if (p_override) {                            if (p_override === true) {                                s = p_obj;                            } else {                                s = p_override;                            }                        }                        p_fn.call(s, "DOMReady", [], p_obj);                    }, 0);                } else {                    this.DOMReadyEvent.subscribe(p_fn, p_obj, p_override);                }            },            /**             * 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}  override  If true, the obj passed in becomes             *                             the execution scope of the listener. If an             *                             object, this object becomes the execution             *                             scope.             * @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, override) {                if (!fn || !fn.call) {// throw new TypeError(sType + " addListener call failed, callback undefined");                    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,                                        override) && 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.on(el, sType, fn, obj, override);                        });                        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, override];                    return true;                }                // if the user chooses to override the scope, we use the custom                // object passed in, otherwise the executing scope will be the                // HTML element that the event is registered on                var scope = el;                if (override) {                    if (override === true) {                        scope = obj;                    } else {                        scope = override;                    }                }                // wrap the function so we can return the obj object when                // the event fires;                var wrappedFn = function(e) {                        return fn.call(scope, YAHOO.util.Event.getEvent(e, el),                                 obj);                    };                var li = [el, sType, fn, wrappedFn, scope, obj, override];                var index = listeners.length;                // cache the listener so we can try to automatically unload                listeners[index] = li;                if (this.useLegacyEvent(el, sType)) {                    var legacyIndex = this.getLegacyIndex(el, sType);                    // Add a new dom0 wrapper if one is not detected for this                    // element                    if ( legacyIndex == -1 ||                                 el != legacyEvents[legacyIndex][0] ) {                        legacyIndex = legacyEvents.length;                        legacyMap[el.id + sType] = legacyIndex;                        // cache the signature for the DOM0 event, and                         // include the existing handler for the event, if any                        legacyEvents[legacyIndex] =                             [el, sType, el["on" + sType]];                        legacyHandlers[legacyIndex] = [];                        el["on" + sType] =                             function(e) {                                YAHOO.util.Event.fireLegacyEvent(                                    YAHOO.util.Event.getEvent(e), legacyIndex);                            };                    }                    // add a reference to the wrapped listener to our custom                    // stack of events                    //legacyHandlers[legacyIndex].push(index);                    legacyHandlers[legacyIndex].push(li);                } else {                    try {                        this._simpleAdd(el, sType, wrappedFn, false);                    } 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;                            },            /**             * When using legacy events, the handler is routed to this object             * so we can fire our custom listener stack.             * @method fireLegacyEvent             * @static             * @private             */            fireLegacyEvent: function(e, legacyIndex) {                var ok=true,le,lh,li,scope,ret;                                lh = legacyHandlers[legacyIndex];                for (var i=0,len=lh.length; i<len; ++i) {                    li = lh[i];                    if ( li && li[this.WFN] ) {                        scope = li[this.ADJ_SCOPE];                        ret = li[this.WFN].call(scope, e);                        ok = (ok && ret);                    }                }                // Fire the original handler if we replaced one.  We fire this                // after the other events to keep stopPropagation/preventDefault                // that happened in the DOM0 handler from touching our DOM2                // substitute                le = legacyEvents[legacyIndex];                if (le && le[2]) {                    le[2](e);                }                                return ok;            },            /**             * Returns the legacy event index that matches the supplied              * signature             * @method getLegacyIndex             * @static             * @private             */            getLegacyIndex: function(el, sType) {                var key = this.generateId(el) + sType;                if (typeof legacyMap[key] == "undefined") {                     return -1;                } else {                    return legacyMap[key];                }            },            /**             * Logic that determines when we should automatically use legacy             * events instead of DOM2 events.  Currently this is limited to old             * Safari browsers with a broken preventDefault             * @method useLegacyEvent             * @static             * @private             */            useLegacyEvent: function(el, sType) {                if (this.webkit && ("click"==sType || "dblclick"==sType)) {                    var v = parseInt(this.webkit, 10);                    if (!isNaN(v) && v<418) {                        return true;                    }                }                return false;            },                                /**             * 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;                // 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=0,len=el.length; i<len; ++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=0, len=unloadListeners.length; i<len; 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(el, sType, fn);                }                if (index >= 0) {                    cacheItem = listeners[index];                }                if (!el || !cacheItem) {                    return false;                }                if (this.useLegacyEvent(el, sType)) {                    var legacyIndex = this.getLegacyIndex(el, sType);                    var llist = legacyHandlers[legacyIndex];                    if (llist) {                        for (i=0, len=llist.length; i<len; ++i) {                            li = llist[i];                            if (li &&                                 li[this.EL] == el &&                                 li[this.TYPE] == sType &&                                 li[this.FN] == fn) {                                    //llist.splice(i, 1);                                    llist[i]=null;                                    break;                            }                        }                    }                } else {                    try {                        this._simpleRemove(el, sType, cacheItem[this.WFN], false);                    } 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(node) {                if (node && 3 == node.nodeType) {                    return node.parentNode;                } else {                    return node;                }            },            /**             * 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;                    }                }                // IE events that target non-browser objects (e.g., VML                // canvas) will sometimes throw errors when you try to                // inspect the properties of the event target.  We try to                // detect this condition, and provide a dummy target (the bound                // element) to eliminate spurious errors.                  // the implementation caused unexpected results in some                 // implementations, so this has been rolled back for now                /*                 if (ev && this.isIE) {                    try {                        var el = ev.srcElement;                    } catch(ex) {                                                 ev.target = boundEl;                    }                }                */                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 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(el, sType, fn) {                for (var i=0,len=listeners.length; i<len; ++i) {                    var li = listeners[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", this),            /**             * 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();                    // Remove the listener to assist with the IE memory issue, but not                    // for other browsers because FF 1.0x does not like it.                    //if (this.isIE) {                        //EU._simpleRemove(window, "load", EU._load);                    //}                }            },            /**             * 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 (this.locked) {                    return false;                }                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 || "complete" !== document.readyState) {                    if (!this.DOMReady) {                        this.startInterval();                        return false;                    }                }                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);                }                // onAvailable                var notAvail = [];                var executeItem = function (el, item) {                    var scope = el;                    if (item.override) {                        if (item.override === true) {                            scope = item.obj;                        } else {                            scope = item.override;                        }                    }                    item.fn.call(scope, item.obj);                };                var i,len,item,el;                // onAvailable                for (i=0,len=onAvailStack.length; i<len; ++i) {                    item = onAvailStack[i];                    if (item && !item.checkReady) {                        el = this.getEl(item.id);                        if (el) {                            executeItem(el, item);                            onAvailStack[i] = null;                        } else {                            notAvail.push(item);                        }                    }                }                // onContentReady                for (i=0,len=onAvailStack.length; i<len; ++i) {                    item = onAvailStack[i];                    if (item && item.checkReady) {                        el = this.getEl(item.id);                        if (el) {                            // The element is available, but not necessarily ready                            // @todo should we test parentNode.nextSibling?                            if (loadComplete || el.nextSibling) {                                executeItem(el, item);                                onAvailStack[i] = null;                            }                        } else {                            notAvail.push(item);                        }                    }                }                retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;                if (tryAgain) {                    // we may need to strip the nulled out items here                    this.startInterval();                } else {                    clearInterval(this._interval);                    this._interval = null;                }                this.locked = false;                return true;            },            /**             * 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=0,len=elListeners.length; i<len ; ++i) {                        var l = elListeners[i];                        // can't use the index on the changing collection                        this.removeListener(oEl, l.type, l.fn, l.index);                        //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 scope             * &nbsp;&nbsp;scope: (boolean)  the derived scope 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 {                    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 && searchList.length > 0) {                        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;                // execute and clear stored unload listeners                for (i=0,len=unloadListeners.length; i<len; ++i) {                    l = unloadListeners[i];                    if (l) {                        var scope = window;                        if (l[EU.ADJ_SCOPE]) {                            if (l[EU.ADJ_SCOPE] === true) {                                scope = l[EU.UNLOAD_OBJ];                            } else {                                scope = l[EU.ADJ_SCOPE];                            }                        }                        l[EU.FN].call(scope, EU.getEvent(e, l[EU.EL]), l[EU.UNLOAD_OBJ] );                        unloadListeners[i] = null;                        l=null;                        scope=null;                    }                }                unloadListeners = null;                // call clearAttributes or remove listeners to handle IE memory leaks                if (YAHOO.env.ua.ie && listeners && listeners.length > 0) {                    j = listeners.length;                    while (j) {                        index = j-1;                        l = listeners[index];                        if (l) {                            //try {                                //l[EU.EL].clearAttributes(); // errors on window objects                            //} catch(ex) {                            EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], index);                            //}                        }                         j--;                    }                    l=null;                }                /*                // remove all listeners                if (listeners && listeners.length > 0) {                    j = listeners.length;                    while (j) {                        index = j-1;                        l = listeners[index];                        if (l) {                            EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], index);                        }                         j = j - 1;                    }                    l=null;                }                */                /*                // kill legacy events                for (i=0,len=legacyEvents.length; i<len; ++i) {                    // dereference the element                    //delete legacyEvents[i][0];                    legacyEvents[i][0] = null;                    // delete the array item                    //delete legacyEvents[i];                    legacyEvents[i] = null;                }                */                legacyEvents = 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() {                // does nothing            },/*            testIEReady: function (){                var n = document.createElement('p'), ready = false;                try {                    // throws an error until the doc is ready                    n.doScroll('left');                     ready = true;                } catch(ex){                     // document is not ready                }                n = null;                return ready;            },*/            /**             * Adds a DOM event directly without the caching, cleanup, scope 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;        /////////////////////////////////////////////////////////////        // DOMReady        // based on work by: Dean Edwards/John Resig/Matthias Miller         // 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) {            // Process onAvailable/onContentReady items when when the             // DOM is ready.            YAHOO.util.Event.onDOMReady(                    YAHOO.util.Event._tryPreloadAttach,                    YAHOO.util.Event, true);            /*            var el, d=document, b=d.body;            // If the library is being injected after window.onload, it            // is not safe to document.write the script tag.  Detecting            // this state doesn't appear possible, so we expect a flag            // in YAHOO_config to be set if the library is being injected.            if (("undefined" !== typeof YAHOO_config) && YAHOO_config.injecting) {                el = document.createElement("script");                var p=d.getElementsByTagName("head")[0] || b;                p.insertBefore(el, p.firstChild);            } else {    d.write('<scr'+'ipt id="_yui_eu_dr" defer="true" src="//:"><'+'/script>');                el=document.getElementById("_yui_eu_dr");            }                        if (el) {                el.onreadystatechange = function() {                    if ("complete" === this.readyState) {                        this.parentNode.removeChild(this);                        YAHOO.util.Event._ready();                    }                };            } else {                // The library was likely injected into the page                // rendering onDOMReady unreliable                // YAHOO.util.Event._ready();            }            el=null;            *//*            (function (){                var n = document.createElement('p');                  try {                    // throws an error if doc is not ready                    n.doScroll('left');                    n = null;                    YAHOO.util.Event._ready();                } catch (ex){                    n = null;setTimeout(arguments.callee, YAHOO.util.Event.POLL_INTERVAL);                }            })();*/            EU._dri = setInterval(function() {                var n = document.createElement('p');                  try {                    // throws an error if doc is not ready                    n.doScroll('left');                    clearInterval(EU._dri);                    EU._dri = null;                    EU._ready();                    n = null;                } catch (ex) {                     n = null;                }            }, EU.POLL_INTERVAL);                 // Safari: The document's readyState in Safari currently will        // change to loaded/complete before images are loaded.        //} else if (EU.webkit) {        } else if (EU.webkit) {            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.        } else {            // @todo will this fire when the library is injected?            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 p_override {boolean}  If true, the obj passed in becomes the      *                              execution scope of the listener     */    subscribe: function(p_type, p_fn, p_obj, p_override) {        this.__yui_events = this.__yui_events || {};        var ce = this.__yui_events[p_type];        if (ce) {            ce.subscribe(p_fn, p_obj, p_override);        } 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, override: p_override } );        }    },    /**     * 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>     *      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 || {};        var events = this.__yui_events;        if (events[p_type]) {        } else {            var scope  = opts.scope  || this;            var silent = (opts.silent);            var ce = new YAHOO.util.CustomEvent(p_type, scope, silent,                    YAHOO.util.CustomEvent.FLAT);            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].override);                }            }        }        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>     * If the custom event has not been explicitly created, it will be     * created now with the default config, scoped to the host object     * @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, arg1, arg2, etc) {        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;    }};/*** 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 (typeof attachTo == 'string') {        attachTo = document.getElementById(attachTo);    }    if (typeof handler == 'function') {        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;            if (keyData.keys instanceof Array) {                for (var i=0;i<keyData.keys.length;i++) {                    dataItem = keyData.keys[i];                    if (dataItem == e.charCode ) {                        keyEvent.fire(e.charCode, e);                        break;                    } else if (dataItem == e.keyCode) {                        keyEvent.fire(e.keyCode, e);                        break;                    }                }            } else {                dataItem = keyData.keys;                if (dataItem == e.charCode ) {                    keyEvent.fire(e.charCode, e);                } else if (dataItem == e.keyCode) {                    keyEvent.fire(e.keyCode, e);                }            }        }    }    /**    * Enables the KeyListener by attaching the DOM event listeners to the     * target DOM element    * @method enable    */    this.enable = function() {        if (! this.enabled) {            YAHOO.util.Event.addListener(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) {            YAHOO.util.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 + "]" : "");    };};/*** Constant representing the DOM "keydown" event.* @property YAHOO.util.KeyListener.KEYDOWN* @static* @final* @type String*/YAHOO.util.KeyListener.KEYDOWN = "keydown";/*** Constant representing the DOM "keyup" event.* @property YAHOO.util.KeyListener.KEYUP* @static* @final* @type String*/YAHOO.util.KeyListener.KEYUP = "keyup";/** * keycode constants for a subset of the special keys * @property KEY * @static * @final */YAHOO.util.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.4.1", build: "742"});