-
- /**
- * Custom event engine, DOM event listener abstraction layer, synthetic DOM
- * events.
- * @module event-custom
- * @submodule event-custom-base
- */
-
-
- // var onsubscribeType = "_event:onsub",
- var YArray = Y.Array,
-
- AFTER = 'after',
- CONFIGS = [
- 'broadcast',
- 'monitored',
- 'bubbles',
- 'context',
- 'contextFn',
- 'currentTarget',
- 'defaultFn',
- 'defaultTargetOnly',
- 'details',
- 'emitFacade',
- 'fireOnce',
- 'async',
- 'host',
- 'preventable',
- 'preventedFn',
- 'queuable',
- 'silent',
- 'stoppedFn',
- 'target',
- 'type'
- ],
-
- CONFIGS_HASH = YArray.hash(CONFIGS),
-
- nativeSlice = Array.prototype.slice,
-
- YUI3_SIGNATURE = 9,
- YUI_LOG = 'yui:log',
-
- mixConfigs = function(r, s, ov) {
- var p;
-
- for (p in s) {
- if (CONFIGS_HASH[p] && (ov || !(p in r))) {
- r[p] = s[p];
- }
- }
-
- return r;
- };
-
- /**
- * The CustomEvent class lets you define events for your application
- * that can be subscribed to by one or more independent component.
- *
- * @param {String} type The type of event, which is passed to the callback
- * when the event fires.
- * @param {object} defaults configuration object.
- * @class CustomEvent
- * @constructor
- */
-
- /**
- * The type of event, returned to subscribers when the event fires
- * @property type
- * @type string
- */
-
- /**
- * By default all custom events are logged in the debug build, set silent
- * to true to disable debug outpu for this event.
- * @property silent
- * @type boolean
- */
-
- Y.CustomEvent = function(type, defaults) {
-
- this._kds = Y.CustomEvent.keepDeprecatedSubs;
-
- this.id = Y.guid();
-
- this.type = type;
- this.silent = this.logSystem = (type === YUI_LOG);
-
- if (this._kds) {
- /**
- * The subscribers to this event
- * @property subscribers
- * @type Subscriber {}
- * @deprecated
- */
-
- /**
- * 'After' subscribers
- * @property afters
- * @type Subscriber {}
- * @deprecated
- */
- this.subscribers = {};
- this.afters = {};
- }
-
- if (defaults) {
- mixConfigs(this, defaults, true);
- }
- };
-
- /**
- * Static flag to enable population of the <a href="#property_subscribers">`subscribers`</a>
- * and <a href="#property_subscribers">`afters`</a> properties held on a `CustomEvent` instance.
- *
- * These properties were changed to private properties (`_subscribers` and `_afters`), and
- * converted from objects to arrays for performance reasons.
- *
- * Setting this property to true will populate the deprecated `subscribers` and `afters`
- * properties for people who may be using them (which is expected to be rare). There will
- * be a performance hit, compared to the new array based implementation.
- *
- * If you are using these deprecated properties for a use case which the public API
- * does not support, please file an enhancement request, and we can provide an alternate
- * public implementation which doesn't have the performance cost required to maintiain the
- * properties as objects.
- *
- * @property keepDeprecatedSubs
- * @static
- * @for CustomEvent
- * @type boolean
- * @default false
- * @deprecated
- */
- Y.CustomEvent.keepDeprecatedSubs = false;
-
- Y.CustomEvent.mixConfigs = mixConfigs;
-
- Y.CustomEvent.prototype = {
-
- constructor: Y.CustomEvent,
-
- /**
- * Monitor when an event is attached or detached.
- *
- * @property monitored
- * @type boolean
- */
-
- /**
- * If 0, this event does not broadcast. If 1, the YUI instance is notified
- * every time this event fires. If 2, the YUI instance and the YUI global
- * (if event is enabled on the global) are notified every time this event
- * fires.
- * @property broadcast
- * @type int
- */
-
- /**
- * Specifies whether this event should be queued when the host is actively
- * processing an event. This will effect exectution order of the callbacks
- * for the various events.
- * @property queuable
- * @type boolean
- * @default false
- */
-
- /**
- * This event has fired if true
- *
- * @property fired
- * @type boolean
- * @default false;
- */
-
- /**
- * An array containing the arguments the custom event
- * was last fired with.
- * @property firedWith
- * @type Array
- */
-
- /**
- * This event should only fire one time if true, and if
- * it has fired, any new subscribers should be notified
- * immediately.
- *
- * @property fireOnce
- * @type boolean
- * @default false;
- */
-
- /**
- * fireOnce listeners will fire syncronously unless async
- * is set to true
- * @property async
- * @type boolean
- * @default false
- */
-
- /**
- * Flag for stopPropagation that is modified during fire()
- * 1 means to stop propagation to bubble targets. 2 means
- * to also stop additional subscribers on this target.
- * @property stopped
- * @type int
- */
-
- /**
- * Flag for preventDefault that is modified during fire().
- * if it is not 0, the default behavior for this event
- * @property prevented
- * @type int
- */
-
- /**
- * Specifies the host for this custom event. This is used
- * to enable event bubbling
- * @property host
- * @type EventTarget
- */
-
- /**
- * The default function to execute after event listeners
- * have fire, but only if the default action was not
- * prevented.
- * @property defaultFn
- * @type Function
- */
-
- /**
- * Flag for the default function to execute only if the
- * firing event is the current target. This happens only
- * when using custom event delegation and setting the
- * flag to `true` mimics the behavior of event delegation
- * in the DOM.
- *
- * @property defaultTargetOnly
- * @type Boolean
- * @default false
- */
-
- /**
- * The function to execute if a subscriber calls
- * stopPropagation or stopImmediatePropagation
- * @property stoppedFn
- * @type Function
- */
-
- /**
- * The function to execute if a subscriber calls
- * preventDefault
- * @property preventedFn
- * @type Function
- */
-
- /**
- * The subscribers to this event
- * @property _subscribers
- * @type Subscriber []
- * @private
- */
-
- /**
- * 'After' subscribers
- * @property _afters
- * @type Subscriber []
- * @private
- */
-
- /**
- * If set to true, the custom event will deliver an EventFacade object
- * that is similar to a DOM event object.
- * @property emitFacade
- * @type boolean
- * @default false
- */
-
- /**
- * Supports multiple options for listener signatures in order to
- * port YUI 2 apps.
- * @property signature
- * @type int
- * @default 9
- */
- signature : YUI3_SIGNATURE,
-
- /**
- * The context the the event will fire from by default. Defaults to the YUI
- * instance.
- * @property context
- * @type object
- */
- context : Y,
-
- /**
- * Specifies whether or not this event's default function
- * can be cancelled by a subscriber by executing preventDefault()
- * on the event facade
- * @property preventable
- * @type boolean
- * @default true
- */
- preventable : true,
-
- /**
- * Specifies whether or not a subscriber can stop the event propagation
- * via stopPropagation(), stopImmediatePropagation(), or halt()
- *
- * Events can only bubble if emitFacade is true.
- *
- * @property bubbles
- * @type boolean
- * @default true
- */
- bubbles : true,
-
- /**
- * Returns the number of subscribers for this event as the sum of the on()
- * subscribers and after() subscribers.
- *
- * @method hasSubs
- * @return Number
- */
- hasSubs: function(when) {
- var s = 0,
- a = 0,
- subs = this._subscribers,
- afters = this._afters,
- sib = this.sibling;
-
- if (subs) {
- s = subs.length;
- }
-
- if (afters) {
- a = afters.length;
- }
-
- if (sib) {
- subs = sib._subscribers;
- afters = sib._afters;
-
- if (subs) {
- s += subs.length;
- }
-
- if (afters) {
- a += afters.length;
- }
- }
-
- if (when) {
- return (when === 'after') ? a : s;
- }
-
- return (s + a);
- },
-
- /**
- * Monitor the event state for the subscribed event. The first parameter
- * is what should be monitored, the rest are the normal parameters when
- * subscribing to an event.
- * @method monitor
- * @param what {string} what to monitor ('detach', 'attach', 'publish').
- * @return {EventHandle} return value from the monitor event subscription.
- */
- monitor: function(what) {
- this.monitored = true;
- var type = this.id + '|' + this.type + '_' + what,
- args = nativeSlice.call(arguments, 0);
- args[0] = type;
- return this.host.on.apply(this.host, args);
- },
-
- /**
- * Get all of the subscribers to this event and any sibling event
- * @method getSubs
- * @return {Array} first item is the on subscribers, second the after.
- */
- getSubs: function() {
-
- var sibling = this.sibling,
- subs = this._subscribers,
- afters = this._afters,
- siblingSubs,
- siblingAfters;
-
- if (sibling) {
- siblingSubs = sibling._subscribers;
- siblingAfters = sibling._afters;
- }
-
- if (siblingSubs) {
- if (subs) {
- subs = subs.concat(siblingSubs);
- } else {
- subs = siblingSubs.concat();
- }
- } else {
- if (subs) {
- subs = subs.concat();
- } else {
- subs = [];
- }
- }
-
- if (siblingAfters) {
- if (afters) {
- afters = afters.concat(siblingAfters);
- } else {
- afters = siblingAfters.concat();
- }
- } else {
- if (afters) {
- afters = afters.concat();
- } else {
- afters = [];
- }
- }
-
- return [subs, afters];
- },
-
- /**
- * Apply configuration properties. Only applies the CONFIG whitelist
- * @method applyConfig
- * @param o hash of properties to apply.
- * @param force {boolean} if true, properties that exist on the event
- * will be overwritten.
- */
- applyConfig: function(o, force) {
- mixConfigs(this, o, force);
- },
-
- /**
- * Create the Subscription for subscribing function, context, and bound
- * arguments. If this is a fireOnce event, the subscriber is immediately
- * notified.
- *
- * @method _on
- * @param fn {Function} Subscription callback
- * @param [context] {Object} Override `this` in the callback
- * @param [args] {Array} bound arguments that will be passed to the callback after the arguments generated by fire()
- * @param [when] {String} "after" to slot into after subscribers
- * @return {EventHandle}
- * @protected
- */
- _on: function(fn, context, args, when) {
-
- if (!fn) { this.log('Invalid callback for CE: ' + this.type); }
-
- var s = new Y.Subscriber(fn, context, args, when),
- firedWith;
-
- if (this.fireOnce && this.fired) {
-
- firedWith = this.firedWith;
-
- // It's a little ugly for this to know about facades,
- // but given the current breakup, not much choice without
- // moving a whole lot of stuff around.
- if (this.emitFacade && this._addFacadeToArgs) {
- this._addFacadeToArgs(firedWith);
- }
-
- if (this.async) {
- setTimeout(Y.bind(this._notify, this, s, firedWith), 0);
- } else {
- this._notify(s, firedWith);
- }
- }
-
- if (when === AFTER) {
- if (!this._afters) {
- this._afters = [];
- }
- this._afters.push(s);
- } else {
- if (!this._subscribers) {
- this._subscribers = [];
- }
- this._subscribers.push(s);
- }
-
- if (this._kds) {
- if (when === AFTER) {
- this.afters[s.id] = s;
- } else {
- this.subscribers[s.id] = s;
- }
- }
-
- return new Y.EventHandle(this, s);
- },
-
- /**
- * Listen for this event
- * @method subscribe
- * @param {Function} fn The function to execute.
- * @return {EventHandle} Unsubscribe handle.
- * @deprecated use on.
- */
- subscribe: function(fn, context) {
- Y.log('ce.subscribe deprecated, use "on"', 'warn', 'deprecated');
- var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null;
- return this._on(fn, context, a, true);
- },
-
- /**
- * Listen for this event
- * @method on
- * @param {Function} fn The function to execute.
- * @param {object} context optional execution context.
- * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
- * when the event fires.
- * @return {EventHandle} An object with a detach method to detch the handler(s).
- */
- on: function(fn, context) {
- var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null;
-
- if (this.monitored && this.host) {
- this.host._monitor('attach', this, {
- args: arguments
- });
- }
- return this._on(fn, context, a, true);
- },
-
- /**
- * Listen for this event after the normal subscribers have been notified and
- * the default behavior has been applied. If a normal subscriber prevents the
- * default behavior, it also prevents after listeners from firing.
- * @method after
- * @param {Function} fn The function to execute.
- * @param {object} context optional execution context.
- * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
- * when the event fires.
- * @return {EventHandle} handle Unsubscribe handle.
- */
- after: function(fn, context) {
- var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null;
- return this._on(fn, context, a, AFTER);
- },
-
- /**
- * Detach listeners.
- * @method detach
- * @param {Function} fn The subscribed function to remove, if not supplied
- * all will be removed.
- * @param {Object} context The context object passed to subscribe.
- * @return {Number} returns the number of subscribers unsubscribed.
- */
- detach: function(fn, context) {
- // unsubscribe handle
- if (fn && fn.detach) {
- return fn.detach();
- }
-
- var i, s,
- found = 0,
- subs = this._subscribers,
- afters = this._afters;
-
- if (subs) {
- for (i = subs.length; i >= 0; i--) {
- s = subs[i];
- if (s && (!fn || fn === s.fn)) {
- this._delete(s, subs, i);
- found++;
- }
- }
- }
-
- if (afters) {
- for (i = afters.length; i >= 0; i--) {
- s = afters[i];
- if (s && (!fn || fn === s.fn)) {
- this._delete(s, afters, i);
- found++;
- }
- }
- }
-
- return found;
- },
-
- /**
- * Detach listeners.
- * @method unsubscribe
- * @param {Function} fn The subscribed function to remove, if not supplied
- * all will be removed.
- * @param {Object} context The context object passed to subscribe.
- * @return {int|undefined} returns the number of subscribers unsubscribed.
- * @deprecated use detach.
- */
- unsubscribe: function() {
- return this.detach.apply(this, arguments);
- },
-
- /**
- * Notify a single subscriber
- * @method _notify
- * @param {Subscriber} s the subscriber.
- * @param {Array} args the arguments array to apply to the listener.
- * @protected
- */
- _notify: function(s, args, ef) {
-
- this.log(this.type + '->' + 'sub: ' + s.id);
-
- var ret;
-
- ret = s.notify(args, this);
-
- if (false === ret || this.stopped > 1) {
- this.log(this.type + ' cancelled by subscriber');
- return false;
- }
-
- return true;
- },
-
- /**
- * Logger abstraction to centralize the application of the silent flag
- * @method log
- * @param {string} msg message to log.
- * @param {string} cat log category.
- */
- log: function(msg, cat) {
- if (!this.silent) { Y.log(this.id + ': ' + msg, cat || 'info', 'event'); }
- },
-
- /**
- * Notifies the subscribers. The callback functions will be executed
- * from the context specified when the event was created, and with the
- * following parameters:
- * <ul>
- * <li>The type of event</li>
- * <li>All of the arguments fire() was executed with as an array</li>
- * <li>The custom object (if any) that was passed into the subscribe()
- * method</li>
- * </ul>
- * @method fire
- * @param {Object*} arguments an arbitrary set of parameters to pass to
- * the handler.
- * @return {boolean} false if one of the subscribers returned false,
- * true otherwise.
- *
- */
- fire: function() {
-
- // push is the fastest way to go from arguments to arrays
- // for most browsers currently
- // http://jsperf.com/push-vs-concat-vs-slice/2
-
- var args = [];
- args.push.apply(args, arguments);
-
- return this._fire(args);
- },
-
- /**
- * Private internal implementation for `fire`, which is can be used directly by
- * `EventTarget` and other event module classes which have already converted from
- * an `arguments` list to an array, to avoid the repeated overhead.
- *
- * @method _fire
- * @private
- * @param {Array} args The array of arguments passed to be passed to handlers.
- * @return {boolean} false if one of the subscribers returned false, true otherwise.
- */
- _fire: function(args) {
-
- if (this.fireOnce && this.fired) {
- this.log('fireOnce event: ' + this.type + ' already fired');
- return true;
- } else {
-
- // this doesn't happen if the event isn't published
- // this.host._monitor('fire', this.type, args);
-
- this.fired = true;
-
- if (this.fireOnce) {
- this.firedWith = args;
- }
-
- if (this.emitFacade) {
- return this.fireComplex(args);
- } else {
- return this.fireSimple(args);
- }
- }
- },
-
- /**
- * Set up for notifying subscribers of non-emitFacade events.
- *
- * @method fireSimple
- * @param args {Array} Arguments passed to fire()
- * @return Boolean false if a subscriber returned false
- * @protected
- */
- fireSimple: function(args) {
- this.stopped = 0;
- this.prevented = 0;
- if (this.hasSubs()) {
- var subs = this.getSubs();
- this._procSubs(subs[0], args);
- this._procSubs(subs[1], args);
- }
- if (this.broadcast) {
- this._broadcast(args);
- }
- return this.stopped ? false : true;
- },
-
- // Requires the event-custom-complex module for full funcitonality.
- fireComplex: function(args) {
- this.log('Missing event-custom-complex needed to emit a facade for: ' + this.type);
- args[0] = args[0] || {};
- return this.fireSimple(args);
- },
-
- /**
- * Notifies a list of subscribers.
- *
- * @method _procSubs
- * @param subs {Array} List of subscribers
- * @param args {Array} Arguments passed to fire()
- * @param ef {}
- * @return Boolean false if a subscriber returns false or stops the event
- * propagation via e.stopPropagation(),
- * e.stopImmediatePropagation(), or e.halt()
- * @private
- */
- _procSubs: function(subs, args, ef) {
- var s, i, l;
-
- for (i = 0, l = subs.length; i < l; i++) {
- s = subs[i];
- if (s && s.fn) {
- if (false === this._notify(s, args, ef)) {
- this.stopped = 2;
- }
- if (this.stopped === 2) {
- return false;
- }
- }
- }
-
- return true;
- },
-
- /**
- * Notifies the YUI instance if the event is configured with broadcast = 1,
- * and both the YUI instance and Y.Global if configured with broadcast = 2.
- *
- * @method _broadcast
- * @param args {Array} Arguments sent to fire()
- * @private
- */
- _broadcast: function(args) {
- if (!this.stopped && this.broadcast) {
-
- var a = args.concat();
- a.unshift(this.type);
-
- if (this.host !== Y) {
- Y.fire.apply(Y, a);
- }
-
- if (this.broadcast === 2) {
- Y.Global.fire.apply(Y.Global, a);
- }
- }
- },
-
- /**
- * Removes all listeners
- * @method unsubscribeAll
- * @return {Number} The number of listeners unsubscribed.
- * @deprecated use detachAll.
- */
- unsubscribeAll: function() {
- return this.detachAll.apply(this, arguments);
- },
-
- /**
- * Removes all listeners
- * @method detachAll
- * @return {Number} The number of listeners unsubscribed.
- */
- detachAll: function() {
- return this.detach();
- },
-
- /**
- * Deletes the subscriber from the internal store of on() and after()
- * subscribers.
- *
- * @method _delete
- * @param s subscriber object.
- * @param subs (optional) on or after subscriber array
- * @param index (optional) The index found.
- * @private
- */
- _delete: function(s, subs, i) {
- var when = s._when;
-
- if (!subs) {
- subs = (when === AFTER) ? this._afters : this._subscribers;
- }
-
- if (subs) {
- i = YArray.indexOf(subs, s, 0);
-
- if (s && subs[i] === s) {
- subs.splice(i, 1);
- }
- }
-
- if (this._kds) {
- if (when === AFTER) {
- delete this.afters[s.id];
- } else {
- delete this.subscribers[s.id];
- }
- }
-
- if (this.monitored && this.host) {
- this.host._monitor('detach', this, {
- ce: this,
- sub: s
- });
- }
-
- if (s) {
- s.deleted = true;
- }
- }
- };
-
-