#

API Docs for: 3.10.3
Show: Inherited Protected Private Deprecated

# 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 {string} 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 {string} 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 {string} 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 {string} 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;

    //////////////////////////////////////////////////////////////////////////