﻿//-----------------------------------------------------------------------------
// XUtils
//
// Copyright 2005-2010 - Xcential Group LLC.
//
//-----------------------------------------------------------------------------

//=============================================================================
// Constructor

function XUtils(
   create
)
{
   create = (create == null) ? true : false;

   if (create)
      return new XUtils(false);

   //--------------------------------------------------------------------------
   // Private Interface

   //--------------------------------------------------------------------------
   // Privileged Interface

   //--------------------------------------------------------------------------
   // Initialization


}

XUtils.prototype.objectClass = "XUtils";

//=============================================================================
// Static Interface

XUtils.SCHEME_GUID   = "guid";
XUtils.SCHEME_DATE   = "date";
XUtils.SCHEME_RANDOM = "random";
XUtils.SCHEME_NAME   = "name";
XUtils.SCHEME_BASE64 = "base64";

XUtils.SHORT_FORM = true;
XUtils.LOWER_CASE = true;

XUtils.AGE_DAYS  = "days";
XUtils.AGE_HOURS = "hours";
XUtils.AGE_MINS  = "minutes";

//-----------------------------------------------------------------------------

XUtils.dimText = function(
   value
)
{

   return (value == null) ? "" : (typeof(value) == "string") ? value : value + "px";
}

//-----------------------------------------------------------------------------

XUtils.convert = function(
   value,
   castAs
)
{
   value = (value == null) ? null : value;
   castAs = (castAs == null) ? null : castAs;

   if (value == null)
      return null;

   if (castAs == null)
      return value;

   switch (typeof(castAs))
   {
      case "string":
         switch (castAs)
         {
            case "string":
               value = XString(value).toString();
               break;
            case "number":
               value = XNumber(value).toNumber();
               break;
            case "boolean":
               value = XBoolean(value).toBoolean();
               break;
         }
         break;
      case "object":
         value = castAs(value);
         break;
      case "number":
         value = XNumber(value).toNumber();
         break;
      case "boolean":
         value = XBoolean(value).toBoolean();
         break;
   }

   return value;
}

//-----------------------------------------------------------------------------

XUtils.generateId = function(
   prefix,
   scheme,
   name,
   maxLength
)
{
   prefix = (prefix == null) ? null : prefix;
   scheme = (scheme == null) ? XUtils.SCHEME_GUID : scheme.toLowerCase();
   name = (name == null) ? null : name;
   maxLength = (maxLength == null) ? 64 : maxLength;

   //--------------------------------------------------------------------------

   function s4()
   {

      return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
   }

   //--------------------------------------------------------------------------

   // urn:legisweb-com:tracking:3F2504E0-4F89-11D3-9A0C-0305E82C3301  |
   //          1         2         3         4         5         6    |
   // 1234567890123456789012345678901234567890123456789012345678901234|

   switch (scheme)
   {
      case XUtils.SCHEME_GUID:
         prefix = (prefix != null) ? prefix : "urn:xcential-com:guid:";
         var id = (s4()+s4()+"-"+s4()+"-"+s4().replace(/^./,"4")+"-"+s4()+"-"+s4()+s4()+s4()).toUpperCase();
         break;
      case XUtils.SCHEME_RANDOM:
         prefix = (prefix != null) ? prefix : "urn:xcential-com:rand:";
         var id = String(Math.floor(Math.random()*10000000));
         break;
      case XUtils.SCHEME_DATE:
         prefix = (prefix != null) ? prefix : "urn:xcential-com:date:";
         var id = String((new Date()).valueOf()) + "." + String(Math.floor(Math.random()*10000000));
         break;
      case XUtils.SCHEME_NAME:
         prefix = (prefix != null) ? prefix : "urn:xcential-com:name:";
         name = (name == null) ? XUtils.generateId("", XUtils.SCHEME_DATE) : XString(name).normalize();
         name = name.toLowerCase();
         name = name.replace(/\&[^;]*;/g, ""); // Take out any entities
         name = name.replace(/[^a-z0-9\-\s]/g, ""); // Take out non-alphanumeric letters
         name = name.replace(/\s+/g, "-");
         var id = name;
         break;
      case XUtils.SCHEME_BASE64:
         prefix = (prefix != null) ? prefix : "urn:xcential-com:base64:";
         var id = XString(name).encode(XString.ENCODE_BASE64).replace(/[^a-z0-9_]/gi,"");
         break;
      default:
         throw "Unrecognized Id generation scheme: " + scheme;
         break;
   }
   var index = id.length - maxLength + prefix.length;
   id = prefix + id.substr((index > 0) ? index : 0);

   return id;

}

