NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / event-custom-complex / event-custom-complex-debug.js
blob0329fb1abbb3ade0bebf48ac9768f1a9dd56f26b
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('event-custom-complex', function (Y, NAME) {
11 /**
12  * Adds event facades, preventable default behavior, and bubbling.
13  * events.
14  * @module event-custom
15  * @submodule event-custom-complex
16  */
18 var FACADE,
19     FACADE_KEYS,
20     YObject = Y.Object,
21     key,
22     EMPTY = {},
23     CEProto = Y.CustomEvent.prototype,
24     ETProto = Y.EventTarget.prototype,
26     mixFacadeProps = function(facade, payload) {
27         var p;
29         for (p in payload) {
30             if (!(FACADE_KEYS.hasOwnProperty(p))) {
31                 facade[p] = payload[p];
32             }
33         }
34     };
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  */
44 Y.EventFacade = function(e, currentTarget) {
46     if (!e) {
47         e = EMPTY;
48     }
50     this._event = e;
52     /**
53      * The arguments passed to fire
54      * @property details
55      * @type Array
56      */
57     this.details = e.details;
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;
66     /**
67      * The real event type
68      * @property _type
69      * @type string
70      * @private
71      */
72     this._type = e.type;
74     //////////////////////////////////////////////////////
76     /**
77      * Node reference for the targeted eventtarget
78      * @property target
79      * @type Node
80      */
81     this.target = e.target;
83     /**
84      * Node reference for the element that the listener was attached to.
85      * @property currentTarget
86      * @type Node
87      */
88     this.currentTarget = currentTarget;
90     /**
91      * Node reference to the relatedTarget
92      * @property relatedTarget
93      * @type Node
94      */
95     this.relatedTarget = e.relatedTarget;
99 Y.mix(Y.EventFacade.prototype, {
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     },
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     },
121     /**
122      * Prevents the event's default behavior
123      * @method preventDefault
124      */
125     preventDefault: function() {
126         this._event.preventDefault();
127         this.prevented = 1;
128     },
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     }
145 CEProto.fireComplex = function(args) {
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;
170     if (stack) {
172         // queue this event if the current item in the queue bubbles
173         if (self.queuable && self.type !== stack.next.type) {
174             self.log('queue ' + self.type);
176             if (!stack.queue) {
177                 stack.queue = [];
178             }
179             stack.queue.push([self, args]);
181             return true;
182         }
183     }
185     hasPotentialSubscribers = self.hasSubs() || yuievt.hasTargets || self.broadcast;
187     self.target = self.target || host;
188     self.currentTarget = host;
190     self.details = args.concat();
192     if (hasPotentialSubscribers) {
194         es = stack || {
196            id: self.id, // id of the first event in the stack
197            next: self,
198            silent: self.silent,
199            stopped: 0,
200            prevented: 0,
201            bubbling: null,
202            type: self.type,
203            // defaultFnQueue: new Y.Queue(),
204            defaultTargetOnly: self.defaultTargetOnly
206         };
208         subs = self.getSubs();
209         ons = subs[0];
210         afters = subs[1];
212         self.stopped = (self.type !== es.type) ? 0 : es.stopped;
213         self.prevented = (self.type !== es.type) ? 0 : es.prevented;
215         if (self.stoppedFn) {
216             // PERF TODO: Can we replace with callback, like preventedFn. Look into history
217             events = new Y.EventTarget({
218                 fireOnce: true,
219                 context: host
220             });
221             self.events = events;
222             events.on('stopped', self.stoppedFn);
223         }
225         // self.log("Firing " + self  + ", " + "args: " + args);
226         self.log("Firing " + self.type);
228         self._facade = null; // kill facade to eliminate stale properties
230         ef = self._createFacade(args);
232         if (ons) {
233             self._procSubs(ons, args, ef);
234         }
236         // bubble if this is hosted in an event target and propagation has not been stopped
237         if (self.bubbles && host.bubble && !self.stopped) {
238             oldbubble = es.bubbling;
240             es.bubbling = self.type;
242             if (es.type !== self.type) {
243                 es.stopped = 0;
244                 es.prevented = 0;
245             }
247             ret = host.bubble(self, args, null, es);
249             self.stopped = Math.max(self.stopped, es.stopped);
250             self.prevented = Math.max(self.prevented, es.prevented);
252             es.bubbling = oldbubble;
253         }
255         prevented = self.prevented;
257         if (prevented) {
258             preventedFn = self.preventedFn;
259             if (preventedFn) {
260                 preventedFn.apply(host, args);
261             }
262         } else {
263             defaultFn = self.defaultFn;
265             if (defaultFn && ((!self.defaultTargetOnly && !es.defaultTargetOnly) || host === ef.target)) {
266                 defaultFn.apply(host, args);
267             }
268         }
270         // broadcast listeners are fired as discreet events on the
271         // YUI instance and potentially the YUI global.
272         if (self.broadcast) {
273             self._broadcast(args);
274         }
276         if (afters && !self.prevented && self.stopped < 2) {
278             // Queue the after
279             afterQueue = es.afterQueue;
281             if (es.id === self.id || self.type !== yuievt.bubbling) {
283                 self._procSubs(afters, args, ef);
285                 if (afterQueue) {
286                     while ((next = afterQueue.last())) {
287                         next();
288                     }
289                 }
290             } else {
291                 postponed = afters;
293                 if (es.execDefaultCnt) {
294                     postponed = Y.merge(postponed);
296                     Y.each(postponed, function(s) {
297                         s.postponed = true;
298                     });
299                 }
301                 if (!afterQueue) {
302                     es.afterQueue = new Y.Queue();
303                 }
305                 es.afterQueue.add(function() {
306                     self._procSubs(postponed, args, ef);
307                 });
308             }
310         }
312         self.target = null;
314         if (es.id === self.id) {
316             queue = es.queue;
318             if (queue) {
319                 while (queue.length) {
320                     q = queue.pop();
321                     ce = q[0];
322                     // set up stack to allow the next item to be processed
323                     es.next = ce;
324                     ce._fire(q[1]);
325                 }
326             }
328             self.stack = null;
329         }
331         ret = !(self.stopped);
333         if (self.type !== yuievt.bubbling) {
334             es.stopped = 0;
335             es.prevented = 0;
336             self.stopped = 0;
337             self.prevented = 0;
338         }
340     } else {
341         defaultFn = self.defaultFn;
343         if(defaultFn) {
344             ef = self._createFacade(args);
346             if ((!self.defaultTargetOnly) || (host === ef.target)) {
347                 defaultFn.apply(host, args);
348             }
349         }
350     }
352     // Kill the cached facade to free up memory.
353     // Otherwise we have the facade from the last fire, sitting around forever.
354     self._facade = null;
356     return ret;
360  * @method _hasPotentialSubscribers
361  * @for CustomEvent
362  * @private
363  * @return {boolean} Whether the event has potential subscribers or not
364  */
365 CEProto._hasPotentialSubscribers = function() {
366     return this.hasSubs() || this.host._yuievt.hasTargets || this.broadcast;
370  * Internal utility method to create a new facade instance and
371  * insert it into the fire argument list, accounting for any payload
372  * merging which needs to happen.
374  * This used to be called `_getFacade`, but the name seemed inappropriate
375  * when it was used without a need for the return value.
377  * @method _createFacade
378  * @private
379  * @param fireArgs {Array} The arguments passed to "fire", which need to be
380  * shifted (and potentially merged) when the facade is added.
381  * @return {EventFacade} The event facade created.
382  */
384 // TODO: Remove (private) _getFacade alias, once synthetic.js is updated.
385 CEProto._createFacade = CEProto._getFacade = function(fireArgs) {
387     var userArgs = this.details,
388         firstArg = userArgs && userArgs[0],
389         firstArgIsObj = (firstArg && (typeof firstArg === "object")),
390         ef = this._facade;
392     if (!ef) {
393         ef = new Y.EventFacade(this, this.currentTarget);
394     }
396     if (firstArgIsObj) {
397         // protect the event facade properties
398         mixFacadeProps(ef, firstArg);
400         // Allow the event type to be faked http://yuilibrary.com/projects/yui3/ticket/2528376
401         if (firstArg.type) {
402             ef.type = firstArg.type;
403         }
405         if (fireArgs) {
406             fireArgs[0] = ef;
407         }
408     } else {
409         if (fireArgs) {
410             fireArgs.unshift(ef);
411         }
412     }
414     // update the details field with the arguments
415     ef.details = this.details;
417     // use the original target when the event bubbled to this target
418     ef.target = this.originalTarget || this.target;
420     ef.currentTarget = this.currentTarget;
421     ef.stopped = 0;
422     ef.prevented = 0;
424     this._facade = ef;
426     return this._facade;
430  * Utility method to manipulate the args array passed in, to add the event facade,
431  * if it's not already the first arg.
433  * @method _addFacadeToArgs
434  * @private
435  * @param {Array} The arguments to manipulate
436  */
437 CEProto._addFacadeToArgs = function(args) {
438     var e = args[0];
440     // Trying not to use instanceof, just to avoid potential cross Y edge case issues.
441     if (!(e && e.halt && e.stopImmediatePropagation && e.stopPropagation && e._event)) {
442         this._createFacade(args);
443     }
447  * Stop propagation to bubble targets
448  * @for CustomEvent
449  * @method stopPropagation
450  */
451 CEProto.stopPropagation = function() {
452     this.stopped = 1;
453     if (this.stack) {
454         this.stack.stopped = 1;
455     }
456     if (this.events) {
457         this.events.fire('stopped', this);
458     }
462  * Stops propagation to bubble targets, and prevents any remaining
463  * subscribers on the current target from executing.
464  * @method stopImmediatePropagation
465  */
466 CEProto.stopImmediatePropagation = function() {
467     this.stopped = 2;
468     if (this.stack) {
469         this.stack.stopped = 2;
470     }
471     if (this.events) {
472         this.events.fire('stopped', this);
473     }
477  * Prevents the execution of this event's defaultFn
478  * @method preventDefault
479  */
480 CEProto.preventDefault = function() {
481     if (this.preventable) {
482         this.prevented = 1;
483         if (this.stack) {
484             this.stack.prevented = 1;
485         }
486     }
490  * Stops the event propagation and prevents the default
491  * event behavior.
492  * @method halt
493  * @param immediate {boolean} if true additional listeners
494  * on the current target will not be executed
495  */
496 CEProto.halt = function(immediate) {
497     if (immediate) {
498         this.stopImmediatePropagation();
499     } else {
500         this.stopPropagation();
501     }
502     this.preventDefault();
506  * Registers another EventTarget as a bubble target.  Bubble order
507  * is determined by the order registered.  Multiple targets can
508  * be specified.
510  * Events can only bubble if emitFacade is true.
512  * Included in the event-custom-complex submodule.
514  * @method addTarget
515  * @chainable
516  * @param o {EventTarget} the target to add
517  * @for EventTarget
518  */
519 ETProto.addTarget = function(o) {
520     var etState = this._yuievt;
522     if (!etState.targets) {
523         etState.targets = {};
524     }
526     etState.targets[Y.stamp(o)] = o;
527     etState.hasTargets = true;
529     return this;
533  * Returns an array of bubble targets for this object.
534  * @method getTargets
535  * @return EventTarget[]
536  */
537 ETProto.getTargets = function() {
538     var targets = this._yuievt.targets;
539     return targets ? YObject.values(targets) : [];
543  * Removes a bubble target
544  * @method removeTarget
545  * @chainable
546  * @param o {EventTarget} the target to remove
547  * @for EventTarget
548  */
549 ETProto.removeTarget = function(o) {
550     var targets = this._yuievt.targets;
552     if (targets) {
553         delete targets[Y.stamp(o, true)];
555         if (YObject.size(targets) === 0) {
556             this._yuievt.hasTargets = false;
557         }
558     }
560     return this;
564  * Propagate an event.  Requires the event-custom-complex module.
565  * @method bubble
566  * @param evt {CustomEvent} the custom event to propagate
567  * @return {boolean} the aggregated return value from Event.Custom.fire
568  * @for EventTarget
569  */
570 ETProto.bubble = function(evt, args, target, es) {
572     var targs = this._yuievt.targets,
573         ret = true,
574         t,
575         ce,
576         i,
577         bc,
578         ce2,
579         type = evt && evt.type,
580         originalTarget = target || (evt && evt.target) || this,
581         oldbubble;
583     if (!evt || ((!evt.stopped) && targs)) {
585         for (i in targs) {
586             if (targs.hasOwnProperty(i)) {
588                 t = targs[i];
590                 ce = t._yuievt.events[type];
592                 if (t._hasSiblings) {
593                     ce2 = t.getSibling(type, ce);
594                 }
596                 if (ce2 && !ce) {
597                     ce = t.publish(type);
598                 }
600                 oldbubble = t._yuievt.bubbling;
601                 t._yuievt.bubbling = type;
603                 // if this event was not published on the bubble target,
604                 // continue propagating the event.
605                 if (!ce) {
606                     if (t._yuievt.hasTargets) {
607                         t.bubble(evt, args, originalTarget, es);
608                     }
609                 } else {
611                     if (ce2) {
612                         ce.sibling = ce2;
613                     }
615                     // set the original target to that the target payload on the facade is correct.
616                     ce.target = originalTarget;
617                     ce.originalTarget = originalTarget;
618                     ce.currentTarget = t;
619                     bc = ce.broadcast;
620                     ce.broadcast = false;
622                     // default publish may not have emitFacade true -- that
623                     // shouldn't be what the implementer meant to do
624                     ce.emitFacade = true;
626                     ce.stack = es;
628                     // TODO: See what's getting in the way of changing this to use
629                     // the more performant ce._fire(args || evt.details || []).
631                     // Something in Widget Parent/Child tests is not happy if we
632                     // change it - maybe evt.details related?
633                     ret = ret && ce.fire.apply(ce, args || evt.details || []);
635                     ce.broadcast = bc;
636                     ce.originalTarget = null;
638                     // stopPropagation() was called
639                     if (ce.stopped) {
640                         break;
641                     }
642                 }
644                 t._yuievt.bubbling = oldbubble;
645             }
646         }
647     }
649     return ret;
653  * @method _hasPotentialSubscribers
654  * @for EventTarget
655  * @private
656  * @param {String} fullType The fully prefixed type name
657  * @return {boolean} Whether the event has potential subscribers or not
658  */
659 ETProto._hasPotentialSubscribers = function(fullType) {
661     var etState = this._yuievt,
662         e = etState.events[fullType];
664     if (e) {
665         return e.hasSubs() || etState.hasTargets  || e.broadcast;
666     } else {
667         return false;
668     }
671 FACADE = new Y.EventFacade();
672 FACADE_KEYS = {};
674 // Flatten whitelist
675 for (key in FACADE) {
676     FACADE_KEYS[key] = true;
680 }, '3.13.0', {"requires": ["event-custom-base"]});