NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / event-base / event-base-debug.js
blobf42cecd1e5f9218afeaf1d93433d7f56f1dbf3f8
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 (function () {
9 var GLOBAL_ENV = YUI.Env;
11 if (!GLOBAL_ENV._ready) {
12     GLOBAL_ENV._ready = function() {
13         GLOBAL_ENV.DOMReady = true;
14         GLOBAL_ENV.remove(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready);
15     };
17     GLOBAL_ENV.add(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready);
19 })();
20 YUI.add('event-base', function (Y, NAME) {
23  * DOM event listener abstraction layer
24  * @module event
25  * @submodule event-base
26  */
28 /**
29  * The domready event fires at the moment the browser's DOM is
30  * usable. In most cases, this is before images are fully
31  * downloaded, allowing you to provide a more responsive user
32  * interface.
33  *
34  * In YUI 3, domready subscribers will be notified immediately if
35  * that moment has already passed when the subscription is created.
36  *
37  * One exception is if the yui.js file is dynamically injected into
38  * the page.  If this is done, you must tell the YUI instance that
39  * you did this in order for DOMReady (and window load events) to
40  * fire normally.  That configuration option is 'injected' -- set
41  * it to true if the yui.js script is not included inline.
42  *
43  * This method is part of the 'event-ready' module, which is a
44  * submodule of 'event'.
45  *
46  * @event domready
47  * @for YUI
48  */
49 Y.publish('domready', {
50     fireOnce: true,
51     async: true
52 });
54 if (YUI.Env.DOMReady) {
55     Y.fire('domready');
56 } else {
57     Y.Do.before(function() { Y.fire('domready'); }, YUI.Env, '_ready');
60 /**
61  * Custom event engine, DOM event listener abstraction layer, synthetic DOM
62  * events.
63  * @module event
64  * @submodule event-base
65  */
67 /**
68  * Wraps a DOM event, properties requiring browser abstraction are
69  * fixed here.  Provids a security layer when required.
70  * @class DOMEventFacade
71  * @param ev {Event} the DOM event
72  * @param currentTarget {HTMLElement} the element the listener was attached to
73  * @param wrapper {Event.Custom} the custom event wrapper for this DOM event
74  */
76     var ua = Y.UA,
78     EMPTY = {},
80     /**
81      * webkit key remapping required for Safari < 3.1
82      * @property webkitKeymap
83      * @private
84      */
85     webkitKeymap = {
86         63232: 38, // up
87         63233: 40, // down
88         63234: 37, // left
89         63235: 39, // right
90         63276: 33, // page up
91         63277: 34, // page down
92         25:     9, // SHIFT-TAB (Safari provides a different key code in
93                    // this case, even though the shiftKey modifier is set)
94         63272: 46, // delete
95         63273: 36, // home
96         63275: 35  // end
97     },
99     /**
100      * Returns a wrapped node.  Intended to be used on event targets,
101      * so it will return the node's parent if the target is a text
102      * node.
103      *
104      * If accessing a property of the node throws an error, this is
105      * probably the anonymous div wrapper Gecko adds inside text
106      * nodes.  This likely will only occur when attempting to access
107      * the relatedTarget.  In this case, we now return null because
108      * the anonymous div is completely useless and we do not know
109      * what the related target was because we can't even get to
110      * the element's parent node.
111      *
112      * @method resolve
113      * @private
114      */
115     resolve = function(n) {
116         if (!n) {
117             return n;
118         }
119         try {
120             if (n && 3 == n.nodeType) {
121                 n = n.parentNode;
122             }
123         } catch(e) {
124             return null;
125         }
127         return Y.one(n);
128     },
130     DOMEventFacade = function(ev, currentTarget, wrapper) {
131         this._event = ev;
132         this._currentTarget = currentTarget;
133         this._wrapper = wrapper || EMPTY;
135         // if not lazy init
136         this.init();
137     };
139 Y.extend(DOMEventFacade, Object, {
141     init: function() {
143         var e = this._event,
144             overrides = this._wrapper.overrides,
145             x = e.pageX,
146             y = e.pageY,
147             c,
148             currentTarget = this._currentTarget;
150         this.altKey   = e.altKey;
151         this.ctrlKey  = e.ctrlKey;
152         this.metaKey  = e.metaKey;
153         this.shiftKey = e.shiftKey;
154         this.type     = (overrides && overrides.type) || e.type;
155         this.clientX  = e.clientX;
156         this.clientY  = e.clientY;
158         this.pageX = x;
159         this.pageY = y;
161         // charCode is unknown in keyup, keydown. keyCode is unknown in keypress.
162         // FF 3.6 - 8+? pass 0 for keyCode in keypress events.
163         // Webkit, FF 3.6-8+?, and IE9+? pass 0 for charCode in keydown, keyup.
164         // Webkit and IE9+? duplicate charCode in keyCode.
165         // Opera never sets charCode, always keyCode (though with the charCode).
166         // IE6-8 don't set charCode or which.
167         // All browsers other than IE6-8 set which=keyCode in keydown, keyup, and
168         // which=charCode in keypress.
169         //
170         // Moral of the story: (e.which || e.keyCode) will always return the
171         // known code for that key event phase. e.keyCode is often different in
172         // keypress from keydown and keyup.
173         c = e.keyCode || e.charCode;
175         if (ua.webkit && (c in webkitKeymap)) {
176             c = webkitKeymap[c];
177         }
179         this.keyCode = c;
180         this.charCode = c;
181         // Fill in e.which for IE - implementers should always use this over
182         // e.keyCode or e.charCode.
183         this.which = e.which || e.charCode || c;
184         // this.button = e.button;
185         this.button = this.which;
187         this.target = resolve(e.target);
188         this.currentTarget = resolve(currentTarget);
189         this.relatedTarget = resolve(e.relatedTarget);
191         if (e.type == "mousewheel" || e.type == "DOMMouseScroll") {
192             this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1);
193         }
195         if (this._touch) {
196             this._touch(e, currentTarget, this._wrapper);
197         }
198     },
200     stopPropagation: function() {
201         this._event.stopPropagation();
202         this._wrapper.stopped = 1;
203         this.stopped = 1;
204     },
206     stopImmediatePropagation: function() {
207         var e = this._event;
208         if (e.stopImmediatePropagation) {
209             e.stopImmediatePropagation();
210         } else {
211             this.stopPropagation();
212         }
213         this._wrapper.stopped = 2;
214         this.stopped = 2;
215     },
217     preventDefault: function(returnValue) {
218         var e = this._event;
219         e.preventDefault();
220         e.returnValue = returnValue || false;
221         this._wrapper.prevented = 1;
222         this.prevented = 1;
223     },
225     halt: function(immediate) {
226         if (immediate) {
227             this.stopImmediatePropagation();
228         } else {
229             this.stopPropagation();
230         }
232         this.preventDefault();
233     }
237 DOMEventFacade.resolve = resolve;
238 Y.DOM2EventFacade = DOMEventFacade;
239 Y.DOMEventFacade = DOMEventFacade;
241     /**
242      * The native event
243      * @property _event
244      * @type {Native DOM Event}
245      * @private
246      */
248     /**
249     The name of the event (e.g. "click")
251     @property type
252     @type {String}
253     **/
255     /**
256     `true` if the "alt" or "option" key is pressed.
258     @property altKey
259     @type {Boolean}
260     **/
262     /**
263     `true` if the shift key is pressed.
265     @property shiftKey
266     @type {Boolean}
267     **/
269     /**
270     `true` if the "Windows" key on a Windows keyboard, "command" key on an
271     Apple keyboard, or "meta" key on other keyboards is pressed.
273     @property metaKey
274     @type {Boolean}
275     **/
277     /**
278     `true` if the "Ctrl" or "control" key is pressed.
280     @property ctrlKey
281     @type {Boolean}
282     **/
284     /**
285      * The X location of the event on the page (including scroll)
286      * @property pageX
287      * @type {Number}
288      */
290     /**
291      * The Y location of the event on the page (including scroll)
292      * @property pageY
293      * @type {Number}
294      */
296     /**
297      * The X location of the event in the viewport
298      * @property clientX
299      * @type {Number}
300      */
302     /**
303      * The Y location of the event in the viewport
304      * @property clientY
305      * @type {Number}
306      */
308     /**
309      * The keyCode for key events.  Uses charCode if keyCode is not available
310      * @property keyCode
311      * @type {Number}
312      */
314     /**
315      * The charCode for key events.  Same as keyCode
316      * @property charCode
317      * @type {Number}
318      */
320     /**
321      * The button that was pushed. 1 for left click, 2 for middle click, 3 for
322      * right click.  This is only reliably populated on `mouseup` events.
323      * @property button
324      * @type {Number}
325      */
327     /**
328      * The button that was pushed.  Same as button.
329      * @property which
330      * @type {Number}
331      */
333     /**
334      * Node reference for the targeted element
335      * @property target
336      * @type {Node}
337      */
339     /**
340      * Node reference for the element that the listener was attached to.
341      * @property currentTarget
342      * @type {Node}
343      */
345     /**
346      * Node reference to the relatedTarget
347      * @property relatedTarget
348      * @type {Node}
349      */
351     /**
352      * Number representing the direction and velocity of the movement of the mousewheel.
353      * Negative is down, the higher the number, the faster.  Applies to the mousewheel event.
354      * @property wheelDelta
355      * @type {Number}
356      */
358     /**
359      * Stops the propagation to the next bubble target
360      * @method stopPropagation
361      */
363     /**
364      * Stops the propagation to the next bubble target and
365      * prevents any additional listeners from being exectued
366      * on the current target.
367      * @method stopImmediatePropagation
368      */
370     /**
371      * Prevents the event's default behavior
372      * @method preventDefault
373      * @param returnValue {string} sets the returnValue of the event to this value
374      * (rather than the default false value).  This can be used to add a customized
375      * confirmation query to the beforeunload event).
376      */
378     /**
379      * Stops the event propagation and prevents the default
380      * event behavior.
381      * @method halt
382      * @param immediate {boolean} if true additional listeners
383      * on the current target will not be executed
384      */
385 (function() {
388  * The event utility provides functions to add and remove event listeners,
389  * event cleansing.  It also tries to automatically remove listeners it
390  * registers during the unload event.
391  * @module event
392  * @main event
393  * @submodule event-base
394  */
397  * The event utility provides functions to add and remove event listeners,
398  * event cleansing.  It also tries to automatically remove listeners it
399  * registers during the unload event.
401  * @class Event
402  * @static
403  */
405 Y.Env.evt.dom_wrappers = {};
406 Y.Env.evt.dom_map = {};
408 var _eventenv = Y.Env.evt,
409     config = Y.config,
410     win = config.win,
411     add = YUI.Env.add,
412     remove = YUI.Env.remove,
414     onLoad = function() {
415         YUI.Env.windowLoaded = true;
416         Y.Event._load();
417         remove(win, "load", onLoad);
418     },
420     onUnload = function() {
421         Y.Event._unload();
422     },
424     EVENT_READY = 'domready',
426     COMPAT_ARG = '~yui|2|compat~',
428     shouldIterate = function(o) {
429         try {
430             // TODO: See if there's a more performant way to return true early on this, for the common case
431             return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) && !o.tagName && !Y.DOM.isWindow(o));
432         } catch(ex) {
433             Y.log("collection check failure", "warn", "event");
434             return false;
435         }
436     },
438     // aliases to support DOM event subscription clean up when the last
439     // subscriber is detached. deleteAndClean overrides the DOM event's wrapper
440     // CustomEvent _delete method.
441     _ceProtoDelete = Y.CustomEvent.prototype._delete,
442     _deleteAndClean = function(s) {
443         var ret = _ceProtoDelete.apply(this, arguments);
445         if (!this.hasSubs()) {
446             Y.Event._clean(this);
447         }
449         return ret;
450     },
452 Event = function() {
454     /**
455      * True after the onload event has fired
456      * @property _loadComplete
457      * @type boolean
458      * @static
459      * @private
460      */
461     var _loadComplete =  false,
463     /**
464      * The number of times to poll after window.onload.  This number is
465      * increased if additional late-bound handlers are requested after
466      * the page load.
467      * @property _retryCount
468      * @static
469      * @private
470      */
471     _retryCount = 0,
473     /**
474      * onAvailable listeners
475      * @property _avail
476      * @static
477      * @private
478      */
479     _avail = [],
481     /**
482      * Custom event wrappers for DOM events.  Key is
483      * 'event:' + Element uid stamp + event type
484      * @property _wrappers
485      * @type Y.Event.Custom
486      * @static
487      * @private
488      */
489     _wrappers = _eventenv.dom_wrappers,
491     _windowLoadKey = null,
493     /**
494      * Custom event wrapper map DOM events.  Key is
495      * Element uid stamp.  Each item is a hash of custom event
496      * wrappers as provided in the _wrappers collection.  This
497      * provides the infrastructure for getListeners.
498      * @property _el_events
499      * @static
500      * @private
501      */
502     _el_events = _eventenv.dom_map;
504     return {
506         /**
507          * The number of times we should look for elements that are not
508          * in the DOM at the time the event is requested after the document
509          * has been loaded.  The default is 1000@amp;40 ms, so it will poll
510          * for 40 seconds or until all outstanding handlers are bound
511          * (whichever comes first).
512          * @property POLL_RETRYS
513          * @type int
514          * @static
515          * @final
516          */
517         POLL_RETRYS: 1000,
519         /**
520          * The poll interval in milliseconds
521          * @property POLL_INTERVAL
522          * @type int
523          * @static
524          * @final
525          */
526         POLL_INTERVAL: 40,
528         /**
529          * addListener/removeListener can throw errors in unexpected scenarios.
530          * These errors are suppressed, the method returns false, and this property
531          * is set
532          * @property lastError
533          * @static
534          * @type Error
535          */
536         lastError: null,
539         /**
540          * poll handle
541          * @property _interval
542          * @static
543          * @private
544          */
545         _interval: null,
547         /**
548          * document readystate poll handle
549          * @property _dri
550          * @static
551          * @private
552          */
553          _dri: null,
555         /**
556          * True when the document is initially usable
557          * @property DOMReady
558          * @type boolean
559          * @static
560          */
561         DOMReady: false,
563         /**
564          * @method startInterval
565          * @static
566          * @private
567          */
568         startInterval: function() {
569             if (!Event._interval) {
570 Event._interval = setInterval(Event._poll, Event.POLL_INTERVAL);
571             }
572         },
574         /**
575          * Executes the supplied callback when the item with the supplied
576          * id is found.  This is meant to be used to execute behavior as
577          * soon as possible as the page loads.  If you use this after the
578          * initial page load it will poll for a fixed time for the element.
579          * The number of times it will poll and the frequency are
580          * configurable.  By default it will poll for 10 seconds.
581          *
582          * <p>The callback is executed with a single parameter:
583          * the custom object parameter, if provided.</p>
584          *
585          * @method onAvailable
586          *
587          * @param {string||string[]}   id the id of the element, or an array
588          * of ids to look for.
589          * @param {function} fn what to execute when the element is found.
590          * @param {object}   p_obj an optional object to be passed back as
591          *                   a parameter to fn.
592          * @param {boolean|object}  p_override If set to true, fn will execute
593          *                   in the context of p_obj, if set to an object it
594          *                   will execute in the context of that object
595          * @param checkContent {boolean} check child node readiness (onContentReady)
596          * @static
597          * @deprecated Use Y.on("available")
598          */
599         // @TODO fix arguments
600         onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) {
602             var a = Y.Array(id), i, availHandle;
604             // Y.log('onAvailable registered for: ' + id);
606             for (i=0; i<a.length; i=i+1) {
607                 _avail.push({
608                     id:         a[i],
609                     fn:         fn,
610                     obj:        p_obj,
611                     override:   p_override,
612                     checkReady: checkContent,
613                     compat:     compat
614                 });
615             }
616             _retryCount = this.POLL_RETRYS;
618             // We want the first test to be immediate, but async
619             setTimeout(Event._poll, 0);
621             availHandle = new Y.EventHandle({
623                 _delete: function() {
624                     // set by the event system for lazy DOM listeners
625                     if (availHandle.handle) {
626                         availHandle.handle.detach();
627                         return;
628                     }
630                     var i, j;
632                     // otherwise try to remove the onAvailable listener(s)
633                     for (i = 0; i < a.length; i++) {
634                         for (j = 0; j < _avail.length; j++) {
635                             if (a[i] === _avail[j].id) {
636                                 _avail.splice(j, 1);
637                             }
638                         }
639                     }
640                 }
642             });
644             return availHandle;
645         },
647         /**
648          * Works the same way as onAvailable, but additionally checks the
649          * state of sibling elements to determine if the content of the
650          * available element is safe to modify.
651          *
652          * <p>The callback is executed with a single parameter:
653          * the custom object parameter, if provided.</p>
654          *
655          * @method onContentReady
656          *
657          * @param {string}   id the id of the element to look for.
658          * @param {function} fn what to execute when the element is ready.
659          * @param {object}   obj an optional object to be passed back as
660          *                   a parameter to fn.
661          * @param {boolean|object}  override If set to true, fn will execute
662          *                   in the context of p_obj.  If an object, fn will
663          *                   exectute in the context of that object
664          *
665          * @static
666          * @deprecated Use Y.on("contentready")
667          */
668         // @TODO fix arguments
669         onContentReady: function(id, fn, obj, override, compat) {
670             return Event.onAvailable(id, fn, obj, override, true, compat);
671         },
673         /**
674          * Adds an event listener
675          *
676          * @method attach
677          *
678          * @param {String}   type     The type of event to append
679          * @param {Function} fn        The method the event invokes
680          * @param {String|HTMLElement|Array|NodeList} el An id, an element
681          *  reference, or a collection of ids and/or elements to assign the
682          *  listener to.
683          * @param {Object}   context optional context object
684          * @param {Boolean|object}  args 0..n arguments to pass to the callback
685          * @return {EventHandle} an object to that can be used to detach the listener
686          *
687          * @static
688          */
690         attach: function(type, fn, el, context) {
691             return Event._attach(Y.Array(arguments, 0, true));
692         },
694         _createWrapper: function (el, type, capture, compat, facade) {
696             var cewrapper,
697                 ek  = Y.stamp(el),
698                 key = 'event:' + ek + type;
700             if (false === facade) {
701                 key += 'native';
702             }
703             if (capture) {
704                 key += 'capture';
705             }
708             cewrapper = _wrappers[key];
711             if (!cewrapper) {
712                 // create CE wrapper
713                 cewrapper = Y.publish(key, {
714                     silent: true,
715                     bubbles: false,
716                     emitFacade:false,
717                     contextFn: function() {
718                         if (compat) {
719                             return cewrapper.el;
720                         } else {
721                             cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el);
722                             return cewrapper.nodeRef;
723                         }
724                     }
725                 });
727                 cewrapper.overrides = {};
729                 // for later removeListener calls
730                 cewrapper.el = el;
731                 cewrapper.key = key;
732                 cewrapper.domkey = ek;
733                 cewrapper.type = type;
734                 cewrapper.fn = function(e) {
735                     cewrapper.fire(Event.getEvent(e, el, (compat || (false === facade))));
736                 };
737                 cewrapper.capture = capture;
739                 if (el == win && type == "load") {
740                     // window load happens once
741                     cewrapper.fireOnce = true;
742                     _windowLoadKey = key;
743                 }
744                 cewrapper._delete = _deleteAndClean;
746                 _wrappers[key] = cewrapper;
747                 _el_events[ek] = _el_events[ek] || {};
748                 _el_events[ek][key] = cewrapper;
750                 add(el, type, cewrapper.fn, capture);
751             }
753             return cewrapper;
755         },
757         _attach: function(args, conf) {
759             var compat,
760                 handles, oEl, cewrapper, context,
761                 fireNow = false, ret,
762                 type = args[0],
763                 fn = args[1],
764                 el = args[2] || win,
765                 facade = conf && conf.facade,
766                 capture = conf && conf.capture,
767                 overrides = conf && conf.overrides;
769             if (args[args.length-1] === COMPAT_ARG) {
770                 compat = true;
771             }
773             if (!fn || !fn.call) {
774 // throw new TypeError(type + " attach call failed, callback undefined");
775 Y.log(type + " attach call failed, invalid callback", "error", "event");
776                 return false;
777             }
779             // The el argument can be an array of elements or element ids.
780             if (shouldIterate(el)) {
782                 handles=[];
784                 Y.each(el, function(v, k) {
785                     args[2] = v;
786                     handles.push(Event._attach(args.slice(), conf));
787                 });
789                 // return (handles.length === 1) ? handles[0] : handles;
790                 return new Y.EventHandle(handles);
792             // If the el argument is a string, we assume it is
793             // actually the id of the element.  If the page is loaded
794             // we convert el to the actual element, otherwise we
795             // defer attaching the event until the element is
796             // ready
797             } else if (Y.Lang.isString(el)) {
799                 // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el);
801                 if (compat) {
802                     oEl = Y.DOM.byId(el);
803                 } else {
805                     oEl = Y.Selector.query(el);
807                     switch (oEl.length) {
808                         case 0:
809                             oEl = null;
810                             break;
811                         case 1:
812                             oEl = oEl[0];
813                             break;
814                         default:
815                             args[2] = oEl;
816                             return Event._attach(args, conf);
817                     }
818                 }
820                 if (oEl) {
822                     el = oEl;
824                 // Not found = defer adding the event until the element is available
825                 } else {
827                     // Y.log(el + ' not found');
828                     ret = Event.onAvailable(el, function() {
829                         // Y.log('lazy attach: ' + args);
831                         ret.handle = Event._attach(args, conf);
833                     }, Event, true, false, compat);
835                     return ret;
837                 }
838             }
840             // Element should be an html element or node
841             if (!el) {
842                 Y.log("unable to attach event " + type, "warn", "event");
843                 return false;
844             }
846             if (Y.Node && Y.instanceOf(el, Y.Node)) {
847                 el = Y.Node.getDOMNode(el);
848             }
850             cewrapper = Event._createWrapper(el, type, capture, compat, facade);
851             if (overrides) {
852                 Y.mix(cewrapper.overrides, overrides);
853             }
855             if (el == win && type == "load") {
857                 // if the load is complete, fire immediately.
858                 // all subscribers, including the current one
859                 // will be notified.
860                 if (YUI.Env.windowLoaded) {
861                     fireNow = true;
862                 }
863             }
865             if (compat) {
866                 args.pop();
867             }
869             context = args[3];
871             // set context to the Node if not specified
872             // ret = cewrapper.on.apply(cewrapper, trimmedArgs);
873             ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null);
875             if (fireNow) {
876                 cewrapper.fire();
877             }
879             return ret;
881         },
883         /**
884          * Removes an event listener.  Supports the signature the event was bound
885          * with, but the preferred way to remove listeners is using the handle
886          * that is returned when using Y.on
887          *
888          * @method detach
889          *
890          * @param {String} type the type of event to remove.
891          * @param {Function} fn the method the event invokes.  If fn is
892          * undefined, then all event handlers for the type of event are
893          * removed.
894          * @param {String|HTMLElement|Array|NodeList|EventHandle} el An
895          * event handle, an id, an element reference, or a collection
896          * of ids and/or elements to remove the listener from.
897          * @return {boolean} true if the unbind was successful, false otherwise.
898          * @static
899          */
900         detach: function(type, fn, el, obj) {
902             var args=Y.Array(arguments, 0, true), compat, l, ok, i,
903                 id, ce;
905             if (args[args.length-1] === COMPAT_ARG) {
906                 compat = true;
907                 // args.pop();
908             }
910             if (type && type.detach) {
911                 return type.detach();
912             }
914             // The el argument can be a string
915             if (typeof el == "string") {
917                 // el = (compat) ? Y.DOM.byId(el) : Y.all(el);
918                 if (compat) {
919                     el = Y.DOM.byId(el);
920                 } else {
921                     el = Y.Selector.query(el);
922                     l = el.length;
923                     if (l < 1) {
924                         el = null;
925                     } else if (l == 1) {
926                         el = el[0];
927                     }
928                 }
929                 // return Event.detach.apply(Event, args);
930             }
932             if (!el) {
933                 return false;
934             }
936             if (el.detach) {
937                 args.splice(2, 1);
938                 return el.detach.apply(el, args);
939             // The el argument can be an array of elements or element ids.
940             } else if (shouldIterate(el)) {
941                 ok = true;
942                 for (i=0, l=el.length; i<l; ++i) {
943                     args[2] = el[i];
944                     ok = ( Y.Event.detach.apply(Y.Event, args) && ok );
945                 }
947                 return ok;
948             }
950             if (!type || !fn || !fn.call) {
951                 return Event.purgeElement(el, false, type);
952             }
954             id = 'event:' + Y.stamp(el) + type;
955             ce = _wrappers[id];
957             if (ce) {
958                 return ce.detach(fn);
959             } else {
960                 return false;
961             }
963         },
965         /**
966          * Finds the event in the window object, the caller's arguments, or
967          * in the arguments of another method in the callstack.  This is
968          * executed automatically for events registered through the event
969          * manager, so the implementer should not normally need to execute
970          * this function at all.
971          * @method getEvent
972          * @param {Event} e the event parameter from the handler
973          * @param {HTMLElement} el the element the listener was attached to
974          * @return {Event} the event
975          * @static
976          */
977         getEvent: function(e, el, noFacade) {
978             var ev = e || win.event;
980             return (noFacade) ? ev :
981                 new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]);
982         },
984         /**
985          * Generates an unique ID for the element if it does not already
986          * have one.
987          * @method generateId
988          * @param el the element to create the id for
989          * @return {string} the resulting id of the element
990          * @static
991          */
992         generateId: function(el) {
993             return Y.DOM.generateID(el);
994         },
996         /**
997          * We want to be able to use getElementsByTagName as a collection
998          * to attach a group of events to.  Unfortunately, different
999          * browsers return different types of collections.  This function
1000          * tests to determine if the object is array-like.  It will also
1001          * fail if the object is an array, but is empty.
1002          * @method _isValidCollection
1003          * @param o the object to test
1004          * @return {boolean} true if the object is array-like and populated
1005          * @deprecated was not meant to be used directly
1006          * @static
1007          * @private
1008          */
1009         _isValidCollection: shouldIterate,
1011         /**
1012          * hook up any deferred listeners
1013          * @method _load
1014          * @static
1015          * @private
1016          */
1017         _load: function(e) {
1018             if (!_loadComplete) {
1019                 // Y.log('Load Complete', 'info', 'event');
1020                 _loadComplete = true;
1022                 // Just in case DOMReady did not go off for some reason
1023                 // E._ready();
1024                 if (Y.fire) {
1025                     Y.fire(EVENT_READY);
1026                 }
1028                 // Available elements may not have been detected before the
1029                 // window load event fires. Try to find them now so that the
1030                 // the user is more likely to get the onAvailable notifications
1031                 // before the window load notification
1032                 Event._poll();
1033             }
1034         },
1036         /**
1037          * Polling function that runs before the onload event fires,
1038          * attempting to attach to DOM Nodes as soon as they are
1039          * available
1040          * @method _poll
1041          * @static
1042          * @private
1043          */
1044         _poll: function() {
1045             if (Event.locked) {
1046                 return;
1047             }
1049             if (Y.UA.ie && !YUI.Env.DOMReady) {
1050                 // Hold off if DOMReady has not fired and check current
1051                 // readyState to protect against the IE operation aborted
1052                 // issue.
1053                 Event.startInterval();
1054                 return;
1055             }
1057             Event.locked = true;
1059             // Y.log.debug("poll");
1060             // keep trying until after the page is loaded.  We need to
1061             // check the page load state prior to trying to bind the
1062             // elements so that we can be certain all elements have been
1063             // tested appropriately
1064             var i, len, item, el, notAvail, executeItem,
1065                 tryAgain = !_loadComplete;
1067             if (!tryAgain) {
1068                 tryAgain = (_retryCount > 0);
1069             }
1071             // onAvailable
1072             notAvail = [];
1074             executeItem = function (el, item) {
1075                 var context, ov = item.override;
1076                 try {
1077                     if (item.compat) {
1078                         if (item.override) {
1079                             if (ov === true) {
1080                                 context = item.obj;
1081                             } else {
1082                                 context = ov;
1083                             }
1084                         } else {
1085                             context = el;
1086                         }
1087                         item.fn.call(context, item.obj);
1088                     } else {
1089                         context = item.obj || Y.one(el);
1090                         item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []);
1091                     }
1092                 } catch (e) {
1093                     Y.log("Error in available or contentReady callback", 'error', 'event');
1094                 }
1095             };
1097             // onAvailable
1098             for (i=0,len=_avail.length; i<len; ++i) {
1099                 item = _avail[i];
1100                 if (item && !item.checkReady) {
1102                     // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
1103                     el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
1105                     if (el) {
1106                         // Y.log('avail: ' + el);
1107                         executeItem(el, item);
1108                         _avail[i] = null;
1109                     } else {
1110                         // Y.log('NOT avail: ' + el);
1111                         notAvail.push(item);
1112                     }
1113                 }
1114             }
1116             // onContentReady
1117             for (i=0,len=_avail.length; i<len; ++i) {
1118                 item = _avail[i];
1119                 if (item && item.checkReady) {
1121                     // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
1122                     el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
1124                     if (el) {
1125                         // The element is available, but not necessarily ready
1126                         // @todo should we test parentNode.nextSibling?
1127                         if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) {
1128                             executeItem(el, item);
1129                             _avail[i] = null;
1130                         }
1131                     } else {
1132                         notAvail.push(item);
1133                     }
1134                 }
1135             }
1137             _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1;
1139             if (tryAgain) {
1140                 // we may need to strip the nulled out items here
1141                 Event.startInterval();
1142             } else {
1143                 clearInterval(Event._interval);
1144                 Event._interval = null;
1145             }
1147             Event.locked = false;
1149             return;
1151         },
1153         /**
1154          * Removes all listeners attached to the given element via addListener.
1155          * Optionally, the node's children can also be purged.
1156          * Optionally, you can specify a specific type of event to remove.
1157          * @method purgeElement
1158          * @param {HTMLElement} el the element to purge
1159          * @param {boolean} recurse recursively purge this element's children
1160          * as well.  Use with caution.
1161          * @param {string} type optional type of listener to purge. If
1162          * left out, all listeners will be removed
1163          * @static
1164          */
1165         purgeElement: function(el, recurse, type) {
1166             // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el,
1167             var oEl = (Y.Lang.isString(el)) ?  Y.Selector.query(el, null, true) : el,
1168                 lis = Event.getListeners(oEl, type), i, len, children, child;
1170             if (recurse && oEl) {
1171                 lis = lis || [];
1172                 children = Y.Selector.query('*', oEl);
1173                 len = children.length;
1174                 for (i = 0; i < len; ++i) {
1175                     child = Event.getListeners(children[i], type);
1176                     if (child) {
1177                         lis = lis.concat(child);
1178                     }
1179                 }
1180             }
1182             if (lis) {
1183                 for (i = 0, len = lis.length; i < len; ++i) {
1184                     lis[i].detachAll();
1185                 }
1186             }
1188         },
1190         /**
1191          * Removes all object references and the DOM proxy subscription for
1192          * a given event for a DOM node.
1193          *
1194          * @method _clean
1195          * @param wrapper {CustomEvent} Custom event proxy for the DOM
1196          *                  subscription
1197          * @private
1198          * @static
1199          * @since 3.4.0
1200          */
1201         _clean: function (wrapper) {
1202             var key    = wrapper.key,
1203                 domkey = wrapper.domkey;
1205             remove(wrapper.el, wrapper.type, wrapper.fn, wrapper.capture);
1206             delete _wrappers[key];
1207             delete Y._yuievt.events[key];
1208             if (_el_events[domkey]) {
1209                 delete _el_events[domkey][key];
1210                 if (!Y.Object.size(_el_events[domkey])) {
1211                     delete _el_events[domkey];
1212                 }
1213             }
1214         },
1216         /**
1217          * Returns all listeners attached to the given element via addListener.
1218          * Optionally, you can specify a specific type of event to return.
1219          * @method getListeners
1220          * @param el {HTMLElement|string} the element or element id to inspect
1221          * @param type {string} optional type of listener to return. If
1222          * left out, all listeners will be returned
1223          * @return {CustomEvent} the custom event wrapper for the DOM event(s)
1224          * @static
1225          */
1226         getListeners: function(el, type) {
1227             var ek = Y.stamp(el, true), evts = _el_events[ek],
1228                 results=[] , key = (type) ? 'event:' + ek + type : null,
1229                 adapters = _eventenv.plugins;
1231             if (!evts) {
1232                 return null;
1233             }
1235             if (key) {
1236                 // look for synthetic events
1237                 if (adapters[type] && adapters[type].eventDef) {
1238                     key += '_synth';
1239                 }
1241                 if (evts[key]) {
1242                     results.push(evts[key]);
1243                 }
1245                 // get native events as well
1246                 key += 'native';
1247                 if (evts[key]) {
1248                     results.push(evts[key]);
1249                 }
1251             } else {
1252                 Y.each(evts, function(v, k) {
1253                     results.push(v);
1254                 });
1255             }
1257             return (results.length) ? results : null;
1258         },
1260         /**
1261          * Removes all listeners registered by pe.event.  Called
1262          * automatically during the unload event.
1263          * @method _unload
1264          * @static
1265          * @private
1266          */
1267         _unload: function(e) {
1268             Y.each(_wrappers, function(v, k) {
1269                 if (v.type == 'unload') {
1270                     v.fire(e);
1271                 }
1272                 v.detachAll();
1273             });
1274             remove(win, "unload", onUnload);
1275         },
1277         /**
1278          * Adds a DOM event directly without the caching, cleanup, context adj, etc
1279          *
1280          * @method nativeAdd
1281          * @param {HTMLElement} el      the element to bind the handler to
1282          * @param {string}      type   the type of event handler
1283          * @param {function}    fn      the callback to invoke
1284          * @param {boolen}      capture capture or bubble phase
1285          * @static
1286          * @private
1287          */
1288         nativeAdd: add,
1290         /**
1291          * Basic remove listener
1292          *
1293          * @method nativeRemove
1294          * @param {HTMLElement} el      the element to bind the handler to
1295          * @param {string}      type   the type of event handler
1296          * @param {function}    fn      the callback to invoke
1297          * @param {boolen}      capture capture or bubble phase
1298          * @static
1299          * @private
1300          */
1301         nativeRemove: remove
1302     };
1304 }();
1306 Y.Event = Event;
1308 if (config.injected || YUI.Env.windowLoaded) {
1309     onLoad();
1310 } else {
1311     add(win, "load", onLoad);
1314 // Process onAvailable/onContentReady items when when the DOM is ready in IE
1315 if (Y.UA.ie) {
1316     Y.on(EVENT_READY, Event._poll);
1318     // In IE6 and below, detach event handlers when the page is unloaded in
1319     // order to try and prevent cross-page memory leaks. This isn't done in
1320     // other browsers because a) it's not necessary, and b) it breaks the
1321     // back/forward cache.
1322     if (Y.UA.ie < 7) {
1323         try {
1324             add(win, "unload", onUnload);
1325         } catch(e) {
1326             Y.log("Registering unload listener failed.", "warn", "event-base");
1327         }
1328     }
1331 Event.Custom = Y.CustomEvent;
1332 Event.Subscriber = Y.Subscriber;
1333 Event.Target = Y.EventTarget;
1334 Event.Handle = Y.EventHandle;
1335 Event.Facade = Y.EventFacade;
1337 Event._poll();
1339 }());
1342  * DOM event listener abstraction layer
1343  * @module event
1344  * @submodule event-base
1345  */
1348  * Executes the callback as soon as the specified element
1349  * is detected in the DOM.  This function expects a selector
1350  * string for the element(s) to detect.  If you already have
1351  * an element reference, you don't need this event.
1352  * @event available
1353  * @param type {string} 'available'
1354  * @param fn {function} the callback function to execute.
1355  * @param el {string} an selector for the element(s) to attach
1356  * @param context optional argument that specifies what 'this' refers to.
1357  * @param args* 0..n additional arguments to pass on to the callback function.
1358  * These arguments will be added after the event object.
1359  * @return {EventHandle} the detach handle
1360  * @for YUI
1361  */
1362 Y.Env.evt.plugins.available = {
1363     on: function(type, fn, id, o) {
1364         var a = arguments.length > 4 ?  Y.Array(arguments, 4, true) : null;
1365         return Y.Event.onAvailable.call(Y.Event, id, fn, o, a);
1366     }
1370  * Executes the callback as soon as the specified element
1371  * is detected in the DOM with a nextSibling property
1372  * (indicating that the element's children are available).
1373  * This function expects a selector
1374  * string for the element(s) to detect.  If you already have
1375  * an element reference, you don't need this event.
1376  * @event contentready
1377  * @param type {string} 'contentready'
1378  * @param fn {function} the callback function to execute.
1379  * @param el {string} an selector for the element(s) to attach.
1380  * @param context optional argument that specifies what 'this' refers to.
1381  * @param args* 0..n additional arguments to pass on to the callback function.
1382  * These arguments will be added after the event object.
1383  * @return {EventHandle} the detach handle
1384  * @for YUI
1385  */
1386 Y.Env.evt.plugins.contentready = {
1387     on: function(type, fn, id, o) {
1388         var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : null;
1389         return Y.Event.onContentReady.call(Y.Event, id, fn, o, a);
1390     }
1394 }, '3.13.0', {"requires": ["event-custom-base"]});