//-----------------------------------------------------------------------------

XUtils.generateMD5 = function(
   text
)
{

   /*
    * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
    * Digest Algorithm, as defined in RFC 1321.
    * Copyright (C) Paul Johnston 1999 - 2000.
    * Updated by Greg Holt 2000 - 2001.
    * See http://pajhome.org.uk/site/legal.html for details.
    */

   /*
    * Convert a 32-bit number to a hex string with ls-byte first
    */
   var hex_chr = "0123456789abcdef";
   function rhex(num)
   {
     var str = "";
     for(var j = 0; j <= 3; j++)
       str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
              hex_chr.charAt((num >> (j * 8)) & 0x0F);
     return str;
   }

   /*
    * Convert a string to a sequence of 16-word blocks, stored as an array.
    * Append padding bits and the length, as described in the MD5 standard.
    */
   function str2blks_MD5(str)
   {
     var nblk = ((str.length + 8) >> 6) + 1;
     var blks = new Array(nblk * 16);
     for(var i = 0; i < nblk * 16; i++) blks[i] = 0;
     for(var i = 0; i < str.length; i++)
       blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);
     blks[i >> 2] |= 0x80 << ((i % 4) * 8);
     blks[nblk * 16 - 2] = str.length * 8;
     return blks;
   }

   /*
    * Add integers, wrapping at 2^32. This uses 16-bit operations internally
    * to work around bugs in some JS interpreters.
    */
   function add(x, y)
   {
     var lsw = (x & 0xFFFF) + (y & 0xFFFF);
     var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
     return (msw << 16) | (lsw & 0xFFFF);
   }

   /*
    * Bitwise rotate a 32-bit number to the left
    */
   function rol(num, cnt)
   {
     return (num << cnt) | (num >>> (32 - cnt));
   }

   /*
    * These functions implement the basic operation for each round of the
    * algorithm.
    */
   function cmn(q, a, b, x, s, t)
   {
     return add(rol(add(add(a, q), add(x, t)), s), b);
   }
   function ff(a, b, c, d, x, s, t)
   {
     return cmn((b & c) | ((~b) & d), a, b, x, s, t);
   }
   function gg(a, b, c, d, x, s, t)
   {
     return cmn((b & d) | (c & (~d)), a, b, x, s, t);
   }
   function hh(a, b, c, d, x, s, t)
   {
     return cmn(b ^ c ^ d, a, b, x, s, t);
   }
   function ii(a, b, c, d, x, s, t)
   {
     return cmn(c ^ (b | (~d)), a, b, x, s, t);
   }

   /*
    * Take a string and return the hex representation of its MD5.
    */
   function calcMD5(str)
   {
     var x = str2blks_MD5(str);
     var a =  1732584193;
     var b = -271733879;
     var c = -1732584194;
     var d =  271733878;

     for(var i = 0; i < x.length; i += 16)
     {
       var olda = a;
       var oldb = b;
       var oldc = c;
       var oldd = d;

       a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
       d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
       c = ff(c, d, a, b, x[i+ 2], 17,  606105819);
       b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
       a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
       d = ff(d, a, b, c, x[i+ 5], 12,  1200080426);
       c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
       b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
       a = ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
       d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
       c = ff(c, d, a, b, x[i+10], 17, -42063);
       b = ff(b, c, d, a, x[i+11], 22, -1990404162);
       a = ff(a, b, c, d, x[i+12], 7 ,  1804603682);
       d = ff(d, a, b, c, x[i+13], 12, -40341101);
       c = ff(c, d, a, b, x[i+14], 17, -1502002290);
       b = ff(b, c, d, a, x[i+15], 22,  1236535329);

       a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
       d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
       c = gg(c, d, a, b, x[i+11], 14,  643717713);
       b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
       a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
       d = gg(d, a, b, c, x[i+10], 9 ,  38016083);
       c = gg(c, d, a, b, x[i+15], 14, -660478335);
       b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
       a = gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
       d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
       c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
       b = gg(b, c, d, a, x[i+ 8], 20,  1163531501);
       a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
       d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
       c = gg(c, d, a, b, x[i+ 7], 14,  1735328473);
       b = gg(b, c, d, a, x[i+12], 20, -1926607734);

       a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
       d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
       c = hh(c, d, a, b, x[i+11], 16,  1839030562);
       b = hh(b, c, d, a, x[i+14], 23, -35309556);
       a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
       d = hh(d, a, b, c, x[i+ 4], 11,  1272893353);
       c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
       b = hh(b, c, d, a, x[i+10], 23, -1094730640);
       a = hh(a, b, c, d, x[i+13], 4 ,  681279174);
       d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
       c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
       b = hh(b, c, d, a, x[i+ 6], 23,  76029189);
       a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
       d = hh(d, a, b, c, x[i+12], 11, -421815835);
       c = hh(c, d, a, b, x[i+15], 16,  530742520);
       b = hh(b, c, d, a, x[i+ 2], 23, -995338651);

       a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
       d = ii(d, a, b, c, x[i+ 7], 10,  1126891415);
       c = ii(c, d, a, b, x[i+14], 15, -1416354905);
       b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
       a = ii(a, b, c, d, x[i+12], 6 ,  1700485571);
       d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
       c = ii(c, d, a, b, x[i+10], 15, -1051523);
       b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
       a = ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
       d = ii(d, a, b, c, x[i+15], 10, -30611744);
       c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
       b = ii(b, c, d, a, x[i+13], 21,  1309151649);
       a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
       d = ii(d, a, b, c, x[i+11], 10, -1120210379);
       c = ii(c, d, a, b, x[i+ 2], 15,  718787259);
       b = ii(b, c, d, a, x[i+ 9], 21, -343485551);

       a = add(a, olda);
       b = add(b, oldb);
       c = add(c, oldc);
       d = add(d, oldd);
     }
     return rhex(a) + rhex(b) + rhex(c) + rhex(d);
   }

   return calcMD5(text);
}

