]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/event/event-delegate.js
Release 6.2.0beta4
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / event / event-delegate.js
1 /*
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 3.0.0
6 build: 1549
7 */
8 YUI.add('event-delegate', function(Y) {
9
10 /**
11  * Adds event delegation support to the library.
12  * 
13  * @module event
14  * @submodule event-delegate
15  */
16
17 var Event = Y.Event,
18         Lang = Y.Lang,
19
20         delegates = {},
21         
22         specialTypes = {
23                 mouseenter: "mouseover",
24                 mouseleave: "mouseout"
25         },
26
27         resolveTextNode = function(n) {
28             try {
29                 if (n && 3 == n.nodeType) {
30                     return n.parentNode;
31                 }
32             } catch(e) { }
33             return n;
34         },
35
36     delegateHandler = function(delegateKey, e, el) {
37
38         var target = resolveTextNode((e.target || e.srcElement)), 
39             tests  = delegates[delegateKey],
40             spec, 
41                         ename,
42                         matched,
43                         fn,
44                         ev;
45
46
47                 var getMatch = function(el, selector, container) {
48                         
49                         var returnVal;
50                         
51                         if (!el || el === container) {
52                                 returnVal = false;
53                         }
54                         else {
55                                 returnVal = Y.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container);
56                         }
57                         
58                         return returnVal;
59                         
60                 };
61
62
63         for (spec in tests) {
64
65             if (tests.hasOwnProperty(spec)) {
66
67                 ename  = tests[spec];
68                                 fn      = tests.fn;
69                                 matched = null;
70
71
72                                 if (Y.Selector.test(target, spec, el)) {
73                                         matched = target;
74                                 }
75                                 else if (Y.Selector.test(target, ((spec.replace(/,/gi, " *,")) + " *"), el)) {
76                                                 
77                                         //      The target is a descendant of an element matching 
78                                         //      the selector, so crawl up to find the ancestor that 
79                                         //      matches the selector
80                                         
81                                         matched = getMatch(target, spec, el);
82                                         
83                                 }
84
85
86                                 if (matched) {
87
88                     if (!ev) {
89                         ev = new Y.DOMEventFacade(e, el);
90                         ev.container = ev.currentTarget;
91                     }
92
93                     ev.currentTarget = Y.Node.get(matched);
94
95                                         Y.publish(ename, {
96                                        contextFn: function() {
97                                            return ev.currentTarget;
98                                        }
99                                    });
100
101                                         if (fn) {
102                                                 fn(ev, ename);
103                                         }
104                                         else {
105                         Y.fire(ename, ev);                                                              
106                                         }
107                                         
108                                 }
109
110             }
111         }
112
113     },
114
115         attach = function (type, key, element) {
116
117                 var focusMethods = {
118                                 focus: Event._attachFocus,
119                                 blur: Event._attachBlur
120                         },
121
122                         attachFn = focusMethods[type],
123
124                         args = [type, 
125                         function (e) {
126                     delegateHandler(key, (e || window.event), element);
127                         }, 
128                         element];
129
130
131                 if (attachFn) {
132                         return attachFn(args, { capture: true, facade: false });
133                 }
134                 else {
135                         return Event._attach(args, { facade: false });
136                 }
137                 
138         },
139
140     sanitize = Y.cached(function(str) {
141         return str.replace(/[|,:]/g, '~');
142     });
143
144 /**
145  * Sets up event delegation on a container element.  The delegated event
146  * will use a supplied selector to test if the target or one of the
147  * descendants of the target match it.  The supplied callback function 
148  * will only be executed if a match was encountered, and, in fact, 
149  * will be executed for each element that matches if you supply an 
150  * ambiguous selector.
151  *
152  * The event object for the delegated event is supplied to the callback
153  * function.  It is modified slightly in order to support all properties
154  * that may be needed for event delegation.  'currentTarget' is set to
155  * the element that matched the delegation specifcation.  'container' is
156  * set to the element that the listener is bound to (this normally would
157  * be the 'currentTarget').
158  *
159  * @event delegate
160  * @param type {string} 'delegate'
161  * @param fn {function} the callback function to execute.  This function
162  * will be provided the event object for the delegated event.
163  * @param el {string|node} the element that is the delegation container
164  * @param delegateType {string} the event type to delegate
165  * @param spec {string} a selector that must match the target of the
166  * event.
167  * @param context optional argument that specifies what 'this' refers to.
168  * @param args* 0..n additional arguments to pass on to the callback function.
169  * These arguments will be added after the event object.
170  * @return {EventHandle} the detach handle
171  * @for YUI
172  * @deprecated use Y.delegate
173  */
174 Y.Env.evt.plugins.delegate = {
175
176     on: function(type, fn, el, delegateType, spec) {
177
178
179                 var args = Y.Array(arguments, 0, true);
180                 
181                 args.splice(3, 1);
182                 
183                 args[0] = delegateType;
184
185                 return Y.delegate.apply(Y, args);
186
187     }
188
189 };
190
191
192 /**
193  * Sets up event delegation on a container element.  The delegated event
194  * will use a supplied selector to test if the target or one of the
195  * descendants of the target match it.  The supplied callback function 
196  * will only be executed if a match was encountered, and, in fact, 
197  * will be executed for each element that matches if you supply an 
198  * ambiguous selector.
199  *
200  * The event object for the delegated event is supplied to the callback
201  * function.  It is modified slightly in order to support all properties
202  * that may be needed for event delegation.  'currentTarget' is set to
203  * the element that matched the delegation specifcation.  'container' is
204  * set to the element that the listener is bound to (this normally would
205  * be the 'currentTarget').
206  *
207  * @method delegate
208  * @param type {string} the event type to delegate
209  * @param fn {function} the callback function to execute.  This function
210  * will be provided the event object for the delegated event.
211  * @param el {string|node} the element that is the delegation container
212  * @param spec {string} a selector that must match the target of the
213  * event.
214  * @param context optional argument that specifies what 'this' refers to.
215  * @param args* 0..n additional arguments to pass on to the callback function.
216  * These arguments will be added after the event object.
217  * @return {EventHandle} the detach handle
218  * @for YUI
219  */
220 Event.delegate = function (type, fn, el, spec) {
221
222     if (!spec) {
223         return false;
224     }
225
226
227     var args = Y.Array(arguments, 0, true),         
228                 element = el,   // HTML element serving as the delegation container
229                 availHandle;    
230
231
232         if (Lang.isString(el)) {
233                 
234                 //      Y.Selector.query returns an array of matches unless specified 
235                 //      to return just the first match.  Since the primary use case for
236                 //      event delegation is to use a single event handler on a container,
237                 //      Y.delegate doesn't currently support being able to bind a 
238                 //      single listener to multiple containers.
239                 
240                 element = Y.Selector.query(el, null, true);
241                 
242                 if (!element) { // Not found, check using onAvailable
243
244                         availHandle = Event.onAvailable(el, function() {
245
246                                 availHandle.handle = Event.delegate.apply(Event, args);
247
248             }, Event, true, false);
249
250             return availHandle;
251                         
252                 }
253                 
254         }
255
256
257         element = Y.Node.getDOMNode(element);
258
259
260         var     guid = Y.stamp(element),
261             
262         // The Custom Event for the delegation spec
263         ename = 'delegate:' + guid + type + sanitize(spec),
264
265         // The key to the listener for the event type and container
266         delegateKey = type + guid,
267
268                 delegate = delegates[delegateKey],
269
270                 domEventHandle,
271                 
272                 ceHandle,
273                 
274                 listeners;
275         
276
277     if (!delegate) {
278
279                 delegate = {};
280
281                 if (specialTypes[type]) {
282                         
283                         if (!Event._fireMouseEnter) {
284                                 return false;                           
285                         }
286                         
287                         type = specialTypes[type];
288                         delegate.fn = Event._fireMouseEnter;
289                         
290                 }
291
292                 //      Create the DOM Event wrapper that will fire the Custom Event
293
294                 domEventHandle = attach(type, delegateKey, element);
295
296
297                 //      Hook into the _delete method for the Custom Event wrapper of this
298                 //      DOM Event in order to clean up the 'delegates' map and unsubscribe
299                 //      the associated Custom Event listeners fired by this DOM event
300                 //      listener if/when the user calls "purgeElement" OR removes all 
301                 //      listeners of the Custom Event.
302                 
303                 Y.after(function (sub) {
304
305                         if (domEventHandle.sub == sub) {
306
307                                 //      Delete this event from the map of known delegates
308                                 delete delegates[delegateKey];
309
310
311                                 //      Unsubscribe all listeners of the Custom Event fired 
312                                 //      by this DOM event.
313                                 Y.detachAll(ename);
314                                 
315                         }
316
317                 }, domEventHandle.evt, "_delete");
318                         
319                 delegate.handle = domEventHandle;
320
321         delegates[delegateKey] = delegate;
322
323     }
324
325
326         listeners = delegate.listeners;
327
328         delegate.listeners = listeners ? (listeners + 1) : 1;
329     delegate[spec] = ename;
330
331
332     args[0] = ename;
333
334     // Remove element, delegation spec
335     args.splice(2, 2);
336         
337
338     // Subscribe to the Custom Event for the delegation spec
339
340         ceHandle = Y.on.apply(Y, args);
341
342
343         //      Hook into the detach method of the handle in order to clean up the 
344         //      'delegates' map and remove the associated DOM event handler 
345         //      responsible for firing this Custom Event if all listener for this 
346         //      event have been removed.
347
348         Y.after(function () {
349                         
350                 delegate.listeners = (delegate.listeners - 1);
351                 
352                 if (delegate.listeners === 0) {
353                         delegate.handle.detach();
354                 }
355
356         }, ceHandle, "detach");
357
358     return ceHandle;
359         
360 };
361
362 Y.delegate = Event.delegate;
363
364
365 }, '3.0.0' ,{requires:['node-base']});