Automatic installer.php lang files by installer_builder (20070726)
[moodle-linuxchix.git] / lib / yui / event / event.js
blob22c97ca2b3503c6c31a4c317db1b24fbb0465200
1 /*
2 Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 0.12.2
6 */
7 /**
8  * The CustomEvent class lets you define events for your application
9  * that can be subscribed to by one or more independent component.
10  *
11  * @param {String}  type The type of event, which is passed to the callback
12  *                  when the event fires
13  * @param {Object}  oScope The context the event will fire from.  "this" will
14  *                  refer to this object in the callback.  Default value: 
15  *                  the window object.  The listener can override this.
16  * @param {boolean} silent pass true to prevent the event from writing to
17  *                  the debugsystem
18  * @param {int}     signature the signature that the custom event subscriber
19  *                  will receive. YAHOO.util.CustomEvent.LIST or 
20  *                  YAHOO.util.CustomEvent.FLAT.  The default is
21  *                  YAHOO.util.CustomEvent.LIST.
22  * @namespace YAHOO.util
23  * @class CustomEvent
24  * @constructor
25  */
26 YAHOO.util.CustomEvent = function(type, oScope, silent, signature) {
28     /**
29      * The type of event, returned to subscribers when the event fires
30      * @property type
31      * @type string
32      */
33     this.type = type;
35     /**
36      * The scope the the event will fire from by default.  Defaults to the window 
37      * obj
38      * @property scope
39      * @type object
40      */
41     this.scope = oScope || window;
43     /**
44      * By default all custom events are logged in the debug build, set silent
45      * to true to disable debug outpu for this event.
46      * @property silent
47      * @type boolean
48      */
49     this.silent = silent;
51     /**
52      * Custom events support two styles of arguments provided to the event
53      * subscribers.  
54      * <ul>
55      * <li>YAHOO.util.CustomEvent.LIST: 
56      *   <ul>
57      *   <li>param1: event name</li>
58      *   <li>param2: array of arguments sent to fire</li>
59      *   <li>param3: <optional> a custom object supplied by the subscriber</li>
60      *   </ul>
61      * </li>
62      * <li>YAHOO.util.CustomEvent.FLAT
63      *   <ul>
64      *   <li>param1: the first argument passed to fire.  If you need to
65      *           pass multiple parameters, use and array or object literal</li>
66      *   <li>param2: <optional> a custom object supplied by the subscriber</li>
67      *   </ul>
68      * </li>
69      * </ul>
70      *   @property signature
71      *   @type int
72      */
73     this.signature = signature || YAHOO.util.CustomEvent.LIST;
75     /**
76      * The subscribers to this event
77      * @property subscribers
78      * @type Subscriber[]
79      */
80     this.subscribers = [];
82     if (!this.silent) {
83     }
85     var onsubscribeType = "_YUICEOnSubscribe";
87     // Only add subscribe events for events that are not generated by 
88     // CustomEvent
89     if (type !== onsubscribeType) {
91         /**
92          * Custom events provide a custom event that fires whenever there is
93          * a new subscriber to the event.  This provides an opportunity to
94          * handle the case where there is a non-repeating event that has
95          * already fired has a new subscriber.  
96          *
97          * @event subscribeEvent
98          * @type YAHOO.util.CustomEvent
99          * @param {Function} fn The function to execute
100          * @param {Object}   obj An object to be passed along when the event 
101          *                       fires
102          * @param {boolean|Object}  override If true, the obj passed in becomes 
103          *                                   the execution scope of the listener.
104          *                                   if an object, that object becomes the
105          *                                   the execution scope.
106          */
107         this.subscribeEvent = 
108                 new YAHOO.util.CustomEvent(onsubscribeType, this, true);
110     } 
114  * Subscriber listener sigature constant.  The LIST type returns three
115  * parameters: the event type, the array of args passed to fire, and
116  * the optional custom object
117  * @property YAHOO.util.CustomEvent.LIST
118  * @static
119  * @type int
120  */
121 YAHOO.util.CustomEvent.LIST = 0;
124  * Subscriber listener sigature constant.  The FLAT type returns two
125  * parameters: the first argument passed to fire and the optional 
126  * custom object
127  * @property YAHOO.util.CustomEvent.FLAT
128  * @static
129  * @type int
130  */
131 YAHOO.util.CustomEvent.FLAT = 1;
133 YAHOO.util.CustomEvent.prototype = {
135     /**
136      * Subscribes the caller to this event
137      * @method subscribe
138      * @param {Function} fn        The function to execute
139      * @param {Object}   obj       An object to be passed along when the event 
140      *                             fires
141      * @param {boolean|Object}  override If true, the obj passed in becomes 
142      *                                   the execution scope of the listener.
143      *                                   if an object, that object becomes the
144      *                                   the execution scope.
145      */
146     subscribe: function(fn, obj, override) {
147         if (this.subscribeEvent) {
148             this.subscribeEvent.fire(fn, obj, override);
149         }
151         this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, override) );
152     },
154     /**
155      * Unsubscribes the caller from this event
156      * @method unsubscribe
157      * @param {Function} fn  The function to execute
158      * @param {Object}   obj  The custom object passed to subscribe (optional)
159      * @return {boolean} True if the subscriber was found and detached.
160      */
161     unsubscribe: function(fn, obj) {
162         var found = false;
163         for (var i=0, len=this.subscribers.length; i<len; ++i) {
164             var s = this.subscribers[i];
165             if (s && s.contains(fn, obj)) {
166                 this._delete(i);
167                 found = true;
168             }
169         }
171         return found;
172     },
174     /**
175      * Notifies the subscribers.  The callback functions will be executed
176      * from the scope specified when the event was created, and with the 
177      * following parameters:
178      *   <ul>
179      *   <li>The type of event</li>
180      *   <li>All of the arguments fire() was executed with as an array</li>
181      *   <li>The custom object (if any) that was passed into the subscribe() 
182      *       method</li>
183      *   </ul>
184      * @method fire 
185      * @param {Object*} arguments an arbitrary set of parameters to pass to 
186      *                            the handler.
187      * @return {boolean} false if one of the subscribers returned false, 
188      *                   true otherwise
189      */
190     fire: function() {
191         var len=this.subscribers.length;
192         if (!len && this.silent) {
193             return true;
194         }
196         var args=[], ret=true, i;
198         for (i=0; i<arguments.length; ++i) {
199             args.push(arguments[i]);
200         }
202         var argslength = args.length;
204         if (!this.silent) {
205         }
207         for (i=0; i<len; ++i) {
208             var s = this.subscribers[i];
209             if (s) {
210                 if (!this.silent) {
211                 }
213                 var scope = s.getScope(this.scope);
215                 if (this.signature == YAHOO.util.CustomEvent.FLAT) {
216                     var param = null;
217                     if (args.length > 0) {
218                         param = args[0];
219                     }
220                     ret = s.fn.call(scope, param, s.obj);
221                 } else {
222                     ret = s.fn.call(scope, this.type, args, s.obj);
223                 }
224                 if (false === ret) {
225                     if (!this.silent) {
226                     }
228                     //break;
229                     return false;
230                 }
231             }
232         }
234         return true;
235     },
237     /**
238      * Removes all listeners
239      * @method unsubscribeAll
240      */
241     unsubscribeAll: function() {
242         for (var i=0, len=this.subscribers.length; i<len; ++i) {
243             this._delete(len - 1 - i);
244         }
245     },
247     /**
248      * @method _delete
249      * @private
250      */
251     _delete: function(index) {
252         var s = this.subscribers[index];
253         if (s) {
254             delete s.fn;
255             delete s.obj;
256         }
258         // delete this.subscribers[index];
259         this.subscribers.splice(index, 1);
260     },
262     /**
263      * @method toString
264      */
265     toString: function() {
266          return "CustomEvent: " + "'" + this.type  + "', " + 
267              "scope: " + this.scope;
269     }
272 /////////////////////////////////////////////////////////////////////
275  * Stores the subscriber information to be used when the event fires.
276  * @param {Function} fn       The function to execute
277  * @param {Object}   obj      An object to be passed along when the event fires
278  * @param {boolean}  override If true, the obj passed in becomes the execution
279  *                            scope of the listener
280  * @class Subscriber
281  * @constructor
282  */
283 YAHOO.util.Subscriber = function(fn, obj, override) {
285     /**
286      * The callback that will be execute when the event fires
287      * @property fn
288      * @type function
289      */
290     this.fn = fn;
292     /**
293      * An optional custom object that will passed to the callback when
294      * the event fires
295      * @property obj
296      * @type object
297      */
298     this.obj = obj || null;
300     /**
301      * The default execution scope for the event listener is defined when the
302      * event is created (usually the object which contains the event).
303      * By setting override to true, the execution scope becomes the custom
304      * object passed in by the subscriber.  If override is an object, that 
305      * object becomes the scope.
306      * @property override
307      * @type boolean|object
308      */
309     this.override = override;
314  * Returns the execution scope for this listener.  If override was set to true
315  * the custom obj will be the scope.  If override is an object, that is the
316  * scope, otherwise the default scope will be used.
317  * @method getScope
318  * @param {Object} defaultScope the scope to use if this listener does not
319  *                              override it.
320  */
321 YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {
322     if (this.override) {
323         if (this.override === true) {
324             return this.obj;
325         } else {
326             return this.override;
327         }
328     }
329     return defaultScope;
333  * Returns true if the fn and obj match this objects properties.
334  * Used by the unsubscribe method to match the right subscriber.
336  * @method contains
337  * @param {Function} fn the function to execute
338  * @param {Object} obj an object to be passed along when the event fires
339  * @return {boolean} true if the supplied arguments match this 
340  *                   subscriber's signature.
341  */
342 YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
343     if (obj) {
344         return (this.fn == fn && this.obj == obj);
345     } else {
346         return (this.fn == fn);
347     }
351  * @method toString
352  */
353 YAHOO.util.Subscriber.prototype.toString = function() {
354     return "Subscriber { obj: " + (this.obj || "")  + 
355            ", override: " +  (this.override || "no") + " }";
359  * The Event Utility provides utilities for managing DOM Events and tools
360  * for building event systems
362  * @module event
363  * @title Event Utility
364  * @namespace YAHOO.util
365  * @requires yahoo
366  */
368 // The first instance of Event will win if it is loaded more than once.
369 if (!YAHOO.util.Event) {
372  * The event utility provides functions to add and remove event listeners,
373  * event cleansing.  It also tries to automatically remove listeners it
374  * registers during the unload event.
376  * @class Event
377  * @static
378  */
379     YAHOO.util.Event = function() {
381         /**
382          * True after the onload event has fired
383          * @property loadComplete
384          * @type boolean
385          * @static
386          * @private
387          */
388         var loadComplete =  false;
390         /**
391          * Cache of wrapped listeners
392          * @property listeners
393          * @type array
394          * @static
395          * @private
396          */
397         var listeners = [];
399         /**
400          * User-defined unload function that will be fired before all events
401          * are detached
402          * @property unloadListeners
403          * @type array
404          * @static
405          * @private
406          */
407         var unloadListeners = [];
409         /**
410          * Cache of DOM0 event handlers to work around issues with DOM2 events
411          * in Safari
412          * @property legacyEvents
413          * @static
414          * @private
415          */
416         var legacyEvents = [];
418         /**
419          * Listener stack for DOM0 events
420          * @property legacyHandlers
421          * @static
422          * @private
423          */
424         var legacyHandlers = [];
426         /**
427          * The number of times to poll after window.onload.  This number is
428          * increased if additional late-bound handlers are requested after
429          * the page load.
430          * @property retryCount
431          * @static
432          * @private
433          */
434         var retryCount = 0;
436         /**
437          * onAvailable listeners
438          * @property onAvailStack
439          * @static
440          * @private
441          */
442         var onAvailStack = [];
444         /**
445          * Lookup table for legacy events
446          * @property legacyMap
447          * @static
448          * @private
449          */
450         var legacyMap = [];
452         /**
453          * Counter for auto id generation
454          * @property counter
455          * @static
456          * @private
457          */
458         var counter = 0;
460         return { // PREPROCESS
462             /**
463              * The number of times we should look for elements that are not
464              * in the DOM at the time the event is requested after the document
465              * has been loaded.  The default is 200@amp;50 ms, so it will poll
466              * for 10 seconds or until all outstanding handlers are bound
467              * (whichever comes first).
468              * @property POLL_RETRYS
469              * @type int
470              * @static
471              * @final
472              */
473             POLL_RETRYS: 200,
475             /**
476              * The poll interval in milliseconds
477              * @property POLL_INTERVAL
478              * @type int
479              * @static
480              * @final
481              */
482             POLL_INTERVAL: 20,
484             /**
485              * Element to bind, int constant
486              * @property EL
487              * @type int
488              * @static
489              * @final
490              */
491             EL: 0,
493             /**
494              * Type of event, int constant
495              * @property TYPE
496              * @type int
497              * @static
498              * @final
499              */
500             TYPE: 1,
502             /**
503              * Function to execute, int constant
504              * @property FN
505              * @type int
506              * @static
507              * @final
508              */
509             FN: 2,
511             /**
512              * Function wrapped for scope correction and cleanup, int constant
513              * @property WFN
514              * @type int
515              * @static
516              * @final
517              */
518             WFN: 3,
520             /**
521              * Object passed in by the user that will be returned as a 
522              * parameter to the callback, int constant
523              * @property OBJ
524              * @type int
525              * @static
526              * @final
527              */
528             OBJ: 3,
530             /**
531              * Adjusted scope, either the element we are registering the event
532              * on or the custom object passed in by the listener, int constant
533              * @property ADJ_SCOPE
534              * @type int
535              * @static
536              * @final
537              */
538             ADJ_SCOPE: 4,
540             /**
541              * Safari detection is necessary to work around the preventDefault
542              * bug that makes it so you can't cancel a href click from the 
543              * handler.  There is not a capabilities check we can use here.
544              * @property isSafari
545              * @private
546              * @static
547              */
548             isSafari: (/Safari|Konqueror|KHTML/gi).test(navigator.userAgent),
550             /**
551              * IE detection needed to properly calculate pageX and pageY.  
552              * capabilities checking didn't seem to work because another 
553              * browser that does not provide the properties have the values 
554              * calculated in a different manner than IE.
555              * @property isIE
556              * @private
557              * @static
558              */
559             isIE: (!this.isSafari && !navigator.userAgent.match(/opera/gi) && 
560                     navigator.userAgent.match(/msie/gi)),
562             /**
563              * poll handle
564              * @property _interval
565              * @private
566              */
567             _interval: null,
569             /**
570              * @method startInterval
571              * @static
572              * @private
573              */
574             startInterval: function() {
575                 if (!this._interval) {
576                     var self = this;
577                     var callback = function() { self._tryPreloadAttach(); };
578                     this._interval = setInterval(callback, this.POLL_INTERVAL);
579                     // this.timeout = setTimeout(callback, i);
580                 }
581             },
583             /**
584              * Executes the supplied callback when the item with the supplied
585              * id is found.  This is meant to be used to execute behavior as
586              * soon as possible as the page loads.  If you use this after the
587              * initial page load it will poll for a fixed time for the element.
588              * The number of times it will poll and the frequency are
589              * configurable.  By default it will poll for 10 seconds.
590              *
591              * @method onAvailable
592              *
593              * @param {string}   p_id the id of the element to look for.
594              * @param {function} p_fn what to execute when the element is found.
595              * @param {object}   p_obj an optional object to be passed back as
596              *                   a parameter to p_fn.
597              * @param {boolean}  p_override If set to true, p_fn will execute
598              *                   in the scope of p_obj
599              *
600              * @static
601              */
602             onAvailable: function(p_id, p_fn, p_obj, p_override) {
603                 onAvailStack.push( { id:         p_id, 
604                                      fn:         p_fn, 
605                                      obj:        p_obj, 
606                                      override:   p_override, 
607                                      checkReady: false    } );
609                 retryCount = this.POLL_RETRYS;
610                 this.startInterval();
611             },
613             /**
614              * Works the same way as onAvailable, but additionally checks the
615              * state of sibling elements to determine if the content of the
616              * available element is safe to modify.
617              *
618              * @method onContentReady
619              *
620              * @param {string}   p_id the id of the element to look for.
621              * @param {function} p_fn what to execute when the element is ready.
622              * @param {object}   p_obj an optional object to be passed back as
623              *                   a parameter to p_fn.
624              * @param {boolean}  p_override If set to true, p_fn will execute
625              *                   in the scope of p_obj
626              *
627              * @static
628              */
629             onContentReady: function(p_id, p_fn, p_obj, p_override) {
630                 onAvailStack.push( { id:         p_id, 
631                                      fn:         p_fn, 
632                                      obj:        p_obj, 
633                                      override:   p_override,
634                                      checkReady: true      } );
636                 retryCount = this.POLL_RETRYS;
637                 this.startInterval();
638             },
640             /**
641              * Appends an event handler
642              *
643              * @method addListener
644              *
645              * @param {Object}   el        The html element to assign the 
646              *                             event to
647              * @param {String}   sType     The type of event to append
648              * @param {Function} fn        The method the event invokes
649              * @param {Object}   obj    An arbitrary object that will be 
650              *                             passed as a parameter to the handler
651              * @param {boolean}  override  If true, the obj passed in becomes
652              *                             the execution scope of the listener
653              * @return {boolean} True if the action was successful or defered,
654              *                        false if one or more of the elements 
655              *                        could not have the listener attached,
656              *                        or if the operation throws an exception.
657              * @static
658              */
659             addListener: function(el, sType, fn, obj, override) {
661                 if (!fn || !fn.call) {
662                     return false;
663                 }
665                 // The el argument can be an array of elements or element ids.
666                 if ( this._isValidCollection(el)) {
667                     var ok = true;
668                     for (var i=0,len=el.length; i<len; ++i) {
669                         ok = this.on(el[i], 
670                                        sType, 
671                                        fn, 
672                                        obj, 
673                                        override) && ok;
674                     }
675                     return ok;
677                 } else if (typeof el == "string") {
678                     var oEl = this.getEl(el);
679                     // If the el argument is a string, we assume it is 
680                     // actually the id of the element.  If the page is loaded
681                     // we convert el to the actual element, otherwise we 
682                     // defer attaching the event until onload event fires
684                     // check to see if we need to delay hooking up the event 
685                     // until after the page loads.
686                     if (oEl) {
687                         el = oEl;
688                     } else {
689                         // defer adding the event until the element is available
690                         this.onAvailable(el, function() {
691                            YAHOO.util.Event.on(el, sType, fn, obj, override);
692                         });
694                         return true;
695                     }
696                 }
698                 // Element should be an html element or an array if we get 
699                 // here.
700                 if (!el) {
701                     return false;
702                 }
704                 // we need to make sure we fire registered unload events 
705                 // prior to automatically unhooking them.  So we hang on to 
706                 // these instead of attaching them to the window and fire the
707                 // handles explicitly during our one unload event.
708                 if ("unload" == sType && obj !== this) {
709                     unloadListeners[unloadListeners.length] =
710                             [el, sType, fn, obj, override];
711                     return true;
712                 }
715                 // if the user chooses to override the scope, we use the custom
716                 // object passed in, otherwise the executing scope will be the
717                 // HTML element that the event is registered on
718                 var scope = el;
719                 if (override) {
720                     if (override === true) {
721                         scope = obj;
722                     } else {
723                         scope = override;
724                     }
725                 }
727                 // wrap the function so we can return the obj object when
728                 // the event fires;
729                 var wrappedFn = function(e) {
730                         return fn.call(scope, YAHOO.util.Event.getEvent(e), 
731                                 obj);
732                     };
734                 var li = [el, sType, fn, wrappedFn, scope];
735                 var index = listeners.length;
736                 // cache the listener so we can try to automatically unload
737                 listeners[index] = li;
739                 if (this.useLegacyEvent(el, sType)) {
740                     var legacyIndex = this.getLegacyIndex(el, sType);
742                     // Add a new dom0 wrapper if one is not detected for this
743                     // element
744                     if ( legacyIndex == -1 || 
745                                 el != legacyEvents[legacyIndex][0] ) {
747                         legacyIndex = legacyEvents.length;
748                         legacyMap[el.id + sType] = legacyIndex;
750                         // cache the signature for the DOM0 event, and 
751                         // include the existing handler for the event, if any
752                         legacyEvents[legacyIndex] = 
753                             [el, sType, el["on" + sType]];
754                         legacyHandlers[legacyIndex] = [];
756                         el["on" + sType] = 
757                             function(e) {
758                                 YAHOO.util.Event.fireLegacyEvent(
759                                     YAHOO.util.Event.getEvent(e), legacyIndex);
760                             };
761                     }
763                     // add a reference to the wrapped listener to our custom
764                     // stack of events
765                     //legacyHandlers[legacyIndex].push(index);
766                     legacyHandlers[legacyIndex].push(li);
768                 } else {
769                     try {
770                         this._simpleAdd(el, sType, wrappedFn, false);
771                     } catch(e) {
772                         // handle an error trying to attach an event.  If it fails
773                         // we need to clean up the cache
774                         this.removeListener(el, sType, fn);
775                         return false;
776                     }
777                 }
779                 return true;
780                 
781             },
783             /**
784              * When using legacy events, the handler is routed to this object
785              * so we can fire our custom listener stack.
786              * @method fireLegacyEvent
787              * @static
788              * @private
789              */
790             fireLegacyEvent: function(e, legacyIndex) {
791                 var ok = true;
793                 var le = legacyHandlers[legacyIndex];
794                 for (var i=0,len=le.length; i<len; ++i) {
795                     var li = le[i];
796                     if ( li && li[this.WFN] ) {
797                         var scope = li[this.ADJ_SCOPE];
798                         var ret = li[this.WFN].call(scope, e);
799                         ok = (ok && ret);
800                     }
801                 }
803                 return ok;
804             },
806             /**
807              * Returns the legacy event index that matches the supplied 
808              * signature
809              * @method getLegacyIndex
810              * @static
811              * @private
812              */
813             getLegacyIndex: function(el, sType) {
814                 var key = this.generateId(el) + sType;
815                 if (typeof legacyMap[key] == "undefined") { 
816                     return -1;
817                 } else {
818                     return legacyMap[key];
819                 }
820             },
822             /**
823              * Logic that determines when we should automatically use legacy
824              * events instead of DOM2 events.
825              * @method useLegacyEvent
826              * @static
827              * @private
828              */
829             useLegacyEvent: function(el, sType) {
830                 if (!el.addEventListener && !el.attachEvent) {
831                     return true;
832                 } else if (this.isSafari) {
833                     if ("click" == sType || "dblclick" == sType) {
834                         return true;
835                     }
836                 }
837                 return false;
838             },
839                     
840             /**
841              * Removes an event handler
842              *
843              * @method removeListener
844              *
845              * @param {Object} el the html element or the id of the element to 
846              * assign the event to.
847              * @param {String} sType the type of event to remove.
848              * @param {Function} fn the method the event invokes.  If fn is
849              * undefined, then all event handlers for the type of event are 
850              * removed.
851              * @return {boolean} true if the unbind was successful, false 
852              * otherwise.
853              * @static
854              */
855             removeListener: function(el, sType, fn) {
856                 var i, len;
858                 // The el argument can be a string
859                 if (typeof el == "string") {
860                     el = this.getEl(el);
861                 // The el argument can be an array of elements or element ids.
862                 } else if ( this._isValidCollection(el)) {
863                     var ok = true;
864                     for (i=0,len=el.length; i<len; ++i) {
865                         ok = ( this.removeListener(el[i], sType, fn) && ok );
866                     }
867                     return ok;
868                 }
870                 if (!fn || !fn.call) {
871                     //return false;
872                     return this.purgeElement(el, false, sType);
873                 }
875                 if ("unload" == sType) {
877                     for (i=0, len=unloadListeners.length; i<len; i++) {
878                         var li = unloadListeners[i];
879                         if (li && 
880                             li[0] == el && 
881                             li[1] == sType && 
882                             li[2] == fn) {
883                                 unloadListeners.splice(i, 1);
884                                 return true;
885                         }
886                     }
888                     return false;
889                 }
891                 var cacheItem = null;
893                 // The index is a hidden parameter; needed to remove it from
894                 // the method signature because it was tempting users to
895                 // try and take advantage of it, which is not possible.
896                 var index = arguments[3];
897   
898                 if ("undefined" == typeof index) {
899                     index = this._getCacheIndex(el, sType, fn);
900                 }
902                 if (index >= 0) {
903                     cacheItem = listeners[index];
904                 }
906                 if (!el || !cacheItem) {
907                     return false;
908                 }
911                 if (this.useLegacyEvent(el, sType)) {
912                     var legacyIndex = this.getLegacyIndex(el, sType);
913                     var llist = legacyHandlers[legacyIndex];
914                     if (llist) {
915                         for (i=0, len=llist.length; i<len; ++i) {
916                             li = llist[i];
917                             if (li && 
918                                 li[this.EL] == el && 
919                                 li[this.TYPE] == sType && 
920                                 li[this.FN] == fn) {
921                                     llist.splice(i, 1);
922                                     break;
923                             }
924                         }
925                     }
927                 } else {
928                     try {
929                         this._simpleRemove(el, sType, cacheItem[this.WFN], false);
930                     } catch(e) {
931                         return false;
932                     }
933                 }
935                 // removed the wrapped handler
936                 delete listeners[index][this.WFN];
937                 delete listeners[index][this.FN];
938                 listeners.splice(index, 1);
940                 return true;
942             },
944             /**
945              * Returns the event's target element
946              * @method getTarget
947              * @param {Event} ev the event
948              * @param {boolean} resolveTextNode when set to true the target's
949              *                  parent will be returned if the target is a 
950              *                  text node.  @deprecated, the text node is
951              *                  now resolved automatically
952              * @return {HTMLElement} the event's target
953              * @static
954              */
955             getTarget: function(ev, resolveTextNode) {
956                 var t = ev.target || ev.srcElement;
957                 return this.resolveTextNode(t);
958             },
960             /**
961              * In some cases, some browsers will return a text node inside
962              * the actual element that was targeted.  This normalizes the
963              * return value for getTarget and getRelatedTarget.
964              * @method resolveTextNode
965              * @param {HTMLElement} node node to resolve
966              * @return {HTMLElement} the normized node
967              * @static
968              */
969             resolveTextNode: function(node) {
970                 // if (node && node.nodeName && 
971                         // "#TEXT" == node.nodeName.toUpperCase()) {
972                 if (node && 3 == node.nodeType) {
973                     return node.parentNode;
974                 } else {
975                     return node;
976                 }
977             },
979             /**
980              * Returns the event's pageX
981              * @method getPageX
982              * @param {Event} ev the event
983              * @return {int} the event's pageX
984              * @static
985              */
986             getPageX: function(ev) {
987                 var x = ev.pageX;
988                 if (!x && 0 !== x) {
989                     x = ev.clientX || 0;
991                     if ( this.isIE ) {
992                         x += this._getScrollLeft();
993                     }
994                 }
996                 return x;
997             },
999             /**
1000              * Returns the event's pageY
1001              * @method getPageY
1002              * @param {Event} ev the event
1003              * @return {int} the event's pageY
1004              * @static
1005              */
1006             getPageY: function(ev) {
1007                 var y = ev.pageY;
1008                 if (!y && 0 !== y) {
1009                     y = ev.clientY || 0;
1011                     if ( this.isIE ) {
1012                         y += this._getScrollTop();
1013                     }
1014                 }
1016                 return y;
1017             },
1019             /**
1020              * Returns the pageX and pageY properties as an indexed array.
1021              * @method getXY
1022              * @param {Event} ev the event
1023              * @return {[x, y]} the pageX and pageY properties of the event
1024              * @static
1025              */
1026             getXY: function(ev) {
1027                 return [this.getPageX(ev), this.getPageY(ev)];
1028             },
1030             /**
1031              * Returns the event's related target 
1032              * @method getRelatedTarget
1033              * @param {Event} ev the event
1034              * @return {HTMLElement} the event's relatedTarget
1035              * @static
1036              */
1037             getRelatedTarget: function(ev) {
1038                 var t = ev.relatedTarget;
1039                 if (!t) {
1040                     if (ev.type == "mouseout") {
1041                         t = ev.toElement;
1042                     } else if (ev.type == "mouseover") {
1043                         t = ev.fromElement;
1044                     }
1045                 }
1047                 return this.resolveTextNode(t);
1048             },
1050             /**
1051              * Returns the time of the event.  If the time is not included, the
1052              * event is modified using the current time.
1053              * @method getTime
1054              * @param {Event} ev the event
1055              * @return {Date} the time of the event
1056              * @static
1057              */
1058             getTime: function(ev) {
1059                 if (!ev.time) {
1060                     var t = new Date().getTime();
1061                     try {
1062                         ev.time = t;
1063                     } catch(e) { 
1064                         return t;
1065                     }
1066                 }
1068                 return ev.time;
1069             },
1071             /**
1072              * Convenience method for stopPropagation + preventDefault
1073              * @method stopEvent
1074              * @param {Event} ev the event
1075              * @static
1076              */
1077             stopEvent: function(ev) {
1078                 this.stopPropagation(ev);
1079                 this.preventDefault(ev);
1080             },
1082             /**
1083              * Stops event propagation
1084              * @method stopPropagation
1085              * @param {Event} ev the event
1086              * @static
1087              */
1088             stopPropagation: function(ev) {
1089                 if (ev.stopPropagation) {
1090                     ev.stopPropagation();
1091                 } else {
1092                     ev.cancelBubble = true;
1093                 }
1094             },
1096             /**
1097              * Prevents the default behavior of the event
1098              * @method preventDefault
1099              * @param {Event} ev the event
1100              * @static
1101              */
1102             preventDefault: function(ev) {
1103                 if (ev.preventDefault) {
1104                     ev.preventDefault();
1105                 } else {
1106                     ev.returnValue = false;
1107                 }
1108             },
1109              
1110             /**
1111              * Finds the event in the window object, the caller's arguments, or
1112              * in the arguments of another method in the callstack.  This is
1113              * executed automatically for events registered through the event
1114              * manager, so the implementer should not normally need to execute
1115              * this function at all.
1116              * @method getEvent
1117              * @param {Event} e the event parameter from the handler
1118              * @return {Event} the event 
1119              * @static
1120              */
1121             getEvent: function(e) {
1122                 var ev = e || window.event;
1124                 if (!ev) {
1125                     var c = this.getEvent.caller;
1126                     while (c) {
1127                         ev = c.arguments[0];
1128                         if (ev && Event == ev.constructor) {
1129                             break;
1130                         }
1131                         c = c.caller;
1132                     }
1133                 }
1135                 return ev;
1136             },
1138             /**
1139              * Returns the charcode for an event
1140              * @method getCharCode
1141              * @param {Event} ev the event
1142              * @return {int} the event's charCode
1143              * @static
1144              */
1145             getCharCode: function(ev) {
1146                 return ev.charCode || ev.keyCode || 0;
1147             },
1149             /**
1150              * Locating the saved event handler data by function ref
1151              *
1152              * @method _getCacheIndex
1153              * @static
1154              * @private
1155              */
1156             _getCacheIndex: function(el, sType, fn) {
1157                 for (var i=0,len=listeners.length; i<len; ++i) {
1158                     var li = listeners[i];
1159                     if ( li                 && 
1160                          li[this.FN] == fn  && 
1161                          li[this.EL] == el  && 
1162                          li[this.TYPE] == sType ) {
1163                         return i;
1164                     }
1165                 }
1167                 return -1;
1168             },
1170             /**
1171              * Generates an unique ID for the element if it does not already 
1172              * have one.
1173              * @method generateId
1174              * @param el the element to create the id for
1175              * @return {string} the resulting id of the element
1176              * @static
1177              */
1178             generateId: function(el) {
1179                 var id = el.id;
1181                 if (!id) {
1182                     id = "yuievtautoid-" + counter;
1183                     ++counter;
1184                     el.id = id;
1185                 }
1187                 return id;
1188             },
1190             /**
1191              * We want to be able to use getElementsByTagName as a collection
1192              * to attach a group of events to.  Unfortunately, different 
1193              * browsers return different types of collections.  This function
1194              * tests to determine if the object is array-like.  It will also 
1195              * fail if the object is an array, but is empty.
1196              * @method _isValidCollection
1197              * @param o the object to test
1198              * @return {boolean} true if the object is array-like and populated
1199              * @static
1200              * @private
1201              */
1202             _isValidCollection: function(o) {
1203                 // this.logger.debug(o.constructor.toString())
1204                 // this.logger.debug(typeof o)
1206                 return ( o                    && // o is something
1207                          o.length             && // o is indexed
1208                          typeof o != "string" && // o is not a string
1209                          !o.tagName           && // o is not an HTML element
1210                          !o.alert             && // o is not a window
1211                          typeof o[0] != "undefined" );
1213             },
1215             /**
1216              * @private
1217              * @property elCache
1218              * DOM element cache
1219              * @static
1220              */
1221             elCache: {},
1223             /**
1224              * We cache elements bound by id because when the unload event 
1225              * fires, we can no longer use document.getElementById
1226              * @method getEl
1227              * @static
1228              * @private
1229              */
1230             getEl: function(id) {
1231                 return document.getElementById(id);
1232             },
1234             /**
1235              * Clears the element cache
1236              * @deprecated Elements are not cached any longer
1237              * @method clearCache
1238              * @static
1239              * @private
1240              */
1241             clearCache: function() { },
1243             /**
1244              * hook up any deferred listeners
1245              * @method _load
1246              * @static
1247              * @private
1248              */
1249             _load: function(e) {
1250                 loadComplete = true;
1251                 var EU = YAHOO.util.Event;
1252                 // Remove the listener to assist with the IE memory issue, but not
1253                 // for other browsers because FF 1.0x does not like it.
1254                 if (this.isIE) {
1255                     EU._simpleRemove(window, "load", EU._load);
1256                 }
1257             },
1259             /**
1260              * Polling function that runs before the onload event fires, 
1261              * attempting to attach to DOM Nodes as soon as they are 
1262              * available
1263              * @method _tryPreloadAttach
1264              * @static
1265              * @private
1266              */
1267             _tryPreloadAttach: function() {
1269                 if (this.locked) {
1270                     return false;
1271                 }
1273                 this.locked = true;
1276                 // keep trying until after the page is loaded.  We need to 
1277                 // check the page load state prior to trying to bind the 
1278                 // elements so that we can be certain all elements have been 
1279                 // tested appropriately
1280                 var tryAgain = !loadComplete;
1281                 if (!tryAgain) {
1282                     tryAgain = (retryCount > 0);
1283                 }
1285                 // onAvailable
1286                 var notAvail = [];
1287                 for (var i=0,len=onAvailStack.length; i<len ; ++i) {
1288                     var item = onAvailStack[i];
1289                     if (item) {
1290                         var el = this.getEl(item.id);
1292                         if (el) {
1293                             // The element is available, but not necessarily ready
1294                             // @todo verify IE7 compatibility
1295                             // @todo should we test parentNode.nextSibling?
1296                             // @todo re-evaluate global content ready
1297                             if ( !item.checkReady || 
1298                                     loadComplete || 
1299                                     el.nextSibling ||
1300                                     (document && document.body) ) {
1302                                 var scope = el;
1303                                 if (item.override) {
1304                                     if (item.override === true) {
1305                                         scope = item.obj;
1306                                     } else {
1307                                         scope = item.override;
1308                                     }
1309                                 }
1310                                 item.fn.call(scope, item.obj);
1311                                 //delete onAvailStack[i];
1312                                 // null out instead of delete for Opera
1313                                 onAvailStack[i] = null;
1314                             }
1315                         } else {
1316                             notAvail.push(item);
1317                         }
1318                     }
1319                 }
1321                 retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
1323                 if (tryAgain) {
1324                     // we may need to strip the nulled out items here
1325                     this.startInterval();
1326                 } else {
1327                     clearInterval(this._interval);
1328                     this._interval = null;
1329                 }
1331                 this.locked = false;
1333                 return true;
1335             },
1337             /**
1338              * Removes all listeners attached to the given element via addListener.
1339              * Optionally, the node's children can also be purged.
1340              * Optionally, you can specify a specific type of event to remove.
1341              * @method purgeElement
1342              * @param {HTMLElement} el the element to purge
1343              * @param {boolean} recurse recursively purge this element's children
1344              * as well.  Use with caution.
1345              * @param {string} sType optional type of listener to purge. If
1346              * left out, all listeners will be removed
1347              * @static
1348              */
1349             purgeElement: function(el, recurse, sType) {
1350                 var elListeners = this.getListeners(el, sType);
1351                 if (elListeners) {
1352                     for (var i=0,len=elListeners.length; i<len ; ++i) {
1353                         var l = elListeners[i];
1354                         // can't use the index on the changing collection
1355                         //this.removeListener(el, l.type, l.fn, l.index);
1356                         this.removeListener(el, l.type, l.fn);
1357                     }
1358                 }
1360                 if (recurse && el && el.childNodes) {
1361                     for (i=0,len=el.childNodes.length; i<len ; ++i) {
1362                         this.purgeElement(el.childNodes[i], recurse, sType);
1363                     }
1364                 }
1365             },
1367             /**
1368              * Returns all listeners attached to the given element via addListener.
1369              * Optionally, you can specify a specific type of event to return.
1370              * @method getListeners
1371              * @param el {HTMLElement} the element to inspect 
1372              * @param sType {string} optional type of listener to return. If
1373              * left out, all listeners will be returned
1374              * @return {Object} the listener. Contains the following fields:
1375              * &nbsp;&nbsp;type:   (string)   the type of event
1376              * &nbsp;&nbsp;fn:     (function) the callback supplied to addListener
1377              * &nbsp;&nbsp;obj:    (object)   the custom object supplied to addListener
1378              * &nbsp;&nbsp;adjust: (boolean)  whether or not to adjust the default scope
1379              * &nbsp;&nbsp;index:  (int)      its position in the Event util listener cache
1380              * @static
1381              */           
1382             getListeners: function(el, sType) {
1383                 var elListeners = [];
1384                 if (listeners && listeners.length > 0) {
1385                     for (var i=0,len=listeners.length; i<len ; ++i) {
1386                         var l = listeners[i];
1387                         if ( l  && l[this.EL] === el && 
1388                                 (!sType || sType === l[this.TYPE]) ) {
1389                             elListeners.push({
1390                                 type:   l[this.TYPE],
1391                                 fn:     l[this.FN],
1392                                 obj:    l[this.OBJ],
1393                                 adjust: l[this.ADJ_SCOPE],
1394                                 index:  i
1395                             });
1396                         }
1397                     }
1398                 }
1400                 return (elListeners.length) ? elListeners : null;
1401             },
1403             /**
1404              * Removes all listeners registered by pe.event.  Called 
1405              * automatically during the unload event.
1406              * @method _unload
1407              * @static
1408              * @private
1409              */
1410             _unload: function(e) {
1412                 var EU = YAHOO.util.Event, i, j, l, len, index;
1414                 for (i=0,len=unloadListeners.length; i<len; ++i) {
1415                     l = unloadListeners[i];
1416                     if (l) {
1417                         var scope = window;
1418                         if (l[EU.ADJ_SCOPE]) {
1419                             if (l[EU.ADJ_SCOPE] === true) {
1420                                 scope = l[EU.OBJ];
1421                             } else {
1422                                 scope = l[EU.ADJ_SCOPE];
1423                             }
1424                         }
1425                         l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ] );
1426                         unloadListeners[i] = null;
1427                         l=null;
1428                         scope=null;
1429                     }
1430                 }
1432                 unloadListeners = null;
1434                 if (listeners && listeners.length > 0) {
1435                     j = listeners.length;
1436                     while (j) {
1437                         index = j-1;
1438                         l = listeners[index];
1439                         if (l) {
1440                             EU.removeListener(l[EU.EL], l[EU.TYPE], 
1441                                     l[EU.FN], index);
1442                         } 
1443                         j = j - 1;
1444                     }
1445                     l=null;
1447                     EU.clearCache();
1448                 }
1450                 for (i=0,len=legacyEvents.length; i<len; ++i) {
1451                     // dereference the element
1452                     //delete legacyEvents[i][0];
1453                     legacyEvents[i][0] = null;
1455                     // delete the array item
1456                     //delete legacyEvents[i];
1457                     legacyEvents[i] = null;
1458                 }
1460                 legacyEvents = null;
1462                 EU._simpleRemove(window, "unload", EU._unload);
1464             },
1466             /**
1467              * Returns scrollLeft
1468              * @method _getScrollLeft
1469              * @static
1470              * @private
1471              */
1472             _getScrollLeft: function() {
1473                 return this._getScroll()[1];
1474             },
1476             /**
1477              * Returns scrollTop
1478              * @method _getScrollTop
1479              * @static
1480              * @private
1481              */
1482             _getScrollTop: function() {
1483                 return this._getScroll()[0];
1484             },
1486             /**
1487              * Returns the scrollTop and scrollLeft.  Used to calculate the 
1488              * pageX and pageY in Internet Explorer
1489              * @method _getScroll
1490              * @static
1491              * @private
1492              */
1493             _getScroll: function() {
1494                 var dd = document.documentElement, db = document.body;
1495                 if (dd && (dd.scrollTop || dd.scrollLeft)) {
1496                     return [dd.scrollTop, dd.scrollLeft];
1497                 } else if (db) {
1498                     return [db.scrollTop, db.scrollLeft];
1499                 } else {
1500                     return [0, 0];
1501                 }
1502             },
1504             /**
1505              * Adds a DOM event directly without the caching, cleanup, scope adj, etc
1506              *
1507              * @method _simpleAdd
1508              * @param {HTMLElement} el      the element to bind the handler to
1509              * @param {string}      sType   the type of event handler
1510              * @param {function}    fn      the callback to invoke
1511              * @param {boolen}      capture capture or bubble phase
1512              * @static
1513              * @private
1514              */
1515             _simpleAdd: function () {
1516                 if (window.addEventListener) {
1517                     return function(el, sType, fn, capture) {
1518                         el.addEventListener(sType, fn, (capture));
1519                     };
1520                 } else if (window.attachEvent) {
1521                     return function(el, sType, fn, capture) {
1522                         el.attachEvent("on" + sType, fn);
1523                     };
1524                 } else {
1525                     return function(){};
1526                 }
1527             }(),
1529             /**
1530              * Basic remove listener
1531              *
1532              * @method _simpleRemove
1533              * @param {HTMLElement} el      the element to bind the handler to
1534              * @param {string}      sType   the type of event handler
1535              * @param {function}    fn      the callback to invoke
1536              * @param {boolen}      capture capture or bubble phase
1537              * @static
1538              * @private
1539              */
1540             _simpleRemove: function() {
1541                 if (window.removeEventListener) {
1542                     return function (el, sType, fn, capture) {
1543                         el.removeEventListener(sType, fn, (capture));
1544                     };
1545                 } else if (window.detachEvent) {
1546                     return function (el, sType, fn) {
1547                         el.detachEvent("on" + sType, fn);
1548                     };
1549                 } else {
1550                     return function(){};
1551                 }
1552             }()
1553         };
1555     }();
1557     (function() {
1558         var EU = YAHOO.util.Event;
1560         /**
1561          * YAHOO.util.Event.on is an alias for addListener
1562          * @method on
1563          * @see addListener
1564          * @static
1565          */
1566         EU.on = EU.addListener;
1568         // YAHOO.mix(EU, YAHOO.util.EventProvider.prototype);
1569         // EU.createEvent("DOMContentReady");
1570         // EU.subscribe("DOMContentReady", EU._load);
1572         if (document && document.body) {
1573             EU._load();
1574         } else {
1575             // EU._simpleAdd(document, "DOMContentLoaded", EU._load);
1576             EU._simpleAdd(window, "load", EU._load);
1577         }
1578         EU._simpleAdd(window, "unload", EU._unload);
1579         EU._tryPreloadAttach();
1580     })();
1584  * EventProvider is designed to be used with YAHOO.augment to wrap 
1585  * CustomEvents in an interface that allows events to be subscribed to 
1586  * and fired by name.  This makes it possible for implementing code to
1587  * subscribe to an event that either has not been created yet, or will
1588  * not be created at all.
1590  * @Class EventProvider
1591  */
1592 YAHOO.util.EventProvider = function() { };
1594 YAHOO.util.EventProvider.prototype = {
1596     /**
1597      * Private storage of custom events
1598      * @property __yui_events
1599      * @type Object[]
1600      * @private
1601      */
1602     __yui_events: null,
1604     /**
1605      * Private storage of custom event subscribers
1606      * @property __yui_subscribers
1607      * @type Object[]
1608      * @private
1609      */
1610     __yui_subscribers: null,
1611     
1612     /**
1613      * Subscribe to a CustomEvent by event type
1614      *
1615      * @method subscribe
1616      * @param p_type     {string}   the type, or name of the event
1617      * @param p_fn       {function} the function to exectute when the event fires
1618      * @param p_obj
1619      * @param p_obj      {Object}   An object to be passed along when the event 
1620      *                              fires
1621      * @param p_override {boolean}  If true, the obj passed in becomes the 
1622      *                              execution scope of the listener
1623      */
1624     subscribe: function(p_type, p_fn, p_obj, p_override) {
1626         this.__yui_events = this.__yui_events || {};
1627         var ce = this.__yui_events[p_type];
1629         if (ce) {
1630             ce.subscribe(p_fn, p_obj, p_override);
1631         } else {
1632             this.__yui_subscribers = this.__yui_subscribers || {};
1633             var subs = this.__yui_subscribers;
1634             if (!subs[p_type]) {
1635                 subs[p_type] = [];
1636             }
1637             subs[p_type].push(
1638                 { fn: p_fn, obj: p_obj, override: p_override } );
1639         }
1640     },
1642     /**
1643      * Unsubscribes the from the specified event
1644      * @method unsubscribe
1645      * @param p_type {string}   The type, or name of the event
1646      * @param p_fn   {Function} The function to execute
1647      * @param p_obj  {Object}   The custom object passed to subscribe (optional)
1648      * @return {boolean} true if the subscriber was found and detached.
1649      */
1650     unsubscribe: function(p_type, p_fn, p_obj) {
1651         this.__yui_events = this.__yui_events || {};
1652         var ce = this.__yui_events[p_type];
1653         if (ce) {
1654             return ce.unsubscribe(p_fn, p_obj);
1655         } else {
1656             return false;
1657         }
1658     },
1660     /**
1661      * Creates a new custom event of the specified type.  If a custom event
1662      * by that name already exists, it will not be re-created.  In either
1663      * case the custom event is returned. 
1664      *
1665      * @method createEvent
1666      *
1667      * @param p_type {string} the type, or name of the event
1668      * @param p_config {object} optional config params.  Valid properties are:
1669      *
1670      *  <ul>
1671      *    <li>
1672      *      scope: defines the default execution scope.  If not defined
1673      *      the default scope will be this instance.
1674      *    </li>
1675      *    <li>
1676      *      silent: if true, the custom event will not generate log messages.
1677      *      This is false by default.
1678      *    </li>
1679      *    <li>
1680      *      onSubscribeCallback: specifies a callback to execute when the
1681      *      event has a new subscriber.  This will fire immediately for
1682      *      each queued subscriber if any exist prior to the creation of
1683      *      the event.
1684      *    </li>
1685      *  </ul>
1686      *
1687      *  @return {CustomEvent} the custom event
1688      *
1689      */
1690     createEvent: function(p_type, p_config) {
1692         this.__yui_events = this.__yui_events || {};
1693         var opts = p_config || {};
1694         var events = this.__yui_events;
1696         if (events[p_type]) {
1697         } else {
1699             var scope  = opts.scope  || this;
1700             var silent = opts.silent || null;
1702             var ce = new YAHOO.util.CustomEvent(p_type, scope, silent,
1703                     YAHOO.util.CustomEvent.FLAT);
1704             events[p_type] = ce;
1706             if (opts.onSubscribeCallback) {
1707                 ce.subscribeEvent.subscribe(opts.onSubscribeCallback);
1708             }
1710             this.__yui_subscribers = this.__yui_subscribers || {};
1711             var qs = this.__yui_subscribers[p_type];
1713             if (qs) {
1714                 for (var i=0; i<qs.length; ++i) {
1715                     ce.subscribe(qs[i].fn, qs[i].obj, qs[i].override);
1716                 }
1717             }
1718         }
1720         return events[p_type];
1721     },
1723    /**
1724      * Fire a custom event by name.  The callback functions will be executed
1725      * from the scope specified when the event was created, and with the 
1726      * following parameters:
1727      *   <ul>
1728      *   <li>The first argument fire() was executed with</li>
1729      *   <li>The custom object (if any) that was passed into the subscribe() 
1730      *       method</li>
1731      *   </ul>
1732      * @method fireEvent
1733      * @param p_type    {string}  the type, or name of the event
1734      * @param arguments {Object*} an arbitrary set of parameters to pass to 
1735      *                            the handler.
1736      * @return {boolean} the return value from CustomEvent.fire, or null if 
1737      *                   the custom event does not exist.
1738      */
1739     fireEvent: function(p_type, arg1, arg2, etc) {
1741         this.__yui_events = this.__yui_events || {};
1742         var ce = this.__yui_events[p_type];
1744         if (ce) {
1745             var args = [];
1746             for (var i=1; i<arguments.length; ++i) {
1747                 args.push(arguments[i]);
1748             }
1749             return ce.fire.apply(ce, args);
1750         } else {
1751             return null;
1752         }
1753     },
1755     /**
1756      * Returns true if the custom event of the provided type has been created
1757      * with createEvent.
1758      * @method hasEvent
1759      * @param type {string} the type, or name of the event
1760      */
1761     hasEvent: function(type) {
1762         if (this.__yui_events) {
1763             if (this.__yui_events[type]) {
1764                 return true;
1765             }
1766         }
1767         return false;
1768     }