//-----------------------------------------------------------------------------

XUtils.KEY_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

XUtils.generateBase64 = function(
   text
)
{

   var base64Text = "";
   var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
   var i = 0;

   //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

   // private method for UTF-8 encoding
   function encodeUTF8(string)
   {
      string = string.replace(/\r\n/g,"\n");
      for (var n=0, utfText=""; n<string.length; n++)
      {
         var char = string.charCodeAt(n);

         if (char < 128)
         {
            utfText += String.fromCharCode(char);
         }
         else if((char > 127) && (char < 2048))
         {
            utfText += String.fromCharCode((char >> 6) | 192);
            utfText += String.fromCharCode((char & 63) | 128);
         }
         else
         {
            utfText += String.fromCharCode((char >> 12) | 224);
            utfText += String.fromCharCode(((char >> 6) & 63) | 128);
            utfText += String.fromCharCode((char & 63) | 128);
         }
      }

      return utfText;
   }

   //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

   text = encodeUTF8(text);

   while (i < text.length)
   {

      chr1 = text.charCodeAt(i++);
      chr2 = text.charCodeAt(i++);
      chr3 = text.charCodeAt(i++);

      enc1 = chr1 >> 2;
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      enc4 = chr3 & 63;

      if (isNaN(chr2))
         enc3 = enc4 = 64;
      else if (isNaN(chr3))
         enc4 = 64;

      base64Text = base64Text +
         XUtils.KEY_STR.charAt(enc1) + XUtils.KEY_STR.charAt(enc2) +
         XUtils.KEY_STR.charAt(enc3) + XUtils.KEY_STR.charAt(enc4);
   }

   return base64Text;
}

//-----------------------------------------------------------------------------

