2 Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 * The CustomEvent class lets you define events for your application
9 * that can be subscribed to by one or more independent component.
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
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
26 YAHOO
.util
.CustomEvent = function(type
, oScope
, silent
, signature
) {
29 * The type of event, returned to subscribers when the event fires
36 * The scope the the event will fire from by default. Defaults to the window
41 this.scope
= oScope
|| window
;
44 * By default all custom events are logged in the debug build, set silent
45 * to true to disable debug outpu for this event.
52 * Custom events support two styles of arguments provided to the event
55 * <li>YAHOO.util.CustomEvent.LIST:
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>
62 * <li>YAHOO.util.CustomEvent.FLAT
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>
73 this.signature
= signature
|| YAHOO
.util
.CustomEvent
.LIST
;
76 * The subscribers to this event
77 * @property subscribers
80 this.subscribers
= [];
85 var onsubscribeType
= "_YUICEOnSubscribe";
87 // Only add subscribe events for events that are not generated by
89 if (type
!== onsubscribeType
) {
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.
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
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.
107 this.subscribeEvent
=
108 new YAHOO
.util
.CustomEvent(onsubscribeType
, this, true);
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
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
127 * @property YAHOO.util.CustomEvent.FLAT
131 YAHOO
.util
.CustomEvent
.FLAT
= 1;
133 YAHOO
.util
.CustomEvent
.prototype = {
136 * Subscribes the caller to this event
138 * @param {Function} fn The function to execute
139 * @param {Object} obj An object to be passed along when the event
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.
146 subscribe: function(fn
, obj
, override
) {
147 if (this.subscribeEvent
) {
148 this.subscribeEvent
.fire(fn
, obj
, override
);
151 this.subscribers
.push( new YAHOO
.util
.Subscriber(fn
, obj
, override
) );
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.
161 unsubscribe: function(fn
, obj
) {
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
)) {
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:
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()
185 * @param {Object*} arguments an arbitrary set of parameters to pass to
187 * @return {boolean} false if one of the subscribers returned false,
191 var len
=this.subscribers
.length
;
192 if (!len
&& this.silent
) {
196 var args
=[], ret
=true, i
;
198 for (i
=0; i
<arguments
.length
; ++i
) {
199 args
.push(arguments
[i
]);
202 var argslength
= args
.length
;
207 for (i
=0; i
<len
; ++i
) {
208 var s
= this.subscribers
[i
];
213 var scope
= s
.getScope(this.scope
);
215 if (this.signature
== YAHOO
.util
.CustomEvent
.FLAT
) {
217 if (args
.length
> 0) {
220 ret
= s
.fn
.call(scope
, param
, s
.obj
);
222 ret
= s
.fn
.call(scope
, this.type
, args
, s
.obj
);
238 * Removes all listeners
239 * @method unsubscribeAll
241 unsubscribeAll: function() {
242 for (var i
=0, len
=this.subscribers
.length
; i
<len
; ++i
) {
243 this._delete(len
- 1 - i
);
251 _delete: function(index
) {
252 var s
= this.subscribers
[index
];
258 // delete this.subscribers[index];
259 this.subscribers
.splice(index
, 1);
265 toString: function() {
266 return "CustomEvent: " + "'" + this.type
+ "', " +
267 "scope: " + this.scope
;
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
283 YAHOO
.util
.Subscriber = function(fn
, obj
, override
) {
286 * The callback that will be execute when the event fires
293 * An optional custom object that will passed to the callback when
298 this.obj
= obj
|| null;
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.
307 * @type boolean|object
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.
318 * @param {Object} defaultScope the scope to use if this listener does not
321 YAHOO
.util
.Subscriber
.prototype.getScope = function(defaultScope
) {
323 if (this.override
=== true) {
326 return this.override
;
333 * Returns true if the fn and obj match this objects properties.
334 * Used by the unsubscribe method to match the right subscriber.
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.
342 YAHOO
.util
.Subscriber
.prototype.contains = function(fn
, obj
) {
344 return (this.fn
== fn
&& this.obj
== obj
);
346 return (this.fn
== fn
);
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
363 * @title Event Utility
364 * @namespace YAHOO.util
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.
379 YAHOO
.util
.Event = function() {
382 * True after the onload event has fired
383 * @property loadComplete
388 var loadComplete
= false;
391 * Cache of wrapped listeners
392 * @property listeners
400 * User-defined unload function that will be fired before all events
402 * @property unloadListeners
407 var unloadListeners
= [];
410 * Cache of DOM0 event handlers to work around issues with DOM2 events
412 * @property legacyEvents
416 var legacyEvents
= [];
419 * Listener stack for DOM0 events
420 * @property legacyHandlers
424 var legacyHandlers
= [];
427 * The number of times to poll after window.onload. This number is
428 * increased if additional late-bound handlers are requested after
430 * @property retryCount
437 * onAvailable listeners
438 * @property onAvailStack
442 var onAvailStack
= [];
445 * Lookup table for legacy events
446 * @property legacyMap
453 * Counter for auto id generation
460 return { // PREPROCESS
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
476 * The poll interval in milliseconds
477 * @property POLL_INTERVAL
485 * Element to bind, int constant
494 * Type of event, int constant
503 * Function to execute, int constant
512 * Function wrapped for scope correction and cleanup, int constant
521 * Object passed in by the user that will be returned as a
522 * parameter to the callback, int constant
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
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.
548 isSafari
: (/Safari|Konqueror|KHTML/gi).test(navigator
.userAgent
),
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.
559 isIE
: (!this.isSafari
&& !navigator
.userAgent
.match(/opera/gi) &&
560 navigator
.userAgent
.match(/msie/gi)),
564 * @property _interval
570 * @method startInterval
574 startInterval: function() {
575 if (!this._interval
) {
577 var callback = function() { self
._tryPreloadAttach(); };
578 this._interval
= setInterval(callback
, this.POLL_INTERVAL
);
579 // this.timeout = setTimeout(callback, i);
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.
591 * @method onAvailable
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
602 onAvailable: function(p_id
, p_fn
, p_obj
, p_override
) {
603 onAvailStack
.push( { id
: p_id
,
606 override
: p_override
,
607 checkReady
: false } );
609 retryCount
= this.POLL_RETRYS
;
610 this.startInterval();
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.
618 * @method onContentReady
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
629 onContentReady: function(p_id
, p_fn
, p_obj
, p_override
) {
630 onAvailStack
.push( { id
: p_id
,
633 override
: p_override
,
634 checkReady
: true } );
636 retryCount
= this.POLL_RETRYS
;
637 this.startInterval();
641 * Appends an event handler
643 * @method addListener
645 * @param {Object} el The html element to assign the
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.
659 addListener: function(el
, sType
, fn
, obj
, override
) {
661 if (!fn
|| !fn
.call
) {
665 // The el argument can be an array of elements or element ids.
666 if ( this._isValidCollection(el
)) {
668 for (var i
=0,len
=el
.length
; i
<len
; ++i
) {
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.
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
);
698 // Element should be an html element or an array if we get
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
];
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
720 if (override
=== true) {
727 // wrap the function so we can return the obj object when
729 var wrappedFn = function(e
) {
730 return fn
.call(scope
, YAHOO
.util
.Event
.getEvent(e
),
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
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
] = [];
758 YAHOO
.util
.Event
.fireLegacyEvent(
759 YAHOO
.util
.Event
.getEvent(e
), legacyIndex
);
763 // add a reference to the wrapped listener to our custom
765 //legacyHandlers[legacyIndex].push(index);
766 legacyHandlers
[legacyIndex
].push(li
);
770 this._simpleAdd(el
, sType
, wrappedFn
, false);
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
);
784 * When using legacy events, the handler is routed to this object
785 * so we can fire our custom listener stack.
786 * @method fireLegacyEvent
790 fireLegacyEvent: function(e
, legacyIndex
) {
793 var le
= legacyHandlers
[legacyIndex
];
794 for (var i
=0,len
=le
.length
; i
<len
; ++i
) {
796 if ( li
&& li
[this.WFN
] ) {
797 var scope
= li
[this.ADJ_SCOPE
];
798 var ret
= li
[this.WFN
].call(scope
, e
);
807 * Returns the legacy event index that matches the supplied
809 * @method getLegacyIndex
813 getLegacyIndex: function(el
, sType
) {
814 var key
= this.generateId(el
) + sType
;
815 if (typeof legacyMap
[key
] == "undefined") {
818 return legacyMap
[key
];
823 * Logic that determines when we should automatically use legacy
824 * events instead of DOM2 events.
825 * @method useLegacyEvent
829 useLegacyEvent: function(el
, sType
) {
830 if (!el
.addEventListener
&& !el
.attachEvent
) {
832 } else if (this.isSafari
) {
833 if ("click" == sType
|| "dblclick" == sType
) {
841 * Removes an event handler
843 * @method removeListener
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
851 * @return {boolean} true if the unbind was successful, false
855 removeListener: function(el
, sType
, fn
) {
858 // The el argument can be a string
859 if (typeof el
== "string") {
861 // The el argument can be an array of elements or element ids.
862 } else if ( this._isValidCollection(el
)) {
864 for (i
=0,len
=el
.length
; i
<len
; ++i
) {
865 ok
= ( this.removeListener(el
[i
], sType
, fn
) && ok
);
870 if (!fn
|| !fn
.call
) {
872 return this.purgeElement(el
, false, sType
);
875 if ("unload" == sType
) {
877 for (i
=0, len
=unloadListeners
.length
; i
<len
; i
++) {
878 var li
= unloadListeners
[i
];
883 unloadListeners
.splice(i
, 1);
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];
898 if ("undefined" == typeof index
) {
899 index
= this._getCacheIndex(el
, sType
, fn
);
903 cacheItem
= listeners
[index
];
906 if (!el
|| !cacheItem
) {
911 if (this.useLegacyEvent(el
, sType
)) {
912 var legacyIndex
= this.getLegacyIndex(el
, sType
);
913 var llist
= legacyHandlers
[legacyIndex
];
915 for (i
=0, len
=llist
.length
; i
<len
; ++i
) {
919 li
[this.TYPE
] == sType
&&
929 this._simpleRemove(el
, sType
, cacheItem
[this.WFN
], false);
935 // removed the wrapped handler
936 delete listeners
[index
][this.WFN
];
937 delete listeners
[index
][this.FN
];
938 listeners
.splice(index
, 1);
945 * Returns the event's target element
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
955 getTarget: function(ev
, resolveTextNode
) {
956 var t
= ev
.target
|| ev
.srcElement
;
957 return this.resolveTextNode(t
);
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
969 resolveTextNode: function(node
) {
970 // if (node && node.nodeName &&
971 // "#TEXT" == node.nodeName.toUpperCase()) {
972 if (node
&& 3 == node
.nodeType
) {
973 return node
.parentNode
;
980 * Returns the event's pageX
982 * @param {Event} ev the event
983 * @return {int} the event's pageX
986 getPageX: function(ev
) {
992 x
+= this._getScrollLeft();
1000 * Returns the event's pageY
1002 * @param {Event} ev the event
1003 * @return {int} the event's pageY
1006 getPageY: function(ev
) {
1008 if (!y
&& 0 !== y
) {
1009 y
= ev
.clientY
|| 0;
1012 y
+= this._getScrollTop();
1020 * Returns the pageX and pageY properties as an indexed array.
1022 * @param {Event} ev the event
1023 * @return {[x, y]} the pageX and pageY properties of the event
1026 getXY: function(ev
) {
1027 return [this.getPageX(ev
), this.getPageY(ev
)];
1031 * Returns the event's related target
1032 * @method getRelatedTarget
1033 * @param {Event} ev the event
1034 * @return {HTMLElement} the event's relatedTarget
1037 getRelatedTarget: function(ev
) {
1038 var t
= ev
.relatedTarget
;
1040 if (ev
.type
== "mouseout") {
1042 } else if (ev
.type
== "mouseover") {
1047 return this.resolveTextNode(t
);
1051 * Returns the time of the event. If the time is not included, the
1052 * event is modified using the current time.
1054 * @param {Event} ev the event
1055 * @return {Date} the time of the event
1058 getTime: function(ev
) {
1060 var t
= new Date().getTime();
1072 * Convenience method for stopPropagation + preventDefault
1074 * @param {Event} ev the event
1077 stopEvent: function(ev
) {
1078 this.stopPropagation(ev
);
1079 this.preventDefault(ev
);
1083 * Stops event propagation
1084 * @method stopPropagation
1085 * @param {Event} ev the event
1088 stopPropagation: function(ev
) {
1089 if (ev
.stopPropagation
) {
1090 ev
.stopPropagation();
1092 ev
.cancelBubble
= true;
1097 * Prevents the default behavior of the event
1098 * @method preventDefault
1099 * @param {Event} ev the event
1102 preventDefault: function(ev
) {
1103 if (ev
.preventDefault
) {
1104 ev
.preventDefault();
1106 ev
.returnValue
= false;
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.
1117 * @param {Event} e the event parameter from the handler
1118 * @return {Event} the event
1121 getEvent: function(e
) {
1122 var ev
= e
|| window
.event
;
1125 var c
= this.getEvent
.caller
;
1127 ev
= c
.arguments
[0];
1128 if (ev
&& Event
== ev
.constructor) {
1139 * Returns the charcode for an event
1140 * @method getCharCode
1141 * @param {Event} ev the event
1142 * @return {int} the event's charCode
1145 getCharCode: function(ev
) {
1146 return ev
.charCode
|| ev
.keyCode
|| 0;
1150 * Locating the saved event handler data by function ref
1152 * @method _getCacheIndex
1156 _getCacheIndex: function(el
, sType
, fn
) {
1157 for (var i
=0,len
=listeners
.length
; i
<len
; ++i
) {
1158 var li
= listeners
[i
];
1160 li
[this.FN
] == fn
&&
1161 li
[this.EL
] == el
&&
1162 li
[this.TYPE
] == sType
) {
1171 * Generates an unique ID for the element if it does not already
1173 * @method generateId
1174 * @param el the element to create the id for
1175 * @return {string} the resulting id of the element
1178 generateId: function(el
) {
1182 id
= "yuievtautoid-" + counter
;
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
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" );
1224 * We cache elements bound by id because when the unload event
1225 * fires, we can no longer use document.getElementById
1230 getEl: function(id
) {
1231 return document
.getElementById(id
);
1235 * Clears the element cache
1236 * @deprecated Elements are not cached any longer
1237 * @method clearCache
1241 clearCache: function() { },
1244 * hook up any deferred listeners
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.
1255 EU
._simpleRemove(window
, "load", EU
._load
);
1260 * Polling function that runs before the onload event fires,
1261 * attempting to attach to DOM Nodes as soon as they are
1263 * @method _tryPreloadAttach
1267 _tryPreloadAttach: function() {
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
;
1282 tryAgain
= (retryCount
> 0);
1287 for (var i
=0,len
=onAvailStack
.length
; i
<len
; ++i
) {
1288 var item
= onAvailStack
[i
];
1290 var el
= this.getEl(item
.id
);
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
||
1300 (document
&& document
.body
) ) {
1303 if (item
.override
) {
1304 if (item
.override
=== true) {
1307 scope
= item
.override
;
1310 item
.fn
.call(scope
, item
.obj
);
1311 //delete onAvailStack[i];
1312 // null out instead of delete for Opera
1313 onAvailStack
[i
] = null;
1316 notAvail
.push(item
);
1321 retryCount
= (notAvail
.length
=== 0) ? 0 : retryCount
- 1;
1324 // we may need to strip the nulled out items here
1325 this.startInterval();
1327 clearInterval(this._interval
);
1328 this._interval
= null;
1331 this.locked
= false;
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
1349 purgeElement: function(el
, recurse
, sType
) {
1350 var elListeners
= this.getListeners(el
, sType
);
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
);
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
);
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 * type: (string) the type of event
1376 * fn: (function) the callback supplied to addListener
1377 * obj: (object) the custom object supplied to addListener
1378 * adjust: (boolean) whether or not to adjust the default scope
1379 * index: (int) its position in the Event util listener cache
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
]) ) {
1393 adjust
: l
[this.ADJ_SCOPE
],
1400 return (elListeners
.length
) ? elListeners
: null;
1404 * Removes all listeners registered by pe.event. Called
1405 * automatically during the unload event.
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
];
1418 if (l
[EU
.ADJ_SCOPE
]) {
1419 if (l
[EU
.ADJ_SCOPE
] === true) {
1422 scope
= l
[EU
.ADJ_SCOPE
];
1425 l
[EU
.FN
].call(scope
, EU
.getEvent(e
), l
[EU
.OBJ
] );
1426 unloadListeners
[i
] = null;
1432 unloadListeners
= null;
1434 if (listeners
&& listeners
.length
> 0) {
1435 j
= listeners
.length
;
1438 l
= listeners
[index
];
1440 EU
.removeListener(l
[EU
.EL
], l
[EU
.TYPE
],
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;
1460 legacyEvents
= null;
1462 EU
._simpleRemove(window
, "unload", EU
._unload
);
1467 * Returns scrollLeft
1468 * @method _getScrollLeft
1472 _getScrollLeft: function() {
1473 return this._getScroll()[1];
1478 * @method _getScrollTop
1482 _getScrollTop: function() {
1483 return this._getScroll()[0];
1487 * Returns the scrollTop and scrollLeft. Used to calculate the
1488 * pageX and pageY in Internet Explorer
1489 * @method _getScroll
1493 _getScroll: function() {
1494 var dd
= document
.documentElement
, db
= document
.body
;
1495 if (dd
&& (dd
.scrollTop
|| dd
.scrollLeft
)) {
1496 return [dd
.scrollTop
, dd
.scrollLeft
];
1498 return [db
.scrollTop
, db
.scrollLeft
];
1505 * Adds a DOM event directly without the caching, cleanup, scope adj, etc
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
1515 _simpleAdd: function () {
1516 if (window
.addEventListener
) {
1517 return function(el
, sType
, fn
, capture
) {
1518 el
.addEventListener(sType
, fn
, (capture
));
1520 } else if (window
.attachEvent
) {
1521 return function(el
, sType
, fn
, capture
) {
1522 el
.attachEvent("on" + sType
, fn
);
1525 return function(){};
1530 * Basic remove listener
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
1540 _simpleRemove: function() {
1541 if (window
.removeEventListener
) {
1542 return function (el
, sType
, fn
, capture
) {
1543 el
.removeEventListener(sType
, fn
, (capture
));
1545 } else if (window
.detachEvent
) {
1546 return function (el
, sType
, fn
) {
1547 el
.detachEvent("on" + sType
, fn
);
1550 return function(){};
1558 var EU
= YAHOO
.util
.Event
;
1561 * YAHOO.util.Event.on is an alias for addListener
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
) {
1575 // EU._simpleAdd(document, "DOMContentLoaded", EU._load);
1576 EU
._simpleAdd(window
, "load", EU
._load
);
1578 EU
._simpleAdd(window
, "unload", EU
._unload
);
1579 EU
._tryPreloadAttach();
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
1592 YAHOO
.util
.EventProvider = function() { };
1594 YAHOO
.util
.EventProvider
.prototype = {
1597 * Private storage of custom events
1598 * @property __yui_events
1605 * Private storage of custom event subscribers
1606 * @property __yui_subscribers
1610 __yui_subscribers
: null,
1613 * Subscribe to a CustomEvent by event type
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
1619 * @param p_obj {Object} An object to be passed along when the event
1621 * @param p_override {boolean} If true, the obj passed in becomes the
1622 * execution scope of the listener
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
];
1630 ce
.subscribe(p_fn
, p_obj
, p_override
);
1632 this.__yui_subscribers
= this.__yui_subscribers
|| {};
1633 var subs
= this.__yui_subscribers
;
1634 if (!subs
[p_type
]) {
1638 { fn
: p_fn
, obj
: p_obj
, override
: p_override
} );
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.
1650 unsubscribe: function(p_type
, p_fn
, p_obj
) {
1651 this.__yui_events
= this.__yui_events
|| {};
1652 var ce
= this.__yui_events
[p_type
];
1654 return ce
.unsubscribe(p_fn
, p_obj
);
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.
1665 * @method createEvent
1667 * @param p_type {string} the type, or name of the event
1668 * @param p_config {object} optional config params. Valid properties are:
1672 * scope: defines the default execution scope. If not defined
1673 * the default scope will be this instance.
1676 * silent: if true, the custom event will not generate log messages.
1677 * This is false by default.
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
1687 * @return {CustomEvent} the custom event
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
]) {
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
);
1710 this.__yui_subscribers
= this.__yui_subscribers
|| {};
1711 var qs
= this.__yui_subscribers
[p_type
];
1714 for (var i
=0; i
<qs
.length
; ++i
) {
1715 ce
.subscribe(qs
[i
].fn
, qs
[i
].obj
, qs
[i
].override
);
1720 return events
[p_type
];
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:
1728 * <li>The first argument fire() was executed with</li>
1729 * <li>The custom object (if any) that was passed into the subscribe()
1733 * @param p_type {string} the type, or name of the event
1734 * @param arguments {Object*} an arbitrary set of parameters to pass to
1736 * @return {boolean} the return value from CustomEvent.fire, or null if
1737 * the custom event does not exist.
1739 fireEvent: function(p_type
, arg1
, arg2
, etc
) {
1741 this.__yui_events
= this.__yui_events
|| {};
1742 var ce
= this.__yui_events
[p_type
];
1746 for (var i
=1; i
<arguments
.length
; ++i
) {
1747 args
.push(arguments
[i
]);
1749 return ce
.fire
.apply(ce
, args
);
1756 * Returns true if the custom event of the provided type has been created
1759 * @param type {string} the type, or name of the event
1761 hasEvent: function(type
) {
1762 if (this.__yui_events
) {
1763 if (this.__yui_events
[type
]) {