/* 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 */ /** * Augments the Event Utility with a delegate method that * facilitates easy creation of delegated event listeners. (Note: Using CSS * selectors as the filtering criteria for delegated event listeners requires * inclusion of the Selector Utility.) * * @module event-delegate * @title Event Utility Event Delegation Module * @namespace YAHOO.util * @requires event */ (function () { var Event = YAHOO.util.Event, Lang = YAHOO.lang, delegates = [], getMatch = function(el, selector, container) { var returnVal; if (!el || el === container) { returnVal = false; } else { returnVal = YAHOO.util.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container); } return returnVal; }; Lang.augmentObject(Event, { /** * Creates a delegate function used to call event listeners specified * via the YAHOO.util.Event.delegate method. * * @method _createDelegate * * @param {Function} fn The method (event listener) to call. * @param {Function|string} filter Function or CSS selector used to * determine for what element(s) the event listener should be called. * @param {Object} obj An arbitrary object that will be * passed as a parameter to the listener. * @param {Boolean|object} overrideContext If true, the value of the * obj parameter becomes the execution context * of the listener. If an object, this object * becomes the execution context. * @return {Function} Function that will call the event listener * specified by the YAHOO.util.Event.delegate method. * @private * @for Event * @static */ _createDelegate: function (fn, filter, obj, overrideContext) { return function (event) { var container = this, target = Event.getTarget(event), selector = filter, // The user might have specified the document object // as the delegation container, in which case it is not // nessary to scope the provided CSS selector(s) to the // delegation container bDocument = (container.nodeType === 9), matchedEl, context, sID, sIDSelector; if (Lang.isFunction(filter)) { matchedEl = filter(target); } else if (Lang.isString(filter)) { if (!bDocument) { sID = container.id; if (!sID) { sID = Event.generateId(container); } // Scope all selectors to the container sIDSelector = ("#" + sID + " "); selector = (sIDSelector + filter).replace(/,/gi, ("," + sIDSelector)); } if (YAHOO.util.Selector.test(target, selector)) { matchedEl = target; } else if (YAHOO.util.Selector.test(target, ((selector.replace(/,/gi, " *,")) + " *"))) { // The target is a descendant of an element matching // the selector, so crawl up to find the ancestor that // matches the selector matchedEl = getMatch(target, selector, container); } } if (matchedEl) { // The default context for delegated listeners is the // element that matched the filter. context = matchedEl; if (overrideContext) { if (overrideContext === true) { context = obj; } else { context = overrideContext; } } // Call the listener passing in the container and the // element that matched the filter in case the user // needs those. return fn.call(context, event, matchedEl, container, obj); } }; }, /** * Appends a delegated event listener. Delegated event listeners * receive three arguments by default: the DOM event, the element * specified by the filtering function or CSS selector, and the * container element (the element to which the event listener is * bound). (Note: Using the delegate method requires the event-delegate * module. Using CSS selectors as the filtering criteria for delegated * event listeners requires inclusion of the Selector Utility.) * * @method delegate * * @param {String|HTMLElement|Array|NodeList} container An id, an element * reference, or a collection of ids and/or elements to assign the * listener to. * @param {String} type The type of event listener to append * @param {Function} fn The method the event invokes * @param {Function|string} filter Function or CSS selector used to * determine for what element(s) the event listener should be called. * When a function is specified, the function should return an * HTML element. Using a CSS Selector requires the inclusion of the * CSS Selector Utility. * @param {Object} obj An arbitrary object that will be * passed as a parameter to the listener * @param {Boolean|object} overrideContext If true, the value of the obj parameter becomes * the execution context of the listener. If an * object, this object becomes the execution * context. * @return {Boolean} Returns 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 * @for Event */ delegate: function (container, type, fn, filter, obj, overrideContext) { var sType = type, fnMouseDelegate, fnDelegate; if (Lang.isString(filter) && !YAHOO.util.Selector) { YAHOO.log("Using a CSS selector to define the filtering criteria for a delegated listener requires the Selector Utility.", "error", "Event"); return false; } if (type == "mouseenter" || type == "mouseleave") { if (!Event._createMouseDelegate) { YAHOO.log("Delegating a " + type + " event requires the event-mouseenter module.", "error", "Event"); return false; } // Look up the real event--either mouseover or mouseout sType = Event._getType(type); fnMouseDelegate = Event._createMouseDelegate(fn, obj, overrideContext); fnDelegate = Event._createDelegate(function (event, matchedEl, container) { return fnMouseDelegate.call(matchedEl, event, container); }, filter, obj, overrideContext); } else { fnDelegate = Event._createDelegate(fn, filter, obj, overrideContext); } delegates.push([container, sType, fn, fnDelegate]); return Event.on(container, sType, fnDelegate); }, /** * Removes a delegated event listener. * * @method removeDelegate * * @param {String|HTMLElement|Array|NodeList} container An id, an element * reference, or a collection of ids and/or elements to remove * the listener from. * @param {String} type The type of event to remove. * @param {Function} fn The method the event invokes. If fn is * undefined, then all event listeners for the type of event are * removed. * @return {boolean} Returns true if the unbind was successful, false * otherwise. * @static * @for Event */ removeDelegate: function (container, type, fn) { var sType = type, returnVal = false, index, cacheItem; // Look up the real event--either mouseover or mouseout if (type == "mouseenter" || type == "mouseleave") { sType = Event._getType(type); } index = Event._getCacheIndex(delegates, container, sType, fn); if (index >= 0) { cacheItem = delegates[index]; } if (container && cacheItem) { returnVal = Event.removeListener(cacheItem[0], cacheItem[1], cacheItem[3]); if (returnVal) { delete delegates[index][2]; delete delegates[index][3]; delegates.splice(index, 1); } } return returnVal; } }); }()); YAHOO.register("event-delegate", YAHOO.util.Event, {version: "2.8.0r4", build: "2449"});