AutorÃa | Ultima modificación | Ver Log |
YUI.add('event-custom-complex', function (Y, NAME) {/*** Adds event facades, preventable default behavior, and bubbling.* events.* @module event-custom* @submodule event-custom-complex*/var FACADE,FACADE_KEYS,YObject = Y.Object,key,EMPTY = {},CEProto = Y.CustomEvent.prototype,ETProto = Y.EventTarget.prototype,mixFacadeProps = function(facade, payload) {var p;for (p in payload) {if (!(FACADE_KEYS.hasOwnProperty(p))) {facade[p] = payload[p];}}};/*** Wraps and protects a custom event for use when emitFacade is set to true.* Requires the event-custom-complex module* @class EventFacade* @param e {Event} the custom event* @param currentTarget {HTMLElement} the element the listener was attached to*/Y.EventFacade = function(e, currentTarget) {if (!e) {e = EMPTY;}this._event = e;/*** The arguments passed to fire* @property details* @type Array*/this.details = e.details;/*** The event type, this can be overridden by the fire() payload* @property type* @type string*/this.type = e.type;/*** The real event type* @property _type* @type string* @private*/this._type = e.type;///////////////////////////////////////////////////////*** Node reference for the targeted eventtarget* @property target* @type Node*/this.target = e.target;/*** Node reference for the element that the listener was attached to.* @property currentTarget* @type Node*/this.currentTarget = currentTarget;/*** Node reference to the relatedTarget* @property relatedTarget* @type Node*/this.relatedTarget = e.relatedTarget;};Y.mix(Y.EventFacade.prototype, {/*** Stops the propagation to the next bubble target* @method stopPropagation*/stopPropagation: function() {this._event.stopPropagation();this.stopped = 1;},/*** Stops the propagation to the next bubble target and* prevents any additional listeners from being exectued* on the current target.* @method stopImmediatePropagation*/stopImmediatePropagation: function() {this._event.stopImmediatePropagation();this.stopped = 2;},/*** Prevents the event's default behavior* @method preventDefault*/preventDefault: function() {this._event.preventDefault();this.prevented = 1;},/*** Stops the event propagation and prevents the default* event behavior.* @method halt* @param immediate {boolean} if true additional listeners* on the current target will not be executed*/halt: function(immediate) {this._event.halt(immediate);this.prevented = 1;this.stopped = (immediate) ? 2 : 1;}});CEProto.fireComplex = function(args) {var es,ef,q,queue,ce,ret = true,events,subs,ons,afters,afterQueue,postponed,prevented,preventedFn,defaultFn,self = this,host = self.host || self,next,oldbubble,stack = self.stack,yuievt = host._yuievt,hasPotentialSubscribers;if (stack) {// queue this event if the current item in the queue bubblesif (self.queuable && self.type !== stack.next.type) {self.log('queue ' + self.type);if (!stack.queue) {stack.queue = [];}stack.queue.push([self, args]);return true;}}hasPotentialSubscribers = self.hasSubs() || yuievt.hasTargets || self.broadcast;self.target = self.target || host;self.currentTarget = host;self.details = args.concat();if (hasPotentialSubscribers) {es = stack || {id: self.id, // id of the first event in the stacknext: self,silent: self.silent,stopped: 0,prevented: 0,bubbling: null,type: self.type,// defaultFnQueue: new Y.Queue(),defaultTargetOnly: self.defaultTargetOnly};subs = self.getSubs();ons = subs[0];afters = subs[1];self.stopped = (self.type !== es.type) ? 0 : es.stopped;self.prevented = (self.type !== es.type) ? 0 : es.prevented;if (self.stoppedFn) {// PERF TODO: Can we replace with callback, like preventedFn. Look into historyevents = new Y.EventTarget({fireOnce: true,context: host});self.events = events;events.on('stopped', self.stoppedFn);}// self.log("Firing " + self + ", " + "args: " + args);self.log("Firing " + self.type);self._facade = null; // kill facade to eliminate stale propertiesef = self._createFacade(args);if (ons) {self._procSubs(ons, args, ef);}// bubble if this is hosted in an event target and propagation has not been stoppedif (self.bubbles && host.bubble && !self.stopped) {oldbubble = es.bubbling;es.bubbling = self.type;if (es.type !== self.type) {es.stopped = 0;es.prevented = 0;}ret = host.bubble(self, args, null, es);self.stopped = Math.max(self.stopped, es.stopped);self.prevented = Math.max(self.prevented, es.prevented);es.bubbling = oldbubble;}prevented = self.prevented;if (prevented) {preventedFn = self.preventedFn;if (preventedFn) {preventedFn.apply(host, args);}} else {defaultFn = self.defaultFn;if (defaultFn && ((!self.defaultTargetOnly && !es.defaultTargetOnly) || host === ef.target)) {defaultFn.apply(host, args);}}// broadcast listeners are fired as discreet events on the// YUI instance and potentially the YUI global.if (self.broadcast) {self._broadcast(args);}if (afters && !self.prevented && self.stopped < 2) {// Queue the afterafterQueue = es.afterQueue;if (es.id === self.id || self.type !== yuievt.bubbling) {self._procSubs(afters, args, ef);if (afterQueue) {while ((next = afterQueue.last())) {next();}}} else {postponed = afters;if (es.execDefaultCnt) {postponed = Y.merge(postponed);Y.each(postponed, function(s) {s.postponed = true;});}if (!afterQueue) {es.afterQueue = new Y.Queue();}es.afterQueue.add(function() {self._procSubs(postponed, args, ef);});}}self.target = null;if (es.id === self.id) {queue = es.queue;if (queue) {while (queue.length) {q = queue.pop();ce = q[0];// set up stack to allow the next item to be processedes.next = ce;ce._fire(q[1]);}}self.stack = null;}ret = !(self.stopped);if (self.type !== yuievt.bubbling) {es.stopped = 0;es.prevented = 0;self.stopped = 0;self.prevented = 0;}} else {defaultFn = self.defaultFn;if(defaultFn) {ef = self._createFacade(args);if ((!self.defaultTargetOnly) || (host === ef.target)) {defaultFn.apply(host, args);}}}// Kill the cached facade to free up memory.// Otherwise we have the facade from the last fire, sitting around forever.self._facade = null;return ret;};/*** @method _hasPotentialSubscribers* @for CustomEvent* @private* @return {boolean} Whether the event has potential subscribers or not*/CEProto._hasPotentialSubscribers = function() {return this.hasSubs() || this.host._yuievt.hasTargets || this.broadcast;};/*** Internal utility method to create a new facade instance and* insert it into the fire argument list, accounting for any payload* merging which needs to happen.** This used to be called `_getFacade`, but the name seemed inappropriate* when it was used without a need for the return value.** @method _createFacade* @private* @param fireArgs {Array} The arguments passed to "fire", which need to be* shifted (and potentially merged) when the facade is added.* @return {EventFacade} The event facade created.*/// TODO: Remove (private) _getFacade alias, once synthetic.js is updated.CEProto._createFacade = CEProto._getFacade = function(fireArgs) {var userArgs = this.details,firstArg = userArgs && userArgs[0],firstArgIsObj = (firstArg && (typeof firstArg === "object")),ef = this._facade;if (!ef) {ef = new Y.EventFacade(this, this.currentTarget);}if (firstArgIsObj) {// protect the event facade propertiesmixFacadeProps(ef, firstArg);// Allow the event type to be faked http://yuilibrary.com/projects/yui3/ticket/2528376if (firstArg.type) {ef.type = firstArg.type;}if (fireArgs) {fireArgs[0] = ef;}} else {if (fireArgs) {fireArgs.unshift(ef);}}// update the details field with the argumentsef.details = this.details;// use the original target when the event bubbled to this targetef.target = this.originalTarget || this.target;ef.currentTarget = this.currentTarget;ef.stopped = 0;ef.prevented = 0;this._facade = ef;return this._facade;};/*** Utility method to manipulate the args array passed in, to add the event facade,* if it's not already the first arg.** @method _addFacadeToArgs* @private* @param {Array} The arguments to manipulate*/CEProto._addFacadeToArgs = function(args) {var e = args[0];// Trying not to use instanceof, just to avoid potential cross Y edge case issues.if (!(e && e.halt && e.stopImmediatePropagation && e.stopPropagation && e._event)) {this._createFacade(args);}};/*** Stop propagation to bubble targets* @for CustomEvent* @method stopPropagation*/CEProto.stopPropagation = function() {this.stopped = 1;if (this.stack) {this.stack.stopped = 1;}if (this.events) {this.events.fire('stopped', this);}};/*** Stops propagation to bubble targets, and prevents any remaining* subscribers on the current target from executing.* @method stopImmediatePropagation*/CEProto.stopImmediatePropagation = function() {this.stopped = 2;if (this.stack) {this.stack.stopped = 2;}if (this.events) {this.events.fire('stopped', this);}};/*** Prevents the execution of this event's defaultFn* @method preventDefault*/CEProto.preventDefault = function() {if (this.preventable) {this.prevented = 1;if (this.stack) {this.stack.prevented = 1;}}};/*** Stops the event propagation and prevents the default* event behavior.* @method halt* @param immediate {boolean} if true additional listeners* on the current target will not be executed*/CEProto.halt = function(immediate) {if (immediate) {this.stopImmediatePropagation();} else {this.stopPropagation();}this.preventDefault();};/*** Registers another EventTarget as a bubble target. Bubble order* is determined by the order registered. Multiple targets can* be specified.** Events can only bubble if emitFacade is true.** Included in the event-custom-complex submodule.** @method addTarget* @chainable* @param o {EventTarget} the target to add* @for EventTarget*/ETProto.addTarget = function(o) {var etState = this._yuievt;if (!etState.targets) {etState.targets = {};}etState.targets[Y.stamp(o)] = o;etState.hasTargets = true;return this;};/*** Returns an array of bubble targets for this object.* @method getTargets* @return EventTarget[]*/ETProto.getTargets = function() {var targets = this._yuievt.targets;return targets ? YObject.values(targets) : [];};/*** Removes a bubble target* @method removeTarget* @chainable* @param o {EventTarget} the target to remove* @for EventTarget*/ETProto.removeTarget = function(o) {var targets = this._yuievt.targets;if (targets) {delete targets[Y.stamp(o, true)];if (YObject.size(targets) === 0) {this._yuievt.hasTargets = false;}}return this;};/*** Propagate an event. Requires the event-custom-complex module.* @method bubble* @param evt {CustomEvent} the custom event to propagate* @return {boolean} the aggregated return value from Event.Custom.fire* @for EventTarget*/ETProto.bubble = function(evt, args, target, es) {var targs = this._yuievt.targets,ret = true,t,ce,i,bc,ce2,type = evt && evt.type,originalTarget = target || (evt && evt.target) || this,oldbubble;if (!evt || ((!evt.stopped) && targs)) {for (i in targs) {if (targs.hasOwnProperty(i)) {t = targs[i];ce = t._yuievt.events[type];if (t._hasSiblings) {ce2 = t.getSibling(type, ce);}if (ce2 && !ce) {ce = t.publish(type);}oldbubble = t._yuievt.bubbling;t._yuievt.bubbling = type;// if this event was not published on the bubble target,// continue propagating the event.if (!ce) {if (t._yuievt.hasTargets) {t.bubble(evt, args, originalTarget, es);}} else {if (ce2) {ce.sibling = ce2;}// set the original target to that the target payload on the facade is correct.ce.target = originalTarget;ce.originalTarget = originalTarget;ce.currentTarget = t;bc = ce.broadcast;ce.broadcast = false;// default publish may not have emitFacade true -- that// shouldn't be what the implementer meant to doce.emitFacade = true;ce.stack = es;// TODO: See what's getting in the way of changing this to use// the more performant ce._fire(args || evt.details || []).// Something in Widget Parent/Child tests is not happy if we// change it - maybe evt.details related?ret = ret && ce.fire.apply(ce, args || evt.details || []);ce.broadcast = bc;ce.originalTarget = null;// stopPropagation() was calledif (ce.stopped) {break;}}t._yuievt.bubbling = oldbubble;}}}return ret;};/*** @method _hasPotentialSubscribers* @for EventTarget* @private* @param {String} fullType The fully prefixed type name* @return {boolean} Whether the event has potential subscribers or not*/ETProto._hasPotentialSubscribers = function(fullType) {var etState = this._yuievt,e = etState.events[fullType];if (e) {return e.hasSubs() || etState.hasTargets || e.broadcast;} else {return false;}};FACADE = new Y.EventFacade();FACADE_KEYS = {};// Flatten whitelistfor (key in FACADE) {FACADE_KEYS[key] = true;}}, '3.18.1', {"requires": ["event-custom-base"]});