Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

event-delegate-debug.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /*
  2. Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.com/yui/license.html
  5. version: 3.4.0
  6. build: nightly
  7. */
  8. YUI.add('event-delegate', function(Y) {
  9. /**
  10. * Adds event delegation support to the library.
  11. *
  12. * @module event
  13. * @submodule event-delegate
  14. */
  15. var toArray = Y.Array,
  16. YLang = Y.Lang,
  17. isString = YLang.isString,
  18. isObject = YLang.isObject,
  19. isArray = YLang.isArray,
  20. selectorTest = Y.Selector.test,
  21. detachCategories = Y.Env.evt.handles;
  22. /**
  23. * <p>Sets up event delegation on a container element. The delegated event
  24. * will use a supplied selector or filtering function to test if the event
  25. * references at least one node that should trigger the subscription
  26. * callback.</p>
  27. *
  28. * <p>Selector string filters will trigger the callback if the event originated
  29. * from a node that matches it or is contained in a node that matches it.
  30. * Function filters are called for each Node up the parent axis to the
  31. * subscribing container node, and receive at each level the Node and the event
  32. * object. The function should return true (or a truthy value) if that Node
  33. * should trigger the subscription callback. Note, it is possible for filters
  34. * to match multiple Nodes for a single event. In this case, the delegate
  35. * callback will be executed for each matching Node.</p>
  36. *
  37. * <p>For each matching Node, the callback will be executed with its 'this'
  38. * object set to the Node matched by the filter (unless a specific context was
  39. * provided during subscription), and the provided event's
  40. * <code>currentTarget</code> will also be set to the matching Node. The
  41. * containing Node from which the subscription was originally made can be
  42. * referenced as <code>e.container</code>.
  43. *
  44. * @method delegate
  45. * @param type {String} the event type to delegate
  46. * @param fn {Function} the callback function to execute. This function
  47. * will be provided the event object for the delegated event.
  48. * @param el {String|node} the element that is the delegation container
  49. * @param spec {string|Function} a selector that must match the target of the
  50. * event or a function to test target and its parents for a match
  51. * @param context optional argument that specifies what 'this' refers to.
  52. * @param args* 0..n additional arguments to pass on to the callback function.
  53. * These arguments will be added after the event object.
  54. * @return {EventHandle} the detach handle
  55. * @for YUI
  56. */
  57. function delegate(type, fn, el, filter) {
  58. var args = toArray(arguments, 0, true),
  59. query = isString(el) ? el : null,
  60. typeBits, synth, container, categories, cat, i, len, handles, handle;
  61. // Support Y.delegate({ click: fnA, key: fnB }, context, filter, ...);
  62. // and Y.delegate(['click', 'key'], fn, context, filter, ...);
  63. if (isObject(type)) {
  64. handles = [];
  65. if (isArray(type)) {
  66. for (i = 0, len = type.length; i < len; ++i) {
  67. args[0] = type[i];
  68. handles.push(Y.delegate.apply(Y, args));
  69. }
  70. } else {
  71. // Y.delegate({'click', fn}, context, filter) =>
  72. // Y.delegate('click', fn, context, filter)
  73. args.unshift(null); // one arg becomes two; need to make space
  74. for (i in type) {
  75. if (type.hasOwnProperty(i)) {
  76. args[0] = i;
  77. args[1] = type[i];
  78. handles.push(Y.delegate.apply(Y, args));
  79. }
  80. }
  81. }
  82. return new Y.EventHandle(handles);
  83. }
  84. typeBits = type.split(/\|/);
  85. if (typeBits.length > 1) {
  86. cat = typeBits.shift();
  87. args[0] = type = typeBits.shift();
  88. }
  89. synth = Y.Node.DOM_EVENTS[type];
  90. if (isObject(synth) && synth.delegate) {
  91. handle = synth.delegate.apply(synth, arguments);
  92. }
  93. if (!handle) {
  94. if (!type || !fn || !el || !filter) {
  95. Y.log("delegate requires type, callback, parent, & filter", "warn");
  96. return;
  97. }
  98. container = (query) ? Y.Selector.query(query, null, true) : el;
  99. if (!container && isString(el)) {
  100. handle = Y.on('available', function () {
  101. Y.mix(handle, Y.delegate.apply(Y, args), true);
  102. }, el);
  103. }
  104. if (!handle && container) {
  105. args.splice(2, 2, container); // remove the filter
  106. handle = Y.Event._attach(args, { facade: false });
  107. handle.sub.filter = filter;
  108. handle.sub._notify = delegate.notifySub;
  109. }
  110. }
  111. if (handle && cat) {
  112. categories = detachCategories[cat] || (detachCategories[cat] = {});
  113. categories = categories[type] || (categories[type] = []);
  114. categories.push(handle);
  115. }
  116. return handle;
  117. }
  118. /**
  119. Overrides the <code>_notify</code> method on the normal DOM subscription to
  120. inject the filtering logic and only proceed in the case of a match.
  121. This method is hosted as a private property of the `delegate` method
  122. (e.g. `Y.delegate.notifySub`)
  123. @method notifySub
  124. @param thisObj {Object} default 'this' object for the callback
  125. @param args {Array} arguments passed to the event's <code>fire()</code>
  126. @param ce {CustomEvent} the custom event managing the DOM subscriptions for
  127. the subscribed event on the subscribing node.
  128. @return {Boolean} false if the event was stopped
  129. @private
  130. @static
  131. @since 3.2.0
  132. **/
  133. delegate.notifySub = function (thisObj, args, ce) {
  134. // Preserve args for other subscribers
  135. args = args.slice();
  136. if (this.args) {
  137. args.push.apply(args, this.args);
  138. }
  139. // Only notify subs if the event occurred on a targeted element
  140. var currentTarget = delegate._applyFilter(this.filter, args, ce),
  141. //container = e.currentTarget,
  142. e, i, len, ret;
  143. if (currentTarget) {
  144. // Support multiple matches up the the container subtree
  145. currentTarget = toArray(currentTarget);
  146. // The second arg is the currentTarget, but we'll be reusing this
  147. // facade, replacing the currentTarget for each use, so it doesn't
  148. // matter what element we seed it with.
  149. e = args[0] = new Y.DOMEventFacade(args[0], ce.el, ce);
  150. e.container = Y.one(ce.el);
  151. for (i = 0, len = currentTarget.length; i < len && !e.stopped; ++i) {
  152. e.currentTarget = Y.one(currentTarget[i]);
  153. ret = this.fn.apply(this.context || e.currentTarget, args);
  154. if (ret === false) { // stop further notifications
  155. break;
  156. }
  157. }
  158. return ret;
  159. }
  160. };
  161. /**
  162. Compiles a selector string into a filter function to identify whether
  163. Nodes along the parent axis of an event's target should trigger event
  164. notification.
  165. This function is memoized, so previously compiled filter functions are
  166. returned if the same selector string is provided.
  167. This function may be useful when defining synthetic events for delegate
  168. handling.
  169. Hosted as a property of the `delegate` method (e.g. `Y.delegate.compileFilter`).
  170. @method compileFilter
  171. @param selector {String} the selector string to base the filtration on
  172. @return {Function}
  173. @since 3.2.0
  174. @static
  175. **/
  176. delegate.compileFilter = Y.cached(function (selector) {
  177. return function (target, e) {
  178. return selectorTest(target._node, selector, e.currentTarget._node);
  179. };
  180. });
  181. /**
  182. Walks up the parent axis of an event's target, and tests each element
  183. against a supplied filter function. If any Nodes, including the container,
  184. satisfy the filter, the delegated callback will be triggered for each.
  185. Hosted as a protected property of the `delegate` method (e.g.
  186. `Y.delegate._applyFilter`).
  187. @method _applyFilter
  188. @param filter {Function} boolean function to test for inclusion in event
  189. notification
  190. @param args {Array} the arguments that would be passed to subscribers
  191. @param ce {CustomEvent} the DOM event wrapper
  192. @return {Node|Node[]|undefined} The Node or Nodes that satisfy the filter
  193. @protected
  194. **/
  195. delegate._applyFilter = function (filter, args, ce) {
  196. var e = args[0],
  197. container = ce.el, // facadeless events in IE, have no e.currentTarget
  198. target = e.target || e.srcElement,
  199. match = [],
  200. isContainer = false;
  201. // Resolve text nodes to their containing element
  202. if (target.nodeType === 3) {
  203. target = target.parentNode;
  204. }
  205. // passing target as the first arg rather than leaving well enough alone
  206. // making 'this' in the filter function refer to the target. This is to
  207. // support bound filter functions.
  208. args.unshift(target);
  209. if (isString(filter)) {
  210. while (target) {
  211. isContainer = (target === container);
  212. if (selectorTest(target, filter, (isContainer ?null: container))) {
  213. match.push(target);
  214. }
  215. if (isContainer) {
  216. break;
  217. }
  218. target = target.parentNode;
  219. }
  220. } else {
  221. // filter functions are implementer code and should receive wrappers
  222. args[0] = Y.one(target);
  223. args[1] = new Y.DOMEventFacade(e, container, ce);
  224. while (target) {
  225. // filter(target, e, extra args...) - this === target
  226. if (filter.apply(args[0], args)) {
  227. match.push(target);
  228. }
  229. if (target === container) {
  230. break;
  231. }
  232. target = target.parentNode;
  233. args[0] = Y.one(target);
  234. }
  235. args[1] = e; // restore the raw DOM event
  236. }
  237. if (match.length <= 1) {
  238. match = match[0]; // single match or undefined
  239. }
  240. // remove the target
  241. args.shift();
  242. return match;
  243. };
  244. /**
  245. * Sets up event delegation on a container element. The delegated event
  246. * will use a supplied filter to test if the callback should be executed.
  247. * This filter can be either a selector string or a function that returns
  248. * a Node to use as the currentTarget for the event.
  249. *
  250. * The event object for the delegated event is supplied to the callback
  251. * function. It is modified slightly in order to support all properties
  252. * that may be needed for event delegation. 'currentTarget' is set to
  253. * the element that matched the selector string filter or the Node returned
  254. * from the filter function. 'container' is set to the element that the
  255. * listener is delegated from (this normally would be the 'currentTarget').
  256. *
  257. * Filter functions will be called with the arguments that would be passed to
  258. * the callback function, including the event object as the first parameter.
  259. * The function should return false (or a falsey value) if the success criteria
  260. * aren't met, and the Node to use as the event's currentTarget and 'this'
  261. * object if they are.
  262. *
  263. * @method delegate
  264. * @param type {string} the event type to delegate
  265. * @param fn {function} the callback function to execute. This function
  266. * will be provided the event object for the delegated event.
  267. * @param el {string|node} the element that is the delegation container
  268. * @param filter {string|function} a selector that must match the target of the
  269. * event or a function that returns a Node or false.
  270. * @param context optional argument that specifies what 'this' refers to.
  271. * @param args* 0..n additional arguments to pass on to the callback function.
  272. * These arguments will be added after the event object.
  273. * @return {EventHandle} the detach handle
  274. * @for YUI
  275. */
  276. Y.delegate = Y.Event.delegate = delegate;
  277. }, '3.4.0' ,{requires:['node-base']});