/* Copyright (c) 2007, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.2.0 */ /** * 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. // @TODO this needs to be changed so that only the state data that needs to // be preserved is kept, while methods are overwritten/added as needed. // This means that the module pattern can't be used. 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; /** * addListener/removeListener can throw errors in unexpected scenarios. * These errors are suppressed, the method returns false, and this property * is set * @property lastError * @type Error */ var lastError = null; return { /** * 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. Since this function has been used outside of this * utility, it was changed to detect all KHTML browser to be more * friendly towards the non-Safari browsers that share the engine. * Internally, the preventDefault bug detection now uses the * webkit property. * @property isSafari * @private * @static * @deprecated */ isSafari: (/KHTML/gi).test(navigator.userAgent), /** * If WebKit is detected, we keep track of the version number of * the engine. * Safari 1.3.2 (312.6): 312.8.1 <-- currently the latest * available on Mac OSX 10.3. * Safari 2.0.2: 416 <-- hasOwnProperty introduced * Safari 2.0.4: 418 <-- preventDefault fixed (I believe) * Safari 2.0.4 (419.3): 418.9.1 <-- current release * * http://developer.apple.com/internet/safari/uamatrix.html * @property webkit */ webkit: function() { var v=navigator.userAgent.match(/AppleWebKit\/([^ ]*)/); if (v&&v[1]) { return v[1]; } return null; }(), /** * 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.webkit && !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