3
Copyright 2012 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
7
YUI.add('event-custom-base', function(Y) {
10
* Custom event engine, DOM event listener abstraction layer, synthetic DOM
12
* @module event-custom
22
* Custom event engine, DOM event listener abstraction layer, synthetic DOM
24
* @module event-custom
25
* @submodule event-custom-base
29
* Allows for the insertion of methods that are executed before or after
41
* Cache of objects touched by the utility
48
* <p>Execute the supplied method before the specified function. Wrapping
49
* function may optionally return an instance of the following classes to
50
* further alter runtime behavior:</p>
52
* <dt></code>Y.Do.Halt(message, returnValue)</code></dt>
53
* <dd>Immediatly stop execution and return
54
* <code>returnValue</code>. No other wrapping functions will be
56
* <dt></code>Y.Do.AlterArgs(message, newArgArray)</code></dt>
57
* <dd>Replace the arguments that the original function will be
59
* <dt></code>Y.Do.Prevent(message)</code></dt>
60
* <dd>Don't execute the wrapped function. Other before phase
61
* wrappers will be executed.</dd>
65
* @param fn {Function} the function to execute
66
* @param obj the object hosting the method to displace
67
* @param sFn {string} the name of the method to displace
68
* @param c The execution context for fn
69
* @param arg* {mixed} 0..n additional arguments to supply to the subscriber
70
* when the event fires.
71
* @return {string} handle for the subscription
74
before: function(fn, obj, sFn, c) {
75
// Y.log('Do before: ' + sFn, 'info', 'event');
78
a = [fn, c].concat(Y.Array(arguments, 4, true));
79
f = Y.rbind.apply(Y, a);
82
return this._inject(DO_BEFORE, f, obj, sFn);
86
* <p>Execute the supplied method after the specified function. Wrapping
87
* function may optionally return an instance of the following classes to
88
* further alter runtime behavior:</p>
90
* <dt></code>Y.Do.Halt(message, returnValue)</code></dt>
91
* <dd>Immediatly stop execution and return
92
* <code>returnValue</code>. No other wrapping functions will be
94
* <dt></code>Y.Do.AlterReturn(message, returnValue)</code></dt>
95
* <dd>Return <code>returnValue</code> instead of the wrapped
96
* method's original return value. This can be further altered by
97
* other after phase wrappers.</dd>
100
* <p>The static properties <code>Y.Do.originalRetVal</code> and
101
* <code>Y.Do.currentRetVal</code> will be populated for reference.</p>
104
* @param fn {Function} the function to execute
105
* @param obj the object hosting the method to displace
106
* @param sFn {string} the name of the method to displace
107
* @param c The execution context for fn
108
* @param arg* {mixed} 0..n additional arguments to supply to the subscriber
109
* @return {string} handle for the subscription
112
after: function(fn, obj, sFn, c) {
115
a = [fn, c].concat(Y.Array(arguments, 4, true));
116
f = Y.rbind.apply(Y, a);
119
return this._inject(DO_AFTER, f, obj, sFn);
123
* Execute the supplied method before or after the specified function.
124
* Used by <code>before</code> and <code>after</code>.
127
* @param when {string} before or after
128
* @param fn {Function} the function to execute
129
* @param obj the object hosting the method to displace
130
* @param sFn {string} the name of the method to displace
131
* @param c The execution context for fn
132
* @return {string} handle for the subscription
136
_inject: function(when, fn, obj, sFn) {
139
var id = Y.stamp(obj), o, sid;
141
if (! this.objs[id]) {
142
// create a map entry for the obj if it doesn't exist
149
// create a map entry for the method if it doesn't exist
150
o[sFn] = new Y.Do.Method(obj, sFn);
152
// re-route the method to our wrapper
155
return o[sFn].exec.apply(o[sFn], arguments);
160
sid = id + Y.stamp(fn) + sFn;
162
// register the callback
163
o[sFn].register(sid, fn, when);
165
return new Y.EventHandle(o[sFn], sid);
170
* Detach a before or after subscription.
173
* @param handle {string} the subscription handle
176
detach: function(handle) {
184
_unload: function(e, me) {
191
//////////////////////////////////////////////////////////////////////////
194
* Contains the return value from the wrapped method, accessible
195
* by 'after' event listeners.
197
* @property originalRetVal
203
* Contains the current state of the return value, consumable by
204
* 'after' event listeners, and updated if an after subscriber
205
* changes the return value generated by the wrapped function.
207
* @property currentRetVal
212
//////////////////////////////////////////////////////////////////////////
215
* Wrapper for a displaced method with aop enabled
218
* @param obj The object to operate on
219
* @param sFn The name of the method to displace
221
DO.Method = function(obj, sFn) {
223
this.methodName = sFn;
224
this.method = obj[sFn];
230
* Register a aop subscriber
232
* @param sid {string} the subscriber id
233
* @param fn {Function} the function to execute
234
* @param when {string} when to execute the function
236
DO.Method.prototype.register = function (sid, fn, when) {
238
this.after[sid] = fn;
240
this.before[sid] = fn;
245
* Unregister a aop subscriber
247
* @param sid {string} the subscriber id
248
* @param fn {Function} the function to execute
249
* @param when {string} when to execute the function
251
DO.Method.prototype._delete = function (sid) {
252
// Y.log('Y.Do._delete: ' + sid, 'info', 'Event');
253
delete this.before[sid];
254
delete this.after[sid];
258
* <p>Execute the wrapped method. All arguments are passed into the wrapping
259
* functions. If any of the before wrappers return an instance of
260
* <code>Y.Do.Halt</code> or <code>Y.Do.Prevent</code>, neither the wrapped
261
* function nor any after phase subscribers will be executed.</p>
263
* <p>The return value will be the return value of the wrapped function or one
264
* provided by a wrapper function via an instance of <code>Y.Do.Halt</code> or
265
* <code>Y.Do.AlterReturn</code>.
268
* @param arg* {any} Arguments are passed to the wrapping and wrapped functions
269
* @return {any} Return value of wrapped function unless overwritten (see above)
271
DO.Method.prototype.exec = function () {
273
var args = Y.Array(arguments, 0, true),
281
if (bf.hasOwnProperty(i)) {
282
ret = bf[i].apply(this.obj, args);
284
switch (ret.constructor) {
301
ret = this.method.apply(this.obj, args);
304
DO.originalRetVal = ret;
305
DO.currentRetVal = ret;
307
// execute after methods.
309
if (af.hasOwnProperty(i)) {
310
newRet = af[i].apply(this.obj, args);
311
// Stop processing if a Halt object is returned
312
if (newRet && newRet.constructor == DO.Halt) {
313
return newRet.retVal;
314
// Check for a new return value
315
} else if (newRet && newRet.constructor == DO.AlterReturn) {
316
ret = newRet.newRetVal;
317
// Update the static retval state
318
DO.currentRetVal = ret;
326
//////////////////////////////////////////////////////////////////////////
329
* Return an AlterArgs object when you want to change the arguments that
330
* were passed into the function. Useful for Do.before subscribers. An
331
* example would be a service that scrubs out illegal characters prior to
332
* executing the core business logic.
333
* @class Do.AlterArgs
335
* @param msg {String} (optional) Explanation of the altered return value
336
* @param newArgs {Array} Call parameters to be used for the original method
337
* instead of the arguments originally passed in.
339
DO.AlterArgs = function(msg, newArgs) {
341
this.newArgs = newArgs;
345
* Return an AlterReturn object when you want to change the result returned
346
* from the core method to the caller. Useful for Do.after subscribers.
347
* @class Do.AlterReturn
349
* @param msg {String} (optional) Explanation of the altered return value
350
* @param newRetVal {any} Return value passed to code that invoked the wrapped
353
DO.AlterReturn = function(msg, newRetVal) {
355
this.newRetVal = newRetVal;
359
* Return a Halt object when you want to terminate the execution
360
* of all subsequent subscribers as well as the wrapped method
361
* if it has not exectued yet. Useful for Do.before subscribers.
364
* @param msg {String} (optional) Explanation of why the termination was done
365
* @param retVal {any} Return value passed to code that invoked the wrapped
368
DO.Halt = function(msg, retVal) {
370
this.retVal = retVal;
374
* Return a Prevent object when you want to prevent the wrapped function
375
* from executing, but want the remaining listeners to execute. Useful
376
* for Do.before subscribers.
379
* @param msg {String} (optional) Explanation of why the termination was done
381
DO.Prevent = function(msg) {
386
* Return an Error object when you want to terminate the execution
387
* of all subsequent method calls.
390
* @param msg {String} (optional) Explanation of the altered return value
391
* @param retVal {any} Return value passed to code that invoked the wrapped
393
* @deprecated use Y.Do.Halt or Y.Do.Prevent
398
//////////////////////////////////////////////////////////////////////////
400
// Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
404
* Custom event engine, DOM event listener abstraction layer, synthetic DOM
406
* @module event-custom
407
* @submodule event-custom-base
411
// var onsubscribeType = "_event:onsub",
440
* The CustomEvent class lets you define events for your application
441
* that can be subscribed to by one or more independent component.
443
* @param {String} type The type of event, which is passed to the callback
444
* when the event fires.
445
* @param {object} o configuration object.
449
Y.CustomEvent = function(type, o) {
451
// if (arguments.length > 2) {
452
// this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
457
this.id = Y.stamp(this);
460
* The type of event, returned to subscribers when the event fires
467
* The context the the event will fire from by default. Defaults to the YUI
475
* Monitor when an event is attached or detached.
477
* @property monitored
480
// this.monitored = false;
482
this.logSystem = (type == YUI_LOG);
485
* If 0, this event does not broadcast. If 1, the YUI instance is notified
486
* every time this event fires. If 2, the YUI instance and the YUI global
487
* (if event is enabled on the global) are notified every time this event
489
* @property broadcast
492
// this.broadcast = 0;
495
* By default all custom events are logged in the debug build, set silent
496
* to true to disable debug outpu for this event.
500
this.silent = this.logSystem;
503
* Specifies whether this event should be queued when the host is actively
504
* processing an event. This will effect exectution order of the callbacks
505
* for the various events.
510
// this.queuable = false;
513
* The subscribers to this event
514
* @property subscribers
515
* @type Subscriber {}
517
this.subscribers = {};
520
* 'After' subscribers
522
* @type Subscriber {}
527
* This event has fired if true
533
// this.fired = false;
536
* An array containing the arguments the custom event
537
* was last fired with.
538
* @property firedWith
544
* This event should only fire one time if true, and if
545
* it has fired, any new subscribers should be notified
552
// this.fireOnce = false;
555
* fireOnce listeners will fire syncronously unless async
561
//this.async = false;
564
* Flag for stopPropagation that is modified during fire()
565
* 1 means to stop propagation to bubble targets. 2 means
566
* to also stop additional subscribers on this target.
573
* Flag for preventDefault that is modified during fire().
574
* if it is not 0, the default behavior for this event
575
* @property prevented
578
// this.prevented = 0;
581
* Specifies the host for this custom event. This is used
582
* to enable event bubbling
589
* The default function to execute after event listeners
590
* have fire, but only if the default action was not
592
* @property defaultFn
595
// this.defaultFn = null;
598
* The function to execute if a subscriber calls
599
* stopPropagation or stopImmediatePropagation
600
* @property stoppedFn
603
// this.stoppedFn = null;
606
* The function to execute if a subscriber calls
608
* @property preventedFn
611
// this.preventedFn = null;
614
* Specifies whether or not this event's default function
615
* can be cancelled by a subscriber by executing preventDefault()
616
* on the event facade
617
* @property preventable
621
this.preventable = true;
624
* Specifies whether or not a subscriber can stop the event propagation
625
* via stopPropagation(), stopImmediatePropagation(), or halt()
627
* Events can only bubble if emitFacade is true.
636
* Supports multiple options for listener signatures in order to
638
* @property signature
642
this.signature = YUI3_SIGNATURE;
647
// this.hasSubscribers = false;
649
// this.hasAfters = false;
652
* If set to true, the custom event will deliver an EventFacade object
653
* that is similar to a DOM event object.
654
* @property emitFacade
658
// this.emitFacade = false;
660
this.applyConfig(o, true);
662
// this.log("Creating " + this.type);
666
Y.CustomEvent.prototype = {
667
constructor: Y.CustomEvent,
670
* Returns the number of subscribers for this event as the sum of the on()
671
* subscribers and after() subscribers.
676
hasSubs: function(when) {
677
var s = this.subCount, a = this.afterCount, sib = this.sibling;
685
return (when == 'after') ? a : s;
692
* Monitor the event state for the subscribed event. The first parameter
693
* is what should be monitored, the rest are the normal parameters when
694
* subscribing to an event.
696
* @param what {string} what to monitor ('detach', 'attach', 'publish').
697
* @return {EventHandle} return value from the monitor event subscription.
699
monitor: function(what) {
700
this.monitored = true;
701
var type = this.id + '|' + this.type + '_' + what,
702
args = Y.Array(arguments, 0, true);
704
return this.host.on.apply(this.host, args);
708
* Get all of the subscribers to this event and any sibling event
710
* @return {Array} first item is the on subscribers, second the after.
712
getSubs: function() {
713
var s = Y.merge(this.subscribers), a = Y.merge(this.afters), sib = this.sibling;
716
Y.mix(s, sib.subscribers);
717
Y.mix(a, sib.afters);
724
* Apply configuration properties. Only applies the CONFIG whitelist
725
* @method applyConfig
726
* @param o hash of properties to apply.
727
* @param force {boolean} if true, properties that exist on the event
728
* will be overwritten.
730
applyConfig: function(o, force) {
732
Y.mix(this, o, force, CONFIGS);
737
* Create the Subscription for subscribing function, context, and bound
738
* arguments. If this is a fireOnce event, the subscriber is immediately
742
* @param fn {Function} Subscription callback
743
* @param [context] {Object} Override `this` in the callback
744
* @param [args] {Array} bound arguments that will be passed to the callback after the arguments generated by fire()
745
* @param [when] {String} "after" to slot into after subscribers
746
* @return {EventHandle}
749
_on: function(fn, context, args, when) {
752
this.log('Invalid callback for CE: ' + this.type);
755
var s = new Y.Subscriber(fn, context, args, when);
757
if (this.fireOnce && this.fired) {
759
setTimeout(Y.bind(this._notify, this, s, this.firedWith), 0);
761
this._notify(s, this.firedWith);
766
this.afters[s.id] = s;
769
this.subscribers[s.id] = s;
773
return new Y.EventHandle(this, s);
778
* Listen for this event
780
* @param {Function} fn The function to execute.
781
* @return {EventHandle} Unsubscribe handle.
782
* @deprecated use on.
784
subscribe: function(fn, context) {
785
Y.log('ce.subscribe deprecated, use "on"', 'warn', 'deprecated');
786
var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
787
return this._on(fn, context, a, true);
791
* Listen for this event
793
* @param {Function} fn The function to execute.
794
* @param {object} context optional execution context.
795
* @param {mixed} arg* 0..n additional arguments to supply to the subscriber
796
* when the event fires.
797
* @return {EventHandle} An object with a detach method to detch the handler(s).
799
on: function(fn, context) {
800
var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
802
this.host._monitor('attach', this.type, {
806
return this._on(fn, context, a, true);
810
* Listen for this event after the normal subscribers have been notified and
811
* the default behavior has been applied. If a normal subscriber prevents the
812
* default behavior, it also prevents after listeners from firing.
814
* @param {Function} fn The function to execute.
815
* @param {object} context optional execution context.
816
* @param {mixed} arg* 0..n additional arguments to supply to the subscriber
817
* when the event fires.
818
* @return {EventHandle} handle Unsubscribe handle.
820
after: function(fn, context) {
821
var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
822
return this._on(fn, context, a, AFTER);
828
* @param {Function} fn The subscribed function to remove, if not supplied
829
* all will be removed.
830
* @param {Object} context The context object passed to subscribe.
831
* @return {int} returns the number of subscribers unsubscribed.
833
detach: function(fn, context) {
834
// unsubscribe handle
835
if (fn && fn.detach) {
841
subs = Y.merge(this.subscribers, this.afters);
844
if (subs.hasOwnProperty(i)) {
846
if (s && (!fn || fn === s.fn)) {
858
* @method unsubscribe
859
* @param {Function} fn The subscribed function to remove, if not supplied
860
* all will be removed.
861
* @param {Object} context The context object passed to subscribe.
862
* @return {int|undefined} returns the number of subscribers unsubscribed.
863
* @deprecated use detach.
865
unsubscribe: function() {
866
return this.detach.apply(this, arguments);
870
* Notify a single subscriber
872
* @param {Subscriber} s the subscriber.
873
* @param {Array} args the arguments array to apply to the listener.
876
_notify: function(s, args, ef) {
878
this.log(this.type + '->' + 'sub: ' + s.id);
882
ret = s.notify(args, this);
884
if (false === ret || this.stopped > 1) {
885
this.log(this.type + ' cancelled by subscriber');
893
* Logger abstraction to centralize the application of the silent flag
895
* @param {string} msg message to log.
896
* @param {string} cat log category.
898
log: function(msg, cat) {
900
Y.log(this.id + ': ' + msg, cat || 'info', 'event');
905
* Notifies the subscribers. The callback functions will be executed
906
* from the context specified when the event was created, and with the
907
* following parameters:
909
* <li>The type of event</li>
910
* <li>All of the arguments fire() was executed with as an array</li>
911
* <li>The custom object (if any) that was passed into the subscribe()
915
* @param {Object*} arguments an arbitrary set of parameters to pass to
917
* @return {boolean} false if one of the subscribers returned false,
922
if (this.fireOnce && this.fired) {
923
this.log('fireOnce event: ' + this.type + ' already fired');
927
var args = Y.Array(arguments, 0, true);
929
// this doesn't happen if the event isn't published
930
// this.host._monitor('fire', this.type, args);
933
this.firedWith = args;
935
if (this.emitFacade) {
936
return this.fireComplex(args);
938
return this.fireSimple(args);
944
* Set up for notifying subscribers of non-emitFacade events.
947
* @param args {Array} Arguments passed to fire()
948
* @return Boolean false if a subscriber returned false
951
fireSimple: function(args) {
954
if (this.hasSubs()) {
955
// this._procSubs(Y.merge(this.subscribers, this.afters), args);
956
var subs = this.getSubs();
957
this._procSubs(subs[0], args);
958
this._procSubs(subs[1], args);
960
this._broadcast(args);
961
return this.stopped ? false : true;
964
// Requires the event-custom-complex module for full funcitonality.
965
fireComplex: function(args) {
966
Y.log('Missing event-custom-complex needed to emit a facade for: ' + this.type);
967
args[0] = args[0] || {};
968
return this.fireSimple(args);
972
* Notifies a list of subscribers.
975
* @param subs {Array} List of subscribers
976
* @param args {Array} Arguments passed to fire()
978
* @return Boolean false if a subscriber returns false or stops the event
979
* propagation via e.stopPropagation(),
980
* e.stopImmediatePropagation(), or e.halt()
983
_procSubs: function(subs, args, ef) {
986
if (subs.hasOwnProperty(i)) {
989
if (false === this._notify(s, args, ef)) {
992
if (this.stopped == 2) {
1003
* Notifies the YUI instance if the event is configured with broadcast = 1,
1004
* and both the YUI instance and Y.Global if configured with broadcast = 2.
1006
* @method _broadcast
1007
* @param args {Array} Arguments sent to fire()
1010
_broadcast: function(args) {
1011
if (!this.stopped && this.broadcast) {
1013
var a = Y.Array(args);
1014
a.unshift(this.type);
1016
if (this.host !== Y) {
1020
if (this.broadcast == 2) {
1021
Y.Global.fire.apply(Y.Global, a);
1027
* Removes all listeners
1028
* @method unsubscribeAll
1029
* @return {int} The number of listeners unsubscribed.
1030
* @deprecated use detachAll.
1032
unsubscribeAll: function() {
1033
return this.detachAll.apply(this, arguments);
1037
* Removes all listeners
1039
* @return {int} The number of listeners unsubscribed.
1041
detachAll: function() {
1042
return this.detach();
1046
* Deletes the subscriber from the internal store of on() and after()
1050
* @param subscriber object.
1053
_delete: function(s) {
1055
if (this.subscribers[s.id]) {
1056
delete this.subscribers[s.id];
1059
if (this.afters[s.id]) {
1060
delete this.afters[s.id];
1066
this.host._monitor('detach', this.type, {
1074
// delete s.context;
1080
* Stores the subscriber information to be used when the event fires.
1081
* @param {Function} fn The wrapped function to execute.
1082
* @param {Object} context The value of the keyword 'this' in the listener.
1083
* @param {Array} args* 0..n additional arguments to supply the listener.
1088
Y.Subscriber = function(fn, context, args) {
1091
* The callback that will be execute when the event fires
1092
* This is wrapped by Y.rbind if obj was supplied.
1099
* Optional 'this' keyword for the listener
1103
this.context = context;
1106
* Unique subscriber id
1110
this.id = Y.stamp(this);
1113
* Additional arguments to propagate to the subscriber
1120
* Custom events for a given fire transaction.
1122
* @type {EventTarget}
1124
// this.events = null;
1127
* This listener only reacts to the event once
1130
// this.once = false;
1134
Y.Subscriber.prototype = {
1135
constructor: Y.Subscriber,
1137
_notify: function(c, args, ce) {
1138
if (this.deleted && !this.postponed) {
1139
if (this.postponed) {
1141
delete this.context;
1143
delete this.postponed;
1147
var a = this.args, ret;
1148
switch (ce.signature) {
1150
ret = this.fn.call(c, ce.type, args, c);
1153
ret = this.fn.call(c, args[0] || null, c);
1158
a = (a) ? args.concat(a) : args;
1159
ret = this.fn.apply(c, a);
1161
ret = this.fn.call(c);
1173
* Executes the subscriber.
1175
* @param args {Array} Arguments array for the subscriber.
1176
* @param ce {CustomEvent} The custom event that sent the notification.
1178
notify: function(args, ce) {
1179
var c = this.context,
1183
c = (ce.contextFn) ? ce.contextFn() : ce.context;
1186
// only catch errors if we will not re-throw them.
1187
if (Y.config.throwFail) {
1188
ret = this._notify(c, args, ce);
1191
ret = this._notify(c, args, ce);
1193
Y.error(this + ' failed: ' + e.message, e);
1201
* Returns true if the fn and obj match this objects properties.
1202
* Used by the unsubscribe method to match the right subscriber.
1205
* @param {Function} fn the function to execute.
1206
* @param {Object} context optional 'this' keyword for the listener.
1207
* @return {boolean} true if the supplied arguments match this
1208
* subscriber's signature.
1210
contains: function(fn, context) {
1212
return ((this.fn == fn) && this.context == context);
1214
return (this.fn == fn);
1220
* Return value from all subscribe operations
1221
* @class EventHandle
1223
* @param {CustomEvent} evt the custom event.
1224
* @param {Subscriber} sub the subscriber.
1226
Y.EventHandle = function(evt, sub) {
1237
* The subscriber object
1245
Y.EventHandle.prototype = {
1246
batch: function(f, c) {
1247
f.call(c || this, this);
1248
if (Y.Lang.isArray(this.evt)) {
1249
Y.Array.each(this.evt, function(h) {
1250
h.batch.call(c || h, f);
1256
* Detaches this subscriber
1258
* @return {int} the number of detached listeners
1260
detach: function() {
1261
var evt = this.evt, detached = 0, i;
1263
// Y.log('EventHandle.detach: ' + this.sub, 'info', 'Event');
1264
if (Y.Lang.isArray(evt)) {
1265
for (i = 0; i < evt.length; i++) {
1266
detached += evt[i].detach();
1269
evt._delete(this.sub);
1279
* Monitor the event state for the subscribed event. The first parameter
1280
* is what should be monitored, the rest are the normal parameters when
1281
* subscribing to an event.
1283
* @param what {string} what to monitor ('attach', 'detach', 'publish').
1284
* @return {EventHandle} return value from the monitor event subscription.
1286
monitor: function(what) {
1287
return this.evt.monitor.apply(this.evt, arguments);
1292
* Custom event engine, DOM event listener abstraction layer, synthetic DOM
1294
* @module event-custom
1295
* @submodule event-custom-base
1299
* EventTarget provides the implementation for any object to
1300
* publish, subscribe and fire to custom events, and also
1301
* alows other EventTargets to target the object with events
1302
* sourced from the other object.
1303
* EventTarget is designed to be used with Y.augment to wrap
1304
* EventCustom in an interface that allows events to be listened to
1305
* and fired by name. This makes it possible for implementing code to
1306
* subscribe to an event that either has not been created yet, or will
1307
* not be created at all.
1308
* @class EventTarget
1309
* @param opts a configuration object
1310
* @config emitFacade {boolean} if true, all events will emit event
1311
* facade payloads by default (default false)
1312
* @config prefix {String} the prefix to apply to non-prefixed event names
1316
PREFIX_DELIMITER = ':',
1317
CATEGORY_DELIMITER = '|',
1318
AFTER_PREFIX = '~AFTER~',
1321
_wildType = Y.cached(function(type) {
1322
return type.replace(/(.*)(:)(.*)/, "*$2$3");
1326
* If the instance has a prefix attribute and the
1327
* event type is not prefixed, the instance prefix is
1328
* applied to the supplied type.
1332
_getType = Y.cached(function(type, pre) {
1334
if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
1338
return pre + PREFIX_DELIMITER + type;
1342
* Returns an array with the detach key (if provided),
1343
* and the prefixed event name from _getType
1344
* Y.on('detachcategory| menu:click', fn)
1345
* @method _parseType
1348
_parseType = Y.cached(function(type, pre) {
1350
var t = type, detachcategory, after, i;
1352
if (!L.isString(t)) {
1356
i = t.indexOf(AFTER_PREFIX);
1360
t = t.substr(AFTER_PREFIX.length);
1364
i = t.indexOf(CATEGORY_DELIMITER);
1367
detachcategory = t.substr(0, (i));
1374
// detach category, full type with instance prefix, is this an after listener, short type
1375
return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
1378
ET = function(opts) {
1380
// Y.log('EventTarget constructor executed: ' + this._yuid);
1382
var o = (L.isObject(opts)) ? opts : {};
1384
this._yuievt = this._yuievt || {
1394
chain: ('chain' in o) ? o.chain : Y.config.chain,
1399
context: o.context || this,
1401
emitFacade: o.emitFacade,
1402
fireOnce: o.fireOnce,
1403
queuable: o.queuable,
1404
monitored: o.monitored,
1405
broadcast: o.broadcast,
1406
defaultTargetOnly: o.defaultTargetOnly,
1407
bubbles: ('bubbles' in o) ? o.bubbles : true
1418
* Listen to a custom event hosted by this object one time.
1419
* This is the equivalent to <code>on</code> except the
1420
* listener is immediatelly detached when it is executed.
1422
* @param {String} type The name of the event
1423
* @param {Function} fn The callback to execute in response to the event
1424
* @param {Object} [context] Override `this` object in callback
1425
* @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
1426
* @return {EventHandle} A subscription handle capable of detaching the
1430
var handle = this.on.apply(this, arguments);
1431
handle.batch(function(hand) {
1433
hand.sub.once = true;
1440
* Listen to a custom event hosted by this object one time.
1441
* This is the equivalent to <code>after</code> except the
1442
* listener is immediatelly detached when it is executed.
1444
* @param {String} type The name of the event
1445
* @param {Function} fn The callback to execute in response to the event
1446
* @param {Object} [context] Override `this` object in callback
1447
* @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
1448
* @return {EventHandle} A subscription handle capable of detaching that
1451
onceAfter: function() {
1452
var handle = this.after.apply(this, arguments);
1453
handle.batch(function(hand) {
1455
hand.sub.once = true;
1462
* Takes the type parameter passed to 'on' and parses out the
1463
* various pieces that could be included in the type. If the
1464
* event type is passed without a prefix, it will be expanded
1465
* to include the prefix one is supplied or the event target
1466
* is configured with a default prefix.
1468
* @param {String} type the type
1469
* @param {String} [pre=this._yuievt.config.prefix] the prefix
1471
* @return {Array} an array containing:
1472
* * the detach category, if supplied,
1473
* * the prefixed event type,
1474
* * whether or not this is an after listener,
1475
* * the supplied event type
1477
parseType: function(type, pre) {
1478
return _parseType(type, pre || this._yuievt.config.prefix);
1482
* Subscribe a callback function to a custom event fired by this object or
1483
* from an object that bubbles its events to this object.
1485
* Callback functions for events published with `emitFacade = true` will
1486
* receive an `EventFacade` as the first argument (typically named "e").
1487
* These callbacks can then call `e.preventDefault()` to disable the
1488
* behavior published to that event's `defaultFn`. See the `EventFacade`
1489
* API for all available properties and methods. Subscribers to
1490
* non-`emitFacade` events will receive the arguments passed to `fire()`
1491
* after the event name.
1493
* To subscribe to multiple events at once, pass an object as the first
1494
* argument, where the key:value pairs correspond to the eventName:callback,
1495
* or pass an array of event names as the first argument to subscribe to
1496
* all listed events with the same callback.
1498
* Returning `false` from a callback is supported as an alternative to
1499
* calling `e.preventDefault(); e.stopPropagation();`. However, it is
1500
* recommended to use the event methods whenever possible.
1503
* @param {String} type The name of the event
1504
* @param {Function} fn The callback to execute in response to the event
1505
* @param {Object} [context] Override `this` object in callback
1506
* @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
1507
* @return {EventHandle} A subscription handle capable of detaching that
1510
on: function(type, fn, context) {
1512
var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
1513
detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
1514
Node = Y.Node, n, domevent, isArr;
1516
// full name, args, detachcategory, after
1517
this._monitor('attach', parts[1], {
1523
if (L.isObject(type)) {
1525
if (L.isFunction(type)) {
1526
return Y.Do.before.apply(Y.Do, arguments);
1531
args = YArray(arguments, 0, true);
1534
if (L.isArray(type)) {
1538
after = type._after;
1541
Y.each(type, function(v, k) {
1543
if (L.isObject(v)) {
1544
f = v.fn || ((L.isFunction(v)) ? v : f);
1548
var nv = (after) ? AFTER_PREFIX : '';
1550
args[0] = nv + ((isArr) ? v : k);
1554
ret.push(this.on.apply(this, args));
1558
return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
1562
detachcategory = parts[0];
1564
shorttype = parts[3];
1566
// extra redirection so we catch adaptor events too. take a look at this.
1567
if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) {
1568
args = YArray(arguments, 0, true);
1569
args.splice(2, 0, Node.getDOMNode(this));
1570
// Y.log("Node detected, redirecting with these args: " + args);
1571
return Y.on.apply(Y, args);
1576
if (Y.instanceOf(this, YUI)) {
1578
adapt = Y.Env.evt.plugins[type];
1579
args = YArray(arguments, 0, true);
1580
args[0] = shorttype;
1585
if (Y.instanceOf(n, Y.NodeList)) {
1586
n = Y.NodeList.getDOMNodes(n);
1587
} else if (Y.instanceOf(n, Node)) {
1588
n = Node.getDOMNode(n);
1591
domevent = (shorttype in Node.DOM_EVENTS);
1593
// Captures both DOM events and event plugins.
1599
// check for the existance of an event adaptor
1601
Y.log('Using adaptor for ' + shorttype + ', ' + n, 'info', 'event');
1602
handle = adapt.on.apply(Y, args);
1603
} else if ((!type) || domevent) {
1604
handle = Y.Event._attach(args);
1610
ce = this._yuievt.events[type] || this.publish(type);
1611
handle = ce._on(fn, context, (arguments.length > 3) ? YArray(arguments, 3, true) : null, (after) ? 'after' : true);
1614
if (detachcategory) {
1615
store[detachcategory] = store[detachcategory] || {};
1616
store[detachcategory][type] = store[detachcategory][type] || [];
1617
store[detachcategory][type].push(handle);
1620
return (this._yuievt.chain) ? this : handle;
1625
* subscribe to an event
1627
* @deprecated use on
1629
subscribe: function() {
1630
Y.log('EventTarget subscribe() is deprecated, use on()', 'warn', 'deprecated');
1631
return this.on.apply(this, arguments);
1635
* Detach one or more listeners the from the specified event
1637
* @param type {string|Object} Either the handle to the subscriber or the
1638
* type of event. If the type
1639
* is not specified, it will attempt to remove
1640
* the listener from all hosted events.
1641
* @param fn {Function} The subscribed function to unsubscribe, if not
1642
* supplied, all subscribers will be removed.
1643
* @param context {Object} The custom object passed to subscribe. This is
1644
* optional, but if supplied will be used to
1645
* disambiguate multiple listeners that are the same
1646
* (e.g., you subscribe many object using a function
1647
* that lives on the prototype)
1648
* @return {EventTarget} the host
1650
detach: function(type, fn, context) {
1651
var evts = this._yuievt.events, i,
1652
Node = Y.Node, isNode = Node && (Y.instanceOf(this, Node));
1654
// detachAll disabled on the Y instance.
1655
if (!type && (this !== Y)) {
1657
if (evts.hasOwnProperty(i)) {
1658
evts[i].detach(fn, context);
1662
Y.Event.purgeElement(Node.getDOMNode(this));
1668
var parts = _parseType(type, this._yuievt.config.prefix),
1669
detachcategory = L.isArray(parts) ? parts[0] : null,
1670
shorttype = (parts) ? parts[3] : null,
1671
adapt, store = Y.Env.evt.handles, detachhost, cat, args,
1674
keyDetacher = function(lcat, ltype, host) {
1675
var handles = lcat[ltype], ce, i;
1677
for (i = handles.length - 1; i >= 0; --i) {
1678
ce = handles[i].evt;
1679
if (ce.host === host || ce.el === host) {
1680
handles[i].detach();
1686
if (detachcategory) {
1688
cat = store[detachcategory];
1690
detachhost = (isNode) ? Y.Node.getDOMNode(this) : this;
1694
keyDetacher(cat, type, detachhost);
1697
if (cat.hasOwnProperty(i)) {
1698
keyDetacher(cat, i, detachhost);
1706
// If this is an event handle, use it to detach
1707
} else if (L.isObject(type) && type.detach) {
1710
// extra redirection so we catch adaptor events too. take a look at this.
1711
} else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
1712
args = YArray(arguments, 0, true);
1713
args[2] = Node.getDOMNode(this);
1714
Y.detach.apply(Y, args);
1718
adapt = Y.Env.evt.plugins[shorttype];
1720
// The YUI instance handles DOM events and adaptors
1721
if (Y.instanceOf(this, YUI)) {
1722
args = YArray(arguments, 0, true);
1723
// use the adaptor specific detach code if
1724
if (adapt && adapt.detach) {
1725
adapt.detach.apply(Y, args);
1728
} else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
1730
Y.Event.detach.apply(Y.Event, args);
1736
ce = evts[parts[1]];
1738
ce.detach(fn, context);
1746
* @method unsubscribe
1747
* @deprecated use detach
1749
unsubscribe: function() {
1750
Y.log('EventTarget unsubscribe() is deprecated, use detach()', 'warn', 'deprecated');
1751
return this.detach.apply(this, arguments);
1755
* Removes all listeners from the specified event. If the event type
1756
* is not specified, all listeners from all hosted custom events will
1759
* @param type {String} The type, or name of the event
1761
detachAll: function(type) {
1762
return this.detach(type);
1766
* Removes all listeners from the specified event. If the event type
1767
* is not specified, all listeners from all hosted custom events will
1769
* @method unsubscribeAll
1770
* @param type {String} The type, or name of the event
1771
* @deprecated use detachAll
1773
unsubscribeAll: function() {
1774
Y.log('EventTarget unsubscribeAll() is deprecated, use detachAll()', 'warn', 'deprecated');
1775
return this.detachAll.apply(this, arguments);
1779
* Creates a new custom event of the specified type. If a custom event
1780
* by that name already exists, it will not be re-created. In either
1781
* case the custom event is returned.
1785
* @param type {String} the type, or name of the event
1786
* @param opts {object} optional config params. Valid properties are:
1790
* 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
1793
* 'bubbles': whether or not this event bubbles (true)
1794
* Events can only bubble if emitFacade is true.
1797
* 'context': the default execution context for the listeners (this)
1800
* 'defaultFn': the default function to execute when this event fires if preventDefault was not called
1803
* 'emitFacade': whether or not this event emits a facade (false)
1806
* 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click'
1809
* 'fireOnce': if an event is configured to fire once, new subscribers after
1810
* the fire will be notified immediately.
1813
* 'async': fireOnce event listeners will fire synchronously if the event has already
1814
* fired unless async is true.
1817
* 'preventable': whether or not preventDefault() has an effect (true)
1820
* 'preventedFn': a function that is executed when preventDefault is called
1823
* 'queuable': whether or not this event can be queued during bubbling (false)
1826
* 'silent': if silent is true, debug messages are not provided for this event.
1829
* 'stoppedFn': a function that is executed when stopPropagation is called
1833
* 'monitored': specifies whether or not this event should send notifications about
1834
* when the event has been attached, detached, or published.
1837
* 'type': the event type (valid option if not provided as the first parameter to publish)
1841
* @return {CustomEvent} the custom event
1844
publish: function(type, opts) {
1845
var events, ce, ret, defaults,
1846
edata = this._yuievt,
1847
pre = edata.config.prefix;
1849
if (L.isObject(type)) {
1851
Y.each(type, function(v, k) {
1852
ret[k] = this.publish(k, v || opts);
1858
type = (pre) ? _getType(type, pre) : type;
1860
this._monitor('publish', type, {
1864
events = edata.events;
1868
// ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
1870
ce.applyConfig(opts, true);
1874
defaults = edata.defaults;
1877
ce = new Y.CustomEvent(type,
1878
(opts) ? Y.merge(defaults, opts) : defaults);
1882
// make sure we turn the broadcast flag off if this
1883
// event was published as a result of bubbling
1884
// if (opts instanceof Y.CustomEvent) {
1885
// events[type].broadcast = false;
1888
return events[type];
1892
* This is the entry point for the event monitoring system.
1893
* You can monitor 'attach', 'detach', 'fire', and 'publish'.
1894
* When configured, these events generate an event. click ->
1895
* click_attach, click_detach, click_publish -- these can
1896
* be subscribed to like other events to monitor the event
1897
* system. Inividual published events can have monitoring
1898
* turned on or off (publish can't be turned off before it
1899
* it published) by setting the events 'monitor' config.
1902
* @param what {String} 'attach', 'detach', 'fire', or 'publish'
1903
* @param type {String} Name of the event being monitored
1904
* @param o {Object} Information about the event interaction, such as
1905
* fire() args, subscription category, publish config
1908
_monitor: function(what, type, o) {
1909
var monitorevt, ce = this.getEvent(type);
1910
if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) {
1911
monitorevt = type + '_' + what;
1912
// Y.log('monitoring: ' + monitorevt);
1914
this.fire.call(this, monitorevt, o);
1919
* Fire a custom event by name. The callback functions will be executed
1920
* from the context specified when the event was created, and with the
1921
* following parameters.
1923
* If the custom event object hasn't been created, then the event hasn't
1924
* been published and it has no subscribers. For performance sake, we
1925
* immediate exit in this case. This means the event won't bubble, so
1926
* if the intention is that a bubble target be notified, the event must
1927
* be published on this object first.
1929
* The first argument is the event type, and any additional arguments are
1930
* passed to the listeners as parameters. If the first of these is an
1931
* object literal, and the event is configured to emit an event facade,
1932
* that object is mixed into the event facade and the facade is provided
1933
* in place of the original object.
1936
* @param type {String|Object} The type of the event, or an object that contains
1937
* a 'type' property.
1938
* @param arguments {Object*} an arbitrary set of parameters to pass to
1939
* the handler. If the first of these is an object literal and the event is
1940
* configured to emit an event facade, the event facade will replace that
1941
* parameter after the properties the object literal contains are copied to
1943
* @return {EventTarget} the event host
1946
fire: function(type) {
1948
var typeIncluded = L.isString(type),
1949
t = (typeIncluded) ? type : (type && type.type),
1950
ce, ret, pre = this._yuievt.config.prefix, ce2,
1951
args = (typeIncluded) ? YArray(arguments, 1, true) : arguments;
1953
t = (pre) ? _getType(t, pre) : t;
1955
this._monitor('fire', t, {
1959
ce = this.getEvent(t, true);
1960
ce2 = this.getSibling(t, ce);
1963
ce = this.publish(t);
1966
// this event has not been published or subscribed to
1968
if (this._yuievt.hasTargets) {
1969
return this.bubble({ type: t }, args, this);
1972
// otherwise there is nothing to be done
1976
ret = ce.fire.apply(ce, args);
1979
return (this._yuievt.chain) ? this : ret;
1982
getSibling: function(type, ce) {
1984
// delegate to *:type events if there are subscribers
1985
if (type.indexOf(PREFIX_DELIMITER) > -1) {
1986
type = _wildType(type);
1987
// console.log(type);
1988
ce2 = this.getEvent(type, true);
1990
// console.log("GOT ONE: " + type);
1991
ce2.applyConfig(ce);
1992
ce2.bubbles = false;
1994
// ret = ce2.fire.apply(ce2, a);
2002
* Returns the custom event of the provided type has been created, a
2003
* falsy value otherwise
2005
* @param type {String} the type, or name of the event
2006
* @param prefixed {String} if true, the type is prefixed already
2007
* @return {CustomEvent} the custom event or null
2009
getEvent: function(type, prefixed) {
2012
pre = this._yuievt.config.prefix;
2013
type = (pre) ? _getType(type, pre) : type;
2015
e = this._yuievt.events;
2016
return e[type] || null;
2020
* Subscribe to a custom event hosted by this object. The
2021
* supplied callback will execute after any listeners add
2022
* via the subscribe method, and after the default function,
2023
* if configured for the event, has executed.
2026
* @param {String} type The name of the event
2027
* @param {Function} fn The callback to execute in response to the event
2028
* @param {Object} [context] Override `this` object in callback
2029
* @param {Any} [arg*] 0..n additional arguments to supply to the subscriber
2030
* @return {EventHandle} A subscription handle capable of detaching the
2033
after: function(type, fn) {
2035
var a = YArray(arguments, 0, true);
2037
switch (L.type(type)) {
2039
return Y.Do.after.apply(Y.Do, arguments);
2041
// YArray.each(a[0], function(v) {
2042
// v = AFTER_PREFIX + v;
2049
a[0] = AFTER_PREFIX + type;
2052
return this.on.apply(this, a);
2057
* Executes the callback before a DOM event, custom event
2058
* or method. If the first argument is a function, it
2059
* is assumed the target is a method. For DOM and custom
2060
* events, this is an alias for Y.on.
2062
* For DOM and custom events:
2063
* type, callback, context, 0-n arguments
2066
* callback, object (method host), methodName, context, 0-n arguments
2069
* @return detach handle
2071
before: function() {
2072
return this.on.apply(this, arguments);
2079
// make Y an event target
2080
Y.mix(Y, ET.prototype);
2081
ET.call(Y, { bubbles: false });
2083
YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
2086
* Hosts YUI page level events. This is where events bubble to
2087
* when the broadcast config is set to 2. This property is
2088
* only available if the custom event module is loaded.
2093
Y.Global = YUI.Env.globalEvents;
2095
// @TODO implement a global namespace function on Y.Global?
2098
`Y.on()` can do many things:
2101
<li>Subscribe to custom events `publish`ed and `fire`d from Y</li>
2102
<li>Subscribe to custom events `publish`ed with `broadcast` 1 or 2 and
2103
`fire`d from any object in the YUI instance sandbox</li>
2104
<li>Subscribe to DOM events</li>
2105
<li>Subscribe to the execution of a method on any object, effectively
2106
treating that method as an event</li>
2109
For custom event subscriptions, pass the custom event name as the first argument and callback as the second. The `this` object in the callback will be `Y` unless an override is passed as the third argument.
2111
Y.on('io:complete', function () {
2112
Y.MyApp.updateStatus('Transaction complete');
2115
To subscribe to DOM events, pass the name of a DOM event as the first argument
2116
and a CSS selector string as the third argument after the callback function.
2117
Alternately, the third argument can be a `Node`, `NodeList`, `HTMLElement`,
2118
array, or simply omitted (the default is the `window` object).
2120
Y.on('click', function (e) {
2123
// proceed with ajax form submission
2124
var url = this.get('action');
2128
The `this` object in DOM event callbacks will be the `Node` targeted by the CSS
2129
selector or other identifier.
2131
`on()` subscribers for DOM events or custom events `publish`ed with a
2132
`defaultFn` can prevent the default behavior with `e.preventDefault()` from the
2133
event object passed as the first parameter to the subscription callback.
2135
To subscribe to the execution of an object method, pass arguments corresponding to the call signature for
2136
<a href="../classes/Do.html#methods_before">`Y.Do.before(...)`</a>.
2138
NOTE: The formal parameter list below is for events, not for function
2139
injection. See `Y.Do.before` for that signature.
2142
@param {String} type DOM or custom event name
2143
@param {Function} fn The callback to execute in response to the event
2144
@param {Object} [context] Override `this` object in callback
2145
@param {Any} [arg*] 0..n additional arguments to supply to the subscriber
2146
@return {EventHandle} A subscription handle capable of detaching the
2153
Listen for an event one time. Equivalent to `on()`, except that
2154
the listener is immediately detached when executed.
2156
See the <a href="#methods_on">`on()` method</a> for additional subscription
2161
@param {String} type DOM or custom event name
2162
@param {Function} fn The callback to execute in response to the event
2163
@param {Object} [context] Override `this` object in callback
2164
@param {Any} [arg*] 0..n additional arguments to supply to the subscriber
2165
@return {EventHandle} A subscription handle capable of detaching the
2171
Listen for an event one time. Equivalent to `once()`, except, like `after()`,
2172
the subscription callback executes after all `on()` subscribers and the event's
2173
`defaultFn` (if configured) have executed. Like `after()` if any `on()` phase
2174
subscriber calls `e.preventDefault()`, neither the `defaultFn` nor the `after()`
2175
subscribers will execute.
2177
The listener is immediately detached when executed.
2179
See the <a href="#methods_on">`on()` method</a> for additional subscription
2184
@param {String} type The custom event name
2185
@param {Function} fn The callback to execute in response to the event
2186
@param {Object} [context] Override `this` object in callback
2187
@param {Any} [arg*] 0..n additional arguments to supply to the subscriber
2188
@return {EventHandle} A subscription handle capable of detaching the
2194
Like `on()`, this method creates a subscription to a custom event or to the
2195
execution of a method on an object.
2197
For events, `after()` subscribers are executed after the event's
2198
`defaultFn` unless `e.preventDefault()` was called from an `on()` subscriber.
2200
See the <a href="#methods_on">`on()` method</a> for additional subscription
2203
NOTE: The subscription signature shown is for events, not for function
2204
injection. See <a href="../classes/Do.html#methods_after">`Y.Do.after`</a>
2210
@param {String} type The custom event name
2211
@param {Function} fn The callback to execute in response to the event
2212
@param {Object} [context] Override `this` object in callback
2213
@param {Any} [args*] 0..n additional arguments to supply to the subscriber
2214
@return {EventHandle} A subscription handle capable of detaching the
2220
}, '3.5.0' ,{requires:['oop']});