Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / core / src / lang / Function.js
index 0067f1b..1dd35e0 100644 (file)
@@ -1,39 +1,50 @@
+/*
+
+This file is part of Ext JS 4
+
+Copyright (c) 2011 Sencha Inc
+
+Contact:  http://www.sencha.com/contact
+
+GNU General Public License Usage
+This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
+
+If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
+
+*/
 /**
  * @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>.
+     * which originally accepts 2 arguments for `name` and `value`.
      * 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>
+     * - `name` and `value` as 2 arguments
+     * - one single object argument with multiple key - value pairs
      *
      * 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>
+     *
+     *     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'
+     *     });
+     *
      * @param {Function} setter
      * @returns {Function} flexSetter
      */
@@ -68,33 +79,40 @@ setValue({
         };
     },
 
-   /**
-     * Create a new function from the provided <code>fn</code>, change <code>this</code> to the provided scope, optionally
+    /**
+     * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
      * overrides arguments for the call. (Defaults to the arguments passed by the caller)
      *
+     * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
+     *
      * @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 {Object} scope (optional) The scope (`this` reference) in which the function is executed.
+     * **If omitted, defaults to the browser window.**
      * @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) {
+        if (arguments.length === 2) {
+            return function() {
+                return fn.apply(scope, arguments);
+            }
+        }
+
         var method = fn,
-            applyArgs;
+            slice = Array.prototype.slice;
 
         return function() {
             var callArgs = args || arguments;
 
             if (appendArgs === true) {
-                callArgs = Array.prototype.slice.call(arguments, 0);
+                callArgs = 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
+            else if (typeof appendArgs == 'number') {
+                callArgs = slice.call(arguments, 0); // copy arguments first
+                Ext.Array.insert(callArgs, appendArgs, args);
             }
 
             return method.apply(scope || window, callArgs);
@@ -102,23 +120,26 @@ setValue({
     },
 
     /**
-     * Create a new function from the provided <code>fn</code>, the arguments of which are pre-set to `args`.
+     * Create a new function from the provided `fn`, 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'
-
+     *     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'
+     *
+     * {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
+     *
      * @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.
+     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
      * @return {Function} The new callback function
      */
     pass: function(fn, args, scope) {
@@ -132,8 +153,8 @@ setValue({
     },
 
     /**
-     * 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.
+     * Create an alias to the provided method property with name `methodName` of `object`.
+     * Note that the execution scope will still be bound to the provided `object` itself.
      *
      * @param {Object/Function} object
      * @param {String} methodName
@@ -149,27 +170,27 @@ setValue({
      * 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>
+     *
+     *     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"
+     *
      * @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).
+     * @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.**
+     * @param {Object} 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) {
@@ -189,16 +210,17 @@ sayHiToFriend('Brian'); // alerts "Hi, Brian"
     },
 
     /**
-    * 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.
-    */
+     * 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 (`this` 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);
@@ -213,27 +235,30 @@ sayHiToFriend('Brian'); // alerts "Hi, Brian"
 
     /**
      * 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>
+     *
+     *     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);
+     *
+     * {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
+     *
      * @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 {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 (`this` reference) in which the function is executed.
+     * **If omitted, defaults to the browser window.**
      * @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
@@ -253,23 +278,21 @@ Ext.Function.defer(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>
+     *     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
      *
      * @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.
+     * @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
      */
@@ -287,15 +310,15 @@ sayGoodbye('Fred'); // both alerts show
     },
 
     /**
-     * <p>Creates a delegate function, optionally with a bound scope which, when called, buffers
+     * 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>
+     * timeout period will begin again.
      *
      * @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
+     * @param {Object} scope (optional) The scope (`this` 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.
@@ -307,7 +330,7 @@ sayGoodbye('Fred'); // both alerts show
             return function() {
                 var me = this;
                 if (timerId) {
-                    clearInterval(timerId);
+                    clearTimeout(timerId);
                     timerId = null;
                 }
                 timerId = setTimeout(function(){
@@ -318,16 +341,16 @@ sayGoodbye('Fred'); // both alerts show
     },
 
     /**
-     * <p>Creates a throttled version of the passed function which, when called repeatedly and
+     * 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>
+     * previous invocation.
      *
-     * <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>
+     * 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.
      *
-     * @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
+     * @param {Function} fn The function to execute at a regular time interval.
+     * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
+     * @param {Object} scope (optional) The scope (`this` 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.
      */
@@ -348,26 +371,101 @@ sayGoodbye('Fred'); // both alerts show
                 timer = setTimeout(execute, interval - elapsed);
             }
         };
