    /**
     * @class Ext.String
     *
     * A collection of useful static methods to deal with strings
     * @singleton
     */

    Ext.String = {
        trimRegex: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
        escapeRe: /('|\\)/g,
        formatRe: /\{(\d+)\}/g,
        escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,

        /**
         * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
         * @param {String} value The string to encode
         * @return {String} The encoded text
         * @method
         */
        htmlEncode: (function() {
            var entities = {
                '&': '&amp;',
                '>': '&gt;',
                '<': '&lt;',
                '"': '&quot;'
            }, keys = [], p, regex;

            for (p in entities) {
                keys.push(p);
            }

            regex = new RegExp('(' + keys.join('|') + ')', 'g');

            return function(value) {
                return (!value) ? value : String(value).replace(regex, function(match, capture) {
                    return entities[capture];
                });
            };
        })(),

        /**
         * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
         * @param {String} value The string to decode
         * @return {String} The decoded text
         * @method
         */
        htmlDecode: (function() {
            var entities = {
                '&amp;': '&',
                '&gt;': '>',
                '&lt;': '<',
                '&quot;': '"'
            }, keys = [], p, regex;

            for (p in entities) {
                keys.push(p);
            }

            regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');

            return function(value) {
                return (!value) ? value : String(value).replace(regex, function(match, capture) {
                    if (capture in entities) {
                        return entities[capture];
                    } else {
                        return String.fromCharCode(parseInt(capture.substr(2), 10));
                    }
                });
            };
        })(),

        /**
         * Appends content to the query string of a URL, handling logic for whether to place
         * a question mark or ampersand.
         * @param {String} url The URL to append to.
         * @param {String} string The content to append to the URL.
         * @return (String) The resulting URL
         */
        urlAppend : function(url, string) {
            if (!Ext.isEmpty(string)) {
                return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
            }

            return url;
        },

        /**
         * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
         * @example
    var s = '  foo bar  ';
    alert('-' + s + '-');         //alerts "- foo bar -"
    alert('-' + Ext.String.trim(s) + '-');  //alerts "-foo bar-"

         * @param {String} string The string to escape
         * @return {String} The trimmed string
         */
        trim: function(string) {
            return string.replace(Ext.String.trimRegex, "");
        },

        /**
         * Capitalize the given string
         * @param {String} string
         * @return {String}
         */
        capitalize: function(string) {
            return string.charAt(0).toUpperCase() + string.substr(1);
        },

        /**
         * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
         * @param {String} value The string to truncate
         * @param {Number} length The maximum length to allow before truncating
         * @param {Boolean} word True to try to find a common word break
         * @return {String} The converted text
         */
        ellipsis: function(value, len, word) {
            if (value && value.length > len) {
                if (word) {
                    var vs = value.substr(0, len - 2),
                    index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
                    if (index !== -1 && index >= (len - 15)) {
                        return vs.substr(0, index) + "...";
                    }
                }
                return value.substr(0, len - 3) + "...";
            }
            return value;
        },

        /**
         * Escapes the passed string for use in a regular expression
         * @param {String} string
         * @return {String}
         */
        escapeRegex: function(string) {
            return string.replace(Ext.String.escapeRegexRe, "\\$1");
        },

        /**
         * Escapes the passed string for ' and \
         * @param {String} string The string to escape
         * @return {String} The escaped string
         */
        escape: function(string) {
            return string.replace(Ext.String.escapeRe, "\\$1");
        },

        /**
         * Utility function that allows you to easily switch a string between two alternating values.  The passed value
         * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
         * they are already different, the first value passed in is returned.  Note that this method returns the new value
         * but does not change the current string.
         * <pre><code>
        // alternate sort directions
        sort = Ext.String.toggle(sort, 'ASC', 'DESC');

        // instead of conditional logic:
        sort = (sort == 'ASC' ? 'DESC' : 'ASC');
           </code></pre>
         * @param {String} string The current string
         * @param {String} value The value to compare to the current string
         * @param {String} other The new value to use if the string already equals the first value passed in
         * @return {String} The new value
         */
        toggle: function(string, value, other) {
            return string === value ? other : value;
        },

        /**
         * Pads the left side of a string with a specified character.  This is especially useful
         * for normalizing number and date strings.  Example usage:
         *
         * <pre><code>
    var s = Ext.String.leftPad('123', 5, '0');
    // s now contains the string: '00123'
           </code></pre>
         * @param {String} string The original string
         * @param {Number} size The total length of the output string
         * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
         * @return {String} The padded string
         */
        leftPad: function(string, size, character) {
            var result = String(string);
            character = character || " ";
            while (result.length < size) {
                result = character + result;
            }
            return result;
        },

        /**
         * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
         * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
         * <pre><code>
    var cls = 'my-class', text = 'Some text';
    var s = Ext.String.format('&lt;div class="{0}">{1}&lt;/div>', cls, text);
    // s now contains the string: '&lt;div class="my-class">Some text&lt;/div>'
           </code></pre>
         * @param {String} string The tokenized string to be formatted
         * @param {String} value1 The value to replace token {0}
         * @param {String} value2 Etc...
         * @return {String} The formatted string
         */
        format: function(format) {
            var args = Ext.Array.toArray(arguments, 1);
            return format.replace(Ext.String.formatRe, function(m, i) {
                return args[i];
            });
        }
    };