// ----------------------------------------------------------------------
// Javascript form validation routines.
// Author: Stephen Poley
//
// Simple routines to quickly pick up obvious typos.
// All validation routines return true if executed by an older browser:
// in this case validation must be left to the server.
//
// Update Aug 2004: have tested that IE 5.0 and IE 5.5 both support DOM model
// sufficiently well, so innerHTML option removed (redundant).
//
// Update Jun 2005: discovered that reason IE wasn't setting focus was
// due to an IE timing bug. Added 0.1 sec delay to fix.
//
// Update Oct 2005: minor tidy-up: unused parameter removed
//
// validatePresent
// validateEmail
// validateTelnr
// validateAge
// ValidDate
// ValidYN
// ValidAmount
// ValidYear
// ValidYearLENow
// ValidZip
// ValidRoutingNumber
// ValidCCExpiration
// ValidCCNumber
// ValidCVVD
// ----------------------------------------------------------------------

nbsp = 160;    // non-breaking space char
node_text = 3; // DOM text node-type
emptyString = /^\s*$/;
glb_vfld;      // retain vfld for timer thread

function left(str, n){
	if (n <= 0)
	    return "";
	else if (n > String(str).length)
	    return str;
	else
	    return String(str).substring(0,n);
};

function right(str, n){
    if (n <= 0)
       return "";
    else if (n > String(str).length)
       return str;
    else {
       var iLen = String(str).length;
       return String(str).substring(iLen, iLen - n);
    }
};

function mid(str, start, len) {
  if (start < 0 || len < 0) return "";
  var iEnd, iLen = String(str).length;
  if (start + len > iLen)
        iEnd = iLen;
  else
        iEnd = start + len;
  return String(str).substring(start,iEnd);
}

// -----------------------------------------
//                  trim
// Trim leading/trailing whitespace off string
// -----------------------------------------

function trim(str)
{
  return str.replace(/^\s+|\s+$/g, '')
};


// -----------------------------------------
//                  setfocus
// Delayed focus setting to get around IE bug
// -----------------------------------------

function setFocusDelayed()
{
  glb_vfld.focus()
}

function setfocus(vfld)
{
  // save vfld in global variable so value retained when routine exits
  glb_vfld = vfld;
  setTimeout( 'setFocusDelayed()', 100 );
}


// -----------------------------------------
//                  msg
// Display warn/error message in HTML element
// commonCheck routine must have previously been called
// -----------------------------------------

function msg(_title,     // title to display for alert box
             msgtype,    // class to give element ("warn" or "error")
             message)    // string to display
{
  // setting an empty string can give problems if later set to a 
  // non-empty string, so ensure a space present. (For Mozilla and Opera one could 
  // simply use a space, but IE demands something more, like a non-breaking space.)
  var dispmessage;

  if (emptyString.test(message)) {}
  else
    fnAlertNZK(_title, message);
};


// -----------------------------------------
//            commonCheck
// Common code for all validation routines to:
// (a) check for older / less-equipped browsers
// (b) check if empty fields are required
// Returns true (validation passed), 
//         false (validation failed) or 
//         proceed (don't know yet)
// -----------------------------------------

var proceed = 2;  

function commonCheck    (vfld,    // element to be validated
                         stitle,  // header of alert box
                         reqd)    // true if required
{
  if (!document.getElementById) 
    return true;  // not available on this browser - leave validation to the server

  if (emptyString.test(vfld.value)) {
    if (reqd) {
      glb_vfld = vfld;
      msg (stitle, "error", "<b class='p14'><center>This field is required.</b></center>");  
      setfocus(vfld);
      return false;
    }
    else 
      return true;  
  }
  return proceed;
};

// -----------------------------------------
//            validatePresent
// Validate if something has been entered
// Returns true if so 
// -----------------------------------------

function validatePresent(vfld,   // element to be validated
                         stitle )  // ititle for the alert box
{
  var stat = commonCheck (vfld, stitle, true);
  if (stat != proceed) return stat;
  return true;
};

