//Modified by / Date:
//Description:
//----------------------------------
//Bjorn Lim - JUN-13-2006
//Added getKeyByIndex() to Collection class
//Added getKeyValFromQueryStr() utility function
//Added getKeyValFromPairList() utility function
//Added copyPrototype() utility function
//----------------------------------
//Bjorn Lim - NOV-15-2005
//New validator: Int'l Phone Number
//New validator: Generic Postal Code
//----------------------------------
//Bjorn Lim - OCT-19-2005
//Removed password STYP restriction on related password confirmation check
//----------------------------------
//Bjorn Lim - SEP-21-2005
//Added new validator: Alphanumeric
//----------------------------------
//Bjorn Lim - AUG-15-2005
//Added doBeforeValidate()/doAfterValidate() function for FormUnit class
//----------------------------------
//Bjorn Lim - JUL-2X-2005/AUG-0X-2005
//The following features were added:
//-New validator: Date (with possible format tokens: dd,d,MM,m,y,yy,yyyy) 
//-New validator: Password (lower + upper + number -> 8 min length)
//-New validator: Password confirmation (requires relating elements)
//-Support for relating elements
//-Support for multiple custom validators
//-Support for overridding the error messages for built-in validators
//-Support for checkboxes
//-Support for multiple form values (e.g. all checked checkboxes)
//-Allow custom processing to be set before and after validation
//-Added getters and setters for the Element object. Though not required by JS, use these to ensure you are getting the proper values
//The following were changed:
//-FTYP* constants are now called STYP*
//-Removed unused constants
//-Minor changes to existing utility functions
//-Framework updated to use changes in Element object
//-Bug fixes
//----------------------------------
//Bjorn Lim - JUL-11-2005
//Added U.S. ZIP code global validation, updated tel no error message,
//updated AlertErrorHandler() to focus on an item
//----------------------------------
//Bjorn Lim - JUN-2005
//Framework created


//---------------------------------------------------------------------------
// Utility Functions
//---------------------------------------------------------------------------

//Retrieve the specified value for a key in the specified querystring

function getKeyValFromQueryStr(sQueryStr, sKey) {
  return getKeyValFromPairList(sQueryStr, '&', '=', sKey);
}

//Retrieve the specified value in a string of delimited key-value pairs
function getKeyValFromPairList(sPairList, sFieldDelim, sValDelim, sKey) {
  var keyValList = new Array();
  keyValList = sPairList.split(sFieldDelim);
  var bIsFound = false;
  var sRetVal = '';

  for(var i=0; (i < keyValList.length) && (bIsFound == false); i++) {
    var keyValPair = new Array();
    keyValPair = keyValList[i].split(sValDelim);
    if (keyValPair[0] == sKey) {
      bIsFound = true;
      sRetVal = keyValPair[1];
      if (sRetVal == null) {
        sRetVal = '';
      }
    }
  }
  return sRetVal;
}

//Trims the left side of the string
function trimLeft(sText) {
  var iLen = sText.length;
  
  if (iLen < 1) {
    return '';
  }
  
  var i = 0;
  while (sText.substring(i, i+1) == ' ') {
    i++;
  }
  return sText.substring(i, iLen);
}

//Trim the right side of the string
function trimRight(sText) {
  var iLen = sText.length;

  if (iLen < 1) {
    return '';
  }

  var i = iLen;
  while (sText.substring(i-1, i) == ' ') {
    i--;
  }
  return sText.substring(0, i);
}

//Trim both sides of the string
function trim(sText) {
  var sTrimText = trimLeft(sText);

  if (sTrimText == '') {
    return '';
  } else {
    return trimRight(sTrimText);
  }
}

//Removes the characters in the list from the string
function stripCharInList(sText, sList) {
  if (isEmptyStr(sText)) {
    return sText;
  }

  var i;
  var cCurr = '';
  var sNewText = '';
  for (i = 0; i < sText.length; i++) {
    cCurr = sText.charAt(i);
    if (sList.indexOf(cCurr) == -1) {
      sNewText += cCurr;
    }
  }
  return sNewText;
}

//Checks to see if the string is not empty
function isEmptyStr(sText) {
  if ((sText == null) || (sText == '')) {
    return true;
  }
  return false;
}

//Checks to see if the object is valid
function isValidObject(oObj) {
  if ((oObj == null) || (typeof(oObj) == 'undefined')) {
    return false;
  }
  return true;
}

//Checks to see if the string is numeric
function isNumeric(sText) {
  var i;
  var cCurr;
  for (i = 0; i < sText.length; i++) {
    cCurr = sText.charAt(i);
    if ((cCurr < '0') || (cCurr > '9')) {
      return false;
    }
  }
  return true;
}

//Checks to see if the object is an array
function isAnArray(oObject) {
  if (oObject == null) {
    return false;
  }

  return isValidObject(oObject.length);
}

