function getRegExp(content, flags) {
  if (!arguments.callee.res) { arguments.callee.res = {}; }
  var res = arguments.callee.res,
      re = res[content + flags];
  if (!re) {
    re = res[content + flags] = new RegExp(content, flags);
  }
  return re;
};

function getTokenizedRegExp(token, flags) {
  if (!arguments.callee.res) { arguments.callee.res = {}; }
  var res = arguments.callee.res,
      re = res[token + flags];
  if (!re) {
    re = res[token + flags] = new RegExp("(^|\\s)" + token + "(?!-)\\b", flags);
  }
  return re;
};

function hasToken(string, token) {
  return string.search(getTokenizedRegExp(token, "g")) >= 0;
};

function removeToken(string, token) {
  return string.replace(getTokenizedRegExp(token, "g"), '').trim();
};

if (!ENTITIES) {
  var ENTITIES = {
    '&lt;'     : '<',
    '&gt;'     : '>',
    '&amp;'    : '&',
/*    '&apos;' : "'",  */
    '&quot;'   : '"',
    '&Eacute;' : 'É',
    '&eacute;' : 'é',
    '&egrave;' : 'è',
    '&ecirc;'  : 'ê',
    '&Agrave;' : 'À',
    '&agrave;' : 'à',
    '&acirc;'  : 'â',
    '&Oelig;'  : 'Œ',
    '&ocirc;'  : 'ô',
    '&oelig;'  : 'œ',
    '&icirc;'  : 'î',
    '&ucirc;'  : 'û'
  };
}

if (!String.prototype.deentitify) {
  /**
   * Returns a copy of this string with all character entities replaced by their
   * respective character. By default, only the 5 predefined XML character
   * entities are supported: &amp;amp; &amp;lt; &amp;gt; &amp;apos; &amp;quot;.
   *
   * To add more entities for replacement, edit or
   * create the ENTITIES global variable, e.g.
   * <blockquote><pre>
   *   ENTITIES['&amp;hellip;'] = '�';
   * </pre></blockquote>
   * @return a copy of this string with character entities replaced by their
   * actual character
   */
  String.prototype.deentitify = function() {
    var s = this, i = s.length, j, v;
    while (i > 0) {
      i = s.lastIndexOf('&', i - 1);
      if (i < 0) { break; }
      j = s.indexOf(';', i);
     if (i + 1 > j) { break; }
      v = s.substring(i, j + 1);
      if (ENTITIES[v]) {
        s = s.substring(0, i) + ENTITIES[v] + s.substring(j + 1);
      }
    }
    return s.toString();
  };
}

if (!String.prototype.entitify) {
  String.prototype.entitify = function() {
    var s = this;

    // First replace all standalone '&' characters (i.e. not part of an entity
    var i = s.length, j, v, t;
    while (i > 0) {
      i = s.lastIndexOf('&', i - 1);
      if (i < 0) { break; }
      j = s.indexOf(';', i);
      if (j > i + 1) {
        v = s.substring(i, j + 1);
        if (ENTITIES[v]) { continue; }
      }
      s = s.substring(0, i) + '&amp;' + s.substring(i + 1);
    }
    for (var entity in ENTITIES) {
      if (entity != '&amp;') {
        s = s.replace(getRegExp(ENTITIES[entity], 'g'), entity);
      }
    }
    return s;
  };
}

if (!String.prototype.quote) {
  String.prototype.quote = function() {
    var c, i, l = this.length, o = '"';
    for (i = 0; i < l; i += 1) {
      c = this.charAt(i);
      if (c >= ' ') {
        if (c == '\\' || c == '"') { o += '\\'; }
        o += c;
      } else {
        switch (c) {
          case '\b':
            o += '\\b';
            break;
          case '\f':
            o += '\\f';
            break;
          case '\n':
            o += '\\n';
            break;
          case '\r':
            o += '\\r';
            break;
          case '\t':
            o += '\\t';
            break;
          default:
            c = c.charCodeAt();
            o += '\\u00' + Math.floor(c / 16).toString(16) +
                 (c % 16).toString(16);
        }
      }
    }
    return o + '"';
  };
}

if (!String.prototype.trim) {
  String.prototype.trim = function() {
    var r = getRegExp("^\\s*(.*?)\\s*$", "mg");
    return this.replace(r, "$1");
  };
}

if (!String.prototype.camelize) {
  String.prototype.camelize = function(ignoreInitial) {
    var s = this.capitalize({ token: '-', ignoreInitial: ignoreInitial });
    return s.replace(getRegExp('-', 'g'), '');
  };
}

if (!String.prototype.capitalize) {
  String.prototype.capitalize = function(options) {
    if (!options) var options = {};
    var token = options.token || ' ';
    var tokens = this.split(token);
    var n = options.firstWordOnly ? 1 : tokens.length;
    var start = options.ignoreInitial ? 1 : 0;
    for (var i = start; i < n; i++) {
      var s = tokens[i];
      tokens[i] = s.charAt(0).toUpperCase() + s.substring(1);
    }
    return tokens.join(token);
  };
}

if (!String.prototype.underscore) {
  String.prototype.underscore = function() {
    r = new RegExp('-', "g");
    return this.replace(r, '_');
  };
}

// From http://community.hdri.net/blogs/ray_blog/archive/2006/02/27/5.aspx
if (!String.prototype.format) {
  String.prototype.format = function() {
    var str = this,
        n = arguments.length;
    for (var i = 0; i < n; i++) {
      var re = getRegExp('\\{' + (i) + '\\}','gm');
      str = str.replace(re, arguments[i]);
    }
    return str;
  };
}