# Yahoo! UI Library

### YAHOO Global 2.7.0

Yahoo! UI Library > yahoo > Lang.js (source view)
Search:
Filters Show Private Show Protected Show Deprecated

    /**
     * Provides the language utilites and extensions used by the library
     * @class YAHOO.lang
     */
    YAHOO.lang = YAHOO.lang || {};

    (function() {

    var L = YAHOO.lang,

        ARRAY_TOSTRING = '[object Array]',
        FUNCTION_TOSTRING = '[object Function]',
        OP = Object.prototype,

        // ADD = ["toString", "valueOf", "hasOwnProperty"],
        ADD = ["toString", "valueOf"],

        OB = {

        /**
         * Determines wheather or not the provided object is an array.
         * @method isArray
         * @param {any} o The object being testing
         * @return {boolean} the result
         */
        isArray: function(o) {
            return OP.toString.apply(o) === ARRAY_TOSTRING;
        },

        /**
         * Determines whether or not the provided object is a boolean
         * @method isBoolean
         * @param {any} o The object being testing
         * @return {boolean} the result
         */
        isBoolean: function(o) {
            return typeof o === 'boolean';
        },

        /**
         * Determines whether or not the provided object is a function.
         * Note: Internet Explorer thinks certain functions are objects:
         *
         * var obj = document.createElement("object");
         * YAHOO.lang.isFunction(obj.getAttribute) // reports false in IE
         *
         * var input = document.createElement("input"); // append to body
         * YAHOO.lang.isFunction(input.focus) // reports false in IE
         *
         * You will have to implement additional tests if these functions
         * matter to you.
         *
         * @method isFunction
         * @param {any} o The object being testing
         * @return {boolean} the result
         */
        isFunction: function(o) {
            return OP.toString.apply(o) === FUNCTION_TOSTRING;
        },

        /**
         * Determines whether or not the provided object is null
         * @method isNull
         * @param {any} o The object being testing
         * @return {boolean} the result
         */
        isNull: function(o) {
            return o === null;
        },

        /**
         * Determines whether or not the provided object is a legal number
         * @method isNumber
         * @param {any} o The object being testing
         * @return {boolean} the result
         */
        isNumber: function(o) {
            return typeof o === 'number' && isFinite(o);
        },

        /**
         * Determines whether or not the provided object is of type object
         * or function
         * @method isObject
         * @param {any} o The object being testing
         * @return {boolean} the result
         */
        isObject: function(o) {
    return (o && (typeof o === 'object' || L.isFunction(o))) || false;
        },

        /**
         * Determines whether or not the provided object is a string
         * @method isString
         * @param {any} o The object being testing
         * @return {boolean} the result
         */
        isString: function(o) {
            return typeof o === 'string';
        },

        /**
         * Determines whether or not the provided object is undefined
         * @method isUndefined
         * @param {any} o The object being testing
         * @return {boolean} the result
         */
        isUndefined: function(o) {
            return typeof o === 'undefined';
        },

        /**
         * IE will not enumerate native functions in a derived object even if the
         * function was overridden.  This is a workaround for specific functions
         * we care about on the Object prototype.
         * @property _IEEnumFix
         * @param {Function} r  the object to receive the augmentation
         * @param {Function} s  the object that supplies the properties to augment
         * @static
         * @private
         */
        _IEEnumFix: (YAHOO.env.ua.ie) ? function(r, s) {
                var i, fname, f;
                for (i=0;i<ADD.length;i=i+1) {

                    fname = ADD[i];
                    f = s[fname];

                    if (L.isFunction(f) && f!=OP[fname]) {
                        r[fname]=f;
                    }
                }
        } : function(){},

        /**
         * Utility to set up the prototype, constructor and superclass properties to
         * support an inheritance strategy that can chain constructors and methods.
         * Static members will not be inherited.
         *
         * @method extend
         * @static
         * @param {Function} subc   the object to modify
         * @param {Function} superc the object to inherit
         * @param {Object} overrides  additional properties/methods to add to the
         *                              subclass prototype.  These will override the
         *                              matching items obtained from the superclass
         *                              if present.
         */
        extend: function(subc, superc, overrides) {
            if (!superc||!subc) {
                throw new Error("extend failed, please check that " +
                                "all dependencies are included.");
            }
            var F = function() {}, i;
            F.prototype=superc.prototype;
            subc.prototype=new F();
            subc.prototype.constructor=subc;
            subc.superclass=superc.prototype;
            if (superc.prototype.constructor == OP.constructor) {
                superc.prototype.constructor=superc;
            }

            if (overrides) {
                for (i in overrides) {
                    if (L.hasOwnProperty(overrides, i)) {
                        subc.prototype[i]=overrides[i];
                    }
                }

                L._IEEnumFix(subc.prototype, overrides);
            }
        },

        /**
         * Applies all properties in the supplier to the receiver if the
         * receiver does not have these properties yet.  Optionally, one or
         * more methods/properties can be specified (as additional
         * parameters).  This option will overwrite the property if receiver
         * has it already.  If true is passed as the third parameter, all
         * properties will be applied and _will_ overwrite properties in
         * the receiver.
         *
         * @method augmentObject
         * @static
         * @since 2.3.0
         * @param {Function} r  the object to receive the augmentation
         * @param {Function} s  the object that supplies the properties to augment
         * @param {String*|boolean}  arguments zero or more properties methods
         *        to augment the receiver with.  If none specified, everything
         *        in the supplier will be used unless it would
         *        overwrite an existing property in the receiver. If true
         *        is specified as the third parameter, all properties will
         *        be applied and will overwrite an existing property in
         *        the receiver
         */
        augmentObject: function(r, s) {
            if (!s||!r) {
                throw new Error("Absorb failed, verify dependencies.");
            }
            var a=arguments, i, p, overrideList=a[2];
            if (overrideList && overrideList!==true) { // only absorb the specified properties
                for (i=2; i<a.length; i=i+1) {
                    r[a[i]] = s[a[i]];
                }
            } else { // take everything, overwriting only if the third parameter is true
                for (p in s) {
                    if (overrideList || !(p in r)) {
                        r[p] = s[p];
                    }
                }

                L._IEEnumFix(r, s);
            }
        },

        /**
         * Same as YAHOO.lang.augmentObject, except it only applies prototype properties
         * @see YAHOO.lang.augmentObject
         * @method augmentProto
         * @static
         * @param {Function} r  the object to receive the augmentation
         * @param {Function} s  the object that supplies the properties to augment
         * @param {String*|boolean}  arguments zero or more properties methods
         *        to augment the receiver with.  If none specified, everything
         *        in the supplier will be used unless it would overwrite an existing
         *        property in the receiver.  if true is specified as the third
         *        parameter, all properties will be applied and will overwrite an
         *        existing property in the receiver
         */
        augmentProto: function(r, s) {
            if (!s||!r) {
                throw new Error("Augment failed, verify dependencies.");
            }
            //var a=[].concat(arguments);
            var a=[r.prototype,s.prototype], i;
            for (i=2;i<arguments.length;i=i+1) {
                a.push(arguments[i]);
            }
            L.augmentObject.apply(this, a);
        },

        /**
         * Returns a simple string representation of the object or array.
         * Other types of objects will be returned unprocessed.  Arrays
         * are expected to be indexed.  Use object notation for
         * associative arrays.
         * @method dump
         * @since 2.3.0
         * @param o {Object} The object to dump
         * @param d {int} How deep to recurse child objects, default 3
         * @return {String} the dump result
         */
        dump: function(o, d) {
            var i,len,s=[],OBJ="{...}",FUN="f(){...}",
                COMMA=', ', ARROW=' => ';

            // Cast non-objects to string
            // Skip dates because the std toString is what we want
            // Skip HTMLElement-like objects because trying to dump
            // an element will cause an unhandled exception in FF 2.x
            if (!L.isObject(o)) {
                return o + "";
            } else if (o instanceof Date || ("nodeType" in o && "tagName" in o)) {
                return o;
            } else if  (L.isFunction(o)) {
                return FUN;
            }

            // dig into child objects the depth specifed. Default 3
            d = (L.isNumber(d)) ? d : 3;

            // arrays [1, 2, 3]
            if (L.isArray(o)) {
                s.push("[");
                for (i=0,len=o.length;i<len;i=i+1) {
                    if (L.isObject(o[i])) {
                        s.push((d > 0) ? L.dump(o[i], d-1) : OBJ);
                    } else {
                        s.push(o[i]);
                    }
                    s.push(COMMA);
                }
                if (s.length > 1) {
                    s.pop();
                }
                s.push("]");
            // objects {k1 => v1, k2 => v2}
            } else {
                s.push("{");
                for (i in o) {
                    if (L.hasOwnProperty(o, i)) {
                        s.push(i + ARROW);
                        if (L.isObject(o[i])) {
                            s.push((d > 0) ? L.dump(o[i], d-1) : OBJ);
                        } else {
                            s.push(o[i]);
                        }
                        s.push(COMMA);
                    }
                }
                if (s.length > 1) {
                    s.pop();
                }
                s.push("}");
            }

            return s.join("");
        },

        /**
         * Does variable substitution on a string. It scans through the string
         * looking for expressions enclosed in { } braces. If an expression
         * is found, it is used a key on the object.  If there is a space in
         * the key, the first word is used for the key and the rest is provided
         * to an optional function to be used to programatically determine the
         * value (the extra information might be used for this decision). If
         * the value for the key in the object, or what is returned from the
         * function has a string value, number value, or object value, it is
         * substituted for the bracket expression and it repeats.  If this
         * value is an object, it uses the Object's toString() if this has
         * been overridden, otherwise it does a shallow dump of the key/value
         * pairs.
         * @method substitute
         * @since 2.3.0
         * @param s {String} The string that will be modified.
         * @param o {Object} An object containing the replacement values
         * @param f {Function} An optional function that can be used to
         *                     process each match.  It receives the key,
         *                     value, and any extra metadata included with
         *                     the key inside of the braces.
         * @return {String} the substituted string
         */
        substitute: function (s, o, f) {
            var i, j, k, key, v, meta, saved=[], token,
                DUMP='dump', SPACE=' ', LBRACE='{', RBRACE='}',
                dump;

            for (;;) {
                i = s.lastIndexOf(LBRACE);
                if (i < 0) {
                    break;
                }
                j = s.indexOf(RBRACE, i);
                if (i + 1 >= j) {
                    break;
                }

                //Extract key and meta info
                token = s.substring(i + 1, j);
                key = token;
                meta = null;
                k = key.indexOf(SPACE);
                if (k > -1) {
                    meta = key.substring(k + 1);
                    key = key.substring(0, k);
                }

                // lookup the value
                v = o[key];

                // if a substitution function was provided, execute it
                if (f) {
                    v = f(key, v, meta);
                }

                if (L.isObject(v)) {
                    if (L.isArray(v)) {
                        v = L.dump(v, parseInt(meta, 10));
                    } else {
                        meta = meta || "";

                        // look for the keyword 'dump', if found force obj dump
                        dump = meta.indexOf(DUMP);
                        if (dump > -1) {
                            meta = meta.substring(4);
                        }

                        // use the toString if it is not the Object toString
                        // and the 'dump' meta info was not found
                        if (v.toString===OP.toString || dump>-1) {
                            v = L.dump(v, parseInt(meta, 10));
                        } else {
                            v = v.toString();
                        }
                    }
                } else if (!L.isString(v) && !L.isNumber(v)) {
                    // This {block} has no replace string. Save it for later.
                    v = "~-" + saved.length + "-~";
                    saved[saved.length] = token;

                    // break;
                }

                s = s.substring(0, i) + v + s.substring(j + 1);

            }

            // restore saved {block}s
            for (i=saved.length-1; i>=0; i=i-1) {
                s = s.replace(new RegExp("~-" + i + "-~"), "{"  + saved[i] + "}", "g");
            }

            return s;
        },

        /**
         * Returns a string without any leading or trailing whitespace.  If
         * the input is not a string, the input will be returned untouched.
         * @method trim
         * @since 2.3.0
         * @param s {string} the string to trim
         * @return {string} the trimmed string
         */
        trim: function(s){
            try {
                return s.replace(/^\s+|\s+$/g, "");
            } catch(e) {
                return s;
            }
        },

        /**
         * Returns a new object containing all of the properties of
         * all the supplied objects.  The properties from later objects
         * will overwrite those in earlier objects.
         * @method merge
         * @since 2.3.0
         * @param arguments {Object*} the objects to merge
         * @return the new merged object
         */
        merge: function() {
            var o={}, a=arguments, l=a.length, i;
            for (i=0; i<l; i=i+1) {
                L.augmentObject(o, a[i], true);
            }
            return o;
        },

        /**
         * Executes the supplied function in the context of the supplied
         * object 'when' milliseconds later.  Executes the function a
         * single time unless periodic is set to true.
         * @method later
         * @since 2.4.0
         * @param when {int} the number of milliseconds to wait until the fn
         * is executed
         * @param o the context object
         * @param fn {Function|String} the function to execute or the name of
         * the method in the 'o' object to execute
         * @param data [Array] data that is provided to the function.  This accepts
         * either a single item or an array.  If an array is provided, the
         * function is executed with one parameter for each array item.  If
         * you need to pass a single array parameter, it needs to be wrapped in
         * an array [myarray]
         * @param periodic {boolean} if true, executes continuously at supplied
         * interval until canceled
         * @return a timer object. Call the cancel() method on this object to
         * stop the timer.
         */
        later: function(when, o, fn, data, periodic) {
            when = when || 0;
            o = o || {};
            var m=fn, d=data, f, r;

            if (L.isString(fn)) {
                m = o[fn];
            }

            if (!m) {
                throw new TypeError("method undefined");
            }

            if (!L.isArray(d)) {
                d = [data];
            }

            f = function() {
                m.apply(o, d);
            };

            r = (periodic) ? setInterval(f, when) : setTimeout(f, when);

            return {
                interval: periodic,
                cancel: function() {
                    if (this.interval) {
                        clearInterval(r);
                    } else {
                        clearTimeout(r);
                    }
                }
            };
        },

        /**
         * A convenience method for detecting a legitimate non-null value.
         * Returns false for null/undefined/NaN, true for other values,
         * including 0/false/''
         * @method isValue
         * @since 2.3.0
         * @param o {any} the item to test
         * @return {boolean} true if it is not null/undefined/NaN || false
         */
        isValue: function(o) {
            // return (o || o === false || o === 0 || o === ''); // Infinity fails
    return (L.isObject(o) || L.isString(o) || L.isNumber(o) || L.isBoolean(o));
        }

    };

    /**
     * Determines whether or not the property was added
     * to the object instance.  Returns false if the property is not present
     * in the object, or was inherited from the prototype.
     * This abstraction is provided to enable hasOwnProperty for Safari 1.3.x.
     * There is a discrepancy between YAHOO.lang.hasOwnProperty and
     * Object.prototype.hasOwnProperty when the property is a primitive added to
     * both the instance AND prototype with the same value:
     * <pre>
     * var A = function() {};
     * A.prototype.foo = 'foo';
     * var a = new A();
     * a.foo = 'foo';
     * alert(a.hasOwnProperty('foo')); // true
     * alert(YAHOO.lang.hasOwnProperty(a, 'foo')); // false when using fallback
     * </pre>
     * @method hasOwnProperty
     * @param {any} o The object being testing
     * @param prop {string} the name of the property to test
     * @return {boolean} the result
     */
    L.hasOwnProperty = (OP.hasOwnProperty) ?
        function(o, prop) {
            return o && o.hasOwnProperty(prop);
        } : function(o, prop) {
            return !L.isUndefined(o[prop]) &&
                    o.constructor.prototype[prop] !== o[prop];
        };

    // new lang wins
    OB.augmentObject(L, OB, true);

    /*
     * An alias for <a href="YAHOO.lang.html">YAHOO.lang</a>
     * @class YAHOO.util.Lang
     */
    YAHOO.util.Lang = L;

    /**
     * Same as YAHOO.lang.augmentObject, except it only applies prototype
     * properties.  This is an alias for augmentProto.
     * @see YAHOO.lang.augmentObject
     * @method augment
     * @static
     * @param {Function} r  the object to receive the augmentation
     * @param {Function} s  the object that supplies the properties to augment
     * @param {String*|boolean}  arguments zero or more properties methods to
     *        augment the receiver with.  If none specified, everything
     *        in the supplier will be used unless it would
     *        overwrite an existing property in the receiver.  if true
     *        is specified as the third parameter, all properties will
     *        be applied and will overwrite an existing property in
     *        the receiver
     */
    L.augment = L.augmentProto;

    /**
     * An alias for <a href="YAHOO.lang.html#augment">YAHOO.lang.augment</a>
     * @for YAHOO
     * @method augment
     * @static
     * @param {Function} r  the object to receive the augmentation
     * @param {Function} s  the object that supplies the properties to augment
     * @param {String*}  arguments zero or more properties methods to
     *        augment the receiver with.  If none specified, everything
     *        in the supplier will be used unless it would
     *        overwrite an existing property in the receiver
     */
    YAHOO.augment = L.augmentProto;

    /**
     * An alias for <a href="YAHOO.lang.html#extend">YAHOO.lang.extend</a>
     * @method extend
     * @static
     * @param {Function} subc   the object to modify
     * @param {Function} superc the object to inherit
     * @param {Object} overrides  additional properties/methods to add to the
     *        subclass prototype.  These will override the
     *        matching items obtained from the superclass if present.
     */
    YAHOO.extend = L.extend;

    })();

Copyright © 2009 Yahoo! Inc. All rights reserved.