//Sets the child object prototype properties to that of the specified parent object
function copyPrototype(parent, child) {
  var sConstructor = parent.toString();
  var aMatch = sConstructor.match( /\s*function (.*)\(/ );
  if (aMatch != null) {
    child.prototype[aMatch[1]] = parent;
  }
  for (var o in parent.prototype) {
    child.prototype[o] = parent.prototype[o];
  }
}

//Checks to see if e-mail address syntax is correct
function checkEMail (sParamAddress) {
  var sAddress = trim(sParamAddress);

  if ((sAddress == null) || (sAddress.length < 7))
    return false;

  if (window.RegExp) {
//    var sRegEx = /^[\w-]+(?:\.[\w-]+)*@(?:[\w-]+\.)+[a-zA-Z]{2,7}$/;
    var sRegEx = /^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/;
    var regEMail = new RegExp(sRegEx);
    if (regEMail.test(sAddress)) {
      return true;
    }
  } else {
    //browser has no regular expression support
    if((sAddress.indexOf("@") >= 0) && (sAddress.indexOf(".") >= 0)) {
      return true;
    }
  }
  return false;
}

//Checks for a valid 10-digit telephone number (area code + number)
function checkUSTelNo (sParamNumber) {
  var sNumber = trim(sParamNumber);

  if ((sNumber == null) || (sNumber.length < 10)) {
    return false;
  }
  
  if (window.RegExp) {
    var sRegEx = /^((\(?[1-9][0-9][0-9]\)?)?\s?-?[1-9][0-9][0-9]\s?-?\d{4})?$/;
    var regTelNo = new RegExp(sRegEx);
    if (regTelNo.test(trim(sNumber))) {
      return true;
    }
  } else {
    //browser has no regular expression support
    var sDigits = "0123456789";
    var sValidDelim = "()- ";
    var iReqLen = "10";
    var sTelNo = stripCharInList(sNumber, sValidDelim);
    if (isNumeric(sTelNo)) {
      if (sTelNo.length == iReqLen) {
        return true;
      }
    }
  }

  return false;
}

//Checks for a valid international telephone number ((+)country code + area code + number)
function checkIntTelNo (sParamNumber) {
  var sNumber = trim(sParamNumber);

  //change min length as necessary
  if ((sNumber == null) || (sNumber.length < 10)) {
    return false;
  }
  
  if (window.RegExp) {
    var sRegEx = /^(\+\d|\d)(\d|-){7,20}$/;
    var regTelNo = new RegExp(sRegEx);
    if (regTelNo.test(trim(sNumber))) {
      return true;
    }
  } else {
    //browser has no regular expression support
    var sDigits = "0123456789";
    var sValidDelim = "+()- ";
    var iReqLen = "10"; //change min length as necessary
    var sTelNo = stripCharInList(sNumber, sValidDelim);
    if (isNumeric(sTelNo)) {
      if (sTelNo.length < iReqLen) {
        return true;
      }
    }
  }

  return false;
}

//Checks for a valid 5 or 5+4 digit US zip code
//allows hyphens or spaces as a delimiter
function checkUSZipCode (sParamZipCode) {
  var sZipCode = trim(sParamZipCode);

  if ((sZipCode == null) || (sZipCode.length < 5)) {
    return false;
  }

  if (window.RegExp) {
    var sRegEx = /(^\d{5}$)|(^\d{5}-\d{4}$)|(^\d{5} \d{4}$)/;
    var regZipCode = new RegExp(sRegEx);
    if (regZipCode.test(trim(sZipCode))) {
      return true;
    }
  } else {
    //browser has no regular expression support
    var sValidList = '0123456789- ';
    var sValidDelim = '- ';
    var iHyphenCount = 0;
    var sCurrChar = '';
    var sSixthChar = '';
    var bInvalidChar = false;
    var iLen = sZipCode.length;

    for (var i = 0; i < iLen; i++) {
      sCurrChar = '' + sZipCode.substring(i, i+1);
      if ((sCurrChar == '-') || (sCurrChar == ' ')) {
        iHyphenCount++;
      }
      if (i == 5) {
        sSixthChar = sCurrChar;
      }
      if (sValidList.indexOf(sCurrChar) == -1) {
        bInvalidChar = true;
      }
    }

    if (bInvalidChar == false) {
      if ((iLen == 5) && (iHyphenCount == 0)) {
        return true;
      } else {
        if ((iLen == 10) && (iHyphenCount == 1) 
            && (sValidDelim.indexOf(sSixthChar) != -1)) {
          return true;
        }
      }
    }
  }
  return false;
}

//Generic postal code check
//Only allows numbers, letters, space and dash (-) as input
function checkGenPostalCode (sParamPostalCode) {
  var sPostalCode = trim(sParamPostalCode);
  sPostalCode = stripCharInList(sPostalCode, ' -');
  return checkAlphaNum(sPostalCode, false);
}

//Returns all characters with the same value from start point (first char = 0)
//iMode 0 = same characters as first character only
//iMode 1 = letters only
//iMode 2 = numbers only
function getToken(sValue, iStartPoint, iMode, iMaxLen) {
  if (isEmptyStr(sValue)) {
    return '';
  }
  if (iStartPoint > (sValue.length - 1)) {
    return '';
  }

  var iCurr = iStartPoint;
  var sRetVal = '';
  var sStartChar = sValue.charAt(iStartPoint);
  var sCurrChar = '';
  var bCheckOK = true;
  while ((bCheckOK == true) && (iCurr < sValue.length)) {
    sCurrChar = sValue.charAt(iCurr);
    if (iMode == 0) {
      bCheckOK = (sCurrChar == sStartChar);
    } else if (iMode == 1) {
      bCheckOK = (((sCurrChar >= 'a') && (sCurrChar <= 'z'))
               || ((sCurrChar >= 'A') && (sCurrChar <= 'Z')));
    } else if (iMode == 2) {
      bCheckOK = ((sCurrChar >= '0') && (sCurrChar <= '9'));
    }
    if (bCheckOK == true) {
      sRetVal += sCurrChar;
      iCurr++;
      if ((iMaxLen != 0) && (sRetVal.length >= iMaxLen)) {
        bCheckOK = false; // max expected length reached, force a return
      }
    }
  }
  return sRetVal;
}

//Checks if the year has a valid number and length
//Accepted formats: yyyy/yy/y
function checkYear(sYearStr, iStartPoint, sYearFormat) {
  if (isEmptyStr(sYearStr) == true) {
    return '';
  }
  var sYearToken = '';
  var sRetVal = ''; // if format is unrecognized, return empty string
  if (sYearFormat == 'yyyy') {
    sYearToken = getToken(sYearStr, iStartPoint, 2, 4);
    if (sYearToken.length == 4) {
      sRetVal = sYearToken;
    }
  } else if (sYearFormat == 'yy') {
    sYearToken = getToken(sYearStr, iStartPoint, 2, 2);
    if (sYearToken.length == 2) {
      sRetVal = sYearToken;
    }
  } else if (sYearFormat == 'y') {
    sYearToken = getToken(sYearStr, iStartPoint, 2, 4);
    if ((sYearToken.length == 1) || (sYearToken.length == 2) || (sYearToken.length == 4)) {
      sRetVal = sYearToken;
    }
  }

  return sRetVal;
}

//Checks if the given year is a leap year
function checkLeapYear(iYear) {
  return (((iYear%4==0) && (iYear%100 != 0)) || (iYear%400 == 0));
}

//Checks if the month has a valid number and length
//Accepted formats: MM/M
function checkMonth(sMonthStr, iStartPoint, sMonthFormat) {
  if (isEmptyStr(sMonthStr) == true) {
    return '';
  }

  var sMonthToken = getToken(sMonthStr, iStartPoint, 2, 2);
  if (isEmptyStr(sMonthToken) == true) {
    return '';
  } else {
    //Consider only the first char if its already > '1'
    //for date inputs without separators e.g. 2005730 (2005-7-30)
    var sFirstChar = sMonthToken.substring(0, 1);
    if (sFirstChar > 1) {
      sMonthToken = sFirstChar;
    }
  }
  var bValid = ((sMonthFormat == 'MM') && (sMonthToken.length == 2))
            || ((sMonthFormat == 'M') && ((sMonthToken.length == 1) || (sMonthToken.length == 2)));

  if (bValid == true) {
    //convert to int before comparing, otherwise it will only compare 1 character for 1 digit months
    if ((sMonthToken >= 1) && (sMonthToken <= 12)) {
      return sMonthToken;
    }
  }

  return '';
}

//Checks if the day has a valid number and length
//Accepted formats: dd/d
function checkDay(sDayStr, iStartPoint, sDayFormat) {
  if (isEmptyStr(sDayStr) == true) {
    return '';
  }

  var sDayToken = getToken(sDayStr, iStartPoint, 2, 2);
  if (isEmptyStr(sDayToken) == true) {
    return '';
  } else {
    //Consider only the first char if its already > '3'
    //for date inputs without separators e.g. 200575 (2005-7-5)
    var sFirstChar = sDayToken.substring(0, 1);
    if (sFirstChar > 3) {
      sDayToken = sFirstChar;
    }
  }
  
  var bValid = ((sDayFormat == 'dd') && (sDayToken.length == 2))
            || ((sDayFormat == 'd') && ((sDayToken.length == 1) || (sDayToken.length == 2)));

  if (bValid == true) {
    if ((sDayToken >= 1) && (sDayToken <= 31)) {
      return sDayToken;
    }
  }

  return '';
}

//Allows MM/mm, yyyy/yy/y, dd/d date format tokens
//returns the parsed date in milliseconds
var BAD_DATE_RET_VAL = 0;
function getDate(sDate, sDateFormat) {
  if ((sDate == null) || (sDate == '')) {
    return BAD_DATE_RET_VAL;
  }

  if ((sDateFormat == null) || (sDateFormat == '')) {
    return BAD_DATE_RET_VAL;
  }

  var iFormatCtr = 0; //format parser counter
  var iValueCtr = 0;
  var sToken = '';
  var sYear = '';
  var sMonth = '';
  var sDay = '';

  while (iFormatCtr < sDateFormat.length) {
    sToken = getToken(sDateFormat, iFormatCtr, 0, 0);
    iFormatCtr += sToken.length;
    if ((sToken == 'yyyy') || (sToken == 'yy') || (sToken == 'y')) {
      sYear = checkYear(sDate, iValueCtr, sToken);
      if (sYear == '') {
        return BAD_DATE_RET_VAL;
      }
      iValueCtr += sYear.length;
      if (sYear.length == 2) {
        if (sYear > 70) {
          sYear = 1900 + (sYear - 0);
        } else {
          sYear = 2000 + (sYear - 0);
        }
      }
    } else if ((sToken == 'MM') || (sToken == 'M')) {
      sMonth = checkMonth(sDate, iValueCtr, sToken);
      if (sMonth == '') {
        return BAD_DATE_RET_VAL;
      }
      iValueCtr += sMonth.length;
    } else if ((sToken == 'dd') || (sToken == 'd')) {
      sDay = checkDay(sDate, iValueCtr, sToken);
      if (sDay == '') {
        return BAD_DATE_RET_VAL;
      }
      iValueCtr += sDay.length;
    } else {
      if (sDate.substring(iValueCtr, iValueCtr + sToken.length) != sToken) {
        return BAD_DATE_RET_VAL;
      } else {
        iValueCtr += sToken.length;
      }
    }
  }
  //Check for left over input not covered by date format
  if (iValueCtr != sDate.length) {
    return BAD_DATE_RET_VAL;
  }
  //Check valid day/month
  if (sMonth == 2) {
    if (checkLeapYear(sYear) == true) {
      if (sDay > 29) {
        return BAD_DATE_RET_VAL;
      }
    } else if (sDay > 28) {
      return BAD_DATE_RET_VAL;
    }
  } else if ((sMonth == 4) || (sMonth == 6) || (sMonth == 9) || (sMonth == 11)) {
    if (sDay > 30) {
      return BAD_DATE_RET_VAL;
    }
  }
  
  var parsedDate = new Date(sYear, sMonth-1, sDay);
  return parsedDate.getTime();
}

//Checks for a valid date given the date format
//Allows MM/mm, yyyy/yy/y, dd/d date format tokens
function checkDate(sDate, sDateFormat) {
  if (getDate(sDate, sDateFormat) == BAD_DATE_RET_VAL) {
    return false;
  }
  return true;
}

//Check for a generic safe password
//iMinLen = Minimum password length
//iMinInstance = Minimum number of instances
var PASS_UPPER_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var PASS_LOWER_CHAR = 'abcdefghijklmnopqrstuvwxyz';
var PASS_NUMERIC = '0123456789';
var PASS_OK = 0;
var PASS_SHORT = 1;
var PASS_NO_UPPER_CASE = 2;
var PASS_NO_LOWER_CASE = 3;
var PASS_NO_NUMBER = 4;
function checkPassword(sPassword, iMinLen, iMinInstance) {
  var iLastLen = sPassword.length;
  if (iLastLen < iMinLen) {
    return PASS_SHORT;
  }

  var sStripPass = sPassword;
  sStripPass = stripCharInList(sStripPass, PASS_UPPER_CHAR);
  if ((iLastLen - sStripPass.length) < iMinInstance) {
    return PASS_NO_UPPER_CASE;
  }
  iLastLen = sStripPass.length;
  sStripPass = stripCharInList(sStripPass, PASS_LOWER_CHAR);
  if ((iLastLen - sStripPass.length) < iMinInstance) {
    return PASS_NO_LOWER_CASE;
  }
  iLastLen = sStripPass.length;
  sStripPass = stripCharInList(sStripPass, PASS_NUMERIC);
  if ((iLastLen - sStripPass.length) < iMinInstance) {
    return PASS_NO_NUMBER;
  }
  return PASS_OK;
}

//Checks whether the given value is alpha numeric or not
var ALPHA_NUM_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
function checkAlphaNum(sValue, bAllowUnderscore) {
  var sNewStr = stripCharInList(sValue, ALPHA_NUM_CHAR);
  if (bAllowUnderscore) {
    sNewStr = stripCharInList(sNewStr, '_');
  }
  if (sNewStr.length > 0) {
    return false;
  }
  return true;
}

//---------------------------------------------------------------------------
// JS Collection Obj
// -B.Lim 6/22/2005
//---------------------------------------------------------------------------

function Collection() {
  this.arrKey = new Array();
  this.arrVal = new Array();
}

Collection.prototype.getKeyIndex = function(sName) {
  var i;
  for (i = 0; i < this.arrKey.length; i++) {
    if (this.arrKey[i] == sName) {
      return i;
    }
  }
  return -1;
}

Collection.prototype.getKeyAtIndex = function(iIndex) {
  if ((iIndex >= 0) && (iIndex < this.arrKey.length)) {
    return this.arrKey[iIndex];
  }
  return null;
}

Collection.prototype.getValIndex = function(oVal) {
  var i;
  for (i = 0; i < this.arrVal.length; i++) {
    if (this.arrVal[i] == oVal) {
      return i;
    }
  }
  return -1;
}

Collection.prototype.getValAtIndex = function(iIndex) {
  if ((iIndex >= 0) && (iIndex < this.arrKey.length)) {
    return this.arrVal[iIndex];
  }
  return null;
}

Collection.prototype.containsKey = function(sKey) {
  if (this.getKeyIndex(sKey) > -1) {
    return true;
  }
  return false;
}

Collection.prototype.clearItems = function() {
  this.arrKey.length = 0;
  this.arrVal.length = 0;
}

Collection.prototype.addItem = function(sKey, oVal) {
  if (!this.containsKey(sKey)) {
    this.arrKey[this.arrKey.length] = sKey;
    this.arrVal[this.arrVal.length] = oVal;
  }
}

Collection.prototype.removeItem = function(sKey) {
  var iIndex = this.getKeyIndex(sKey);
  if (iIndex >= 0) {
    this.arrKey.splice(iIndex, 1);
    this.arrVal.splice(iIndex, 1);
  }
}

Collection.prototype.setItem = function(sKey, oVal) {
  var iIndex = this.getKeyIndex(sKey);
  if (iIndex >= 0) {
    this.arrVal[iIndex] = oVal;
  }
}

Collection.prototype.getItem = function(sKey) {
  var iIndex = this.getKeyIndex(sKey);
  if (iIndex >= 0) {
    return this.getValAtIndex(iIndex);
  }
  return null;
}

Collection.prototype.getSize = function() {
  return this.arrKey.length;
}

//---------------------------------------------------------------------------
// Constants
//---------------------------------------------------------------------------

//Expected form types
var INTYP_TEXT = 'text';
var INTYP_TEXTAREA = 'textarea';
var INTYP_PASSWORD = 'password';
var INTYP_CHECKBOX = 'checkbox';
var INTYP_RADIO = 'radio';
var INTYP_SELECT = 'select';
var INTYP_SUBMIT = 'submit';
var INTYP_HIDDEN = 'hidden';
var INTYP_BUTTON = 'button';

//Delimiter for storing checkbox values
var CHKBOX_DELIM = ',';

//Generic types for built-in validation
var DTYP_NONE = 0;      // ignore all built-in validation related to generic types
var DTYP_STRING = 1;    // generic string
var DTYP_DATE = 2;      // generic date
var DTYP_NUMBER = 3;    // generic number

//Special types for built-in validation
//Alternative: Add the special validator to the element's validator list
var STYP_NONE = 0;        // no special validation, read as generic
var STYP_EMAIL = 1;       // - string - e-mail
var STYP_US_TEL_NO = 2;   // - string - US tel no format
var STYP_INT_TEL_NO = 3;  // - string - international tel no format
var STYP_US_ZIP = 4;      // - string - US zip code format: #####[-####]
var STYP_GEN_POSTAL = 5;  // - string - generic postal code format
var STYP_PASSWORD = 6;    // - string - generic password format: alphanumeric+!@#$
var STYP_ALPHANUMERIC = 7;// - string - alphanumeric

//Sample formats for a specific data type (display formatting or data parsing)
//specify in Element.Format
var FMT_NONE = '';
//Common number formats
var FMT_NUMBER_CMN = '###,###,###.##';
var FMT_NUMBER_ZERO = '###,###,##0.0#';
//Common date formats
var FMT_DATE_US = 'M/d/y';
var FMT_DATE_JPN = 'y/M/d';
var FMT_DATE_EU = 'd/M/y';

//Related item identifiers (must be unique keys)
var REL_NONE = 0;      //Generic attached item
var REL_RANGE = 1;     //Related element containing upperbound of range
var REL_PWCONFIRM = 2; //Related element storing confirmation password

//Error codes, must be unique
var ERR_REQUIRED_CHK = 1;
var ERR_MINLEN_CHK = 2;
var ERR_MAXLEN_CHK = 3;
var ERR_ALPHANUMERIC_CHK = 50;
var ERR_NUMERIC_CHK = 100;
var ERR_EMAIL_CHK = 200;
var ERR_US_TEL_NO_CHK = 210;
var ERR_INT_TEL_NO_CHK = 211;
var ERR_US_ZIP_CHK = 220;
var ERR_GEN_POSTAL_CHK = 221;
var ERR_DATE_CHK = 300;
var ERR_PWORD_CHK = 400;
var ERR_PWORDCONFIRM_CHK = 410;

//Error messages
var ERRMSG_DELIM = '%%';
var ERRMSG_REQUIRED_CHK = 'The field "' + ERRMSG_DELIM + '" is required and must be filled.';
var ERRMSG_MINLEN_CHK = '"' + ERRMSG_DELIM + '" is below the minimum length of ' + ERRMSG_DELIM + '.';
var ERRMSG_MAXLEN_CHK = '"' + ERRMSG_DELIM + '" has exceeded the maximum length of ' + ERRMSG_DELIM + '.';
var ERRMSG_ALPHANUMERIC_CHK = '"' + ERRMSG_DELIM + '" must have an alphanumeric value (contains only letters and numbers).';
var ERRMSG_NUMERIC_CHK = '"' + ERRMSG_DELIM + '" must have a numeric value.';
var ERRMSG_EMAIL_CHK = '"' + ERRMSG_DELIM + '" contains an invalid e-mail address.';
var ERRMSG_TEL_NO_CHK = '"' + ERRMSG_DELIM + '" must be a valid telephone number (area code + tel no).';
var ERRMSG_INT_TEL_NO_CHK = '"' + ERRMSG_DELIM + '" must be a valid international telephone number.';
var ERRMSG_US_ZIP_CHK = '"' + ERRMSG_DELIM + '" must be a properly formatted 5-digit ZIP or 9-digit ZIP+4 code (NNNNN or NNNNN-mmmm).';
var ERRMSG_GEN_POSTAL_CHK = '"' + ERRMSG_DELIM + '" must be a properly formatted postal code.';
var ERRMSG_DATE_CHK = '"' + ERRMSG_DELIM + '" must be a properly formatted date (' + ERRMSG_DELIM + ').';
var ERRMSG_PWORD_CHK = '"' + ERRMSG_DELIM + '" must be atleast 8 characters long and contain one or more capital letters, small letters, AND numbers.';
var ERRMSG_PWORDCONFIRM_CHK = '"' + ERRMSG_DELIM + '" must match the value in ' + '"' + ERRMSG_DELIM + '"';

//---------------------------------------------------------------------------
// Validator FW
// -B.Lim 6/22/2005
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
//Form element class
//-Represents the field to be validated
//---------------------------------------------------------------------------
function FormElem(oUnit) {
  this.Unit = oUnit;                       //reference to the parent unit object
  this.InputObj = null;                    //reference to the input object
  this.InputName = '';                     //input object name
  this.InputType = '';                     //input object type
  this.DisplayName = '';                   //actual field name displayed
  this.DataType = null;                    //expected generic data type
  this.SpecType = null;                    //expected special format type
  this.FormValue = new Array();            //value taken from the form input, may be an array
  this.Required = false;                   //indicates a required field
  this.MinLen = 0;                         //indicates minimum allowed length
  this.MaxLen = 0;                         //indicates maximum allowed length
  this.Format = '';                        //indicates the display / validation format
  this.RelatedElemList = new Collection(); //indicates the list of form elements related to this element
  this.ValidatorList = new Collection();   //indicates the list of custom validators
  this.CustomErrList = new ErrorElem('');  //indicates the list of custom error messages
  this.Formatter = null;                   //formatter to use with this element's output
}

//setters and getters (use these instead of direct access to properties
//in case special validations/operations are added)
FormElem.prototype.setInputObj = function(oInputObj) {
  this.InputObj = oInputObj;
}

FormElem.prototype.getInputObj = function() {
  return InputObj;
}

FormElem.prototype.setInputName = function(sInputName) {
  this.InputName = sInputName;
  this.CustomErrList.setInputName(sInputName);
}

FormElem.prototype.getInputName = function() {
  return this.InputName;
}

FormElem.prototype.setInputType = function(sInputType) {
  this.InputType = sInputType;
}

FormElem.prototype.getInputType = function() {
  return this.InputType;
}

FormElem.prototype.setDisplayName = function(sDisplayName) {
  this.DisplayName = sDisplayName;
}

FormElem.prototype.getDisplayName = function() {
  return this.DisplayName;
}

FormElem.prototype.setDataType = function(oDataType) {
  this.DataType = oDataType;
}

FormElem.prototype.getDataType = function() {
  return this.DataType;
}

FormElem.prototype.setSpecType = function(oSpecType) {
  this.SpecType = oSpecType;
}

FormElem.prototype.getSpecType = function() {
  return this.SpecType;
}

FormElem.prototype.addFormValue = function(sFormValue) {
  this.FormValue[this.FormValue.length++] = sFormValue;
}

FormElem.prototype.setFormValue = function(sFormValue) {
  this.FormValue[0] = sFormValue;
  //this.FormValue = sFormValue;
}

FormElem.prototype.getFormValueByIndex = function(iIndex) {
  if (iIndex < this.FormValue.length) {
    return this.FormValue[iIndex];
  }

  return null;
}

FormElem.prototype.getFormValue = function() {
  return this.getFormValueByIndex(0);
  //return this.FormValue;
}

FormElem.prototype.clearFormValue = function() {
  this.FormValue.length = 0;
}

FormElem.prototype.hasMultValues = function() {
  return (this.FormValue.length > 1);
}

FormElem.prototype.setRequired = function(bRequired) {
  this.Required = bRequired;
}

FormElem.prototype.getRequired = function() {
  return this.Required;
}

FormElem.prototype.setMinLen = function(iMinLen) {
  this.MinLen = iMinLen;
}

FormElem.prototype.getMinLen = function() {
  return this.MinLen;
}

FormElem.prototype.setMaxLen = function(iMaxLen) {
  this.MaxLen = iMaxLen;
}

FormElem.prototype.getMaxLen = function() {
  return this.MaxLen;
}

FormElem.prototype.setFormat = function(sFormat) {
  this.Format = sFormat;
}

FormElem.prototype.getFormat = function() {
  return this.Format;
}

//Add additional processing before ALL validation
FormElem.prototype.doBeforeValidate = function(oThisElem) {
}

//Add additional processing after ALL validation
FormElem.prototype.doAfterValidate = function(oThisElem) {
}

//Sets the format value to a value that will make formatters/validators ignore it
FormElem.prototype.clearFormat = function() {
  this.Format = '';
}

//Adds a related element
//The key must be a unique value that would allow the related element to be matched
//to the correct validation
FormElem.prototype.addRelElem = function(sKey, oElem) {
  this.RelatedElemList.addItem(sKey, oElem);
}

//Retrieves the specified related element
FormElem.prototype.getRelElem = function(sKey) {
  return this.RelatedElemList.getItem(sKey);
}

//Returns the nth related element (unsorted list)
FormElem.prototype.getRelElemByIndex = function(iIndex) {
  return this.RelatedElemList.getValAtIndex(iIndex);
}

//Clears all related elements
FormElem.prototype.clearRelElemList = function() {
  return this.RelatedElemList.clearItems();
}

//Returns the number of related elements
FormElem.prototype.getRelElemCount = function() {
  return this.RelatedElemList.getSize();
}

//Adds a custom validator
FormElem.prototype.addValidator = function(oValidator) {
  this.ValidatorList.addItem(this.ValidatorList.getSize(), oValidator);
}

//Returns the nth validator (unsorted list)
FormElem.prototype.getValidatorByIndex = function(iIndex) {
  return this.ValidatorList.getValAtIndex(iIndex);
}

//Clears all validators
FormElem.prototype.clearValidatorList = function() {
  return this.ValidatorList.clearItems();
}

//Returns the number of validators
FormElem.prototype.getValidatorCount = function() {
  return this.ValidatorList.getSize();
}

//Adds a custom error message that will replace all error messages for the element
//with the same error ID
FormElem.prototype.addCustErr = function(iID, sMessage, arrParams) {
  this.CustomErrList.addErr(iID, sMessage, arrParams);
}

//Retrieves the specified custom error by error code
FormElem.prototype.getCustErr = function(sErrorCode) {
  return this.CustomErrList.getErr(sErrorCode);
}

//Returns the nth related element (unsorted list)
FormElem.prototype.getCustErrByIndex = function(iIndex) {
  return this.CustomErrList.getErrByIndex(iIndex);
}

//Removes a cust error if it exists
FormElem.prototype.removeCustErr = function(iID) {
  this.CustomErrList.removeErr(iID);
}

//Clears all related elements
FormElem.prototype.clearCustErrList = function() {
  return this.CustomErrList.clearErr();
}

//Returns the number of related elements
FormElem.prototype.getCustErrCount = function() {
  return this.CustomErrList.getSize();
}

//Sets the custom error to the specified message, if the said custom error exists
FormElem.prototype.setCustErr = function(iID, sMessage, arrParams) {
  this.CustomErrList.setErr(iID, sMessage, arrParams);
}

//Resets the specified custom error to reflect changes in user input
FormElem.prototype.resetCustErr = function(iID, sMessage, arrParams) {
  if (this.CustomErrList.hasErr(iID)) {
    this.setCustErr(iID, sMessage, arrParams);
  } else {
    this.addCustErr(iID, sMessage, arrParams);
  }
}

//Sets the formatter
FormElem.prototype.setFormatter = function(oFormatter) {
  this.Formatter = oFormatter;
}

//Clears the formatter
FormElem.prototype.clearFormatter = function(oFormatter) {
  this.Formatter = null;
}

//---------------------------------------------------------------------------
//Error message class
//-Error messages for a specified form element
//---------------------------------------------------------------------------
function ErrorElem(sInputName) {
  this.InputName = sInputName;
  this.ErrMsgList = new Collection();
  this.ParamList = new Collection();
}

//Returns the related input name the error message(s) are for
ErrorElem.prototype.getInputName = function() {
  return this.InputName;
}

ErrorElem.prototype.setInputName = function(sInputName) {
  this.InputName = sInputName;
}

//Adds an error message if the specified iID/errorcode does not exist
ErrorElem.prototype.addErr = function(iID, sMessage, oParams) {
  if (this.ErrMsgList.containsKey(iID)) {
    return;
  }
  this.ErrMsgList.addItem(iID, sMessage);
  this.ParamList.addItem(iID, oParams);
}

//Returns the error message (based on Msg index) with applied parameters
ErrorElem.prototype.getErrByIndex = function(iIndex) {
  var sMsg = this.ErrMsgList.getValAtIndex(iIndex);
  var oParams = this.ParamList.getValAtIndex(iIndex);
  var iLen = oParams.getSize();
  var i;

  if (iLen > 0) {
    for (i = 0; i < iLen; i++) {
      sMsg = sMsg.replace(ERRMSG_DELIM, oParams.getValAtIndex(i));
    }
  }
  return sMsg;
}

//Returns the error message (based on Error Msg ID) with applied parameters
ErrorElem.prototype.getErr = function(iID) {
  var iIndex = this.ErrMsgList.getKeyIndex(iID);
  if (iIndex >= 0) {
    return this.getErrByIndex(iIndex);
  }
  return '';
}

//Returns true if an error message has been specified with the said ID
ErrorElem.prototype.hasErr = function(iID) {
  var iIndex = this.ErrMsgList.getKeyIndex(iID);
  if (iIndex >= 0) {
    return true;
  }
  return false;
}

//Sets the error message for the specified ID to the specified values, if it exists
ErrorElem.prototype.setErr = function(iID, sMessage, oParams) {
  this.ErrMsgList.setItem(iID, sMessage);
  this.ParamList.setItem(iID, oParams);
}

//Removes the error message and related parameters
ErrorElem.prototype.removeErr = function(iID) {
  this.ErrMsgList.removeItem(iID);
  this.ParamList.removeItem(iID);
}

//Clears all error messages for the element
ErrorElem.prototype.clearErr = function() {
  this.ErrMsgList.clearItems();
  this.ParamList.clearItems();
}

ErrorElem.prototype.getSize = function() {
  return this.ErrMsgList.getSize();
}

//---------------------------------------------------------------------------
//Error message collection class
//-Central repository for error messages
//---------------------------------------------------------------------------
function ErrorCollection(oUnit) {
  this.Unit = oUnit;
}
ErrorCollection.prototype = new Collection();
ErrorCollection.superclass = Collection.prototype;

//Adds an error to the list if it does not exist for that particular form input
//Custom error messages should be built right before validation, to allow such things as 
//current form values to be set (override doBeforeValidate())
ErrorCollection.prototype.addErrToList = function(sInputName, iID, sMessage, arrParams) {
  var oElem = null;
  var sErrorMessage = sMessage;
  //Check element object for custom error messages
  //meant for the particular validation (has id = iID)
  oElem = this.Unit.getElem(sInputName);
  //Do not process if error message was added for item not in the FormUnit
  if (oElem != null) {
    var sCustErrMsg = oElem.getCustErr(iID);
    if (isEmptyStr(sCustErrMsg) == false) {
      sErrorMessage = sCustErrMsg;
    }
  }

  var iIndex = this.getKeyIndex(sInputName);
  if (iIndex >= 0) {
    this.getValAtIndex(iIndex).addErr(iID, sErrorMessage, arrParams);
  } else {
    var oNew = new ErrorElem(sInputName);
    oNew.addErr(iID, sErrorMessage, arrParams);
    this.addItem(sInputName, oNew);
  }
}


//---------------------------------------------------------------------------
//Form element collection class
//-Main/managing class (functions added as necessary)
//---------------------------------------------------------------------------
function FormUnit(oFormObj) {
  this.FormObj  = oFormObj;
  this.FormName = '';
  this.ElemList = new Collection();
  this.ValidatorList = new Collection();
  this.FormatterList = new Collection();
  this.ErrorList = new ErrorCollection(this);
  this.ErrorHandler = null;
}

//Adds an element to the list
FormUnit.prototype.addElem = function(sInputName, sInputType, sDisplayName,
                                      sDataType, sSpecType, bRequired, 
                                      iMinLen, iMaxLen) {
  var oElem = new FormElem(this);
  oElem.setInputName(sInputName);
  oElem.setInputType(sInputType);
  oElem.setDisplayName(sDisplayName);
  oElem.setDataType(sDataType);
  oElem.setSpecType(sSpecType);
  oElem.setRequired(bRequired);
  oElem.setMinLen(iMinLen);
  oElem.setMaxLen(iMaxLen);
  this.ElemList.addItem(sInputName, oElem);
}

//Returns the element with the specified input name
FormUnit.prototype.getElem = function(sInputName) {
  return this.ElemList.getItem(sInputName);
}

//Returns the nth element in the collection
FormUnit.prototype.getElemAtIndex = function(iIndex) {
  return this.ElemList.getValAtIndex(iIndex);
}

//Removes the element with the specified input name
FormUnit.prototype.removeElem = function(sInputName) {
  this.ElemList.removeItem(sInputName);
}

//Adds a global validator to the list
FormUnit.prototype.addValidator = function(sName, oFunc) {
  this.ValidatorList.addItem(sName, oFunc);
}

//Adds a formatter to the list
FormUnit.prototype.addFormatter = function(sName, oFunc) {
  this.FormatterList.addItem(sName, oFunc);
}

//Sets the general error handler
FormUnit.prototype.setErrorHandler = function(oFunc) {
  this.ErrorHandler = oFunc;
}

//Collect the form values to prepare for validation / reformatting display
//Currently only handles text, select, and radio fields
FormUnit.prototype.collectValues = function() {
  var iElemCount = this.ElemList.getSize();
  var oFormElem = null;
  var oUnitElem = null;
  for (var i = 0; i < iElemCount; i++) {
    oUnitElem = this.getElemAtIndex(i);
    oFormElem = this.FormObj.elements[oUnitElem.getInputName()];
    //alert(oUnitElem.getInputName() + ' ' + oUnitElem.getInputType());
    //alert(oFormElem.name + ' ' + oFormElem.length + ' ' + oFormElem.type);

    if ((oUnitElem.InputType == INTYP_TEXT) || (oUnitElem.InputType == INTYP_TEXTAREA) || (oUnitElem.InputType == INTYP_HIDDEN)) {
      oUnitElem.setFormValue(trim(oFormElem.value));
      oFormElem.value = trim(oFormElem.value);
    } else if (oUnitElem.getInputType() == INTYP_SELECT) {
      if (isAnArray(oFormElem.options)) {
        for (var j = 0; j < oFormElem.options.length; j++) {
          if (oFormElem.options[j].selected == true) {
            oUnitElem.setFormValue(oFormElem.options[j].value);
          }
        }
      } else {
        oUnitElem.setFormValue(oFormElem.options.value);
      }    
    } else if (oUnitElem.getInputType() == INTYP_RADIO) {
      if (isAnArray(oFormElem)) {
        for(var k = 0; k < oFormElem.length; k++) {
          if(oFormElem[k].checked) {
            oUnitElem.setFormValue(oFormElem[k].value);
          }
        }
      } else {
        if(oFormElem.checked) {
          oUnitElem.setFormValue(oFormElem.value);
        }
      }
    } else if (oUnitElem.getInputType() == INTYP_CHECKBOX) {
      if (isAnArray(oFormElem)) {
        for(var l = 0; l < oFormElem.length; l++) {
          if(oFormElem[l].checked) {
            oUnitElem.addFormValue(oFormElem[l].value);
          }
        }
      } else {
        if(oFormElem.checked) {
          oUnitElem.setFormValue(oFormElem.value);
        }
      }
    }//else-if
  }//for
}

FormUnit.prototype.doBeforeValidate = function() {
}

FormUnit.prototype.doAfterValidate = function() {
}

//Validates the elements in the form against the validators in the list
FormUnit.prototype.validateForm = function() {
  var iValidatorCount = this.ValidatorList.getSize();
  var iElemCount = this.ElemList.getSize();
  var oValidator = null;
  var oElem = null;
  var i, j, k;

  this.doBeforeValidate();

  if ((iValidatorCount < 1) || (iElemCount < 1)) {
    return;
  }

  //Validate the elements
  for (i = 0; i < iElemCount; i++) {
    oElem = this.ElemList.getValAtIndex(i);
    oElem.doBeforeValidate(oElem);

    //Call the global validators
    for (j = 0; j < iValidatorCount; j++) {
      oValidator = this.ValidatorList.getValAtIndex(j);
      oValidator(oElem, this.ErrorList);
    }

    //Call any local validator assigned to the element
    var iCValCount = oElem.getValidatorCount();    
    if (iCValCount > 0) {
      for (k = 0; k < iCValCount; k++) {
        oValidator = oElem.getValidatorByIndex(k);
        oValidator(oElem, this.ErrorList);
      }
    }
    
    oElem.doAfterValidate(oElem);
  }

  this.doAfterValidate();
}

//Initialize common global validators
//When modifying, note that the first field passed must be unique
FormUnit.prototype.initCmnValidators = function() {
  this.addValidator(ERR_REQUIRED_CHK, validateRequired);
  this.addValidator(ERR_MINLEN_CHK, validateMinLen);
  this.addValidator(ERR_MAXLEN_CHK, validateMaxLen);
  this.addValidator(ERR_NUMERIC_CHK, validateNumeric);
  this.addValidator(ERR_ALPHANUMERIC_CHK, validateAlphanumeric);
  this.addValidator(ERR_EMAIL_CHK, validateEmail);
  this.addValidator(ERR_US_TEL_NO_CHK, validateUSTelNo);
  this.addValidator(ERR_US_ZIP_CHK, validateUSZipCode);
  this.addValidator(ERR_DATE_CHK, validateDate);
  this.addValidator(ERR_PWORD_CHK, validatePassword);
  this.addValidator(ERR_PWORDCONFIRM_CHK, validatePasswordConfirm);
}

//Processing on initialization 
FormUnit.prototype.init = function() {
  this.initCmnValidators();
}

//Processing to be done on submit, submits form
FormUnit.prototype.submitForm = function() {
//  alert('Collecting form values');
  this.collectValues();
//  alert('Clearing error list');
  this.ErrorList.clearItems();
//  alert('Validating form');
  this.validateForm();
//  alert('Showing errors');
  this.setErrorHandler(alertErrorHandler);
  var iRes = this.ErrorHandler(this);
  if (iRes == true) {
    this.FormObj.submit();
//    alert('Submitted!');
  }
  return false;
}

//Presents error messages in an alert box
//Shows only the first item with one or more errors
//Moves focus to the affected item
function alertErrorHandler(oUnit) {
  var oErrorList = oUnit.ErrorList;
  var oErr = null;
  var sErrStr = '';
  var sInputName = '';
  var oUnitElem = null;
  var oFormElem = null;

  if (oErrorList.getSize() > 0) {
//    for (i = 0; i < oErrorList.getSize(); i++)
//      oErr = oErrorList.getValAtIndex(i);
    oErr = oErrorList.getValAtIndex(0);
    sInputName = oErr.getInputName();
    for (j = 0; j < oErr.getSize(); j++) {
      sErrStr = sErrStr + oErr.getErrByIndex(j) + '\n';
    }
    alert(sErrStr);
    //shift focus
    if (isValidObject(oUnit.FormObj.elements[sInputName]) == true) {
      oUnitElem = oUnit.getElem(sInputName);
      oFormElem = oUnit.FormObj.elements[sInputName];
      if ((oUnitElem.getInputType() == INTYP_TEXT) || (oUnitElem.getInputType() == INTYP_TEXTAREA) || (oUnitElem.getInputType() == INTYP_SELECT)) {
        oFormElem.focus();
      } else if (oUnitElem.getInputType() == INTYP_RADIO) {
        oFormElem[0].focus();
      }
    }
    return false;
  }
  return true;
}

//---------------------------------------------------------------------------
//Global/common validators
//-Applied to all fields
//---------------------------------------------------------------------------

//Validates fields that are marked 'required'
//Currently only handles text fields
function validateRequired (oElem, oErrList) {
  var sVal = oElem.getFormValue();

  if (!oElem.getRequired()) {
    return;
  }

  if ((sVal == null) || (sVal == '')) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_REQUIRED_CHK, ERRMSG_REQUIRED_CHK, oParams);
  }
}

