[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/event-custom-complex/ -> event-custom-complex.js (source)

   1  /*
   2  YUI 3.17.2 (build 9c3c78e)
   3  Copyright 2014 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 = self.stack,
 167          yuievt = host._yuievt,
 168          hasPotentialSubscribers;
 169  
 170      if (stack) {
 171  
 172          // queue this event if the current item in the queue bubbles
 173          if (self.queuable && self.type !== stack.next.type) {
 174  
 175              if (!stack.queue) {
 176                  stack.queue = [];
 177              }
 178              stack.queue.push([self, args]);
 179  
 180              return true;
 181          }
 182      }
 183  
 184      hasPotentialSubscribers = self.hasSubs() || yuievt.hasTargets || self.broadcast;
 185  
 186      self.target = self.target || host;
 187      self.currentTarget = host;
 188  
 189      self.details = args.concat();
 190  
 191      if (hasPotentialSubscribers) {
 192  
 193          es = stack || {
 194  
 195             id: self.id, // id of the first event in the stack
 196             next: self,
 197             silent: self.silent,
 198             stopped: 0,
 199             prevented: 0,
 200             bubbling: null,
 201             type: self.type,
 202             // defaultFnQueue: new Y.Queue(),
 203             defaultTargetOnly: self.defaultTargetOnly
 204  
 205          };
 206  
 207          subs = self.getSubs();
 208          ons = subs[0];
 209          afters = subs[1];
 210  
 211          self.stopped = (self.type !== es.type) ? 0 : es.stopped;
 212          self.prevented = (self.type !== es.type) ? 0 : es.prevented;
 213  
 214          if (self.stoppedFn) {
 215              // PERF TODO: Can we replace with callback, like preventedFn. Look into history
 216              events = new Y.EventTarget({
 217                  fireOnce: true,
 218                  context: host
 219              });
 220              self.events = events;
 221              events.on('stopped', self.stoppedFn);
 222          }
 223  
 224  
 225          self._facade = null; // kill facade to eliminate stale properties
 226  
 227          ef = self._createFacade(args);
 228  
 229          if (ons) {
 230              self._procSubs(ons, args, ef);
 231          }
 232  
 233          // bubble if this is hosted in an event target and propagation has not been stopped
 234          if (self.bubbles && host.bubble && !self.stopped) {
 235              oldbubble = es.bubbling;
 236  
 237              es.bubbling = self.type;
 238  
 239              if (es.type !== self.type) {
 240                  es.stopped = 0;
 241                  es.prevented = 0;
 242              }
 243  
 244              ret = host.bubble(self, args, null, es);
 245  
 246              self.stopped = Math.max(self.stopped, es.stopped);
 247              self.prevented = Math.max(self.prevented, es.prevented);
 248  
 249              es.bubbling = oldbubble;
 250          }
 251  
 252          prevented = self.prevented;
 253  
 254          if (prevented) {
 255              preventedFn = self.preventedFn;
 256              if (preventedFn) {
 257                  preventedFn.apply(host, args);
 258              }
 259          } else {
 260              defaultFn = self.defaultFn;
 261  
 262              if (defaultFn && ((!self.defaultTargetOnly && !es.defaultTargetOnly) || host === ef.target)) {
 263                  defaultFn.apply(host, args);
 264              }
 265          }
 266  
 267          // broadcast listeners are fired as discreet events on the
 268          // YUI instance and potentially the YUI global.
 269          if (self.broadcast) {
 270              self._broadcast(args);
 271          }
 272  
 273          if (afters && !self.prevented && self.stopped < 2) {
 274  
 275              // Queue the after
 276              afterQueue = es.afterQueue;
 277  
 278              if (es.id === self.id || self.type !== yuievt.bubbling) {
 279  
 280                  self._procSubs(afters, args, ef);
 281  
 282                  if (afterQueue) {
 283                      while ((next = afterQueue.last())) {
 284                          next();
 285                      }
 286                  }
 287              } else {
 288                  postponed = afters;
 289  
 290                  if (es.execDefaultCnt) {
 291                      postponed = Y.merge(postponed);
 292  
 293                      Y.each(postponed, function(s) {
 294                          s.postponed = true;
 295                      });
 296                  }
 297  
 298                  if (!afterQueue) {
 299                      es.afterQueue = new Y.Queue();
 300                  }
 301  
 302                  es.afterQueue.add(function() {
 303                      self._procSubs(postponed, args, ef);
 304                  });
 305              }
 306  
 307          }
 308  
 309          self.target = null;
 310  
 311          if (es.id === self.id) {
 312  
 313              queue = es.queue;
 314  
 315              if (queue) {
 316                  while (queue.length) {
 317                      q = queue.pop();
 318                      ce = q[0];
 319                      // set up stack to allow the next item to be processed
 320                      es.next = ce;
 321                      ce._fire(q[1]);
 322                  }
 323              }
 324  
 325              self.stack = null;
 326          }
 327  
 328          ret = !(self.stopped);
 329  
 330          if (self.type !== yuievt.bubbling) {
 331              es.stopped = 0;
 332              es.prevented = 0;
 333              self.stopped = 0;
 334              self.prevented = 0;
 335          }
 336  
 337      } else {
 338          defaultFn = self.defaultFn;
 339  
 340          if(defaultFn) {
 341              ef = self._createFacade(args);
 342  
 343              if ((!self.defaultTargetOnly) || (host === ef.target)) {
 344                  defaultFn.apply(host, args);
 345              }
 346          }
 347      }
 348  
 349      // Kill the cached facade to free up memory.
 350      // Otherwise we have the facade from the last fire, sitting around forever.
 351      self._facade = null;
 352  
 353      return ret;
 354  };
 355  
 356  /**
 357   * @method _hasPotentialSubscribers
 358   * @for CustomEvent
 359   * @private
 360   * @return {boolean} Whether the event has potential subscribers or not
 361   */
 362  CEProto._hasPotentialSubscribers = function() {
 363      return this.hasSubs() || this.host._yuievt.hasTargets || this.broadcast;
 364  };
 365  
 366  /**
 367   * Internal utility method to create a new facade instance and
 368   * insert it into the fire argument list, accounting for any payload
 369   * merging which needs to happen.
 370   *
 371   * This used to be called `_getFacade`, but the name seemed inappropriate
 372   * when it was used without a need for the return value.
 373   *
 374   * @method _createFacade
 375   * @private
 376   * @param fireArgs {Array} The arguments passed to "fire", which need to be
 377   * shifted (and potentially merged) when the facade is added.
 378   * @return {EventFacade} The event facade created.
 379   */
 380  
 381  // TODO: Remove (private) _getFacade alias, once synthetic.js is updated.
 382  CEProto._createFacade = CEProto._getFacade = function(fireArgs) {
 383  
 384      var userArgs = this.details,
 385          firstArg = userArgs && userArgs[0],
 386          firstArgIsObj = (firstArg && (typeof firstArg === "object")),
 387          ef = this._facade;
 388  
 389      if (!ef) {
 390          ef = new Y.EventFacade(this, this.currentTarget);
 391      }
 392  
 393      if (firstArgIsObj) {
 394          // protect the event facade properties
 395          mixFacadeProps(ef, firstArg);
 396  
 397          // Allow the event type to be faked http://yuilibrary.com/projects/yui3/ticket/2528376
 398          if (firstArg.type) {
 399              ef.type = firstArg.type;
 400          }
 401  
 402          if (fireArgs) {
 403              fireArgs[0] = ef;
 404          }
 405      } else {
 406          if (fireArgs) {
 407              fireArgs.unshift(ef);
 408          }
 409      }
 410  
 411      // update the details field with the arguments
 412      ef.details = this.details;
 413  
 414      // use the original target when the event bubbled to this target
 415      ef.target = this.originalTarget || this.target;
 416  
 417      ef.currentTarget = this.currentTarget;
 418      ef.stopped = 0;
 419      ef.prevented = 0;
 420  
 421      this._facade = ef;
 422  
 423      return this._facade;
 424  };
 425  
 426  /**
 427   * Utility method to manipulate the args array passed in, to add the event facade,
 428   * if it's not already the first arg.
 429   *
 430   * @method _addFacadeToArgs
 431   * @private
 432   * @param {Array} The arguments to manipulate
 433   */
 434  CEProto._addFacadeToArgs = function(args) {
 435      var e = args[0];
 436  
 437      // Trying not to use instanceof, just to avoid potential cross Y edge case issues.
 438      if (!(e && e.halt && e.stopImmediatePropagation && e.stopPropagation && e._event)) {
 439          this._createFacade(args);
 440      }
 441  };
 442  
 443  /**
 444   * Stop propagation to bubble targets
 445   * @for CustomEvent
 446   * @method stopPropagation
 447   */
 448  CEProto.stopPropagation = function() {
 449      this.stopped = 1;
 450      if (this.stack) {
 451          this.stack.stopped = 1;
 452      }
 453      if (this.events) {
 454          this.events.fire('stopped', this);
 455      }
 456  };
 457  
 458  /**
 459   * Stops propagation to bubble targets, and prevents any remaining
 460   * subscribers on the current target from executing.
 461   * @method stopImmediatePropagation
 462   */
 463  CEProto.stopImmediatePropagation = function() {
 464      this.stopped = 2;
 465      if (this.stack) {
 466          this.stack.stopped = 2;
 467      }
 468      if (this.events) {
 469          this.events.fire('stopped', this);
 470      }
 471  };
 472  
 473  /**
 474   * Prevents the execution of this event's defaultFn
 475   * @method preventDefault
 476   */
 477  CEProto.preventDefault = function() {
 478      if (this.preventable) {
 479          this.prevented = 1;
 480          if (this.stack) {
 481              this.stack.prevented = 1;
 482          }
 483      }
 484  };
 485  
 486  /**
 487   * Stops the event propagation and prevents the default
 488   * event behavior.
 489   * @method halt
 490   * @param immediate {boolean} if true additional listeners
 491   * on the current target will not be executed
 492   */
 493  CEProto.halt = function(immediate) {
 494      if (immediate) {
 495          this.stopImmediatePropagation();
 496      } else {
 497          this.stopPropagation();
 498      }
 499      this.preventDefault();
 500  };
 501  
 502  /**
 503   * Registers another EventTarget as a bubble target.  Bubble order
 504   * is determined by the order registered.  Multiple targets can
 505   * be specified.
 506   *
 507   * Events can only bubble if emitFacade is true.
 508   *
 509   * Included in the event-custom-complex submodule.
 510   *
 511   * @method addTarget
 512   * @chainable
 513   * @param o {EventTarget} the target to add
 514   * @for EventTarget
 515   */
 516  ETProto.addTarget = function(o) {
 517      var etState = this._yuievt;
 518  
 519      if (!etState.targets) {
 520          etState.targets = {};
 521      }
 522  
 523      etState.targets[Y.stamp(o)] = o;
 524      etState.hasTargets = true;
 525  
 526      return this;
 527  };
 528  
 529  /**
 530   * Returns an array of bubble targets for this object.
 531   * @method getTargets
 532   * @return EventTarget[]
 533   */
 534  ETProto.getTargets = function() {
 535      var targets = this._yuievt.targets;
 536      return targets ? YObject.values(targets) : [];
 537  };
 538  
 539  /**
 540   * Removes a bubble target
 541   * @method removeTarget
 542   * @chainable
 543   * @param o {EventTarget} the target to remove
 544   * @for EventTarget
 545   */
 546  ETProto.removeTarget = function(o) {
 547      var targets = this._yuievt.targets;
 548  
 549      if (targets) {
 550          delete targets[Y.stamp(o, true)];
 551  
 552          if (YObject.size(targets) === 0) {
 553              this._yuievt.hasTargets = false;
 554          }
 555      }
 556  
 557      return this;
 558  };
 559  
 560  /**
 561   * Propagate an event.  Requires the event-custom-complex module.
 562   * @method bubble
 563   * @param evt {CustomEvent} the custom event to propagate
 564   * @return {boolean} the aggregated return value from Event.Custom.fire
 565   * @for EventTarget
 566   */
 567  ETProto.bubble = function(evt, args, target, es) {
 568  
 569      var targs = this._yuievt.targets,
 570          ret = true,
 571          t,
 572          ce,
 573          i,
 574          bc,
 575          ce2,
 576          type = evt && evt.type,
 577          originalTarget = target || (evt && evt.target) || this,
 578          oldbubble;
 579  
 580      if (!evt || ((!evt.stopped) && targs)) {
 581  
 582          for (i in targs) {
 583              if (targs.hasOwnProperty(i)) {
 584  
 585                  t = targs[i];
 586  
 587                  ce = t._yuievt.events[type];
 588  
 589                  if (t._hasSiblings) {
 590                      ce2 = t.getSibling(type, ce);
 591                  }
 592  
 593                  if (ce2 && !ce) {
 594                      ce = t.publish(type);
 595                  }
 596  
 597                  oldbubble = t._yuievt.bubbling;
 598                  t._yuievt.bubbling = type;
 599  
 600                  // if this event was not published on the bubble target,
 601                  // continue propagating the event.
 602                  if (!ce) {
 603                      if (t._yuievt.hasTargets) {
 604                          t.bubble(evt, args, originalTarget, es);
 605                      }
 606                  } else {
 607  
 608                      if (ce2) {
 609                          ce.sibling = ce2;
 610                      }
 611  
 612                      // set the original target to that the target payload on the facade is correct.
 613                      ce.target = originalTarget;
 614                      ce.originalTarget = originalTarget;
 615                      ce.currentTarget = t;
 616                      bc = ce.broadcast;
 617                      ce.broadcast = false;
 618  
 619                      // default publish may not have emitFacade true -- that
 620                      // shouldn't be what the implementer meant to do
 621                      ce.emitFacade = true;
 622  
 623                      ce.stack = es;
 624  
 625                      // TODO: See what's getting in the way of changing this to use
 626                      // the more performant ce._fire(args || evt.details || []).
 627  
 628                      // Something in Widget Parent/Child tests is not happy if we
 629                      // change it - maybe evt.details related?
 630                      ret = ret && ce.fire.apply(ce, args || evt.details || []);
 631  
 632                      ce.broadcast = bc;
 633                      ce.originalTarget = null;
 634  
 635                      // stopPropagation() was called
 636                      if (ce.stopped) {
 637                          break;
 638                      }
 639                  }
 640  
 641                  t._yuievt.bubbling = oldbubble;
 642              }
 643          }
 644      }
 645  
 646      return ret;
 647  };
 648  
 649  /**
 650   * @method _hasPotentialSubscribers
 651   * @for EventTarget
 652   * @private
 653   * @param {String} fullType The fully prefixed type name
 654   * @return {boolean} Whether the event has potential subscribers or not
 655   */
 656  ETProto._hasPotentialSubscribers = function(fullType) {
 657  
 658      var etState = this._yuievt,
 659          e = etState.events[fullType];
 660  
 661      if (e) {
 662          return e.hasSubs() || etState.hasTargets  || e.broadcast;
 663      } else {
 664          return false;
 665      }
 666  };
 667  
 668  FACADE = new Y.EventFacade();
 669  FACADE_KEYS = {};
 670  
 671  // Flatten whitelist
 672  for (key in FACADE) {
 673      FACADE_KEYS[key] = true;
 674  }
 675  
 676  
 677  }, '3.17.2', {"requires": ["event-custom-base"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1