event.js
上传用户:q2283699q
上传日期:2022-05-17
资源大小:10704k
文件大小:81k
源码类别:

Ftp客户端

开发平台:

Visual C++

  1. /*
  2. Copyright (c) 2007, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.net/yui/license.txt
  5. version: 2.3.0
  6. */
  7. /**
  8.  * The CustomEvent class lets you define events for your application
  9.  * that can be subscribed to by one or more independent component.
  10.  *
  11.  * @param {String}  type The type of event, which is passed to the callback
  12.  *                  when the event fires
  13.  * @param {Object}  oScope The context the event will fire from.  "this" will
  14.  *                  refer to this object in the callback.  Default value: 
  15.  *                  the window object.  The listener can override this.
  16.  * @param {boolean} silent pass true to prevent the event from writing to
  17.  *                  the debugsystem
  18.  * @param {int}     signature the signature that the custom event subscriber
  19.  *                  will receive. YAHOO.util.CustomEvent.LIST or 
  20.  *                  YAHOO.util.CustomEvent.FLAT.  The default is
  21.  *                  YAHOO.util.CustomEvent.LIST.
  22.  * @namespace YAHOO.util
  23.  * @class CustomEvent
  24.  * @constructor
  25.  */
  26. YAHOO.util.CustomEvent = function(type, oScope, silent, signature) {
  27.     /**
  28.      * The type of event, returned to subscribers when the event fires
  29.      * @property type
  30.      * @type string
  31.      */
  32.     this.type = type;
  33.     /**
  34.      * The scope the the event will fire from by default.  Defaults to the window 
  35.      * obj
  36.      * @property scope
  37.      * @type object
  38.      */
  39.     this.scope = oScope || window;
  40.     /**
  41.      * By default all custom events are logged in the debug build, set silent
  42.      * to true to disable debug outpu for this event.
  43.      * @property silent
  44.      * @type boolean
  45.      */
  46.     this.silent = silent;
  47.     /**
  48.      * Custom events support two styles of arguments provided to the event
  49.      * subscribers.  
  50.      * <ul>
  51.      * <li>YAHOO.util.CustomEvent.LIST: 
  52.      *   <ul>
  53.      *   <li>param1: event name</li>
  54.      *   <li>param2: array of arguments sent to fire</li>
  55.      *   <li>param3: <optional> a custom object supplied by the subscriber</li>
  56.      *   </ul>
  57.      * </li>
  58.      * <li>YAHOO.util.CustomEvent.FLAT
  59.      *   <ul>
  60.      *   <li>param1: the first argument passed to fire.  If you need to
  61.      *           pass multiple parameters, use and array or object literal</li>
  62.      *   <li>param2: <optional> a custom object supplied by the subscriber</li>
  63.      *   </ul>
  64.      * </li>
  65.      * </ul>
  66.      *   @property signature
  67.      *   @type int
  68.      */
  69.     this.signature = signature || YAHOO.util.CustomEvent.LIST;
  70.     /**
  71.      * The subscribers to this event
  72.      * @property subscribers
  73.      * @type Subscriber[]
  74.      */
  75.     this.subscribers = [];
  76.     if (!this.silent) {
  77.     }
  78.     var onsubscribeType = "_YUICEOnSubscribe";
  79.     // Only add subscribe events for events that are not generated by 
  80.     // CustomEvent
  81.     if (type !== onsubscribeType) {
  82.         /**
  83.          * Custom events provide a custom event that fires whenever there is
  84.          * a new subscriber to the event.  This provides an opportunity to
  85.          * handle the case where there is a non-repeating event that has
  86.          * already fired has a new subscriber.  
  87.          *
  88.          * @event subscribeEvent
  89.          * @type YAHOO.util.CustomEvent
  90.          * @param {Function} fn The function to execute
  91.          * @param {Object}   obj An object to be passed along when the event 
  92.          *                       fires
  93.          * @param {boolean|Object}  override If true, the obj passed in becomes 
  94.          *                                   the execution scope of the listener.
  95.          *                                   if an object, that object becomes the
  96.          *                                   the execution scope.
  97.          */
  98.         this.subscribeEvent = 
  99.                 new YAHOO.util.CustomEvent(onsubscribeType, this, true);
  100.     } 
  101. };
  102. /**
  103.  * Subscriber listener sigature constant.  The LIST type returns three
  104.  * parameters: the event type, the array of args passed to fire, and
  105.  * the optional custom object
  106.  * @property YAHOO.util.CustomEvent.LIST
  107.  * @static
  108.  * @type int
  109.  */
  110. YAHOO.util.CustomEvent.LIST = 0;
  111. /**
  112.  * Subscriber listener sigature constant.  The FLAT type returns two
  113.  * parameters: the first argument passed to fire and the optional 
  114.  * custom object
  115.  * @property YAHOO.util.CustomEvent.FLAT
  116.  * @static
  117.  * @type int
  118.  */
  119. YAHOO.util.CustomEvent.FLAT = 1;
  120. YAHOO.util.CustomEvent.prototype = {
  121.     /**
  122.      * Subscribes the caller to this event
  123.      * @method subscribe
  124.      * @param {Function} fn        The function to execute
  125.      * @param {Object}   obj       An object to be passed along when the event 
  126.      *                             fires
  127.      * @param {boolean|Object}  override If true, the obj passed in becomes 
  128.      *                                   the execution scope of the listener.
  129.      *                                   if an object, that object becomes the
  130.      *                                   the execution scope.
  131.      */
  132.     subscribe: function(fn, obj, override) {
  133.         if (!fn) {
  134. throw new Error("Invalid callback for subscriber to '" + this.type + "'");
  135.         }
  136.         if (this.subscribeEvent) {
  137.             this.subscribeEvent.fire(fn, obj, override);
  138.         }
  139.         this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, override) );
  140.     },
  141.     /**
  142.      * Unsubscribes subscribers.
  143.      * @method unsubscribe
  144.      * @param {Function} fn  The subscribed function to remove, if not supplied
  145.      *                       all will be removed
  146.      * @param {Object}   obj  The custom object passed to subscribe.  This is
  147.      *                        optional, but if supplied will be used to
  148.      *                        disambiguate multiple listeners that are the same
  149.      *                        (e.g., you subscribe many object using a function
  150.      *                        that lives on the prototype)
  151.      * @return {boolean} True if the subscriber was found and detached.
  152.      */
  153.     unsubscribe: function(fn, obj) {
  154.         if (!fn) {
  155.             return this.unsubscribeAll();
  156.         }
  157.         var found = false;
  158.         for (var i=0, len=this.subscribers.length; i<len; ++i) {
  159.             var s = this.subscribers[i];
  160.             if (s && s.contains(fn, obj)) {
  161.                 this._delete(i);
  162.                 found = true;
  163.             }
  164.         }
  165.         return found;
  166.     },
  167.     /**
  168.      * Notifies the subscribers.  The callback functions will be executed
  169.      * from the scope specified when the event was created, and with the 
  170.      * following parameters:
  171.      *   <ul>
  172.      *   <li>The type of event</li>
  173.      *   <li>All of the arguments fire() was executed with as an array</li>
  174.      *   <li>The custom object (if any) that was passed into the subscribe() 
  175.      *       method</li>
  176.      *   </ul>
  177.      * @method fire 
  178.      * @param {Object*} arguments an arbitrary set of parameters to pass to 
  179.      *                            the handler.
  180.      * @return {boolean} false if one of the subscribers returned false, 
  181.      *                   true otherwise
  182.      */
  183.     fire: function() {
  184.         var len=this.subscribers.length;
  185.         if (!len && this.silent) {
  186.             return true;
  187.         }
  188.         var args=[], ret=true, i, rebuild=false;
  189.         for (i=0; i<arguments.length; ++i) {
  190.             args.push(arguments[i]);
  191.         }
  192.         var argslength = args.length;
  193.         if (!this.silent) {
  194.         }
  195.         for (i=0; i<len; ++i) {
  196.             var s = this.subscribers[i];
  197.             if (!s) {
  198.                 rebuild=true;
  199.             } else {
  200.                 if (!this.silent) {
  201.                 }
  202.                 var scope = s.getScope(this.scope);
  203.                 if (this.signature == YAHOO.util.CustomEvent.FLAT) {
  204.                     var param = null;
  205.                     if (args.length > 0) {
  206.                         param = args[0];
  207.                     }
  208.                     ret = s.fn.call(scope, param, s.obj);
  209.                 } else {
  210.                     ret = s.fn.call(scope, this.type, args, s.obj);
  211.                 }
  212.                 if (false === ret) {
  213.                     if (!this.silent) {
  214.                     }
  215.                     //break;
  216.                     return false;
  217.                 }
  218.             }
  219.         }
  220.         if (rebuild) {
  221.             var newlist=[],subs=this.subscribers;
  222.             for (i=0,len=subs.length; i<len; ++i) {
  223.                 s = subs[i];
  224.                 newlist.push(subs[i]);
  225.             }
  226.             this.subscribers=newlist;
  227.         }
  228.         return true;
  229.     },
  230.     /**
  231.      * Removes all listeners
  232.      * @method unsubscribeAll
  233.      * @return {int} The number of listeners unsubscribed
  234.      */
  235.     unsubscribeAll: function() {
  236.         for (var i=0, len=this.subscribers.length; i<len; ++i) {
  237.             this._delete(len - 1 - i);
  238.         }
  239.         this.subscribers=[];
  240.         return i;
  241.     },
  242.     /**
  243.      * @method _delete
  244.      * @private
  245.      */
  246.     _delete: function(index) {
  247.         var s = this.subscribers[index];
  248.         if (s) {
  249.             delete s.fn;
  250.             delete s.obj;
  251.         }
  252.         this.subscribers[index]=null;
  253.     },
  254.     /**
  255.      * @method toString
  256.      */
  257.     toString: function() {
  258.          return "CustomEvent: " + "'" + this.type  + "', " + 
  259.              "scope: " + this.scope;
  260.     }
  261. };
  262. /////////////////////////////////////////////////////////////////////
  263. /**
  264.  * Stores the subscriber information to be used when the event fires.
  265.  * @param {Function} fn       The function to execute
  266.  * @param {Object}   obj      An object to be passed along when the event fires
  267.  * @param {boolean}  override If true, the obj passed in becomes the execution
  268.  *                            scope of the listener
  269.  * @class Subscriber
  270.  * @constructor
  271.  */
  272. YAHOO.util.Subscriber = function(fn, obj, override) {
  273.     /**
  274.      * The callback that will be execute when the event fires
  275.      * @property fn
  276.      * @type function
  277.      */
  278.     this.fn = fn;
  279.     /**
  280.      * An optional custom object that will passed to the callback when
  281.      * the event fires
  282.      * @property obj
  283.      * @type object
  284.      */
  285.     this.obj = YAHOO.lang.isUndefined(obj) ? null : obj;
  286.     /**
  287.      * The default execution scope for the event listener is defined when the
  288.      * event is created (usually the object which contains the event).
  289.      * By setting override to true, the execution scope becomes the custom
  290.      * object passed in by the subscriber.  If override is an object, that 
  291.      * object becomes the scope.
  292.      * @property override
  293.      * @type boolean|object
  294.      */
  295.     this.override = override;
  296. };
  297. /**
  298.  * Returns the execution scope for this listener.  If override was set to true
  299.  * the custom obj will be the scope.  If override is an object, that is the
  300.  * scope, otherwise the default scope will be used.
  301.  * @method getScope
  302.  * @param {Object} defaultScope the scope to use if this listener does not
  303.  *                              override it.
  304.  */
  305. YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {
  306.     if (this.override) {
  307.         if (this.override === true) {
  308.             return this.obj;
  309.         } else {
  310.             return this.override;
  311.         }
  312.     }
  313.     return defaultScope;
  314. };
  315. /**
  316.  * Returns true if the fn and obj match this objects properties.
  317.  * Used by the unsubscribe method to match the right subscriber.
  318.  *
  319.  * @method contains
  320.  * @param {Function} fn the function to execute
  321.  * @param {Object} obj an object to be passed along when the event fires
  322.  * @return {boolean} true if the supplied arguments match this 
  323.  *                   subscriber's signature.
  324.  */
  325. YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
  326.     if (obj) {
  327.         return (this.fn == fn && this.obj == obj);
  328.     } else {
  329.         return (this.fn == fn);
  330.     }
  331. };
  332. /**
  333.  * @method toString
  334.  */
  335. YAHOO.util.Subscriber.prototype.toString = function() {
  336.     return "Subscriber { obj: " + this.obj  + 
  337.            ", override: " +  (this.override || "no") + " }";
  338. };
  339. /**
  340.  * The Event Utility provides utilities for managing DOM Events and tools
  341.  * for building event systems
  342.  *
  343.  * @module event
  344.  * @title Event Utility
  345.  * @namespace YAHOO.util
  346.  * @requires yahoo
  347.  */
  348. // The first instance of Event will win if it is loaded more than once.
  349. // @TODO this needs to be changed so that only the state data that needs to
  350. // be preserved is kept, while methods are overwritten/added as needed.
  351. // This means that the module pattern can't be used.
  352. if (!YAHOO.util.Event) {
  353. /**
  354.  * The event utility provides functions to add and remove event listeners,
  355.  * event cleansing.  It also tries to automatically remove listeners it
  356.  * registers during the unload event.
  357.  *
  358.  * @class Event
  359.  * @static
  360.  */
  361.     YAHOO.util.Event = function() {
  362.         /**
  363.          * True after the onload event has fired
  364.          * @property loadComplete
  365.          * @type boolean
  366.          * @static
  367.          * @private
  368.          */
  369.         var loadComplete =  false;
  370.         /**
  371.          * True when the document is initially usable
  372.          * @property DOMReady
  373.          * @type boolean
  374.          * @static
  375.          * @private
  376.          */
  377.         var DOMReady = false;
  378.         /**
  379.          * Cache of wrapped listeners
  380.          * @property listeners
  381.          * @type array
  382.          * @static
  383.          * @private
  384.          */
  385.         var listeners = [];
  386.         /**
  387.          * User-defined unload function that will be fired before all events
  388.          * are detached
  389.          * @property unloadListeners
  390.          * @type array
  391.          * @static
  392.          * @private
  393.          */
  394.         var unloadListeners = [];
  395.         /**
  396.          * Cache of DOM0 event handlers to work around issues with DOM2 events
  397.          * in Safari
  398.          * @property legacyEvents
  399.          * @static
  400.          * @private
  401.          */
  402.         var legacyEvents = [];
  403.         /**
  404.          * Listener stack for DOM0 events
  405.          * @property legacyHandlers
  406.          * @static
  407.          * @private
  408.          */
  409.         var legacyHandlers = [];
  410.         /**
  411.          * The number of times to poll after window.onload.  This number is
  412.          * increased if additional late-bound handlers are requested after
  413.          * the page load.
  414.          * @property retryCount
  415.          * @static
  416.          * @private
  417.          */
  418.         var retryCount = 0;
  419.         /**
  420.          * onAvailable listeners
  421.          * @property onAvailStack
  422.          * @static
  423.          * @private
  424.          */
  425.         var onAvailStack = [];
  426.         /**
  427.          * Lookup table for legacy events
  428.          * @property legacyMap
  429.          * @static
  430.          * @private
  431.          */
  432.         var legacyMap = [];
  433.         /**
  434.          * Counter for auto id generation
  435.          * @property counter
  436.          * @static
  437.          * @private
  438.          */
  439.         var counter = 0;
  440.         
  441.         /**
  442.          * Normalized keycodes for webkit/safari
  443.          * @property webkitKeymap
  444.          * @type {int: int}
  445.          * @private
  446.          * @static
  447.          * @final
  448.          */
  449.         var webkitKeymap = {
  450.             63232: 38, // up
  451.             63233: 40, // down
  452.             63234: 37, // left
  453.             63235: 39  // right
  454.         };
  455.         return {
  456.             /**
  457.              * The number of times we should look for elements that are not
  458.              * in the DOM at the time the event is requested after the document
  459.              * has been loaded.  The default is 4000@amp;10 ms, so it will poll
  460.              * for 40 seconds or until all outstanding handlers are bound
  461.              * (whichever comes first).
  462.              * @property POLL_RETRYS
  463.              * @type int
  464.              * @static
  465.              * @final
  466.              */
  467.             POLL_RETRYS: 4000,
  468.             /**
  469.              * The poll interval in milliseconds
  470.              * @property POLL_INTERVAL
  471.              * @type int
  472.              * @static
  473.              * @final
  474.              */
  475.             POLL_INTERVAL: 10,
  476.             /**
  477.              * Element to bind, int constant
  478.              * @property EL
  479.              * @type int
  480.              * @static
  481.              * @final
  482.              */
  483.             EL: 0,
  484.             /**
  485.              * Type of event, int constant
  486.              * @property TYPE
  487.              * @type int
  488.              * @static
  489.              * @final
  490.              */
  491.             TYPE: 1,
  492.             /**
  493.              * Function to execute, int constant
  494.              * @property FN
  495.              * @type int
  496.              * @static
  497.              * @final
  498.              */
  499.             FN: 2,
  500.             /**
  501.              * Function wrapped for scope correction and cleanup, int constant
  502.              * @property WFN
  503.              * @type int
  504.              * @static
  505.              * @final
  506.              */
  507.             WFN: 3,
  508.             /**
  509.              * Object passed in by the user that will be returned as a 
  510.              * parameter to the callback, int constant
  511.              * @property OBJ
  512.              * @type int
  513.              * @static
  514.              * @final
  515.              */
  516.             OBJ: 3,
  517.             /**
  518.              * Adjusted scope, either the element we are registering the event
  519.              * on or the custom object passed in by the listener, int constant
  520.              * @property ADJ_SCOPE
  521.              * @type int
  522.              * @static
  523.              * @final
  524.              */
  525.             ADJ_SCOPE: 4,
  526.             /**
  527.              * addListener/removeListener can throw errors in unexpected scenarios.
  528.              * These errors are suppressed, the method returns false, and this property
  529.              * is set
  530.              * @property lastError
  531.              * @static
  532.              * @type Error
  533.              */
  534.             lastError: null,
  535.             /**
  536.              * Safari detection
  537.              * @property isSafari
  538.              * @private
  539.              * @static
  540.              * @deprecated use YAHOO.env.ua.webkit
  541.              */
  542.             isSafari: YAHOO.env.ua.webkit,
  543.             
  544.             /**
  545.              * webkit version
  546.              * @property webkit
  547.              * @type string
  548.              * @private
  549.              * @static
  550.              * @deprecated use YAHOO.env.ua.webkit
  551.              */
  552.             webkit: YAHOO.env.ua.webkit,
  553.             
  554.             /**
  555.              * IE detection 
  556.              * @property isIE
  557.              * @private
  558.              * @static
  559.              * @deprecated use YAHOO.env.ua.ie
  560.              */
  561.             isIE: YAHOO.env.ua.ie,
  562.             /**
  563.              * poll handle
  564.              * @property _interval
  565.              * @static
  566.              * @private
  567.              */
  568.             _interval: null,
  569.             /**
  570.              * @method startInterval
  571.              * @static
  572.              * @private
  573.              */
  574.             startInterval: function() {
  575.                 if (!this._interval) {
  576.                     var self = this;
  577.                     var callback = function() { self._tryPreloadAttach(); };
  578.                     this._interval = setInterval(callback, this.POLL_INTERVAL);
  579.                 }
  580.             },
  581.             /**
  582.              * Executes the supplied callback when the item with the supplied
  583.              * id is found.  This is meant to be used to execute behavior as
  584.              * soon as possible as the page loads.  If you use this after the
  585.              * initial page load it will poll for a fixed time for the element.
  586.              * The number of times it will poll and the frequency are
  587.              * configurable.  By default it will poll for 10 seconds.
  588.              *
  589.              * <p>The callback is executed with a single parameter:
  590.              * the custom object parameter, if provided.</p>
  591.              *
  592.              * @method onAvailable
  593.              *
  594.              * @param {string}   p_id the id of the element to look for.
  595.              * @param {function} p_fn what to execute when the element is found.
  596.              * @param {object}   p_obj an optional object to be passed back as
  597.              *                   a parameter to p_fn.
  598.              * @param {boolean|object}  p_override If set to true, p_fn will execute
  599.              *                   in the scope of p_obj, if set to an object it
  600.              *                   will execute in the scope of that object
  601.              *
  602.              * @static
  603.              */
  604.             onAvailable: function(p_id, p_fn, p_obj, p_override) {
  605.                 onAvailStack.push( { id:         p_id, 
  606.                                      fn:         p_fn, 
  607.                                      obj:        p_obj, 
  608.                                      override:   p_override, 
  609.                                      checkReady: false    } );
  610.                 retryCount = this.POLL_RETRYS;
  611.                 this.startInterval();
  612.             },
  613.             /**
  614.              * Executes the supplied callback when the DOM is first usable.  This
  615.              * will execute immediately if called after the DOMReady event has
  616.              * fired.   @todo the DOMContentReady event does not fire when the
  617.              * script is dynamically injected into the page.  This means the
  618.              * DOMReady custom event will never fire in FireFox or Opera when the
  619.              * library is injected.  It _will_ fire in Safari, and the IE 
  620.              * implementation would allow for us to fire it if the defered script
  621.              * is not available.  We want this to behave the same in all browsers.
  622.              * Is there a way to identify when the script has been injected 
  623.              * instead of included inline?  Is there a way to know whether the 
  624.              * window onload event has fired without having had a listener attached 
  625.              * to it when it did so?
  626.              *
  627.              * <p>The callback is a CustomEvent, so the signature is:</p>
  628.              * <p>type <string>, args <array>, customobject <object></p>
  629.              * <p>For DOMReady events, there are no fire argments, so the
  630.              * signature is:</p>
  631.              * <p>"DOMReady", [], obj</p>
  632.              *
  633.              *
  634.              * @method onDOMReady
  635.              *
  636.              * @param {function} p_fn what to execute when the element is found.
  637.              * @param {object}   p_obj an optional object to be passed back as
  638.              *                   a parameter to p_fn.
  639.              * @param {boolean|object}  p_scope If set to true, p_fn will execute
  640.              *                   in the scope of p_obj, if set to an object it
  641.              *                   will execute in the scope of that object
  642.              *
  643.              * @static
  644.              */
  645.             onDOMReady: function(p_fn, p_obj, p_override) {
  646.                 if (DOMReady) {
  647.                     setTimeout(function() {
  648.                         var s = window;
  649.                         if (p_override) {
  650.                             if (p_override === true) {
  651.                                 s = p_obj;
  652.                             } else {
  653.                                 s = p_override;
  654.                             }
  655.                         }
  656.                         p_fn.call(s, "DOMReady", [], p_obj);
  657.                     }, 0);
  658.                 } else {
  659.                     this.DOMReadyEvent.subscribe(p_fn, p_obj, p_override);
  660.                 }
  661.             },
  662.             /**
  663.              * Works the same way as onAvailable, but additionally checks the
  664.              * state of sibling elements to determine if the content of the
  665.              * available element is safe to modify.
  666.              *
  667.              * <p>The callback is executed with a single parameter:
  668.              * the custom object parameter, if provided.</p>
  669.              *
  670.              * @method onContentReady
  671.              *
  672.              * @param {string}   p_id the id of the element to look for.
  673.              * @param {function} p_fn what to execute when the element is ready.
  674.              * @param {object}   p_obj an optional object to be passed back as
  675.              *                   a parameter to p_fn.
  676.              * @param {boolean|object}  p_override If set to true, p_fn will execute
  677.              *                   in the scope of p_obj.  If an object, p_fn will
  678.              *                   exectute in the scope of that object
  679.              *
  680.              * @static
  681.              */
  682.             onContentReady: function(p_id, p_fn, p_obj, p_override) {
  683.                 onAvailStack.push( { id:         p_id, 
  684.                                      fn:         p_fn, 
  685.                                      obj:        p_obj, 
  686.                                      override:   p_override,
  687.                                      checkReady: true      } );
  688.                 retryCount = this.POLL_RETRYS;
  689.                 this.startInterval();
  690.             },
  691.             /**
  692.              * Appends an event handler
  693.              *
  694.              * @method addListener
  695.              *
  696.              * @param {String|HTMLElement|Array|NodeList} el An id, an element 
  697.              *  reference, or a collection of ids and/or elements to assign the 
  698.              *  listener to.
  699.              * @param {String}   sType     The type of event to append
  700.              * @param {Function} fn        The method the event invokes
  701.              * @param {Object}   obj    An arbitrary object that will be 
  702.              *                             passed as a parameter to the handler
  703.              * @param {Boolean|object}  override  If true, the obj passed in becomes
  704.              *                             the execution scope of the listener. If an
  705.              *                             object, this object becomes the execution
  706.              *                             scope.
  707.              * @return {Boolean} True if the action was successful or defered,
  708.              *                        false if one or more of the elements 
  709.              *                        could not have the listener attached,
  710.              *                        or if the operation throws an exception.
  711.              * @static
  712.              */
  713.             addListener: function(el, sType, fn, obj, override) {
  714.                 if (!fn || !fn.call) {
  715. // throw new TypeError(sType + " addListener call failed, callback undefined");
  716.                     return false;
  717.                 }
  718.                 // The el argument can be an array of elements or element ids.
  719.                 if ( this._isValidCollection(el)) {
  720.                     var ok = true;
  721.                     for (var i=0,len=el.length; i<len; ++i) {
  722.                         ok = this.on(el[i], 
  723.                                        sType, 
  724.                                        fn, 
  725.                                        obj, 
  726.                                        override) && ok;
  727.                     }
  728.                     return ok;
  729.                 } else if (YAHOO.lang.isString(el)) {
  730.                     var oEl = this.getEl(el);
  731.                     // If the el argument is a string, we assume it is 
  732.                     // actually the id of the element.  If the page is loaded
  733.                     // we convert el to the actual element, otherwise we 
  734.                     // defer attaching the event until onload event fires
  735.                     // check to see if we need to delay hooking up the event 
  736.                     // until after the page loads.
  737.                     if (oEl) {
  738.                         el = oEl;
  739.                     } else {
  740.                         // defer adding the event until the element is available
  741.                         this.onAvailable(el, function() {
  742.                            YAHOO.util.Event.on(el, sType, fn, obj, override);
  743.                         });
  744.                         return true;
  745.                     }
  746.                 }
  747.                 // Element should be an html element or an array if we get 
  748.                 // here.
  749.                 if (!el) {
  750.                     return false;
  751.                 }
  752.                 // we need to make sure we fire registered unload events 
  753.                 // prior to automatically unhooking them.  So we hang on to 
  754.                 // these instead of attaching them to the window and fire the
  755.                 // handles explicitly during our one unload event.
  756.                 if ("unload" == sType && obj !== this) {
  757.                     unloadListeners[unloadListeners.length] =
  758.                             [el, sType, fn, obj, override];
  759.                     return true;
  760.                 }
  761.                 // if the user chooses to override the scope, we use the custom
  762.                 // object passed in, otherwise the executing scope will be the
  763.                 // HTML element that the event is registered on
  764.                 var scope = el;
  765.                 if (override) {
  766.                     if (override === true) {
  767.                         scope = obj;
  768.                     } else {
  769.                         scope = override;
  770.                     }
  771.                 }
  772.                 // wrap the function so we can return the obj object when
  773.                 // the event fires;
  774.                 var wrappedFn = function(e) {
  775.                         return fn.call(scope, YAHOO.util.Event.getEvent(e), 
  776.                                 obj);
  777.                     };
  778.                 var li = [el, sType, fn, wrappedFn, scope];
  779.                 var index = listeners.length;
  780.                 // cache the listener so we can try to automatically unload
  781.                 listeners[index] = li;
  782.                 if (this.useLegacyEvent(el, sType)) {
  783.                     var legacyIndex = this.getLegacyIndex(el, sType);
  784.                     // Add a new dom0 wrapper if one is not detected for this
  785.                     // element
  786.                     if ( legacyIndex == -1 || 
  787.                                 el != legacyEvents[legacyIndex][0] ) {
  788.                         legacyIndex = legacyEvents.length;
  789.                         legacyMap[el.id + sType] = legacyIndex;
  790.                         // cache the signature for the DOM0 event, and 
  791.                         // include the existing handler for the event, if any
  792.                         legacyEvents[legacyIndex] = 
  793.                             [el, sType, el["on" + sType]];
  794.                         legacyHandlers[legacyIndex] = [];
  795.                         el["on" + sType] = 
  796.                             function(e) {
  797.                                 YAHOO.util.Event.fireLegacyEvent(
  798.                                     YAHOO.util.Event.getEvent(e), legacyIndex);
  799.                             };
  800.                     }
  801.                     // add a reference to the wrapped listener to our custom
  802.                     // stack of events
  803.                     //legacyHandlers[legacyIndex].push(index);
  804.                     legacyHandlers[legacyIndex].push(li);
  805.                 } else {
  806.                     try {
  807.                         this._simpleAdd(el, sType, wrappedFn, false);
  808.                     } catch(ex) {
  809.                         // handle an error trying to attach an event.  If it fails
  810.                         // we need to clean up the cache
  811.                         this.lastError = ex;
  812.                         this.removeListener(el, sType, fn);
  813.                         return false;
  814.                     }
  815.                 }
  816.                 return true;
  817.                 
  818.             },
  819.             /**
  820.              * When using legacy events, the handler is routed to this object
  821.              * so we can fire our custom listener stack.
  822.              * @method fireLegacyEvent
  823.              * @static
  824.              * @private
  825.              */
  826.             fireLegacyEvent: function(e, legacyIndex) {
  827.                 var ok=true,le,lh,li,scope,ret;
  828.                 
  829.                 lh = legacyHandlers[legacyIndex];
  830.                 for (var i=0,len=lh.length; i<len; ++i) {
  831.                     li = lh[i];
  832.                     if ( li && li[this.WFN] ) {
  833.                         scope = li[this.ADJ_SCOPE];
  834.                         ret = li[this.WFN].call(scope, e);
  835.                         ok = (ok && ret);
  836.                     }
  837.                 }
  838.                 // Fire the original handler if we replaced one.  We fire this
  839.                 // after the other events to keep stopPropagation/preventDefault
  840.                 // that happened in the DOM0 handler from touching our DOM2
  841.                 // substitute
  842.                 le = legacyEvents[legacyIndex];
  843.                 if (le && le[2]) {
  844.                     le[2](e);
  845.                 }
  846.                 
  847.                 return ok;
  848.             },
  849.             /**
  850.              * Returns the legacy event index that matches the supplied 
  851.              * signature
  852.              * @method getLegacyIndex
  853.              * @static
  854.              * @private
  855.              */
  856.             getLegacyIndex: function(el, sType) {
  857.                 var key = this.generateId(el) + sType;
  858.                 if (typeof legacyMap[key] == "undefined") { 
  859.                     return -1;
  860.                 } else {
  861.                     return legacyMap[key];
  862.                 }
  863.             },
  864.             /**
  865.              * Logic that determines when we should automatically use legacy
  866.              * events instead of DOM2 events.  Currently this is limited to old
  867.              * Safari browsers with a broken preventDefault
  868.              * @method useLegacyEvent
  869.              * @static
  870.              * @private
  871.              */
  872.             useLegacyEvent: function(el, sType) {
  873.                 if (this.webkit && ("click"==sType || "dblclick"==sType)) {
  874.                     var v = parseInt(this.webkit, 10);
  875.                     if (!isNaN(v) && v<418) {
  876.                         return true;
  877.                     }
  878.                 }
  879.                 return false;
  880.             },
  881.                     
  882.             /**
  883.              * Removes an event listener
  884.              *
  885.              * @method removeListener
  886.              *
  887.              * @param {String|HTMLElement|Array|NodeList} el An id, an element 
  888.              *  reference, or a collection of ids and/or elements to remove
  889.              *  the listener from.
  890.              * @param {String} sType the type of event to remove.
  891.              * @param {Function} fn the method the event invokes.  If fn is
  892.              *  undefined, then all event handlers for the type of event are 
  893.              *  removed.
  894.              * @return {boolean} true if the unbind was successful, false 
  895.              *  otherwise.
  896.              * @static
  897.              */
  898.             removeListener: function(el, sType, fn) {
  899.                 var i, len;
  900.                 // The el argument can be a string
  901.                 if (typeof el == "string") {
  902.                     el = this.getEl(el);
  903.                 // The el argument can be an array of elements or element ids.
  904.                 } else if ( this._isValidCollection(el)) {
  905.                     var ok = true;
  906.                     for (i=0,len=el.length; i<len; ++i) {
  907.                         ok = ( this.removeListener(el[i], sType, fn) && ok );
  908.                     }
  909.                     return ok;
  910.                 }
  911.                 if (!fn || !fn.call) {
  912.                     //return false;
  913.                     return this.purgeElement(el, false, sType);
  914.                 }
  915.                 if ("unload" == sType) {
  916.                     for (i=0, len=unloadListeners.length; i<len; i++) {
  917.                         var li = unloadListeners[i];
  918.                         if (li && 
  919.                             li[0] == el && 
  920.                             li[1] == sType && 
  921.                             li[2] == fn) {
  922.                                 //unloadListeners.splice(i, 1);
  923.                                 unloadListeners[i]=null;
  924.                                 return true;
  925.                         }
  926.                     }
  927.                     return false;
  928.                 }
  929.                 var cacheItem = null;
  930.                 // The index is a hidden parameter; needed to remove it from
  931.                 // the method signature because it was tempting users to
  932.                 // try and take advantage of it, which is not possible.
  933.                 var index = arguments[3];
  934.   
  935.                 if ("undefined" == typeof index) {
  936.                     index = this._getCacheIndex(el, sType, fn);
  937.                 }
  938.                 if (index >= 0) {
  939.                     cacheItem = listeners[index];
  940.                 }
  941.                 if (!el || !cacheItem) {
  942.                     return false;
  943.                 }
  944.                 if (this.useLegacyEvent(el, sType)) {
  945.                     var legacyIndex = this.getLegacyIndex(el, sType);
  946.                     var llist = legacyHandlers[legacyIndex];
  947.                     if (llist) {
  948.                         for (i=0, len=llist.length; i<len; ++i) {
  949.                             li = llist[i];
  950.                             if (li && 
  951.                                 li[this.EL] == el && 
  952.                                 li[this.TYPE] == sType && 
  953.                                 li[this.FN] == fn) {
  954.                                     //llist.splice(i, 1);
  955.                                     llist[i]=null;
  956.                                     break;
  957.                             }
  958.                         }
  959.                     }
  960.                 } else {
  961.                     try {
  962.                         this._simpleRemove(el, sType, cacheItem[this.WFN], false);
  963.                     } catch(ex) {
  964.                         this.lastError = ex;
  965.                         return false;
  966.                     }
  967.                 }
  968.                 // removed the wrapped handler
  969.                 delete listeners[index][this.WFN];
  970.                 delete listeners[index][this.FN];
  971.                 //listeners.splice(index, 1);
  972.                 listeners[index]=null;
  973.                 return true;
  974.             },
  975.             /**
  976.              * Returns the event's target element.  Safari sometimes provides
  977.              * a text node, and this is automatically resolved to the text
  978.              * node's parent so that it behaves like other browsers.
  979.              * @method getTarget
  980.              * @param {Event} ev the event
  981.              * @param {boolean} resolveTextNode when set to true the target's
  982.              *                  parent will be returned if the target is a 
  983.              *                  text node.  @deprecated, the text node is
  984.              *                  now resolved automatically
  985.              * @return {HTMLElement} the event's target
  986.              * @static
  987.              */
  988.             getTarget: function(ev, resolveTextNode) {
  989.                 var t = ev.target || ev.srcElement;
  990.                 return this.resolveTextNode(t);
  991.             },
  992.             /**
  993.              * In some cases, some browsers will return a text node inside
  994.              * the actual element that was targeted.  This normalizes the
  995.              * return value for getTarget and getRelatedTarget.
  996.              * @method resolveTextNode
  997.              * @param {HTMLElement} node node to resolve
  998.              * @return {HTMLElement} the normized node
  999.              * @static
  1000.              */
  1001.             resolveTextNode: function(node) {
  1002.                 if (node && 3 == node.nodeType) {
  1003.                     return node.parentNode;
  1004.                 } else {
  1005.                     return node;
  1006.                 }
  1007.             },
  1008.             /**
  1009.              * Returns the event's pageX
  1010.              * @method getPageX
  1011.              * @param {Event} ev the event
  1012.              * @return {int} the event's pageX
  1013.              * @static
  1014.              */
  1015.             getPageX: function(ev) {
  1016.                 var x = ev.pageX;
  1017.                 if (!x && 0 !== x) {
  1018.                     x = ev.clientX || 0;
  1019.                     if ( this.isIE ) {
  1020.                         x += this._getScrollLeft();
  1021.                     }
  1022.                 }
  1023.                 return x;
  1024.             },
  1025.             /**
  1026.              * Returns the event's pageY
  1027.              * @method getPageY
  1028.              * @param {Event} ev the event
  1029.              * @return {int} the event's pageY
  1030.              * @static
  1031.              */
  1032.             getPageY: function(ev) {
  1033.                 var y = ev.pageY;
  1034.                 if (!y && 0 !== y) {
  1035.                     y = ev.clientY || 0;
  1036.                     if ( this.isIE ) {
  1037.                         y += this._getScrollTop();
  1038.                     }
  1039.                 }
  1040.                 return y;
  1041.             },
  1042.             /**
  1043.              * Returns the pageX and pageY properties as an indexed array.
  1044.              * @method getXY
  1045.              * @param {Event} ev the event
  1046.              * @return {[x, y]} the pageX and pageY properties of the event
  1047.              * @static
  1048.              */
  1049.             getXY: function(ev) {
  1050.                 return [this.getPageX(ev), this.getPageY(ev)];
  1051.             },
  1052.             /**
  1053.              * Returns the event's related target 
  1054.              * @method getRelatedTarget
  1055.              * @param {Event} ev the event
  1056.              * @return {HTMLElement} the event's relatedTarget
  1057.              * @static
  1058.              */
  1059.             getRelatedTarget: function(ev) {
  1060.                 var t = ev.relatedTarget;
  1061.                 if (!t) {
  1062.                     if (ev.type == "mouseout") {
  1063.                         t = ev.toElement;
  1064.                     } else if (ev.type == "mouseover") {
  1065.                         t = ev.fromElement;
  1066.                     }
  1067.                 }
  1068.                 return this.resolveTextNode(t);
  1069.             },
  1070.             /**
  1071.              * Returns the time of the event.  If the time is not included, the
  1072.              * event is modified using the current time.
  1073.              * @method getTime
  1074.              * @param {Event} ev the event
  1075.              * @return {Date} the time of the event
  1076.              * @static
  1077.              */
  1078.             getTime: function(ev) {
  1079.                 if (!ev.time) {
  1080.                     var t = new Date().getTime();
  1081.                     try {
  1082.                         ev.time = t;
  1083.                     } catch(ex) { 
  1084.                         this.lastError = ex;
  1085.                         return t;
  1086.                     }
  1087.                 }
  1088.                 return ev.time;
  1089.             },
  1090.             /**
  1091.              * Convenience method for stopPropagation + preventDefault
  1092.              * @method stopEvent
  1093.              * @param {Event} ev the event
  1094.              * @static
  1095.              */
  1096.             stopEvent: function(ev) {
  1097.                 this.stopPropagation(ev);
  1098.                 this.preventDefault(ev);
  1099.             },
  1100.             /**
  1101.              * Stops event propagation
  1102.              * @method stopPropagation
  1103.              * @param {Event} ev the event
  1104.              * @static
  1105.              */
  1106.             stopPropagation: function(ev) {
  1107.                 if (ev.stopPropagation) {
  1108.                     ev.stopPropagation();
  1109.                 } else {
  1110.                     ev.cancelBubble = true;
  1111.                 }
  1112.             },
  1113.             /**
  1114.              * Prevents the default behavior of the event
  1115.              * @method preventDefault
  1116.              * @param {Event} ev the event
  1117.              * @static
  1118.              */
  1119.             preventDefault: function(ev) {
  1120.                 if (ev.preventDefault) {
  1121.                     ev.preventDefault();
  1122.                 } else {
  1123.                     ev.returnValue = false;
  1124.                 }
  1125.             },
  1126.              
  1127.             /**
  1128.              * Finds the event in the window object, the caller's arguments, or
  1129.              * in the arguments of another method in the callstack.  This is
  1130.              * executed automatically for events registered through the event
  1131.              * manager, so the implementer should not normally need to execute
  1132.              * this function at all.
  1133.              * @method getEvent
  1134.              * @param {Event} e the event parameter from the handler
  1135.              * @return {Event} the event 
  1136.              * @static
  1137.              */
  1138.             getEvent: function(e) {
  1139.                 var ev = e || window.event;
  1140.                 if (!ev) {
  1141.                     var c = this.getEvent.caller;
  1142.                     while (c) {
  1143.                         ev = c.arguments[0];
  1144.                         if (ev && Event == ev.constructor) {
  1145.                             break;
  1146.                         }
  1147.                         c = c.caller;
  1148.                     }
  1149.                 }
  1150.                 return ev;
  1151.             },
  1152.             /**
  1153.              * Returns the charcode for an event
  1154.              * @method getCharCode
  1155.              * @param {Event} ev the event
  1156.              * @return {int} the event's charCode
  1157.              * @static
  1158.              */
  1159.             getCharCode: function(ev) {
  1160.                 var code = ev.keyCode || ev.charCode || 0;
  1161.                 // webkit normalization
  1162.                 if (YAHOO.env.ua.webkit && (code in webkitKeymap)) {
  1163.                     code = webkitKeymap[code];
  1164.                 }
  1165.                 return code;
  1166.             },
  1167.             /**
  1168.              * Locating the saved event handler data by function ref
  1169.              *
  1170.              * @method _getCacheIndex
  1171.              * @static
  1172.              * @private
  1173.              */
  1174.             _getCacheIndex: function(el, sType, fn) {
  1175.                 for (var i=0,len=listeners.length; i<len; ++i) {
  1176.                     var li = listeners[i];
  1177.                     if ( li                 && 
  1178.                          li[this.FN] == fn  && 
  1179.                          li[this.EL] == el  && 
  1180.                          li[this.TYPE] == sType ) {
  1181.                         return i;
  1182.                     }
  1183.                 }
  1184.                 return -1;
  1185.             },
  1186.             /**
  1187.              * Generates an unique ID for the element if it does not already 
  1188.              * have one.
  1189.              * @method generateId
  1190.              * @param el the element to create the id for
  1191.              * @return {string} the resulting id of the element
  1192.              * @static
  1193.              */
  1194.             generateId: function(el) {
  1195.                 var id = el.id;
  1196.                 if (!id) {
  1197.                     id = "yuievtautoid-" + counter;
  1198.                     ++counter;
  1199.                     el.id = id;
  1200.                 }
  1201.                 return id;
  1202.             },
  1203.             /**
  1204.              * We want to be able to use getElementsByTagName as a collection
  1205.              * to attach a group of events to.  Unfortunately, different 
  1206.              * browsers return different types of collections.  This function
  1207.              * tests to determine if the object is array-like.  It will also 
  1208.              * fail if the object is an array, but is empty.
  1209.              * @method _isValidCollection
  1210.              * @param o the object to test
  1211.              * @return {boolean} true if the object is array-like and populated
  1212.              * @static
  1213.              * @private
  1214.              */
  1215.             _isValidCollection: function(o) {
  1216.                 try {
  1217.                     return ( o                    && // o is something
  1218.                              o.length             && // o is indexed
  1219.                              typeof o != "string" && // o is not a string
  1220.                              !o.tagName           && // o is not an HTML element
  1221.                              !o.alert             && // o is not a window
  1222.                              typeof o[0] != "undefined" );
  1223.                 } catch(e) {
  1224.                     return false;
  1225.                 }
  1226.             },
  1227.             /**
  1228.              * @private
  1229.              * @property elCache
  1230.              * DOM element cache
  1231.              * @static
  1232.              * @deprecated Elements are not cached due to issues that arise when
  1233.              * elements are removed and re-added
  1234.              */
  1235.             elCache: {},
  1236.             /**
  1237.              * We cache elements bound by id because when the unload event 
  1238.              * fires, we can no longer use document.getElementById
  1239.              * @method getEl
  1240.              * @static
  1241.              * @private
  1242.              * @deprecated Elements are not cached any longer
  1243.              */
  1244.             getEl: function(id) {
  1245.                 return document.getElementById(id);
  1246.             },
  1247.             /**
  1248.              * Clears the element cache
  1249.              * @deprecated Elements are not cached any longer
  1250.              * @method clearCache
  1251.              * @static
  1252.              * @private
  1253.              */
  1254.             clearCache: function() { },
  1255.             /**
  1256.              * Custom event the fires when the dom is initially usable
  1257.              * @event DOMReadyEvent
  1258.              */
  1259.             DOMReadyEvent: new YAHOO.util.CustomEvent("DOMReady", this),
  1260.             /**
  1261.              * hook up any deferred listeners
  1262.              * @method _load
  1263.              * @static
  1264.              * @private
  1265.              */
  1266.             _load: function(e) {
  1267.                 if (!loadComplete) {
  1268.                     loadComplete = true;
  1269.                     var EU = YAHOO.util.Event;
  1270.                     // Just in case DOMReady did not go off for some reason
  1271.                     EU._ready();
  1272.                     // Available elements may not have been detected before the
  1273.                     // window load event fires. Try to find them now so that the
  1274.                     // the user is more likely to get the onAvailable notifications
  1275.                     // before the window load notification
  1276.                     EU._tryPreloadAttach();
  1277.                     // Remove the listener to assist with the IE memory issue, but not
  1278.                     // for other browsers because FF 1.0x does not like it.
  1279.                     //if (this.isIE) {
  1280.                         //EU._simpleRemove(window, "load", EU._load);
  1281.                     //}
  1282.                 }
  1283.             },
  1284.             /**
  1285.              * Fires the DOMReady event listeners the first time the document is
  1286.              * usable.
  1287.              * @method _ready
  1288.              * @static
  1289.              * @private
  1290.              */
  1291.             _ready: function(e) {
  1292.                 if (!DOMReady) {
  1293.                     DOMReady=true;
  1294.                     var EU = YAHOO.util.Event;
  1295.                     // Fire the content ready custom event
  1296.                     EU.DOMReadyEvent.fire();
  1297.                     // Remove the DOMContentLoaded (FF/Opera)
  1298.                     EU._simpleRemove(document, "DOMContentLoaded", EU._ready);
  1299.                 }
  1300.             },
  1301.             /**
  1302.              * Polling function that runs before the onload event fires, 
  1303.              * attempting to attach to DOM Nodes as soon as they are 
  1304.              * available
  1305.              * @method _tryPreloadAttach
  1306.              * @static
  1307.              * @private
  1308.              */
  1309.             _tryPreloadAttach: function() {
  1310.                 if (this.locked) {
  1311.                     return false;
  1312.                 }
  1313.                 if (this.isIE) {
  1314.                     // Hold off if DOMReady has not fired and check current
  1315.                     // readyState to protect against the IE operation aborted
  1316.                     // issue.
  1317.                     //if (!DOMReady || "complete" !== document.readyState) {
  1318.                     if (!DOMReady) {
  1319.                         this.startInterval();
  1320.                         return false;
  1321.                     }
  1322.                 }
  1323.                 this.locked = true;
  1324.                 // keep trying until after the page is loaded.  We need to 
  1325.                 // check the page load state prior to trying to bind the 
  1326.                 // elements so that we can be certain all elements have been 
  1327.                 // tested appropriately
  1328.                 var tryAgain = !loadComplete;
  1329.                 if (!tryAgain) {
  1330.                     tryAgain = (retryCount > 0);
  1331.                 }
  1332.                 // onAvailable
  1333.                 var notAvail = [];
  1334.                 var executeItem = function (el, item) {
  1335.                     var scope = el;
  1336.                     if (item.override) {
  1337.                         if (item.override === true) {
  1338.                             scope = item.obj;
  1339.                         } else {
  1340.                             scope = item.override;
  1341.                         }
  1342.                     }
  1343.                     item.fn.call(scope, item.obj);
  1344.                 };
  1345.                 var i,len,item,el;
  1346.                 // onAvailable
  1347.                 for (i=0,len=onAvailStack.length; i<len; ++i) {
  1348.                     item = onAvailStack[i];
  1349.                     if (item && !item.checkReady) {
  1350.                         el = this.getEl(item.id);
  1351.                         if (el) {
  1352.                             executeItem(el, item);
  1353.                             onAvailStack[i] = null;
  1354.                         } else {
  1355.                             notAvail.push(item);
  1356.                         }
  1357.                     }
  1358.                 }
  1359.                 // onContentReady
  1360.                 for (i=0,len=onAvailStack.length; i<len; ++i) {
  1361.                     item = onAvailStack[i];
  1362.                     if (item && item.checkReady) {
  1363.                         el = this.getEl(item.id);
  1364.                         if (el) {
  1365.                             // The element is available, but not necessarily ready
  1366.                             // @todo should we test parentNode.nextSibling?
  1367.                             if (loadComplete || el.nextSibling) {
  1368.                                 executeItem(el, item);
  1369.                                 onAvailStack[i] = null;
  1370.                             }
  1371.                         } else {
  1372.                             notAvail.push(item);
  1373.                         }
  1374.                     }
  1375.                 }
  1376.                 retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
  1377.                 if (tryAgain) {
  1378.                     // we may need to strip the nulled out items here
  1379.                     this.startInterval();
  1380.                 } else {
  1381.                     clearInterval(this._interval);
  1382.                     this._interval = null;
  1383.                 }
  1384.                 this.locked = false;
  1385.                 return true;
  1386.             },
  1387.             /**
  1388.              * Removes all listeners attached to the given element via addListener.
  1389.              * Optionally, the node's children can also be purged.
  1390.              * Optionally, you can specify a specific type of event to remove.
  1391.              * @method purgeElement
  1392.              * @param {HTMLElement} el the element to purge
  1393.              * @param {boolean} recurse recursively purge this element's children
  1394.              * as well.  Use with caution.
  1395.              * @param {string} sType optional type of listener to purge. If
  1396.              * left out, all listeners will be removed
  1397.              * @static
  1398.              */
  1399.             purgeElement: function(el, recurse, sType) {
  1400.                 var elListeners = this.getListeners(el, sType);
  1401.                 if (elListeners) {
  1402.                     for (var i=0,len=elListeners.length; i<len ; ++i) {
  1403.                         var l = elListeners[i];
  1404.                         // can't use the index on the changing collection
  1405.                         this.removeListener(el, l.type, l.fn, l.index);
  1406.                         //this.removeListener(el, l.type, l.fn);
  1407.                     }
  1408.                 }
  1409.                 if (recurse && el && el.childNodes) {
  1410.                     for (i=0,len=el.childNodes.length; i<len ; ++i) {
  1411.                         this.purgeElement(el.childNodes[i], recurse, sType);
  1412.                     }
  1413.                 }
  1414.             },
  1415.             /**
  1416.              * Returns all listeners attached to the given element via addListener.
  1417.              * Optionally, you can specify a specific type of event to return.
  1418.              * @method getListeners
  1419.              * @param el {HTMLElement} the element to inspect 
  1420.              * @param sType {string} optional type of listener to return. If
  1421.              * left out, all listeners will be returned
  1422.              * @return {Object} the listener. Contains the following fields:
  1423.              * &nbsp;&nbsp;type:   (string)   the type of event
  1424.              * &nbsp;&nbsp;fn:     (function) the callback supplied to addListener
  1425.              * &nbsp;&nbsp;obj:    (object)   the custom object supplied to addListener
  1426.              * &nbsp;&nbsp;adjust: (boolean)  whether or not to adjust the default scope
  1427.              * &nbsp;&nbsp;index:  (int)      its position in the Event util listener cache
  1428.              * @static
  1429.              */           
  1430.             getListeners: function(el, sType) {
  1431.                 var results=[], searchLists;
  1432.                 if (!sType) {
  1433.                     searchLists = [listeners, unloadListeners];
  1434.                 } else if (sType == "unload") {
  1435.                     searchLists = [unloadListeners];
  1436.                 } else {
  1437.                     searchLists = [listeners];
  1438.                 }
  1439.                 for (var j=0;j<searchLists.length; ++j) {
  1440.                     var searchList = searchLists[j];
  1441.                     if (searchList && searchList.length > 0) {
  1442.                         for (var i=0,len=searchList.length; i<len ; ++i) {
  1443.                             var l = searchList[i];
  1444.                             if ( l  && l[this.EL] === el && 
  1445.                                     (!sType || sType === l[this.TYPE]) ) {
  1446.                                 results.push({
  1447.                                     type:   l[this.TYPE],
  1448.                                     fn:     l[this.FN],
  1449.                                     obj:    l[this.OBJ],
  1450.                                     adjust: l[this.ADJ_SCOPE],
  1451.                                     index:  i
  1452.                                 });
  1453.                             }
  1454.                         }
  1455.                     }
  1456.                 }
  1457.                 return (results.length) ? results : null;
  1458.             },
  1459.             /**
  1460.              * Removes all listeners registered by pe.event.  Called 
  1461.              * automatically during the unload event.
  1462.              * @method _unload
  1463.              * @static
  1464.              * @private
  1465.              */
  1466.             _unload: function(e) {
  1467.                 var EU = YAHOO.util.Event, i, j, l, len, index;
  1468.                 for (i=0,len=unloadListeners.length; i<len; ++i) {
  1469.                     l = unloadListeners[i];
  1470.                     if (l) {
  1471.                         var scope = window;
  1472.                         if (l[EU.ADJ_SCOPE]) {
  1473.                             if (l[EU.ADJ_SCOPE] === true) {
  1474.                                 scope = l[EU.OBJ];
  1475.                             } else {
  1476.                                 scope = l[EU.ADJ_SCOPE];
  1477.                             }
  1478.                         }
  1479.                         l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ] );
  1480.                         unloadListeners[i] = null;
  1481.                         l=null;
  1482.                         scope=null;
  1483.                     }
  1484.                 }
  1485.                 unloadListeners = null;
  1486.                 if (listeners && listeners.length > 0) {
  1487.                     j = listeners.length;
  1488.                     while (j) {
  1489.                         index = j-1;
  1490.                         l = listeners[index];
  1491.                         if (l) {
  1492.                             EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], index);
  1493.                         } 
  1494.                         j = j - 1;
  1495.                     }
  1496.                     l=null;
  1497.                     EU.clearCache();
  1498.                 }
  1499.                 for (i=0,len=legacyEvents.length; i<len; ++i) {
  1500.                     // dereference the element
  1501.                     //delete legacyEvents[i][0];
  1502.                     legacyEvents[i][0] = null;
  1503.                     // delete the array item
  1504.                     //delete legacyEvents[i];
  1505.                     legacyEvents[i] = null;
  1506.                 }
  1507.                 legacyEvents = null;
  1508.                 EU._simpleRemove(window, "unload", EU._unload);
  1509.             },
  1510.             /**
  1511.              * Returns scrollLeft
  1512.              * @method _getScrollLeft
  1513.              * @static
  1514.              * @private
  1515.              */
  1516.             _getScrollLeft: function() {
  1517.                 return this._getScroll()[1];
  1518.             },
  1519.             /**
  1520.              * Returns scrollTop
  1521.              * @method _getScrollTop
  1522.              * @static
  1523.              * @private
  1524.              */
  1525.             _getScrollTop: function() {
  1526.                 return this._getScroll()[0];
  1527.             },
  1528.             /**
  1529.              * Returns the scrollTop and scrollLeft.  Used to calculate the 
  1530.              * pageX and pageY in Internet Explorer
  1531.              * @method _getScroll
  1532.              * @static
  1533.              * @private
  1534.              */
  1535.             _getScroll: function() {
  1536.                 var dd = document.documentElement, db = document.body;
  1537.                 if (dd && (dd.scrollTop || dd.scrollLeft)) {
  1538.                     return [dd.scrollTop, dd.scrollLeft];
  1539.                 } else if (db) {
  1540.                     return [db.scrollTop, db.scrollLeft];
  1541.                 } else {
  1542.                     return [0, 0];
  1543.                 }
  1544.             },
  1545.             
  1546.             /**
  1547.              * Used by old versions of CustomEvent, restored for backwards
  1548.              * compatibility
  1549.              * @method regCE
  1550.              * @private
  1551.              * @static
  1552.              * @deprecated still here for backwards compatibility
  1553.              */
  1554.             regCE: function() {
  1555.                 // does nothing
  1556.             },
  1557.             /**
  1558.              * Adds a DOM event directly without the caching, cleanup, scope adj, etc
  1559.              *
  1560.              * @method _simpleAdd
  1561.              * @param {HTMLElement} el      the element to bind the handler to
  1562.              * @param {string}      sType   the type of event handler
  1563.              * @param {function}    fn      the callback to invoke
  1564.              * @param {boolen}      capture capture or bubble phase
  1565.              * @static
  1566.              * @private
  1567.              */
  1568.             _simpleAdd: function () {
  1569.                 if (window.addEventListener) {
  1570.                     return function(el, sType, fn, capture) {
  1571.                         el.addEventListener(sType, fn, (capture));
  1572.                     };
  1573.                 } else if (window.attachEvent) {
  1574.                     return function(el, sType, fn, capture) {
  1575.                         el.attachEvent("on" + sType, fn);
  1576.                     };
  1577.                 } else {
  1578.                     return function(){};
  1579.                 }
  1580.             }(),
  1581.             /**
  1582.              * Basic remove listener
  1583.              *
  1584.              * @method _simpleRemove
  1585.              * @param {HTMLElement} el      the element to bind the handler to
  1586.              * @param {string}      sType   the type of event handler
  1587.              * @param {function}    fn      the callback to invoke
  1588.              * @param {boolen}      capture capture or bubble phase
  1589.              * @static
  1590.              * @private
  1591.              */
  1592.             _simpleRemove: function() {
  1593.                 if (window.removeEventListener) {
  1594.                     return function (el, sType, fn, capture) {
  1595.                         el.removeEventListener(sType, fn, (capture));
  1596.                     };
  1597.                 } else if (window.detachEvent) {
  1598.                     return function (el, sType, fn) {
  1599.                         el.detachEvent("on" + sType, fn);
  1600.                     };
  1601.                 } else {
  1602.                     return function(){};
  1603.                 }
  1604.             }()
  1605.         };
  1606.     }();
  1607.     (function() {
  1608.         var EU = YAHOO.util.Event;
  1609.         /**
  1610.          * YAHOO.util.Event.on is an alias for addListener
  1611.          * @method on
  1612.          * @see addListener
  1613.          * @static
  1614.          */
  1615.         EU.on = EU.addListener;
  1616.         /////////////////////////////////////////////////////////////
  1617.         // DOMReady
  1618.         // based on work by: Dean Edwards/John Resig/Matthias Miller 
  1619.         // Internet Explorer: use the readyState of a defered script.
  1620.         // This isolates what appears to be a safe moment to manipulate
  1621.         // the DOM prior to when the document's readyState suggests
  1622.         // it is safe to do so.
  1623.         if (EU.isIE) {
  1624.             // Process onAvailable/onContentReady items when when the 
  1625.             // DOM is ready.
  1626.             YAHOO.util.Event.onDOMReady(
  1627.                     YAHOO.util.Event._tryPreloadAttach,
  1628.                     YAHOO.util.Event, true);
  1629.             var el, d=document, b=d.body;
  1630.             // If the library is being injected after window.onload, it
  1631.             // is not safe to document.write the script tag.  Detecting
  1632.             // this state doesn't appear possible, so we expect a flag
  1633.             // in YAHOO_config to be set if the library is being injected.
  1634.             if (("undefined" !== typeof YAHOO_config) && YAHOO_config.injecting) {
  1635.                 //var dr = d.readyState;
  1636.                 //if ("complete" === dr || "interactive" === dr) {
  1637.                     //YAHOO.util.Event._ready();
  1638.                     //YAHOO.util.Event._tryPreloadAttach();
  1639.                 //} else {
  1640.                     el = document.createElement("script");
  1641.                     var p=d.getElementsByTagName("head")[0] || b;
  1642.                     p.insertBefore(el, p.firstChild);
  1643.                 //}
  1644.             } else {
  1645.     d.write('<scr'+'ipt id="_yui_eu_dr" defer="true" src="//:"><'+'/script>');
  1646.                 el=document.getElementById("_yui_eu_dr");
  1647.             }
  1648.             
  1649.             if (el) {
  1650.                 el.onreadystatechange = function() {
  1651.                     if ("complete" === this.readyState) {
  1652.                         this.parentNode.removeChild(this);
  1653.                         YAHOO.util.Event._ready();
  1654.                     }
  1655.                 };
  1656.             } else {
  1657.                 // The library was likely injected into the page
  1658.                 // rendering onDOMReady unreliable
  1659.                 // YAHOO.util.Event._ready();
  1660.             }
  1661.             el=null;
  1662.         
  1663.         // Safari: The document's readyState in Safari currently will
  1664.         // change to loaded/complete before images are loaded.
  1665.         //} else if (EU.webkit) {
  1666.         } else if (EU.webkit) {
  1667.             EU._drwatch = setInterval(function(){
  1668.                 var rs=document.readyState;
  1669.                 if ("loaded" == rs || "complete" == rs) {
  1670.                     clearInterval(EU._drwatch);
  1671.                     EU._drwatch = null;
  1672.                     EU._ready();
  1673.                 }
  1674.             }, EU.POLL_INTERVAL); 
  1675.         // FireFox and Opera: These browsers provide a event for this
  1676.         // moment.
  1677.         } else {
  1678.             // @todo will this fire when the library is injected?
  1679.             EU._simpleAdd(document, "DOMContentLoaded", EU._ready);
  1680.         }
  1681.         /////////////////////////////////////////////////////////////
  1682.         EU._simpleAdd(window, "load", EU._load);
  1683.         EU._simpleAdd(window, "unload", EU._unload);
  1684.         EU._tryPreloadAttach();
  1685.     })();
  1686. }
  1687. /**
  1688.  * EventProvider is designed to be used with YAHOO.augment to wrap 
  1689.  * CustomEvents in an interface that allows events to be subscribed to 
  1690.  * and fired by name.  This makes it possible for implementing code to
  1691.  * subscribe to an event that either has not been created yet, or will
  1692.  * not be created at all.
  1693.  *
  1694.  * @Class EventProvider
  1695.  */
  1696. YAHOO.util.EventProvider = function() { };
  1697. YAHOO.util.EventProvider.prototype = {
  1698.     /**
  1699.      * Private storage of custom events
  1700.      * @property __yui_events
  1701.      * @type Object[]
  1702.      * @private
  1703.      */
  1704.     __yui_events: null,
  1705.     /**
  1706.      * Private storage of custom event subscribers
  1707.      * @property __yui_subscribers
  1708.      * @type Object[]
  1709.      * @private
  1710.      */
  1711.     __yui_subscribers: null,
  1712.     
  1713.     /**
  1714.      * Subscribe to a CustomEvent by event type
  1715.      *
  1716.      * @method subscribe
  1717.      * @param p_type     {string}   the type, or name of the event
  1718.      * @param p_fn       {function} the function to exectute when the event fires
  1719.      * @param p_obj      {Object}   An object to be passed along when the event 
  1720.      *                              fires
  1721.      * @param p_override {boolean}  If true, the obj passed in becomes the 
  1722.      *                              execution scope of the listener
  1723.      */
  1724.     subscribe: function(p_type, p_fn, p_obj, p_override) {
  1725.         this.__yui_events = this.__yui_events || {};
  1726.         var ce = this.__yui_events[p_type];
  1727.         if (ce) {
  1728.             ce.subscribe(p_fn, p_obj, p_override);
  1729.         } else {
  1730.             this.__yui_subscribers = this.__yui_subscribers || {};
  1731.             var subs = this.__yui_subscribers;
  1732.             if (!subs[p_type]) {
  1733.                 subs[p_type] = [];
  1734.             }
  1735.             subs[p_type].push(
  1736.                 { fn: p_fn, obj: p_obj, override: p_override } );
  1737.         }
  1738.     },
  1739.     /**
  1740.      * Unsubscribes one or more listeners the from the specified event
  1741.      * @method unsubscribe
  1742.      * @param p_type {string}   The type, or name of the event.  If the type
  1743.      *                          is not specified, it will attempt to remove
  1744.      *                          the listener from all hosted events.
  1745.      * @param p_fn   {Function} The subscribed function to unsubscribe, if not
  1746.      *                          supplied, all subscribers will be removed.
  1747.      * @param p_obj  {Object}   The custom object passed to subscribe.  This is
  1748.      *                        optional, but if supplied will be used to
  1749.      *                        disambiguate multiple listeners that are the same
  1750.      *                        (e.g., you subscribe many object using a function
  1751.      *                        that lives on the prototype)
  1752.      * @return {boolean} true if the subscriber was found and detached.
  1753.      */
  1754.     unsubscribe: function(p_type, p_fn, p_obj) {
  1755.         this.__yui_events = this.__yui_events || {};
  1756.         var evts = this.__yui_events;
  1757.         if (p_type) {
  1758.             var ce = evts[p_type];
  1759.             if (ce) {
  1760.                 return ce.unsubscribe(p_fn, p_obj);
  1761.             }
  1762.         } else {
  1763.             for (var i in evts) {
  1764.                 var ret = true;
  1765.                 if (YAHOO.lang.hasOwnProperty(evts, i)) {
  1766.                     ret = ret && evts[i].unsubscribe(p_fn, p_obj);
  1767.                 }
  1768.             }
  1769.             return ret;
  1770.         }
  1771.         return false;
  1772.     },
  1773.     
  1774.     /**
  1775.      * Removes all listeners from the specified event.  If the event type
  1776.      * is not specified, all listeners from all hosted custom events will
  1777.      * be removed.
  1778.      * @method unsubscribeAll
  1779.      * @param p_type {string}   The type, or name of the event
  1780.      */
  1781.     unsubscribeAll: function(p_type) {
  1782.         return this.unsubscribe(p_type);
  1783.     },
  1784.     /**
  1785.      * Creates a new custom event of the specified type.  If a custom event
  1786.      * by that name already exists, it will not be re-created.  In either
  1787.      * case the custom event is returned. 
  1788.      *
  1789.      * @method createEvent
  1790.      *
  1791.      * @param p_type {string} the type, or name of the event
  1792.      * @param p_config {object} optional config params.  Valid properties are:
  1793.      *
  1794.      *  <ul>
  1795.      *    <li>
  1796.      *      scope: defines the default execution scope.  If not defined
  1797.      *      the default scope will be this instance.
  1798.      *    </li>
  1799.      *    <li>
  1800.      *      silent: if true, the custom event will not generate log messages.
  1801.      *      This is false by default.
  1802.      *    </li>
  1803.      *    <li>
  1804.      *      onSubscribeCallback: specifies a callback to execute when the
  1805.      *      event has a new subscriber.  This will fire immediately for
  1806.      *      each queued subscriber if any exist prior to the creation of
  1807.      *      the event.
  1808.      *    </li>
  1809.      *  </ul>
  1810.      *
  1811.      *  @return {CustomEvent} the custom event
  1812.      *
  1813.      */
  1814.     createEvent: function(p_type, p_config) {
  1815.         this.__yui_events = this.__yui_events || {};
  1816.         var opts = p_config || {};
  1817.         var events = this.__yui_events;
  1818.         if (events[p_type]) {
  1819.         } else {
  1820.             var scope  = opts.scope  || this;
  1821.             var silent = (opts.silent);
  1822.             var ce = new YAHOO.util.CustomEvent(p_type, scope, silent,
  1823.                     YAHOO.util.CustomEvent.FLAT);
  1824.             events[p_type] = ce;
  1825.             if (opts.onSubscribeCallback) {
  1826.                 ce.subscribeEvent.subscribe(opts.onSubscribeCallback);
  1827.             }
  1828.             this.__yui_subscribers = this.__yui_subscribers || {};
  1829.             var qs = this.__yui_subscribers[p_type];
  1830.             if (qs) {
  1831.                 for (var i=0; i<qs.length; ++i) {
  1832.                     ce.subscribe(qs[i].fn, qs[i].obj, qs[i].override);
  1833.                 }
  1834.             }
  1835.         }
  1836.         return events[p_type];
  1837.     },
  1838.    /**
  1839.      * Fire a custom event by name.  The callback functions will be executed
  1840.      * from the scope specified when the event was created, and with the 
  1841.      * following parameters:
  1842.      *   <ul>
  1843.      *   <li>The first argument fire() was executed with</li>
  1844.      *   <li>The custom object (if any) that was passed into the subscribe() 
  1845.      *       method</li>
  1846.      *   </ul>
  1847.      * If the custom event has not been explicitly created, it will be
  1848.      * created now with the default config, scoped to the host object
  1849.      * @method fireEvent
  1850.      * @param p_type    {string}  the type, or name of the event
  1851.      * @param arguments {Object*} an arbitrary set of parameters to pass to 
  1852.      *                            the handler.
  1853.      * @return {boolean} the return value from CustomEvent.fire
  1854.      *                   
  1855.      */
  1856.     fireEvent: function(p_type, arg1, arg2, etc) {
  1857.         this.__yui_events = this.__yui_events || {};
  1858.         var ce = this.__yui_events[p_type];
  1859.         if (!ce) {
  1860.             return null;
  1861.         }
  1862.         var args = [];
  1863.         for (var i=1; i<arguments.length; ++i) {
  1864.             args.push(arguments[i]);
  1865.         }
  1866.         return ce.fire.apply(ce, args);
  1867.     },
  1868.     /**
  1869.      * Returns true if the custom event of the provided type has been created
  1870.      * with createEvent.
  1871.      * @method hasEvent
  1872.      * @param type {string} the type, or name of the event
  1873.      */
  1874.     hasEvent: function(type) {
  1875.         if (this.__yui_events) {
  1876.             if (this.__yui_events[type]) {
  1877.                 return true;
  1878.             }
  1879.         }
  1880.         return false;
  1881.     }
  1882. };
  1883. /**
  1884. * KeyListener is a utility that provides an easy interface for listening for
  1885. * keydown/keyup events fired against DOM elements.
  1886. * @namespace YAHOO.util
  1887. * @class KeyListener
  1888. * @constructor
  1889. * @param {HTMLElement} attachTo The element or element ID to which the key 
  1890. *                               event should be attached
  1891. * @param {String}      attachTo The element or element ID to which the key
  1892. *                               event should be attached
  1893. * @param {Object}      keyData  The object literal representing the key(s) 
  1894. *                               to detect. Possible attributes are 
  1895. *                               shift(boolean), alt(boolean), ctrl(boolean) 
  1896. *                               and keys(either an int or an array of ints 
  1897. *                               representing keycodes).
  1898. * @param {Function}    handler  The CustomEvent handler to fire when the 
  1899. *                               key event is detected
  1900. * @param {Object}      handler  An object literal representing the handler. 
  1901. * @param {String}      event    Optional. The event (keydown or keyup) to 
  1902. *                               listen for. Defaults automatically to keydown.
  1903. *
  1904. * @knownissue the "keypress" event is completely broken in Safari 2.x and below.
  1905. *             the workaround is use "keydown" for key listening.  However, if
  1906. *             it is desired to prevent the default behavior of the keystroke,
  1907. *             that can only be done on the keypress event.  This makes key
  1908. *             handling quite ugly.
  1909. * @knownissue keydown is also broken in Safari 2.x and below for the ESC key.
  1910. *             There currently is no workaround other than choosing another
  1911. *             key to listen for.
  1912. */
  1913. YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) {
  1914.     if (!attachTo) {
  1915.     } else if (!keyData) {
  1916.     } else if (!handler) {
  1917.     } 
  1918.     
  1919.     if (!event) {
  1920.         event = YAHOO.util.KeyListener.KEYDOWN;
  1921.     }
  1922.     /**
  1923.     * The CustomEvent fired internally when a key is pressed
  1924.     * @event keyEvent
  1925.     * @private
  1926.     * @param {Object} keyData The object literal representing the key(s) to 
  1927.     *                         detect. Possible attributes are shift(boolean), 
  1928.     *                         alt(boolean), ctrl(boolean) and keys(either an 
  1929.     *                         int or an array of ints representing keycodes).
  1930.     */
  1931.     var keyEvent = new YAHOO.util.CustomEvent("keyPressed");
  1932.     
  1933.     /**
  1934.     * The CustomEvent fired when the KeyListener is enabled via the enable() 
  1935.     * function
  1936.     * @event enabledEvent
  1937.     * @param {Object} keyData The object literal representing the key(s) to 
  1938.     *                         detect. Possible attributes are shift(boolean), 
  1939.     *                         alt(boolean), ctrl(boolean) and keys(either an 
  1940.     *                         int or an array of ints representing keycodes).
  1941.     */
  1942.     this.enabledEvent = new YAHOO.util.CustomEvent("enabled");
  1943.     /**
  1944.     * The CustomEvent fired when the KeyListener is disabled via the 
  1945.     * disable() function
  1946.     * @event disabledEvent
  1947.     * @param {Object} keyData The object literal representing the key(s) to 
  1948.     *                         detect. Possible attributes are shift(boolean), 
  1949.     *                         alt(boolean), ctrl(boolean) and keys(either an 
  1950.     *                         int or an array of ints representing keycodes).
  1951.     */
  1952.     this.disabledEvent = new YAHOO.util.CustomEvent("disabled");
  1953.     if (typeof attachTo == 'string') {
  1954.         attachTo = document.getElementById(attachTo);
  1955.     }
  1956.     if (typeof handler == 'function') {
  1957.         keyEvent.subscribe(handler);
  1958.     } else {
  1959.         keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope);
  1960.     }
  1961.     /**
  1962.     * Handles the key event when a key is pressed.
  1963.     * @method handleKeyPress
  1964.     * @param {DOMEvent} e   The keypress DOM event
  1965.     * @param {Object}   obj The DOM event scope object
  1966.     * @private
  1967.     */
  1968.     function handleKeyPress(e, obj) {
  1969.         if (! keyData.shift) {  
  1970.             keyData.shift = false; 
  1971.         }
  1972.         if (! keyData.alt) {    
  1973.             keyData.alt = false;
  1974.         }
  1975.         if (! keyData.ctrl) {
  1976.             keyData.ctrl = false;
  1977.         }
  1978.         // check held down modifying keys first
  1979.         if (e.shiftKey == keyData.shift && 
  1980.             e.altKey   == keyData.alt &&
  1981.             e.ctrlKey  == keyData.ctrl) { // if we pass this, all modifiers match
  1982.             
  1983.             var dataItem;
  1984.             var keyPressed;
  1985.             if (keyData.keys instanceof Array) {
  1986.                 for (var i=0;i<keyData.keys.length;i++) {
  1987.                     dataItem = keyData.keys[i];
  1988.                     if (dataItem == e.charCode ) {
  1989.                         keyEvent.fire(e.charCode, e);
  1990.                         break;
  1991.                     } else if (dataItem == e.keyCode) {
  1992.                         keyEvent.fire(e.keyCode, e);
  1993.                         break;
  1994.                     }
  1995.                 }
  1996.             } else {
  1997.                 dataItem = keyData.keys;
  1998.                 if (dataItem == e.charCode ) {
  1999.                     keyEvent.fire(e.charCode, e);
  2000.                 } else if (dataItem == e.keyCode) {
  2001.                     keyEvent.fire(e.keyCode, e);
  2002.                 }
  2003.             }
  2004.         }
  2005.     }
  2006.     /**
  2007.     * Enables the KeyListener by attaching the DOM event listeners to the 
  2008.     * target DOM element
  2009.     * @method enable
  2010.     */
  2011.     this.enable = function() {
  2012.         if (! this.enabled) {
  2013.             YAHOO.util.Event.addListener(attachTo, event, handleKeyPress);
  2014.             this.enabledEvent.fire(keyData);
  2015.         }
  2016.         /**
  2017.         * Boolean indicating the enabled/disabled state of the Tooltip
  2018.         * @property enabled
  2019.         * @type Boolean
  2020.         */
  2021.         this.enabled = true;
  2022.     };
  2023.     /**
  2024.     * Disables the KeyListener by removing the DOM event listeners from the 
  2025.     * target DOM element
  2026.     * @method disable
  2027.     */
  2028.     this.disable = function() {
  2029.         if (this.enabled) {
  2030.             YAHOO.util.Event.removeListener(attachTo, event, handleKeyPress);
  2031.             this.disabledEvent.fire(keyData);
  2032.         }
  2033.         this.enabled = false;
  2034.     };
  2035.     /**
  2036.     * Returns a String representation of the object.
  2037.     * @method toString
  2038.     * @return {String}  The string representation of the KeyListener
  2039.     */ 
  2040.     this.toString = function() {
  2041.         return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + 
  2042.                 (attachTo.id ? "[" + attachTo.id + "]" : "");
  2043.     };
  2044. };
  2045. /**
  2046. * Constant representing the DOM "keydown" event.
  2047. * @property YAHOO.util.KeyListener.KEYDOWN
  2048. * @static
  2049. * @final
  2050. * @type String
  2051. */
  2052. YAHOO.util.KeyListener.KEYDOWN = "keydown";
  2053. /**
  2054. * Constant representing the DOM "keyup" event.
  2055. * @property YAHOO.util.KeyListener.KEYUP
  2056. * @static
  2057. * @final
  2058. * @type String
  2059. */
  2060. YAHOO.util.KeyListener.KEYUP = "keyup";
  2061. YAHOO.register("event", YAHOO.util.Event, {version: "2.3.0", build: "442"});