//Validates fields that have a minimum length > 0
function validateMinLen (oElem, oErrList) {
  var sVal = oElem.getFormValue();

  if ((isEmptyStr(sVal)) || (oElem.getMinLen() < 1)) {
    return;
  }

  if (sVal.length < oElem.getMinLen()) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oParams.addItem('1', oElem.getMinLen());
    oErrList.addErrToList(oElem.getInputName(), ERR_MINLEN_CHK, ERRMSG_MINLEN_CHK, oParams);
  }
}

//Validates fields that have a maximum length > 0
function validateMaxLen (oElem, oErrList) {
  var sVal = oElem.getFormValue();
  
  if ((isEmptyStr(sVal)) || (oElem.getMaxLen() < 1)) {
    return;
  }

  if (sVal.length > oElem.getMaxLen()) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oParams.addItem('1', oElem.getMaxLen());
    oErrList.addErrToList(oElem.getInputName(), ERR_MAXLEN_CHK, ERRMSG_MAXLEN_CHK, oParams);
  }
}

//Validates fields with the special type of 'Alphanumeric'
function validateAlphanumeric (oElem, oErrList) {
  var sVal = oElem.getFormValue();
  
  if ((oElem.getSpecType() != STYP_ALPHANUMERIC) || isEmptyStr(sVal)) {
    return;
  }

  if (checkAlphaNum(sVal, false) == false) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_ALPHANUMERIC_CHK, ERRMSG_ALPHANUMERIC_CHK, oParams);
  }
}

