Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
}, '3.18.1', {"requires": ["event-custom-base"]});