// -----------------------------------------
//               validateEmail
// Validate if e-mail address
// Returns true if so (and also if could not be executed because of old browser)
// -----------------------------------------

function validateEmail  (vfld,   // element to be validated
                         stitle,   // id of element to receive info/error msg
                         reqd)   // true if required
{
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);  // value of field with whitespace trimmed off
  var email = /^[^@]+@[^@.]+\.[^@]*\w\w$/
  if (!email.test(tfld)) {
    msg (stitle, "error", "<b class='p14'><center>This is not a valid e-mail address.</center></b>");
    setfocus(vfld);
    return false;
  }

  var email2 = /^[A-Za-z][\w.-]+@\w[\w.-]+\.[\w.-]*[A-Za-z][A-Za-z]$/
  if (!email2.test(tfld)) 
    msg (stitle, "warn", "<b class='p14'><center>Unusual e-mail address - check if correct.</center></b>");
  return true;
};


// -----------------------------------------
//            validateTelnr
// Validate telephone number
// Returns true if so (and also if could not be executed because of old browser)
// Permits spaces, hyphens, brackets and leading +
// -----------------------------------------

function validateTelnr  (vfld,   // element to be validated
                         stitle,   // id of element to receive info/error msg
                         reqd)   // true if required
{
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);  // value of field with whitespace trimmed off
  var telnr = /^\+?[0-9 ()-]+[0-9]$/
  if (!telnr.test(tfld)) {
    msg (stitle, "error", "<b class='p14'><center>Not a valid telephone number.  Permitted digits, space ()- and leading +.</center></b>");
    setfocus(vfld);
    return false;
  }

  var x = tfld.replace(/[^0-9]/g,'');  //  remove everything except numbers
  
  var numdigits = 0;
  for (var j=0; j<tfld.length; j++)
    if (tfld.charAt(j)>='0' && tfld.charAt(j)<='9') numdigits++;

  if (numdigits<6) {
    msg (stitle, "error", "<b class='p14'><center>The phone number only has " + numdigits + " digits - too short.</center></b>");
    setfocus(vfld);
    return false;
  }

  if (numdigits>14)
    msg (stitle, "warn", "<b class='p14'><center>The phone number has more than 14 digits - check if correct.</b></center>");
  else { 
    if (numdigits<10)
      msg (stitle, "warn", "<b class='p14'><center>The phone number only has " + numdigits + " digits - check if correct.</b></center>");
  }

  //  if we get here, we probably are OK so insert the dashes where needed.
  var y = x;
  if (x.length == 10) var y = left(x,3) + '-' + mid(x,3,3) + '-' + right(x,4);
  if ( (x.length == 11) && (left(x,1) == '1')) var y = '1-' + mid(x,1,3) + '-' + mid(x,4,3) + '-' + right(x,4);
  vfld.value = y;

  return true;
};

// -----------------------------------------
//             validateAge
// Validate person's age
// Returns true if OK 
// -----------------------------------------

function validateAge    (vfld,   // element to be validated
                         stitle,
                         reqd)   // true if required
{
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);
  var ageRE = /^[0-9]{1,3}$/
  if (!ageRE.test(tfld)) {
    msg (stitle, "error", "<b class='p14'><center>Not a valid age.</b></center>");
    setfocus(vfld);
    return false;
  }

  if (tfld>=200) {
    msg (stitle, "error", "<b class='p14'><center>Not a valid age.</b></center>");
    setfocus(vfld);
    return false;
  }

  if (tfld>110) msg (stitle, "warn", "<b class='p14'><center>Older than 110: check if correct.</b></center>");
  else {
    if (tfld<7) msg (stitle, "warn", "<b class='p14'><center>Bit young for this, aren't you?</b></center>");
  }
  return true;
};