XUtils.generateName = function(
   text1,
   text2
)
{
   text1 = (text1 == null) ? null : text1;
   text2 = (text2 == null) ? null : text2;

   function cleanName(
      name
   )
   {
      name = (name == null) ? null : name;

      if (name == null)
         return null;

      name = XString(name).normalize();
      name = name.replace(/[^a-z0-9\-\_\$]/ig, "_");
      name = name.substr(0,1).toLowerCase() + name.substr(1);

      return name;
   }

   var name1 = cleanName(text1);
   var name2 = cleanName(text2);

   var name = ((XString(name1).isSomething()) ? name1 : "") + ((XString(name1).isSomething() && XString(name2).isSomething()) ? "." : "") + ((XString(name2).isSomething()) ? name2 : "");

   return name;
}

//-----------------------------------------------------------------------------

XUtils.marshall = function(
   item
)
{
   item = (item == null) ? null : item;

   if (item == null)
      return null;

   switch (typeof(item))
   {
      case "string":
      case "number":
      case "boolean":
         var newItem = item;
         break;
      case "object":
         var newItem = [];
         //if (item.length != null)
         //{
         //   for (var i=0; i<item.length; i++)
         //      newItem.push([i,XUtils.marshall(item[i])]);
         //}
         //else
         //{
            for (key in item)
               newItem.push([key,XUtils.marshall(item[key])]);
         //}
         break;
      case "function":
         break;
         throw "Cannot marshall functions.";
   }

   return newItem;
}

//-----------------------------------------------------------------------------

XUtils.unmarshall = function(
   item
)
{
   item = (item == null) ? null : item;

   if (item == null)
      return null;

   switch (typeof(item))
   {
      case "string":
      case "number":
      case "boolean":
         var newItem = item;
         break;
      case "object":
         var newItem = [];
         if (item.length)
         {
            for (var i=0; i<item.length; i++)
               newItem[item[i][0]] = XUtils.unmarshall(item[i][1]);
         }
         break;
      case "function":
         throw "Cannot unmarshall functions.";
   }

   return newItem;
}

//-----------------------------------------------------------------------------

XUtils.clean = function(
   text
)
{
   var dirtyWords = new Array();
   dirtyWords.push("cock\\s+sucker");
   dirtyWords.push("mother\\s+fucker");
   dirtyWords.push("cunt");
   dirtyWords.push("shit");
   dirtyWords.push("fuck");
   dirtyWords.push("tits");

   for (var i=0; i<dirtyWords.length; i++)
      text = text.replace(RegExp(dirtyWords[i],"gi"), "XXXX");

   return text;
}

//-----------------------------------------------------------------------------

XUtils.formatPhone = function(
   text,
   areaCode
)
{
   areaCode = (areaCode == null) ? null : XString(areaCode).toString();

   if (XString(text).isNothing())
      return null;

   if (XMatch(text,/\(([0-9]{3})\)/))
   {
      areaCode = XMatch.matches[1];
      text = XMatch.leftContext + XMatch.rightContext;
   }

   var number = null;
   if (XMatch(text,/([0-9]{3}\-[0-9]{4})/))
   {
      number = XMatch.matches[1];
      text = XMatch.leftContext + XMatch.rightContext;
   }
   else if (XMatch(text,/([0-9]{3})([0-9]{4})/))
   {
      number = XMatch.matches[1] + "-" + XMatch.matches[2];
      text = XMatch.leftContext + XMatch.rightContext;
   }

   var phone = ((areaCode) ? "(" + areaCode + ") " : "") + ((number) ? number : "") + ((XString(text).isSomething()) ? " " + text : "");

   return XString(phone).normalize();
}

//-----------------------------------------------------------------------------

XUtils.isValidId = function(
   id,
   maxLength
)
{
   maxLength = (maxLength == null) ? 32 : maxLength;

   if (id == null)
      return false;
   if ((/\s/).test(id))
      return false;
   if ((/^[^a-z]/i).test(id))
      return false;
   if (id.length > maxLength)
      return false;

   return true;
}

//-----------------------------------------------------------------------------

