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