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