//Validates fields that have a data type of 'number' and special type of 'none'
function validateNumeric (oElem, oErrList) {
  var sVal = oElem.getFormValue();
  
  if ((oElem.getDataType() != DTYP_NUMBER) || (oElem.getSpecType() != STYP_NONE) || isEmptyStr(sVal)) {
    return;
  }

  if (isNumeric(sVal) == false) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_NUMERIC_CHK, ERRMSG_NUMERIC_CHK, oParams);
  }
}

//Validates fields with the special type of 'E-mail'
function validateEmail(oElem, oErrList) {
  var sVal = oElem.getFormValue();
  
  if ((oElem.getSpecType() != STYP_EMAIL) || isEmptyStr(sVal)) {
    return;
  }

  if (checkEMail(sVal) == false) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_EMAIL_CHK, ERRMSG_EMAIL_CHK, oParams);
  }
}

//Validates fields with the special type of 'US Telephone No'
function validateUSTelNo(oElem, oErrList) {
  var sVal = oElem.getFormValue();
  
  if ((oElem.getSpecType() != STYP_US_TEL_NO) || isEmptyStr(sVal)) {
    return;
  }

  if (checkUSTelNo(sVal) == false) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_US_TEL_NO_CHK, ERRMSG_TEL_NO_CHK, oParams);
  }  
}

