]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/event/event.js
Release 6.2.0beta4
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / event / event.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 /*
9  * DOM event listener abstraction layer
10  * @module event
11  * @submodule event-base
12  */
13
14 (function() {
15
16
17 // Unlike most of the library, this code has to be executed as soon as it is
18 // introduced into the page -- and it should only be executed one time
19 // regardless of the number of instances that use it.
20
21 var GLOBAL_ENV = YUI.Env, 
22
23     C = YUI.config, 
24
25     D = C.doc, 
26
27     POLL_INTERVAL = C.pollInterval || 40,
28
29     _ready = function(e) {
30         GLOBAL_ENV._ready();
31     };
32
33     if (!GLOBAL_ENV._ready) {
34
35         GLOBAL_ENV._ready = function() {
36             if (!GLOBAL_ENV.DOMReady) {
37                 GLOBAL_ENV.DOMReady=true;
38
39                 // Remove the DOMContentLoaded (FF/Opera/Safari)
40                 if (D.removeEventListener) {
41                     D.removeEventListener("DOMContentLoaded", _ready, false);
42                 }
43             }
44         };
45
46         // create custom event
47
48 /*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
49
50         // Internet Explorer: use the readyState of a defered script.
51         // This isolates what appears to be a safe moment to manipulate
52         // the DOM prior to when the document's readyState suggests
53         // it is safe to do so.
54         if (navigator.userAgent.match(/MSIE/)) {
55
56             if (self !== self.top) {
57                 document.onreadystatechange = function() {
58                     if (document.readyState == 'complete') {
59                         document.onreadystatechange = null;
60                         _ready();
61                     }
62                 };
63             } else {
64
65                 GLOBAL_ENV._dri = setInterval(function() {
66                     try {
67                         // throws an error if doc is not ready
68                         document.documentElement.doScroll('left');
69                         clearInterval(GLOBAL_ENV._dri);
70                         GLOBAL_ENV._dri = null;
71                         _ready();
72                     } catch (ex) { 
73                     }
74                 }, POLL_INTERVAL); 
75             }
76
77         // FireFox, Opera, Safari 3+: These browsers provide a event for this
78         // moment.
79         } else {
80             D.addEventListener("DOMContentLoaded", _ready, false);
81         }
82
83         /////////////////////////////////////////////////////////////
84     }
85
86 })();
87 YUI.add('event-base', function(Y) {
88
89 (function() {
90 /*
91  * DOM event listener abstraction layer
92  * @module event
93  * @submodule event-base
94  */
95
96 var GLOBAL_ENV = YUI.Env,
97     
98     yready = function() {
99         Y.fire('domready');
100     };
101
102 Y.publish('domready', {
103     fireOnce: true
104 });
105
106 if (GLOBAL_ENV.DOMReady) {
107     // console.log('DOMReady already fired', 'info', 'event');
108     yready();
109 } else {
110     // console.log('setting up before listener', 'info', 'event');
111     // console.log('env: ' + YUI.Env.windowLoaded, 'info', 'event');
112     Y.before(yready, GLOBAL_ENV, "_ready");
113 }
114
115 })();
116 (function() {
117
118 /**
119  * Custom event engine, DOM event listener abstraction layer, synthetic DOM 
120  * events.
121  * @module event
122  * @submodule event-base
123  */
124
125 /**
126  * Wraps a DOM event, properties requiring browser abstraction are
127  * fixed here.  Provids a security layer when required.
128  * @class DOMEventFacade
129  * @param ev {Event} the DOM event
130  * @param currentTarget {HTMLElement} the element the listener was attached to
131  * @param wrapper {Event.Custom} the custom event wrapper for this DOM event
132  */
133
134 /*
135  * @TODO constants? LEFTBUTTON, MIDDLEBUTTON, RIGHTBUTTON, keys
136  */
137
138 /*
139
140 var whitelist = {
141     altKey          : 1,
142     // "button"          : 1, // we supply
143     // "bubbles"         : 1, // needed?
144     // "cancelable"      : 1, // needed? 
145     // "charCode"        : 1, // we supply
146     cancelBubble    : 1,
147     // "currentTarget"   : 1, // we supply
148     ctrlKey         : 1,
149     clientX         : 1, // needed?
150     clientY         : 1, // needed?
151     detail          : 1, // not fully implemented
152     // "fromElement"     : 1,
153     keyCode         : 1,
154     // "height"          : 1, // needed?
155     // "initEvent"       : 1, // need the init events?
156     // "initMouseEvent"  : 1,
157     // "initUIEvent"     : 1,
158     // "layerX"          : 1, // needed?
159     // "layerY"          : 1, // needed?
160     metaKey         : 1,
161     // "modifiers"       : 1, // needed?
162     // "offsetX"         : 1, // needed?
163     // "offsetY"         : 1, // needed?
164     // "preventDefault"  : 1, // we supply
165     // "reason"          : 1, // IE proprietary
166     // "relatedTarget"   : 1,
167     // "returnValue"     : 1, // needed?
168     shiftKey        : 1,
169     // "srcUrn"          : 1, // IE proprietary
170     // "srcElement"      : 1,
171     // "srcFilter"       : 1, IE proprietary
172     // "stopPropagation" : 1, // we supply
173     // "target"          : 1,
174     // "timeStamp"       : 1, // needed?
175     // "toElement"       : 1,
176     type            : 1,
177     // "view"            : 1,
178     // "which"           : 1, // we supply
179     // "width"           : 1, // needed?
180     x               : 1,
181     y               : 1
182 },
183
184 */
185
186     var ua = Y.UA,
187
188     /**
189      * webkit key remapping required for Safari < 3.1
190      * @property webkitKeymap
191      * @private
192      */
193     webkitKeymap = {
194         63232: 38, // up
195         63233: 40, // down
196         63234: 37, // left
197         63235: 39, // right
198         63276: 33, // page up
199         63277: 34, // page down
200         25:     9, // SHIFT-TAB (Safari provides a different key code in
201                    // this case, even though the shiftKey modifier is set)
202                 63272: 46, // delete
203                 63273: 36, // home
204                 63275: 35  // end
205     },
206
207     /**
208      * Returns a wrapped node.  Intended to be used on event targets,
209      * so it will return the node's parent if the target is a text
210      * node.
211      *
212      * If accessing a property of the node throws an error, this is
213      * probably the anonymous div wrapper Gecko adds inside text
214      * nodes.  This likely will only occur when attempting to access
215      * the relatedTarget.  In this case, we now return null because
216      * the anonymous div is completely useless and we do not know
217      * what the related target was because we can't even get to
218      * the element's parent node.
219      *
220      * @method resolve
221      * @private
222      */
223     resolve = function(n) {
224         try {
225             if (n && 3 == n.nodeType) {
226                 n = n.parentNode;
227             }
228         } catch(e) { 
229             return null;
230         }
231
232         return Y.one(n);
233     };
234
235
236 // provide a single event with browser abstractions resolved
237 //
238 // include all properties for both browers?
239 // include only DOM2 spec properties?
240 // provide browser-specific facade?
241
242 Y.DOMEventFacade = function(ev, currentTarget, wrapper) {
243
244     wrapper = wrapper || {};
245
246     var e = ev, ot = currentTarget, d = Y.config.doc, b = d.body,
247         x = e.pageX, y = e.pageY, c, t;
248
249     this.altKey   = e.altKey;
250     this.ctrlKey  = e.ctrlKey;
251     this.metaKey  = e.metaKey;
252     this.shiftKey = e.shiftKey;
253     this.type     = e.type;
254     this.clientX  = e.clientX;
255     this.clientY  = e.clientY;
256
257     //////////////////////////////////////////////////////
258
259     if (!x && 0 !== x) {
260         x = e.clientX || 0;
261         y = e.clientY || 0;
262
263         if (ua.ie) {
264             x += Math.max(d.documentElement.scrollLeft, b.scrollLeft);
265             y += Math.max(d.documentElement.scrollTop, b.scrollTop);
266         }
267     }
268
269     this._yuifacade = true;
270
271     /**
272      * The native event
273      * @property _event
274      */
275     this._event = e;
276
277     /**
278      * The X location of the event on the page (including scroll)
279      * @property pageX
280      * @type int
281      */
282     this.pageX = x;
283
284     /**
285      * The Y location of the event on the page (including scroll)
286      * @property pageY
287      * @type int
288      */
289     this.pageY = y;
290
291     //////////////////////////////////////////////////////
292
293     c = e.keyCode || e.charCode || 0;
294
295     if (ua.webkit && (c in webkitKeymap)) {
296         c = webkitKeymap[c];
297     }
298
299     /**
300      * The keyCode for key events.  Uses charCode if keyCode is not available
301      * @property keyCode
302      * @type int
303      */
304     this.keyCode = c;
305
306     /**
307      * The charCode for key events.  Same as keyCode
308      * @property charCode
309      * @type int
310      */
311     this.charCode = c;
312
313     //////////////////////////////////////////////////////
314
315     /**
316      * The button that was pushed.
317      * @property button
318      * @type int
319      */
320     this.button = e.which || e.button;
321
322     /**
323      * The button that was pushed.  Same as button.
324      * @property which
325      * @type int
326      */
327     this.which = this.button;
328
329     //////////////////////////////////////////////////////
330
331     /**
332      * Node reference for the targeted element
333      * @propery target
334      * @type Node
335      */
336     this.target = resolve(e.target || e.srcElement);
337
338     /**
339      * Node reference for the element that the listener was attached to.
340      * @propery currentTarget
341      * @type Node
342      */
343     this.currentTarget = resolve(ot);
344
345     t = e.relatedTarget;
346
347     if (!t) {
348         if (e.type == "mouseout") {
349             t = e.toElement;
350         } else if (e.type == "mouseover") {
351             t = e.fromElement;
352         }
353     }
354
355     /**
356      * Node reference to the relatedTarget
357      * @propery relatedTarget
358      * @type Node
359      */
360     this.relatedTarget = resolve(t);
361
362     /**
363      * Number representing the direction and velocity of the movement of the mousewheel.
364      * Negative is down, the higher the number, the faster.  Applies to the mousewheel event.
365      * @property wheelDelta
366      * @type int
367      */
368     if (e.type == "mousewheel" || e.type == "DOMMouseScroll") {
369         this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1);
370     }
371
372     //////////////////////////////////////////////////////
373     // methods
374
375     /**
376      * Stops the propagation to the next bubble target
377      * @method stopPropagation
378      */
379     this.stopPropagation = function() {
380         if (e.stopPropagation) {
381             e.stopPropagation();
382         } else {
383             e.cancelBubble = true;
384         }
385         wrapper.stopped = 1;
386     };
387
388     /**
389      * Stops the propagation to the next bubble target and
390      * prevents any additional listeners from being exectued
391      * on the current target.
392      * @method stopImmediatePropagation
393      */
394     this.stopImmediatePropagation = function() {
395         if (e.stopImmediatePropagation) {
396             e.stopImmediatePropagation();
397         } else {
398             this.stopPropagation();
399         }
400         wrapper.stopped = 2;
401     };
402
403     /**
404      * Prevents the event's default behavior
405      * @method preventDefault
406      * @param returnValue {string} sets the returnValue of the event to this value
407      * (rather than the default false value).  This can be used to add a customized 
408      * confirmation query to the beforeunload event).
409      */
410     this.preventDefault = function(returnValue) {
411         if (e.preventDefault) {
412             e.preventDefault();
413         }
414         e.returnValue = returnValue || false;
415         wrapper.prevented = 1;
416     };
417
418     /**
419      * Stops the event propagation and prevents the default
420      * event behavior.
421      * @method halt
422      * @param immediate {boolean} if true additional listeners
423      * on the current target will not be executed
424      */
425     this.halt = function(immediate) {
426         if (immediate) {
427             this.stopImmediatePropagation();
428         } else {
429             this.stopPropagation();
430         }
431
432         this.preventDefault();
433     };
434
435 };
436
437 })();
438 (function() {
439 /**
440  * DOM event listener abstraction layer
441  * @module event
442  * @submodule event-base
443  */
444
445 /**
446  * The event utility provides functions to add and remove event listeners,
447  * event cleansing.  It also tries to automatically remove listeners it
448  * registers during the unload event.
449  *
450  * @class Event
451  * @static
452  */
453
454 Y.Env.evt.dom_wrappers = {};
455 Y.Env.evt.dom_map = {};
456
457 var _eventenv = Y.Env.evt,
458 add = YUI.Env.add,
459 remove = YUI.Env.remove,
460
461 onLoad = function() {
462     YUI.Env.windowLoaded = true;
463     Y.Event._load();
464     remove(window, "load", onLoad);
465 },
466
467 onUnload = function() {
468     Y.Event._unload();
469     remove(window, "unload", onUnload);
470 },
471
472 EVENT_READY = 'domready',
473
474 COMPAT_ARG = '~yui|2|compat~',
475
476 shouldIterate = function(o) {
477     try {
478         return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) && !o.tagName && !o.alert);
479     } catch(ex) {
480         return false;
481     }
482
483 },
484
485 Event = function() {
486
487     /**
488      * True after the onload event has fired
489      * @property _loadComplete
490      * @type boolean
491      * @static
492      * @private
493      */
494     var _loadComplete =  false,
495
496     /**
497      * The number of times to poll after window.onload.  This number is
498      * increased if additional late-bound handlers are requested after
499      * the page load.
500      * @property _retryCount
501      * @static
502      * @private
503      */
504     _retryCount = 0,
505
506     /**
507      * onAvailable listeners
508      * @property _avail
509      * @static
510      * @private
511      */
512     _avail = [],
513
514     /**
515      * Custom event wrappers for DOM events.  Key is 
516      * 'event:' + Element uid stamp + event type
517      * @property _wrappers
518      * @type Y.Event.Custom
519      * @static
520      * @private
521      */
522     _wrappers = _eventenv.dom_wrappers,
523
524     _windowLoadKey = null,
525
526     /**
527      * Custom event wrapper map DOM events.  Key is 
528      * Element uid stamp.  Each item is a hash of custom event
529      * wrappers as provided in the _wrappers collection.  This
530      * provides the infrastructure for getListeners.
531      * @property _el_events
532      * @static
533      * @private
534      */
535     _el_events = _eventenv.dom_map;
536
537     return {
538
539         /**
540          * The number of times we should look for elements that are not
541          * in the DOM at the time the event is requested after the document
542          * has been loaded.  The default is 1000@amp;40 ms, so it will poll
543          * for 40 seconds or until all outstanding handlers are bound
544          * (whichever comes first).
545          * @property POLL_RETRYS
546          * @type int
547          * @static
548          * @final
549          */
550         POLL_RETRYS: 1000,
551
552         /**
553          * The poll interval in milliseconds
554          * @property POLL_INTERVAL
555          * @type int
556          * @static
557          * @final
558          */
559         POLL_INTERVAL: 40,
560
561         /**
562          * addListener/removeListener can throw errors in unexpected scenarios.
563          * These errors are suppressed, the method returns false, and this property
564          * is set
565          * @property lastError
566          * @static
567          * @type Error
568          */
569         lastError: null,
570
571
572         /**
573          * poll handle
574          * @property _interval
575          * @static
576          * @private
577          */
578         _interval: null,
579
580         /**
581          * document readystate poll handle
582          * @property _dri
583          * @static
584          * @private
585          */
586          _dri: null,
587
588         /**
589          * True when the document is initially usable
590          * @property DOMReady
591          * @type boolean
592          * @static
593          */
594         DOMReady: false,
595
596         /**
597          * @method startInterval
598          * @static
599          * @private
600          */
601         startInterval: function() {
602             var E = Y.Event;
603
604             if (!E._interval) {
605 E._interval = setInterval(Y.bind(E._poll, E), E.POLL_INTERVAL);
606             }
607         },
608
609         /**
610          * Executes the supplied callback when the item with the supplied
611          * id is found.  This is meant to be used to execute behavior as
612          * soon as possible as the page loads.  If you use this after the
613          * initial page load it will poll for a fixed time for the element.
614          * The number of times it will poll and the frequency are
615          * configurable.  By default it will poll for 10 seconds.
616          *
617          * <p>The callback is executed with a single parameter:
618          * the custom object parameter, if provided.</p>
619          *
620          * @method onAvailable
621          *
622          * @param {string||string[]}   id the id of the element, or an array
623          * of ids to look for.
624          * @param {function} fn what to execute when the element is found.
625          * @param {object}   p_obj an optional object to be passed back as
626          *                   a parameter to fn.
627          * @param {boolean|object}  p_override If set to true, fn will execute
628          *                   in the context of p_obj, if set to an object it
629          *                   will execute in the context of that object
630          * @param checkContent {boolean} check child node readiness (onContentReady)
631          * @static
632          * @deprecated Use Y.on("available")
633          */
634         // @TODO fix arguments
635         onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) {
636
637             var a = Y.Array(id), i, availHandle;
638
639
640             for (i=0; i<a.length; i=i+1) {
641                 _avail.push({ 
642                     id:         a[i], 
643                     fn:         fn, 
644                     obj:        p_obj, 
645                     override:   p_override, 
646                     checkReady: checkContent,
647                     compat:     compat 
648                 });
649             }
650             _retryCount = this.POLL_RETRYS;
651
652             // We want the first test to be immediate, but async
653             setTimeout(Y.bind(Y.Event._poll, Y.Event), 0);
654
655             availHandle = new Y.EventHandle({
656
657                 _delete: function() {
658                     // set by the event system for lazy DOM listeners
659                     if (availHandle.handle) {
660                         availHandle.handle.detach();
661                                                 return;
662                     }
663
664                     var i, j;
665
666                     // otherwise try to remove the onAvailable listener(s)
667                     for (i = 0; i < a.length; i++) {
668                         for (j = 0; j < _avail.length; j++) {
669                             if (a[i] === _avail[j].id) {
670                                 _avail.splice(j, 1);
671                             }
672                         }
673                     }
674                 }
675
676             });
677
678             return availHandle;
679         },
680
681         /**
682          * Works the same way as onAvailable, but additionally checks the
683          * state of sibling elements to determine if the content of the
684          * available element is safe to modify.
685          *
686          * <p>The callback is executed with a single parameter:
687          * the custom object parameter, if provided.</p>
688          *
689          * @method onContentReady
690          *
691          * @param {string}   id the id of the element to look for.
692          * @param {function} fn what to execute when the element is ready.
693          * @param {object}   p_obj an optional object to be passed back as
694          *                   a parameter to fn.
695          * @param {boolean|object}  p_override If set to true, fn will execute
696          *                   in the context of p_obj.  If an object, fn will
697          *                   exectute in the context of that object
698          *
699          * @static
700          * @deprecated Use Y.on("contentready")
701          */
702         // @TODO fix arguments
703         onContentReady: function(id, fn, p_obj, p_override, compat) {
704             return this.onAvailable(id, fn, p_obj, p_override, true, compat);
705         },
706
707         /**
708          * Adds an event listener
709          *
710          * @method attach
711          *
712          * @param {String}   type     The type of event to append
713          * @param {Function} fn        The method the event invokes
714          * @param {String|HTMLElement|Array|NodeList} el An id, an element 
715          *  reference, or a collection of ids and/or elements to assign the 
716          *  listener to.
717          * @param {Object}   context optional context object
718          * @param {Boolean|object}  args 0..n arguments to pass to the callback
719          * @return {EventHandle} an object to that can be used to detach the listener
720          *                     
721          * @static
722          */
723
724         attach: function(type, fn, el, context) {
725             return Y.Event._attach(Y.Array(arguments, 0, true));
726         },
727
728                 _createWrapper: function (el, type, capture, compat, facade) {
729
730             var ek = Y.stamp(el),
731                     key = 'event:' + ek + type,
732                     cewrapper;
733
734
735             if (false === facade) {
736                 key += 'native';
737             }
738             if (capture) {
739                 key += 'capture';
740             }
741
742
743             cewrapper = _wrappers[key];
744             
745
746             if (!cewrapper) {
747                 // create CE wrapper
748                 cewrapper = Y.publish(key, {
749                     silent: true,
750                     bubbles: false,
751                     contextFn: function() {
752                         cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el);
753                         return cewrapper.nodeRef;
754                     }
755                 });
756             
757                 // for later removeListener calls
758                 cewrapper.el = el;
759                 cewrapper.key = key;
760                 cewrapper.domkey = ek;
761                 cewrapper.type = type;
762                 cewrapper.fn = function(e) {
763                     cewrapper.fire(Y.Event.getEvent(e, el, (compat || (false === facade))));
764                 };
765                                 cewrapper.capture = capture;
766             
767                 if (el == Y.config.win && type == "load") {
768                     // window load happens once
769                     cewrapper.fireOnce = true;
770                     _windowLoadKey = key;
771                 }
772             
773                 _wrappers[key] = cewrapper;
774                 _el_events[ek] = _el_events[ek] || {};
775                 _el_events[ek][key] = cewrapper;
776             
777                 add(el, type, cewrapper.fn, capture);
778             }
779
780                         return cewrapper;
781                         
782                 },
783
784         _attach: function(args, config) {
785
786             var compat, E=Y.Event,
787                 handles, oEl, cewrapper, context, 
788                 fireNow = false, ret,
789                 type = args[0],
790                 fn = args[1],
791                 el = args[2] || Y.config.win,
792                 facade = config && config.facade,
793                 capture = config && config.capture;
794
795             if (args[args.length-1] === COMPAT_ARG) {
796                 compat = true;
797                 // trimmedArgs.pop();
798             }
799
800             if (!fn || !fn.call) {
801 // throw new TypeError(type + " attach call failed, callback undefined");
802                 return false;
803             }
804
805             // The el argument can be an array of elements or element ids.
806             if (shouldIterate(el)) {
807
808                 handles=[];
809                 
810                 Y.each(el, function(v, k) {
811                     args[2] = v;
812                     handles.push(E._attach(args, config));
813                 });
814
815                 // return (handles.length === 1) ? handles[0] : handles;
816                 return new Y.EventHandle(handles);
817
818             // If the el argument is a string, we assume it is 
819             // actually the id of the element.  If the page is loaded
820             // we convert el to the actual element, otherwise we 
821             // defer attaching the event until the element is
822             // ready
823             } else if (Y.Lang.isString(el)) {
824
825                 // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el);
826
827                 if (compat) {
828                     oEl = Y.DOM.byId(el);
829                 } else {
830
831                     oEl = Y.Selector.query(el);
832
833                     switch (oEl.length) {
834                         case 0:
835                             oEl = null;
836                             break;
837                         case 1:
838                             oEl = oEl[0];
839                             break;
840                         default:
841                             args[2] = oEl;
842                             return E._attach(args, config);
843                     }
844                 }
845
846                 if (oEl) {
847
848                     el = oEl;
849
850                 // Not found = defer adding the event until the element is available
851                 } else {
852
853                     ret = this.onAvailable(el, function() {
854                         
855                         ret.handle = E._attach(args, config);
856
857                     }, E, true, false, compat);
858
859                     return ret;
860
861                 }
862             }
863
864             // Element should be an html element or node
865             if (!el) {
866                 return false;
867             }
868
869             if (Y.Node && el instanceof Y.Node) {
870                 el = Y.Node.getDOMNode(el);
871             }
872
873                         cewrapper = this._createWrapper(el, type, capture, compat, facade);
874
875             if (el == Y.config.win && type == "load") {
876
877                 // if the load is complete, fire immediately.
878                 // all subscribers, including the current one
879                 // will be notified.
880                 if (YUI.Env.windowLoaded) {
881                     fireNow = true;
882                 }
883             }
884
885             if (compat) {
886                 args.pop();
887             }
888
889             context = args[3];
890
891             // set context to the Node if not specified
892             // ret = cewrapper.on.apply(cewrapper, trimmedArgs);
893             ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null);
894
895             if (fireNow) {
896                 cewrapper.fire();
897             }
898
899             return ret;
900
901         },
902
903         /**
904          * Removes an event listener.  Supports the signature the event was bound
905          * with, but the preferred way to remove listeners is using the handle
906          * that is returned when using Y.on
907          *
908          * @method detach
909          *
910          * @param {String} type the type of event to remove.
911          * @param {Function} fn the method the event invokes.  If fn is
912          * undefined, then all event handlers for the type of event are 
913          * removed.
914          * @param {String|HTMLElement|Array|NodeList|EventHandle} el An 
915          * event handle, an id, an element reference, or a collection 
916          * of ids and/or elements to remove the listener from.
917          * @return {boolean} true if the unbind was successful, false otherwise.
918          * @static
919          */
920         detach: function(type, fn, el, obj) {
921
922             var args=Y.Array(arguments, 0, true), compat, i, l, ok,
923                 id, ce;
924
925             if (args[args.length-1] === COMPAT_ARG) {
926                 compat = true;
927                 // args.pop();
928             }
929
930             if (type && type.detach) {
931                 return type.detach();
932             }
933
934             // The el argument can be a string
935             if (typeof el == "string") {
936
937                 // el = (compat) ? Y.DOM.byId(el) : Y.all(el);
938                 if (compat) {
939                     el = Y.DOM.byId(el);
940                 } else {
941                     el = Y.Selector.query(el);
942                     l = el.length;
943                     if (l < 1) {
944                         el = null;
945                     } else if (l == 1) {
946                         el = el[0];
947                     }
948                 }
949                 // return Y.Event.detach.apply(Y.Event, args);
950
951             // The el argument can be an array of elements or element ids.
952             } 
953             
954             if (!el) {
955                 return false;
956             }
957             
958             if (shouldIterate(el)) {
959
960                 ok = true;
961                 for (i=0, l=el.length; i<l; ++i) {
962                     args[2] = el[i];
963                     ok = ( Y.Event.detach.apply(Y.Event, args) && ok );
964                 }
965
966                 return ok;
967
968             }
969
970             if (!type || !fn || !fn.call) {
971                 return this.purgeElement(el, false, type);
972             }
973
974             id = 'event:' + Y.stamp(el) + type;
975             ce = _wrappers[id];
976
977             if (ce) {
978                 return ce.detach(fn);
979             } else {
980                 return false;
981             }
982
983         },
984
985         /**
986          * Finds the event in the window object, the caller's arguments, or
987          * in the arguments of another method in the callstack.  This is
988          * executed automatically for events registered through the event
989          * manager, so the implementer should not normally need to execute
990          * this function at all.
991          * @method getEvent
992          * @param {Event} e the event parameter from the handler
993          * @param {HTMLElement} el the element the listener was attached to
994          * @return {Event} the event 
995          * @static
996          */
997         getEvent: function(e, el, noFacade) {
998             var ev = e || window.event;
999
1000             return (noFacade) ? ev : 
1001                 new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]);
1002         },
1003
1004         /**
1005          * Generates an unique ID for the element if it does not already 
1006          * have one.
1007          * @method generateId
1008          * @param el the element to create the id for
1009          * @return {string} the resulting id of the element
1010          * @static
1011          */
1012         generateId: function(el) {
1013             var id = el.id;
1014
1015             if (!id) {
1016                 id = Y.stamp(el);
1017                 el.id = id;
1018             }
1019
1020             return id;
1021         },
1022
1023         /**
1024          * We want to be able to use getElementsByTagName as a collection
1025          * to attach a group of events to.  Unfortunately, different 
1026          * browsers return different types of collections.  This function
1027          * tests to determine if the object is array-like.  It will also 
1028          * fail if the object is an array, but is empty.
1029          * @method _isValidCollection
1030          * @param o the object to test
1031          * @return {boolean} true if the object is array-like and populated
1032          * @deprecated was not meant to be used directly
1033          * @static
1034          * @private
1035          */
1036         _isValidCollection: shouldIterate,
1037
1038         /**
1039          * hook up any deferred listeners
1040          * @method _load
1041          * @static
1042          * @private
1043          */
1044         _load: function(e) {
1045
1046             if (!_loadComplete) {
1047
1048
1049                 _loadComplete = true;
1050
1051                 // Just in case DOMReady did not go off for some reason
1052                 // E._ready();
1053                 if (Y.fire) {
1054                     Y.fire(EVENT_READY);
1055                 }
1056
1057                 // Available elements may not have been detected before the
1058                 // window load event fires. Try to find them now so that the
1059                 // the user is more likely to get the onAvailable notifications
1060                 // before the window load notification
1061                 Y.Event._poll();
1062
1063             }
1064         },
1065
1066         /**
1067          * Polling function that runs before the onload event fires, 
1068          * attempting to attach to DOM Nodes as soon as they are 
1069          * available
1070          * @method _poll
1071          * @static
1072          * @private
1073          */
1074         _poll: function() {
1075
1076             if (this.locked) {
1077                 return;
1078             }
1079
1080             if (Y.UA.ie && !YUI.Env.DOMReady) {
1081                 // Hold off if DOMReady has not fired and check current
1082                 // readyState to protect against the IE operation aborted
1083                 // issue.
1084                 this.startInterval();
1085                 return;
1086             }
1087
1088             this.locked = true;
1089
1090
1091             // keep trying until after the page is loaded.  We need to 
1092             // check the page load state prior to trying to bind the 
1093             // elements so that we can be certain all elements have been 
1094             // tested appropriately
1095             var tryAgain = !_loadComplete, notAvail, executeItem,
1096                 i, len, item, el;
1097
1098             if (!tryAgain) {
1099                 tryAgain = (_retryCount > 0);
1100             }
1101
1102             // onAvailable
1103             notAvail = [];
1104
1105             executeItem = function (el, item) {
1106
1107                 var context, ov = item.override;
1108
1109                 if (item.compat) {
1110
1111                     if (item.override) {
1112                         if (ov === true) {
1113                             context = item.obj;
1114                         } else {
1115                             context = ov;
1116                         }
1117                     } else {
1118                         context = el;
1119                     }
1120
1121                     item.fn.call(context, item.obj);
1122
1123                 } else {
1124                     context = item.obj || Y.one(el);
1125                     item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []);
1126                 }
1127
1128             };
1129
1130
1131             // onAvailable
1132             for (i=0,len=_avail.length; i<len; ++i) {
1133                 item = _avail[i];
1134                 if (item && !item.checkReady) {
1135
1136                     // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
1137                     el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
1138
1139                     if (el) {
1140                         executeItem(el, item);
1141                         _avail[i] = null;
1142                     } else {
1143                         notAvail.push(item);
1144                     }
1145                 }
1146             }
1147
1148             // onContentReady
1149             for (i=0,len=_avail.length; i<len; ++i) {
1150                 item = _avail[i];
1151                 if (item && item.checkReady) {
1152
1153                     // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
1154                     el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
1155
1156                     if (el) {
1157                         // The element is available, but not necessarily ready
1158                         // @todo should we test parentNode.nextSibling?
1159                         if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) {
1160                             executeItem(el, item);
1161                             _avail[i] = null;
1162                         }
1163                     } else {
1164                         notAvail.push(item);
1165                     }
1166                 }
1167             }
1168
1169             _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1;
1170
1171             if (tryAgain) {
1172                 // we may need to strip the nulled out items here
1173                 this.startInterval();
1174             } else {
1175                 clearInterval(this._interval);
1176                 this._interval = null;
1177             }
1178
1179             this.locked = false;
1180
1181             return;
1182
1183         },
1184
1185         /**
1186          * Removes all listeners attached to the given element via addListener.
1187          * Optionally, the node's children can also be purged.
1188          * Optionally, you can specify a specific type of event to remove.
1189          * @method purgeElement
1190          * @param {HTMLElement} el the element to purge
1191          * @param {boolean} recurse recursively purge this element's children
1192          * as well.  Use with caution.
1193          * @param {string} type optional type of listener to purge. If
1194          * left out, all listeners will be removed
1195          * @static
1196          */
1197         purgeElement: function(el, recurse, type) {
1198             // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el,
1199             var oEl = (Y.Lang.isString(el)) ?  Y.Selector.query(el, null, true) : el,
1200                 lis = this.getListeners(oEl, type), i, len, props;
1201             if (lis) {
1202                 for (i=0,len=lis.length; i<len ; ++i) {
1203                     props = lis[i];
1204                     props.detachAll();
1205                     remove(props.el, props.type, props.fn, props.capture);
1206                     delete _wrappers[props.key];
1207                     delete _el_events[props.domkey][props.key];
1208                 }
1209
1210             }
1211
1212             if (recurse && oEl && oEl.childNodes) {
1213                 for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
1214                     this.purgeElement(oEl.childNodes[i], recurse, type);
1215                 }
1216             }
1217
1218         },
1219
1220         /**
1221          * Returns all listeners attached to the given element via addListener.
1222          * Optionally, you can specify a specific type of event to return.
1223          * @method getListeners
1224          * @param el {HTMLElement|string} the element or element id to inspect 
1225          * @param type {string} optional type of listener to return. If
1226          * left out, all listeners will be returned
1227          * @return {Y.Custom.Event} the custom event wrapper for the DOM event(s)
1228          * @static
1229          */           
1230         getListeners: function(el, type) {
1231             var ek = Y.stamp(el, true), evts = _el_events[ek],
1232                 results=[] , key = (type) ? 'event:' + ek + type : null;
1233
1234             if (!evts) {
1235                 return null;
1236             }
1237
1238             if (key) {
1239                 if (evts[key]) {
1240                     results.push(evts[key]);
1241                 }
1242
1243                 // get native events as well
1244                 key += 'native';
1245                 if (evts[key]) {
1246                     results.push(evts[key]);
1247                 }
1248
1249             } else {
1250                 Y.each(evts, function(v, k) {
1251                     results.push(v);
1252                 });
1253             }
1254
1255             return (results.length) ? results : null;
1256         },
1257
1258         /**
1259          * Removes all listeners registered by pe.event.  Called 
1260          * automatically during the unload event.
1261          * @method _unload
1262          * @static
1263          * @private
1264          */
1265         _unload: function(e) {
1266             Y.each(_wrappers, function(v, k) {
1267                 v.detachAll();
1268                 remove(v.el, v.type, v.fn, v.capture);
1269                 delete _wrappers[k];
1270                 delete _el_events[v.domkey][k];
1271             });
1272         },
1273
1274         
1275         /**
1276          * Adds a DOM event directly without the caching, cleanup, context adj, etc
1277          *
1278          * @method nativeAdd
1279          * @param {HTMLElement} el      the element to bind the handler to
1280          * @param {string}      type   the type of event handler
1281          * @param {function}    fn      the callback to invoke
1282          * @param {boolen}      capture capture or bubble phase
1283          * @static
1284          * @private
1285          */
1286         nativeAdd: add,
1287
1288         /**
1289          * Basic remove listener
1290          *
1291          * @method nativeRemove
1292          * @param {HTMLElement} el      the element to bind the handler to
1293          * @param {string}      type   the type of event handler
1294          * @param {function}    fn      the callback to invoke
1295          * @param {boolen}      capture capture or bubble phase
1296          * @static
1297          * @private
1298          */
1299         nativeRemove: remove
1300     };
1301
1302 }();
1303
1304 Y.Event = Event;
1305
1306
1307 if (Y.config.injected || YUI.Env.windowLoaded) {
1308     onLoad();
1309 } else {
1310     add(window, "load", onLoad);
1311 }
1312
1313 // Process onAvailable/onContentReady items when when the DOM is ready in IE
1314 if (Y.UA.ie) {
1315     Y.on(EVENT_READY, Event._poll, Event, true);
1316 }
1317
1318 Y.on("unload", onUnload);
1319
1320 Event.Custom = Y.CustomEvent;
1321 Event.Subscriber = Y.Subscriber;
1322 Event.Target = Y.EventTarget;
1323 Event.Handle = Y.EventHandle;
1324 Event.Facade = Y.EventFacade;
1325
1326 Event._poll();
1327
1328 })();
1329
1330 /**
1331  * DOM event listener abstraction layer
1332  * @module event
1333  * @submodule event-base
1334  */
1335
1336 /**
1337  * Executes the callback as soon as the specified element 
1338  * is detected in the DOM.
1339  * @event available
1340  * @param type {string} 'available'
1341  * @param fn {function} the callback function to execute.
1342  * @param el {string|HTMLElement|collection} the element(s) to attach
1343  * @param context optional argument that specifies what 'this' refers to.
1344  * @param args* 0..n additional arguments to pass on to the callback function.
1345  * These arguments will be added after the event object.
1346  * @return {EventHandle} the detach handle
1347  * @for YUI
1348  */
1349 Y.Env.evt.plugins.available = {
1350     on: function(type, fn, id, o) {
1351         var a = arguments.length > 4 ?  Y.Array(arguments, 4, true) : [];
1352         return Y.Event.onAvailable.call(Y.Event, id, fn, o, a);
1353     }
1354 };
1355
1356 /**
1357  * Executes the callback as soon as the specified element 
1358  * is detected in the DOM with a nextSibling property
1359  * (indicating that the element's children are available)
1360  * @event contentready
1361  * @param type {string} 'contentready'
1362  * @param fn {function} the callback function to execute.
1363  * @param el {string|HTMLElement|collection} the element(s) to attach
1364  * @param context optional argument that specifies what 'this' refers to.
1365  * @param args* 0..n additional arguments to pass on to the callback function.
1366  * These arguments will be added after the event object.
1367  * @return {EventHandle} the detach handle
1368  * @for YUI
1369  */
1370 Y.Env.evt.plugins.contentready = {
1371     on: function(type, fn, id, o) {
1372         var a = arguments.length > 4 ?  Y.Array(arguments, 4, true) : [];
1373         return Y.Event.onContentReady.call(Y.Event, id, fn, o, a);
1374     }
1375 };
1376
1377
1378 }, '3.0.0' ,{requires:['event-custom-base']});
1379 YUI.add('event-delegate', function(Y) {
1380
1381 /**
1382  * Adds event delegation support to the library.
1383  * 
1384  * @module event
1385  * @submodule event-delegate
1386  */
1387
1388 var Event = Y.Event,
1389         Lang = Y.Lang,
1390
1391         delegates = {},
1392         
1393         specialTypes = {
1394                 mouseenter: "mouseover",
1395                 mouseleave: "mouseout"
1396         },
1397
1398         resolveTextNode = function(n) {
1399             try {
1400                 if (n && 3 == n.nodeType) {
1401                     return n.parentNode;
1402                 }
1403             } catch(e) { }
1404             return n;
1405         },
1406
1407     delegateHandler = function(delegateKey, e, el) {
1408
1409         var target = resolveTextNode((e.target || e.srcElement)), 
1410             tests  = delegates[delegateKey],
1411             spec, 
1412                         ename,
1413                         matched,
1414                         fn,
1415                         ev;
1416
1417
1418                 var getMatch = function(el, selector, container) {
1419                         
1420                         var returnVal;
1421                         
1422                         if (!el || el === container) {
1423                                 returnVal = false;
1424                         }
1425                         else {
1426                                 returnVal = Y.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container);
1427                         }
1428                         
1429                         return returnVal;
1430                         
1431                 };
1432
1433
1434         for (spec in tests) {
1435
1436             if (tests.hasOwnProperty(spec)) {
1437
1438                 ename  = tests[spec];
1439                                 fn      = tests.fn;
1440                                 matched = null;
1441
1442
1443                                 if (Y.Selector.test(target, spec, el)) {
1444                                         matched = target;
1445                                 }
1446                                 else if (Y.Selector.test(target, ((spec.replace(/,/gi, " *,")) + " *"), el)) {
1447                                                 
1448                                         //      The target is a descendant of an element matching 
1449                                         //      the selector, so crawl up to find the ancestor that 
1450                                         //      matches the selector
1451                                         
1452                                         matched = getMatch(target, spec, el);
1453                                         
1454                                 }
1455
1456
1457                                 if (matched) {
1458
1459                     if (!ev) {
1460                         ev = new Y.DOMEventFacade(e, el);
1461                         ev.container = ev.currentTarget;
1462                     }
1463
1464                     ev.currentTarget = Y.Node.get(matched);
1465
1466                                         Y.publish(ename, {
1467                                        contextFn: function() {
1468                                            return ev.currentTarget;
1469                                        }
1470                                    });
1471
1472                                         if (fn) {
1473                                                 fn(ev, ename);
1474                                         }
1475                                         else {
1476                         Y.fire(ename, ev);                                                              
1477                                         }
1478                                         
1479                                 }
1480
1481             }
1482         }
1483
1484     },
1485
1486         attach = function (type, key, element) {
1487
1488                 var focusMethods = {
1489                                 focus: Event._attachFocus,
1490                                 blur: Event._attachBlur
1491                         },
1492
1493                         attachFn = focusMethods[type],
1494
1495                         args = [type, 
1496                         function (e) {
1497                     delegateHandler(key, (e || window.event), element);
1498                         }, 
1499                         element];
1500
1501
1502                 if (attachFn) {
1503                         return attachFn(args, { capture: true, facade: false });
1504                 }
1505                 else {
1506                         return Event._attach(args, { facade: false });
1507                 }
1508                 
1509         },
1510
1511     sanitize = Y.cached(function(str) {
1512         return str.replace(/[|,:]/g, '~');
1513     });
1514
1515 /**
1516  * Sets up event delegation on a container element.  The delegated event
1517  * will use a supplied selector to test if the target or one of the
1518  * descendants of the target match it.  The supplied callback function 
1519  * will only be executed if a match was encountered, and, in fact, 
1520  * will be executed for each element that matches if you supply an 
1521  * ambiguous selector.
1522  *
1523  * The event object for the delegated event is supplied to the callback
1524  * function.  It is modified slightly in order to support all properties
1525  * that may be needed for event delegation.  'currentTarget' is set to
1526  * the element that matched the delegation specifcation.  'container' is
1527  * set to the element that the listener is bound to (this normally would
1528  * be the 'currentTarget').
1529  *
1530  * @event delegate
1531  * @param type {string} 'delegate'
1532  * @param fn {function} the callback function to execute.  This function
1533  * will be provided the event object for the delegated event.
1534  * @param el {string|node} the element that is the delegation container
1535  * @param delegateType {string} the event type to delegate
1536  * @param spec {string} a selector that must match the target of the
1537  * event.
1538  * @param context optional argument that specifies what 'this' refers to.
1539  * @param args* 0..n additional arguments to pass on to the callback function.
1540  * These arguments will be added after the event object.
1541  * @return {EventHandle} the detach handle
1542  * @for YUI
1543  * @deprecated use Y.delegate
1544  */
1545 Y.Env.evt.plugins.delegate = {
1546
1547     on: function(type, fn, el, delegateType, spec) {
1548
1549
1550                 var args = Y.Array(arguments, 0, true);
1551                 
1552                 args.splice(3, 1);
1553                 
1554                 args[0] = delegateType;
1555
1556                 return Y.delegate.apply(Y, args);
1557
1558     }
1559
1560 };
1561
1562
1563 /**
1564  * Sets up event delegation on a container element.  The delegated event
1565  * will use a supplied selector to test if the target or one of the
1566  * descendants of the target match it.  The supplied callback function 
1567  * will only be executed if a match was encountered, and, in fact, 
1568  * will be executed for each element that matches if you supply an 
1569  * ambiguous selector.
1570  *
1571  * The event object for the delegated event is supplied to the callback
1572  * function.  It is modified slightly in order to support all properties
1573  * that may be needed for event delegation.  'currentTarget' is set to
1574  * the element that matched the delegation specifcation.  'container' is
1575  * set to the element that the listener is bound to (this normally would
1576  * be the 'currentTarget').
1577  *
1578  * @method delegate
1579  * @param type {string} the event type to delegate
1580  * @param fn {function} the callback function to execute.  This function
1581  * will be provided the event object for the delegated event.
1582  * @param el {string|node} the element that is the delegation container
1583  * @param spec {string} a selector that must match the target of the
1584  * event.
1585  * @param context optional argument that specifies what 'this' refers to.
1586  * @param args* 0..n additional arguments to pass on to the callback function.
1587  * These arguments will be added after the event object.
1588  * @return {EventHandle} the detach handle
1589  * @for YUI
1590  */
1591 Event.delegate = function (type, fn, el, spec) {
1592
1593     if (!spec) {
1594         return false;
1595     }
1596
1597
1598     var args = Y.Array(arguments, 0, true),         
1599                 element = el,   // HTML element serving as the delegation container
1600                 availHandle;    
1601
1602
1603         if (Lang.isString(el)) {
1604                 
1605                 //      Y.Selector.query returns an array of matches unless specified 
1606                 //      to return just the first match.  Since the primary use case for
1607                 //      event delegation is to use a single event handler on a container,
1608                 //      Y.delegate doesn't currently support being able to bind a 
1609                 //      single listener to multiple containers.
1610                 
1611                 element = Y.Selector.query(el, null, true);
1612                 
1613                 if (!element) { // Not found, check using onAvailable
1614
1615                         availHandle = Event.onAvailable(el, function() {
1616
1617                                 availHandle.handle = Event.delegate.apply(Event, args);
1618
1619             }, Event, true, false);
1620
1621             return availHandle;
1622                         
1623                 }
1624                 
1625         }
1626
1627
1628         element = Y.Node.getDOMNode(element);
1629
1630
1631         var     guid = Y.stamp(element),
1632             
1633         // The Custom Event for the delegation spec
1634         ename = 'delegate:' + guid + type + sanitize(spec),
1635
1636         // The key to the listener for the event type and container
1637         delegateKey = type + guid,
1638
1639                 delegate = delegates[delegateKey],
1640
1641                 domEventHandle,
1642                 
1643                 ceHandle,
1644                 
1645                 listeners;
1646         
1647
1648     if (!delegate) {
1649
1650                 delegate = {};
1651
1652                 if (specialTypes[type]) {
1653                         
1654                         if (!Event._fireMouseEnter) {
1655                                 return false;                           
1656                         }
1657                         
1658                         type = specialTypes[type];
1659                         delegate.fn = Event._fireMouseEnter;
1660                         
1661                 }
1662
1663                 //      Create the DOM Event wrapper that will fire the Custom Event
1664
1665                 domEventHandle = attach(type, delegateKey, element);
1666
1667
1668                 //      Hook into the _delete method for the Custom Event wrapper of this
1669                 //      DOM Event in order to clean up the 'delegates' map and unsubscribe
1670                 //      the associated Custom Event listeners fired by this DOM event
1671                 //      listener if/when the user calls "purgeElement" OR removes all 
1672                 //      listeners of the Custom Event.
1673                 
1674                 Y.after(function (sub) {
1675
1676                         if (domEventHandle.sub == sub) {
1677
1678                                 //      Delete this event from the map of known delegates
1679                                 delete delegates[delegateKey];
1680
1681
1682                                 //      Unsubscribe all listeners of the Custom Event fired 
1683                                 //      by this DOM event.
1684                                 Y.detachAll(ename);
1685                                 
1686                         }
1687
1688                 }, domEventHandle.evt, "_delete");
1689                         
1690                 delegate.handle = domEventHandle;
1691
1692         delegates[delegateKey] = delegate;
1693
1694     }
1695
1696
1697         listeners = delegate.listeners;
1698
1699         delegate.listeners = listeners ? (listeners + 1) : 1;
1700     delegate[spec] = ename;
1701
1702
1703     args[0] = ename;
1704
1705     // Remove element, delegation spec
1706     args.splice(2, 2);
1707         
1708
1709     // Subscribe to the Custom Event for the delegation spec
1710
1711         ceHandle = Y.on.apply(Y, args);
1712
1713
1714         //      Hook into the detach method of the handle in order to clean up the 
1715         //      'delegates' map and remove the associated DOM event handler 
1716         //      responsible for firing this Custom Event if all listener for this 
1717         //      event have been removed.
1718
1719         Y.after(function () {
1720                         
1721                 delegate.listeners = (delegate.listeners - 1);
1722                 
1723                 if (delegate.listeners === 0) {
1724                         delegate.handle.detach();
1725                 }
1726
1727         }, ceHandle, "detach");
1728
1729     return ceHandle;
1730         
1731 };
1732
1733 Y.delegate = Event.delegate;
1734
1735
1736 }, '3.0.0' ,{requires:['node-base']});
1737 YUI.add('event-mousewheel', function(Y) {
1738
1739 /**
1740  * Adds mousewheel event support
1741  * @module event
1742  * @submodule event-mousewheel
1743  */
1744 var DOM_MOUSE_SCROLL = 'DOMMouseScroll',
1745     fixArgs = function(args) {
1746         var a = Y.Array(args, 0, true), target;
1747         if (Y.UA.gecko) {
1748             a[0] = DOM_MOUSE_SCROLL;
1749             target = Y.config.win;
1750         } else {
1751             target = Y.config.doc;
1752         }
1753
1754         if (a.length < 3) {
1755             a[2] = target;
1756         } else {
1757             a.splice(2, 0, target);
1758         }
1759
1760         return a;
1761     };
1762
1763 /**
1764  * Mousewheel event.  This listener is automatically attached to the
1765  * correct target, so one should not be supplied.  Mouse wheel 
1766  * direction and velocity is stored in the 'mouseDelta' field.
1767  * @event mousewheel
1768  * @param type {string} 'mousewheel'
1769  * @param fn {function} the callback to execute
1770  * @param context optional context object
1771  * @param args 0..n additional arguments to provide to the listener.
1772  * @return {EventHandle} the detach handle
1773  * @for YUI
1774  */
1775 Y.Env.evt.plugins.mousewheel = {
1776     on: function() {
1777         return Y.Event._attach(fixArgs(arguments));
1778     },
1779
1780     detach: function() {
1781         return Y.Event.detach.apply(Y.Event, fixArgs(arguments));
1782     }
1783 };
1784
1785
1786 }, '3.0.0' ,{requires:['node-base']});
1787 YUI.add('event-mouseenter', function(Y) {
1788
1789 /**
1790  * Adds support for mouseenter/mouseleave events
1791  * @module event
1792  * @submodule event-mouseenter
1793  */
1794 var Event = Y.Event,
1795         Lang = Y.Lang,
1796
1797         plugins = Y.Env.evt.plugins,
1798         
1799         listeners = {},
1800
1801         eventConfig = {
1802
1803         on: function(type, fn, el) {
1804
1805                     var args = Y.Array(arguments, 0, true),         
1806                                 element = el,
1807                                 availHandle;
1808
1809
1810                         if (Lang.isString(el)) {
1811
1812                                 //      Need to use Y.all because if el is a string it could be a 
1813                                 //      selector that returns a NodeList
1814
1815                                 element = Y.all(el);
1816
1817                                 if (element.size() === 0) { // Not found, check using onAvailable
1818
1819                             availHandle = Event.onAvailable(el, function() {
1820
1821                                 availHandle.handle = Y.on.apply(Y, args);
1822
1823                             }, Event, true, false);
1824                 
1825                                         return availHandle;
1826
1827                                 }
1828
1829                         }
1830                         
1831
1832                 var sDOMEvent = (type === "mouseenter") ? "mouseover" : "mouseout",
1833
1834                                 //      The name of the custom event
1835                                 sEventName = type + ":" + Y.stamp(element) + sDOMEvent,
1836
1837                                 listener = listeners[sEventName],
1838
1839                                 domEventHandle,
1840                                 
1841                                 ceHandle,
1842                                 
1843                                 nListeners;
1844
1845
1846                         //      Bind an actual DOM event listener that will call the 
1847                         //      the custom event                                
1848                         if (!listener) {
1849                                 
1850                                 domEventHandle = Y.on(sDOMEvent, Y.rbind(Event._fireMouseEnter, Y, sEventName), element);
1851
1852                                 //      Hook into the _delete method for the Custom Event wrapper of this
1853                                 //      DOM Event in order to clean up the 'listeners' map and unsubscribe
1854                                 //      the associated Custom Event listeners fired by this DOM event
1855                                 //      listener if/when the user calls "purgeElement" OR removes all 
1856                                 //      listeners of the Custom Event.
1857
1858                                 Y.after(function (sub) {
1859
1860                                         if (domEventHandle.sub == sub) {
1861
1862                                                 //      Delete this event from the map of known mouseenter 
1863                                                 //      and mouseleave listeners
1864                                                 delete listeners[sEventName];
1865
1866
1867                                                 //      Unsubscribe all listeners of the Custom Event fired 
1868                                                 //      by this DOM event.
1869                                                 Y.detachAll(sEventName);
1870
1871                                         }
1872
1873                                 }, domEventHandle.evt, "_delete");
1874                                 
1875
1876                                 listener = {};                          
1877                                 listener.handle = domEventHandle;                               
1878
1879                                 listeners[sEventName] = listener;
1880
1881                         }
1882
1883                         nListeners = listener.count;
1884
1885                         listener.count = nListeners ? (nListeners + 1) : 1;
1886
1887                 args[0] = sEventName;
1888
1889                 // Remove the element from the args
1890                         args.splice(2, 1);
1891
1892                 // Subscribe to the custom event
1893                 ceHandle = Y.on.apply(Y, args);
1894         
1895                         //      Hook into the detach method of the handle in order to clean up the 
1896                         //      'listeners' map and remove the associated DOM event handler 
1897                         //      responsible for firing this Custom Event if all listener for this 
1898                         //      event have been removed.
1899
1900                         Y.after(function () {
1901
1902                                 listener.count = (listener.count - 1);
1903
1904                                 if (listener.count === 0) {
1905                                         listener.handle.detach();
1906                                 }
1907
1908                         }, ceHandle, "detach"); 
1909         
1910         
1911                         return ceHandle;
1912
1913             }
1914
1915         };
1916         
1917
1918 Event._fireMouseEnter = function (e, eventName) {
1919
1920         var relatedTarget = e.relatedTarget,
1921                 currentTarget = e.currentTarget;
1922
1923         if (currentTarget !== relatedTarget && 
1924                 !currentTarget.contains(relatedTarget)) {
1925
1926                 Y.publish(eventName, {
1927                contextFn: function() {
1928                    return currentTarget;
1929                }
1930            });                  
1931
1932                 Y.fire(eventName, e);
1933
1934         }
1935
1936 };      
1937
1938
1939 /**
1940  * Sets up a "mouseenter" listener&#151;a listener that is called the first time 
1941  * the user's mouse enters the specified element(s).
1942  * 
1943  * @event mouseenter
1944  * @param type {string} "mouseenter"
1945  * @param fn {function} The method the event invokes.
1946  * @param el {string|node} The element(s) to assign the listener to.
1947  * @param spec {string} Optional.  String representing a selector that must 
1948  * match the target of the event in order for the listener to be called.
1949  * @return {EventHandle} the detach handle
1950  * @for YUI
1951  */
1952 plugins.mouseenter = eventConfig;
1953
1954 /**
1955 * Sets up a "mouseleave" listener&#151;a listener that is called the first time 
1956 * the user's mouse leaves the specified element(s).
1957
1958 * @event mouseleave
1959 * @param type {string} "mouseleave"
1960 * @param fn {function} The method the event invokes.
1961 * @param el {string|node} The element(s) to assign the listener to.
1962 * @param spec {string} Optional.  String representing a selector that must 
1963 * match the target of the event in order for the listener to be called.
1964 * @return {EventHandle} the detach handle
1965 * @for YUI
1966  */
1967 plugins.mouseleave = eventConfig;
1968
1969
1970 }, '3.0.0' ,{requires:['node-base']});
1971 YUI.add('event-key', function(Y) {
1972
1973 /**
1974  * Functionality to listen for one or more specific key combinations.
1975  * @module event
1976  * @submodule event-key
1977  */
1978
1979 /**
1980  * Add a key listener.  The listener will only be notified if the
1981  * keystroke detected meets the supplied specification.  The
1982  * spec consists of the key event type, followed by a colon,
1983  * followed by zero or more comma separated key codes, followed
1984  * by zero or more modifiers delimited by a plus sign.  Ex:
1985  * press:12,65+shift+ctrl
1986  * @event key
1987  * @for YUI
1988  * @param type {string} 'key'
1989  * @param fn {function} the function to execute
1990  * @param id {string|HTMLElement|collection} the element(s) to bind
1991  * @param spec {string} the keyCode and modifier specification
1992  * @param o optional context object
1993  * @param args 0..n additional arguments to provide to the listener.
1994  * @return {Event.Handle} the detach handle
1995  */
1996 Y.Env.evt.plugins.key = {
1997
1998     on: function(type, fn, id, spec, o) {
1999         var a = Y.Array(arguments, 0, true), parsed, etype, criteria, ename;
2000
2001         parsed = spec && spec.split(':');
2002
2003         if (!spec || spec.indexOf(':') == -1 || !parsed[1]) {
2004             a[0] = 'key' + ((parsed && parsed[0]) || 'press');
2005             return Y.on.apply(Y, a);
2006         }
2007
2008         // key event type: 'down', 'up', or 'press'
2009         etype = parsed[0];
2010
2011         // list of key codes optionally followed by modifiers
2012         criteria = (parsed[1]) ? parsed[1].split(/,|\+/) : null;
2013
2014         // the name of the custom event that will be created for the spec
2015         ename = (Y.Lang.isString(id) ? id : Y.stamp(id)) + spec;
2016
2017         ename = ename.replace(/,/g, '_');
2018
2019         if (!Y.getEvent(ename)) {
2020
2021             // subscribe spec validator to the DOM event
2022             Y.on(type + etype, function(e) {
2023
2024                 
2025                 var passed = false, failed = false, i, crit, critInt;
2026
2027                 for (i=0; i<criteria.length; i=i+1) {
2028                     crit = criteria[i]; 
2029                     critInt = parseInt(crit, 10);
2030
2031                     // pass this section if any supplied keyCode 
2032                     // is found
2033                     if (Y.Lang.isNumber(critInt)) {
2034
2035                         if (e.charCode === critInt) {
2036                             passed = true;
2037                         } else {
2038                             failed = true;
2039                         }
2040
2041                     // only check modifier if no keyCode was specified
2042                     // or the keyCode check was successful.  pass only 
2043                     // if every modifier passes
2044                     } else if (passed || !failed) {
2045                         passed = (e[crit + 'Key']);
2046                         failed = !passed;
2047                     }                    
2048                 }
2049
2050                 // fire spec custom event if spec if met
2051                 if (passed) {
2052                     Y.fire(ename, e);
2053                 }
2054
2055             }, id);
2056
2057         }
2058
2059         // subscribe supplied listener to custom event for spec validator
2060         // remove element and spec.
2061         a.splice(2, 2);
2062         a[0] = ename;
2063
2064         return Y.on.apply(Y, a);
2065     }
2066 };
2067
2068
2069 }, '3.0.0' ,{requires:['node-base']});
2070 YUI.add('event-focus', function(Y) {
2071
2072 /**
2073  * Adds focus and blur event listener support.  These events normally
2074  * do not bubble, so this adds support for that so these events
2075  * can be used in event delegation scenarios.
2076  * 
2077  * @module event
2078  * @submodule event-focus
2079  */
2080 (function() {
2081
2082 var UA = Y.UA,
2083         Event = Y.Event,
2084         plugins = Y.Env.evt.plugins,
2085         ie = UA.ie,
2086         bUseMutation = (UA.opera || UA.webkit),
2087         eventNames = {
2088                 focus: (ie ? 'focusin' : (bUseMutation ? 'DOMFocusIn' : 'focus')),
2089                 blur: (ie ? 'focusout' : (bUseMutation ? 'DOMFocusOut' : 'blur'))
2090         },
2091
2092         //      Only need to use capture phase for Gecko since it doesn't support 
2093         //      focusin, focusout, DOMFocusIn, or DOMFocusOut
2094     CAPTURE_CONFIG = { capture: (UA.gecko ? true : false) },
2095
2096
2097         attach = function (args, config) {
2098
2099             var a = Y.Array(args, 0, true);
2100                 a[0] = eventNames[a[0]];
2101             return Event._attach(a, config);
2102
2103         },
2104         
2105         eventAdapter = {
2106
2107                 on: function () {
2108                         return attach(arguments, CAPTURE_CONFIG);
2109                 }
2110
2111         };
2112
2113
2114 Event._attachFocus = attach;
2115 Event._attachBlur = attach;
2116
2117 /**
2118  * Adds a DOM focus listener.  Uses the focusin event in IE, 
2119  * DOMFocusIn for Opera and Webkit, and the capture phase for Gecko so that
2120  * the event propagates in a way that enables event delegation.
2121  *
2122  * @for YUI
2123  * @event focus
2124  * @param type {string} 'focus'
2125  * @param fn {function} the callback function to execute
2126  * @param o {string|HTMLElement|collection} the element(s) to bind
2127  * @param context optional context object
2128  * @param args 0..n additional arguments to provide to the listener.
2129  * @return {EventHandle} the detach handle
2130  */
2131 plugins.focus = eventAdapter;
2132
2133 /**
2134  * Adds a DOM blur listener.  Uses the focusout event in IE, 
2135  * DOMFocusOut for Opera and Webkit, and the capture phase for Gecko so that
2136  * the event propagates in a way that enables event delegation.
2137  *
2138  * @for YUI
2139  * @event blur
2140  * @param type {string} 'blur'
2141  * @param fn {function} the callback function to execute
2142  * @param o {string|HTMLElement|collection} the element(s) to bind
2143  * @param context optional context object
2144  * @param args 0..n additional arguments to provide to the listener.
2145  * @return {EventHandle} the detach handle
2146  */
2147 plugins.blur = eventAdapter;
2148
2149 })();
2150
2151
2152 }, '3.0.0' ,{requires:['node-base']});
2153 YUI.add('event-resize', function(Y) {
2154
2155 /**
2156  * Adds a window resize event that has its behavior normalized to fire at the
2157  * end of the resize rather than constantly during the resize.
2158  * @module event
2159  * @submodule event-resize
2160  */
2161 (function() {
2162
2163 var detachHandle,
2164
2165     timerHandle,
2166
2167     CE_NAME = 'window:resize',
2168
2169     handler = function(e) {
2170
2171         if (Y.UA.gecko) {
2172
2173             Y.fire(CE_NAME, e);
2174
2175         } else {
2176
2177             if (timerHandle) {
2178                 timerHandle.cancel();
2179             }
2180
2181             timerHandle = Y.later(Y.config.windowResizeDelay || 40, Y, function() {
2182                 Y.fire(CE_NAME, e);
2183             });
2184         }
2185         
2186     };
2187
2188
2189 /**
2190  * Firefox fires the window resize event once when the resize action
2191  * finishes, other browsers fire the event periodically during the
2192  * resize.  This code uses timeout logic to simulate the Firefox 
2193  * behavior in other browsers.
2194  * @event windowresize
2195  * @for YUI
2196  */
2197 Y.Env.evt.plugins.windowresize = {
2198
2199     on: function(type, fn) {
2200
2201         // check for single window listener and add if needed
2202         if (!detachHandle) {
2203             detachHandle = Y.Event._attach(['resize', handler]);
2204         }
2205
2206         var a = Y.Array(arguments, 0, true);
2207         a[0] = CE_NAME;
2208
2209         return Y.on.apply(Y, a);
2210     }
2211 };
2212
2213 })();
2214
2215
2216 }, '3.0.0' ,{requires:['node-base']});
2217
2218
2219 YUI.add('event', function(Y){}, '3.0.0' ,{use:['event-base', 'event-delegate', 'event-mousewheel', 'event-mouseenter', 'event-key', 'event-focus', 'event-resize']});
2220