Version 3.18.1
Show:

File: event-custom/js/event-do.js

            /**
             * Custom event engine, DOM event listener abstraction layer, synthetic DOM
             * events.
             * @module event-custom
             * @submodule event-custom-base
             */
            
            /**
             * Allows for the insertion of methods that are executed before or after
             * a specified method
             * @class Do
             * @static
             */
            
            var DO_BEFORE = 0,
                DO_AFTER = 1,
            
            DO = {
            
                /**
                 * Cache of objects touched by the utility
                 * @property objs
                 * @static
                 * @deprecated Since 3.6.0. The `_yuiaop` property on the AOP'd object
                 * replaces the role of this property, but is considered to be private, and
                 * is only mentioned to provide a migration path.
                 *
                 * If you have a use case which warrants migration to the _yuiaop property,
                 * please file a ticket to let us know what it's used for and we can see if
                 * we need to expose hooks for that functionality more formally.
                 */
                objs: null,
            
                /**
                 * <p>Execute the supplied method before the specified function.  Wrapping
                 * function may optionally return an instance of the following classes to
                 * further alter runtime behavior:</p>
                 * <dl>
                 *     <dt></code>Y.Do.Halt(message, returnValue)</code></dt>
                 *         <dd>Immediatly stop execution and return
                 *         <code>returnValue</code>.  No other wrapping functions will be
                 *         executed.</dd>
                 *     <dt></code>Y.Do.AlterArgs(message, newArgArray)</code></dt>
                 *         <dd>Replace the arguments that the original function will be
                 *         called with.</dd>
                 *     <dt></code>Y.Do.Prevent(message)</code></dt>
                 *         <dd>Don't execute the wrapped function.  Other before phase
                 *         wrappers will be executed.</dd>
                 * </dl>
                 *
                 * @method before
                 * @param fn {Function} the function to execute
                 * @param obj the object hosting the method to displace
                 * @param sFn {string} the name of the method to displace
                 * @param c The execution context for fn
                 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
                 * when the event fires.
                 * @return {EventHandle} handle for the subscription
                 * @static
                 */
                before: function(fn, obj, sFn, c) {
                    // Y.log('Do before: ' + sFn, 'info', 'event');
                    var f = fn, a;
                    if (c) {
                        a = [fn, c].concat(Y.Array(arguments, 4, true));
                        f = Y.rbind.apply(Y, a);
                    }
            
                    return this._inject(DO_BEFORE, f, obj, sFn);
                },
            
                /**
                 * <p>Execute the supplied method after the specified function.  Wrapping
                 * function may optionally return an instance of the following classes to
                 * further alter runtime behavior:</p>
                 * <dl>
                 *     <dt></code>Y.Do.Halt(message, returnValue)</code></dt>
                 *         <dd>Immediatly stop execution and return
                 *         <code>returnValue</code>.  No other wrapping functions will be
                 *         executed.</dd>
                 *     <dt></code>Y.Do.AlterReturn(message, returnValue)</code></dt>
                 *         <dd>Return <code>returnValue</code> instead of the wrapped
                 *         method's original return value.  This can be further altered by
                 *         other after phase wrappers.</dd>
                 * </dl>
                 *
                 * <p>The static properties <code>Y.Do.originalRetVal</code> and
                 * <code>Y.Do.currentRetVal</code> will be populated for reference.</p>
                 *
                 * @method after
                 * @param fn {Function} the function to execute
                 * @param obj the object hosting the method to displace
                 * @param sFn {string} the name of the method to displace
                 * @param c The execution context for fn
                 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
                 * @return {EventHandle} handle for the subscription
                 * @static
                 */
                after: function(fn, obj, sFn, c) {
                    var f = fn, a;
                    if (c) {
                        a = [fn, c].concat(Y.Array(arguments, 4, true));
                        f = Y.rbind.apply(Y, a);
                    }
            
                    return this._inject(DO_AFTER, f, obj, sFn);
                },
            
                /**
                 * Execute the supplied method before or after the specified function.
                 * Used by <code>before</code> and <code>after</code>.
                 *
                 * @method _inject
                 * @param when {string} before or after
                 * @param fn {Function} the function to execute
                 * @param obj the object hosting the method to displace
                 * @param sFn {string} the name of the method to displace
                 * @param c The execution context for fn
                 * @return {EventHandle} handle for the subscription
                 * @private
                 * @static
                 */
                _inject: function(when, fn, obj, sFn) {
                    // object id
                    var id = Y.stamp(obj), o, sid;
            
                    if (!obj._yuiaop) {
                        // create a map entry for the obj if it doesn't exist, to hold overridden methods
                        obj._yuiaop = {};
                    }
            
                    o = obj._yuiaop;
            
                    if (!o[sFn]) {
                        // create a map entry for the method if it doesn't exist
                        o[sFn] = new Y.Do.Method(obj, sFn);
            
                        // re-route the method to our wrapper
                        obj[sFn] = function() {
                            return o[sFn].exec.apply(o[sFn], arguments);
                        };
                    }
            
                    // subscriber id
                    sid = id + Y.stamp(fn) + sFn;
            
                    // register the callback
                    o[sFn].register(sid, fn, when);
            
                    return new Y.EventHandle(o[sFn], sid);
                },
            
                /**
                 * Detach a before or after subscription.
                 *
                 * @method detach
                 * @param handle {EventHandle} the subscription handle
                 * @static
                 */
                detach: function(handle) {
                    if (handle.detach) {
                        handle.detach();
                    }
                }
            };
            
            Y.Do = DO;
            
            //////////////////////////////////////////////////////////////////////////
            
            /**
             * Contains the return value from the wrapped method, accessible
             * by 'after' event listeners.
             *
             * @property originalRetVal
             * @static
             * @since 3.2.0
             */
            
            /**
             * Contains the current state of the return value, consumable by
             * 'after' event listeners, and updated if an after subscriber
             * changes the return value generated by the wrapped function.
             *
             * @property currentRetVal
             * @static
             * @since 3.2.0
             */
            
            //////////////////////////////////////////////////////////////////////////
            
            /**
             * Wrapper for a displaced method with aop enabled
             * @class Do.Method
             * @constructor
             * @param obj The object to operate on
             * @param sFn The name of the method to displace
             */
            DO.Method = function(obj, sFn) {
                this.obj = obj;
                this.methodName = sFn;
                this.method = obj[sFn];
                this.before = {};
                this.after = {};
            };
            
            /**
             * Register a aop subscriber
             * @method register
             * @param sid {string} the subscriber id
             * @param fn {Function} the function to execute
             * @param when {string} when to execute the function
             */
            DO.Method.prototype.register = function (sid, fn, when) {
                if (when) {
                    this.after[sid] = fn;
                } else {
                    this.before[sid] = fn;
                }
            };
            
            /**
             * Unregister a aop subscriber
             * @method delete
             * @param sid {string} the subscriber id
             * @param fn {Function} the function to execute
             * @param when {string} when to execute the function
             */
            DO.Method.prototype._delete = function (sid) {
                // Y.log('Y.Do._delete: ' + sid, 'info', 'Event');
                delete this.before[sid];
                delete this.after[sid];
            };
            
            /**
             * <p>Execute the wrapped method.  All arguments are passed into the wrapping
             * functions.  If any of the before wrappers return an instance of
             * <code>Y.Do.Halt</code> or <code>Y.Do.Prevent</code>, neither the wrapped
             * function nor any after phase subscribers will be executed.</p>
             *
             * <p>The return value will be the return value of the wrapped function or one
             * provided by a wrapper function via an instance of <code>Y.Do.Halt</code> or
             * <code>Y.Do.AlterReturn</code>.
             *
             * @method exec
             * @param arg* {any} Arguments are passed to the wrapping and wrapped functions
             * @return {any} Return value of wrapped function unless overwritten (see above)
             */
            DO.Method.prototype.exec = function () {
            
                var args = Y.Array(arguments, 0, true),
                    i, ret, newRet,
                    bf = this.before,
                    af = this.after,
                    prevented = false;
            
                // execute before
                for (i in bf) {
                    if (bf.hasOwnProperty(i)) {
                        ret = bf[i].apply(this.obj, args);
                        if (ret) {
                            switch (ret.constructor) {
                                case DO.Halt:
                                    return ret.retVal;
                                case DO.AlterArgs:
                                    args = ret.newArgs;
                                    break;
                                case DO.Prevent:
                                    prevented = true;
                                    break;
                                default:
                            }
                        }
                    }
                }
            
                // execute method
                if (!prevented) {
                    ret = this.method.apply(this.obj, args);
                }
            
                DO.originalRetVal = ret;
                DO.currentRetVal = ret;
            
                // execute after methods.
                for (i in af) {
                    if (af.hasOwnProperty(i)) {
                        newRet = af[i].apply(this.obj, args);
                        // Stop processing if a Halt object is returned
                        if (newRet && newRet.constructor === DO.Halt) {
                            return newRet.retVal;
                        // Check for a new return value
                        } else if (newRet && newRet.constructor === DO.AlterReturn) {
                            ret = newRet.newRetVal;
                            // Update the static retval state
                            DO.currentRetVal = ret;
                        }
                    }
                }
            
                return ret;
            };
            
            //////////////////////////////////////////////////////////////////////////
            
            /**
             * Return an AlterArgs object when you want to change the arguments that
             * were passed into the function.  Useful for Do.before subscribers.  An
             * example would be a service that scrubs out illegal characters prior to
             * executing the core business logic.
             * @class Do.AlterArgs
             * @constructor
             * @param msg {String} (optional) Explanation of the altered return value
             * @param newArgs {Array} Call parameters to be used for the original method
             *                        instead of the arguments originally passed in.
             */
            DO.AlterArgs = function(msg, newArgs) {
                this.msg = msg;
                this.newArgs = newArgs;
            };
            
            /**
             * Return an AlterReturn object when you want to change the result returned
             * from the core method to the caller.  Useful for Do.after subscribers.
             * @class Do.AlterReturn
             * @constructor
             * @param msg {String} (optional) Explanation of the altered return value
             * @param newRetVal {any} Return value passed to code that invoked the wrapped
             *                      function.
             */
            DO.AlterReturn = function(msg, newRetVal) {
                this.msg = msg;
                this.newRetVal = newRetVal;
            };
            
            /**
             * Return a Halt object when you want to terminate the execution
             * of all subsequent subscribers as well as the wrapped method
             * if it has not exectued yet.  Useful for Do.before subscribers.
             * @class Do.Halt
             * @constructor
             * @param msg {String} (optional) Explanation of why the termination was done
             * @param retVal {any} Return value passed to code that invoked the wrapped
             *                      function.
             */
            DO.Halt = function(msg, retVal) {
                this.msg = msg;
                this.retVal = retVal;
            };
            
            /**
             * Return a Prevent object when you want to prevent the wrapped function
             * from executing, but want the remaining listeners to execute.  Useful
             * for Do.before subscribers.
             * @class Do.Prevent
             * @constructor
             * @param msg {String} (optional) Explanation of why the termination was done
             */
            DO.Prevent = function(msg) {
                this.msg = msg;
            };
            
            /**
             * Return an Error object when you want to terminate the execution
             * of all subsequent method calls.
             * @class Do.Error
             * @constructor
             * @param msg {String} (optional) Explanation of the altered return value
             * @param retVal {any} Return value passed to code that invoked the wrapped
             *                      function.
             * @deprecated use Y.Do.Halt or Y.Do.Prevent
             */
            DO.Error = DO.Halt;
            
            
            //////////////////////////////////////////////////////////////////////////