//Validates fields with the special type of 'International Telephone No'
function validateIntTelNo(oElem, oErrList) {
  var sVal = oElem.getFormValue();
  
  if ((oElem.getSpecType() != STYP_INT_TEL_NO) || isEmptyStr(sVal)) {
    return;
  }

  if (checkIntTelNo(sVal) == false) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_INT_TEL_NO_CHK, ERRMSG_INT_TEL_NO_CHK, oParams);
  }  
}

//Validates fields with the special type of 'US ZIP Code'
function validateUSZipCode(oElem, oErrList) {
  var sVal = oElem.getFormValue();

  if ((oElem.getSpecType() != STYP_US_ZIP) || isEmptyStr(sVal)) {
    return;
  }

  if (checkUSZipCode(sVal) == false) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_US_ZIP_CHK, ERRMSG_US_ZIP_CHK, oParams);
  }
}

//Validates fields with the special type of 'Generic Postal Code'
function validateGenPostalCode(oElem, oErrList) {
  var sVal = oElem.getFormValue();

  if ((oElem.getSpecType() != STYP_GEN_POSTAL) || isEmptyStr(sVal)) {
    return;
  }

  if (checkGenPostalCode(sVal) == false) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_GEN_POSTAL_CHK, ERRMSG_GEN_POSTAL_CHK, oParams);
  }
}

