text: /* ************************************************************************
  
     qooxdoo - the new era of web development
  
     http://qooxdoo.org
  
     Copyright:
       2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
  
     License:
       MIT: https://opensource.org/licenses/MIT
       See the LICENSE file in the project's top-level directory for details.
  
     Authors:
       * Sebastian Werner (wpbasti)
       * Andreas Ecker (ecker)
  
     ======================================================================
  
     This class contains code based on the following work:
  
     * Mootools
       http://mootools.net/
       Version 1.1.1
  
       Copyright:
         (c) 2007 Valerio Proietti
  
       License:
         MIT: http://www.opensource.org/licenses/mit-license.php
  
     and
  
     * XRegExp
     http://xregexp.com/
     Version 1.5
  
     Copyright:
         (c) 2006-2007, Steven Levithan <http://stevenlevithan.com>
  
       License:
         MIT: http://www.opensource.org/licenses/mit-license.php
  
       Authors:
         * Steven Levithan
  
  ************************************************************************ */
  
  /**
   * String helper functions
   *
   * The native JavaScript String is not modified by this class. However,
   * there are modifications to the native String in {@link qx.lang.normalize.String} for
   * browsers that do not support certain features.
   *
   * @require(qx.lang.normalize.String)
   */
  qx.Bootstrap.define("qx.lang.String",
  {
    statics :
    {
  
      /**
       * Unicode letters.  they are taken from Steve Levithan's excellent XRegExp library [http://xregexp.com/addons/unicode/unicode-base.js]
       */
      __unicodeLetters : "0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",
  
      /**
       * A RegExp that matches the first letter in a word - unicode aware
       */
      __unicodeFirstLetterInWordRegexp : null,
  
      /**
       * @type {Map} Cache for often used string operations [camelCasing and hyphenation]
       * e.g. marginTop => margin-top
       */
      __stringsMap : {},
  
      /**
       * Converts a hyphenated string (separated by '-') to camel case.
       *
       * Example:
       * <pre class='javascript'>qx.lang.String.camelCase("I-like-cookies"); //returns "ILikeCookies"</pre>
       *
       * @param str {String} hyphenated string
       * @return {String} camelcase string
       */
      camelCase : function(str)
      {
        var result = this.__stringsMap[str];
        if (!result) {
          result = str.replace(/\-([a-z])/g, function(match, chr) {
            return chr.toUpperCase();
          });
          if (str.indexOf("-") >= 0) {
            this.__stringsMap[str] = result;
          }
        }
        return result;
      },
  
  
      /**
       * Converts a camelcased string to a hyphenated (separated by '-') string.
       *
       * Example:
       * <pre class='javascript'>qx.lang.String.hyphenate("weLikeCookies"); //returns "we-like-cookies"</pre>
       *
       * @param str {String} camelcased string
       * @return {String} hyphenated string
       */
      hyphenate: function(str)
      {
        var result = this.__stringsMap[str];
        if (!result) {
          result = str.replace(/[A-Z]/g, function(match){
            return  ('-' + match.charAt(0).toLowerCase());
          });
          if (str.indexOf("-") == -1) {
            this.__stringsMap[str] = result;
          }
        }
        return result;
      },
  
  
      /**
       * Converts a string to camel case.
       *
       * Example:
       * <pre class='javascript'>qx.lang.String.camelCase("i like cookies"); //returns "I Like Cookies"</pre>
       *
       * @param str {String} any string
       * @return {String} capitalized string
       */
      capitalize: function(str){
        if(this.__unicodeFirstLetterInWordRegexp === null) {
          var unicodeEscapePrefix = '\\u';
          this.__unicodeFirstLetterInWordRegexp = new RegExp("(^|[^" + this.__unicodeLetters.replace(/[0-9A-F]{4}/g,function(match){return unicodeEscapePrefix+match;}) + "])[" + this.__unicodeLetters.replace(/[0-9A-F]{4}/g,function(match){return unicodeEscapePrefix+match;}) + "]", "g");
        }
        return str.replace(this.__unicodeFirstLetterInWordRegexp, function(match) {
          return match.toUpperCase();
        });
      },
  
  
      /**
       * Removes all extraneous whitespace from a string and trims it
       *
       * Example:
       *
       * <code>
       * qx.lang.String.clean(" i      like     cookies      \n\n");
       * </code>
       *
       * Returns "i like cookies"
       *
       * @param str {String} the string to clean up
       * @return {String} Cleaned up string
       */
      clean: function(str){
        return str.replace(/\s+/g, ' ').trim();
      },
  
  
      /**
       * removes white space from the left side of a string
       *
       * @param str {String} the string to trim
       * @return {String} the trimmed string
       */
      trimLeft : function(str) {
        return str.replace(/^\s+/, "");
      },
  
  
      /**
       * removes white space from the right side of a string
       *
       * @param str {String} the string to trim
       * @return {String} the trimmed string
       */
      trimRight : function(str) {
        return str.replace(/\s+$/, "");
      },
  
  
      /**
       * Check whether the string starts with the given substring
       *
       * @deprecated {6.0} Please use String instance startsWith method instead
       *
       * @param fullstr {String} the string to search in
       * @param substr {String} the substring to look for
       * @return {Boolean} whether the string starts with the given substring
       */
      startsWith : function(fullstr, substr) {
        return fullstr.startsWith(substr);
      },
  
  
      /**
       * Check whether the string ends with the given substring
       *
       * @deprecated {6.0} Please use String instance endsWith method instead
       *
       * @param fullstr {String} the string to search in
       * @param substr {String} the substring to look for
       * @return {Boolean} whether the string ends with the given substring
       */
      endsWith : function(fullstr, substr) {
        return fullstr.endsWith(substr);
      },
  
  
      /**
       * Returns a string, which repeats a string 'length' times
       *
       * @param str {String} string used to repeat
       * @param times {Integer} the number of repetitions
       * @return {String} repeated string
       */
      repeat : function(str, times) {
        return str.length > 0 ? new Array(times + 1).join(str) : "";
      },
  
  
      /**
       * Pad a string up to a given length. Padding characters are added to the left of the string.
       *
       * @param str {String} the string to pad
       * @param length {Integer} the final length of the string
       * @param ch {String} character used to fill up the string
       * @return {String} padded string
       */
      pad : function(str, length, ch)
      {
        var padLength = length - str.length;
        if (padLength > 0)
        {
          if (typeof ch === "undefined") {
            ch = "0";
          }
          return this.repeat(ch, padLength) + str;
        }
        else
        {
          return str;
        }
      },
  
  
      /**
       * Convert the first character of the string to upper case.
       *
       * @signature function(str)
       * @param str {String} the string
       * @return {String} the string with an upper case first character
       */
      firstUp : qx.Bootstrap.firstUp,
  
  
      /**
       * Convert the first character of the string to lower case.
       *
       * @signature function(str)
       * @param str {String} the string
       * @return {String} the string with a lower case first character
       */
      firstLow : qx.Bootstrap.firstLow,
  
  
      /**
       * Check whether the string contains a given substring
       *
       * @param str {String} the string
       * @param substring {String} substring to search for
       * @return {Boolean} whether the string contains the substring
       */
      contains : function(str, substring) {
        return str.indexOf(substring) != -1;
      },
  
  
      /**
       * Print a list of arguments using a format string
       * In the format string occurrences of %n are replaced by the n'th element of the args list.
       * You can give an object as argument.
       * In this case you should specify namedArgument in your string with %{namedArgument}.
       * The named argument will be replace by the value of the property of the object named "namedArgument"
       * Example:
       * <pre class='javascript'>qx.lang.String.format("Hello %1, my name is %2", ["Egon", "Franz"]) == "Hello Egon, my name is Franz"</pre>
       * <pre class='javascript'>qx.lang.String.format("Hello %{yourName}, my name is %{myName}", {yourName: "Egon", myName: "Franz"}) == "Hello Egon, my name is Franz"</pre>
       *
       * @param pattern {String} format string
       * @param args {Array} array of arguments to insert into the format string
       * @return {String} the formatted string
       */
      format : function(pattern, args)
      {
        var str = pattern;
        var regexp = /%(\d+)|%{(\S[^}\s]*)}/g;
        if (!Array.isArray(args)) {
          args = [args];
        }
  
        var argsIsObject = (args.length === 1 && typeof args[0] === "object");
  
        str = str.replace(regexp, function(matchedSubString, numberArgument, namedArgument) {
          if (namedArgument) {
            if (argsIsObject) {
              return args[0][namedArgument];
            }
  
            return namedArgument;
          }
  
          if (numberArgument && numberArgument > 0 && args.length >= numberArgument) {
            var index = numberArgument - 1;
            return args[index];
          }
  
          return matchedSubString;
        });
        return str;
      },
  
  
      /**
       * Escapes all chars that have a special meaning in regular expressions
       *
       * @param str {String} the string where to escape the chars.
       * @return {String} the string with the escaped chars.
       */
      escapeRegexpChars : function(str) {
        return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
      },
  
  
      /**
       * Converts a string to an array of characters.
       * <pre>"hello" => [ "h", "e", "l", "l", "o" ];</pre>
       *
       * @param str {String} the string which should be split
       * @return {Array} the result array of characters
       */
      toArray : function(str) {
        return str.split(/\B|\b/g);
      },
  
  
      /**
       * Remove HTML/XML tags from a string
       * Example:
       * <pre class='javascript'>qx.lang.String.stripTags("&lt;h1>Hello&lt;/h1>") == "Hello"</pre>
       *
       * @param str {String} string containing tags
       * @return {String} the string with stripped tags
       */
      stripTags : function(str) {
        return str.replace(/<\/?[^>]+>/gi, "");
      },
  
  
      /**
       * Strips <script> tags including its content from the given string.
       *
       * @param str {String} string containing tags
       * @param exec {Boolean?false} Whether the filtered code should be executed
       * @return {String} The filtered string
       */
      stripScripts: function(str, exec)
      {
        var scripts = "";
        var text = str.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function()
        {
          scripts += arguments[1] + '\n';
          return "";
        });
  
        if (exec === true) {
          qx.lang.Function.globalEval(scripts);
        }
  
        return text;
      },
  
  
      /**
       * Quotes the given string.
       * @param str {String} String to quote.
       * @return {String} The quoted string.
       */
      quote : function(str) {
        return '"' + str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"") + '"';
      }
    }
  });
  