/* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.8.0r4 */ /** * 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} context 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. * @param fireOnce {boolean} If configured to fire once, the custom event * will only notify subscribers a single time regardless of how many times * the event is fired. In addition, new subscribers will be notified * immediately if the event has already been fired. * @namespace YAHOO.util * @class CustomEvent * @constructor */ YAHOO.util.CustomEvent = function(type, context, silent, signature, fireOnce) { /** * The type of event, returned to subscribers when the event fires * @property type * @type string */ this.type = type; /** * The context the event will fire from by default. Defaults to the window obj. * @property scope * @type object */ this.scope = context || window; /** * By default all custom events are logged in the debug build. Set silent to true * to disable debug output for this event. * @property silent * @type boolean */ this.silent = silent; /** * If configured to fire once, the custom event will only notify subscribers * a single time regardless of how many times the event is fired. In addition, * new subscribers will be notified immediately if the event has already been * fired. * @property fireOnce * @type boolean * @default false */ this.fireOnce = fireOnce; /** * Indicates whether or not this event has ever been fired. * @property fired * @type boolean * @default false */ this.fired = false; /** * For fireOnce events the arguments the event was fired with are stored * so that new subscribers get the proper payload. * @property firedWith * @type Array */ this.firedWith = null; /** * 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 fn {Function} The function to execute * @param obj <Object> An object to be passed along when the event fires. * Defaults to the custom event. * @param override <boolean|Object> If true, the obj passed in becomes the * execution context of the listener. If an object, that object becomes * the execution context. Defaults to the custom event. */ this.subscribeEvent = new YAHOO.util.CustomEvent(onsubscribeType, this, true); } /** * In order to make it possible to execute the rest of the subscriber * stack when one thows an exception, the subscribers exceptions are * caught. The most recent exception is stored in this property * @property lastError * @type Error */ this.lastError = null; }; /** * 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. * overrideContext <boolean|Object> If true, the obj passed in becomes the execution * context of the listener. If an object, that object becomes the execution context. */ subscribe: function(fn, obj, overrideContext) { if (!fn) { throw new Error("Invalid callback for subscriber to '" + this.type + "'"); } if (this.subscribeEvent) { this.subscribeEvent.fire(fn, obj, overrideContext); } var s = new YAHOO.util.Subscriber(fn, obj, overrideContext); if (this.fireOnce && this.fired) { this.notify(s, this.firedWith); } else { this.subscribers.push(s); } }, /** * Unsubscribes subscribers. * @method unsubscribe * @param {Function} fn The subscribed function to remove, if not supplied * all will be removed * @param {Object} obj The custom object passed to subscribe. This is * optional, but if supplied will be used to * disambiguate multiple listeners that are the same * (e.g., you subscribe many object using a function * that lives on the prototype) * @return {boolean} True if the subscriber was found and detached. */ unsubscribe: function(fn, obj) { if (!fn) { return this.unsubscribeAll(); } 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 context 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() { this.lastError = null; var errors = [], len=this.subscribers.length; var args=[].slice.call(arguments, 0), ret=true, i, rebuild=false; if (this.fireOnce) { if (this.fired) { return true; } else { this.firedWith = args; } } this.fired = true; if (!len && this.silent) { return true; } if (!this.silent) { } // make a copy of the subscribers so that there are // no index problems if one subscriber removes another. var subs = this.subscribers.slice(); for (i=0; i<len; ++i) { var s = subs[i]; if (!s) { rebuild=true; } else { ret = this.notify(s, args); if (false === ret) { if (!this.silent) { } break; } } } return (ret !== false); }, notify: function(s, args) { var ret, param=null, scope = s.getScope(this.scope), throwErrors = YAHOO.util.Event.throwErrors; if (!this.silent) { } if (this.signature == YAHOO.util.CustomEvent.FLAT) { if (args.length > 0) { param = args[0]; } try { ret = s.fn.call(scope, param, s.obj); } catch(e) { this.lastError = e; // errors.push(e); if (throwErrors) { throw e; } } } else { try { ret = s.fn.call(scope, this.type, args, s.obj); } catch(ex) { this.lastError = ex; if (throwErrors) { throw ex; } } } return ret; }, /** * Removes all listeners * @method unsubscribeAll * @return {int} The number of listeners unsubscribed */ unsubscribeAll: function() { var l = this.subscribers.length, i; for (i=l-1; i>-1; i--) { this._delete(i); } this.subscribers=[]; return l; }, /** * @method _delete * @private */ _delete: function(index) { var s = this.subscribers[index]; if (s) { delete s.fn; delete s.obj; } // this.subscribers[index]=null; this.subscribers.splice(index, 1); }, /** * @method toString */ toString: function() { return "CustomEvent: " + "'" + this.type + "', " + "context: " + 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} overrideContext If true, the obj passed in becomes the execution * context of the listener * @class Subscriber * @constructor */ YAHOO.util.Subscriber = function(fn, obj, overrideContext) { /** * 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 = YAHOO.lang.isUndefined(obj) ? null : obj; /** * The default execution context for the event listener is defined when the * event is created (usually the object which contains the event). * By setting overrideContext to true, the execution context becomes the custom * object passed in by the subscriber. If overrideContext is an object, that * object becomes the context. * @property overrideContext * @type boolean|object */ this.overrideContext = overrideContext; }; /** * Returns the execution context for this listener. If overrideContext was set to true * the custom obj will be the context. If overrideContext is an object, that is the * context, otherwise the default context will be used. * @method getScope * @param {Object} defaultScope the context to use if this listener does not * override it. */ YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) { if (this.overrideContext) { if (this.overrideContext === true) { return this.obj; } else { return this.overrideContext; } } 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 + ", overrideContext: " + (this.overrideContext || "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. // @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 */ listeners = [], /** * User-defined unload function that will be fired before all events * are detached * @property unloadListeners * @type array * @static * @private */ unloadListeners = [], /** * 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 */ retryCount = 0, /** * onAvailable listeners * @property onAvailStack * @static * @private */ onAvailStack = [], /** * Counter for auto id generation * @property counter * @static * @private */ counter = 0, /** * Normalized keycodes for webkit/safari * @property webkitKeymap * @type {int: int} * @private * @static * @final */ webkitKeymap = { 63232: 38, // up 63233: 40, // down 63234: 37, // left 63235: 39, // right 63276: 33, // page up 63277: 34, // page down 25: 9 // SHIFT-TAB (Safari provides a different key code in // this case, even though the shiftKey modifier is set) }, isIE = YAHOO.env.ua.ie, // String constants used by the addFocusListener and removeFocusListener methods FOCUSIN = "focusin", FOCUSOUT = "focusout"; 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 500@amp;40 ms, so it will poll * for 20 seconds or until all outstanding handlers are bound * (whichever comes first). * @property POLL_RETRYS * @type int * @static * @final */ POLL_RETRYS: 500, /** * The poll interval in milliseconds * @property POLL_INTERVAL * @type int * @static * @final */ POLL_INTERVAL: 40, /** * 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 context 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. Specific to * unload listeners * @property OBJ * @type int * @static * @final */ UNLOAD_OBJ: 3, /** * Adjusted context, 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, /** * The original obj passed into addListener * @property OBJ * @type int * @static * @final */ OBJ: 5, /** * The original context parameter passed into addListener * @property OVERRIDE * @type int * @static * @final */ OVERRIDE: 6, /** * The original capture parameter passed into addListener * @property CAPTURE * @type int * @static * @final */ CAPTURE: 7, /** * addListener/removeListener can throw errors in unexpected scenarios. * These errors are suppressed, the method returns false, and this property * is set * @property lastError * @static * @type Error */ lastError: null, /** * Safari detection * @property isSafari * @private * @static * @deprecated use YAHOO.env.ua.webkit */ isSafari: YAHOO.env.ua.webkit, /** * webkit version * @property webkit * @type string * @private * @static * @deprecated use YAHOO.env.ua.webkit */ webkit: YAHOO.env.ua.webkit, /** * IE detection * @property isIE * @private * @static * @deprecated use YAHOO.env.ua.ie */ isIE: isIE, /** * poll handle * @property _interval * @static * @private */ _interval: null, /** * document readystate poll handle * @property _dri * @static * @private */ _dri: null, /** * Map of special event types * @property _specialTypes * @static * @private */ _specialTypes: { focusin: (isIE ? "focusin" : "focus"), focusout: (isIE ? "focusout" : "blur") }, /** * True when the document is initially usable * @property DOMReady * @type boolean * @static */ DOMReady: false, /** * Errors thrown by subscribers of custom events are caught * and the error message is written to the debug console. If * this property is set to true, it will also re-throw the * error. * @property throwErrors * @type boolean * @default false */ throwErrors: false, /** * @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._interval = YAHOO.lang.later(this.POLL_INTERVAL, this, this._tryPreloadAttach, null, true); } }, /** * 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. * * <p>The callback is executed with a single parameter: * the custom object parameter, if provided.</p> * * @method onAvailable * * @param {string||string[]} id the id of the element, or an array * of ids to look for. * @param {function} fn what to execute when the element is found. * @param {object} obj an optional object to be passed back as * a parameter to fn. * @param {boolean|object} overrideContext If set to true, fn will execute * in the context of obj, if set to an object it * will execute in the context of that object * @param checkContent {boolean} check child node readiness (onContentReady) * @static */ onAvailable: function(id, fn, obj, overrideContext, checkContent) { var a = (YAHOO.lang.isString(id)) ? [id] : id; for (var i=0; i<a.length; i=i+1) { onAvailStack.push({id: a[i], fn: fn, obj: obj, overrideContext: overrideContext, checkReady: checkContent }); } 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. * * <p>The callback is executed with a single parameter: * the custom object parameter, if provided.</p> * * @method onContentReady * * @param {string} id the id of the element to look for. * @param {function} fn what to execute when the element is ready. * @param {object} obj an optional object to be passed back as * a parameter to fn. * @param {boolean|object} overrideContext If set to true, fn will execute * in the context of obj. If an object, fn will * exectute in the context of that object * * @static */ onContentReady: function(id, fn, obj, overrideContext) { this.onAvailable(id, fn, obj, overrideContext, true); }, /** * Executes the supplied callback when the DOM is first usable. This * will execute immediately if called after the DOMReady event has * fired. @todo the DOMContentReady event does not fire when the * script is dynamically injected into the page. This means the * DOMReady custom event will never fire in FireFox or Opera when the * library is injected. It _will_ fire in Safari, and the IE * implementation would allow for us to fire it if the defered script * is not available. We want this to behave the same in all browsers. * Is there a way to identify when the script has been injected * instead of included inline? Is there a way to know whether the * window onload event has fired without having had a listener attached * to it when it did so? * * <p>The callback is a CustomEvent, so the signature is:</p> * <p>type <string>, args <array>, customobject <object></p> * <p>For DOMReady events, there are no fire argments, so the * signature is:</p> * <p>"DOMReady", [], obj</p> * * * @method onDOMReady * * @param {function} fn what to execute when the element is found. * @param {object} obj an optional object to be passed back as * a parameter to fn. * @param {boolean|object} overrideContext If set to true, fn will execute * in the context of obj, if set to an object it * will execute in the context of that object * * @static */ // onDOMReady: function(fn, obj, overrideContext) { onDOMReady: function() { this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent, arguments); }, /** * Appends an event handler * * @method _addListener * * @param {String|HTMLElement|Array|NodeList} el An id, an element * reference, or a collection of ids and/or elements to assign the * listener 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|object} overrideContext If true, the obj passed in becomes * the execution context of the listener. If an * object, this object becomes the execution * context. * @param {boolen} capture capture or bubble phase * @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. * @private * @static */ _addListener: function(el, sType, fn, obj, overrideContext, bCapture) { 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, overrideContext) && ok; } return ok; } else if (YAHOO.lang.isString(el)) { 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._addListener(el, sType, fn, obj, overrideContext, bCapture); }); 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, overrideContext]; return true; } // if the user chooses to override the context, we use the custom // object passed in, otherwise the executing context will be the // HTML element that the event is registered on var context = el; if (overrideContext) { if (overrideContext === true) { context = obj; } else { context = overrideContext; } } // wrap the function so we can return the obj object when // the event fires; var wrappedFn = function(e) { return fn.call(context, YAHOO.util.Event.getEvent(e, el), obj); }; var li = [el, sType, fn, wrappedFn, context, obj, overrideContext, bCapture]; var index = listeners.length; // cache the listener so we can try to automatically unload listeners[index] = li; try { this._simpleAdd(el, sType, wrappedFn, bCapture); } catch(ex) { // handle an error trying to attach an event. If it fails // we need to clean up the cache this.lastError = ex; this.removeListener(el, sType, fn); return false; } return true; }, /** * Checks to see if the type requested is a special type * (as defined by the _specialTypes hash), and (if so) returns * the special type name. * * @method _getType * * @param {String} sType The type to look up * @private */ _getType: function (type) { return this._specialTypes[type] || type; }, /** * Appends an event handler * * @method addListener * * @param {String|HTMLElement|Array|NodeList} el An id, an element * reference, or a collection of ids and/or elements to assign the * listener 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|object} overrideContext If true, the obj passed in becomes * the execution context of the listener. If an * object, this object becomes the execution * context. * @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, overrideContext) { var capture = ((sType == FOCUSIN || sType == FOCUSOUT) && !YAHOO.env.ua.ie) ? true : false; return this._addListener(el, this._getType(sType), fn, obj, overrideContext, capture); }, /** * Attaches a focusin event listener to the specified element for * the purpose of listening for the focus event on the element's * descendants. * @method addFocusListener * * @param {String|HTMLElement|Array|NodeList} el An id, an element * reference, or a collection of ids and/or elements to assign the * listener to. * @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|object} overrideContext If true, the obj passed in becomes * the execution context of the listener. If an * object, this object becomes the execution * context. * @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 * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type. */ addFocusListener: function (el, fn, obj, overrideContext) { return this.on(el, FOCUSIN, fn, obj, overrideContext); }, /** * Removes a focusin event listener to the specified element for * the purpose of listening for the focus event on the element's * descendants. * * @method removeFocusListener * * @param {String|HTMLElement|Array|NodeList} el An id, an element * reference, or a collection of ids and/or elements to remove * the listener from. * @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 * @deprecated use YAHOO.util.Event.removeListener and specify "focusin" as the event type. */ removeFocusListener: function (el, fn) { return this.removeListener(el, FOCUSIN, fn); }, /** * Attaches a focusout event listener to the specified element for * the purpose of listening for the blur event on the element's * descendants. * * @method addBlurListener * * @param {String|HTMLElement|Array|NodeList} el An id, an element * reference, or a collection of ids and/or elements to assign the * listener to. * @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|object} overrideContext If true, the obj passed in becomes * the execution context of the listener. If an * object, this object becomes the execution * context. * @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 * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type. */ addBlurListener: function (el, fn, obj, overrideContext) { return this.on(el, FOCUSOUT, fn, obj, overrideContext); }, /** * Removes a focusout event listener to the specified element for * the purpose of listening for the blur event on the element's * descendants. * * @method removeBlurListener * * @param {String|HTMLElement|Array|NodeList} el An id, an element * reference, or a collection of ids and/or elements to remove * the listener from. * @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 * @deprecated use YAHOO.util.Event.removeListener and specify "focusout" as the event type. */ removeBlurListener: function (el, fn) { return this.removeListener(el, FOCUSOUT, fn); }, /** * Removes an event listener * * @method removeListener * * @param {String|HTMLElement|Array|NodeList} el An id, an element * reference, or a collection of ids and/or elements to remove * the listener from. * @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, li; sType = this._getType(sType); // 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=el.length-1; i>-1; 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=unloadListeners.length-1; i>-1; i--) { li = unloadListeners[i]; if (li && li[0] == el && li[1] == sType && li[2] == fn) { unloadListeners.splice(i, 1); // unloadListeners[i]=null; 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(listeners, el, sType, fn); } if (index >= 0) { cacheItem = listeners[index]; } if (!el || !cacheItem) { return false; } var bCapture = cacheItem[this.CAPTURE] === true ? true : false; try { this._simpleRemove(el, sType, cacheItem[this.WFN], bCapture); } catch(ex) { this.lastError = ex; return false; } // removed the wrapped handler delete listeners[index][this.WFN]; delete listeners[index][this.FN]; listeners.splice(index, 1); // listeners[index]=null; return true; }, /** * Returns the event's target element. Safari sometimes provides * a text node, and this is automatically resolved to the text * node's parent so that it behaves like other browsers. * @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(n) { try { if (n && 3 == n.nodeType) { return n.parentNode; } } catch(e) { } return n; }, /** * 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(ex) { this.lastError = ex; 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 * @param {HTMLElement} boundEl the element the listener is attached to * @return {Event} the event * @static */ getEvent: function(e, boundEl) { 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) { var code = ev.keyCode || ev.charCode || 0; // webkit key normalization if (YAHOO.env.ua.webkit && (code in webkitKeymap)) { code = webkitKeymap[code]; } return code; }, /** * Locating the saved event handler data by function ref * * @method _getCacheIndex * @static * @private */ _getCacheIndex: function(a, el, sType, fn) { for (var i=0, l=a.length; i<l; i=i+1) { var li = a[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) { try { return ( o && // o is something typeof o !== "string" && // o is not a string o.length && // o is indexed !o.tagName && // o is not an HTML element !o.alert && // o is not a window typeof o[0] !== "undefined" ); } catch(ex) { return false; } }, /** * @private * @property elCache * DOM element cache * @static * @deprecated Elements are not cached due to issues that arise when * elements are removed and re-added */ elCache: {}, /** * We cache elements bound by id because when the unload event * fires, we can no longer use document.getElementById * @method getEl * @static * @private * @deprecated Elements are not cached any longer */ getEl: function(id) { return (typeof id === "string") ? document.getElementById(id) : id; }, /** * Clears the element cache * @deprecated Elements are not cached any longer * @method clearCache * @static * @private */ clearCache: function() { }, /** * Custom event the fires when the dom is initially usable * @event DOMReadyEvent */ DOMReadyEvent: new YAHOO.util.CustomEvent("DOMReady", YAHOO, 0, 0, 1), /** * hook up any deferred listeners * @method _load * @static * @private */ _load: function(e) { if (!loadComplete) { loadComplete = true; var EU = YAHOO.util.Event; // Just in case DOMReady did not go off for some reason EU._ready(); // Available elements may not have been detected before the // window load event fires. Try to find them now so that the // the user is more likely to get the onAvailable notifications // before the window load notification EU._tryPreloadAttach(); } }, /** * Fires the DOMReady event listeners the first time the document is * usable. * @method _ready * @static * @private */ _ready: function(e) { var EU = YAHOO.util.Event; if (!EU.DOMReady) { EU.DOMReady=true; // Fire the content ready custom event EU.DOMReadyEvent.fire(); // Remove the DOMContentLoaded (FF/Opera) EU._simpleRemove(document, "DOMContentLoaded", EU._ready); } }, /** * 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 (onAvailStack.length === 0) { retryCount = 0; if (this._interval) { // clearInterval(this._interval); this._interval.cancel(); this._interval = null; } return; } if (this.locked) { return; } if (this.isIE) { // Hold off if DOMReady has not fired and check current // readyState to protect against the IE operation aborted // issue. if (!this.DOMReady) { this.startInterval(); return; } } 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 && onAvailStack.length > 0); } // onAvailable var notAvail = []; var executeItem = function (el, item) { var context = el; if (item.overrideContext) { if (item.overrideContext === true) { context = item.obj; } else { context = item.overrideContext; } } item.fn.call(context, item.obj); }; var i, len, item, el, ready=[]; // onAvailable onContentReady for (i=0, len=onAvailStack.length; i<len; i=i+1) { item = onAvailStack[i]; if (item) { el = this.getEl(item.id); if (el) { if (item.checkReady) { if (loadComplete || el.nextSibling || !tryAgain) { ready.push(item); onAvailStack[i] = null; } } else { executeItem(el, item); onAvailStack[i] = null; } } else { notAvail.push(item); } } } // make sure onContentReady fires after onAvailable for (i=0, len=ready.length; i<len; i=i+1) { item = ready[i]; executeItem(this.getEl(item.id), item); } retryCount--; if (tryAgain) { for (i=onAvailStack.length-1; i>-1; i--) { item = onAvailStack[i]; if (!item || !item.id) { onAvailStack.splice(i, 1); } } this.startInterval(); } else { if (this._interval) { // clearInterval(this._interval); this._interval.cancel(); this._interval = null; } } this.locked = false; }, /** * 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 oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el; var elListeners = this.getListeners(oEl, sType), i, len; if (elListeners) { for (i=elListeners.length-1; i>-1; i--) { var l = elListeners[i]; this.removeListener(oEl, l.type, l.fn); } } if (recurse && oEl && oEl.childNodes) { for (i=0,len=oEl.childNodes.length; i<len ; ++i) { this.purgeElement(oEl.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|string} the element or element id 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|object) whether or not to adjust the default context * scope: (boolean) the derived context based on the adjust parameter * index: (int) its position in the Event util listener cache * @static */ getListeners: function(el, sType) { var results=[], searchLists; if (!sType) { searchLists = [listeners, unloadListeners]; } else if (sType === "unload") { searchLists = [unloadListeners]; } else { sType = this._getType(sType); searchLists = [listeners]; } var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el; for (var j=0;j<searchLists.length; j=j+1) { var searchList = searchLists[j]; if (searchList) { for (var i=0,len=searchList.length; i<len ; ++i) { var l = searchList[i]; if ( l && l[this.EL] === oEl && (!sType || sType === l[this.TYPE]) ) { results.push({ type: l[this.TYPE], fn: l[this.FN], obj: l[this.OBJ], adjust: l[this.OVERRIDE], scope: l[this.ADJ_SCOPE], index: i }); } } } } return (results.length) ? results : 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, ul = unloadListeners.slice(), context; // execute and clear stored unload listeners for (i=0, len=unloadListeners.length; i<len; ++i) { l = ul[i]; if (l) { context = window; if (l[EU.ADJ_SCOPE]) { if (l[EU.ADJ_SCOPE] === true) { context = l[EU.UNLOAD_OBJ]; } else { context = l[EU.ADJ_SCOPE]; } } l[EU.FN].call(context, EU.getEvent(e, l[EU.EL]), l[EU.UNLOAD_OBJ] ); ul[i] = null; } } l = null; context = null; unloadListeners = null; // Remove listeners to handle IE memory leaks // 2.5.0 listeners are removed for all browsers again. FireFox preserves // at least some listeners between page refreshes, potentially causing // errors during page load (mouseover listeners firing before they // should if the user moves the mouse at the correct moment). if (listeners) { for (j=listeners.length-1; j>-1; j--) { l = listeners[j]; if (l) { EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], j); } } l=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]; } }, /** * Used by old versions of CustomEvent, restored for backwards * compatibility * @method regCE * @private * @static * @deprecated still here for backwards compatibility */ regCE: function() {}, /** * Adds a DOM event directly without the caching, cleanup, context 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.util.Event.onFocus is an alias for addFocusListener * @method onFocus * @see addFocusListener * @static * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type. */ EU.onFocus = EU.addFocusListener; /** * YAHOO.util.Event.onBlur is an alias for addBlurListener * @method onBlur * @see addBlurListener * @static * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type. */ EU.onBlur = EU.addBlurListener; /*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */ // Internet Explorer: use the readyState of a defered script. // This isolates what appears to be a safe moment to manipulate // the DOM prior to when the document's readyState suggests // it is safe to do so. if (EU.isIE) { if (self !== self.top) { document.onreadystatechange = function() { if (document.readyState == 'complete') { document.onreadystatechange = null; EU._ready(); } }; } else { // Process onAvailable/onContentReady items when the // DOM is ready. YAHOO.util.Event.onDOMReady( YAHOO.util.Event._tryPreloadAttach, YAHOO.util.Event, true); var n = document.createElement('p'); EU._dri = setInterval(function() { try { // throws an error if doc is not ready n.doScroll('left'); clearInterval(EU._dri); EU._dri = null; EU._ready(); n = null; } catch (ex) { } }, EU.POLL_INTERVAL); } // The document's readyState in Safari currently will // change to loaded/complete before images are loaded. } else if (EU.webkit && EU.webkit < 525) { EU._dri = setInterval(function() { var rs=document.readyState; if ("loaded" == rs || "complete" == rs) { clearInterval(EU._dri); EU._dri = null; EU._ready(); } }, EU.POLL_INTERVAL); // FireFox and Opera: These browsers provide a event for this // moment. The latest WebKit releases now support this event. } else { EU._simpleAdd(document, "DOMContentLoaded", EU._ready); } ///////////////////////////////////////////////////////////// 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 {Object} An object to be passed along when the event * fires * @param overrideContext {boolean} If true, the obj passed in becomes the * execution scope of the listener */ subscribe: function(p_type, p_fn, p_obj, overrideContext) { this.__yui_events = this.__yui_events || {}; var ce = this.__yui_events[p_type]; if (ce) { ce.subscribe(p_fn, p_obj, overrideContext); } 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, overrideContext: overrideContext } ); } }, /** * Unsubscribes one or more listeners the from the specified event * @method unsubscribe * @param p_type {string} The type, or name of the event. If the type * is not specified, it will attempt to remove * the listener from all hosted events. * @param p_fn {Function} The subscribed function to unsubscribe, if not * supplied, all subscribers will be removed. * @param p_obj {Object} The custom object passed to subscribe. This is * optional, but if supplied will be used to * disambiguate multiple listeners that are the same * (e.g., you subscribe many object using a function * that lives on the prototype) * @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 evts = this.__yui_events; if (p_type) { var ce = evts[p_type]; if (ce) { return ce.unsubscribe(p_fn, p_obj); } } else { var ret = true; for (var i in evts) { if (YAHOO.lang.hasOwnProperty(evts, i)) { ret = ret && evts[i].unsubscribe(p_fn, p_obj); } } return ret; } return false; }, /** * Removes all listeners from the specified event. If the event type * is not specified, all listeners from all hosted custom events will * be removed. * @method unsubscribeAll * @param p_type {string} The type, or name of the event */ unsubscribeAll: function(p_type) { return this.unsubscribe(p_type); }, /** * 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> * fireOnce: if true, the custom event will only notify subscribers * once regardless of the number of times the event is fired. In * addition, new subscribers will be executed immediately if the * event has already fired. * 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 || {}, events = this.__yui_events, ce; if (events[p_type]) { } else { ce = new YAHOO.util.CustomEvent(p_type, opts.scope || this, opts.silent, YAHOO.util.CustomEvent.FLAT, opts.fireOnce); 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].overrideContext); } } } 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 * */ fireEvent: function(p_type) { this.__yui_events = this.__yui_events || {}; var ce = this.__yui_events[p_type]; if (!ce) { return null; } var args = []; for (var i=1; i<arguments.length; ++i) { args.push(arguments[i]); } return ce.fire.apply(ce, args); }, /** * 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; } }; (function() { var Event = YAHOO.util.Event, Lang = YAHOO.lang; /** * KeyListener is a utility that provides an easy interface for listening for * keydown/keyup events fired against DOM elements. * @namespace YAHOO.util * @class KeyListener * @constructor * @param {HTMLElement} attachTo The element or element ID to which the key * event should be attached * @param {String} attachTo The element or element ID to which the key * event should be attached * @param {Object} keyData The object literal representing the key(s) * to detect. Possible attributes are * shift(boolean), alt(boolean), ctrl(boolean) * and keys(either an int or an array of ints * representing keycodes). * @param {Function} handler The CustomEvent handler to fire when the * key event is detected * @param {Object} handler An object literal representing the handler. * @param {String} event Optional. The event (keydown or keyup) to * listen for. Defaults automatically to keydown. * * @knownissue the "keypress" event is completely broken in Safari 2.x and below. * the workaround is use "keydown" for key listening. However, if * it is desired to prevent the default behavior of the keystroke, * that can only be done on the keypress event. This makes key * handling quite ugly. * @knownissue keydown is also broken in Safari 2.x and below for the ESC key. * There currently is no workaround other than choosing another * key to listen for. */ YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) { if (!attachTo) { } else if (!keyData) { } else if (!handler) { } if (!event) { event = YAHOO.util.KeyListener.KEYDOWN; } /** * The CustomEvent fired internally when a key is pressed * @event keyEvent * @private * @param {Object} keyData The object literal representing the key(s) to * detect. Possible attributes are shift(boolean), * alt(boolean), ctrl(boolean) and keys(either an * int or an array of ints representing keycodes). */ var keyEvent = new YAHOO.util.CustomEvent("keyPressed"); /** * The CustomEvent fired when the KeyListener is enabled via the enable() * function * @event enabledEvent * @param {Object} keyData The object literal representing the key(s) to * detect. Possible attributes are shift(boolean), * alt(boolean), ctrl(boolean) and keys(either an * int or an array of ints representing keycodes). */ this.enabledEvent = new YAHOO.util.CustomEvent("enabled"); /** * The CustomEvent fired when the KeyListener is disabled via the * disable() function * @event disabledEvent * @param {Object} keyData The object literal representing the key(s) to * detect. Possible attributes are shift(boolean), * alt(boolean), ctrl(boolean) and keys(either an * int or an array of ints representing keycodes). */ this.disabledEvent = new YAHOO.util.CustomEvent("disabled"); if (Lang.isString(attachTo)) { attachTo = document.getElementById(attachTo); // No Dom util } if (Lang.isFunction(handler)) { keyEvent.subscribe(handler); } else { keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope); } /** * Handles the key event when a key is pressed. * @method handleKeyPress * @param {DOMEvent} e The keypress DOM event * @param {Object} obj The DOM event scope object * @private */ function handleKeyPress(e, obj) { if (! keyData.shift) { keyData.shift = false; } if (! keyData.alt) { keyData.alt = false; } if (! keyData.ctrl) { keyData.ctrl = false; } // check held down modifying keys first if (e.shiftKey == keyData.shift && e.altKey == keyData.alt && e.ctrlKey == keyData.ctrl) { // if we pass this, all modifiers match var dataItem, keys = keyData.keys, key; if (YAHOO.lang.isArray(keys)) { for (var i=0;i<keys.length;i++) { dataItem = keys[i]; key = Event.getCharCode(e); if (dataItem == key) { keyEvent.fire(key, e); break; } } } else { key = Event.getCharCode(e); if (keys == key ) { keyEvent.fire(key, e); } } } } /** * Enables the KeyListener by attaching the DOM event listeners to the * target DOM element * @method enable */ this.enable = function() { if (! this.enabled) { Event.on(attachTo, event, handleKeyPress); this.enabledEvent.fire(keyData); } /** * Boolean indicating the enabled/disabled state of the Tooltip * @property enabled * @type Boolean */ this.enabled = true; }; /** * Disables the KeyListener by removing the DOM event listeners from the * target DOM element * @method disable */ this.disable = function() { if (this.enabled) { Event.removeListener(attachTo, event, handleKeyPress); this.disabledEvent.fire(keyData); } this.enabled = false; }; /** * Returns a String representation of the object. * @method toString * @return {String} The string representation of the KeyListener */ this.toString = function() { return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + (attachTo.id ? "[" + attachTo.id + "]" : ""); }; }; var KeyListener = YAHOO.util.KeyListener; /** * Constant representing the DOM "keydown" event. * @property YAHOO.util.KeyListener.KEYDOWN * @static * @final * @type String */ KeyListener.KEYDOWN = "keydown"; /** * Constant representing the DOM "keyup" event. * @property YAHOO.util.KeyListener.KEYUP * @static * @final * @type String */ KeyListener.KEYUP = "keyup"; /** * keycode constants for a subset of the special keys * @property KEY * @static * @final */ KeyListener.KEY = { ALT : 18, BACK_SPACE : 8, CAPS_LOCK : 20, CONTROL : 17, DELETE : 46, DOWN : 40, END : 35, ENTER : 13, ESCAPE : 27, HOME : 36, LEFT : 37, META : 224, NUM_LOCK : 144, PAGE_DOWN : 34, PAGE_UP : 33, PAUSE : 19, PRINTSCREEN : 44, RIGHT : 39, SCROLL_LOCK : 145, SHIFT : 16, SPACE : 32, TAB : 9, UP : 38 }; })(); YAHOO.register("event", YAHOO.util.Event, {version: "2.8.0r4", build: "2449"});