function ValidDate (vfld,   // element to be validated
                    stitle,   // id of element to receive info/error msg
                    reqd)   // true if required
{
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var d = trim(vfld.value);
  var l = d.length;
  var a = new Array();
  a = d.split('/');
  var dl = a.length;
  if (l < 6 ) {
    msg (stitle, "error", "<b class='p14'><center>Date too short.</b></center>");
    setfocus(vfld);
    return false;
  }
  if (dl != 3) {
    msg (stitle, "error", "<b class='p14'><center>Bad format.</b></center>");
    setfocus(vfld);
    return false;
  }

  l = a[0].length;
  if ( l < 2)
    a[0] = "0" + a[0];

  l = a[1].length;
  if ( l < 2)
    a[1] = "0" + a[1];

  l = a[2].length;
  if ( l == 2)
    a[2] = "20" + a[2];

  x = a[0] + "/" + a[1] + "/" + a[2];
  vfld.value = x;

  mm = Number(a[0]);
  dd = Number(a[1]);
  yy = Number(a[2]);

  if ( isNaN(mm) ){
     msg (stitle, "error", "<b class='p14'><center>Month is not a number.</b></center>" );
     setfocus(vfld);
     return false;
  }

  if ( isNaN(dd) ){
     msg (stitle, "error", "<b class='p14'><center>Day is not a number.</b></center>" );
     setfocus(vfld);
     return false;
  }

  if ( isNaN(yy) ){
     msg (stitle, "error", "<b class='p14'><center>Year is not a number.</b></center>" );
     setfocus(vfld);
     return false;
  }

  if ( (mm < 1) || (mm > 12) ){
    msg (stitle, "error", "<b class='p14'><center>Month is invalid.</b></center>");
    setfocus(vfld);
    return false;
  }
  if ( (dd < 1) || (dd > 31) ){
    msg (stitle, "error", "<b class='p14'><center>Day is invalid.</b></center>");
    setfocus(vfld);
    return false;
  }
  if ( (yy < 1800) || (yy > 2030) ){
    msg (stitle, "error", "<b class='p14'><center>Year must be between 1800 and 2030.</b></center>");
    setfocus(vfld);
    return false;
  }
  return true;
};

function ValidYN (vfld,   // element to be validated
                  stitle,   // id of element to receive info/error msg
                  reqd)   // true if required
{
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var d = trim(vfld.value);

  var l = d.length;
  if (l < 1 ) {
    msg (stitle, "error", "<b class='p14'><center>Must not be blank.</b></center>");
    setfocus(vfld);
    return false;
  }

  d=d.toUpperCase();
  if ( !( (d == 'Y') || (d=='N') ) ){
    msg (stitle, "error", "<b class='p14'><center>Enter 'Y' or 'N'.</b></center>");
    setfocus(vfld);
    return false;
  }
  vfld.value = d;  

  return true;
};

function ValidAmount (vfld,   // element to be validated
                      stitle,   // id of element to receive info/error msg
                      reqd)   // true if required
{
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var d = trim(vfld.value);
  var l = d.length;
  var err=0;

  /* 	Strip dollar signs and commas if present before continuing  */
  for (var j=0; j<l; j++)
    if (d.charAt(j)=='$' || d.charAt(j)==',') err=1;

  if (err == 1) {
    d=d.replace(/\$/g,"");
    d=d.replace(/,/g,"");
    l = d.length;
    vfld.value = d;  
  }

  err=0;
  for (var j=0; j<l; j++)
    if ( (d.charAt(j)>='0' && d.charAt(j)<='9') || (d.charAt(j)=='.') );
    else {
      msg (stitle, "error", "<b class='p14'><center>Numbers or '.' only.</b></center>");
      setfocus(vfld);
      return false;
    }

  x=Number(d);
  if (x==0) {
    msg (stitle, "error", "<b class='p14'><center>Amount must be > 0.</b></center>");
    setfocus(vfld);
    return false;
  }
  return true;
};

