NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / event-custom-base / event-custom-base-debug.js
blobf9907a861a54a5bdfc6586d89c162dce636bd627
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 YUI.add('event-custom-base', function (Y, NAME) {
10 /**
11  * Custom event engine, DOM event listener abstraction layer, synthetic DOM
12  * events.
13  * @module event-custom
14  */
16 Y.Env.evt = {
17     handles: {},
18     plugins: {}
22 /**
23  * Custom event engine, DOM event listener abstraction layer, synthetic DOM
24  * events.
25  * @module event-custom
26  * @submodule event-custom-base
27  */
29 /**
30  * Allows for the insertion of methods that are executed before or after
31  * a specified method
32  * @class Do
33  * @static
34  */
36 var DO_BEFORE = 0,
37     DO_AFTER = 1,
39 DO = {
41     /**
42      * Cache of objects touched by the utility
43      * @property objs
44      * @static
45      * @deprecated Since 3.6.0. The `_yuiaop` property on the AOP'd object
46      * replaces the role of this property, but is considered to be private, and
47      * is only mentioned to provide a migration path.
48      *
49      * If you have a use case which warrants migration to the _yuiaop property,
50      * please file a ticket to let us know what it's used for and we can see if
51      * we need to expose hooks for that functionality more formally.
52      */
53     objs: null,
55     /**
56      * <p>Execute the supplied method before the specified function.  Wrapping
57      * function may optionally return an instance of the following classes to
58      * further alter runtime behavior:</p>
59      * <dl>
60      *     <dt></code>Y.Do.Halt(message, returnValue)</code></dt>
61      *         <dd>Immediatly stop execution and return
62      *         <code>returnValue</code>.  No other wrapping functions will be
63      *         executed.</dd>
64      *     <dt></code>Y.Do.AlterArgs(message, newArgArray)</code></dt>
65      *         <dd>Replace the arguments that the original function will be
66      *         called with.</dd>
67      *     <dt></code>Y.Do.Prevent(message)</code></dt>
68      *         <dd>Don't execute the wrapped function.  Other before phase
69      *         wrappers will be executed.</dd>
70      * </dl>
71      *
72      * @method before
73      * @param fn {Function} the function to execute
74      * @param obj the object hosting the method to displace
75      * @param sFn {string} the name of the method to displace
76      * @param c The execution context for fn
77      * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
78      * when the event fires.
79      * @return {string} handle for the subscription
80      * @static
81      */
82     before: function(fn, obj, sFn, c) {
83         // Y.log('Do before: ' + sFn, 'info', 'event');
84         var f = fn, a;
85         if (c) {
86             a = [fn, c].concat(Y.Array(arguments, 4, true));
87             f = Y.rbind.apply(Y, a);
88         }
90         return this._inject(DO_BEFORE, f, obj, sFn);
91     },
93     /**
94      * <p>Execute the supplied method after the specified function.  Wrapping
95      * function may optionally return an instance of the following classes to
96      * further alter runtime behavior:</p>
97      * <dl>
98      *     <dt></code>Y.Do.Halt(message, returnValue)</code></dt>
99      *         <dd>Immediatly stop execution and return
100      *         <code>returnValue</code>.  No other wrapping functions will be
101      *         executed.</dd>
102      *     <dt></code>Y.Do.AlterReturn(message, returnValue)</code></dt>
103      *         <dd>Return <code>returnValue</code> instead of the wrapped
104      *         method's original return value.  This can be further altered by
105      *         other after phase wrappers.</dd>
106      * </dl>
107      *
108      * <p>The static properties <code>Y.Do.originalRetVal</code> and
109      * <code>Y.Do.currentRetVal</code> will be populated for reference.</p>
110      *
111      * @method after
112      * @param fn {Function} the function to execute
113      * @param obj the object hosting the method to displace
114      * @param sFn {string} the name of the method to displace
115      * @param c The execution context for fn
116      * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
117      * @return {string} handle for the subscription
118      * @static
119      */
120     after: function(fn, obj, sFn, c) {
121         var f = fn, a;
122         if (c) {
123             a = [fn, c].concat(Y.Array(arguments, 4, true));
124             f = Y.rbind.apply(Y, a);
125         }
127         return this._inject(DO_AFTER, f, obj, sFn);
128     },
130     /**
131      * Execute the supplied method before or after the specified function.
132      * Used by <code>before</code> and <code>after</code>.
133      *
134      * @method _inject
135      * @param when {string} before or after
136      * @param fn {Function} the function to execute
137      * @param obj the object hosting the method to displace
138      * @param sFn {string} the name of the method to displace
139      * @param c The execution context for fn
140      * @return {string} handle for the subscription
141      * @private
142      * @static
143      */
144     _inject: function(when, fn, obj, sFn) {
145         // object id
146         var id = Y.stamp(obj), o, sid;
148         if (!obj._yuiaop) {
149             // create a map entry for the obj if it doesn't exist, to hold overridden methods
150             obj._yuiaop = {};
151         }
153         o = obj._yuiaop;
155         if (!o[sFn]) {
156             // create a map entry for the method if it doesn't exist
157             o[sFn] = new Y.Do.Method(obj, sFn);
159             // re-route the method to our wrapper
160             obj[sFn] = function() {
161                 return o[sFn].exec.apply(o[sFn], arguments);
162             };
163         }
165         // subscriber id
166         sid = id + Y.stamp(fn) + sFn;
168         // register the callback
169         o[sFn].register(sid, fn, when);
171         return new Y.EventHandle(o[sFn], sid);
172     },
174     /**
175      * Detach a before or after subscription.
176      *
177      * @method detach
178      * @param handle {string} the subscription handle
179      * @static
180      */
181     detach: function(handle) {
182         if (handle.detach) {
183             handle.detach();
184         }
185     }
188 Y.Do = DO;
190 //////////////////////////////////////////////////////////////////////////
193  * Contains the return value from the wrapped method, accessible
194  * by 'after' event listeners.
196  * @property originalRetVal
197  * @static
198  * @since 3.2.0
199  */
202  * Contains the current state of the return value, consumable by
203  * 'after' event listeners, and updated if an after subscriber
204  * changes the return value generated by the wrapped function.
206  * @property currentRetVal
207  * @static
208  * @since 3.2.0
209  */
211 //////////////////////////////////////////////////////////////////////////
214  * Wrapper for a displaced method with aop enabled
215  * @class Do.Method
216  * @constructor
217  * @param obj The object to operate on
218  * @param sFn The name of the method to displace
219  */
220 DO.Method = function(obj, sFn) {
221     this.obj = obj;
222     this.methodName = sFn;
223     this.method = obj[sFn];
224     this.before = {};
225     this.after = {};
229  * Register a aop subscriber
230  * @method register
231  * @param sid {string} the subscriber id
232  * @param fn {Function} the function to execute
233  * @param when {string} when to execute the function
234  */
235 DO.Method.prototype.register = function (sid, fn, when) {
236     if (when) {
237         this.after[sid] = fn;
238     } else {
239         this.before[sid] = fn;
240     }
244  * Unregister a aop subscriber
245  * @method delete
246  * @param sid {string} the subscriber id
247  * @param fn {Function} the function to execute
248  * @param when {string} when to execute the function
249  */
250 DO.Method.prototype._delete = function (sid) {
251     // Y.log('Y.Do._delete: ' + sid, 'info', 'Event');
252     delete this.before[sid];
253     delete this.after[sid];
257  * <p>Execute the wrapped method.  All arguments are passed into the wrapping
258  * functions.  If any of the before wrappers return an instance of
259  * <code>Y.Do.Halt</code> or <code>Y.Do.Prevent</code>, neither the wrapped
260  * function nor any after phase subscribers will be executed.</p>
262  * <p>The return value will be the return value of the wrapped function or one
263  * provided by a wrapper function via an instance of <code>Y.Do.Halt</code> or
264  * <code>Y.Do.AlterReturn</code>.
266  * @method exec
267  * @param arg* {any} Arguments are passed to the wrapping and wrapped functions
268  * @return {any} Return value of wrapped function unless overwritten (see above)
269  */
270 DO.Method.prototype.exec = function () {
272     var args = Y.Array(arguments, 0, true),
273         i, ret, newRet,
274         bf = this.before,
275         af = this.after,
276         prevented = false;
278     // execute before
279     for (i in bf) {
280         if (bf.hasOwnProperty(i)) {
281             ret = bf[i].apply(this.obj, args);
282             if (ret) {
283                 switch (ret.constructor) {
284                     case DO.Halt:
285                         return ret.retVal;
286                     case DO.AlterArgs:
287                         args = ret.newArgs;
288                         break;
289                     case DO.Prevent:
290                         prevented = true;
291                         break;
292                     default:
293                 }
294             }
295         }
296     }
298     // execute method
299     if (!prevented) {
300         ret = this.method.apply(this.obj, args);
301     }
303     DO.originalRetVal = ret;
304     DO.currentRetVal = ret;
306     // execute after methods.
307     for (i in af) {
308         if (af.hasOwnProperty(i)) {
309             newRet = af[i].apply(this.obj, args);
310             // Stop processing if a Halt object is returned
311             if (newRet && newRet.constructor === DO.Halt) {
312                 return newRet.retVal;
313             // Check for a new return value
314             } else if (newRet && newRet.constructor === DO.AlterReturn) {
315                 ret = newRet.newRetVal;
316                 // Update the static retval state
317                 DO.currentRetVal = ret;
318             }
319         }
320     }
322     return ret;
325 //////////////////////////////////////////////////////////////////////////
328  * Return an AlterArgs object when you want to change the arguments that
329  * were passed into the function.  Useful for Do.before subscribers.  An
330  * example would be a service that scrubs out illegal characters prior to
331  * executing the core business logic.
332  * @class Do.AlterArgs
333  * @constructor
334  * @param msg {String} (optional) Explanation of the altered return value
335  * @param newArgs {Array} Call parameters to be used for the original method
336  *                        instead of the arguments originally passed in.
337  */
338 DO.AlterArgs = function(msg, newArgs) {
339     this.msg = msg;
340     this.newArgs = newArgs;
344  * Return an AlterReturn object when you want to change the result returned
345  * from the core method to the caller.  Useful for Do.after subscribers.
346  * @class Do.AlterReturn
347  * @constructor
348  * @param msg {String} (optional) Explanation of the altered return value
349  * @param newRetVal {any} Return value passed to code that invoked the wrapped
350  *                      function.
351  */
352 DO.AlterReturn = function(msg, newRetVal) {
353     this.msg = msg;
354     this.newRetVal = newRetVal;
358  * Return a Halt object when you want to terminate the execution
359  * of all subsequent subscribers as well as the wrapped method
360  * if it has not exectued yet.  Useful for Do.before subscribers.
361  * @class Do.Halt
362  * @constructor
363  * @param msg {String} (optional) Explanation of why the termination was done
364  * @param retVal {any} Return value passed to code that invoked the wrapped
365  *                      function.
366  */
367 DO.Halt = function(msg, retVal) {
368     this.msg = msg;
369     this.retVal = retVal;
373  * Return a Prevent object when you want to prevent the wrapped function
374  * from executing, but want the remaining listeners to execute.  Useful
375  * for Do.before subscribers.
376  * @class Do.Prevent
377  * @constructor
378  * @param msg {String} (optional) Explanation of why the termination was done
379  */
380 DO.Prevent = function(msg) {
381     this.msg = msg;
385  * Return an Error object when you want to terminate the execution
386  * of all subsequent method calls.
387  * @class Do.Error
388  * @constructor
389  * @param msg {String} (optional) Explanation of the altered return value
390  * @param retVal {any} Return value passed to code that invoked the wrapped
391  *                      function.
392  * @deprecated use Y.Do.Halt or Y.Do.Prevent
393  */
394 DO.Error = DO.Halt;
397 //////////////////////////////////////////////////////////////////////////
400  * Custom event engine, DOM event listener abstraction layer, synthetic DOM
401  * events.
402  * @module event-custom
403  * @submodule event-custom-base
404  */
407 // var onsubscribeType = "_event:onsub",
408 var YArray = Y.Array,
410     AFTER = 'after',
411     CONFIGS = [
412         'broadcast',
413         'monitored',
414         'bubbles',
415         'context',
416         'contextFn',
417         'currentTarget',
418         'defaultFn',
419         'defaultTargetOnly',
420         'details',
421         'emitFacade',
422         'fireOnce',
423         'async',
424         'host',
425         'preventable',
426         'preventedFn',
427         'queuable',
428         'silent',
429         'stoppedFn',
430         'target',
431         'type'
432     ],
434     CONFIGS_HASH = YArray.hash(CONFIGS),
436     nativeSlice = Array.prototype.slice,
438     YUI3_SIGNATURE = 9,
439     YUI_LOG = 'yui:log',
441     mixConfigs = function(r, s, ov) {
442         var p;
444         for (p in s) {
445             if (CONFIGS_HASH[p] && (ov || !(p in r))) {
446                 r[p] = s[p];
447             }
448         }
450         return r;
451     };
454  * The CustomEvent class lets you define events for your application
455  * that can be subscribed to by one or more independent component.
457  * @param {String} type The type of event, which is passed to the callback
458  * when the event fires.
459  * @param {object} defaults configuration object.
460  * @class CustomEvent
461  * @constructor
462  */
464  /**
465  * The type of event, returned to subscribers when the event fires
466  * @property type
467  * @type string
468  */
471  * By default all custom events are logged in the debug build, set silent
472  * to true to disable debug outpu for this event.
473  * @property silent
474  * @type boolean
475  */
477 Y.CustomEvent = function(type, defaults) {
479     this._kds = Y.CustomEvent.keepDeprecatedSubs;
481     this.id = Y.guid();
483     this.type = type;
484     this.silent = this.logSystem = (type === YUI_LOG);
486     if (this._kds) {
487         /**
488          * The subscribers to this event
489          * @property subscribers
490          * @type Subscriber {}
491          * @deprecated
492          */
494         /**
495          * 'After' subscribers
496          * @property afters
497          * @type Subscriber {}
498          * @deprecated
499          */
500         this.subscribers = {};
501         this.afters = {};
502     }
504     if (defaults) {
505         mixConfigs(this, defaults, true);
506     }
510  * Static flag to enable population of the <a href="#property_subscribers">`subscribers`</a>
511  * and  <a href="#property_subscribers">`afters`</a> properties held on a `CustomEvent` instance.
513  * These properties were changed to private properties (`_subscribers` and `_afters`), and
514  * converted from objects to arrays for performance reasons.
516  * Setting this property to true will populate the deprecated `subscribers` and `afters`
517  * properties for people who may be using them (which is expected to be rare). There will
518  * be a performance hit, compared to the new array based implementation.
520  * If you are using these deprecated properties for a use case which the public API
521  * does not support, please file an enhancement request, and we can provide an alternate
522  * public implementation which doesn't have the performance cost required to maintiain the
523  * properties as objects.
525  * @property keepDeprecatedSubs
526  * @static
527  * @for CustomEvent
528  * @type boolean
529  * @default false
530  * @deprecated
531  */
532 Y.CustomEvent.keepDeprecatedSubs = false;
534 Y.CustomEvent.mixConfigs = mixConfigs;
536 Y.CustomEvent.prototype = {
538     constructor: Y.CustomEvent,
540     /**
541      * Monitor when an event is attached or detached.
542      *
543      * @property monitored
544      * @type boolean
545      */
547     /**
548      * If 0, this event does not broadcast.  If 1, the YUI instance is notified
549      * every time this event fires.  If 2, the YUI instance and the YUI global
550      * (if event is enabled on the global) are notified every time this event
551      * fires.
552      * @property broadcast
553      * @type int
554      */
556     /**
557      * Specifies whether this event should be queued when the host is actively
558      * processing an event.  This will effect exectution order of the callbacks
559      * for the various events.
560      * @property queuable
561      * @type boolean
562      * @default false
563      */
565     /**
566      * This event has fired if true
567      *
568      * @property fired
569      * @type boolean
570      * @default false;
571      */
573     /**
574      * An array containing the arguments the custom event
575      * was last fired with.
576      * @property firedWith
577      * @type Array
578      */
580     /**
581      * This event should only fire one time if true, and if
582      * it has fired, any new subscribers should be notified
583      * immediately.
584      *
585      * @property fireOnce
586      * @type boolean
587      * @default false;
588      */
590     /**
591      * fireOnce listeners will fire syncronously unless async
592      * is set to true
593      * @property async
594      * @type boolean
595      * @default false
596      */
598     /**
599      * Flag for stopPropagation that is modified during fire()
600      * 1 means to stop propagation to bubble targets.  2 means
601      * to also stop additional subscribers on this target.
602      * @property stopped
603      * @type int
604      */
606     /**
607      * Flag for preventDefault that is modified during fire().
608      * if it is not 0, the default behavior for this event
609      * @property prevented
610      * @type int
611      */
613     /**
614      * Specifies the host for this custom event.  This is used
615      * to enable event bubbling
616      * @property host
617      * @type EventTarget
618      */
620     /**
621      * The default function to execute after event listeners
622      * have fire, but only if the default action was not
623      * prevented.
624      * @property defaultFn
625      * @type Function
626      */
628     /**
629      * The function to execute if a subscriber calls
630      * stopPropagation or stopImmediatePropagation
631      * @property stoppedFn
632      * @type Function
633      */
635     /**
636      * The function to execute if a subscriber calls
637      * preventDefault
638      * @property preventedFn
639      * @type Function
640      */
642     /**
643      * The subscribers to this event
644      * @property _subscribers
645      * @type Subscriber []
646      * @private
647      */
649     /**
650      * 'After' subscribers
651      * @property _afters
652      * @type Subscriber []
653      * @private
654      */
656     /**
657      * If set to true, the custom event will deliver an EventFacade object
658      * that is similar to a DOM event object.
659      * @property emitFacade
660      * @type boolean
661      * @default false
662      */
664     /**
665      * Supports multiple options for listener signatures in order to
666      * port YUI 2 apps.
667      * @property signature
668      * @type int
669      * @default 9
670      */
671     signature : YUI3_SIGNATURE,
673     /**
674      * The context the the event will fire from by default.  Defaults to the YUI
675      * instance.
676      * @property context
677      * @type object
678      */
679     context : Y,
681     /**
682      * Specifies whether or not this event's default function
683      * can be cancelled by a subscriber by executing preventDefault()
684      * on the event facade
685      * @property preventable
686      * @type boolean
687      * @default true
688      */
689     preventable : true,
691     /**
692      * Specifies whether or not a subscriber can stop the event propagation
693      * via stopPropagation(), stopImmediatePropagation(), or halt()
694      *
695      * Events can only bubble if emitFacade is true.
696      *
697      * @property bubbles
698      * @type boolean
699      * @default true
700      */
701     bubbles : true,
703     /**
704      * Returns the number of subscribers for this event as the sum of the on()
705      * subscribers and after() subscribers.
706      *
707      * @method hasSubs
708      * @return Number
709      */
710     hasSubs: function(when) {
711         var s = 0,
712             a = 0,
713             subs = this._subscribers,
714             afters = this._afters,
715             sib = this.sibling;
717         if (subs) {
718             s = subs.length;
719         }
721         if (afters) {
722             a = afters.length;
723         }
725         if (sib) {
726             subs = sib._subscribers;
727             afters = sib._afters;
729             if (subs) {
730                 s += subs.length;
731             }
733             if (afters) {
734                 a += afters.length;
735             }
736         }
738         if (when) {
739             return (when === 'after') ? a : s;
740         }
742         return (s + a);
743     },
745     /**
746      * Monitor the event state for the subscribed event.  The first parameter
747      * is what should be monitored, the rest are the normal parameters when
748      * subscribing to an event.
749      * @method monitor
750      * @param what {string} what to monitor ('detach', 'attach', 'publish').
751      * @return {EventHandle} return value from the monitor event subscription.
752      */
753     monitor: function(what) {
754         this.monitored = true;
755         var type = this.id + '|' + this.type + '_' + what,
756             args = nativeSlice.call(arguments, 0);
757         args[0] = type;
758         return this.host.on.apply(this.host, args);
759     },
761     /**
762      * Get all of the subscribers to this event and any sibling event
763      * @method getSubs
764      * @return {Array} first item is the on subscribers, second the after.
765      */
766     getSubs: function() {
768         var sibling = this.sibling,
769             subs = this._subscribers,
770             afters = this._afters,
771             siblingSubs,
772             siblingAfters;
774         if (sibling) {
775             siblingSubs = sibling._subscribers;
776             siblingAfters = sibling._afters;
777         }
779         if (siblingSubs) {
780             if (subs) {
781                 subs = subs.concat(siblingSubs);
782             } else {
783                 subs = siblingSubs.concat();
784             }
785         } else {
786             if (subs) {
787                 subs = subs.concat();
788             } else {
789                 subs = [];
790             }
791         }
793         if (siblingAfters) {
794             if (afters) {
795                 afters = afters.concat(siblingAfters);
796             } else {
797                 afters = siblingAfters.concat();
798             }
799         } else {
800             if (afters) {
801                 afters = afters.concat();
802             } else {
803                 afters = [];
804             }
805         }
807         return [subs, afters];
808     },
810     /**
811      * Apply configuration properties.  Only applies the CONFIG whitelist
812      * @method applyConfig
813      * @param o hash of properties to apply.
814      * @param force {boolean} if true, properties that exist on the event
815      * will be overwritten.
816      */
817     applyConfig: function(o, force) {
818         mixConfigs(this, o, force);
819     },
821     /**
822      * Create the Subscription for subscribing function, context, and bound
823      * arguments.  If this is a fireOnce event, the subscriber is immediately
824      * notified.
825      *
826      * @method _on
827      * @param fn {Function} Subscription callback
828      * @param [context] {Object} Override `this` in the callback
829      * @param [args] {Array} bound arguments that will be passed to the callback after the arguments generated by fire()
830      * @param [when] {String} "after" to slot into after subscribers
831      * @return {EventHandle}
832      * @protected
833      */
834     _on: function(fn, context, args, when) {
836         if (!fn) { this.log('Invalid callback for CE: ' + this.type); }
838         var s = new Y.Subscriber(fn, context, args, when),
839             firedWith;
841         if (this.fireOnce && this.fired) {
843             firedWith = this.firedWith;
845             // It's a little ugly for this to know about facades,
846             // but given the current breakup, not much choice without
847             // moving a whole lot of stuff around.
848             if (this.emitFacade && this._addFacadeToArgs) {
849                 this._addFacadeToArgs(firedWith);
850             }
852             if (this.async) {
853                 setTimeout(Y.bind(this._notify, this, s, firedWith), 0);
854             } else {
855                 this._notify(s, firedWith);
856             }
857         }
859         if (when === AFTER) {
860             if (!this._afters) {
861                 this._afters = [];
862             }
863             this._afters.push(s);
864         } else {
865             if (!this._subscribers) {
866                 this._subscribers = [];
867             }
868             this._subscribers.push(s);
869         }
871         if (this._kds) {
872             if (when === AFTER) {
873                 this.afters[s.id] = s;
874             } else {
875                 this.subscribers[s.id] = s;
876             }
877         }
879         return new Y.EventHandle(this, s);
880     },
882     /**
883      * Listen for this event
884      * @method subscribe
885      * @param {Function} fn The function to execute.
886      * @return {EventHandle} Unsubscribe handle.
887      * @deprecated use on.
888      */
889     subscribe: function(fn, context) {
890         Y.log('ce.subscribe deprecated, use "on"', 'warn', 'deprecated');
891         var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null;
892         return this._on(fn, context, a, true);
893     },
895     /**
896      * Listen for this event
897      * @method on
898      * @param {Function} fn The function to execute.
899      * @param {object} context optional execution context.
900      * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
901      * when the event fires.
902      * @return {EventHandle} An object with a detach method to detch the handler(s).
903      */
904     on: function(fn, context) {
905         var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null;
907         if (this.monitored && this.host) {
908             this.host._monitor('attach', this, {
909                 args: arguments
910             });
911         }
912         return this._on(fn, context, a, true);
913     },
915     /**
916      * Listen for this event after the normal subscribers have been notified and
917      * the default behavior has been applied.  If a normal subscriber prevents the
918      * default behavior, it also prevents after listeners from firing.
919      * @method after
920      * @param {Function} fn The function to execute.
921      * @param {object} context optional execution context.
922      * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
923      * when the event fires.
924      * @return {EventHandle} handle Unsubscribe handle.
925      */
926     after: function(fn, context) {
927         var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null;
928         return this._on(fn, context, a, AFTER);
929     },
931     /**
932      * Detach listeners.
933      * @method detach
934      * @param {Function} fn  The subscribed function to remove, if not supplied
935      *                       all will be removed.
936      * @param {Object}   context The context object passed to subscribe.
937      * @return {int} returns the number of subscribers unsubscribed.
938      */
939     detach: function(fn, context) {
940         // unsubscribe handle
941         if (fn && fn.detach) {
942             return fn.detach();
943         }
945         var i, s,
946             found = 0,
947             subs = this._subscribers,
948             afters = this._afters;
950         if (subs) {
951             for (i = subs.length; i >= 0; i--) {
952                 s = subs[i];
953                 if (s && (!fn || fn === s.fn)) {
954                     this._delete(s, subs, i);
955                     found++;
956                 }
957             }
958         }
960         if (afters) {
961             for (i = afters.length; i >= 0; i--) {
962                 s = afters[i];
963                 if (s && (!fn || fn === s.fn)) {
964                     this._delete(s, afters, i);
965                     found++;
966                 }
967             }
968         }
970         return found;
971     },
973     /**
974      * Detach listeners.
975      * @method unsubscribe
976      * @param {Function} fn  The subscribed function to remove, if not supplied
977      *                       all will be removed.
978      * @param {Object}   context The context object passed to subscribe.
979      * @return {int|undefined} returns the number of subscribers unsubscribed.
980      * @deprecated use detach.
981      */
982     unsubscribe: function() {
983         return this.detach.apply(this, arguments);
984     },
986     /**
987      * Notify a single subscriber
988      * @method _notify
989      * @param {Subscriber} s the subscriber.
990      * @param {Array} args the arguments array to apply to the listener.
991      * @protected
992      */
993     _notify: function(s, args, ef) {
995         this.log(this.type + '->' + 'sub: ' + s.id);
997         var ret;
999         ret = s.notify(args, this);
1001         if (false === ret || this.stopped > 1) {
1002             this.log(this.type + ' cancelled by subscriber');
1003             return false;
1004         }
1006         return true;
1007     },
1009     /**
1010      * Logger abstraction to centralize the application of the silent flag
1011      * @method log
1012      * @param {string} msg message to log.
1013      * @param {string} cat log category.
1014      */
1015     log: function(msg, cat) {
1016         if (!this.silent) { Y.log(this.id + ': ' + msg, cat || 'info', 'event'); }
1017     },
1019     /**
1020      * Notifies the subscribers.  The callback functions will be executed
1021      * from the context specified when the event was created, and with the
1022      * following parameters:
1023      *   <ul>
1024      *   <li>The type of event</li>
1025      *   <li>All of the arguments fire() was executed with as an array</li>
1026      *   <li>The custom object (if any) that was passed into the subscribe()
1027      *       method</li>
1028      *   </ul>
1029      * @method fire
1030      * @param {Object*} arguments an arbitrary set of parameters to pass to
1031      *                            the handler.
1032      * @return {boolean} false if one of the subscribers returned false,
1033      *                   true otherwise.
1034      *
1035      */
1036     fire: function() {
1038         // push is the fastest way to go from arguments to arrays
1039         // for most browsers currently
1040         // http://jsperf.com/push-vs-concat-vs-slice/2
1042         var args = [];
1043         args.push.apply(args, arguments);
1045         return this._fire(args);
1046     },
1048     /**
1049      * Private internal implementation for `fire`, which is can be used directly by
1050      * `EventTarget` and other event module classes which have already converted from
1051      * an `arguments` list to an array, to avoid the repeated overhead.
1052      *
1053      * @method _fire
1054      * @private
1055      * @param {Array} args The array of arguments passed to be passed to handlers.
1056      * @return {boolean} false if one of the subscribers returned false, true otherwise.
1057      */
1058     _fire: function(args) {
1060         if (this.fireOnce && this.fired) {
1061             this.log('fireOnce event: ' + this.type + ' already fired');
1062             return true;
1063         } else {
1065             // this doesn't happen if the event isn't published
1066             // this.host._monitor('fire', this.type, args);
1068             this.fired = true;
1070             if (this.fireOnce) {
1071                 this.firedWith = args;
1072             }
1074             if (this.emitFacade) {
1075                 return this.fireComplex(args);
1076             } else {
1077                 return this.fireSimple(args);
1078             }
1079         }
1080     },
1082     /**
1083      * Set up for notifying subscribers of non-emitFacade events.
1084      *
1085      * @method fireSimple
1086      * @param args {Array} Arguments passed to fire()
1087      * @return Boolean false if a subscriber returned false
1088      * @protected
1089      */
1090     fireSimple: function(args) {
1091         this.stopped = 0;
1092         this.prevented = 0;
1093         if (this.hasSubs()) {
1094             var subs = this.getSubs();
1095             this._procSubs(subs[0], args);
1096             this._procSubs(subs[1], args);
1097         }
1098         if (this.broadcast) {
1099             this._broadcast(args);
1100         }
1101         return this.stopped ? false : true;
1102     },
1104     // Requires the event-custom-complex module for full funcitonality.
1105     fireComplex: function(args) {
1106         this.log('Missing event-custom-complex needed to emit a facade for: ' + this.type);
1107         args[0] = args[0] || {};
1108         return this.fireSimple(args);
1109     },
1111     /**
1112      * Notifies a list of subscribers.
1113      *
1114      * @method _procSubs
1115      * @param subs {Array} List of subscribers
1116      * @param args {Array} Arguments passed to fire()
1117      * @param ef {}
1118      * @return Boolean false if a subscriber returns false or stops the event
1119      *              propagation via e.stopPropagation(),
1120      *              e.stopImmediatePropagation(), or e.halt()
1121      * @private
1122      */
1123     _procSubs: function(subs, args, ef) {
1124         var s, i, l;
1126         for (i = 0, l = subs.length; i < l; i++) {
1127             s = subs[i];
1128             if (s && s.fn) {
1129                 if (false === this._notify(s, args, ef)) {
1130                     this.stopped = 2;
1131                 }
1132                 if (this.stopped === 2) {
1133                     return false;
1134                 }
1135             }
1136         }
1138         return true;
1139     },
1141     /**
1142      * Notifies the YUI instance if the event is configured with broadcast = 1,
1143      * and both the YUI instance and Y.Global if configured with broadcast = 2.
1144      *
1145      * @method _broadcast
1146      * @param args {Array} Arguments sent to fire()
1147      * @private
1148      */
1149     _broadcast: function(args) {
1150         if (!this.stopped && this.broadcast) {
1152             var a = args.concat();
1153             a.unshift(this.type);
1155             if (this.host !== Y) {
1156                 Y.fire.apply(Y, a);
1157             }
1159             if (this.broadcast === 2) {
1160                 Y.Global.fire.apply(Y.Global, a);
1161             }
1162         }
1163     },
1165     /**
1166      * Removes all listeners
1167      * @method unsubscribeAll
1168      * @return {int} The number of listeners unsubscribed.
1169      * @deprecated use detachAll.
1170      */
1171     unsubscribeAll: function() {
1172         return this.detachAll.apply(this, arguments);
1173     },
1175     /**
1176      * Removes all listeners
1177      * @method detachAll
1178      * @return {int} The number of listeners unsubscribed.
1179      */
1180     detachAll: function() {
1181         return this.detach();
1182     },
1184     /**
1185      * Deletes the subscriber from the internal store of on() and after()
1186      * subscribers.
1187      *
1188      * @method _delete
1189      * @param s subscriber object.
1190      * @param subs (optional) on or after subscriber array
1191      * @param index (optional) The index found.
1192      * @private
1193      */
1194     _delete: function(s, subs, i) {
1195         var when = s._when;
1197         if (!subs) {
1198             subs = (when === AFTER) ? this._afters : this._subscribers;
1199         }
1201         if (subs) {
1202             i = YArray.indexOf(subs, s, 0);
1204             if (s && subs[i] === s) {
1205                 subs.splice(i, 1);
1206             }
1207         }
1209         if (this._kds) {
1210             if (when === AFTER) {
1211                 delete this.afters[s.id];
1212             } else {
1213                 delete this.subscribers[s.id];
1214             }
1215         }
1217         if (this.monitored && this.host) {
1218             this.host._monitor('detach', this, {
1219                 ce: this,
1220                 sub: s
1221             });
1222         }
1224         if (s) {
1225             s.deleted = true;
1226         }
1227     }
1230  * Stores the subscriber information to be used when the event fires.
1231  * @param {Function} fn       The wrapped function to execute.
1232  * @param {Object}   context  The value of the keyword 'this' in the listener.
1233  * @param {Array} args*       0..n additional arguments to supply the listener.
1235  * @class Subscriber
1236  * @constructor
1237  */
1238 Y.Subscriber = function(fn, context, args, when) {
1240     /**
1241      * The callback that will be execute when the event fires
1242      * This is wrapped by Y.rbind if obj was supplied.
1243      * @property fn
1244      * @type Function
1245      */
1246     this.fn = fn;
1248     /**
1249      * Optional 'this' keyword for the listener
1250      * @property context
1251      * @type Object
1252      */
1253     this.context = context;
1255     /**
1256      * Unique subscriber id
1257      * @property id
1258      * @type String
1259      */
1260     this.id = Y.guid();
1262     /**
1263      * Additional arguments to propagate to the subscriber
1264      * @property args
1265      * @type Array
1266      */
1267     this.args = args;
1269     this._when = when;
1271     /**
1272      * Custom events for a given fire transaction.
1273      * @property events
1274      * @type {EventTarget}
1275      */
1276     // this.events = null;
1278     /**
1279      * This listener only reacts to the event once
1280      * @property once
1281      */
1282     // this.once = false;
1286 Y.Subscriber.prototype = {
1287     constructor: Y.Subscriber,
1289     _notify: function(c, args, ce) {
1290         if (this.deleted && !this.postponed) {
1291             if (this.postponed) {
1292                 delete this.fn;
1293                 delete this.context;
1294             } else {
1295                 delete this.postponed;
1296                 return null;
1297             }
1298         }
1299         var a = this.args, ret;
1300         switch (ce.signature) {
1301             case 0:
1302                 ret = this.fn.call(c, ce.type, args, c);
1303                 break;
1304             case 1:
1305                 ret = this.fn.call(c, args[0] || null, c);
1306                 break;
1307             default:
1308                 if (a || args) {
1309                     args = args || [];
1310                     a = (a) ? args.concat(a) : args;
1311                     ret = this.fn.apply(c, a);
1312                 } else {
1313                     ret = this.fn.call(c);
1314                 }
1315         }
1317         if (this.once) {
1318             ce._delete(this);
1319         }
1321         return ret;
1322     },
1324     /**
1325      * Executes the subscriber.
1326      * @method notify
1327      * @param args {Array} Arguments array for the subscriber.
1328      * @param ce {CustomEvent} The custom event that sent the notification.
1329      */
1330     notify: function(args, ce) {
1331         var c = this.context,
1332             ret = true;
1334         if (!c) {
1335             c = (ce.contextFn) ? ce.contextFn() : ce.context;
1336         }
1338         // only catch errors if we will not re-throw them.
1339         if (Y.config && Y.config.throwFail) {
1340             ret = this._notify(c, args, ce);
1341         } else {
1342             try {
1343                 ret = this._notify(c, args, ce);
1344             } catch (e) {
1345                 Y.error(this + ' failed: ' + e.message, e);
1346             }
1347         }
1349         return ret;
1350     },
1352     /**
1353      * Returns true if the fn and obj match this objects properties.
1354      * Used by the unsubscribe method to match the right subscriber.
1355      *
1356      * @method contains
1357      * @param {Function} fn the function to execute.
1358      * @param {Object} context optional 'this' keyword for the listener.
1359      * @return {boolean} true if the supplied arguments match this
1360      *                   subscriber's signature.
1361      */
1362     contains: function(fn, context) {
1363         if (context) {
1364             return ((this.fn === fn) && this.context === context);
1365         } else {
1366             return (this.fn === fn);
1367         }
1368     },
1370     valueOf : function() {
1371         return this.id;
1372     }
1376  * Return value from all subscribe operations
1377  * @class EventHandle
1378  * @constructor
1379  * @param {CustomEvent} evt the custom event.
1380  * @param {Subscriber} sub the subscriber.
1381  */
1382 Y.EventHandle = function(evt, sub) {
1384     /**
1385      * The custom event
1386      *
1387      * @property evt
1388      * @type CustomEvent
1389      */
1390     this.evt = evt;
1392     /**
1393      * The subscriber object
1394      *
1395      * @property sub
1396      * @type Subscriber
1397      */
1398     this.sub = sub;
1401 Y.EventHandle.prototype = {
1402     batch: function(f, c) {
1403         f.call(c || this, this);
1404         if (Y.Lang.isArray(this.evt)) {
1405             Y.Array.each(this.evt, function(h) {
1406                 h.batch.call(c || h, f);
1407             });
1408         }
1409     },
1411     /**
1412      * Detaches this subscriber
1413      * @method detach
1414      * @return {int} the number of detached listeners
1415      */
1416     detach: function() {
1417         var evt = this.evt, detached = 0, i;
1418         if (evt) {
1419             // Y.log('EventHandle.detach: ' + this.sub, 'info', 'Event');
1420             if (Y.Lang.isArray(evt)) {
1421                 for (i = 0; i < evt.length; i++) {
1422                     detached += evt[i].detach();
1423                 }
1424             } else {
1425                 evt._delete(this.sub);
1426                 detached = 1;
1427             }
1429         }
1431         return detached;
1432     },
1434     /**
1435      * Monitor the event state for the subscribed event.  The first parameter
1436      * is what should be monitored, the rest are the normal parameters when
1437      * subscribing to an event.
1438      * @method monitor
1439      * @param what {string} what to monitor ('attach', 'detach', 'publish').
1440      * @return {EventHandle} return value from the monitor event subscription.
1441      */
1442     monitor: function(what) {
1443         return this.evt.monitor.apply(this.evt, arguments);
1444     }
1448  * Custom event engine, DOM event listener abstraction layer, synthetic DOM
1449  * events.
1450  * @module event-custom
1451  * @submodule event-custom-base
1452  */
1455  * EventTarget provides the implementation for any object to
1456  * publish, subscribe and fire to custom events, and also
1457  * alows other EventTargets to target the object with events
1458  * sourced from the other object.
1459  * EventTarget is designed to be used with Y.augment to wrap
1460  * EventCustom in an interface that allows events to be listened to
1461  * and fired by name.  This makes it possible for implementing code to
1462  * subscribe to an event that either has not been created yet, or will
1463  * not be created at all.
1464  * @class EventTarget
1465  * @param opts a configuration object
1466  * @config emitFacade {boolean} if true, all events will emit event
1467  * facade payloads by default (default false)
1468  * @config prefix {String} the prefix to apply to non-prefixed event names
1469  */
1471 var L = Y.Lang,
1472     PREFIX_DELIMITER = ':',
1473     CATEGORY_DELIMITER = '|',
1474     AFTER_PREFIX = '~AFTER~',
1475     WILD_TYPE_RE = /(.*?)(:)(.*?)/,
1477     _wildType = Y.cached(function(type) {
1478         return type.replace(WILD_TYPE_RE, "*$2$3");
1479     }),
1481     /**
1482      * If the instance has a prefix attribute and the
1483      * event type is not prefixed, the instance prefix is
1484      * applied to the supplied type.
1485      * @method _getType
1486      * @private
1487      */
1488     _getType = function(type, pre) {
1490         if (!pre || !type || type.indexOf(PREFIX_DELIMITER) > -1) {
1491             return type;
1492         }
1494         return pre + PREFIX_DELIMITER + type;
1495     },
1497     /**
1498      * Returns an array with the detach key (if provided),
1499      * and the prefixed event name from _getType
1500      * Y.on('detachcategory| menu:click', fn)
1501      * @method _parseType
1502      * @private
1503      */
1504     _parseType = Y.cached(function(type, pre) {
1506         var t = type, detachcategory, after, i;
1508         if (!L.isString(t)) {
1509             return t;
1510         }
1512         i = t.indexOf(AFTER_PREFIX);
1514         if (i > -1) {
1515             after = true;
1516             t = t.substr(AFTER_PREFIX.length);
1517         }
1519         i = t.indexOf(CATEGORY_DELIMITER);
1521         if (i > -1) {
1522             detachcategory = t.substr(0, (i));
1523             t = t.substr(i+1);
1524             if (t === '*') {
1525                 t = null;
1526             }
1527         }
1529         // detach category, full type with instance prefix, is this an after listener, short type
1530         return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
1531     }),
1533     ET = function(opts) {
1535         var etState = this._yuievt,
1536             etConfig;
1538         if (!etState) {
1539             etState = this._yuievt = {
1540                 events: {},    // PERF: Not much point instantiating lazily. We're bound to have events
1541                 targets: null, // PERF: Instantiate lazily, if user actually adds target,
1542                 config: {
1543                     host: this,
1544                     context: this
1545                 },
1546                 chain: Y.config.chain
1547             };
1548         }
1550         etConfig = etState.config;
1552         if (opts) {
1553             mixConfigs(etConfig, opts, true);
1555             if (opts.chain !== undefined) {
1556                 etState.chain = opts.chain;
1557             }
1559             if (opts.prefix) {
1560                 etConfig.prefix = opts.prefix;
1561             }
1562         }
1563     };
1565 ET.prototype = {
1567     constructor: ET,
1569     /**
1570      * Listen to a custom event hosted by this object one time.
1571      * This is the equivalent to <code>on</code> except the
1572      * listener is immediatelly detached when it is executed.
1573      * @method once
1574      * @param {String} type The name of the event
1575      * @param {Function} fn The callback to execute in response to the event
1576      * @param {Object} [context] Override `this` object in callback
1577      * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
1578      * @return {EventHandle} A subscription handle capable of detaching the
1579      *                       subscription
1580      */
1581     once: function() {
1582         var handle = this.on.apply(this, arguments);
1583         handle.batch(function(hand) {
1584             if (hand.sub) {
1585                 hand.sub.once = true;
1586             }
1587         });
1588         return handle;
1589     },
1591     /**
1592      * Listen to a custom event hosted by this object one time.
1593      * This is the equivalent to <code>after</code> except the
1594      * listener is immediatelly detached when it is executed.
1595      * @method onceAfter
1596      * @param {String} type The name of the event
1597      * @param {Function} fn The callback to execute in response to the event
1598      * @param {Object} [context] Override `this` object in callback
1599      * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
1600      * @return {EventHandle} A subscription handle capable of detaching that
1601      *                       subscription
1602      */
1603     onceAfter: function() {
1604         var handle = this.after.apply(this, arguments);
1605         handle.batch(function(hand) {
1606             if (hand.sub) {
1607                 hand.sub.once = true;
1608             }
1609         });
1610         return handle;
1611     },
1613     /**
1614      * Takes the type parameter passed to 'on' and parses out the
1615      * various pieces that could be included in the type.  If the
1616      * event type is passed without a prefix, it will be expanded
1617      * to include the prefix one is supplied or the event target
1618      * is configured with a default prefix.
1619      * @method parseType
1620      * @param {String} type the type
1621      * @param {String} [pre=this._yuievt.config.prefix] the prefix
1622      * @since 3.3.0
1623      * @return {Array} an array containing:
1624      *  * the detach category, if supplied,
1625      *  * the prefixed event type,
1626      *  * whether or not this is an after listener,
1627      *  * the supplied event type
1628      */
1629     parseType: function(type, pre) {
1630         return _parseType(type, pre || this._yuievt.config.prefix);
1631     },
1633     /**
1634      * Subscribe a callback function to a custom event fired by this object or
1635      * from an object that bubbles its events to this object.
1636      *
1637      * Callback functions for events published with `emitFacade = true` will
1638      * receive an `EventFacade` as the first argument (typically named "e").
1639      * These callbacks can then call `e.preventDefault()` to disable the
1640      * behavior published to that event's `defaultFn`.  See the `EventFacade`
1641      * API for all available properties and methods. Subscribers to
1642      * non-`emitFacade` events will receive the arguments passed to `fire()`
1643      * after the event name.
1644      *
1645      * To subscribe to multiple events at once, pass an object as the first
1646      * argument, where the key:value pairs correspond to the eventName:callback,
1647      * or pass an array of event names as the first argument to subscribe to
1648      * all listed events with the same callback.
1649      *
1650      * Returning `false` from a callback is supported as an alternative to
1651      * calling `e.preventDefault(); e.stopPropagation();`.  However, it is
1652      * recommended to use the event methods whenever possible.
1653      *
1654      * @method on
1655      * @param {String} type The name of the event
1656      * @param {Function} fn The callback to execute in response to the event
1657      * @param {Object} [context] Override `this` object in callback
1658      * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
1659      * @return {EventHandle} A subscription handle capable of detaching that
1660      *                       subscription
1661      */
1662     on: function(type, fn, context) {
1664         var yuievt = this._yuievt,
1665             parts = _parseType(type, yuievt.config.prefix), f, c, args, ret, ce,
1666             detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
1667             Node = Y.Node, n, domevent, isArr;
1669         // full name, args, detachcategory, after
1670         this._monitor('attach', parts[1], {
1671             args: arguments,
1672             category: parts[0],
1673             after: parts[2]
1674         });
1676         if (L.isObject(type)) {
1678             if (L.isFunction(type)) {
1679                 return Y.Do.before.apply(Y.Do, arguments);
1680             }
1682             f = fn;
1683             c = context;
1684             args = nativeSlice.call(arguments, 0);
1685             ret = [];
1687             if (L.isArray(type)) {
1688                 isArr = true;
1689             }
1691             after = type._after;
1692             delete type._after;
1694             Y.each(type, function(v, k) {
1696                 if (L.isObject(v)) {
1697                     f = v.fn || ((L.isFunction(v)) ? v : f);
1698                     c = v.context || c;
1699                 }
1701                 var nv = (after) ? AFTER_PREFIX : '';
1703                 args[0] = nv + ((isArr) ? v : k);
1704                 args[1] = f;
1705                 args[2] = c;
1707                 ret.push(this.on.apply(this, args));
1709             }, this);
1711             return (yuievt.chain) ? this : new Y.EventHandle(ret);
1712         }
1714         detachcategory = parts[0];
1715         after = parts[2];
1716         shorttype = parts[3];
1718         // extra redirection so we catch adaptor events too.  take a look at this.
1719         if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) {
1720             args = nativeSlice.call(arguments, 0);
1721             args.splice(2, 0, Node.getDOMNode(this));
1722             // Y.log("Node detected, redirecting with these args: " + args);
1723             return Y.on.apply(Y, args);
1724         }
1726         type = parts[1];
1728         if (Y.instanceOf(this, YUI)) {
1730             adapt = Y.Env.evt.plugins[type];
1731             args  = nativeSlice.call(arguments, 0);
1732             args[0] = shorttype;
1734             if (Node) {
1735                 n = args[2];
1737                 if (Y.instanceOf(n, Y.NodeList)) {
1738                     n = Y.NodeList.getDOMNodes(n);
1739                 } else if (Y.instanceOf(n, Node)) {
1740                     n = Node.getDOMNode(n);
1741                 }
1743                 domevent = (shorttype in Node.DOM_EVENTS);
1745                 // Captures both DOM events and event plugins.
1746                 if (domevent) {
1747                     args[2] = n;
1748                 }
1749             }
1751             // check for the existance of an event adaptor
1752             if (adapt) {
1753                 Y.log('Using adaptor for ' + shorttype + ', ' + n, 'info', 'event');
1754                 handle = adapt.on.apply(Y, args);
1755             } else if ((!type) || domevent) {
1756                 handle = Y.Event._attach(args);
1757             }
1759         }
1761         if (!handle) {
1762             ce = yuievt.events[type] || this.publish(type);
1763             handle = ce._on(fn, context, (arguments.length > 3) ? nativeSlice.call(arguments, 3) : null, (after) ? 'after' : true);
1765             // TODO: More robust regex, accounting for category
1766             if (type.indexOf("*:") !== -1) {
1767                 this._hasSiblings = true;
1768             }
1769         }
1771         if (detachcategory) {
1772             store[detachcategory] = store[detachcategory] || {};
1773             store[detachcategory][type] = store[detachcategory][type] || [];
1774             store[detachcategory][type].push(handle);
1775         }
1777         return (yuievt.chain) ? this : handle;
1779     },
1781     /**
1782      * subscribe to an event
1783      * @method subscribe
1784      * @deprecated use on
1785      */
1786     subscribe: function() {
1787         Y.log('EventTarget subscribe() is deprecated, use on()', 'warn', 'deprecated');
1788         return this.on.apply(this, arguments);
1789     },
1791     /**
1792      * Detach one or more listeners the from the specified event
1793      * @method detach
1794      * @param type {string|Object}   Either the handle to the subscriber or the
1795      *                        type of event.  If the type
1796      *                        is not specified, it will attempt to remove
1797      *                        the listener from all hosted events.
1798      * @param fn   {Function} The subscribed function to unsubscribe, if not
1799      *                          supplied, all subscribers will be removed.
1800      * @param context  {Object}   The custom object passed to subscribe.  This is
1801      *                        optional, but if supplied will be used to
1802      *                        disambiguate multiple listeners that are the same
1803      *                        (e.g., you subscribe many object using a function
1804      *                        that lives on the prototype)
1805      * @return {EventTarget} the host
1806      */
1807     detach: function(type, fn, context) {
1809         var evts = this._yuievt.events,
1810             i,
1811             Node = Y.Node,
1812             isNode = Node && (Y.instanceOf(this, Node));
1814         // detachAll disabled on the Y instance.
1815         if (!type && (this !== Y)) {
1816             for (i in evts) {
1817                 if (evts.hasOwnProperty(i)) {
1818                     evts[i].detach(fn, context);
1819                 }
1820             }
1821             if (isNode) {
1822                 Y.Event.purgeElement(Node.getDOMNode(this));
1823             }
1825             return this;
1826         }
1828         var parts = _parseType(type, this._yuievt.config.prefix),
1829         detachcategory = L.isArray(parts) ? parts[0] : null,
1830         shorttype = (parts) ? parts[3] : null,
1831         adapt, store = Y.Env.evt.handles, detachhost, cat, args,
1832         ce,
1834         keyDetacher = function(lcat, ltype, host) {
1835             var handles = lcat[ltype], ce, i;
1836             if (handles) {
1837                 for (i = handles.length - 1; i >= 0; --i) {
1838                     ce = handles[i].evt;
1839                     if (ce.host === host || ce.el === host) {
1840                         handles[i].detach();
1841                     }
1842                 }
1843             }
1844         };
1846         if (detachcategory) {
1848             cat = store[detachcategory];
1849             type = parts[1];
1850             detachhost = (isNode) ? Y.Node.getDOMNode(this) : this;
1852             if (cat) {
1853                 if (type) {
1854                     keyDetacher(cat, type, detachhost);
1855                 } else {
1856                     for (i in cat) {
1857                         if (cat.hasOwnProperty(i)) {
1858                             keyDetacher(cat, i, detachhost);
1859                         }
1860                     }
1861                 }
1863                 return this;
1864             }
1866         // If this is an event handle, use it to detach
1867         } else if (L.isObject(type) && type.detach) {
1868             type.detach();
1869             return this;
1870         // extra redirection so we catch adaptor events too.  take a look at this.
1871         } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
1872             args = nativeSlice.call(arguments, 0);
1873             args[2] = Node.getDOMNode(this);
1874             Y.detach.apply(Y, args);
1875             return this;
1876         }
1878         adapt = Y.Env.evt.plugins[shorttype];
1880         // The YUI instance handles DOM events and adaptors
1881         if (Y.instanceOf(this, YUI)) {
1882             args = nativeSlice.call(arguments, 0);
1883             // use the adaptor specific detach code if
1884             if (adapt && adapt.detach) {
1885                 adapt.detach.apply(Y, args);
1886                 return this;
1887             // DOM event fork
1888             } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
1889                 args[0] = type;
1890                 Y.Event.detach.apply(Y.Event, args);
1891                 return this;
1892             }
1893         }
1895         // ce = evts[type];
1896         ce = evts[parts[1]];
1897         if (ce) {
1898             ce.detach(fn, context);
1899         }
1901         return this;
1902     },
1904     /**
1905      * detach a listener
1906      * @method unsubscribe
1907      * @deprecated use detach
1908      */
1909     unsubscribe: function() {
1910 Y.log('EventTarget unsubscribe() is deprecated, use detach()', 'warn', 'deprecated');
1911         return this.detach.apply(this, arguments);
1912     },
1914     /**
1915      * Removes all listeners from the specified event.  If the event type
1916      * is not specified, all listeners from all hosted custom events will
1917      * be removed.
1918      * @method detachAll
1919      * @param type {String}   The type, or name of the event
1920      */
1921     detachAll: function(type) {
1922         return this.detach(type);
1923     },
1925     /**
1926      * Removes all listeners from the specified event.  If the event type
1927      * is not specified, all listeners from all hosted custom events will
1928      * be removed.
1929      * @method unsubscribeAll
1930      * @param type {String}   The type, or name of the event
1931      * @deprecated use detachAll
1932      */
1933     unsubscribeAll: function() {
1934 Y.log('EventTarget unsubscribeAll() is deprecated, use detachAll()', 'warn', 'deprecated');
1935         return this.detachAll.apply(this, arguments);
1936     },
1938     /**
1939      * Creates a new custom event of the specified type.  If a custom event
1940      * by that name already exists, it will not be re-created.  In either
1941      * case the custom event is returned.
1942      *
1943      * @method publish
1944      *
1945      * @param type {String} the type, or name of the event
1946      * @param opts {object} optional config params.  Valid properties are:
1947      *
1948      *  <ul>
1949      *    <li>
1950      *   'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
1951      *    </li>
1952      *    <li>
1953      *   'bubbles': whether or not this event bubbles (true)
1954      *              Events can only bubble if emitFacade is true.
1955      *    </li>
1956      *    <li>
1957      *   'context': the default execution context for the listeners (this)
1958      *    </li>
1959      *    <li>
1960      *   'defaultFn': the default function to execute when this event fires if preventDefault was not called
1961      *    </li>
1962      *    <li>
1963      *   'emitFacade': whether or not this event emits a facade (false)
1964      *    </li>
1965      *    <li>
1966      *   'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click'
1967      *    </li>
1968      *    <li>
1969      *   'fireOnce': if an event is configured to fire once, new subscribers after
1970      *   the fire will be notified immediately.
1971      *    </li>
1972      *    <li>
1973      *   'async': fireOnce event listeners will fire synchronously if the event has already
1974      *    fired unless async is true.
1975      *    </li>
1976      *    <li>
1977      *   'preventable': whether or not preventDefault() has an effect (true)
1978      *    </li>
1979      *    <li>
1980      *   'preventedFn': a function that is executed when preventDefault is called
1981      *    </li>
1982      *    <li>
1983      *   'queuable': whether or not this event can be queued during bubbling (false)
1984      *    </li>
1985      *    <li>
1986      *   'silent': if silent is true, debug messages are not provided for this event.
1987      *    </li>
1988      *    <li>
1989      *   'stoppedFn': a function that is executed when stopPropagation is called
1990      *    </li>
1991      *
1992      *    <li>
1993      *   'monitored': specifies whether or not this event should send notifications about
1994      *   when the event has been attached, detached, or published.
1995      *    </li>
1996      *    <li>
1997      *   'type': the event type (valid option if not provided as the first parameter to publish)
1998      *    </li>
1999      *  </ul>
2000      *
2001      *  @return {CustomEvent} the custom event
2002      *
2003      */
2004     publish: function(type, opts) {
2006         var ret,
2007             etState = this._yuievt,
2008             etConfig = etState.config,
2009             pre = etConfig.prefix;
2011         if (typeof type === "string")  {
2012             if (pre) {
2013                 type = _getType(type, pre);
2014             }
2015             ret = this._publish(type, etConfig, opts);
2016         } else {
2017             ret = {};
2019             Y.each(type, function(v, k) {
2020                 if (pre) {
2021                     k = _getType(k, pre);
2022                 }
2023                 ret[k] = this._publish(k, etConfig, v || opts);
2024             }, this);
2026         }
2028         return ret;
2029     },
2031     /**
2032      * Returns the fully qualified type, given a short type string.
2033      * That is, returns "foo:bar" when given "bar" if "foo" is the configured prefix.
2034      *
2035      * NOTE: This method, unlike _getType, does no checking of the value passed in, and
2036      * is designed to be used with the low level _publish() method, for critical path
2037      * implementations which need to fast-track publish for performance reasons.
2038      *
2039      * @method _getFullType
2040      * @private
2041      * @param {String} type The short type to prefix
2042      * @return {String} The prefixed type, if a prefix is set, otherwise the type passed in
2043      */
2044     _getFullType : function(type) {
2046         var pre = this._yuievt.config.prefix;
2048         if (pre) {
2049             return pre + PREFIX_DELIMITER + type;
2050         } else {
2051             return type;
2052         }
2053     },
2055     /**
2056      * The low level event publish implementation. It expects all the massaging to have been done
2057      * outside of this method. e.g. the `type` to `fullType` conversion. It's designed to be a fast
2058      * path publish, which can be used by critical code paths to improve performance.
2059      *
2060      * @method _publish
2061      * @private
2062      * @param {String} fullType The prefixed type of the event to publish.
2063      * @param {Object} etOpts The EventTarget specific configuration to mix into the published event.
2064      * @param {Object} ceOpts The publish specific configuration to mix into the published event.
2065      * @return {CustomEvent} The published event. If called without `etOpts` or `ceOpts`, this will
2066      * be the default `CustomEvent` instance, and can be configured independently.
2067      */
2068     _publish : function(fullType, etOpts, ceOpts) {
2070         var ce,
2071             etState = this._yuievt,
2072             etConfig = etState.config,
2073             host = etConfig.host,
2074             context = etConfig.context,
2075             events = etState.events;
2077         ce = events[fullType];
2079         // PERF: Hate to pull the check out of monitor, but trying to keep critical path tight.
2080         if ((etConfig.monitored && !ce) || (ce && ce.monitored)) {
2081             this._monitor('publish', fullType, {
2082                 args: arguments
2083             });
2084         }
2086         if (!ce) {
2087             // Publish event
2088             ce = events[fullType] = new Y.CustomEvent(fullType, etOpts);
2090             if (!etOpts) {
2091                 ce.host = host;
2092                 ce.context = context;
2093             }
2094         }
2096         if (ceOpts) {
2097             mixConfigs(ce, ceOpts, true);
2098         }
2100         return ce;
2101     },
2103     /**
2104      * This is the entry point for the event monitoring system.
2105      * You can monitor 'attach', 'detach', 'fire', and 'publish'.
2106      * When configured, these events generate an event.  click ->
2107      * click_attach, click_detach, click_publish -- these can
2108      * be subscribed to like other events to monitor the event
2109      * system.  Inividual published events can have monitoring
2110      * turned on or off (publish can't be turned off before it
2111      * it published) by setting the events 'monitor' config.
2112      *
2113      * @method _monitor
2114      * @param what {String} 'attach', 'detach', 'fire', or 'publish'
2115      * @param eventType {String|CustomEvent} The prefixed name of the event being monitored, or the CustomEvent object.
2116      * @param o {Object} Information about the event interaction, such as
2117      *                  fire() args, subscription category, publish config
2118      * @private
2119      */
2120     _monitor: function(what, eventType, o) {
2121         var monitorevt, ce, type;
2123         if (eventType) {
2124             if (typeof eventType === "string") {
2125                 type = eventType;
2126                 ce = this.getEvent(eventType, true);
2127             } else {
2128                 ce = eventType;
2129                 type = eventType.type;
2130             }
2132             if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) {
2133                 monitorevt = type + '_' + what;
2134                 o.monitored = what;
2135                 this.fire.call(this, monitorevt, o);
2136             }
2137         }
2138     },
2140     /**
2141      * Fire a custom event by name.  The callback functions will be executed
2142      * from the context specified when the event was created, and with the
2143      * following parameters.
2144      *
2145      * The first argument is the event type, and any additional arguments are
2146      * passed to the listeners as parameters.  If the first of these is an
2147      * object literal, and the event is configured to emit an event facade,
2148      * that object is mixed into the event facade and the facade is provided
2149      * in place of the original object.
2150      *
2151      * If the custom event object hasn't been created, then the event hasn't
2152      * been published and it has no subscribers.  For performance sake, we
2153      * immediate exit in this case.  This means the event won't bubble, so
2154      * if the intention is that a bubble target be notified, the event must
2155      * be published on this object first.
2156      *
2157      * @method fire
2158      * @param type {String|Object} The type of the event, or an object that contains
2159      * a 'type' property.
2160      * @param arguments {Object*} an arbitrary set of parameters to pass to
2161      * the handler.  If the first of these is an object literal and the event is
2162      * configured to emit an event facade, the event facade will replace that
2163      * parameter after the properties the object literal contains are copied to
2164      * the event facade.
2165      * @return {Boolean} True if the whole lifecycle of the event went through,
2166      * false if at any point the event propagation was halted.
2167      */
2168     fire: function(type) {
2170         var typeIncluded = (typeof type === "string"),
2171             argCount = arguments.length,
2172             t = type,
2173             yuievt = this._yuievt,
2174             etConfig = yuievt.config,
2175             pre = etConfig.prefix,
2176             ret,
2177             ce,
2178             ce2,
2179             args;
2181         if (typeIncluded && argCount <= 3) {
2183             // PERF: Try to avoid slice/iteration for the common signatures
2185             // Most common
2186             if (argCount === 2) {
2187                 args = [arguments[1]]; // fire("foo", {})
2188             } else if (argCount === 3) {
2189                 args = [arguments[1], arguments[2]]; // fire("foo", {}, opts)
2190             } else {
2191                 args = []; // fire("foo")
2192             }
2194         } else {
2195             args = nativeSlice.call(arguments, ((typeIncluded) ? 1 : 0));
2196         }
2198         if (!typeIncluded) {
2199             t = (type && type.type);
2200         }
2202         if (pre) {
2203             t = _getType(t, pre);
2204         }
2206         ce = yuievt.events[t];
2208         if (this._hasSiblings) {
2209             ce2 = this.getSibling(t, ce);
2211             if (ce2 && !ce) {
2212                 ce = this.publish(t);
2213             }
2214         }
2216         // PERF: trying to avoid function call, since this is a critical path
2217         if ((etConfig.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) {
2218             this._monitor('fire', (ce || t), {
2219                 args: args
2220             });
2221         }
2223         // this event has not been published or subscribed to
2224         if (!ce) {
2225             if (yuievt.hasTargets) {
2226                 return this.bubble({ type: t }, args, this);
2227             }
2229             // otherwise there is nothing to be done
2230             ret = true;
2231         } else {
2233             if (ce2) {
2234                 ce.sibling = ce2;
2235             }
2237             ret = ce._fire(args);
2238         }
2240         return (yuievt.chain) ? this : ret;
2241     },
2243     getSibling: function(type, ce) {
2244         var ce2;
2246         // delegate to *:type events if there are subscribers
2247         if (type.indexOf(PREFIX_DELIMITER) > -1) {
2248             type = _wildType(type);
2249             ce2 = this.getEvent(type, true);
2250             if (ce2) {
2251                 ce2.applyConfig(ce);
2252                 ce2.bubbles = false;
2253                 ce2.broadcast = 0;
2254             }
2255         }
2257         return ce2;
2258     },
2260     /**
2261      * Returns the custom event of the provided type has been created, a
2262      * falsy value otherwise
2263      * @method getEvent
2264      * @param type {String} the type, or name of the event
2265      * @param prefixed {String} if true, the type is prefixed already
2266      * @return {CustomEvent} the custom event or null
2267      */
2268     getEvent: function(type, prefixed) {
2269         var pre, e;
2271         if (!prefixed) {
2272             pre = this._yuievt.config.prefix;
2273             type = (pre) ? _getType(type, pre) : type;
2274         }
2275         e = this._yuievt.events;
2276         return e[type] || null;
2277     },
2279     /**
2280      * Subscribe to a custom event hosted by this object.  The
2281      * supplied callback will execute after any listeners add
2282      * via the subscribe method, and after the default function,
2283      * if configured for the event, has executed.
2284      *
2285      * @method after
2286      * @param {String} type The name of the event
2287      * @param {Function} fn The callback to execute in response to the event
2288      * @param {Object} [context] Override `this` object in callback
2289      * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
2290      * @return {EventHandle} A subscription handle capable of detaching the
2291      *                       subscription
2292      */
2293     after: function(type, fn) {
2295         var a = nativeSlice.call(arguments, 0);
2297         switch (L.type(type)) {
2298             case 'function':
2299                 return Y.Do.after.apply(Y.Do, arguments);
2300             case 'array':
2301             //     YArray.each(a[0], function(v) {
2302             //         v = AFTER_PREFIX + v;
2303             //     });
2304             //     break;
2305             case 'object':
2306                 a[0]._after = true;
2307                 break;
2308             default:
2309                 a[0] = AFTER_PREFIX + type;
2310         }
2312         return this.on.apply(this, a);
2314     },
2316     /**
2317      * Executes the callback before a DOM event, custom event
2318      * or method.  If the first argument is a function, it
2319      * is assumed the target is a method.  For DOM and custom
2320      * events, this is an alias for Y.on.
2321      *
2322      * For DOM and custom events:
2323      * type, callback, context, 0-n arguments
2324      *
2325      * For methods:
2326      * callback, object (method host), methodName, context, 0-n arguments
2327      *
2328      * @method before
2329      * @return detach handle
2330      */
2331     before: function() {
2332         return this.on.apply(this, arguments);
2333     }
2337 Y.EventTarget = ET;
2339 // make Y an event target
2340 Y.mix(Y, ET.prototype);
2341 ET.call(Y, { bubbles: false });
2343 YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
2346  * Hosts YUI page level events.  This is where events bubble to
2347  * when the broadcast config is set to 2.  This property is
2348  * only available if the custom event module is loaded.
2349  * @property Global
2350  * @type EventTarget
2351  * @for YUI
2352  */
2353 Y.Global = YUI.Env.globalEvents;
2355 // @TODO implement a global namespace function on Y.Global?
2358 `Y.on()` can do many things:
2360 <ul>
2361     <li>Subscribe to custom events `publish`ed and `fire`d from Y</li>
2362     <li>Subscribe to custom events `publish`ed with `broadcast` 1 or 2 and
2363         `fire`d from any object in the YUI instance sandbox</li>
2364     <li>Subscribe to DOM events</li>
2365     <li>Subscribe to the execution of a method on any object, effectively
2366     treating that method as an event</li>
2367 </ul>
2369 For custom event subscriptions, pass the custom event name as the first argument
2370 and callback as the second. The `this` object in the callback will be `Y` unless
2371 an override is passed as the third argument.
2373     Y.on('io:complete', function () {
2374         Y.MyApp.updateStatus('Transaction complete');
2375     });
2377 To subscribe to DOM events, pass the name of a DOM event as the first argument
2378 and a CSS selector string as the third argument after the callback function.
2379 Alternately, the third argument can be a `Node`, `NodeList`, `HTMLElement`,
2380 array, or simply omitted (the default is the `window` object).
2382     Y.on('click', function (e) {
2383         e.preventDefault();
2385         // proceed with ajax form submission
2386         var url = this.get('action');
2387         ...
2388     }, '#my-form');
2390 The `this` object in DOM event callbacks will be the `Node` targeted by the CSS
2391 selector or other identifier.
2393 `on()` subscribers for DOM events or custom events `publish`ed with a
2394 `defaultFn` can prevent the default behavior with `e.preventDefault()` from the
2395 event object passed as the first parameter to the subscription callback.
2397 To subscribe to the execution of an object method, pass arguments corresponding to the call signature for
2398 <a href="../classes/Do.html#methods_before">`Y.Do.before(...)`</a>.
2400 NOTE: The formal parameter list below is for events, not for function
2401 injection.  See `Y.Do.before` for that signature.
2403 @method on
2404 @param {String} type DOM or custom event name
2405 @param {Function} fn The callback to execute in response to the event
2406 @param {Object} [context] Override `this` object in callback
2407 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
2408 @return {EventHandle} A subscription handle capable of detaching the
2409                       subscription
2410 @see Do.before
2411 @for YUI
2415 Listen for an event one time.  Equivalent to `on()`, except that
2416 the listener is immediately detached when executed.
2418 See the <a href="#methods_on">`on()` method</a> for additional subscription
2419 options.
2421 @see on
2422 @method once
2423 @param {String} type DOM or custom event name
2424 @param {Function} fn The callback to execute in response to the event
2425 @param {Object} [context] Override `this` object in callback
2426 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
2427 @return {EventHandle} A subscription handle capable of detaching the
2428                       subscription
2429 @for YUI
2433 Listen for an event one time.  Equivalent to `once()`, except, like `after()`,
2434 the subscription callback executes after all `on()` subscribers and the event's
2435 `defaultFn` (if configured) have executed.  Like `after()` if any `on()` phase
2436 subscriber calls `e.preventDefault()`, neither the `defaultFn` nor the `after()`
2437 subscribers will execute.
2439 The listener is immediately detached when executed.
2441 See the <a href="#methods_on">`on()` method</a> for additional subscription
2442 options.
2444 @see once
2445 @method onceAfter
2446 @param {String} type The custom event name
2447 @param {Function} fn The callback to execute in response to the event
2448 @param {Object} [context] Override `this` object in callback
2449 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
2450 @return {EventHandle} A subscription handle capable of detaching the
2451                       subscription
2452 @for YUI
2456 Like `on()`, this method creates a subscription to a custom event or to the
2457 execution of a method on an object.
2459 For events, `after()` subscribers are executed after the event's
2460 `defaultFn` unless `e.preventDefault()` was called from an `on()` subscriber.
2462 See the <a href="#methods_on">`on()` method</a> for additional subscription
2463 options.
2465 NOTE: The subscription signature shown is for events, not for function
2466 injection.  See <a href="../classes/Do.html#methods_after">`Y.Do.after`</a>
2467 for that signature.
2469 @see on
2470 @see Do.after
2471 @method after
2472 @param {String} type The custom event name
2473 @param {Function} fn The callback to execute in response to the event
2474 @param {Object} [context] Override `this` object in callback
2475 @param {Any} [args*] 0..n additional arguments to supply to the subscriber
2476 @return {EventHandle} A subscription handle capable of detaching the
2477                       subscription
2478 @for YUI
2482 }, '3.13.0', {"requires": ["oop"]});