/** * Allows to chain method executions without having to rely on additional APIs. * * For example, say you have a class with an asynchronous method "performAction" which takes some parameters * and when computation is done invokes a "callback" parameter: * * var myObj = { * performAction: function(arg1, arg2, callback) { * // doest something * } * }; * * And now you have a requirement that you need to make it possible to chain several methods, this can be easily achieved * with SimpleChain: * * var chain = Ext.create('MF.intercepting.SimpleChain', { * interceptors: [ * { * performAction: function(arg1, arg2, arg3, next) { * // received arguments passed to "chain.start()" method * // arg1 = "foo" * // arg2 = "bar" * // arg3 = "baz" * next(arg1, "replaced-2nd-arg", arg3); * } * }, * { * performAction: function(arg1, arg2, arg3, next) { * // received values from previous callback * // arg1 = "foo" * // arg2 = "replaced-2nd-arg" * // arg3 = "baz" * next("replaced-1st-arg", arg2, arg3); * } * } * ] * }]; * * chain.start('pefromAction', ['foo', 'bar', 'baz'], function(arg1, arg2, arg3) { * // values received from last interceptor * // arg1 = "replaced-1st-arg" * // arg2 = "replaced-2nd-arg" * // arg3 = "baz" * }); * * @author Sergei Lissovski <sergei.lissovski@modera.org> */ Ext.define('MF.intercepting.SimpleChain', { requires: [ 'MF.Util' ], /** * Interceptors. When you invoke #start method then every's interceptor method with name "methodName" and arguments * specified in start's method "args" will be attempted to be invoked. * * @cfg {Object[]} interceptors */ constructor: function (config) { MF.Util.validateRequiredConfigParams(this, config, ['interceptors']); Ext.apply(this, config); }, /** * Starts executing the chain, a given *methodName* is going to be invoked on every interceptors with * *args*. * * @param {String} methodName A name of a method to invoke on configured interceptors. * @param {Mixed[]} args Interceptors are going to be invoked with this sets of arguments. * @param {Function} callback When there are no interceptors left this callback will be invoked. * @param {Object} callbackContext Optional. If provided then passed #callback is going to be invoked * with this context, otherwise SimpleChain's context is used. */ start: function(methodName, args, callback, callbackContext) { var me = this; var position = 0; var callNext = function() { if (me.interceptors[position]) { var itc = me.interceptors[position]; if (!Ext.isFunction(itc[methodName])) { throw Ext.String.format( '{0}.start(): interceptor at position {1} does not have method "{2}".', me.$className, position, methodName ); } args.push(function(receivedArgs) { args = []; Ext.each(arguments, function(arg) { args.push(arg); }); position++; callNext(); }); itc[methodName].apply(itc, args); } else { callback.apply(callbackContext || me, args); } }; callNext(); } });