/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. version 0.9.0 */ /** * @class 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. * @constructor */ YAHOO.util.CustomEvent = function(type, oScope) { /** * The type of event, returned to subscribers when the event fires * @type string */ this.type = type; /** * The scope the the event will fire from. Defaults to the window obj * @type object */ this.scope = oScope || window; /** * The subscribers to this event * @type array */ this.subscribers = []; // Register with the event utility for automatic cleanup. Made optional // so that CustomEvent can be used independently of pe.event if (YAHOO.util["Event"]) { YAHOO.util.Event.regCE(this); } }; YAHOO.util.CustomEvent.prototype = { /** * Subscribes the caller to this event * @param {Function} fn The function to execute * @param {Object} obj An object to be passed along when the event fires * @param {boolean} bOverride If true, the obj passed in becomes the execution * scope of the listener */ subscribe: function(fn, obj, bOverride) { this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, bOverride) ); }, /** * Unsubscribes the caller from this event * @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 subscriber was found and detached. */ unsubscribe: function(fn, obj) { var found = false; for (var i=0; i * - The type of event * - All of the arguments fire() was executed with as an array * - The custom object (if any) that was passed into the subscribe() method * * * @param {Array} an arbitrary set of parameters to pass to the handler */ fire: function() { for (var i=0; i= 0) { cacheItem = listeners[index]; } if (!el || !cacheItem) { return false; } if (el.removeEventListener) { el.removeEventListener(sType, cacheItem[this.WFN], false); // alert("adsf"); } else if (el.detachEvent) { el.detachEvent("on" + sType, cacheItem[this.WFN]); } // removed the wrapped handler delete listeners[index][this.WFN]; delete listeners[index][this.FN]; delete listeners[index]; return true; }, /** * Returns the event's target element * @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 * @return {HTMLElement} the event's target */ getTarget: function(ev, resolveTextNode) { var t = ev.target || ev.srcElement; if (resolveTextNode && t && "#text" == t.nodeName) { return t.parentNode; } else { return t; } }, /** * Returns the event's pageX * @param {Event} ev the event * @return {int} the event's pageX */ 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 * @param {Event} ev the event * @return {int} the event's pageY */ 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 event's related target * @param {Event} ev the event * @return {HTMLElement} the event's relatedTarget */ 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 t; }, /** * Returns the time of the event. If the time is not included, the * event is modified using the current time. * @param {Event} ev the event * @return {Date} the time of the event */ getTime: function(ev) { if (!ev.time) { var t = new Date().getTime(); try { ev.time = t; } catch(e) { // can't set the time property return t; } } return ev.time; }, /** * Convenience method for stopPropagation + preventDefault * @param {Event} ev the event */ stopEvent: function(ev) { this.stopPropagation(ev); this.preventDefault(ev); }, /** * Stops event propagation * @param {Event} ev the event */ stopPropagation: function(ev) { if (ev.stopPropagation) { ev.stopPropagation(); } else { ev.cancelBubble = true; } }, /** * Prevents the default behavior of the event * @param {Event} ev the event */ preventDefault: function(ev) { if (ev.preventDefault) { ev.preventDefault(); } else { ev.returnValue = false; } }, /** * Returns the event, should not be necessary for user to call * @param {Event} the event parameter from the handler * @return {Event} the event */ 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 * @param {Event} ev the event * @return {int} the event's charCode */ getCharCode: function(ev) { return ev.charCode || (ev.type == "keypress") ? ev.keyCode : 0; }, /** * @private * Locating the saved event handler data by function ref */ _getCacheIndex: function(el, sType, fn) { for (var i=0; i< listeners.length; ++i) { var li = listeners[i]; if ( li && li[this.FN] == fn && li[this.EL] == el && li[this.TYPE] == sType ) { return i; } } return -1; }, /** * 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. * @param o the object to test * @return {boolean} true if the object is array-like and populated */ _isValidCollection: function(o) { // alert(o.constructor.toString()) // alert(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 * DOM element cache */ elCache: {}, /** * We cache elements bound by id because when the unload event * fires, we can no longer use document.getElementById * @private */ getEl: function(id) { /* // this is a problem when replaced via document.getElementById if (! this.elCache[id]) { try { var el = document.getElementById(id); if (el) { this.elCache[id] = el; } } catch (er) { } } return this.elCache[id]; */ return document.getElementById(id); }, /** * Clears the element cache */ clearCache: function() { for (i in this.elCache) { delete this.elCache[i]; } }, /** * Called by CustomEvent instances to provide a handle to the * event * that can be removed later on. Should be package * protected. * @private */ regCE: function(ce) { customEvents.push(ce); }, /** * @private * hook up any deferred listeners */ _load: function(e) { loadComplete = true; }, /** * Polling function that runs before the onload event fires, * attempting * to attach to DOM Nodes as soon as they are * available * @private */ _tryPreloadAttach: function() { // 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; for (var i=0; i < delayedListeners.length; ++i) { var d = delayedListeners[i]; // There may be a race condition here, so we need to // verify the array element is usable. if (d) { // el will be null if document.getElementById did not // work var el = this.getEl(d[this.EL]); if (el) { this.on(el, d[this.TYPE], d[this.FN], d[this.SCOPE], d[this.ADJ_SCOPE]); delete delayedListeners[i]; } } } if (tryAgain) { setTimeout("YAHOO.util.Event._tryPreloadAttach()", 50); } }, /** * Removes all listeners registered by pe.event. Called * automatically during the unload event. */ _unload: function(e, me) { for (var i=0; i < unloadListeners.length; ++i) { var l = unloadListeners[i]; if (l) { var scope = (l[this.ADJ_SCOPE]) ? l[this.SCOPE]: window; l[this.FN].call(scope, this.getEvent(e), l[this.SCOPE] ); } } if (listeners && listeners.length > 0) { for (i = 0; i < listeners.length; ++i) { l = listeners[i]; if (l) { this.removeListener(l[this.EL], l[this.TYPE], l[this.FN]); } } this.clearCache(); } for (i = 0; i < customEvents.length; ++i) { customEvents[i].unsubscribeAll(); delete customEvents[i]; } for (i = 0; i < legacyEvents.length; ++i) { // dereference the element delete legacyEvents[i][0]; // delete the array item delete legacyEvents[i]; } }, /** * Returns scrollLeft * @private */ _getScrollLeft: function() { return this._getScroll()[1]; }, /** * Returns scrollTop * @private */ _getScrollTop: function() { return this._getScroll()[0]; }, /** * Returns the scrollTop and scrollLeft. Used to calculate the * pageX and pageY in Internet Explorer * @private */ _getScroll: function() { var dd = document.documentElement; db = document.body; if (dd && dd.scrollTop) { return [dd.scrollTop, dd.scrollLeft]; } else if (db) { return [db.scrollTop, db.scrollLeft]; } else { return [0, 0]; } } }; } (); YAHOO.util.Event.on = YAHOO.util.Event.addListener; if (document && document.body) { YAHOO.util.Event._load(); } else { YAHOO.util.Event.on(window, "load", YAHOO.util.Event._load, YAHOO.util.Event, true); } YAHOO.util.Event.on(window, "unload", YAHOO.util.Event._unload, YAHOO.util.Event, true); YAHOO.util.Event._tryPreloadAttach(); }