src/cm/media/js/lib/yui/yui_3.10.3/build/event-custom-complex/event-custom-complex.js
changeset 525 89ef5ed3c48b
equal deleted inserted replaced
524:322d0feea350 525:89ef5ed3c48b
       
     1 /*
       
     2 YUI 3.10.3 (build 2fb5187)
       
     3 Copyright 2013 Yahoo! Inc. All rights reserved.
       
     4 Licensed under the BSD License.
       
     5 http://yuilibrary.com/license/
       
     6 */
       
     7 
       
     8 YUI.add('event-custom-complex', function (Y, NAME) {
       
     9 
       
    10 
       
    11 /**
       
    12  * Adds event facades, preventable default behavior, and bubbling.
       
    13  * events.
       
    14  * @module event-custom
       
    15  * @submodule event-custom-complex
       
    16  */
       
    17 
       
    18 var FACADE,
       
    19     FACADE_KEYS,
       
    20     YObject = Y.Object,
       
    21     key,
       
    22     EMPTY = {},
       
    23     CEProto = Y.CustomEvent.prototype,
       
    24     ETProto = Y.EventTarget.prototype,
       
    25 
       
    26     mixFacadeProps = function(facade, payload) {
       
    27         var p;
       
    28 
       
    29         for (p in payload) {
       
    30             if (!(FACADE_KEYS.hasOwnProperty(p))) {
       
    31                 facade[p] = payload[p];
       
    32             }
       
    33         }
       
    34     };
       
    35 
       
    36 /**
       
    37  * Wraps and protects a custom event for use when emitFacade is set to true.
       
    38  * Requires the event-custom-complex module
       
    39  * @class EventFacade
       
    40  * @param e {Event} the custom event
       
    41  * @param currentTarget {HTMLElement} the element the listener was attached to
       
    42  */
       
    43 
       
    44 Y.EventFacade = function(e, currentTarget) {
       
    45 
       
    46     if (!e) {
       
    47         e = EMPTY;
       
    48     }
       
    49 
       
    50     this._event = e;
       
    51 
       
    52     /**
       
    53      * The arguments passed to fire
       
    54      * @property details
       
    55      * @type Array
       
    56      */
       
    57     this.details = e.details;
       
    58 
       
    59     /**
       
    60      * The event type, this can be overridden by the fire() payload
       
    61      * @property type
       
    62      * @type string
       
    63      */
       
    64     this.type = e.type;
       
    65 
       
    66     /**
       
    67      * The real event type
       
    68      * @property _type
       
    69      * @type string
       
    70      * @private
       
    71      */
       
    72     this._type = e.type;
       
    73 
       
    74     //////////////////////////////////////////////////////
       
    75 
       
    76     /**
       
    77      * Node reference for the targeted eventtarget
       
    78      * @property target
       
    79      * @type Node
       
    80      */
       
    81     this.target = e.target;
       
    82 
       
    83     /**
       
    84      * Node reference for the element that the listener was attached to.
       
    85      * @property currentTarget
       
    86      * @type Node
       
    87      */
       
    88     this.currentTarget = currentTarget;
       
    89 
       
    90     /**
       
    91      * Node reference to the relatedTarget
       
    92      * @property relatedTarget
       
    93      * @type Node
       
    94      */
       
    95     this.relatedTarget = e.relatedTarget;
       
    96 
       
    97 };
       
    98 
       
    99 Y.mix(Y.EventFacade.prototype, {
       
   100 
       
   101     /**
       
   102      * Stops the propagation to the next bubble target
       
   103      * @method stopPropagation
       
   104      */
       
   105     stopPropagation: function() {
       
   106         this._event.stopPropagation();
       
   107         this.stopped = 1;
       
   108     },
       
   109 
       
   110     /**
       
   111      * Stops the propagation to the next bubble target and
       
   112      * prevents any additional listeners from being exectued
       
   113      * on the current target.
       
   114      * @method stopImmediatePropagation
       
   115      */
       
   116     stopImmediatePropagation: function() {
       
   117         this._event.stopImmediatePropagation();
       
   118         this.stopped = 2;
       
   119     },
       
   120 
       
   121     /**
       
   122      * Prevents the event's default behavior
       
   123      * @method preventDefault
       
   124      */
       
   125     preventDefault: function() {
       
   126         this._event.preventDefault();
       
   127         this.prevented = 1;
       
   128     },
       
   129 
       
   130     /**
       
   131      * Stops the event propagation and prevents the default
       
   132      * event behavior.
       
   133      * @method halt
       
   134      * @param immediate {boolean} if true additional listeners
       
   135      * on the current target will not be executed
       
   136      */
       
   137     halt: function(immediate) {
       
   138         this._event.halt(immediate);
       
   139         this.prevented = 1;
       
   140         this.stopped = (immediate) ? 2 : 1;
       
   141     }
       
   142 
       
   143 });
       
   144 
       
   145 CEProto.fireComplex = function(args) {
       
   146 
       
   147     var es,
       
   148         ef,
       
   149         q,
       
   150         queue,
       
   151         ce,
       
   152         ret = true,
       
   153         events,
       
   154         subs,
       
   155         ons,
       
   156         afters,
       
   157         afterQueue,
       
   158         postponed,
       
   159         prevented,
       
   160         preventedFn,
       
   161         defaultFn,
       
   162         self = this,
       
   163         host = self.host || self,
       
   164         next,
       
   165         oldbubble,
       
   166         stack,
       
   167         yuievt = host._yuievt,
       
   168         hasPotentialSubscribers;
       
   169 
       
   170     stack = self.stack;
       
   171 
       
   172     if (stack) {
       
   173 
       
   174         // queue this event if the current item in the queue bubbles
       
   175         if (self.queuable && self.type !== stack.next.type) {
       
   176 
       
   177             if (!stack.queue) {
       
   178                 stack.queue = [];
       
   179             }
       
   180             stack.queue.push([self, args]);
       
   181 
       
   182             return true;
       
   183         }
       
   184     }
       
   185 
       
   186     hasPotentialSubscribers = self.hasSubs() || yuievt.hasTargets || self.broadcast;
       
   187 
       
   188     self.target = self.target || host;
       
   189     self.currentTarget = host;
       
   190 
       
   191     self.details = args.concat();
       
   192 
       
   193     if (hasPotentialSubscribers) {
       
   194 
       
   195         es = stack || {
       
   196 
       
   197            id: self.id, // id of the first event in the stack
       
   198            next: self,
       
   199            silent: self.silent,
       
   200            stopped: 0,
       
   201            prevented: 0,
       
   202            bubbling: null,
       
   203            type: self.type,
       
   204            // defaultFnQueue: new Y.Queue(),
       
   205            defaultTargetOnly: self.defaultTargetOnly
       
   206 
       
   207         };
       
   208 
       
   209         subs = self.getSubs();
       
   210         ons = subs[0];
       
   211         afters = subs[1];
       
   212 
       
   213         self.stopped = (self.type !== es.type) ? 0 : es.stopped;
       
   214         self.prevented = (self.type !== es.type) ? 0 : es.prevented;
       
   215 
       
   216         if (self.stoppedFn) {
       
   217             // PERF TODO: Can we replace with callback, like preventedFn. Look into history
       
   218             events = new Y.EventTarget({
       
   219                 fireOnce: true,
       
   220                 context: host
       
   221             });
       
   222             self.events = events;
       
   223             events.on('stopped', self.stoppedFn);
       
   224         }
       
   225 
       
   226 
       
   227         self._facade = null; // kill facade to eliminate stale properties
       
   228 
       
   229         ef = self._getFacade(args);
       
   230 
       
   231         if (ons) {
       
   232             self._procSubs(ons, args, ef);
       
   233         }
       
   234 
       
   235         // bubble if this is hosted in an event target and propagation has not been stopped
       
   236         if (self.bubbles && host.bubble && !self.stopped) {
       
   237             oldbubble = es.bubbling;
       
   238 
       
   239             es.bubbling = self.type;
       
   240 
       
   241             if (es.type !== self.type) {
       
   242                 es.stopped = 0;
       
   243                 es.prevented = 0;
       
   244             }
       
   245 
       
   246             ret = host.bubble(self, args, null, es);
       
   247 
       
   248             self.stopped = Math.max(self.stopped, es.stopped);
       
   249             self.prevented = Math.max(self.prevented, es.prevented);
       
   250 
       
   251             es.bubbling = oldbubble;
       
   252         }
       
   253 
       
   254         prevented = self.prevented;
       
   255 
       
   256         if (prevented) {
       
   257             preventedFn = self.preventedFn;
       
   258             if (preventedFn) {
       
   259                 preventedFn.apply(host, args);
       
   260             }
       
   261         } else {
       
   262             defaultFn = self.defaultFn;
       
   263 
       
   264             if (defaultFn && ((!self.defaultTargetOnly && !es.defaultTargetOnly) || host === ef.target)) {
       
   265                 defaultFn.apply(host, args);
       
   266             }
       
   267         }
       
   268 
       
   269         // broadcast listeners are fired as discreet events on the
       
   270         // YUI instance and potentially the YUI global.
       
   271         if (self.broadcast) {
       
   272             self._broadcast(args);
       
   273         }
       
   274 
       
   275         if (afters && !self.prevented && self.stopped < 2) {
       
   276 
       
   277             // Queue the after
       
   278             afterQueue = es.afterQueue;
       
   279 
       
   280             if (es.id === self.id || self.type !== yuievt.bubbling) {
       
   281 
       
   282                 self._procSubs(afters, args, ef);
       
   283 
       
   284                 if (afterQueue) {
       
   285                     while ((next = afterQueue.last())) {
       
   286                         next();
       
   287                     }
       
   288                 }
       
   289             } else {
       
   290                 postponed = afters;
       
   291 
       
   292                 if (es.execDefaultCnt) {
       
   293                     postponed = Y.merge(postponed);
       
   294 
       
   295                     Y.each(postponed, function(s) {
       
   296                         s.postponed = true;
       
   297                     });
       
   298                 }
       
   299 
       
   300                 if (!afterQueue) {
       
   301                     es.afterQueue = new Y.Queue();
       
   302                 }
       
   303 
       
   304                 es.afterQueue.add(function() {
       
   305                     self._procSubs(postponed, args, ef);
       
   306                 });
       
   307             }
       
   308 
       
   309         }
       
   310 
       
   311         self.target = null;
       
   312 
       
   313         if (es.id === self.id) {
       
   314 
       
   315             queue = es.queue;
       
   316 
       
   317             if (queue) {
       
   318                 while (queue.length) {
       
   319                     q = queue.pop();
       
   320                     ce = q[0];
       
   321                     // set up stack to allow the next item to be processed
       
   322                     es.next = ce;
       
   323                     ce._fire(q[1]);
       
   324                 }
       
   325             }
       
   326 
       
   327             self.stack = null;
       
   328         }
       
   329 
       
   330         ret = !(self.stopped);
       
   331 
       
   332         if (self.type !== yuievt.bubbling) {
       
   333             es.stopped = 0;
       
   334             es.prevented = 0;
       
   335             self.stopped = 0;
       
   336             self.prevented = 0;
       
   337         }
       
   338 
       
   339     } else {
       
   340         defaultFn = self.defaultFn;
       
   341 
       
   342         if(defaultFn) {
       
   343             ef = self._getFacade(args);
       
   344 
       
   345             if ((!self.defaultTargetOnly) || (host === ef.target)) {
       
   346                 defaultFn.apply(host, args);
       
   347             }
       
   348         }
       
   349     }
       
   350 
       
   351     // Kill the cached facade to free up memory.
       
   352     // Otherwise we have the facade from the last fire, sitting around forever.
       
   353     self._facade = null;
       
   354 
       
   355     return ret;
       
   356 };
       
   357 
       
   358 CEProto._getFacade = function(fireArgs) {
       
   359 
       
   360     var userArgs = this.details,
       
   361         firstArg = userArgs && userArgs[0],
       
   362         firstArgIsObj = (firstArg && (typeof firstArg === "object")),
       
   363         ef = this._facade;
       
   364 
       
   365     if (!ef) {
       
   366         ef = new Y.EventFacade(this, this.currentTarget);
       
   367     }
       
   368 
       
   369     if (firstArgIsObj) {
       
   370         // protect the event facade properties
       
   371         mixFacadeProps(ef, firstArg);
       
   372 
       
   373         // Allow the event type to be faked http://yuilibrary.com/projects/yui3/ticket/2528376
       
   374         if (firstArg.type) {
       
   375             ef.type = firstArg.type;
       
   376         }
       
   377 
       
   378         if (fireArgs) {
       
   379             fireArgs[0] = ef;
       
   380         }
       
   381     } else {
       
   382         if (fireArgs) {
       
   383             fireArgs.unshift(ef);
       
   384         }
       
   385     }
       
   386 
       
   387     // update the details field with the arguments
       
   388     ef.details = this.details;
       
   389 
       
   390     // use the original target when the event bubbled to this target
       
   391     ef.target = this.originalTarget || this.target;
       
   392 
       
   393     ef.currentTarget = this.currentTarget;
       
   394     ef.stopped = 0;
       
   395     ef.prevented = 0;
       
   396 
       
   397     this._facade = ef;
       
   398 
       
   399     return this._facade;
       
   400 };
       
   401 
       
   402 /**
       
   403  * Stop propagation to bubble targets
       
   404  * @for CustomEvent
       
   405  * @method stopPropagation
       
   406  */
       
   407 CEProto.stopPropagation = function() {
       
   408     this.stopped = 1;
       
   409     if (this.stack) {
       
   410         this.stack.stopped = 1;
       
   411     }
       
   412     if (this.events) {
       
   413         this.events.fire('stopped', this);
       
   414     }
       
   415 };
       
   416 
       
   417 /**
       
   418  * Stops propagation to bubble targets, and prevents any remaining
       
   419  * subscribers on the current target from executing.
       
   420  * @method stopImmediatePropagation
       
   421  */
       
   422 CEProto.stopImmediatePropagation = function() {
       
   423     this.stopped = 2;
       
   424     if (this.stack) {
       
   425         this.stack.stopped = 2;
       
   426     }
       
   427     if (this.events) {
       
   428         this.events.fire('stopped', this);
       
   429     }
       
   430 };
       
   431 
       
   432 /**
       
   433  * Prevents the execution of this event's defaultFn
       
   434  * @method preventDefault
       
   435  */
       
   436 CEProto.preventDefault = function() {
       
   437     if (this.preventable) {
       
   438         this.prevented = 1;
       
   439         if (this.stack) {
       
   440             this.stack.prevented = 1;
       
   441         }
       
   442     }
       
   443 };
       
   444 
       
   445 /**
       
   446  * Stops the event propagation and prevents the default
       
   447  * event behavior.
       
   448  * @method halt
       
   449  * @param immediate {boolean} if true additional listeners
       
   450  * on the current target will not be executed
       
   451  */
       
   452 CEProto.halt = function(immediate) {
       
   453     if (immediate) {
       
   454         this.stopImmediatePropagation();
       
   455     } else {
       
   456         this.stopPropagation();
       
   457     }
       
   458     this.preventDefault();
       
   459 };
       
   460 
       
   461 /**
       
   462  * Registers another EventTarget as a bubble target.  Bubble order
       
   463  * is determined by the order registered.  Multiple targets can
       
   464  * be specified.
       
   465  *
       
   466  * Events can only bubble if emitFacade is true.
       
   467  *
       
   468  * Included in the event-custom-complex submodule.
       
   469  *
       
   470  * @method addTarget
       
   471  * @param o {EventTarget} the target to add
       
   472  * @for EventTarget
       
   473  */
       
   474 ETProto.addTarget = function(o) {
       
   475     var etState = this._yuievt;
       
   476 
       
   477     if (!etState.targets) {
       
   478         etState.targets = {};
       
   479     }
       
   480 
       
   481     etState.targets[Y.stamp(o)] = o;
       
   482     etState.hasTargets = true;
       
   483 };
       
   484 
       
   485 /**
       
   486  * Returns an array of bubble targets for this object.
       
   487  * @method getTargets
       
   488  * @return EventTarget[]
       
   489  */
       
   490 ETProto.getTargets = function() {
       
   491     var targets = this._yuievt.targets;
       
   492     return targets ? YObject.values(targets) : [];
       
   493 };
       
   494 
       
   495 /**
       
   496  * Removes a bubble target
       
   497  * @method removeTarget
       
   498  * @param o {EventTarget} the target to remove
       
   499  * @for EventTarget
       
   500  */
       
   501 ETProto.removeTarget = function(o) {
       
   502     var targets = this._yuievt.targets;
       
   503 
       
   504     if (targets) {
       
   505         delete targets[Y.stamp(o, true)];
       
   506 
       
   507         if (YObject.size(targets) === 0) {
       
   508             this._yuievt.hasTargets = false;
       
   509         }
       
   510     }
       
   511 };
       
   512 
       
   513 /**
       
   514  * Propagate an event.  Requires the event-custom-complex module.
       
   515  * @method bubble
       
   516  * @param evt {CustomEvent} the custom event to propagate
       
   517  * @return {boolean} the aggregated return value from Event.Custom.fire
       
   518  * @for EventTarget
       
   519  */
       
   520 ETProto.bubble = function(evt, args, target, es) {
       
   521 
       
   522     var targs = this._yuievt.targets,
       
   523         ret = true,
       
   524         t,
       
   525         ce,
       
   526         i,
       
   527         bc,
       
   528         ce2,
       
   529         type = evt && evt.type,
       
   530         originalTarget = target || (evt && evt.target) || this,
       
   531         oldbubble;
       
   532 
       
   533     if (!evt || ((!evt.stopped) && targs)) {
       
   534 
       
   535         for (i in targs) {
       
   536             if (targs.hasOwnProperty(i)) {
       
   537 
       
   538                 t = targs[i];
       
   539 
       
   540                 ce = t._yuievt.events[type];
       
   541 
       
   542                 if (t._hasSiblings) {
       
   543                     ce2 = t.getSibling(type, ce);
       
   544                 }
       
   545 
       
   546                 if (ce2 && !ce) {
       
   547                     ce = t.publish(type);
       
   548                 }
       
   549 
       
   550                 oldbubble = t._yuievt.bubbling;
       
   551                 t._yuievt.bubbling = type;
       
   552 
       
   553                 // if this event was not published on the bubble target,
       
   554                 // continue propagating the event.
       
   555                 if (!ce) {
       
   556                     if (t._yuievt.hasTargets) {
       
   557                         t.bubble(evt, args, originalTarget, es);
       
   558                     }
       
   559                 } else {
       
   560 
       
   561                     if (ce2) {
       
   562                         ce.sibling = ce2;
       
   563                     }
       
   564 
       
   565                     // set the original target to that the target payload on the facade is correct.
       
   566                     ce.target = originalTarget;
       
   567                     ce.originalTarget = originalTarget;
       
   568                     ce.currentTarget = t;
       
   569                     bc = ce.broadcast;
       
   570                     ce.broadcast = false;
       
   571 
       
   572                     // default publish may not have emitFacade true -- that
       
   573                     // shouldn't be what the implementer meant to do
       
   574                     ce.emitFacade = true;
       
   575 
       
   576                     ce.stack = es;
       
   577 
       
   578                     // TODO: See what's getting in the way of changing this to use
       
   579                     // the more performant ce._fire(args || evt.details || []).
       
   580 
       
   581                     // Something in Widget Parent/Child tests is not happy if we
       
   582                     // change it - maybe evt.details related?
       
   583                     ret = ret && ce.fire.apply(ce, args || evt.details || []);
       
   584 
       
   585                     ce.broadcast = bc;
       
   586                     ce.originalTarget = null;
       
   587 
       
   588                     // stopPropagation() was called
       
   589                     if (ce.stopped) {
       
   590                         break;
       
   591                     }
       
   592                 }
       
   593 
       
   594                 t._yuievt.bubbling = oldbubble;
       
   595             }
       
   596         }
       
   597     }
       
   598 
       
   599     return ret;
       
   600 };
       
   601 
       
   602 FACADE = new Y.EventFacade();
       
   603 FACADE_KEYS = {};
       
   604 
       
   605 // Flatten whitelist
       
   606 for (key in FACADE) {
       
   607     FACADE_KEYS[key] = true;
       
   608 }
       
   609 
       
   610 
       
   611 }, '3.10.3', {"requires": ["event-custom-base"]});