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