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