function ValidYear (vfld,   // element to be validated
                    stitle,   // id of element to receive info/error msg
                    reqd)   // true if required
{
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var d = trim(vfld.value);
  var l = d.length;
  var err=0;

  /* 	Strip dollar signs and commas if present before continuing  */
  for (var j=0; j<l; j++)
    if (d.charAt(j)=='$' || d.charAt(j)==',') err=1;

  if (err == 1) {
    d=d.replace(/\$/g,"");
    d=d.replace(/,/g,"");
    l = d.length;
    vfld.value = d;  
  }

  err=0;
  for (var j=0; j<l; j++)
    if ( (d.charAt(j)>='0' && d.charAt(j)<='9') );
    else {
      msg (stitle, "error", "<b class='p14'><center>The year must contain numbers only.</b></center>");
      setfocus(vfld);
      return false;
    }

  if (d.length != 4) {
    msg (stitle, "error", "<b class='p14'><center>The year must contain only four digits.</b></center>");
    setfocus(vfld);
    return false;
  }

  x=Number(d);
  if (x < 1801) {
    msg (stitle, "error", "<b class='p14'><center>The year must be greater than 1800.</b></center>");
    setfocus(vfld);
    return false;
  }

  if (x > 2020) {
    msg (stitle, "error", "<b class='p14'><center>The year must be less than 2021.</b></center>");
    setfocus(vfld);
    return false;
  }

  return true;
};


function ValidYearLENow (vfld,    // element to be validated
                        stitle,   // id of element to receive info/error msg
                        reqd)     // true if required
{
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var now = new Date();
  var thisYear = now.getFullYear();

  var d = trim(vfld.value);
  var l = d.length;
  var err=0;

  /* 	Strip dollar signs and commas if present before continuing  */
  for (var j=0; j<l; j++)
    if (d.charAt(j)=='$' || d.charAt(j)==',') err=1;

  if (err == 1) {
    d=d.replace(/\$/g,"");
    d=d.replace(/,/g,"");
    l = d.length;
    vfld.value = d;  
  }

  err=0;
  for (var j=0; j<l; j++)
    if ( (d.charAt(j)>='0' && d.charAt(j)<='9') );
    else {
      msg (stitle, "error", "<b class='p14'><center>The year must contain numbers only.</b></center>");
      setfocus(vfld);
      return false;
    }

  if (d.length != 4) {
    msg (stitle, "error", "<b class='p14'><center>The year must contain only four digits.</b></center>");
    setfocus(vfld);
    return false;
  }

  x=Number(d);
  if (x < 1801) {
    msg (stitle, "error", "<b class='p14'><center>The year must be greater than 1800.</b></center>");
    setfocus(vfld);
    return false;
  }

  if (x > thisYear) {
    msg (stitle, "error", "<b class='p14'><center>The year must be less than or equal to this year.</b></center>");
    setfocus(vfld);
    return false;
  }

  return true;
};



function ValidZip (vfld,     // element to be validated
                   stitle,   // ititle for the alert box
                   reqd)     // true if required
{
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var d = trim(vfld.value);
  var l = d.length;
  var err=0;

  for (var j=0; j<l; j++)
    if ( (d.charAt(j)>='0' && d.charAt(j)<='9') );
    else {
      msg (stitle, "error", "<b class='p14'><center>The zip code must contain numbers only.</b></center>");
      setfocus(vfld);
      return false;
    }

  if (l != 5) {
    msg (stitle, "error", "<b class='p14'><center>The zip code must contain five digits.</b></center>");
    setfocus(vfld);
    return false;
  }
  return true;
};

