2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('event-custom-base', function(Y) {
11 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
13 * @module event-custom
23 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
25 * @module event-custom
26 * @submodule event-custom-base
30 * Allows for the insertion of methods that are executed before or after
42 * Cache of objects touched by the utility
49 * Execute the supplied method before the specified function
51 * @param fn {Function} the function to execute
52 * @param obj the object hosting the method to displace
53 * @param sFn {string} the name of the method to displace
54 * @param c The execution context for fn
55 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
56 * when the event fires.
57 * @return {string} handle for the subscription
60 before: function(fn, obj, sFn, c) {
63 a = [fn, c].concat(Y.Array(arguments, 4, true));
64 f = Y.rbind.apply(Y, a);
67 return this._inject(DO_BEFORE, f, obj, sFn);
71 * Execute the supplied method after the specified function
73 * @param fn {Function} the function to execute
74 * @param obj the object hosting the method to displace
75 * @param sFn {string} the name of the method to displace
76 * @param c The execution context for fn
77 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
78 * @return {string} handle for the subscription
81 after: function(fn, obj, sFn, c) {
84 a = [fn, c].concat(Y.Array(arguments, 4, true));
85 f = Y.rbind.apply(Y, a);
88 return this._inject(DO_AFTER, f, obj, sFn);
92 * Execute the supplied method after the specified function
94 * @param when {string} before or after
95 * @param fn {Function} the function to execute
96 * @param obj the object hosting the method to displace
97 * @param sFn {string} the name of the method to displace
98 * @param c The execution context for fn
99 * @return {string} handle for the subscription
103 _inject: function(when, fn, obj, sFn) {
106 var id = Y.stamp(obj), o, sid;
108 if (! this.objs[id]) {
109 // create a map entry for the obj if it doesn't exist
116 // create a map entry for the method if it doesn't exist
117 o[sFn] = new Y.Do.Method(obj, sFn);
119 // re-route the method to our wrapper
122 return o[sFn].exec.apply(o[sFn], arguments);
127 sid = id + Y.stamp(fn) + sFn;
129 // register the callback
130 o[sFn].register(sid, fn, when);
132 return new Y.EventHandle(o[sFn], sid);
137 * Detach a before or after subscription
139 * @param handle {string} the subscription handle
141 detach: function(handle) {
149 _unload: function(e, me) {
156 //////////////////////////////////////////////////////////////////////////
159 * Contains the return value from the wrapped method, accessible
160 * by 'after' event listeners.
162 * @property Do.originalRetVal
168 * Contains the current state of the return value, consumable by
169 * 'after' event listeners, and updated if an after subscriber
170 * changes the return value generated by the wrapped function.
172 * @property Do.currentRetVal
177 //////////////////////////////////////////////////////////////////////////
180 * Wrapper for a displaced method with aop enabled
183 * @param obj The object to operate on
184 * @param sFn The name of the method to displace
186 DO.Method = function(obj, sFn) {
188 this.methodName = sFn;
189 this.method = obj[sFn];
195 * Register a aop subscriber
197 * @param sid {string} the subscriber id
198 * @param fn {Function} the function to execute
199 * @param when {string} when to execute the function
201 DO.Method.prototype.register = function (sid, fn, when) {
203 this.after[sid] = fn;
205 this.before[sid] = fn;
210 * Unregister a aop subscriber
212 * @param sid {string} the subscriber id
213 * @param fn {Function} the function to execute
214 * @param when {string} when to execute the function
216 DO.Method.prototype._delete = function (sid) {
217 delete this.before[sid];
218 delete this.after[sid];
222 * Execute the wrapped method
225 DO.Method.prototype.exec = function () {
227 var args = Y.Array(arguments, 0, true),
235 if (bf.hasOwnProperty(i)) {
236 ret = bf[i].apply(this.obj, args);
238 switch (ret.constructor) {
255 ret = this.method.apply(this.obj, args);
258 DO.originalRetVal = ret;
259 DO.currentRetVal = ret;
261 // execute after methods.
263 if (af.hasOwnProperty(i)) {
264 newRet = af[i].apply(this.obj, args);
265 // Stop processing if a Halt object is returned
266 if (newRet && newRet.constructor == DO.Halt) {
267 return newRet.retVal;
268 // Check for a new return value
269 } else if (newRet && newRet.constructor == DO.AlterReturn) {
270 ret = newRet.newRetVal;
271 // Update the static retval state
272 DO.currentRetVal = ret;
280 //////////////////////////////////////////////////////////////////////////
283 * Return an AlterArgs object when you want to change the arguments that
284 * were passed into the function. An example would be a service that scrubs
285 * out illegal characters prior to executing the core business logic.
286 * @class Do.AlterArgs
288 DO.AlterArgs = function(msg, newArgs) {
290 this.newArgs = newArgs;
294 * Return an AlterReturn object when you want to change the result returned
295 * from the core method to the caller
296 * @class Do.AlterReturn
298 DO.AlterReturn = function(msg, newRetVal) {
300 this.newRetVal = newRetVal;
304 * Return a Halt object when you want to terminate the execution
305 * of all subsequent subscribers as well as the wrapped method
306 * if it has not exectued yet.
309 DO.Halt = function(msg, retVal) {
311 this.retVal = retVal;
315 * Return a Prevent object when you want to prevent the wrapped function
316 * from executing, but want the remaining listeners to execute
319 DO.Prevent = function(msg) {
324 * Return an Error object when you want to terminate the execution
325 * of all subsequent method calls.
327 * @deprecated use Y.Do.Halt or Y.Do.Prevent
332 //////////////////////////////////////////////////////////////////////////
334 // Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
338 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
340 * @module event-custom
341 * @submodule event-custom-base
345 // var onsubscribeType = "_event:onsub",
374 * Return value from all subscribe operations
377 * @param {CustomEvent} evt the custom event.
378 * @param {Subscriber} sub the subscriber.
380 Y.EventHandle = function(evt, sub) {
389 * The subscriber object
395 Y.EventHandle.prototype = {
396 batch: function(f, c) {
397 f.call(c || this, this);
398 if (Y.Lang.isArray(this.evt)) {
399 Y.Array.each(this.evt, function(h) {
400 h.batch.call(c || h, f);
406 * Detaches this subscriber
408 * @return {int} the number of detached listeners
411 var evt = this.evt, detached = 0, i;
413 if (Y.Lang.isArray(evt)) {
414 for (i = 0; i < evt.length; i++) {
415 detached += evt[i].detach();
418 evt._delete(this.sub);
428 * Monitor the event state for the subscribed event. The first parameter
429 * is what should be monitored, the rest are the normal parameters when
430 * subscribing to an event.
432 * @param what {string} what to monitor ('attach', 'detach', 'publish').
433 * @return {EventHandle} return value from the monitor event subscription.
435 monitor: function(what) {
436 return this.evt.monitor.apply(this.evt, arguments);
441 * The CustomEvent class lets you define events for your application
442 * that can be subscribed to by one or more independent component.
444 * @param {String} type The type of event, which is passed to the callback
445 * when the event fires.
446 * @param {object} o configuration object.
450 Y.CustomEvent = function(type, o) {
452 // if (arguments.length > 2) {
453 // this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
458 this.id = Y.stamp(this);
461 * The type of event, returned to subscribers when the event fires
468 * The context the the event will fire from by default. Defaults to the YUI
476 * Monitor when an event is attached or detached.
478 * @property monitored
481 // this.monitored = false;
483 this.logSystem = (type == YUI_LOG);
486 * If 0, this event does not broadcast. If 1, the YUI instance is notified
487 * every time this event fires. If 2, the YUI instance and the YUI global
488 * (if event is enabled on the global) are notified every time this event
490 * @property broadcast
493 // this.broadcast = 0;
496 * By default all custom events are logged in the debug build, set silent
497 * to true to disable debug outpu for this event.
501 this.silent = this.logSystem;
504 * Specifies whether this event should be queued when the host is actively
505 * processing an event. This will effect exectution order of the callbacks
506 * for the various events.
511 // this.queuable = false;
514 * The subscribers to this event
515 * @property subscribers
516 * @type Subscriber {}
518 this.subscribers = {};
521 * 'After' subscribers
523 * @type Subscriber {}
528 * This event has fired if true
534 // this.fired = false;
537 * An array containing the arguments the custom event
538 * was last fired with.
539 * @property firedWith
545 * This event should only fire one time if true, and if
546 * it has fired, any new subscribers should be notified
553 // this.fireOnce = false;
556 * fireOnce listeners will fire syncronously unless async
562 //this.async = false;
565 * Flag for stopPropagation that is modified during fire()
566 * 1 means to stop propagation to bubble targets. 2 means
567 * to also stop additional subscribers on this target.
574 * Flag for preventDefault that is modified during fire().
575 * if it is not 0, the default behavior for this event
576 * @property prevented
579 // this.prevented = 0;
582 * Specifies the host for this custom event. This is used
583 * to enable event bubbling
590 * The default function to execute after event listeners
591 * have fire, but only if the default action was not
593 * @property defaultFn
596 // this.defaultFn = null;
599 * The function to execute if a subscriber calls
600 * stopPropagation or stopImmediatePropagation
601 * @property stoppedFn
604 // this.stoppedFn = null;
607 * The function to execute if a subscriber calls
609 * @property preventedFn
612 // this.preventedFn = null;
615 * Specifies whether or not this event's default function
616 * can be cancelled by a subscriber by executing preventDefault()
617 * on the event facade
618 * @property preventable
622 this.preventable = true;
625 * Specifies whether or not a subscriber can stop the event propagation
626 * via stopPropagation(), stopImmediatePropagation(), or halt()
628 * Events can only bubble if emitFacade is true.
637 * Supports multiple options for listener signatures in order to
639 * @property signature
643 this.signature = YUI3_SIGNATURE;
648 // this.hasSubscribers = false;
650 // this.hasAfters = false;
653 * If set to true, the custom event will deliver an EventFacade object
654 * that is similar to a DOM event object.
655 * @property emitFacade
659 // this.emitFacade = false;
661 this.applyConfig(o, true);
663 // this.log("Creating " + this.type);
667 Y.CustomEvent.prototype = {
669 hasSubs: function(when) {
670 var s = this.subCount, a = this.afterCount, sib = this.sibling;
678 return (when == 'after') ? a : s;
685 * Monitor the event state for the subscribed event. The first parameter
686 * is what should be monitored, the rest are the normal parameters when
687 * subscribing to an event.
689 * @param what {string} what to monitor ('detach', 'attach', 'publish').
690 * @return {EventHandle} return value from the monitor event subscription.
692 monitor: function(what) {
693 this.monitored = true;
694 var type = this.id + '|' + this.type + '_' + what,
695 args = Y.Array(arguments, 0, true);
697 return this.host.on.apply(this.host, args);
701 * Get all of the subscribers to this event and any sibling event
703 * @return {Array} first item is the on subscribers, second the after.
705 getSubs: function() {
706 var s = Y.merge(this.subscribers), a = Y.merge(this.afters), sib = this.sibling;
709 Y.mix(s, sib.subscribers);
710 Y.mix(a, sib.afters);
717 * Apply configuration properties. Only applies the CONFIG whitelist
718 * @method applyConfig
719 * @param o hash of properties to apply.
720 * @param force {boolean} if true, properties that exist on the event
721 * will be overwritten.
723 applyConfig: function(o, force) {
725 Y.mix(this, o, force, CONFIGS);
729 _on: function(fn, context, args, when) {
732 this.log('Invalid callback for CE: ' + this.type);
735 var s = new Y.Subscriber(fn, context, args, when);
737 if (this.fireOnce && this.fired) {
739 setTimeout(Y.bind(this._notify, this, s, this.firedWith), 0);
741 this._notify(s, this.firedWith);
746 this.afters[s.id] = s;
749 this.subscribers[s.id] = s;
753 return new Y.EventHandle(this, s);
758 * Listen for this event
760 * @param {Function} fn The function to execute.
761 * @return {EventHandle} Unsubscribe handle.
762 * @deprecated use on.
764 subscribe: function(fn, context) {
765 var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
766 return this._on(fn, context, a, true);
770 * Listen for this event
772 * @param {Function} fn The function to execute.
773 * @param {object} context optional execution context.
774 * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
775 * when the event fires.
776 * @return {EventHandle} An object with a detach method to detch the handler(s).
778 on: function(fn, context) {
779 var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
781 this.host._monitor('attach', this.type, {
785 return this._on(fn, context, a, true);
789 * Listen for this event after the normal subscribers have been notified and
790 * the default behavior has been applied. If a normal subscriber prevents the
791 * default behavior, it also prevents after listeners from firing.
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} handle Unsubscribe handle.
799 after: function(fn, context) {
800 var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
801 return this._on(fn, context, a, AFTER);
807 * @param {Function} fn The subscribed function to remove, if not supplied
808 * all will be removed.
809 * @param {Object} context The context object passed to subscribe.
810 * @return {int} returns the number of subscribers unsubscribed.
812 detach: function(fn, context) {
813 // unsubscribe handle
814 if (fn && fn.detach) {
820 subs = Y.merge(this.subscribers, this.afters);
823 if (subs.hasOwnProperty(i)) {
825 if (s && (!fn || fn === s.fn)) {
837 * @method unsubscribe
838 * @param {Function} fn The subscribed function to remove, if not supplied
839 * all will be removed.
840 * @param {Object} context The context object passed to subscribe.
841 * @return {int|undefined} returns the number of subscribers unsubscribed.
842 * @deprecated use detach.
844 unsubscribe: function() {
845 return this.detach.apply(this, arguments);
849 * Notify a single subscriber
851 * @param {Subscriber} s the subscriber.
852 * @param {Array} args the arguments array to apply to the listener.
855 _notify: function(s, args, ef) {
857 this.log(this.type + '->' + 'sub: ' + s.id);
861 ret = s.notify(args, this);
863 if (false === ret || this.stopped > 1) {
864 this.log(this.type + ' cancelled by subscriber');
872 * Logger abstraction to centralize the application of the silent flag
874 * @param {string} msg message to log.
875 * @param {string} cat log category.
877 log: function(msg, cat) {
883 * Notifies the subscribers. The callback functions will be executed
884 * from the context specified when the event was created, and with the
885 * following parameters:
887 * <li>The type of event</li>
888 * <li>All of the arguments fire() was executed with as an array</li>
889 * <li>The custom object (if any) that was passed into the subscribe()
893 * @param {Object*} arguments an arbitrary set of parameters to pass to
895 * @return {boolean} false if one of the subscribers returned false,
900 if (this.fireOnce && this.fired) {
901 this.log('fireOnce event: ' + this.type + ' already fired');
905 var args = Y.Array(arguments, 0, true);
907 // this doesn't happen if the event isn't published
908 // this.host._monitor('fire', this.type, args);
911 this.firedWith = args;
913 if (this.emitFacade) {
914 return this.fireComplex(args);
916 return this.fireSimple(args);
921 fireSimple: function(args) {
924 if (this.hasSubs()) {
925 // this._procSubs(Y.merge(this.subscribers, this.afters), args);
926 var subs = this.getSubs();
927 this._procSubs(subs[0], args);
928 this._procSubs(subs[1], args);
930 this._broadcast(args);
931 return this.stopped ? false : true;
934 // Requires the event-custom-complex module for full funcitonality.
935 fireComplex: function(args) {
936 args[0] = args[0] || {};
937 return this.fireSimple(args);
940 _procSubs: function(subs, args, ef) {
943 if (subs.hasOwnProperty(i)) {
946 if (false === this._notify(s, args, ef)) {
949 if (this.stopped == 2) {
959 _broadcast: function(args) {
960 if (!this.stopped && this.broadcast) {
962 var a = Y.Array(args);
963 a.unshift(this.type);
965 if (this.host !== Y) {
969 if (this.broadcast == 2) {
970 Y.Global.fire.apply(Y.Global, a);
976 * Removes all listeners
977 * @method unsubscribeAll
978 * @return {int} The number of listeners unsubscribed.
979 * @deprecated use detachAll.
981 unsubscribeAll: function() {
982 return this.detachAll.apply(this, arguments);
986 * Removes all listeners
988 * @return {int} The number of listeners unsubscribed.
990 detachAll: function() {
991 return this.detach();
996 * @param subscriber object.
999 _delete: function(s) {
1001 if (this.subscribers[s.id]) {
1002 delete this.subscribers[s.id];
1005 if (this.afters[s.id]) {
1006 delete this.afters[s.id];
1012 this.host._monitor('detach', this.type, {
1020 // delete s.context;
1026 /////////////////////////////////////////////////////////////////////
1029 * Stores the subscriber information to be used when the event fires.
1030 * @param {Function} fn The wrapped function to execute.
1031 * @param {Object} context The value of the keyword 'this' in the listener.
1032 * @param {Array} args* 0..n additional arguments to supply the listener.
1037 Y.Subscriber = function(fn, context, args) {
1040 * The callback that will be execute when the event fires
1041 * This is wrapped by Y.rbind if obj was supplied.
1048 * Optional 'this' keyword for the listener
1052 this.context = context;
1055 * Unique subscriber id
1059 this.id = Y.stamp(this);
1062 * Additional arguments to propagate to the subscriber
1069 * Custom events for a given fire transaction.
1071 * @type {EventTarget}
1073 // this.events = null;
1076 * This listener only reacts to the event once
1079 // this.once = false;
1083 Y.Subscriber.prototype = {
1085 _notify: function(c, args, ce) {
1086 if (this.deleted && !this.postponed) {
1087 if (this.postponed) {
1089 delete this.context;
1091 delete this.postponed;
1095 var a = this.args, ret;
1096 switch (ce.signature) {
1098 ret = this.fn.call(c, ce.type, args, c);
1101 ret = this.fn.call(c, args[0] || null, c);
1106 a = (a) ? args.concat(a) : args;
1107 ret = this.fn.apply(c, a);
1109 ret = this.fn.call(c);
1121 * Executes the subscriber.
1123 * @param args {Array} Arguments array for the subscriber.
1124 * @param ce {CustomEvent} The custom event that sent the notification.
1126 notify: function(args, ce) {
1127 var c = this.context,
1131 c = (ce.contextFn) ? ce.contextFn() : ce.context;
1134 // only catch errors if we will not re-throw them.
1135 if (Y.config.throwFail) {
1136 ret = this._notify(c, args, ce);
1139 ret = this._notify(c, args, ce);
1141 Y.error(this + ' failed: ' + e.message, e);
1149 * Returns true if the fn and obj match this objects properties.
1150 * Used by the unsubscribe method to match the right subscriber.
1153 * @param {Function} fn the function to execute.
1154 * @param {Object} context optional 'this' keyword for the listener.
1155 * @return {boolean} true if the supplied arguments match this
1156 * subscriber's signature.
1158 contains: function(fn, context) {
1160 return ((this.fn == fn) && this.context == context);
1162 return (this.fn == fn);
1169 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
1171 * @module event-custom
1172 * @submodule event-custom-base
1176 * EventTarget provides the implementation for any object to
1177 * publish, subscribe and fire to custom events, and also
1178 * alows other EventTargets to target the object with events
1179 * sourced from the other object.
1180 * EventTarget is designed to be used with Y.augment to wrap
1181 * EventCustom in an interface that allows events to be listened to
1182 * and fired by name. This makes it possible for implementing code to
1183 * subscribe to an event that either has not been created yet, or will
1184 * not be created at all.
1185 * @class EventTarget
1186 * @param opts a configuration object
1187 * @config emitFacade {boolean} if true, all events will emit event
1188 * facade payloads by default (default false)
1189 * @config prefix {string} the prefix to apply to non-prefixed event names
1190 * @config chain {boolean} if true, on/after/detach return the host to allow
1191 * chaining, otherwise they return an EventHandle (default false)
1195 PREFIX_DELIMITER = ':',
1196 CATEGORY_DELIMITER = '|',
1197 AFTER_PREFIX = '~AFTER~',
1200 _wildType = Y.cached(function(type) {
1201 return type.replace(/(.*)(:)(.*)/, "*$2$3");
1205 * If the instance has a prefix attribute and the
1206 * event type is not prefixed, the instance prefix is
1207 * applied to the supplied type.
1211 _getType = Y.cached(function(type, pre) {
1213 if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
1217 return pre + PREFIX_DELIMITER + type;
1221 * Returns an array with the detach key (if provided),
1222 * and the prefixed event name from _getType
1223 * Y.on('detachcategory| menu:click', fn)
1224 * @method _parseType
1227 _parseType = Y.cached(function(type, pre) {
1229 var t = type, detachcategory, after, i;
1231 if (!L.isString(t)) {
1235 i = t.indexOf(AFTER_PREFIX);
1239 t = t.substr(AFTER_PREFIX.length);
1242 i = t.indexOf(CATEGORY_DELIMITER);
1245 detachcategory = t.substr(0, (i));
1252 // detach category, full type with instance prefix, is this an after listener, short type
1253 return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
1256 ET = function(opts) {
1259 var o = (L.isObject(opts)) ? opts : {};
1261 this._yuievt = this._yuievt || {
1271 chain: ('chain' in o) ? o.chain : Y.config.chain,
1276 context: o.context || this,
1278 emitFacade: o.emitFacade,
1279 fireOnce: o.fireOnce,
1280 queuable: o.queuable,
1281 monitored: o.monitored,
1282 broadcast: o.broadcast,
1283 defaultTargetOnly: o.defaultTargetOnly,
1284 bubbles: ('bubbles' in o) ? o.bubbles : true
1294 * Listen to a custom event hosted by this object one time.
1295 * This is the equivalent to <code>on</code> except the
1296 * listener is immediatelly detached when it is executed.
1298 * @param type {string} The type of the event
1299 * @param fn {Function} The callback
1300 * @param context {object} optional execution context.
1301 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
1302 * @return the event target or a detach handle per 'chain' config
1305 var handle = this.on.apply(this, arguments);
1306 handle.batch(function(hand) {
1308 hand.sub.once = true;
1315 * Takes the type parameter passed to 'on' and parses out the
1316 * various pieces that could be included in the type. If the
1317 * event type is passed without a prefix, it will be expanded
1318 * to include the prefix one is supplied or the event target
1319 * is configured with a default prefix.
1321 * @param {string} type the type
1322 * @param {string} [pre=this._yuievt.config.prefix] the prefix
1324 * @return {Array} an array containing:
1325 * * the detach category, if supplied,
1326 * * the prefixed event type,
1327 * * whether or not this is an after listener,
1328 * * the supplied event type
1330 parseType: function(type, pre) {
1331 return _parseType(type, pre || this._yuievt.config.prefix);
1335 * Subscribe to a custom event hosted by this object
1337 * @param type {string} The type of the event
1338 * @param fn {Function} The callback
1339 * @param context {object} optional execution context.
1340 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
1341 * @return the event target or a detach handle per 'chain' config
1343 on: function(type, fn, context) {
1345 var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
1346 detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
1347 Node = Y.Node, n, domevent, isArr;
1349 // full name, args, detachcategory, after
1350 this._monitor('attach', parts[1], {
1356 if (L.isObject(type)) {
1358 if (L.isFunction(type)) {
1359 return Y.Do.before.apply(Y.Do, arguments);
1364 args = YArray(arguments, 0, true);
1367 if (L.isArray(type)) {
1371 after = type._after;
1374 Y.each(type, function(v, k) {
1376 if (L.isObject(v)) {
1377 f = v.fn || ((L.isFunction(v)) ? v : f);
1381 var nv = (after) ? AFTER_PREFIX : '';
1383 args[0] = nv + ((isArr) ? v : k);
1387 ret.push(this.on.apply(this, args));
1391 return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
1395 detachcategory = parts[0];
1397 shorttype = parts[3];
1399 // extra redirection so we catch adaptor events too. take a look at this.
1400 if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) {
1401 args = YArray(arguments, 0, true);
1402 args.splice(2, 0, Node.getDOMNode(this));
1403 return Y.on.apply(Y, args);
1408 if (Y.instanceOf(this, YUI)) {
1410 adapt = Y.Env.evt.plugins[type];
1411 args = YArray(arguments, 0, true);
1412 args[0] = shorttype;
1417 if (Y.instanceOf(n, Y.NodeList)) {
1418 n = Y.NodeList.getDOMNodes(n);
1419 } else if (Y.instanceOf(n, Node)) {
1420 n = Node.getDOMNode(n);
1423 domevent = (shorttype in Node.DOM_EVENTS);
1425 // Captures both DOM events and event plugins.
1431 // check for the existance of an event adaptor
1433 handle = adapt.on.apply(Y, args);
1434 } else if ((!type) || domevent) {
1435 handle = Y.Event._attach(args);
1441 ce = this._yuievt.events[type] || this.publish(type);
1442 handle = ce._on(fn, context, (arguments.length > 3) ? YArray(arguments, 3, true) : null, (after) ? 'after' : true);
1445 if (detachcategory) {
1446 store[detachcategory] = store[detachcategory] || {};
1447 store[detachcategory][type] = store[detachcategory][type] || [];
1448 store[detachcategory][type].push(handle);
1451 return (this._yuievt.chain) ? this : handle;
1456 * subscribe to an event
1458 * @deprecated use on
1460 subscribe: function() {
1461 return this.on.apply(this, arguments);
1465 * Detach one or more listeners the from the specified event
1467 * @param type {string|Object} Either the handle to the subscriber or the
1468 * type of event. If the type
1469 * is not specified, it will attempt to remove
1470 * the listener from all hosted events.
1471 * @param fn {Function} The subscribed function to unsubscribe, if not
1472 * supplied, all subscribers will be removed.
1473 * @param context {Object} The custom object passed to subscribe. This is
1474 * optional, but if supplied will be used to
1475 * disambiguate multiple listeners that are the same
1476 * (e.g., you subscribe many object using a function
1477 * that lives on the prototype)
1478 * @return {EventTarget} the host
1480 detach: function(type, fn, context) {
1481 var evts = this._yuievt.events, i,
1482 Node = Y.Node, isNode = Node && (Y.instanceOf(this, Node));
1484 // detachAll disabled on the Y instance.
1485 if (!type && (this !== Y)) {
1487 if (evts.hasOwnProperty(i)) {
1488 evts[i].detach(fn, context);
1492 Y.Event.purgeElement(Node.getDOMNode(this));
1498 var parts = _parseType(type, this._yuievt.config.prefix),
1499 detachcategory = L.isArray(parts) ? parts[0] : null,
1500 shorttype = (parts) ? parts[3] : null,
1501 adapt, store = Y.Env.evt.handles, detachhost, cat, args,
1504 keyDetacher = function(lcat, ltype, host) {
1505 var handles = lcat[ltype], ce, i;
1507 for (i = handles.length - 1; i >= 0; --i) {
1508 ce = handles[i].evt;
1509 if (ce.host === host || ce.el === host) {
1510 handles[i].detach();
1516 if (detachcategory) {
1518 cat = store[detachcategory];
1520 detachhost = (isNode) ? Y.Node.getDOMNode(this) : this;
1524 keyDetacher(cat, type, detachhost);
1527 if (cat.hasOwnProperty(i)) {
1528 keyDetacher(cat, i, detachhost);
1536 // If this is an event handle, use it to detach
1537 } else if (L.isObject(type) && type.detach) {
1540 // extra redirection so we catch adaptor events too. take a look at this.
1541 } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
1542 args = YArray(arguments, 0, true);
1543 args[2] = Node.getDOMNode(this);
1544 Y.detach.apply(Y, args);
1548 adapt = Y.Env.evt.plugins[shorttype];
1550 // The YUI instance handles DOM events and adaptors
1551 if (Y.instanceOf(this, YUI)) {
1552 args = YArray(arguments, 0, true);
1553 // use the adaptor specific detach code if
1554 if (adapt && adapt.detach) {
1555 adapt.detach.apply(Y, args);
1558 } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
1560 Y.Event.detach.apply(Y.Event, args);
1566 ce = evts[parts[1]];
1568 ce.detach(fn, context);
1576 * @method unsubscribe
1577 * @deprecated use detach
1579 unsubscribe: function() {
1580 return this.detach.apply(this, arguments);
1584 * Removes all listeners from the specified event. If the event type
1585 * is not specified, all listeners from all hosted custom events will
1588 * @param type {string} The type, or name of the event
1590 detachAll: function(type) {
1591 return this.detach(type);
1595 * Removes all listeners from the specified event. If the event type
1596 * is not specified, all listeners from all hosted custom events will
1598 * @method unsubscribeAll
1599 * @param type {string} The type, or name of the event
1600 * @deprecated use detachAll
1602 unsubscribeAll: function() {
1603 return this.detachAll.apply(this, arguments);
1607 * Creates a new custom event of the specified type. If a custom event
1608 * by that name already exists, it will not be re-created. In either
1609 * case the custom event is returned.
1613 * @param type {string} the type, or name of the event
1614 * @param opts {object} optional config params. Valid properties are:
1618 * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
1621 * 'bubbles': whether or not this event bubbles (true)
1622 * Events can only bubble if emitFacade is true.
1625 * 'context': the default execution context for the listeners (this)
1628 * 'defaultFn': the default function to execute when this event fires if preventDefault was not called
1631 * 'emitFacade': whether or not this event emits a facade (false)
1634 * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click'
1637 * 'fireOnce': if an event is configured to fire once, new subscribers after
1638 * the fire will be notified immediately.
1641 * 'async': fireOnce event listeners will fire synchronously if the event has already
1642 * fired unless async is true.
1645 * 'preventable': whether or not preventDefault() has an effect (true)
1648 * 'preventedFn': a function that is executed when preventDefault is called
1651 * 'queuable': whether or not this event can be queued during bubbling (false)
1654 * 'silent': if silent is true, debug messages are not provided for this event.
1657 * 'stoppedFn': a function that is executed when stopPropagation is called
1661 * 'monitored': specifies whether or not this event should send notifications about
1662 * when the event has been attached, detached, or published.
1665 * 'type': the event type (valid option if not provided as the first parameter to publish)
1669 * @return {CustomEvent} the custom event
1672 publish: function(type, opts) {
1673 var events, ce, ret, defaults,
1674 edata = this._yuievt,
1675 pre = edata.config.prefix;
1677 type = (pre) ? _getType(type, pre) : type;
1679 this._monitor('publish', type, {
1683 if (L.isObject(type)) {
1685 Y.each(type, function(v, k) {
1686 ret[k] = this.publish(k, v || opts);
1692 events = edata.events;
1696 // ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
1698 ce.applyConfig(opts, true);
1702 defaults = edata.defaults;
1705 ce = new Y.CustomEvent(type,
1706 (opts) ? Y.merge(defaults, opts) : defaults);
1710 // make sure we turn the broadcast flag off if this
1711 // event was published as a result of bubbling
1712 // if (opts instanceof Y.CustomEvent) {
1713 // events[type].broadcast = false;
1716 return events[type];
1720 * This is the entry point for the event monitoring system.
1721 * You can monitor 'attach', 'detach', 'fire', and 'publish'.
1722 * When configured, these events generate an event. click ->
1723 * click_attach, click_detach, click_publish -- these can
1724 * be subscribed to like other events to monitor the event
1725 * system. Inividual published events can have monitoring
1726 * turned on or off (publish can't be turned off before it
1727 * it published) by setting the events 'monitor' config.
1731 _monitor: function(what, type, o) {
1732 var monitorevt, ce = this.getEvent(type);
1733 if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) {
1734 monitorevt = type + '_' + what;
1736 this.fire.call(this, monitorevt, o);
1741 * Fire a custom event by name. The callback functions will be executed
1742 * from the context specified when the event was created, and with the
1743 * following parameters.
1745 * If the custom event object hasn't been created, then the event hasn't
1746 * been published and it has no subscribers. For performance sake, we
1747 * immediate exit in this case. This means the event won't bubble, so
1748 * if the intention is that a bubble target be notified, the event must
1749 * be published on this object first.
1751 * The first argument is the event type, and any additional arguments are
1752 * passed to the listeners as parameters. If the first of these is an
1753 * object literal, and the event is configured to emit an event facade,
1754 * that object is mixed into the event facade and the facade is provided
1755 * in place of the original object.
1758 * @param type {String|Object} The type of the event, or an object that contains
1759 * a 'type' property.
1760 * @param arguments {Object*} an arbitrary set of parameters to pass to
1761 * the handler. If the first of these is an object literal and the event is
1762 * configured to emit an event facade, the event facade will replace that
1763 * parameter after the properties the object literal contains are copied to
1765 * @return {EventTarget} the event host
1768 fire: function(type) {
1770 var typeIncluded = L.isString(type),
1771 t = (typeIncluded) ? type : (type && type.type),
1772 ce, ret, pre = this._yuievt.config.prefix, ce2,
1773 args = (typeIncluded) ? YArray(arguments, 1, true) : arguments;
1775 t = (pre) ? _getType(t, pre) : t;
1777 this._monitor('fire', t, {
1781 ce = this.getEvent(t, true);
1782 ce2 = this.getSibling(t, ce);
1785 ce = this.publish(t);
1788 // this event has not been published or subscribed to
1790 if (this._yuievt.hasTargets) {
1791 return this.bubble({ type: t }, args, this);
1794 // otherwise there is nothing to be done
1798 ret = ce.fire.apply(ce, args);
1801 return (this._yuievt.chain) ? this : ret;
1804 getSibling: function(type, ce) {
1806 // delegate to *:type events if there are subscribers
1807 if (type.indexOf(PREFIX_DELIMITER) > -1) {
1808 type = _wildType(type);
1809 // console.log(type);
1810 ce2 = this.getEvent(type, true);
1812 // console.log("GOT ONE: " + type);
1813 ce2.applyConfig(ce);
1814 ce2.bubbles = false;
1816 // ret = ce2.fire.apply(ce2, a);
1824 * Returns the custom event of the provided type has been created, a
1825 * falsy value otherwise
1827 * @param type {string} the type, or name of the event
1828 * @param prefixed {string} if true, the type is prefixed already
1829 * @return {CustomEvent} the custom event or null
1831 getEvent: function(type, prefixed) {
1834 pre = this._yuievt.config.prefix;
1835 type = (pre) ? _getType(type, pre) : type;
1837 e = this._yuievt.events;
1838 return e[type] || null;
1842 * Subscribe to a custom event hosted by this object. The
1843 * supplied callback will execute after any listeners add
1844 * via the subscribe method, and after the default function,
1845 * if configured for the event, has executed.
1847 * @param type {string} The type of the event
1848 * @param fn {Function} The callback
1849 * @param context {object} optional execution context.
1850 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
1851 * @return the event target or a detach handle per 'chain' config
1853 after: function(type, fn) {
1855 var a = YArray(arguments, 0, true);
1857 switch (L.type(type)) {
1859 return Y.Do.after.apply(Y.Do, arguments);
1861 // YArray.each(a[0], function(v) {
1862 // v = AFTER_PREFIX + v;
1869 a[0] = AFTER_PREFIX + type;
1872 return this.on.apply(this, a);
1877 * Executes the callback before a DOM event, custom event
1878 * or method. If the first argument is a function, it
1879 * is assumed the target is a method. For DOM and custom
1880 * events, this is an alias for Y.on.
1882 * For DOM and custom events:
1883 * type, callback, context, 0-n arguments
1886 * callback, object (method host), methodName, context, 0-n arguments
1889 * @return detach handle
1891 before: function() {
1892 return this.on.apply(this, arguments);
1899 // make Y an event target
1900 Y.mix(Y, ET.prototype, false, false, {
1906 YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
1909 * Hosts YUI page level events. This is where events bubble to
1910 * when the broadcast config is set to 2. This property is
1911 * only available if the custom event module is loaded.
1916 Y.Global = YUI.Env.globalEvents;
1918 // @TODO implement a global namespace function on Y.Global?
1921 * <code>YUI</code>'s <code>on</code> method is a unified interface for subscribing to
1922 * most events exposed by YUI. This includes custom events, DOM events, and
1923 * function events. <code>detach</code> is also provided to remove listeners
1924 * serviced by this function.
1926 * The signature that <code>on</code> accepts varies depending on the type
1927 * of event being consumed. Refer to the specific methods that will
1928 * service a specific request for additional information about subscribing
1929 * to that type of event.
1932 * <li>Custom events. These events are defined by various
1933 * modules in the library. This type of event is delegated to
1934 * <code>EventTarget</code>'s <code>on</code> method.
1936 * <li>The type of the event</li>
1937 * <li>The callback to execute</li>
1938 * <li>An optional context object</li>
1939 * <li>0..n additional arguments to supply the callback.</li>
1942 * <code>Y.on('drag:drophit', function() { // start work });</code>
1944 * <li>DOM events. These are moments reported by the browser related
1945 * to browser functionality and user interaction.
1946 * This type of event is delegated to <code>Event</code>'s
1947 * <code>attach</code> method.
1949 * <li>The type of the event</li>
1950 * <li>The callback to execute</li>
1951 * <li>The specification for the Node(s) to attach the listener
1952 * to. This can be a selector, collections, or Node/Element
1954 * <li>An optional context object</li>
1955 * <li>0..n additional arguments to supply the callback.</li>
1958 * <code>Y.on('click', function(e) { // something was clicked }, '#someelement');</code>
1960 * <li>Function events. These events can be used to react before or after a
1961 * function is executed. This type of event is delegated to <code>Event.Do</code>'s
1962 * <code>before</code> method.
1964 * <li>The callback to execute</li>
1965 * <li>The object that has the function that will be listened for.</li>
1966 * <li>The name of the function to listen for.</li>
1967 * <li>An optional context object</li>
1968 * <li>0..n additional arguments to supply the callback.</li>
1970 * Example <code>Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname');</code>
1974 * <code>on</code> corresponds to the moment before any default behavior of
1975 * the event. <code>after</code> works the same way, but these listeners
1976 * execute after the event's default behavior. <code>before</code> is an
1977 * alias for <code>on</code>.
1980 * @param type event type (this parameter does not apply for function events)
1981 * @param fn the callback
1982 * @param context optionally change the value of 'this' in the callback
1983 * @param args* 0..n additional arguments to pass to the callback.
1984 * @return the event target or a detach handle per 'chain' config
1989 * Listen for an event one time. Equivalent to <code>on</code>, except that
1990 * the listener is immediately detached when executed.
1993 * @param type event type (this parameter does not apply for function events)
1994 * @param fn the callback
1995 * @param context optionally change the value of 'this' in the callback
1996 * @param args* 0..n additional arguments to pass to the callback.
1997 * @return the event target or a detach handle per 'chain' config
2002 * after() is a unified interface for subscribing to
2003 * most events exposed by YUI. This includes custom events,
2004 * DOM events, and AOP events. This works the same way as
2005 * the on() function, only it operates after any default
2006 * behavior for the event has executed. @see <code>on</code> for more
2009 * @param type event type (this parameter does not apply for function events)
2010 * @param fn the callback
2011 * @param context optionally change the value of 'this' in the callback
2012 * @param args* 0..n additional arguments to pass to the callback.
2013 * @return the event target or a detach handle per 'chain' config
2018 }, '3.3.0' ,{requires:['oop']});