]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/event-custom/event-custom.js
Release 6.2.0beta4
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / event-custom / event-custom.js
1 /*
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 3.0.0
6 build: 1549
7 */
8 YUI.add('event-custom-base', function(Y) {
9
10 /**
11  * Custom event engine, DOM event listener abstraction layer, synthetic DOM
12  * events.
13  * @module event-custom
14  */
15
16 Y.Env.evt = {
17     handles: {},
18     plugins: {}
19 };
20
21
22 /**
23  * Custom event engine, DOM event listener abstraction layer, synthetic DOM 
24  * events.
25  * @module event-custom
26  * @submodule event-custom-base
27  */
28 (function() {
29
30 /**
31  * Allows for the insertion of methods that are executed before or after
32  * a specified method
33  * @class Do
34  * @static
35  */
36
37 var BEFORE = 0,
38     AFTER = 1;
39
40 Y.Do = {
41
42     /**
43      * Cache of objects touched by the utility
44      * @property objs
45      * @static
46      */
47     objs: {},
48
49     /**
50      * Execute the supplied method before the specified function
51      * @method before
52      * @param fn {Function} the function to execute
53      * @param obj the object hosting the method to displace
54      * @param sFn {string} the name of the method to displace
55      * @param c The execution context for fn
56      * @return {string} handle for the subscription
57      * @static
58      */
59     before: function(fn, obj, sFn, c) {
60         var f = fn, a;
61         if (c) {
62             a = [fn, c].concat(Y.Array(arguments, 4, true));
63             f = Y.rbind.apply(Y, a);
64         }
65
66         return this._inject(BEFORE, f, obj, sFn);
67     },
68
69     /**
70      * Execute the supplied method after the specified function
71      * @method after
72      * @param fn {Function} the function to execute
73      * @param obj the object hosting the method to displace
74      * @param sFn {string} the name of the method to displace
75      * @param c The execution context for fn
76      * @return {string} handle for the subscription
77      * @static
78      */
79     after: function(fn, obj, sFn, c) {
80         var f = fn, a;
81         if (c) {
82             a = [fn, c].concat(Y.Array(arguments, 4, true));
83             f = Y.rbind.apply(Y, a);
84         }
85
86         return this._inject(AFTER, f, obj, sFn);
87     },
88
89     /**
90      * Execute the supplied method after the specified function
91      * @method _inject
92      * @param when {string} before or after
93      * @param fn {Function} the function to execute
94      * @param obj the object hosting the method to displace
95      * @param sFn {string} the name of the method to displace
96      * @param c The execution context for fn
97      * @return {string} handle for the subscription
98      * @private
99      * @static
100      */
101     _inject: function(when, fn, obj, sFn) {
102
103         // object id
104         var id = Y.stamp(obj), o, sid;
105
106         if (! this.objs[id]) {
107             // create a map entry for the obj if it doesn't exist
108             this.objs[id] = {};
109         }
110
111         o = this.objs[id];
112
113         if (! o[sFn]) {
114             // create a map entry for the method if it doesn't exist
115             o[sFn] = new Y.Do.Method(obj, sFn);
116
117             // re-route the method to our wrapper
118             obj[sFn] = 
119                 function() {
120                     return o[sFn].exec.apply(o[sFn], arguments);
121                 };
122         }
123
124         // subscriber id
125         sid = id + Y.stamp(fn) + sFn;
126
127         // register the callback
128         o[sFn].register(sid, fn, when);
129
130         return new Y.EventHandle(o[sFn], sid);
131
132     },
133
134     /**
135      * Detach a before or after subscription
136      * @method detach
137      * @param handle {string} the subscription handle
138      */
139     detach: function(handle) {
140
141         if (handle.detach) {
142             handle.detach();
143         }
144
145     },
146
147     _unload: function(e, me) {
148
149     }
150 };
151
152 //////////////////////////////////////////////////////////////////////////
153
154 /**
155  * Wrapper for a displaced method with aop enabled
156  * @class Do.Method
157  * @constructor
158  * @param obj The object to operate on
159  * @param sFn The name of the method to displace
160  */
161 Y.Do.Method = function(obj, sFn) {
162     this.obj = obj;
163     this.methodName = sFn;
164     this.method = obj[sFn];
165     this.before = {};
166     this.after = {};
167 };
168
169 /**
170  * Register a aop subscriber
171  * @method register
172  * @param sid {string} the subscriber id
173  * @param fn {Function} the function to execute
174  * @param when {string} when to execute the function
175  */
176 Y.Do.Method.prototype.register = function (sid, fn, when) {
177     if (when) {
178         this.after[sid] = fn;
179     } else {
180         this.before[sid] = fn;
181     }
182 };
183
184 /**
185  * Unregister a aop subscriber
186  * @method delete
187  * @param sid {string} the subscriber id
188  * @param fn {Function} the function to execute
189  * @param when {string} when to execute the function
190  */
191 Y.Do.Method.prototype._delete = function (sid) {
192     delete this.before[sid];
193     delete this.after[sid];
194 };
195
196 /**
197  * Execute the wrapped method
198  * @method exec
199  */
200 Y.Do.Method.prototype.exec = function () {
201
202     var args = Y.Array(arguments, 0, true), 
203         i, ret, newRet, 
204         bf = this.before,
205         af = this.after,
206         prevented = false;
207
208     // execute before
209     for (i in bf) {
210         if (bf.hasOwnProperty(i)) {
211             ret = bf[i].apply(this.obj, args);
212             if (ret) {
213                 switch (ret.constructor) {
214                     case Y.Do.Halt:
215                         return ret.retVal;
216                     case Y.Do.AlterArgs:
217                         args = ret.newArgs;
218                         break;
219                     case Y.Do.Prevent:
220                         prevented = true;
221                         break;
222                     default:
223                 }
224             }
225         }
226     }
227
228     // execute method
229     if (!prevented) {
230         ret = this.method.apply(this.obj, args);
231     }
232
233     // execute after methods.
234     for (i in af) {
235         if (af.hasOwnProperty(i)) {
236             newRet = af[i].apply(this.obj, args);
237             // Stop processing if a Halt object is returned
238             if (newRet && newRet.constructor == Y.Do.Halt) {
239                 return newRet.retVal;
240             // Check for a new return value
241             } else if (newRet && newRet.constructor == Y.Do.AlterReturn) {
242                 ret = newRet.newRetVal;
243             }
244         }
245     }
246
247     return ret;
248 };
249
250 //////////////////////////////////////////////////////////////////////////
251
252
253 /**
254  * Return an AlterArgs object when you want to change the arguments that
255  * were passed into the function.  An example would be a service that scrubs
256  * out illegal characters prior to executing the core business logic.
257  * @class Do.AlterArgs
258  */
259 Y.Do.AlterArgs = function(msg, newArgs) {
260     this.msg = msg;
261     this.newArgs = newArgs;
262 };
263
264 /**
265  * Return an AlterReturn object when you want to change the result returned
266  * from the core method to the caller
267  * @class Do.AlterReturn
268  */
269 Y.Do.AlterReturn = function(msg, newRetVal) {
270     this.msg = msg;
271     this.newRetVal = newRetVal;
272 };
273
274 /**
275  * Return a Halt object when you want to terminate the execution
276  * of all subsequent subscribers as well as the wrapped method
277  * if it has not exectued yet.
278  * @class Do.Halt
279  */
280 Y.Do.Halt = function(msg, retVal) {
281     this.msg = msg;
282     this.retVal = retVal;
283 };
284
285 /**
286  * Return a Prevent object when you want to prevent the wrapped function
287  * from executing, but want the remaining listeners to execute
288  * @class Do.Prevent
289  */
290 Y.Do.Prevent = function(msg) {
291     this.msg = msg;
292 };
293
294 /**
295  * Return an Error object when you want to terminate the execution
296  * of all subsequent method calls.
297  * @class Do.Error
298  * @deprecated use Y.Do.Halt or Y.Do.Prevent
299  */
300 Y.Do.Error = Y.Do.Halt;
301
302 //////////////////////////////////////////////////////////////////////////
303
304 // Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
305
306 })();
307
308 /**
309  * Custom event engine, DOM event listener abstraction layer, synthetic DOM 
310  * events.
311  * @module event-custom
312  * @submodule event-custom-base
313  */
314
315 /**
316  * Return value from all subscribe operations
317  * @class EventHandle
318  * @constructor
319  * @param evt {CustomEvent} the custom event
320  * @param sub {Subscriber} the subscriber
321  */
322
323 // var onsubscribeType = "_event:onsub",
324 var AFTER = 'after', 
325     CONFIGS = [
326         'broadcast',
327         'bubbles',
328         'context',
329         'contextFn',
330         'currentTarget',
331         'defaultFn',
332         'details',
333         'emitFacade',
334         'fireOnce',
335         'host',
336         'preventable',
337         'preventedFn',
338         'queuable',
339         'silent',
340         'stoppedFn',
341         'target',
342         'type'
343     ],
344
345
346     YUI3_SIGNATURE = 9,
347     YUI_LOG = 'yui:log';
348
349 Y.EventHandle = function(evt, sub) {
350
351     /**
352      * The custom event
353      * @type CustomEvent
354      */
355     this.evt = evt;
356
357     /**
358      * The subscriber object
359      * @type Subscriber
360      */
361     this.sub = sub;
362 };
363
364 Y.EventHandle.prototype = {
365
366     /**
367      * Detaches this subscriber
368      * @method detach
369      */
370     detach: function() {
371         var evt = this.evt, i;
372         if (evt) {
373             if (Y.Lang.isArray(evt)) {
374                 for (i=0; i<evt.length; i++) {
375                     evt[i].detach();
376                 }
377             } else { 
378                 evt._delete(this.sub);
379             }
380         }
381     }
382 };
383
384 /**
385  * The CustomEvent class lets you define events for your application
386  * that can be subscribed to by one or more independent component.
387  *
388  * @param {String}  type The type of event, which is passed to the callback
389  *                  when the event fires
390  * @param o configuration object
391  * @class CustomEvent
392  * @constructor
393  */
394 Y.CustomEvent = function(type, o) {
395
396     // if (arguments.length > 2) {
397 // this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
398     // }
399
400     o = o || {};
401
402     this.id = Y.stamp(this);
403
404     /**
405      * The type of event, returned to subscribers when the event fires
406      * @property type
407      * @type string
408      */
409     this.type = type;
410
411     /**
412      * The context the the event will fire from by default.  Defaults to the YUI
413      * instance.
414      * @property context
415      * @type object
416      */
417     this.context = Y;
418
419     this.logSystem = (type == YUI_LOG);
420
421     /**
422      * If 0, this event does not broadcast.  If 1, the YUI instance is notified
423      * every time this event fires.  If 2, the YUI instance and the YUI global
424      * (if event is enabled on the global) are notified every time this event
425      * fires.
426      * @property broadcast
427      * @type int
428      */
429     // this.broadcast = 0;
430
431     /**
432      * By default all custom events are logged in the debug build, set silent
433      * to true to disable debug outpu for this event.
434      * @property silent
435      * @type boolean
436      */
437     this.silent = this.logSystem;
438
439     /**
440      * Specifies whether this event should be queued when the host is actively
441      * processing an event.  This will effect exectution order of the callbacks
442      * for the various events.
443      * @property queuable
444      * @type boolean
445      * @default false
446      */
447     // this.queuable = false;
448
449     /**
450      * The subscribers to this event
451      * @property subscribers
452      * @type Subscriber{}
453      */
454     this.subscribers = {};
455
456     /**
457      * 'After' subscribers
458      * @property afters
459      * @type Subscriber{}
460      */
461     this.afters = {};
462
463     /**
464      * This event has fired if true
465      *
466      * @property fired
467      * @type boolean
468      * @default false;
469      */
470     // this.fired = false;
471
472     /**
473      * An array containing the arguments the custom event
474      * was last fired with.
475      * @property firedWith
476      * @type Array
477      */
478     // this.firedWith;
479
480     /**
481      * This event should only fire one time if true, and if
482      * it has fired, any new subscribers should be notified
483      * immediately.
484      *
485      * @property fireOnce
486      * @type boolean
487      * @default false;
488      */
489     // this.fireOnce = false;
490
491     /**
492      * Flag for stopPropagation that is modified during fire()
493      * 1 means to stop propagation to bubble targets.  2 means
494      * to also stop additional subscribers on this target.
495      * @property stopped
496      * @type int
497      */
498     // this.stopped = 0;
499
500     /**
501      * Flag for preventDefault that is modified during fire().
502      * if it is not 0, the default behavior for this event
503      * @property prevented
504      * @type int
505      */
506     // this.prevented = 0;
507
508     /**
509      * Specifies the host for this custom event.  This is used
510      * to enable event bubbling
511      * @property host
512      * @type EventTarget
513      */
514     // this.host = null;
515
516     /**
517      * The default function to execute after event listeners
518      * have fire, but only if the default action was not
519      * prevented.
520      * @property defaultFn
521      * @type Function
522      */
523     // this.defaultFn = null;
524
525     /**
526      * The function to execute if a subscriber calls
527      * stopPropagation or stopImmediatePropagation
528      * @property stoppedFn
529      * @type Function
530      */
531     // this.stoppedFn = null;
532
533     /**
534      * The function to execute if a subscriber calls
535      * preventDefault
536      * @property preventedFn
537      * @type Function
538      */
539     // this.preventedFn = null;
540
541     /**
542      * Specifies whether or not this event's default function
543      * can be cancelled by a subscriber by executing preventDefault() 
544      * on the event facade 
545      * @property preventable 
546      * @type boolean 
547      * @default true
548      */
549     this.preventable = true;
550
551     /**
552      * Specifies whether or not a subscriber can stop the event propagation
553      * via stopPropagation(), stopImmediatePropagation(), or halt()
554      * @property bubbles
555      * @type boolean
556      * @default true
557      */
558     this.bubbles = true;
559
560     /**
561      * Supports multiple options for listener signatures in order to
562      * port YUI 2 apps.
563      * @property signature
564      * @type int
565      * @default 9
566      */
567     this.signature = YUI3_SIGNATURE;
568
569     // this.hasSubscribers = false;
570
571     // this.hasAfters = false;
572
573     /**
574      * If set to true, the custom event will deliver an EventFacade object
575      * that is similar to a DOM event object.
576      * @property emitFacade
577      * @type boolean
578      * @default false
579      */
580     // this.emitFacade = false;
581
582     this.applyConfig(o, true);
583
584     // this.log("Creating " + this.type);
585
586 };
587
588 Y.CustomEvent.prototype = {
589
590     /**
591      * Apply configuration properties.  Only applies the CONFIG whitelist
592      * @method applyConfig
593      * @param o hash of properties to apply
594      * @param force {boolean} if true, properties that exist on the event 
595      * will be overwritten.
596      */
597     applyConfig: function(o, force) {
598         if (o) {
599             Y.mix(this, o, force, CONFIGS);
600         }
601     },
602
603     _on: function(fn, context, args, when) {
604
605         if (!fn) {
606             this.log("Invalid callback for CE: " + this.type);
607         }
608
609         var s = new Y.Subscriber(fn, context, args, when);
610
611         if (this.fireOnce && this.fired) {
612             Y.later(0, this, Y.bind(this._notify, this, s, this.firedWith));
613         }
614
615         if (when == AFTER) {
616             this.afters[s.id] = s;
617             this.hasAfters = true;
618         } else {
619             this.subscribers[s.id] = s;
620             this.hasSubscribers = true;
621         }
622
623         return new Y.EventHandle(this, s);
624
625     },
626
627     /**
628      * Listen for this event
629      * @method subscribe
630      * @param {Function} fn        The function to execute
631      * @return {EventHandle|EventTarget} unsubscribe handle or a
632      * chainable event target depending on the 'chain' config.
633      * @deprecated use on
634      */
635     subscribe: function(fn, context) {
636         var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
637         return this._on(fn, context, a, true);
638     },
639
640     /**
641      * Listen for this event
642      * @method on
643      * @param {Function} fn        The function to execute
644      * @return {EventHandle|EventTarget} unsubscribe handle or a
645      * chainable event target depending on the 'chain' config.
646      */
647     on: function(fn, context) {
648         var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
649         return this._on(fn, context, a, true);
650     },
651
652     /**
653      * Listen for this event after the normal subscribers have been notified and
654      * the default behavior has been applied.  If a normal subscriber prevents the 
655      * default behavior, it also prevents after listeners from firing.
656      * @method after
657      * @param {Function} fn        The function to execute
658      * @return {EventHandle|EventTarget} unsubscribe handle or a
659      * chainable event target depending on the 'chain' config.
660      */
661     after: function(fn, context) {
662         var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
663         return this._on(fn, context, a, AFTER);
664     },
665
666     /**
667      * Detach listeners.
668      * @method detach 
669      * @param {Function} fn  The subscribed function to remove, if not supplied
670      *                       all will be removed
671      * @param {Object}   context The context object passed to subscribe.
672      * @return {int|EventTarget} returns a chainable event target
673      * or the number of subscribers unsubscribed.
674      */
675     detach: function(fn, context) {
676         // unsubscribe handle
677         if (fn && fn.detach) {
678             return fn.detach();
679         }
680
681         var found = 0, subs = this.subscribers, i, s;
682
683         for (i in subs) {
684             if (subs.hasOwnProperty(i)) {
685                 s = subs[i];
686                 if (s && (!fn || fn === s.fn)) {
687                     this._delete(s);
688                     found++;
689                 }
690             }
691         }
692
693         return found;
694     },
695
696     /**
697      * Detach listeners.
698      * @method unsubscribe
699      * @param {Function} fn  The subscribed function to remove, if not supplied
700      *                       all will be removed
701      * @param {Object}   context The context object passed to subscribe.
702      * @return {boolean|EventTarget} returns a chainable event target
703      * or a boolean for legacy detach support.
704      * @deprecated use detach
705      */
706     unsubscribe: function() {
707         return this.detach.apply(this, arguments);
708     },
709
710
711     /**
712      * Notify a single subscriber
713      * @method _notify
714      * @param s {Subscriber} the subscriber
715      * @param args {Array} the arguments array to apply to the listener
716      * @private
717      */
718     _notify: function(s, args, ef) {
719
720         this.log(this.type + "->" + "sub: " +  s.id);
721
722         var ret;
723
724         ret = s.notify(args, this);
725
726         if (false === ret || this.stopped > 1) {
727             this.log(this.type + " cancelled by subscriber");
728             return false;
729         }
730
731         return true;
732     },
733
734     /**
735      * Logger abstraction to centralize the application of the silent flag
736      * @method log
737      * @param msg {string} message to log
738      * @param cat {string} log category
739      */
740     log: function(msg, cat) {
741         if (!this.silent) {
742         }
743     },
744
745     /**
746      * Notifies the subscribers.  The callback functions will be executed
747      * from the context specified when the event was created, and with the 
748      * following parameters:
749      *   <ul>
750      *   <li>The type of event</li>
751      *   <li>All of the arguments fire() was executed with as an array</li>
752      *   <li>The custom object (if any) that was passed into the subscribe() 
753      *       method</li>
754      *   </ul>
755      * @method fire 
756      * @param {Object*} arguments an arbitrary set of parameters to pass to 
757      *                            the handler.
758      * @return {boolean} false if one of the subscribers returned false, 
759      *                   true otherwise
760      * 
761      */
762     fire: function() {
763         if (this.fireOnce && this.fired) {
764             this.log('fireOnce event: ' + this.type + ' already fired');
765             return true;
766         } else {
767
768             var args = Y.Array(arguments, 0, true);
769
770             this.fired = true;
771             this.firedWith = args;
772
773             if (this.emitFacade) {
774                 return this.fireComplex(args);
775             } else {
776                 return this.fireSimple(args);
777             }
778         }
779     },
780
781     fireSimple: function(args) {
782         if (this.hasSubscribers || this.hasAfters) {
783             this._procSubs(Y.merge(this.subscribers, this.afters), args);
784         }
785         this._broadcast(args);
786         return this.stopped ? false : true;
787     },
788
789     // Requires the event-custom-complex module for full funcitonality.
790     fireComplex: function(args) {
791         args[0] = args[0] || {};
792         return this.fireSimple(args);
793     },
794
795     _procSubs: function(subs, args, ef) {
796         var s, i;
797         for (i in subs) {
798             if (subs.hasOwnProperty(i)) {
799                 s = subs[i];
800                 if (s && s.fn) {
801                     if (false === this._notify(s, args, ef)) {
802                         this.stopped = 2;
803                     }
804                     if (this.stopped == 2) {
805                         return false;
806                     }
807                 }
808             }
809         }
810
811         return true;
812     },
813
814     _broadcast: function(args) {
815         if (!this.stopped && this.broadcast) {
816
817             var a = Y.Array(args);
818             a.unshift(this.type);
819
820             if (this.host !== Y) {
821                 Y.fire.apply(Y, a);
822             }
823
824             if (this.broadcast == 2) {
825                 Y.Global.fire.apply(Y.Global, a);
826             }
827         }
828     },
829
830     /**
831      * Removes all listeners
832      * @method unsubscribeAll
833      * @return {int} The number of listeners unsubscribed
834      * @deprecated use detachAll
835      */
836     unsubscribeAll: function() {
837         return this.detachAll.apply(this, arguments);
838     },
839
840     /**
841      * Removes all listeners
842      * @method detachAll
843      * @return {int} The number of listeners unsubscribed
844      */
845     detachAll: function() {
846         return this.detach();
847     },
848
849     /**
850      * @method _delete
851      * @param subscriber object
852      * @private
853      */
854     _delete: function(s) {
855         if (s) {
856             delete s.fn;
857             delete s.context;
858             delete this.subscribers[s.id];
859             delete this.afters[s.id];
860         }
861     }
862 };
863
864 /////////////////////////////////////////////////////////////////////
865
866 /**
867  * Stores the subscriber information to be used when the event fires.
868  * @param {Function} fn       The wrapped function to execute
869  * @param {Object}   context  The value of the keyword 'this' in the listener
870  * @param {Array} args*       0..n additional arguments to supply the listener
871  *
872  * @class Subscriber
873  * @constructor
874  */
875 Y.Subscriber = function(fn, context, args) {
876
877     /**
878      * The callback that will be execute when the event fires
879      * This is wrapped by Y.rbind if obj was supplied.
880      * @property fn
881      * @type Function
882      */
883     this.fn = fn;
884
885     /**
886      * Optional 'this' keyword for the listener
887      * @property context
888      * @type Object
889      */
890     this.context = context;
891
892     /**
893      * Unique subscriber id
894      * @property id
895      * @type String
896      */
897     this.id = Y.stamp(this);
898
899     /**
900      * Additional arguments to propagate to the subscriber
901      * @property args
902      * @type Array
903      */
904     this.args = args;
905
906     /**
907      * Custom events for a given fire transaction.
908      * @property events
909      * @type {EventTarget}
910      */
911     this.events = null;
912     
913 };
914
915 Y.Subscriber.prototype = {
916
917     _notify: function(c, args, ce) {
918         var a = this.args, ret;
919         switch (ce.signature) {
920             case 0:
921                 ret = this.fn.call(c, ce.type, args, c);
922                 break;
923             case 1:
924                 ret = this.fn.call(c, args[0] || null, c);
925                 break;
926             default:
927                 if (a || args) {
928                     args = args || [];
929                     a = (a) ? args.concat(a) : args;
930                     ret = this.fn.apply(c, a);
931                 } else {
932                     ret = this.fn.call(c);
933                 }
934         }
935
936         return ret;
937     },
938
939     /**
940      * Executes the subscriber.
941      * @method notify
942      * @param args {Array} Arguments array for the subscriber
943      * @param ce {CustomEvent} The custom event that sent the notification
944      */
945     notify: function(args, ce) {
946         var c = this.context,
947             ret = true;
948
949         if (!c) {
950             c = (ce.contextFn) ? ce.contextFn() : ce.context;
951         }
952
953         // only catch errors if we will not re-throw them.
954         if (Y.config.throwFail) {
955             ret = this._notify(c, args, ce);
956         } else {
957             try {
958                 ret = this._notify(c, args, ce);
959             } catch(e) {
960                 Y.error(this + ' failed: ' + e.message, e);
961             }
962         }
963
964         return ret;
965     },
966
967     /**
968      * Returns true if the fn and obj match this objects properties.
969      * Used by the unsubscribe method to match the right subscriber.
970      *
971      * @method contains
972      * @param {Function} fn the function to execute
973      * @param {Object} context optional 'this' keyword for the listener
974      * @return {boolean} true if the supplied arguments match this 
975      *                   subscriber's signature.
976      */
977     contains: function(fn, context) {
978         if (context) {
979             return ((this.fn == fn) && this.context == context);
980         } else {
981             return (this.fn == fn);
982         }
983     }
984
985 };
986
987 /**
988  * Custom event engine, DOM event listener abstraction layer, synthetic DOM 
989  * events.
990  * @module event-custom
991  * @submodule event-custom-base
992  */
993 (function() {
994
995 /**
996  * EventTarget provides the implementation for any object to
997  * publish, subscribe and fire to custom events, and also
998  * alows other EventTargets to target the object with events
999  * sourced from the other object.
1000  * EventTarget is designed to be used with Y.augment to wrap 
1001  * EventCustom in an interface that allows events to be listened to 
1002  * and fired by name.  This makes it possible for implementing code to
1003  * subscribe to an event that either has not been created yet, or will
1004  * not be created at all.
1005  * @class EventTarget
1006  * @param opts a configuration object
1007  * @config emitFacade {boolean} if true, all events will emit event 
1008  * facade payloads by default (default false)
1009  * @config prefix {string} the prefix to apply to non-prefixed event names 
1010  * @config chain {boolean} if true, on/after/detach return the host to allow 
1011  * chaining, otherwise they return an EventHandle (default false)
1012  */
1013
1014 var L = Y.Lang,
1015     PREFIX_DELIMITER = ':',
1016     CATEGORY_DELIMITER = '|',
1017     AFTER_PREFIX = '~AFTER~',
1018
1019     /**
1020      * If the instance has a prefix attribute and the
1021      * event type is not prefixed, the instance prefix is
1022      * applied to the supplied type.
1023      * @method _getType
1024      * @private
1025      */
1026     _getType = Y.cached(function(type, pre) {
1027
1028         if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
1029             return type;
1030         } 
1031
1032         return pre + PREFIX_DELIMITER + type;
1033     }),
1034
1035     /**
1036      * Returns an array with the detach key (if provided),
1037      * and the prefixed event name from _getType
1038      * Y.on('detachcategory, menu:click', fn)
1039      * @method _parseType
1040      * @private
1041      */
1042     _parseType = Y.cached(function(type, pre) {
1043
1044         var t = type, detachcategory, after, i;
1045
1046         if (!L.isString(t)) {
1047             return t;
1048         } 
1049         
1050         i = t.indexOf(AFTER_PREFIX);
1051
1052         if (i > -1) {
1053             after = true;
1054             t = t.substr(AFTER_PREFIX.length);
1055         }
1056
1057         i = t.indexOf(CATEGORY_DELIMITER);
1058
1059         if (i > -1) {
1060             detachcategory = t.substr(0, (i));
1061             t = t.substr(i+1);
1062             if (t == '*') {
1063                 t = null;
1064             }
1065         }
1066
1067         // detach category, full type with instance prefix, is this an after listener, short type
1068         return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
1069     }),
1070
1071     ET = function(opts) {
1072
1073
1074         var o = (L.isObject(opts)) ? opts : {};
1075
1076         this._yuievt = this._yuievt || {
1077
1078             id: Y.guid(),
1079
1080             events: {},
1081
1082             targets: {},
1083
1084             config: o,
1085
1086             chain: ('chain' in o) ? o.chain : Y.config.chain,
1087
1088             defaults: {
1089                 context: o.context || this, 
1090                 host: this,
1091                 emitFacade: o.emitFacade,
1092                 fireOnce: o.fireOnce,
1093                 queuable: o.queuable,
1094                 broadcast: o.broadcast,
1095                 bubbles: ('bubbles' in o) ? o.bubbles : true
1096             }
1097         };
1098
1099     };
1100
1101
1102 ET.prototype = {
1103
1104     /**
1105      * Subscribe to a custom event hosted by this object
1106      * @method on 
1107      * @param type    {string}   The type of the event
1108      * @param fn {Function} The callback
1109      * @return the event target or a detach handle per 'chain' config
1110      */
1111     on: function(type, fn, context, x) {
1112
1113         var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
1114             detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
1115             Node = Y.Node, n, domevent;
1116
1117         if (L.isObject(type)) {
1118
1119             if (L.isFunction(type)) {
1120                 return Y.Do.before.apply(Y.Do, arguments);
1121             }
1122
1123             f = fn; 
1124             c = context; 
1125             args = Y.Array(arguments, 0, true);
1126             ret = {};
1127             after = type._after;
1128             delete type._after;
1129
1130             Y.each(type, function(v, k) {
1131
1132                 if (v) {
1133                     f = v.fn || ((Y.Lang.isFunction(v)) ? v : f);
1134                     c = v.context || c;
1135                 }
1136
1137                 args[0] = (after) ? AFTER_PREFIX + k : k;
1138                 args[1] = f;
1139                 args[2] = c;
1140
1141                 ret[k] = this.on.apply(this, args); 
1142
1143             }, this);
1144
1145             return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
1146
1147         }
1148         
1149         detachcategory = parts[0];
1150         after = parts[2];
1151         shorttype = parts[3];
1152
1153         // extra redirection so we catch adaptor events too.  take a look at this.
1154         if (Node && (this instanceof Node) && (shorttype in Node.DOM_EVENTS)) {
1155             args = Y.Array(arguments, 0, true);
1156             args.splice(2, 0, Node.getDOMNode(this));
1157             return Y.on.apply(Y, args);
1158         }
1159
1160         type = parts[1];
1161
1162         if (this instanceof YUI) {
1163
1164             adapt = Y.Env.evt.plugins[type];
1165             args  = Y.Array(arguments, 0, true);
1166             args[0] = shorttype;
1167
1168             if (Node) {
1169                 n = args[2];
1170
1171                 if (n instanceof Y.NodeList) {
1172                     n = Y.NodeList.getDOMNodes(n);
1173                 } else if (n instanceof Node) {
1174                     n = Node.getDOMNode(n);
1175                 }
1176
1177                 domevent = (shorttype in Node.DOM_EVENTS);
1178
1179                 // Captures both DOM events and event plugins.
1180                 if (domevent) {
1181                     args[2] = n;
1182                 }
1183             }
1184
1185             // check for the existance of an event adaptor
1186             if (adapt) {
1187                 handle = adapt.on.apply(Y, args);
1188             } else if ((!type) || domevent) {
1189                 handle = Y.Event._attach(args);
1190             }
1191
1192         } 
1193
1194         if (!handle) {
1195             ce = this._yuievt.events[type] || this.publish(type);
1196             handle = ce._on(fn, context, (arguments.length > 3) ? Y.Array(arguments, 3, true) : null, (after) ? 'after' : true);
1197         }
1198
1199         if (detachcategory) {
1200             store[detachcategory] = store[detachcategory] || {};
1201             store[detachcategory][type] = store[detachcategory][type] || [];
1202             store[detachcategory][type].push(handle);
1203         }
1204
1205         return (this._yuievt.chain) ? this : handle;
1206
1207     },
1208
1209     /**
1210      * subscribe to an event
1211      * @method subscribe
1212      * @deprecated use on
1213      */
1214     subscribe: function() {
1215         return this.on.apply(this, arguments);
1216     },
1217
1218     /**
1219      * Detach one or more listeners the from the specified event
1220      * @method detach 
1221      * @param type {string|Object}   Either the handle to the subscriber or the 
1222      *                        type of event.  If the type
1223      *                        is not specified, it will attempt to remove
1224      *                        the listener from all hosted events.
1225      * @param fn   {Function} The subscribed function to unsubscribe, if not
1226      *                          supplied, all subscribers will be removed.
1227      * @param context  {Object}   The custom object passed to subscribe.  This is
1228      *                        optional, but if supplied will be used to
1229      *                        disambiguate multiple listeners that are the same
1230      *                        (e.g., you subscribe many object using a function
1231      *                        that lives on the prototype)
1232      * @return {EventTarget} the host
1233      */
1234     detach: function(type, fn, context) {
1235         var evts = this._yuievt.events, i, ret,
1236             Node = Y.Node, isNode = (this instanceof Node);
1237
1238         // detachAll disabled on the Y instance.
1239         if (!type && (this !== Y)) {
1240             for (i in evts) {
1241                 if (evts.hasOwnProperty(i)) {
1242                     ret = evts[i].detach(fn, context);
1243                 }
1244             }
1245             if (isNode) {
1246
1247                 Y.Event.purgeElement(Node.getDOMNode(this));
1248             }
1249
1250             return ret;
1251         }
1252
1253         var parts = _parseType(type, this._yuievt.config.prefix), 
1254         detachcategory = L.isArray(parts) ? parts[0] : null,
1255         shorttype = (parts) ? parts[3] : null,
1256         handle, adapt, store = Y.Env.evt.handles, cat, args,
1257         ce,
1258
1259         keyDetacher = function(lcat, ltype) {
1260             var handles = lcat[ltype];
1261             if (handles) {
1262                 while (handles.length) {
1263                     handle = handles.pop();
1264                     handle.detach();
1265                 }
1266             }
1267         };
1268
1269         if (detachcategory) {
1270
1271             cat = store[detachcategory];
1272             type = parts[1];
1273
1274             if (cat) {
1275                 if (type) {
1276                     keyDetacher(cat, type);
1277                 } else {
1278                     for (i in cat) {
1279                         if (cat.hasOwnProperty(i)) {
1280                             keyDetacher(cat, i);
1281                         }
1282                     }
1283                 }
1284
1285                 return (this._yuievt.chain) ? this : true;
1286             }
1287
1288         // If this is an event handle, use it to detach
1289         } else if (L.isObject(type) && type.detach) {
1290             ret = type.detach();
1291             return (this._yuievt.chain) ? this : ret;
1292         // extra redirection so we catch adaptor events too.  take a look at this.
1293         } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
1294             args = Y.Array(arguments, 0, true);
1295             args[2] = Node.getDOMNode(this);
1296             return Y.detach.apply(Y, args);
1297         }
1298
1299         adapt = Y.Env.evt.plugins[shorttype];
1300
1301         // The YUI instance handles DOM events and adaptors
1302         if (this instanceof YUI) {
1303             args = Y.Array(arguments, 0, true);
1304             // use the adaptor specific detach code if
1305             if (adapt && adapt.detach) {
1306                 return adapt.detach.apply(Y, args);
1307             // DOM event fork
1308             } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
1309                 args[0] = type;
1310                 return Y.Event.detach.apply(Y.Event, args);
1311             }
1312         }
1313
1314         ce = evts[type];
1315         if (ce) {
1316             ret = ce.detach(fn, context);
1317         }
1318
1319         return (this._yuievt.chain) ? this : ret;
1320     },
1321
1322     /**
1323      * detach a listener
1324      * @method unsubscribe
1325      * @deprecated use detach
1326      */
1327     unsubscribe: function() {
1328         return this.detach.apply(this, arguments);
1329     },
1330     
1331     /**
1332      * Removes all listeners from the specified event.  If the event type
1333      * is not specified, all listeners from all hosted custom events will
1334      * be removed.
1335      * @method detachAll
1336      * @param type {string}   The type, or name of the event
1337      */
1338     detachAll: function(type) {
1339         return this.detach(type);
1340     },
1341
1342     /**
1343      * Removes all listeners from the specified event.  If the event type
1344      * is not specified, all listeners from all hosted custom events will
1345      * be removed.
1346      * @method unsubscribeAll
1347      * @param type {string}   The type, or name of the event
1348      * @deprecated use detachAll
1349      */
1350     unsubscribeAll: function() {
1351         return this.detachAll.apply(this, arguments);
1352     },
1353
1354     /**
1355      * Creates a new custom event of the specified type.  If a custom event
1356      * by that name already exists, it will not be re-created.  In either
1357      * case the custom event is returned. 
1358      *
1359      * @method publish
1360      *
1361      * @param type {string} the type, or name of the event
1362      * @param opts {object} optional config params.  Valid properties are:
1363      *
1364      *  <ul>
1365      *    <li>
1366      *   'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
1367      *    </li>
1368      *    <li>
1369      *   'bubbles': whether or not this event bubbles (true)
1370      *    </li>
1371      *    <li>
1372      *   'context': the default execution context for the listeners (this)
1373      *    </li>
1374      *    <li>
1375      *   'defaultFn': the default function to execute when this event fires if preventDefault was not called
1376      *    </li>
1377      *    <li>
1378      *   'emitFacade': whether or not this event emits a facade (false)
1379      *    </li>
1380      *    <li>
1381      *   'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click' 
1382      *    </li>
1383      *    <li>
1384      *   'fireOnce': if an event is configured to fire once, new subscribers after
1385      *   the fire will be notified immediately.
1386      *    </li>
1387      *    <li>
1388      *   'preventable': whether or not preventDefault() has an effect (true)
1389      *    </li>
1390      *    <li>
1391      *   'preventedFn': a function that is executed when preventDefault is called
1392      *    </li>
1393      *    <li>
1394      *   'queuable': whether or not this event can be queued during bubbling (false)
1395      *    </li>
1396      *    <li>
1397      *   'silent': if silent is true, debug messages are not provided for this event.
1398      *    </li>
1399      *    <li>
1400      *   'stoppedFn': a function that is executed when stopPropagation is called
1401      *    </li>
1402      *    <li>
1403      *   'type': the event type (valid option if not provided as the first parameter to publish)
1404      *    </li>
1405      *  </ul>
1406      *
1407      *  @return {Event.Custom} the custom event
1408      *
1409      */
1410     publish: function(type, opts) {
1411         var events, ce, ret, pre = this._yuievt.config.prefix;
1412
1413         type = (pre) ? _getType(type, pre) : type;
1414
1415
1416         if (L.isObject(type)) {
1417             ret = {};
1418             Y.each(type, function(v, k) {
1419                 ret[k] = this.publish(k, v || opts); 
1420             }, this);
1421
1422             return ret;
1423         }
1424
1425         events = this._yuievt.events; 
1426         ce = events[type];
1427
1428         if (ce) {
1429 // ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
1430             if (opts) {
1431                 ce.applyConfig(opts, true);
1432             }
1433         } else {
1434             // apply defaults
1435             ce = new Y.CustomEvent(type, (opts) ? Y.mix(opts, this._yuievt.defaults) : this._yuievt.defaults);
1436             events[type] = ce;
1437         }
1438
1439         // make sure we turn the broadcast flag off if this
1440         // event was published as a result of bubbling
1441         // if (opts instanceof Y.CustomEvent) {
1442           //   events[type].broadcast = false;
1443         // }
1444
1445         return events[type];
1446     },
1447
1448     /**
1449      * Registers another EventTarget as a bubble target.  Bubble order
1450      * is determined by the order registered.  Multiple targets can
1451      * be specified.
1452      * @method addTarget
1453      * @param o {EventTarget} the target to add
1454      */
1455     addTarget: function(o) {
1456         this._yuievt.targets[Y.stamp(o)] = o;
1457         this._yuievt.hasTargets = true;
1458     },
1459
1460     /**
1461      * Removes a bubble target
1462      * @method removeTarget
1463      * @param o {EventTarget} the target to remove
1464      */
1465     removeTarget: function(o) {
1466         delete this._yuievt.targets[Y.stamp(o)];
1467     },
1468
1469    /**
1470      * Fire a custom event by name.  The callback functions will be executed
1471      * from the context specified when the event was created, and with the 
1472      * following parameters.
1473      *
1474      * If the custom event object hasn't been created, then the event hasn't 
1475      * been published and it has no subscribers.  For performance sake, we 
1476      * immediate exit in this case.  This means the event won't bubble, so 
1477      * if the intention is that a bubble target be notified, the event must 
1478      * be published on this object first.
1479      *
1480      * The first argument is the event type, and any additional arguments are
1481      * passed to the listeners as parameters.  If the first of these is an
1482      * object literal, and the event is configured to emit an event facade,
1483      * that object is mixed into the event facade and the facade is provided 
1484      * in place of the original object.
1485      *
1486      * @method fire
1487      * @param type {String|Object} The type of the event, or an object that contains
1488      * a 'type' property.
1489      * @param arguments {Object*} an arbitrary set of parameters to pass to 
1490      * the handler.  If the first of these is an object literal and the event is
1491      * configured to emit an event facade, the event facade will replace that
1492      * parameter after the properties the object literal contains are copied to
1493      * the event facade.
1494      * @return {Event.Target} the event host
1495      *                   
1496      */
1497     fire: function(type) {
1498
1499         var typeIncluded = L.isString(type),
1500             t = (typeIncluded) ? type : (type && type.type),
1501             ce, a, ret, pre=this._yuievt.config.prefix;
1502
1503         t = (pre) ? _getType(t, pre) : t;
1504         ce = this.getEvent(t, true);
1505
1506         // this event has not been published or subscribed to
1507         if (!ce) {
1508             
1509             if (this._yuievt.hasTargets) {
1510                 a = (typeIncluded) ? arguments : Y.Array(arguments, 0, true).unshift(t);
1511                 return this.bubble(null, a, this);
1512             }
1513
1514             // otherwise there is nothing to be done
1515             ret = true;
1516
1517         } else {
1518
1519             a = Y.Array(arguments, (typeIncluded) ? 1 : 0, true);
1520             ret = ce.fire.apply(ce, a);
1521
1522             // clear target for next fire()
1523             ce.target = null;
1524         }
1525
1526         return (this._yuievt.chain) ? this : ret;
1527     },
1528
1529     /**
1530      * Returns the custom event of the provided type has been created, a
1531      * falsy value otherwise
1532      * @method getEvent
1533      * @param type {string} the type, or name of the event
1534      * @param prefixed {string} if true, the type is prefixed already
1535      * @return {Event.Custom} the custom event or null
1536      */
1537     getEvent: function(type, prefixed) {
1538         var pre, e;
1539         if (!prefixed) {
1540             pre = this._yuievt.config.prefix;
1541             type = (pre) ? _getType(type, pre) : type;
1542         }
1543         e = this._yuievt.events;
1544         return (e && type in e) ? e[type] : null;
1545     },
1546
1547     /**
1548      * Subscribe to a custom event hosted by this object.  The
1549      * supplied callback will execute after any listeners add
1550      * via the subscribe method, and after the default function,
1551      * if configured for the event, has executed.
1552      * @method after
1553      * @param type    {string}   The type of the event
1554      * @param fn {Function} The callback
1555      * @return the event target or a detach handle per 'chain' config
1556      */
1557     after: function(type, fn) {
1558
1559         var a = Y.Array(arguments, 0, true);
1560
1561         switch (L.type(type)) {
1562             case 'function':
1563                 return Y.Do.after.apply(Y.Do, arguments);
1564             case 'object':
1565                 a[0]._after = true;
1566                 break;
1567             default:
1568                 a[0] = AFTER_PREFIX + type;
1569         }
1570
1571         return this.on.apply(this, a);
1572
1573     },
1574
1575     /**
1576      * Executes the callback before a DOM event, custom event
1577      * or method.  If the first argument is a function, it
1578      * is assumed the target is a method.  For DOM and custom
1579      * events, this is an alias for Y.on.
1580      *
1581      * For DOM and custom events:
1582      * type, callback, context, 0-n arguments
1583      *  
1584      * For methods:
1585      * callback, object (method host), methodName, context, 0-n arguments
1586      *
1587      * @method before
1588      * @return detach handle
1589      * @deprecated use the on method
1590      */
1591     before: function() { 
1592         return this.on.apply(this, arguments);
1593     }
1594
1595 };
1596
1597 Y.EventTarget = ET;
1598
1599 // make Y an event target
1600 Y.mix(Y, ET.prototype, false, false, { 
1601     bubbles: false 
1602 });
1603
1604 ET.call(Y);
1605
1606 YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
1607
1608 /**
1609  * Hosts YUI page level events.  This is where events bubble to
1610  * when the broadcast config is set to 2.  This property is
1611  * only available if the custom event module is loaded.
1612  * @property Global
1613  * @type EventTarget
1614  * @for YUI
1615  */
1616 Y.Global = YUI.Env.globalEvents;
1617
1618 // @TODO implement a global namespace function on Y.Global?
1619
1620 })();
1621
1622
1623 /**
1624  * <code>YUI</code>'s <code>on</code> method is a unified interface for subscribing to
1625  * most events exposed by YUI.  This includes custom events, DOM events, and 
1626  * function events.  <code>detach</code> is also provided to remove listeners
1627  * serviced by this function.
1628  *
1629  * The signature that <code>on</code> accepts varies depending on the type
1630  * of event being consumed.  Refer to the specific methods that will
1631  * service a specific request for additional information about subscribing
1632  * to that type of event.
1633  *
1634  * <ul>
1635  * <li>Custom events.  These events are defined by various
1636  * modules in the library.  This type of event is delegated to
1637  * <code>EventTarget</code>'s <code>on</code> method.
1638  *   <ul>
1639  *     <li>The type of the event</li>
1640  *     <li>The callback to execute</li>
1641  *     <li>An optional context object</li>
1642  *     <li>0..n additional arguments to supply the callback.</li>
1643  *   </ul>
1644  *   Example: 
1645  *   <code>Y.on('domready', function() { // start work });</code>
1646  * </li>
1647  * <li>DOM events.  These are moments reported by the browser related
1648  * to browser functionality and user interaction.
1649  * This type of event is delegated to <code>Event</code>'s 
1650  * <code>attach</code> method.
1651  *   <ul>
1652  *     <li>The type of the event</li>
1653  *     <li>The callback to execute</li>
1654  *     <li>The specification for the Node(s) to attach the listener
1655  *     to.  This can be a selector, collections, or Node/Element
1656  *     refereces.</li>
1657  *     <li>An optional context object</li>
1658  *     <li>0..n additional arguments to supply the callback.</li>
1659  *   </ul>
1660  *   Example: 
1661  *   <code>Y.on('click', function(e) { // something was clicked }, '#someelement');</code>
1662  * </li>
1663  * <li>Function events.  These events can be used to react before or after a
1664  * function is executed.  This type of event is delegated to <code>Event.Do</code>'s 
1665  * <code>before</code> method.
1666  *   <ul>
1667  *     <li>The callback to execute</li>
1668  *     <li>The object that has the function that will be listened for.</li>
1669  *     <li>The name of the function to listen for.</li>
1670  *     <li>An optional context object</li>
1671  *     <li>0..n additional arguments to supply the callback.</li>
1672  *   </ul>
1673  *   Example <code>Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname');</code>
1674  * </li>
1675  * </ul>
1676  *
1677  * <code>on</code> corresponds to the moment before any default behavior of
1678  * the event.  <code>after</code> works the same way, but these listeners
1679  * execute after the event's default behavior.  <code>before</code> is an
1680  * alias for <code>on</code>.
1681  *
1682  * @method on 
1683  * @param type** event type (this parameter does not apply for function events)
1684  * @param fn the callback
1685  * @param target** a descriptor for the target (applies to custom events only).
1686  * For function events, this is the object that contains the function to
1687  * execute.
1688  * @param extra** 0..n Extra information a particular event may need.  These
1689  * will be documented with the event.  In the case of function events, this
1690  * is the name of the function to execute on the host.  In the case of
1691  * delegate listeners, this is the event delegation specification.
1692  * @param context optionally change the value of 'this' in the callback
1693  * @param args* 0..n additional arguments to pass to the callback.
1694  * @return the event target or a detach handle per 'chain' config
1695  * @for YUI
1696  */
1697
1698 /**
1699  * after() is a unified interface for subscribing to
1700  * most events exposed by YUI.  This includes custom events,
1701  * DOM events, and AOP events.  This works the same way as
1702  * the on() function, only it operates after any default
1703  * behavior for the event has executed. @see <code>on</code> for more 
1704  * information.
1705  * @method after
1706  * @param type event type (this parameter does not apply for function events)
1707  * @param fn the callback
1708  * @param target a descriptor for the target (applies to custom events only).
1709  * For function events, this is the object that contains the function to
1710  * execute.
1711  * @param extra 0..n Extra information a particular event may need.  These
1712  * will be documented with the event.  In the case of function events, this
1713  * is the name of the function to execute on the host.  In the case of
1714  * delegate listeners, this is the event delegation specification.
1715  * @param context optionally change the value of 'this' in the callback
1716  * @param args* 0..n additional arguments to pass to the callback.
1717  * @return the event target or a detach handle per 'chain' config
1718  * @for YUI
1719  */
1720
1721
1722 }, '3.0.0' ,{requires:['oop']});
1723 YUI.add('event-custom-complex', function(Y) {
1724
1725
1726 /**
1727  * Adds event facades, preventable default behavior, and bubbling.
1728  * events.
1729  * @module event-custom
1730  * @submodule event-custom-complex
1731  */
1732
1733 (function() {
1734
1735 var FACADE, FACADE_KEYS, CEProto = Y.CustomEvent.prototype;
1736
1737 /**
1738  * Wraps and protects a custom event for use when emitFacade is set to true.
1739  * Requires the event-custom-complex module
1740  * @class EventFacade
1741  * @param e {Event} the custom event
1742  * @param currentTarget {HTMLElement} the element the listener was attached to
1743  */
1744
1745 Y.EventFacade = function(e, currentTarget) {
1746
1747     e = e || {};
1748
1749     /**
1750      * The arguments passed to fire 
1751      * @property details
1752      * @type Array
1753      */
1754     this.details = e.details;
1755
1756     /**
1757      * The event type
1758      * @property type
1759      * @type string
1760      */
1761     this.type = e.type;
1762
1763     //////////////////////////////////////////////////////
1764
1765     /**
1766      * Node reference for the targeted eventtarget
1767      * @propery target
1768      * @type Node
1769      */
1770     this.target = e.target;
1771
1772     /**
1773      * Node reference for the element that the listener was attached to.
1774      * @propery currentTarget
1775      * @type Node
1776      */
1777     this.currentTarget = currentTarget;
1778
1779     /**
1780      * Node reference to the relatedTarget
1781      * @propery relatedTarget
1782      * @type Node
1783      */
1784     this.relatedTarget = e.relatedTarget;
1785     
1786     /**
1787      * Stops the propagation to the next bubble target
1788      * @method stopPropagation
1789      */
1790     this.stopPropagation = function() {
1791         e.stopPropagation();
1792     };
1793
1794     /**
1795      * Stops the propagation to the next bubble target and
1796      * prevents any additional listeners from being exectued
1797      * on the current target.
1798      * @method stopImmediatePropagation
1799      */
1800     this.stopImmediatePropagation = function() {
1801         e.stopImmediatePropagation();
1802     };
1803
1804     /**
1805      * Prevents the event's default behavior
1806      * @method preventDefault
1807      */
1808     this.preventDefault = function() {
1809         e.preventDefault();
1810     };
1811
1812     /**
1813      * Stops the event propagation and prevents the default
1814      * event behavior.
1815      * @method halt
1816      * @param immediate {boolean} if true additional listeners
1817      * on the current target will not be executed
1818      */
1819     this.halt = function(immediate) {
1820         e.halt(immediate);
1821     };
1822
1823 };
1824
1825 CEProto.fireComplex = function(args) {
1826     var es = Y.Env._eventstack, ef, q, queue, ce, ret, events;
1827
1828     if (es) {
1829         // queue this event if the current item in the queue bubbles
1830         if (this.queuable && this.type != es.next.type) {
1831             this.log('queue ' + this.type);
1832             es.queue.push([this, args]);
1833             return true;
1834         }
1835     } else {
1836         Y.Env._eventstack = {
1837            // id of the first event in the stack
1838            id: this.id,
1839            next: this,
1840            silent: this.silent,
1841            stopped: 0,
1842            prevented: 0,
1843            queue: []
1844         };
1845         es = Y.Env._eventstack;
1846     }
1847
1848     this.stopped = 0;
1849     this.prevented = 0;
1850     this.target = this.target || this.host;
1851
1852     events = new Y.EventTarget({
1853         fireOnce: true,
1854         context: this.host
1855     });
1856
1857     this.events = events;
1858
1859     if (this.preventedFn) {
1860         events.on('prevented', this.preventedFn);
1861     }
1862
1863     if (this.stoppedFn) {
1864         events.on('stopped', this.stoppedFn);
1865     }
1866
1867     this.currentTarget = this.host || this.currentTarget;
1868
1869     this.details = args.slice(); // original arguments in the details
1870
1871     // this.log("Firing " + this  + ", " + "args: " + args);
1872     this.log("Firing " + this.type);
1873
1874     this._facade = null; // kill facade to eliminate stale properties
1875
1876     ef = this._getFacade(args);
1877
1878     if (Y.Lang.isObject(args[0])) {
1879         args[0] = ef;
1880     } else {
1881         args.unshift(ef);
1882     }
1883
1884     if (this.hasSubscribers) {
1885         this._procSubs(Y.merge(this.subscribers), args, ef);
1886     }
1887
1888     // bubble if this is hosted in an event target and propagation has not been stopped
1889     if (this.bubbles && this.host && this.host.bubble && !this.stopped) {
1890         es.stopped = 0;
1891         es.prevented = 0;
1892         ret = this.host.bubble(this);
1893
1894         this.stopped = Math.max(this.stopped, es.stopped);
1895         this.prevented = Math.max(this.prevented, es.prevented);
1896
1897     }
1898
1899     // execute the default behavior if not prevented
1900     if (this.defaultFn && !this.prevented) {
1901         this.defaultFn.apply(this.host || this, args);
1902     }
1903
1904     // broadcast listeners are fired as discreet events on the
1905     // YUI instance and potentially the YUI global.
1906     this._broadcast(args);
1907
1908     // process after listeners.  If the default behavior was
1909     // prevented, the after events don't fire.
1910     if (this.hasAfters && !this.prevented && this.stopped < 2) {
1911         this._procSubs(Y.merge(this.afters), args, ef);
1912     }
1913
1914     if (es.id === this.id) {
1915         queue = es.queue;
1916
1917         while (queue.length) {
1918             q = queue.pop(); 
1919             ce = q[0];
1920             es.stopped = 0;
1921             es.prevented = 0;
1922             // set up stack to allow the next item to be processed
1923             es.next = ce;
1924             ce.fire.apply(ce, q[1]);
1925         }
1926
1927         Y.Env._eventstack = null;
1928     } 
1929
1930     return this.stopped ? false : true;
1931 };
1932
1933 CEProto._getFacade = function() {
1934
1935     var ef = this._facade, o, o2,
1936     args = this.details;
1937
1938     if (!ef) {
1939         ef = new Y.EventFacade(this, this.currentTarget);
1940     }
1941
1942     // if the first argument is an object literal, apply the
1943     // properties to the event facade
1944     o = args && args[0];
1945
1946     if (Y.Lang.isObject(o, true)) {
1947
1948         o2 = {};
1949
1950         // protect the event facade properties
1951         Y.mix(o2, ef, true, FACADE_KEYS);
1952
1953         // mix the data
1954         Y.mix(ef, o, true);
1955
1956         // restore ef
1957         Y.mix(ef, o2, true, FACADE_KEYS);
1958     }
1959
1960     // update the details field with the arguments
1961     // ef.type = this.type;
1962     ef.details = this.details;
1963     ef.target = this.target;
1964     ef.currentTarget = this.currentTarget;
1965     ef.stopped = 0;
1966     ef.prevented = 0;
1967
1968     this._facade = ef;
1969
1970     return this._facade;
1971 };
1972
1973 /**
1974  * Stop propagation to bubble targets
1975  * @for CustomEvent
1976  * @method stopPropagation
1977  */
1978 CEProto.stopPropagation = function() {
1979     this.stopped = 1;
1980     Y.Env._eventstack.stopped = 1;
1981     this.events.fire('stopped', this);
1982 };
1983
1984 /**
1985  * Stops propagation to bubble targets, and prevents any remaining
1986  * subscribers on the current target from executing.
1987  * @method stopImmediatePropagation
1988  */
1989 CEProto.stopImmediatePropagation = function() {
1990     this.stopped = 2;
1991     Y.Env._eventstack.stopped = 2;
1992     this.events.fire('stopped', this);
1993 };
1994
1995 /**
1996  * Prevents the execution of this event's defaultFn
1997  * @method preventDefault
1998  */
1999 CEProto.preventDefault = function() {
2000     if (this.preventable) {
2001         this.prevented = 1;
2002         Y.Env._eventstack.prevented = 1;
2003         this.events.fire('prevented', this);
2004     }
2005 };
2006
2007 /**
2008  * Stops the event propagation and prevents the default
2009  * event behavior.
2010  * @method halt
2011  * @param immediate {boolean} if true additional listeners
2012  * on the current target will not be executed
2013  */
2014 CEProto.halt = function(immediate) {
2015     if (immediate) {
2016         this.stopImmediatePropagation();
2017     } else {
2018         this.stopPropagation();
2019     }
2020     this.preventDefault();
2021 };
2022
2023 /**
2024  * Propagate an event.  Requires the event-custom-complex module.
2025  * @method bubble
2026  * @param evt {Event.Custom} the custom event to propagate
2027  * @return {boolean} the aggregated return value from Event.Custom.fire
2028  * @for EventTarget
2029  */
2030 Y.EventTarget.prototype.bubble = function(evt, args, target) {
2031
2032     var targs = this._yuievt.targets, ret = true,
2033         t, type, ce, i, bc;
2034
2035     if (!evt || ((!evt.stopped) && targs)) {
2036
2037         for (i in targs) {
2038             if (targs.hasOwnProperty(i)) {
2039                 t = targs[i]; 
2040                 type = evt && evt.type;
2041                 ce = t.getEvent(type, true); 
2042                     
2043                 // if this event was not published on the bubble target,
2044                 // publish it with sensible default properties
2045                 if (!ce) {
2046
2047                     if (t._yuievt.hasTargets) {
2048                         t.bubble.call(t, evt, args, target);
2049                     }
2050
2051                 } else {
2052                     ce.target = target || (evt && evt.target) || this;
2053                     ce.currentTarget = t;
2054
2055                     bc = ce.broadcast;
2056                     ce.broadcast = false;
2057                     ret = ret && ce.fire.apply(ce, args || evt.details);
2058                     ce.broadcast = bc;
2059
2060                     // stopPropagation() was called
2061                     if (ce.stopped) {
2062                         break;
2063                     }
2064                 }
2065             }
2066         }
2067     }
2068
2069     return ret;
2070 };
2071
2072 FACADE = new Y.EventFacade();
2073 FACADE_KEYS = Y.Object.keys(FACADE);
2074
2075 })();
2076
2077
2078 }, '3.0.0' ,{requires:['event-custom-base']});
2079
2080
2081 YUI.add('event-custom', function(Y){}, '3.0.0' ,{use:['event-custom-base', 'event-custom-complex']});
2082