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