function ValidRoutingNumber(vfld, stitle, reqd) {
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var sum = 0.0;
  var RoutingNumber = vfld.value;
  Sum =  (3 * parseInt(mid(RoutingNumber, 0, 1))) + (7 * parseInt(mid(RoutingNumber, 1, 1))) + parseInt(mid(RoutingNumber, 2, 1));
  Sum += (3 * parseInt(mid(RoutingNumber, 3, 1))) + (7 * parseInt(mid(RoutingNumber, 4, 1))) + parseInt(mid(RoutingNumber, 5, 1));
  Sum += (3 * parseInt(mid(RoutingNumber, 6, 1))) + (7 * parseInt(mid(RoutingNumber, 7, 1))) + parseInt(mid(RoutingNumber, 8, 1));
  if ((Sum % 10) == 0) return true;
  else {
    msg (stitle, "error", "<b class='p14'><center>The Routing Number is improperly formatted.</b></center>");
    setfocus(vfld);
    return false;
  }
}

function ValidCCExpiration(vfld, stitle, reqd) {
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var x = trim(vfld.value);
  var l = x.length;

  if (l != 5) {
    msg (stitle, "error", "<b class='p14'><center>Incorrect format (1).  Must be MM/YY - two-digit month, slash, two-digit year.</b></center>");
    setfocus(vfld);
    return false;
  }

  if (mid(x,2,1) != '/') {
    msg (stitle, "error", "<b class='p14'><center>Incorrect format (2).  Must be MM/YY - two-digit month, slash, two-digit year.</b></center>" + mid(x,2,1));
    setfocus(vfld);
    return false;
  }

  var d = new Date();
  var YY = right(d.getFullYear(),2) * 1;
  var MM = d.getMonth() + 1;
  var splitResult = vfld.value.split("/");
  var mm = splitResult[0];
  var yy = splitResult[1];

  if ((mm < 1) || (mm > 12)) {
    msg (stitle, "error", "<b class='p14'><center>The expiration month must be between 1 and 12.</b></center>");
    setfocus(vfld);
    return false;
  }

  if ((yy < YY) || (yy > (YY+5))) {
    msg (stitle, "error", "<b class='p14'><center>The expiration year must be between " + YY + " and " + (YY+5) + ".</b></center>");
    setfocus(vfld);
    return false;
  }

  if ((mm < MM) && (yy <= YY)) {
    msg (stitle, "error", "<b class='p14'><center>This card has already expired.</b></center>");
    setfocus(vfld);
    return false;
  }

  var cutoffMM = MM + 2;
  var cutoffYY = YY;
  if (cutoffMM > 12) {
    cutoffMM = 1;
    cutoffYY++;
  }
  cutoffYYMM = right((100 + cutoffYY),2).toString() + right((100 + cutoffMM),2);
  thisyymm = yy.toString() + right((100 + mm),2);

  if (cutoffYYMM >= thisyymm)
    msg (stitle, "error", "<b class='p14'><center>NOTICE: This card has almost expired.</b></center>");

  return true;
}

