/** * @class Ext.Function * * A collection of useful static methods to deal with function callbacks * @singleton */ Ext.Function = { /** * A very commonly used method throughout the framework. It acts as a wrapper around another method * which originally accepts 2 arguments for <code>name</code> and <code>value</code>. * The wrapped function then allows "flexible" value setting of either: * * <ul> * <li><code>name</code> and <code>value</code> as 2 arguments</li> * <li>one single object argument with multiple key - value pairs</li> * </ul> * * For example: * <pre><code> var setValue = Ext.Function.flexSetter(function(name, value) { this[name] = value; }); // Afterwards // Setting a single name - value setValue('name1', 'value1'); // Settings multiple name - value pairs setValue({ name1: 'value1', name2: 'value2', name3: 'value3' }); * </code></pre> * @param {Function} setter * @returns {Function} flexSetter */ flexSetter: function(fn) { return function(a, b) { var k, i; if (a === null) { return this; } if (typeof a !== 'string') { for (k in a) { if (a.hasOwnProperty(k)) { fn.call(this, k, a[k]); } } if (Ext.enumerables) { for (i = Ext.enumerables.length; i--;) { k = Ext.enumerables[i]; if (a.hasOwnProperty(k)) { fn.call(this, k, a[k]); } } } } else { fn.call(this, a, b); } return this; }; }, /** * Create a new function from the provided <code>fn</code>, change <code>this</code> to the provided scope, optionally * overrides arguments for the call. (Defaults to the arguments passed by the caller) * * @param {Function} fn The function to delegate. * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed. * <b>If omitted, defaults to the browser window.</b> * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller) * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding, * if a number the args are inserted at the specified position * @return {Function} The new function */ bind: function(fn, scope, args, appendArgs) { var method = fn, applyArgs; return function() { var callArgs = args || arguments; if (appendArgs === true) { callArgs = Array.prototype.slice.call(arguments, 0); callArgs = callArgs.concat(args); } else if (Ext.isNumber(appendArgs)) { callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first applyArgs = [appendArgs, 0].concat(args); // create method call params Array.prototype.splice.apply(callArgs, applyArgs); // splice them in } return method.apply(scope || window, callArgs); }; }, /** * Create a new function from the provided <code>fn</code>, the arguments of which are pre-set to `args`. * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones. * This is especially useful when creating callbacks. * For example: * var originalFunction = function(){ alert(Ext.Array.from(arguments).join(' ')); }; var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']); callback(); // alerts 'Hello World' callback('by Me'); // alerts 'Hello World by Me' * @param {Function} fn The original function * @param {Array} args The arguments to pass to new callback * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed. * @return {Function} The new callback function */ pass: function(fn, args, scope) { if (args) { args = Ext.Array.from(args); } return function() { return fn.apply(scope, args.concat(Ext.Array.toArray(arguments))); }; }, /** * Create an alias to the provided method property with name <code>methodName</code> of <code>object</code>. * Note that the execution scope will still be bound to the provided <code>object</code> itself. * * @param {Object/Function} object * @param {String} methodName * @return {Function} aliasFn */ alias: function(object, methodName) { return function() { return object[methodName].apply(object, arguments); }; }, /** * Creates an interceptor function. The passed function is called before the original one. If it returns false, * the original one is not called. The resulting function returns the results of the original function. * The passed function is called with the parameters of the original function. Example usage: * <pre><code> var sayHi = function(name){ alert('Hi, ' + name); } sayHi('Fred'); // alerts "Hi, Fred" // create a new function that validates input without // directly modifying the original function: var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){ return name == 'Brian'; }); sayHiToFriend('Fred'); // no alert sayHiToFriend('Brian'); // alerts "Hi, Brian" </code></pre> * @param {Function} origFn The original function. * @param {Function} newFn The function to call before the original * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed. * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b> * @param {Mixed} returnValue (optional) The value to return if the passed function return false (defaults to null). * @return {Function} The new function */ createInterceptor: function(origFn, newFn, scope, returnValue) { var method = origFn; if (!Ext.isFunction(newFn)) { return origFn; } else { return function() { var me = this, args = arguments; newFn.target = me; newFn.method = origFn; return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null; }; } }, /** * Creates a delegate (callback) which, when called, executes after a specific delay. * @param {Function} fn The function which will be called on a delay when the returned function is called. * Optionally, a replacement (or additional) argument list may be specified. * @param {Number} delay The number of milliseconds to defer execution by whenever called. * @param {Object} scope (optional) The scope (<code>this</code> reference) used by the function at execution time. * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller) * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding, * if a number the args are inserted at the specified position. * @return {Function} A function which, when called, executes the original function after the specified delay. */ createDelayed: function(fn, delay, scope, args, appendArgs) { if (scope || args) { fn = Ext.Function.bind(fn, scope, args, appendArgs); } return function() { var me = this; setTimeout(function() { fn.apply(me, arguments); }, delay); }; }, /** * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage: * <pre><code> var sayHi = function(name){ alert('Hi, ' + name); } // executes immediately: sayHi('Fred'); // executes after 2 seconds: Ext.Function.defer(sayHi, 2000, this, ['Fred']); // this syntax is sometimes useful for deferring // execution of an anonymous function: Ext.Function.defer(function(){ alert('Anonymous'); }, 100); </code></pre> * @param {Function} fn The function to defer. * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately) * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed. * <b>If omitted, defaults to the browser window.</b> * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller) * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding, * if a number the args are inserted at the specified position * @return {Number} The timeout id that can be used with clearTimeout */ defer: function(fn, millis, obj, args, appendArgs) { fn = Ext.Function.bind(fn, obj, args, appendArgs); if (millis > 0) { return setTimeout(fn, millis); } fn(); return 0; }, /** * Create a combined function call sequence of the original function + the passed function. * The resulting function returns the results of the original function. * The passed function is called with the parameters of the original function. Example usage: * * <pre><code> var sayHi = function(name){ alert('Hi, ' + name); } sayHi('Fred'); // alerts "Hi, Fred" var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){ alert('Bye, ' + name); }); sayGoodbye('Fred'); // both alerts show * </code></pre> * * @param {Function} origFn The original function. * @param {Function} newFn The function to sequence * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed. * If omitted, defaults to the scope in which the original function is called or the browser window. * @return {Function} The new function */ createSequence: function(origFn, newFn, scope) { if (!Ext.isFunction(newFn)) { return origFn; } else { return function() { var retval = origFn.apply(this || window, arguments); newFn.apply(scope || this || window, arguments); return retval; }; } }, /** * <p>Creates a delegate function, optionally with a bound scope which, when called, buffers * the execution of the passed function for the configured number of milliseconds. * If called again within that period, the impending invocation will be canceled, and the * timeout period will begin again.</p> * * @param {Function} fn The function to invoke on a buffered timer. * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the * function. * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which * the passed function is executed. If omitted, defaults to the scope specified by the caller. * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments * passed by the caller. * @return {Function} A function which invokes the passed function after buffering for the specified time. */ createBuffered: function(fn, buffer, scope, args) { return function(){ var timerId; return function() { var me = this; if (timerId) { clearInterval(timerId); timerId = null; } timerId = setTimeout(function(){ fn.apply(scope || me, args || arguments); }, buffer); }; }(); }, /** * <p>Creates a throttled version of the passed function which, when called repeatedly and * rapidly, invokes the passed function only after a certain interval has elapsed since the * previous invocation.</p> * * <p>This is useful for wrapping functions which may be called repeatedly, such as * a handler of a mouse move event when the processing is expensive.</p> * * @param fn {Function} The function to execute at a regular time interval. * @param interval {Number} The interval <b>in milliseconds</b> on which the passed function is executed. * @param scope (optional) The scope (<code><b>this</b></code> reference) in which * the passed function is executed. If omitted, defaults to the scope specified by the caller. * @returns {Function} A function which invokes the passed function at the specified interval. */ createThrottled: function(fn, interval, scope) { var lastCallTime, elapsed, lastArgs, timer, execute = function() { fn.apply(scope || this, lastArgs); lastCallTime = new Date().getTime(); }; return function() { elapsed = new Date().getTime() - lastCallTime; lastArgs = arguments; clearTimeout(timer); if (!lastCallTime || (elapsed >= interval)) { execute(); } else { timer = setTimeout(execute, interval - elapsed); } }; } }; /** * Shorthand for {@link Ext.Function#defer} * @member Ext * @method defer */ Ext.defer = Ext.Function.alias(Ext.Function, 'defer'); /** * Shorthand for {@link Ext.Function#pass} * @member Ext * @method pass */ Ext.pass = Ext.Function.alias(Ext.Function, 'pass'); /** * Shorthand for {@link Ext.Function#bind} * @member Ext * @method bind */ Ext.bind = Ext.Function.alias(Ext.Function, 'bind');