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