XUtils.createId = function(
   name,
   lowerCase
)
{
   name = (name == null) ? "" : XString(name).normalize();
   lowerCase = (lowerCase == null) ? false : lowerCase;

   if (name.length == 0)
      return null;

   var id = "";

   name = name.replace(/[^a-z0-9_]/g, "");
   name = name.replace(/([^\sA-Z])([A-Z])/g, "$1 $2");
   var words = name.split(" ");
   for (var i=0; i<words.length; i++)
   {
      var word = words[i];
      if (i==0)
         id += word.toLowerCase();
      else if (i+1<words.length)
         id += word.substr(0,1).toUpperCase() + word.substr(1).toLowerCase();
      else
         id += word.substr(0,1).toUpperCase() + word.substr(1);
   }

   return (lowerCase) ? id.toLowerCase() : id;
}

//-----------------------------------------------------------------------------

XUtils.createKey = function(
   name,
   shortForm,
   maxLength
)
{
   name = (name == null) ? "" : XString(name).normalize();
   shortForm = (shortForm == null) ? false : shortForm;
   maxLength = (maxLength == null) ? null : maxLength;

   var key = "_" + name.toLowerCase() + "_"
   key = XString(key).removeAccents();
   key = key.replace(/a\._/g, "a#_"); // Protect the A. from being recognized as "a"
   key = key.replace(/[\.\_\s\-]+/g, "_");
   while ((/_(\&|and|of|or|the|for|a|an)_/).test(key))
      key = key.replace(/_(\&|and|of|or|the|for|a|an)_/g,"_");
   key = key.replace(/[^a-z0-9_]/g, "");
   if (shortForm)
   {
      key = key.replace(/_a/g, "_A");
      key = key.replace(/_e/g, "_E");
      key = key.replace(/_i/g, "_I");
      key = key.replace(/_o/g, "_O");
      key = key.replace(/_u/g, "_U");
      key = key.replace(/[aeiou]/g, "");
      if ((/[^_]_[^_]/).test(key))
         key = key.replace(/_([^_]{1})[^_]+/g,"_$1"); // Initials of name
      else
         key = key.replace(/_([^_]{2,3})[^_]*/g,"_$1"); // First 2 non-vowels in name
      key = key.replace(/_/g, "");
      key = key.toLowerCase();
   }
   key = key.replace(/^_/,"");
   key = key.replace(/_$/,"");
   key = key.replace(/__+/,"_");

   return (maxLength) ? key.substr(0,maxLength) : key;
}

//-----------------------------------------------------------------------------

XUtils.createToken = function(
   name
)
{

   return XUtils.createKey(name);
}

//-----------------------------------------------------------------------------

XUtils.getAge = function(
   updatedDate,
   units
)
{
   updatedDate = (typeof(updated) == "string") ? Date(updatedDate) : updatedDate;
   units = (units == null) ? XUtils.AGE_DAYS : units;

   var today = new Date();

   switch (units)
   {
      case XUtils.AGE_DAYS:  return Math.round((today - updatedDate)/(24 * 60 * 60 * 1000)*10)/10;
      case XUtils.AGE_HOURS: return Math.round((today - updatedDate)/(60 * 60 * 1000)*10)/10;
      case XUtils.AGE_MINS:  return Math.round((today - updatedDate)/(60 * 1000)*10)/10;
   }

   return 0;
}

//-----------------------------------------------------------------------------

// For use when working with VBScript in ASP

XUtils.iif = function(
   condition,
   trueValue,
   falseValue
)
{
   condition = (condition == null) ? false : condition;
   trueValue = (trueValue == null) ? null : trueValue;
   falseValue = (falseValue == null) ? null : falseValue;

   return (condition) ? trueValue : falseValue;
}

//-----------------------------------------------------------------------------

XUtils.isNull = function(
   testValue,
   defaultValue
)
{
   testValue = (testValue == null) ? null : testValue;
   defaultValue = (defaultValue == null) ? null : defaultValue;

   return (testValue == null)  ? defaultValue : testValue;
}

//=============================================================================

function $(
   id,
   context
)
{
   context = (context == null) ? document : context;

   element = context.getElementById(id);

   return element;
}

//-----------------------------------------------------------------------------

function $$(
   name,
   context
)
{
   context = (context == null) ? document : context;

   element = context.getElementsByName(name);

   return element;
}

//-----------------------------------------------------------------------------

function $$$(
   tagName,
   context
)
{
   context = (context == null) ? document : context;

   element = context.getElementsByTagName(tagName);

   return element;
}

//=============================================================================

