
/// utility

String.prototype.ltrim = function() { return this.replace(/^\s+/, ''); };

String.prototype.rtrim = function() { return this.replace(/\s+$/, ''); };

String.prototype.trim  = function() { return this.ltrim().rtrim(); };

String.prototype.normalizeSpaces = function(tospace) {
 return this.replace(new RegExp('(\\s)\\s*','g'),tospace?' ':'$1').trim();
};

String.prototype.tidy = function() {
 return this.trim().normalizeSpaces(true);
}

String.prototype.escapeHTML = function(simple) {
 var s = this.replace(/&/g,'&amp;').replace(new RegExp('<','g'),'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
 return simple ? s : s.replace(/[^\x20-\x7e\s]/g,
  function(c) { var d = c.charCodeAt(0); return '&#'+(d<10?'000':d<100?'00':d<1000?'0':'')+d+';'; }
 );
}

String.prototype.unescapeHTML = function(simple) {
 s = simple ? this : this.replace(/&#(\d+?);/g, function(x,d) { return String.fromCharCode(d) });
 return s.replace(/&quot;/g,'"').replace(/&gt;/g,'>').replace(/&lt;/g,'<').replace(/&amp;/g,'&');
};

/// crypto

String.prototype.padToBlockLength = function(n) {
 var p = (Math.ceil(this.length / n) * n - this.length) || n;
 return this + String.fromCharCode(p).x(p);
}
String.prototype.removePadding = function() {
 return this.slice(0, -(this.charCodeAt(this.length - 1)));
}

String.prototype.x = function(n) { for (var i = 0, s = ''; i < n; i++) { s += this; } return s; }

function HMACs(h, k, m) { // 8bit str (pad: '\\', '6')
 if (k.length > 64) k = h(k);
 return h('' + sxor(k, '\x5c'.x(64), 1) + h('' + sxor(k, '\x36'.x(64), 1) + m));
}

function HMAC(h, k, m) { return s2h(HMACs(h, k, m)); }

/*
 * Arcfour Encryption, also using MD5/SHA-1 - see Schneier AC2 pp397-8,436-45
 */

var  RC4DISCARDS = 1024; // 256 minimum recommended

function RC4(k, m) { // args and return value are 8bit strings
 k = k.x(Math.ceil(256 / k.length));
 for (var i = 0, n = k.length, S = [], K = []; i < 256; i++) {
  S[i] = i; K[i] = k.charCodeAt(i) & 255;
 }
 for (var i = j = 0; i < 256; i++) { // initialize S(ubstitution)-box with key
  j = (j + S[i] + K[i]) & 255; var x = S[i]; S[i] = S[j]; S[j] = x;
 }
 var i, j;
 for (var l = 0; l < RC4DISCARDS; l++) { // discard 1st bytes of keystream
  i = (i + 1) & 255; j = (j + S[i]) & 255; var x = S[i]; S[i] = S[j]; S[j] = x;
 }
 for (var l = 0, n = m.length, a = []; l < n; l++) { // en/decrypt msg...
  i = (i + 1) & 255; j = (j + S[i]) & 255; var x = S[i]; S[i] = S[j]; S[j] = x;
  a[l] = (m.charCodeAt(l) ^ S[S[i] + S[j]]) & 255;
 }
 return String.fromCharCode.apply(null, a);
}

function RC4encrypt(k, p, h) { // [API] h is MD5s or SHA1s
 var kh = h(k), ke = h('' + k + kh), ki = h('' + k + ke), bl = kh.length;
 p = p.padToBlockLength(bl); // only to disguise p.length
 var c = iv = h('' + ki + p + '|' + Math.random() + '|' + (new Date()).getTime());
 c += RC4(HMACs(h, ke, iv), p);
 return stob64('' + c + HMACs(h, kh, c));
}

function RC4decrypt(k, c64, h) { // [API] optional hfn is MD5s or SHA1s
 var kh = SHA1s(k), kd = SHA1s('' + k + kh);
 var c = b64tos(c64), bl = kh.length, hmac = c.slice(-bl), c = c.slice(0,-bl);
 if (HMACs(h, kh, c) != hmac) { alert(99);return null; }// wrong key assumed
 var iv = c.slice(0, bl), c = c.slice(bl);
 var p = RC4(HMACs(h, kd, iv), c);
 return p.removePadding();
}

/*
 * FIPS PUB 180-1: Secure Hash Standard, SHA-1 - see Schneier AC2 pp442-5
 */

function SHA1 (s) { return s2h(SHA1s(s)); } // [API] standard 40 char hexstring

function SHA1s(s) { return bi2s(SHA1Transforms(s2bi(SHA1pad(s)))); } // 8bit str

function SHA1test(warn) { // best called at startup
 if (SHA1('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq') ==
     '84983e441c3bd26ebaae4aa1f95129e5e54670f1') return true; // more or less
 if (warn)
  alert('SHA1 ERROR:\nScripts on this page might not run properly in this\n' +
        'web browser. Maybe you should try a newer version?.');
 return false;
}

function SHA1Transforms(m) {
 var h = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];
 for (var i = 0, n = m.length, x=0xffffffff, w = []; i < n; i += 16) {
  var a = h[0], b = h[1], c = h[2], d = h[3], e = h[4];
  for (var t = 0; t < 80; t++) {
   w[t] = t < 16 ? m[i+t] : rotl(w[t-3]^w[t-8]^w[t-14]^w[t-16], 1);
   var temp = rotl(a, 5) + e + w[t] +
   (t<20 ? ((b&c)|(~b&d))      + 0x5a827999 : t<40 ? (b^c^d) + 0x6ed9eba1 :
    t<60 ? ((b&c)|(b&d)|(c&d)) + 0x8f1bbcdc :        (b^c^d) + 0xca62c1d6);
   e = d; d = c; c = rotl(b, 30); b = a; a = temp;
  }
  h[0] = (h[0] + a) & x; h[1] = (h[1] + b) & x; h[2] = (h[2] + c) & x;
  h[3] = (h[3] + d) & x; h[4] = (h[4] + e) & x;
 }
 return h; // ('& 0xffffffff' above to prevent int overflow)
}

function SHA1pad(s) { // pad and append string length in bits, max 2**32-1
 var n = s.length;
 return s + bi2s([0x80000000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]).substring(0,
  64-((n+8)&63)) + bi2s([0,n<<3]);
} // note md5 is the same, but with little-endian ints...

// Pick-and-Mix Conversions and Common Functions...

function stob64(s) { // [binary mode: no lf->crlf's - see rfc2045]
 if (typeof s != 'string') return '';
 var B = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
 for (var i = c = 0, n = s.length, b = ''; ; ) {
  var x = s.charCodeAt(i++), y = s.charCodeAt(i++), z = s.charCodeAt(i++);
  if (i > n) break;
  b += B.charAt(63&(x>>2)) + B.charAt(63&((x<<4)+(y>>4)))+
       B.charAt(63&((y<<2)+(z>>6))) + B.charAt(63&z);
  if (++c > 18) { b += '\n'; c = 0; }
 }
 if      (i - n == 1)
  b += B.charAt(63&(x>>2)) + B.charAt(63&((x<<4)+(y>>4))) + B.charAt(63&(y<<2))+'=';
 else if (i - n == 2)
  b += B.charAt(63&(x>>2)) + B.charAt(63&(x<<4))+'==';
 return b;
} // used by all en/decrypt functions

function b64tos(b) {
 if (typeof b != 'string') return '';
 var B = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
 for (var i = 0, n = b.length, s = '', a = []; i < n; ) {
  for (var j = 0; j < 4 && i < n; i++) {
   var c = B.indexOf(b.charAt(i));
   if (c != -1) a[j++] = c;
  }
  if (j < 4) break;
  s += String.fromCharCode(
   255&((a[0]<<2)+(a[1]>>4)), 255&((a[1]<<4)+(a[2]>>2)), 255&(a[2]<<6)+(a[3]));
 }
 if (j % 4 > 1) s += String.fromCharCode(255&((a[0]<<2)+(a[1]>>4)));
 if (j % 4 > 2) s += String.fromCharCode(255&((a[1]<<4)+(a[2]>>2)));
 return s;
} // used by all en/decrypt functions

function sxor(s,t,pad) { // xor strings s^t optionally zero pad to longer
 var n = pad ? Math.max(s.length, t.length) : Math.min(s.length, t.length);
 for (var i = 0, a = []; i < n; i++) // NaN coercion optional!
  a[i] = ((s.charCodeAt(i)||0) ^ (t.charCodeAt(i)||0)) & 255;
 return String.fromCharCode.apply(null, a);
} // used by all en/decrypt functions

function rotl(x,n) { return (x<<n)|(x>>>(32-n)); } // used by MD5/SHA-1

function s2h(s) { // 8bit string to hexstring
 for (var i = 0, n = s.length, x = '0123456789abcdef', h = ''; i < n; i++) {
  var c = s.charCodeAt(i); h += x.charAt((c & 255) >> 4) + x.charAt(c & 15);
 }
 return h;
} // used by MD5/SHA-1

function bi2s(ia) { // array of big-endian 32bit ints to 8bit string
 for(var i = 0, j = 0, n = ia.length, m = 255, a = []; i < n; i++)
  a[j++] = (ia[i]>>>24)&m, a[j++] = (ia[i]>>>16)&m,
  a[j++] = (ia[i]>>>8)&m, a[j++] = ia[i]&m;
 return String.fromCharCode.apply(null, a);
} // used by SHA-1

function s2bi(s) { // 8bit string to big-endian 32bit int array
 for (var i = 0, n = s.length, a = []; i < n; i++)
  a[i >>> 2] = (a[i >>> 2] || 0) | (s.charCodeAt(i) & 255) << ((3 - (i & 3)) << 3);
 return a;
} // used by SHA-1

/// prng

var _rand, _rand_count;

function rand_string(size, min, max) {
 for (var a = [], i = 0; i < size; ) {
  var c = _rand.charCodeAt(_rand_count);
  if (++_rand_count >= _rand.length) srand();
  if (c < min || c > max) continue;
  a[i++] = c;
 }
 return String.fromCharCode.apply(null, a);
}

function srand(e) {
 var seed = document.cookie + ':' + Math.random() + ':' + (new Date()).getTime() + ':' + _rand;
 if (e) seed += ':' + e.clientX + ':' + e.clientY;
 _rand_count = 0;
 _rand = SHA1s(seed);
}
document.addEventListener ? document.addEventListener('click', srand, false) : document.attachEvent ? document.attachEvent('onclick', srand) : null;
window.addEventListener ? window.addEventListener('load', srand, false) : window.attachEvent ? window.attachEvent('onload', srand) : null;

///