function ValidCCNumber(vfld, stitle, reqd) {
  var ccNumb = vfld.value;
  var valid = "0123456789" // Valid digits in a credit card number

  var sCCN = ccNumb.toString(); // string of ccNumb
  var len = ccNumb.length; // The length of the submitted cc number

  // Strip everything except numbers
  x = "";
  for (var j=0; j<len; j++) {
    temp = "" + sCCN.substring(j, j+1);
    if (valid.indexOf(temp) == "-1"){}
    else x = x + temp;
  }

  len = x.length;
  var iCCN = parseInt(x); // integer of ccNumb
  var iTotal = 0;         // integer total set at zero
  var bNum = true;        // by default assume it is a number
  var bResult = false;    // by default assume it is NOT a valid cc
  var temp;               // temp variable for parsing string
  var calc;               // used for calculation of each digit

  // First make sure the leading digits match an allowed pattern based on the length of the number.
  var ch1 = left(x,1);
  if ( len == 16 ) {      // could be MC, Visa, or Disc, 
    switch(ch1) {
      case '4':  // Visa
        bResult = true;
        break;
      case '5':  // MC  (51, 52, 53, 54, 55)
        var allowed = "51, 52, 53, 54, 55";
        var ch2 = left(x,2);
        if (allowed.indexOf(ch2) == "-1") bResult = false;
        else bResult = true;
        break;
      case '6':  // Discover (6011)
        if (left(x,4) == "6011") bResult = true;
        else bResult = false;
        break;
      default: bResult = false;
    }
  }

  if ( len == 15 ) {      // could be Amex (34, 37)
    switch(ch1) {
      case '3':  //Amex
        var allowed = "34, 37";
        var ch2 = left(x,2);
        if (allowed.indexOf(ch2) == "-1") bResult = false;
        else bResult = true;
        break;
      default: bResult = false;
    }
  }

  if ( len == 14 ) {      // could be Diner's Club / Carte Blanch
    switch(ch1) {
      case '3':  // Dinre's Club or Carte Blanche
        var allowed2 = "36, 38";
        var allowed3 = "301, 302, 303, 304, 305";
        var ch2 = left(x,2);
        var ch3 = left(x,3);
        if (allowed3.indexOf(ch3) != "-1") bResult = true;
        else 
          if (allowed2.indexOf(ch2) != "-1") bResult = true;
          else bResult = false;
        break;
      default: bResult = false;
    }
  }

  if ( len == 13 ) {      // could be Amex
    switch(ch1) {
      case '4':  //Visa
        bResult = true;
        break;
      default: bResult = false;
    }
  }

  // Determine if it is the proper length
  if (len == 0) {
    bResult = false;
  } 

  if ( bResult == true ) {            // only look further if we have passed the first digits test.
    if (len >= 15) {                  // 15 or 16 for Amex or V/MC
      for (var i=len; i>0; i--) {
        calc = parseInt(iCCN) % 10;   // right most digit
        calc = parseInt(calc);        // assure it is an integer
        iTotal += calc;               // running total of the card number as we loop - Do Nothing to first digit
        i--;                          // decrement the count - move to the next digit in the card
        iCCN = iCCN / 10;             // subtracts right most digit from ccNumb
        calc = parseInt(iCCN) % 10 ;  // NEXT right most digit
        calc = calc *2;               // multiply the digit by two

        // Instead of some screwy method of converting 16 to a string and then parsing 1 and 6 and then adding them to make 7,
        // I use a simple switch statement to change the value of calc2 to 7 if 16 is the multiple.
        switch(calc){
          case 10: calc = 1; break; //5*2=10 & 1+0 = 1
          case 12: calc = 3; break; //6*2=12 & 1+2 = 3
          case 14: calc = 5; break; //7*2=14 & 1+4 = 5
          case 16: calc = 7; break; //8*2=16 & 1+6 = 7
          case 18: calc = 9; break; //9*2=18 & 1+8 = 9
          default: calc = calc; //4*2= 8 & 8 = 8 -same for all lower numbers
        }
        iCCN = iCCN / 10; // subtracts right most digit from ccNum
        iTotal += calc; // running total of the card number as we loop
      }

      if ((iTotal%10)==0){ // check to see if the sum Mod 10 is zero
        bResult = true; // This IS (or could be) a valid credit card number.
      } 
      else {
        bResult = false; // This could NOT be a valid credit card number
      }
    }
  }

  // change alert to on-page display or other indication as needed.
  if (!bResult) {
    msg (stitle, "error", "<b class='p14'><center>The Credit Card Number is invalid.</b></center>");
    setfocus(vfld);
    return false;
  }

  return true;
}


function AppropriateContent(vfld, stitle, reqd) {
  var stat = commonCheck (vfld, stitle, reqd);
  if (stat != proceed) return stat;

  var d = trim(vfld.value);
  var l = d.length;

  if ( d.search(/http:/) != -1) {
    msg (stitle, "error", "<b class='p14'><center>Innapropriate content detected..</b></center>");
    setfocus(vfld);
    return false;
  }

  return true;
}