//Validates fields with the data type 'Date' and a given format
function validateDate(oElem, oErrList) {
  var sVal = oElem.getFormValue();
  var sFormat = oElem.getFormat();

  if ((oElem.getDataType() != DTYP_DATE) || isEmptyStr(sVal)) {
    return;
  }
  if (isEmptyStr(sFormat)) {
    sFormat = FMT_DATE_US;
  }

  if (checkDate(sVal, sFormat) == false) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oParams.addItem('1', sFormat);
    oErrList.addErrToList(oElem.getInputName(), ERR_DATE_CHK, ERRMSG_DATE_CHK, oParams);
  }
}

//Validates fields with the data type 'string' and special type of 'password'
function validatePassword(oElem, oErrList) {
  var sVal = oElem.getFormValue();
  var sFormat = oElem.getFormat();

  if ((oElem.getDataType() != DTYP_STRING) || (oElem.getSpecType() != STYP_PASSWORD) || isEmptyStr(sVal)) {
    return;
  }
  if (checkPassword(sVal, 8, 1) != PASS_OK) {
    var oParams = new Collection();
    oParams.addItem('0', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_PWORD_CHK, ERRMSG_PWORD_CHK, oParams);
  }
}

//Validates password fields with a related confirmation password
function validatePasswordConfirm(oElem, oErrList) {
  var sVal = oElem.getFormValue();

  if ((oElem.getDataType() != DTYP_STRING) || isEmptyStr(sVal)) {
    return;
  }

  var oRelElem = oElem.getRelElem(REL_PWCONFIRM);
  if (oRelElem == null) {
    return;
  }

  var sVal = oElem.getFormValue();
  var sConfirmVal = oRelElem.getFormValue();

  if (sVal != sConfirmVal) {
    var oParams = new Collection();
    oParams.addItem('0', oRelElem.getDisplayName());
    oParams.addItem('1', oElem.getDisplayName());
    oErrList.addErrToList(oElem.getInputName(), ERR_PWORDCONFIRM_CHK, ERRMSG_PWORDCONFIRM_CHK, oParams);
  }
}