+    },
+
+    /**
+     * Adds behavior to an existing method that is executed before the
+     * original behavior of the function.  For example:
+     * 
+     *     var soup = {
+     *         contents: [],
+     *         add: function(ingredient) {
+     *             this.contents.push(ingredient);
+     *         }
+     *     };
+     *     Ext.Function.interceptBefore(soup, "add", function(ingredient){
+     *         if (!this.contents.length && ingredient !== "water") {
+     *             // Always add water to start with
+     *             this.contents.push("water");
+     *         }
+     *     });
+     *     soup.add("onions");
+     *     soup.add("salt");
+     *     soup.contents; // will contain: water, onions, salt
+     * 
+     * @param {Object} object The target object
+     * @param {String} methodName Name of the method to override
+     * @param {Function} fn Function with the new behavior.  It will
+     * be called with the same arguments as the original method.  The
+     * return value of this function will be the return value of the
+     * new method.
+     * @return {Function} The new function just created.
+     */
+    interceptBefore: function(object, methodName, fn) {
+        var method = object[methodName] || Ext.emptyFn;
+
+        return object[methodName] = function() {
+            var ret = fn.apply(this, arguments);
+            method.apply(this, arguments);
+
+            return ret;
+        };
+    },
+
+    /**
+     * Adds behavior to an existing method that is executed after the
+     * original behavior of the function.  For example:
+     * 
+     *     var soup = {
+     *         contents: [],
+     *         add: function(ingredient) {
+     *             this.contents.push(ingredient);
+     *         }
+     *     };
+     *     Ext.Function.interceptAfter(soup, "add", function(ingredient){
+     *         // Always add a bit of extra salt
+     *         this.contents.push("salt");
+     *     });
+     *     soup.add("water");
+     *     soup.add("onions");
+     *     soup.contents; // will contain: water, salt, onions, salt
+     * 
+     * @param {Object} object The target object
+     * @param {String} methodName Name of the method to override
+     * @param {Function} fn Function with the new behavior.  It will
+     * be called with the same arguments as the original method.  The
+     * return value of this function will be the return value of the
+     * new method.
+     * @return {Function} The new function just created.
+     */
+    interceptAfter: function(object, methodName, fn) {
+        var method = object[methodName] || Ext.emptyFn;
+
+        return object[methodName] = function() {
+            method.apply(this, arguments);
+            return fn.apply(this, arguments);
+        };
     }
 };
 
 /**
- * Shorthand for {@link Ext.Function#defer}
+ * @method
  * @member Ext
- * @method defer
+ * @alias Ext.Function#defer
  */
 Ext.defer = Ext.Function.alias(Ext.Function, 'defer');
 
 /**
- * Shorthand for {@link Ext.Function#pass}
+ * @method
  * @member Ext
- * @method pass
+ * @alias Ext.Function#pass
  */
 Ext.pass = Ext.Function.alias(Ext.Function, 'pass');
 
 /**
- * Shorthand for {@link Ext.Function#bind}
+ * @method
  * @member Ext
- * @method bind
+ * @alias Ext.Function#bind
  */
 Ext.bind = Ext.Function.alias(Ext.Function, 'bind');
+