diff options
Diffstat (limited to 'reports/site_media/yui/event/event.js')
-rw-r--r-- | reports/site_media/yui/event/event.js | 1771 |
1 files changed, 1771 insertions, 0 deletions
diff --git a/reports/site_media/yui/event/event.js b/reports/site_media/yui/event/event.js new file mode 100644 index 000000000..22c97ca2b --- /dev/null +++ b/reports/site_media/yui/event/event.js @@ -0,0 +1,1771 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 0.12.2 +*/ +/** + * The CustomEvent class lets you define events for your application + * that can be subscribed to by one or more independent component. + * + * @param {String} type The type of event, which is passed to the callback + * when the event fires + * @param {Object} oScope The context the event will fire from. "this" will + * refer to this object in the callback. Default value: + * the window object. The listener can override this. + * @param {boolean} silent pass true to prevent the event from writing to + * the debugsystem + * @param {int} signature the signature that the custom event subscriber + * will receive. YAHOO.util.CustomEvent.LIST or + * YAHOO.util.CustomEvent.FLAT. The default is + * YAHOO.util.CustomEvent.LIST. + * @namespace YAHOO.util + * @class CustomEvent + * @constructor + */ +YAHOO.util.CustomEvent = function(type, oScope, silent, signature) { + + /** + * The type of event, returned to subscribers when the event fires + * @property type + * @type string + */ + this.type = type; + + /** + * The scope the the event will fire from by default. Defaults to the window + * obj + * @property scope + * @type object + */ + this.scope = oScope || window; + + /** + * By default all custom events are logged in the debug build, set silent + * to true to disable debug outpu for this event. + * @property silent + * @type boolean + */ + this.silent = silent; + + /** + * Custom events support two styles of arguments provided to the event + * subscribers. + * <ul> + * <li>YAHOO.util.CustomEvent.LIST: + * <ul> + * <li>param1: event name</li> + * <li>param2: array of arguments sent to fire</li> + * <li>param3: <optional> a custom object supplied by the subscriber</li> + * </ul> + * </li> + * <li>YAHOO.util.CustomEvent.FLAT + * <ul> + * <li>param1: the first argument passed to fire. If you need to + * pass multiple parameters, use and array or object literal</li> + * <li>param2: <optional> a custom object supplied by the subscriber</li> + * </ul> + * </li> + * </ul> + * @property signature + * @type int + */ + this.signature = signature || YAHOO.util.CustomEvent.LIST; + + /** + * The subscribers to this event + * @property subscribers + * @type Subscriber[] + */ + this.subscribers = []; + + if (!this.silent) { + } + + var onsubscribeType = "_YUICEOnSubscribe"; + + // Only add subscribe events for events that are not generated by + // CustomEvent + if (type !== onsubscribeType) { + + /** + * Custom events provide a custom event that fires whenever there is + * a new subscriber to the event. This provides an opportunity to + * handle the case where there is a non-repeating event that has + * already fired has a new subscriber. + * + * @event subscribeEvent + * @type YAHOO.util.CustomEvent + * @param {Function} fn The function to execute + * @param {Object} obj An object to be passed along when the event + * fires + * @param {boolean|Object} override If true, the obj passed in becomes + * the execution scope of the listener. + * if an object, that object becomes the + * the execution scope. + */ + this.subscribeEvent = + new YAHOO.util.CustomEvent(onsubscribeType, this, true); + + } +}; + +/** + * Subscriber listener sigature constant. The LIST type returns three + * parameters: the event type, the array of args passed to fire, and + * the optional custom object + * @property YAHOO.util.CustomEvent.LIST + * @static + * @type int + */ +YAHOO.util.CustomEvent.LIST = 0; + +/** + * Subscriber listener sigature constant. The FLAT type returns two + * parameters: the first argument passed to fire and the optional + * custom object + * @property YAHOO.util.CustomEvent.FLAT + * @static + * @type int + */ +YAHOO.util.CustomEvent.FLAT = 1; + +YAHOO.util.CustomEvent.prototype = { + + /** + * Subscribes the caller to this event + * @method subscribe + * @param {Function} fn The function to execute + * @param {Object} obj An object to be passed along when the event + * fires + * @param {boolean|Object} override If true, the obj passed in becomes + * the execution scope of the listener. + * if an object, that object becomes the + * the execution scope. + */ + subscribe: function(fn, obj, override) { + if (this.subscribeEvent) { + this.subscribeEvent.fire(fn, obj, override); + } + + this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, override) ); + }, + + /** + * Unsubscribes the caller from this event + * @method unsubscribe + * @param {Function} fn The function to execute + * @param {Object} obj The custom object passed to subscribe (optional) + * @return {boolean} True if the subscriber was found and detached. + */ + unsubscribe: function(fn, obj) { + var found = false; + for (var i=0, len=this.subscribers.length; i<len; ++i) { + var s = this.subscribers[i]; + if (s && s.contains(fn, obj)) { + this._delete(i); + found = true; + } + } + + return found; + }, + + /** + * Notifies the subscribers. The callback functions will be executed + * from the scope specified when the event was created, and with the + * following parameters: + * <ul> + * <li>The type of event</li> + * <li>All of the arguments fire() was executed with as an array</li> + * <li>The custom object (if any) that was passed into the subscribe() + * method</li> + * </ul> + * @method fire + * @param {Object*} arguments an arbitrary set of parameters to pass to + * the handler. + * @return {boolean} false if one of the subscribers returned false, + * true otherwise + */ + fire: function() { + var len=this.subscribers.length; + if (!len && this.silent) { + return true; + } + + var args=[], ret=true, i; + + for (i=0; i<arguments.length; ++i) { + args.push(arguments[i]); + } + + var argslength = args.length; + + if (!this.silent) { + } + + for (i=0; i<len; ++i) { + var s = this.subscribers[i]; + if (s) { + if (!this.silent) { + } + + var scope = s.getScope(this.scope); + + if (this.signature == YAHOO.util.CustomEvent.FLAT) { + var param = null; + if (args.length > 0) { + param = args[0]; + } + ret = s.fn.call(scope, param, s.obj); + } else { + ret = s.fn.call(scope, this.type, args, s.obj); + } + if (false === ret) { + if (!this.silent) { + } + + //break; + return false; + } + } + } + + return true; + }, + + /** + * Removes all listeners + * @method unsubscribeAll + */ + unsubscribeAll: function() { + for (var i=0, len=this.subscribers.length; i<len; ++i) { + this._delete(len - 1 - i); + } + }, + + /** + * @method _delete + * @private + */ + _delete: function(index) { + var s = this.subscribers[index]; + if (s) { + delete s.fn; + delete s.obj; + } + + // delete this.subscribers[index]; + this.subscribers.splice(index, 1); + }, + + /** + * @method toString + */ + toString: function() { + return "CustomEvent: " + "'" + this.type + "', " + + "scope: " + this.scope; + + } +}; + +///////////////////////////////////////////////////////////////////// + +/** + * Stores the subscriber information to be used when the event fires. + * @param {Function} fn The function to execute + * @param {Object} obj An object to be passed along when the event fires + * @param {boolean} override If true, the obj passed in becomes the execution + * scope of the listener + * @class Subscriber + * @constructor + */ +YAHOO.util.Subscriber = function(fn, obj, override) { + + /** + * The callback that will be execute when the event fires + * @property fn + * @type function + */ + this.fn = fn; + + /** + * An optional custom object that will passed to the callback when + * the event fires + * @property obj + * @type object + */ + this.obj = obj || null; + + /** + * The default execution scope for the event listener is defined when the + * event is created (usually the object which contains the event). + * By setting override to true, the execution scope becomes the custom + * object passed in by the subscriber. If override is an object, that + * object becomes the scope. + * @property override + * @type boolean|object + */ + this.override = override; + +}; + +/** + * Returns the execution scope for this listener. If override was set to true + * the custom obj will be the scope. If override is an object, that is the + * scope, otherwise the default scope will be used. + * @method getScope + * @param {Object} defaultScope the scope to use if this listener does not + * override it. + */ +YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) { + if (this.override) { + if (this.override === true) { + return this.obj; + } else { + return this.override; + } + } + return defaultScope; +}; + +/** + * Returns true if the fn and obj match this objects properties. + * Used by the unsubscribe method to match the right subscriber. + * + * @method contains + * @param {Function} fn the function to execute + * @param {Object} obj an object to be passed along when the event fires + * @return {boolean} true if the supplied arguments match this + * subscriber's signature. + */ +YAHOO.util.Subscriber.prototype.contains = function(fn, obj) { + if (obj) { + return (this.fn == fn && this.obj == obj); + } else { + return (this.fn == fn); + } +}; + +/** + * @method toString + */ +YAHOO.util.Subscriber.prototype.toString = function() { + return "Subscriber { obj: " + (this.obj || "") + + ", override: " + (this.override || "no") + " }"; +}; + +/** + * The Event Utility provides utilities for managing DOM Events and tools + * for building event systems + * + * @module event + * @title Event Utility + * @namespace YAHOO.util + * @requires yahoo + */ + +// The first instance of Event will win if it is loaded more than once. +if (!YAHOO.util.Event) { + +/** + * The event utility provides functions to add and remove event listeners, + * event cleansing. It also tries to automatically remove listeners it + * registers during the unload event. + * + * @class Event + * @static + */ + YAHOO.util.Event = function() { + + /** + * True after the onload event has fired + * @property loadComplete + * @type boolean + * @static + * @private + */ + var loadComplete = false; + + /** + * Cache of wrapped listeners + * @property listeners + * @type array + * @static + * @private + */ + var listeners = []; + + /** + * User-defined unload function that will be fired before all events + * are detached + * @property unloadListeners + * @type array + * @static + * @private + */ + var unloadListeners = []; + + /** + * Cache of DOM0 event handlers to work around issues with DOM2 events + * in Safari + * @property legacyEvents + * @static + * @private + */ + var legacyEvents = []; + + /** + * Listener stack for DOM0 events + * @property legacyHandlers + * @static + * @private + */ + var legacyHandlers = []; + + /** + * The number of times to poll after window.onload. This number is + * increased if additional late-bound handlers are requested after + * the page load. + * @property retryCount + * @static + * @private + */ + var retryCount = 0; + + /** + * onAvailable listeners + * @property onAvailStack + * @static + * @private + */ + var onAvailStack = []; + + /** + * Lookup table for legacy events + * @property legacyMap + * @static + * @private + */ + var legacyMap = []; + + /** + * Counter for auto id generation + * @property counter + * @static + * @private + */ + var counter = 0; + + return { // PREPROCESS + + /** + * The number of times we should look for elements that are not + * in the DOM at the time the event is requested after the document + * has been loaded. The default is 200@amp;50 ms, so it will poll + * for 10 seconds or until all outstanding handlers are bound + * (whichever comes first). + * @property POLL_RETRYS + * @type int + * @static + * @final + */ + POLL_RETRYS: 200, + + /** + * The poll interval in milliseconds + * @property POLL_INTERVAL + * @type int + * @static + * @final + */ + POLL_INTERVAL: 20, + + /** + * Element to bind, int constant + * @property EL + * @type int + * @static + * @final + */ + EL: 0, + + /** + * Type of event, int constant + * @property TYPE + * @type int + * @static + * @final + */ + TYPE: 1, + + /** + * Function to execute, int constant + * @property FN + * @type int + * @static + * @final + */ + FN: 2, + + /** + * Function wrapped for scope correction and cleanup, int constant + * @property WFN + * @type int + * @static + * @final + */ + WFN: 3, + + /** + * Object passed in by the user that will be returned as a + * parameter to the callback, int constant + * @property OBJ + * @type int + * @static + * @final + */ + OBJ: 3, + + /** + * Adjusted scope, either the element we are registering the event + * on or the custom object passed in by the listener, int constant + * @property ADJ_SCOPE + * @type int + * @static + * @final + */ + ADJ_SCOPE: 4, + + /** + * Safari detection is necessary to work around the preventDefault + * bug that makes it so you can't cancel a href click from the + * handler. There is not a capabilities check we can use here. + * @property isSafari + * @private + * @static + */ + isSafari: (/Safari|Konqueror|KHTML/gi).test(navigator.userAgent), + + /** + * IE detection needed to properly calculate pageX and pageY. + * capabilities checking didn't seem to work because another + * browser that does not provide the properties have the values + * calculated in a different manner than IE. + * @property isIE + * @private + * @static + */ + isIE: (!this.isSafari && !navigator.userAgent.match(/opera/gi) && + navigator.userAgent.match(/msie/gi)), + + /** + * poll handle + * @property _interval + * @private + */ + _interval: null, + + /** + * @method startInterval + * @static + * @private + */ + startInterval: function() { + if (!this._interval) { + var self = this; + var callback = function() { self._tryPreloadAttach(); }; + this._interval = setInterval(callback, this.POLL_INTERVAL); + // this.timeout = setTimeout(callback, i); + } + }, + + /** + * Executes the supplied callback when the item with the supplied + * id is found. This is meant to be used to execute behavior as + * soon as possible as the page loads. If you use this after the + * initial page load it will poll for a fixed time for the element. + * The number of times it will poll and the frequency are + * configurable. By default it will poll for 10 seconds. + * + * @method onAvailable + * + * @param {string} p_id the id of the element to look for. + * @param {function} p_fn what to execute when the element is found. + * @param {object} p_obj an optional object to be passed back as + * a parameter to p_fn. + * @param {boolean} p_override If set to true, p_fn will execute + * in the scope of p_obj + * + * @static + */ + onAvailable: function(p_id, p_fn, p_obj, p_override) { + onAvailStack.push( { id: p_id, + fn: p_fn, + obj: p_obj, + override: p_override, + checkReady: false } ); + + retryCount = this.POLL_RETRYS; + this.startInterval(); + }, + + /** + * Works the same way as onAvailable, but additionally checks the + * state of sibling elements to determine if the content of the + * available element is safe to modify. + * + * @method onContentReady + * + * @param {string} p_id the id of the element to look for. + * @param {function} p_fn what to execute when the element is ready. + * @param {object} p_obj an optional object to be passed back as + * a parameter to p_fn. + * @param {boolean} p_override If set to true, p_fn will execute + * in the scope of p_obj + * + * @static + */ + onContentReady: function(p_id, p_fn, p_obj, p_override) { + onAvailStack.push( { id: p_id, + fn: p_fn, + obj: p_obj, + override: p_override, + checkReady: true } ); + + retryCount = this.POLL_RETRYS; + this.startInterval(); + }, + + /** + * Appends an event handler + * + * @method addListener + * + * @param {Object} el The html element to assign the + * event to + * @param {String} sType The type of event to append + * @param {Function} fn The method the event invokes + * @param {Object} obj An arbitrary object that will be + * passed as a parameter to the handler + * @param {boolean} override If true, the obj passed in becomes + * the execution scope of the listener + * @return {boolean} True if the action was successful or defered, + * false if one or more of the elements + * could not have the listener attached, + * or if the operation throws an exception. + * @static + */ + addListener: function(el, sType, fn, obj, override) { + + if (!fn || !fn.call) { + return false; + } + + // The el argument can be an array of elements or element ids. + if ( this._isValidCollection(el)) { + var ok = true; + for (var i=0,len=el.length; i<len; ++i) { + ok = this.on(el[i], + sType, + fn, + obj, + override) && ok; + } + return ok; + + } else if (typeof el == "string") { + var oEl = this.getEl(el); + // If the el argument is a string, we assume it is + // actually the id of the element. If the page is loaded + // we convert el to the actual element, otherwise we + // defer attaching the event until onload event fires + + // check to see if we need to delay hooking up the event + // until after the page loads. + if (oEl) { + el = oEl; + } else { + // defer adding the event until the element is available + this.onAvailable(el, function() { + YAHOO.util.Event.on(el, sType, fn, obj, override); + }); + + return true; + } + } + + // Element should be an html element or an array if we get + // here. + if (!el) { + return false; + } + + // we need to make sure we fire registered unload events + // prior to automatically unhooking them. So we hang on to + // these instead of attaching them to the window and fire the + // handles explicitly during our one unload event. + if ("unload" == sType && obj !== this) { + unloadListeners[unloadListeners.length] = + [el, sType, fn, obj, override]; + return true; + } + + + // if the user chooses to override the scope, we use the custom + // object passed in, otherwise the executing scope will be the + // HTML element that the event is registered on + var scope = el; + if (override) { + if (override === true) { + scope = obj; + } else { + scope = override; + } + } + + // wrap the function so we can return the obj object when + // the event fires; + var wrappedFn = function(e) { + return fn.call(scope, YAHOO.util.Event.getEvent(e), + obj); + }; + + var li = [el, sType, fn, wrappedFn, scope]; + var index = listeners.length; + // cache the listener so we can try to automatically unload + listeners[index] = li; + + if (this.useLegacyEvent(el, sType)) { + var legacyIndex = this.getLegacyIndex(el, sType); + + // Add a new dom0 wrapper if one is not detected for this + // element + if ( legacyIndex == -1 || + el != legacyEvents[legacyIndex][0] ) { + + legacyIndex = legacyEvents.length; + legacyMap[el.id + sType] = legacyIndex; + + // cache the signature for the DOM0 event, and + // include the existing handler for the event, if any + legacyEvents[legacyIndex] = + [el, sType, el["on" + sType]]; + legacyHandlers[legacyIndex] = []; + + el["on" + sType] = + function(e) { + YAHOO.util.Event.fireLegacyEvent( + YAHOO.util.Event.getEvent(e), legacyIndex); + }; + } + + // add a reference to the wrapped listener to our custom + // stack of events + //legacyHandlers[legacyIndex].push(index); + legacyHandlers[legacyIndex].push(li); + + } else { + try { + this._simpleAdd(el, sType, wrappedFn, false); + } catch(e) { + // handle an error trying to attach an event. If it fails + // we need to clean up the cache + this.removeListener(el, sType, fn); + return false; + } + } + + return true; + + }, + + /** + * When using legacy events, the handler is routed to this object + * so we can fire our custom listener stack. + * @method fireLegacyEvent + * @static + * @private + */ + fireLegacyEvent: function(e, legacyIndex) { + var ok = true; + + var le = legacyHandlers[legacyIndex]; + for (var i=0,len=le.length; i<len; ++i) { + var li = le[i]; + if ( li && li[this.WFN] ) { + var scope = li[this.ADJ_SCOPE]; + var ret = li[this.WFN].call(scope, e); + ok = (ok && ret); + } + } + + return ok; + }, + + /** + * Returns the legacy event index that matches the supplied + * signature + * @method getLegacyIndex + * @static + * @private + */ + getLegacyIndex: function(el, sType) { + var key = this.generateId(el) + sType; + if (typeof legacyMap[key] == "undefined") { + return -1; + } else { + return legacyMap[key]; + } + }, + + /** + * Logic that determines when we should automatically use legacy + * events instead of DOM2 events. + * @method useLegacyEvent + * @static + * @private + */ + useLegacyEvent: function(el, sType) { + if (!el.addEventListener && !el.attachEvent) { + return true; + } else if (this.isSafari) { + if ("click" == sType || "dblclick" == sType) { + return true; + } + } + return false; + }, + + /** + * Removes an event handler + * + * @method removeListener + * + * @param {Object} el the html element or the id of the element to + * assign the event to. + * @param {String} sType the type of event to remove. + * @param {Function} fn the method the event invokes. If fn is + * undefined, then all event handlers for the type of event are + * removed. + * @return {boolean} true if the unbind was successful, false + * otherwise. + * @static + */ + removeListener: function(el, sType, fn) { + var i, len; + + // The el argument can be a string + if (typeof el == "string") { + el = this.getEl(el); + // The el argument can be an array of elements or element ids. + } else if ( this._isValidCollection(el)) { + var ok = true; + for (i=0,len=el.length; i<len; ++i) { + ok = ( this.removeListener(el[i], sType, fn) && ok ); + } + return ok; + } + + if (!fn || !fn.call) { + //return false; + return this.purgeElement(el, false, sType); + } + + if ("unload" == sType) { + + for (i=0, len=unloadListeners.length; i<len; i++) { + var li = unloadListeners[i]; + if (li && + li[0] == el && + li[1] == sType && + li[2] == fn) { + unloadListeners.splice(i, 1); + return true; + } + } + + return false; + } + + var cacheItem = null; + + // The index is a hidden parameter; needed to remove it from + // the method signature because it was tempting users to + // try and take advantage of it, which is not possible. + var index = arguments[3]; + + if ("undefined" == typeof index) { + index = this._getCacheIndex(el, sType, fn); + } + + if (index >= 0) { + cacheItem = listeners[index]; + } + + if (!el || !cacheItem) { + return false; + } + + + if (this.useLegacyEvent(el, sType)) { + var legacyIndex = this.getLegacyIndex(el, sType); + var llist = legacyHandlers[legacyIndex]; + if (llist) { + for (i=0, len=llist.length; i<len; ++i) { + li = llist[i]; + if (li && + li[this.EL] == el && + li[this.TYPE] == sType && + li[this.FN] == fn) { + llist.splice(i, 1); + break; + } + } + } + + } else { + try { + this._simpleRemove(el, sType, cacheItem[this.WFN], false); + } catch(e) { + return false; + } + } + + // removed the wrapped handler + delete listeners[index][this.WFN]; + delete listeners[index][this.FN]; + listeners.splice(index, 1); + + return true; + + }, + + /** + * Returns the event's target element + * @method getTarget + * @param {Event} ev the event + * @param {boolean} resolveTextNode when set to true the target's + * parent will be returned if the target is a + * text node. @deprecated, the text node is + * now resolved automatically + * @return {HTMLElement} the event's target + * @static + */ + getTarget: function(ev, resolveTextNode) { + var t = ev.target || ev.srcElement; + return this.resolveTextNode(t); + }, + + /** + * In some cases, some browsers will return a text node inside + * the actual element that was targeted. This normalizes the + * return value for getTarget and getRelatedTarget. + * @method resolveTextNode + * @param {HTMLElement} node node to resolve + * @return {HTMLElement} the normized node + * @static + */ + resolveTextNode: function(node) { + // if (node && node.nodeName && + // "#TEXT" == node.nodeName.toUpperCase()) { + if (node && 3 == node.nodeType) { + return node.parentNode; + } else { + return node; + } + }, + + /** + * Returns the event's pageX + * @method getPageX + * @param {Event} ev the event + * @return {int} the event's pageX + * @static + */ + getPageX: function(ev) { + var x = ev.pageX; + if (!x && 0 !== x) { + x = ev.clientX || 0; + + if ( this.isIE ) { + x += this._getScrollLeft(); + } + } + + return x; + }, + + /** + * Returns the event's pageY + * @method getPageY + * @param {Event} ev the event + * @return {int} the event's pageY + * @static + */ + getPageY: function(ev) { + var y = ev.pageY; + if (!y && 0 !== y) { + y = ev.clientY || 0; + + if ( this.isIE ) { + y += this._getScrollTop(); + } + } + + return y; + }, + + /** + * Returns the pageX and pageY properties as an indexed array. + * @method getXY + * @param {Event} ev the event + * @return {[x, y]} the pageX and pageY properties of the event + * @static + */ + getXY: function(ev) { + return [this.getPageX(ev), this.getPageY(ev)]; + }, + + /** + * Returns the event's related target + * @method getRelatedTarget + * @param {Event} ev the event + * @return {HTMLElement} the event's relatedTarget + * @static + */ + getRelatedTarget: function(ev) { + var t = ev.relatedTarget; + if (!t) { + if (ev.type == "mouseout") { + t = ev.toElement; + } else if (ev.type == "mouseover") { + t = ev.fromElement; + } + } + + return this.resolveTextNode(t); + }, + + /** + * Returns the time of the event. If the time is not included, the + * event is modified using the current time. + * @method getTime + * @param {Event} ev the event + * @return {Date} the time of the event + * @static + */ + getTime: function(ev) { + if (!ev.time) { + var t = new Date().getTime(); + try { + ev.time = t; + } catch(e) { + return t; + } + } + + return ev.time; + }, + + /** + * Convenience method for stopPropagation + preventDefault + * @method stopEvent + * @param {Event} ev the event + * @static + */ + stopEvent: function(ev) { + this.stopPropagation(ev); + this.preventDefault(ev); + }, + + /** + * Stops event propagation + * @method stopPropagation + * @param {Event} ev the event + * @static + */ + stopPropagation: function(ev) { + if (ev.stopPropagation) { + ev.stopPropagation(); + } else { + ev.cancelBubble = true; + } + }, + + /** + * Prevents the default behavior of the event + * @method preventDefault + * @param {Event} ev the event + * @static + */ + preventDefault: function(ev) { + if (ev.preventDefault) { + ev.preventDefault(); + } else { + ev.returnValue = false; + } + }, + + /** + * Finds the event in the window object, the caller's arguments, or + * in the arguments of another method in the callstack. This is + * executed automatically for events registered through the event + * manager, so the implementer should not normally need to execute + * this function at all. + * @method getEvent + * @param {Event} e the event parameter from the handler + * @return {Event} the event + * @static + */ + getEvent: function(e) { + var ev = e || window.event; + + if (!ev) { + var c = this.getEvent.caller; + while (c) { + ev = c.arguments[0]; + if (ev && Event == ev.constructor) { + break; + } + c = c.caller; + } + } + + return ev; + }, + + /** + * Returns the charcode for an event + * @method getCharCode + * @param {Event} ev the event + * @return {int} the event's charCode + * @static + */ + getCharCode: function(ev) { + return ev.charCode || ev.keyCode || 0; + }, + + /** + * Locating the saved event handler data by function ref + * + * @method _getCacheIndex + * @static + * @private + */ + _getCacheIndex: function(el, sType, fn) { + for (var i=0,len=listeners.length; i<len; ++i) { + var li = listeners[i]; + if ( li && + li[this.FN] == fn && + li[this.EL] == el && + li[this.TYPE] == sType ) { + return i; + } + } + + return -1; + }, + + /** + * Generates an unique ID for the element if it does not already + * have one. + * @method generateId + * @param el the element to create the id for + * @return {string} the resulting id of the element + * @static + */ + generateId: function(el) { + var id = el.id; + + if (!id) { + id = "yuievtautoid-" + counter; + ++counter; + el.id = id; + } + + return id; + }, + + /** + * We want to be able to use getElementsByTagName as a collection + * to attach a group of events to. Unfortunately, different + * browsers return different types of collections. This function + * tests to determine if the object is array-like. It will also + * fail if the object is an array, but is empty. + * @method _isValidCollection + * @param o the object to test + * @return {boolean} true if the object is array-like and populated + * @static + * @private + */ + _isValidCollection: function(o) { + // this.logger.debug(o.constructor.toString()) + // this.logger.debug(typeof o) + + return ( o && // o is something + o.length && // o is indexed + typeof o != "string" && // o is not a string + !o.tagName && // o is not an HTML element + !o.alert && // o is not a window + typeof o[0] != "undefined" ); + + }, + + /** + * @private + * @property elCache + * DOM element cache + * @static + */ + elCache: {}, + + /** + * We cache elements bound by id because when the unload event + * fires, we can no longer use document.getElementById + * @method getEl + * @static + * @private + */ + getEl: function(id) { + return document.getElementById(id); + }, + + /** + * Clears the element cache + * @deprecated Elements are not cached any longer + * @method clearCache + * @static + * @private + */ + clearCache: function() { }, + + /** + * hook up any deferred listeners + * @method _load + * @static + * @private + */ + _load: function(e) { + loadComplete = true; + var EU = YAHOO.util.Event; + // Remove the listener to assist with the IE memory issue, but not + // for other browsers because FF 1.0x does not like it. + if (this.isIE) { + EU._simpleRemove(window, "load", EU._load); + } + }, + + /** + * Polling function that runs before the onload event fires, + * attempting to attach to DOM Nodes as soon as they are + * available + * @method _tryPreloadAttach + * @static + * @private + */ + _tryPreloadAttach: function() { + + if (this.locked) { + return false; + } + + this.locked = true; + + + // keep trying until after the page is loaded. We need to + // check the page load state prior to trying to bind the + // elements so that we can be certain all elements have been + // tested appropriately + var tryAgain = !loadComplete; + if (!tryAgain) { + tryAgain = (retryCount > 0); + } + + // onAvailable + var notAvail = []; + for (var i=0,len=onAvailStack.length; i<len ; ++i) { + var item = onAvailStack[i]; + if (item) { + var el = this.getEl(item.id); + + if (el) { + // The element is available, but not necessarily ready + // @todo verify IE7 compatibility + // @todo should we test parentNode.nextSibling? + // @todo re-evaluate global content ready + if ( !item.checkReady || + loadComplete || + el.nextSibling || + (document && document.body) ) { + + var scope = el; + if (item.override) { + if (item.override === true) { + scope = item.obj; + } else { + scope = item.override; + } + } + item.fn.call(scope, item.obj); + //delete onAvailStack[i]; + // null out instead of delete for Opera + onAvailStack[i] = null; + } + } else { + notAvail.push(item); + } + } + } + + retryCount = (notAvail.length === 0) ? 0 : retryCount - 1; + + if (tryAgain) { + // we may need to strip the nulled out items here + this.startInterval(); + } else { + clearInterval(this._interval); + this._interval = null; + } + + this.locked = false; + + return true; + + }, + + /** + * Removes all listeners attached to the given element via addListener. + * Optionally, the node's children can also be purged. + * Optionally, you can specify a specific type of event to remove. + * @method purgeElement + * @param {HTMLElement} el the element to purge + * @param {boolean} recurse recursively purge this element's children + * as well. Use with caution. + * @param {string} sType optional type of listener to purge. If + * left out, all listeners will be removed + * @static + */ + purgeElement: function(el, recurse, sType) { + var elListeners = this.getListeners(el, sType); + if (elListeners) { + for (var i=0,len=elListeners.length; i<len ; ++i) { + var l = elListeners[i]; + // can't use the index on the changing collection + //this.removeListener(el, l.type, l.fn, l.index); + this.removeListener(el, l.type, l.fn); + } + } + + if (recurse && el && el.childNodes) { + for (i=0,len=el.childNodes.length; i<len ; ++i) { + this.purgeElement(el.childNodes[i], recurse, sType); + } + } + }, + + /** + * Returns all listeners attached to the given element via addListener. + * Optionally, you can specify a specific type of event to return. + * @method getListeners + * @param el {HTMLElement} the element to inspect + * @param sType {string} optional type of listener to return. If + * left out, all listeners will be returned + * @return {Object} the listener. Contains the following fields: + * type: (string) the type of event + * fn: (function) the callback supplied to addListener + * obj: (object) the custom object supplied to addListener + * adjust: (boolean) whether or not to adjust the default scope + * index: (int) its position in the Event util listener cache + * @static + */ + getListeners: function(el, sType) { + var elListeners = []; + if (listeners && listeners.length > 0) { + for (var i=0,len=listeners.length; i<len ; ++i) { + var l = listeners[i]; + if ( l && l[this.EL] === el && + (!sType || sType === l[this.TYPE]) ) { + elListeners.push({ + type: l[this.TYPE], + fn: l[this.FN], + obj: l[this.OBJ], + adjust: l[this.ADJ_SCOPE], + index: i + }); + } + } + } + + return (elListeners.length) ? elListeners : null; + }, + + /** + * Removes all listeners registered by pe.event. Called + * automatically during the unload event. + * @method _unload + * @static + * @private + */ + _unload: function(e) { + + var EU = YAHOO.util.Event, i, j, l, len, index; + + for (i=0,len=unloadListeners.length; i<len; ++i) { + l = unloadListeners[i]; + if (l) { + var scope = window; + if (l[EU.ADJ_SCOPE]) { + if (l[EU.ADJ_SCOPE] === true) { + scope = l[EU.OBJ]; + } else { + scope = l[EU.ADJ_SCOPE]; + } + } + l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ] ); + unloadListeners[i] = null; + l=null; + scope=null; + } + } + + unloadListeners = null; + + if (listeners && listeners.length > 0) { + j = listeners.length; + while (j) { + index = j-1; + l = listeners[index]; + if (l) { + EU.removeListener(l[EU.EL], l[EU.TYPE], + l[EU.FN], index); + } + j = j - 1; + } + l=null; + + EU.clearCache(); + } + + for (i=0,len=legacyEvents.length; i<len; ++i) { + // dereference the element + //delete legacyEvents[i][0]; + legacyEvents[i][0] = null; + + // delete the array item + //delete legacyEvents[i]; + legacyEvents[i] = null; + } + + legacyEvents = null; + + EU._simpleRemove(window, "unload", EU._unload); + + }, + + /** + * Returns scrollLeft + * @method _getScrollLeft + * @static + * @private + */ + _getScrollLeft: function() { + return this._getScroll()[1]; + }, + + /** + * Returns scrollTop + * @method _getScrollTop + * @static + * @private + */ + _getScrollTop: function() { + return this._getScroll()[0]; + }, + + /** + * Returns the scrollTop and scrollLeft. Used to calculate the + * pageX and pageY in Internet Explorer + * @method _getScroll + * @static + * @private + */ + _getScroll: function() { + var dd = document.documentElement, db = document.body; + if (dd && (dd.scrollTop || dd.scrollLeft)) { + return [dd.scrollTop, dd.scrollLeft]; + } else if (db) { + return [db.scrollTop, db.scrollLeft]; + } else { + return [0, 0]; + } + }, + + /** + * Adds a DOM event directly without the caching, cleanup, scope adj, etc + * + * @method _simpleAdd + * @param {HTMLElement} el the element to bind the handler to + * @param {string} sType the type of event handler + * @param {function} fn the callback to invoke + * @param {boolen} capture capture or bubble phase + * @static + * @private + */ + _simpleAdd: function () { + if (window.addEventListener) { + return function(el, sType, fn, capture) { + el.addEventListener(sType, fn, (capture)); + }; + } else if (window.attachEvent) { + return function(el, sType, fn, capture) { + el.attachEvent("on" + sType, fn); + }; + } else { + return function(){}; + } + }(), + + /** + * Basic remove listener + * + * @method _simpleRemove + * @param {HTMLElement} el the element to bind the handler to + * @param {string} sType the type of event handler + * @param {function} fn the callback to invoke + * @param {boolen} capture capture or bubble phase + * @static + * @private + */ + _simpleRemove: function() { + if (window.removeEventListener) { + return function (el, sType, fn, capture) { + el.removeEventListener(sType, fn, (capture)); + }; + } else if (window.detachEvent) { + return function (el, sType, fn) { + el.detachEvent("on" + sType, fn); + }; + } else { + return function(){}; + } + }() + }; + + }(); + + (function() { + var EU = YAHOO.util.Event; + + /** + * YAHOO.util.Event.on is an alias for addListener + * @method on + * @see addListener + * @static + */ + EU.on = EU.addListener; + + // YAHOO.mix(EU, YAHOO.util.EventProvider.prototype); + // EU.createEvent("DOMContentReady"); + // EU.subscribe("DOMContentReady", EU._load); + + if (document && document.body) { + EU._load(); + } else { + // EU._simpleAdd(document, "DOMContentLoaded", EU._load); + EU._simpleAdd(window, "load", EU._load); + } + EU._simpleAdd(window, "unload", EU._unload); + EU._tryPreloadAttach(); + })(); +} + +/** + * EventProvider is designed to be used with YAHOO.augment to wrap + * CustomEvents in an interface that allows events to be subscribed to + * and fired by name. This makes it possible for implementing code to + * subscribe to an event that either has not been created yet, or will + * not be created at all. + * + * @Class EventProvider + */ +YAHOO.util.EventProvider = function() { }; + +YAHOO.util.EventProvider.prototype = { + + /** + * Private storage of custom events + * @property __yui_events + * @type Object[] + * @private + */ + __yui_events: null, + + /** + * Private storage of custom event subscribers + * @property __yui_subscribers + * @type Object[] + * @private + */ + __yui_subscribers: null, + + /** + * Subscribe to a CustomEvent by event type + * + * @method subscribe + * @param p_type {string} the type, or name of the event + * @param p_fn {function} the function to exectute when the event fires + * @param p_obj + * @param p_obj {Object} An object to be passed along when the event + * fires + * @param p_override {boolean} If true, the obj passed in becomes the + * execution scope of the listener + */ + subscribe: function(p_type, p_fn, p_obj, p_override) { + + this.__yui_events = this.__yui_events || {}; + var ce = this.__yui_events[p_type]; + + if (ce) { + ce.subscribe(p_fn, p_obj, p_override); + } else { + this.__yui_subscribers = this.__yui_subscribers || {}; + var subs = this.__yui_subscribers; + if (!subs[p_type]) { + subs[p_type] = []; + } + subs[p_type].push( + { fn: p_fn, obj: p_obj, override: p_override } ); + } + }, + + /** + * Unsubscribes the from the specified event + * @method unsubscribe + * @param p_type {string} The type, or name of the event + * @param p_fn {Function} The function to execute + * @param p_obj {Object} The custom object passed to subscribe (optional) + * @return {boolean} true if the subscriber was found and detached. + */ + unsubscribe: function(p_type, p_fn, p_obj) { + this.__yui_events = this.__yui_events || {}; + var ce = this.__yui_events[p_type]; + if (ce) { + return ce.unsubscribe(p_fn, p_obj); + } else { + return false; + } + }, + + /** + * Creates a new custom event of the specified type. If a custom event + * by that name already exists, it will not be re-created. In either + * case the custom event is returned. + * + * @method createEvent + * + * @param p_type {string} the type, or name of the event + * @param p_config {object} optional config params. Valid properties are: + * + * <ul> + * <li> + * scope: defines the default execution scope. If not defined + * the default scope will be this instance. + * </li> + * <li> + * silent: if true, the custom event will not generate log messages. + * This is false by default. + * </li> + * <li> + * onSubscribeCallback: specifies a callback to execute when the + * event has a new subscriber. This will fire immediately for + * each queued subscriber if any exist prior to the creation of + * the event. + * </li> + * </ul> + * + * @return {CustomEvent} the custom event + * + */ + createEvent: function(p_type, p_config) { + + this.__yui_events = this.__yui_events || {}; + var opts = p_config || {}; + var events = this.__yui_events; + + if (events[p_type]) { + } else { + + var scope = opts.scope || this; + var silent = opts.silent || null; + + var ce = new YAHOO.util.CustomEvent(p_type, scope, silent, + YAHOO.util.CustomEvent.FLAT); + events[p_type] = ce; + + if (opts.onSubscribeCallback) { + ce.subscribeEvent.subscribe(opts.onSubscribeCallback); + } + + this.__yui_subscribers = this.__yui_subscribers || {}; + var qs = this.__yui_subscribers[p_type]; + + if (qs) { + for (var i=0; i<qs.length; ++i) { + ce.subscribe(qs[i].fn, qs[i].obj, qs[i].override); + } + } + } + + return events[p_type]; + }, + + /** + * Fire a custom event by name. The callback functions will be executed + * from the scope specified when the event was created, and with the + * following parameters: + * <ul> + * <li>The first argument fire() was executed with</li> + * <li>The custom object (if any) that was passed into the subscribe() + * method</li> + * </ul> + * @method fireEvent + * @param p_type {string} the type, or name of the event + * @param arguments {Object*} an arbitrary set of parameters to pass to + * the handler. + * @return {boolean} the return value from CustomEvent.fire, or null if + * the custom event does not exist. + */ + fireEvent: function(p_type, arg1, arg2, etc) { + + this.__yui_events = this.__yui_events || {}; + var ce = this.__yui_events[p_type]; + + if (ce) { + var args = []; + for (var i=1; i<arguments.length; ++i) { + args.push(arguments[i]); + } + return ce.fire.apply(ce, args); + } else { + return null; + } + }, + + /** + * Returns true if the custom event of the provided type has been created + * with createEvent. + * @method hasEvent + * @param type {string} the type, or name of the event + */ + hasEvent: function(type) { + if (this.__yui_events) { + if (this.__yui_events[type]) { + return true; + } + } + return false; + } + +}; + |