// ----------------------------------------------------------------------
// 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
// ----------------------------------------------------------------------

var nbsp = 160;    // non-breaking space char
var node_text = 3; // DOM text node-type
var emptyString = /^\s*$/
var glb_vfld;      // retain vfld for timer thread

var USStateCodes = "AL|AK|AS|AZ|AR|CA|CO|CT|DE|DC|FM|FL|GA|GU|HI|ID|IL|IN|IA|KS|KY|LA|ME|MH|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|MP|OH|OK|OR|PW|PA|PR|RI|SC|SD|TN|TX|UT|VT|VI|VA|WA|WV|WI|WY|AE|AA|AE|AE|AP";

// -----------------------------------------
//                  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(fld,     // id of element to display message in
             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)) 
    dispmessage = String.fromCharCode(nbsp);    
  else  
    dispmessage = message;

  var elem = document.getElementById(fld);
  elem.firstChild.nodeValue = dispmessage;  
  
  elem.className = msgtype;   // set the CSS class to adjust appearance of 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
                         ifld,   // id of element to receive info/error msg
                         reqd)   // true if required
{
  if (!document.getElementById) 
    return true;  // not available on this browser - leave validation to the server
  var elem = document.getElementById(ifld);
  if (!elem.firstChild)
    return true;  // not available on this browser 
  if (elem.firstChild.nodeType != node_text)
    return true;  // ifld is wrong type of node  

  if (emptyString.test(vfld.value)) {
    if (reqd) {
      msg (ifld, "error", "ERROR: required");  
      setfocus(vfld);
      return false;
    }
    else {
      msg (ifld, "warn", "");   // OK
      return true;  
    }
  }
  return proceed;
}

// -----------------------------------------
//            validatePresent
// Validate if something has been entered
// Returns true if so 
// -----------------------------------------

function validatePresent(vfld,   // element to be validated
                         ifld )  // id of element to receive info/error msg
{
  var stat = commonCheck (vfld, ifld, true);
  if (stat != proceed) return stat;

  msg (ifld, "warn", "");  
  return true;
};

function resetErrors(iflds)
{
 var i;
 for (i = 0; i < iflds.length; i++ )
 {
  msg(iflds[i], "warn", "");
 }
 return true;
}

function validateZIP(vfld,   // element to be validated
                     ifld,   // id of element to receive info/error msg
                     reqd)   // true if required
{
  msg(ifld, "warn", "");                         // Remove any old error messages
  var stat = commonCheck (vfld, ifld, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);
  var tmp = tfld;
  tfld = "";
  var pass = true;
  for (i = 0; i < tmp.length; i++ )    
  {
   if ((tmp.charAt(i) != " ") && (tmp.charAt(i) != "-")) { // remove valid non-digit chars
    if ((tmp.charAt(i) >= "0") || (tmp.charAt(i) <= "9")) { // check all other chars
     tfld += tmp.charAt(i);
    } else { pass = false; }
   }
  }
  if (!((tfld.length == 5) || (tfld.length == 9))) {  // Ensure the proper length
   pass = false;
  }
  if (!pass) {
   msg(ifld, "error", "ERROR: not a valid ZIP code.  Must be 5 or 9 digits long, not including spaces and dashes.");
   setfocus(vfld);
   return false;
  }
  return true;
};

function validateState(vfld,   // element to be validated
                       ifld,   // id of element to receive info/error msg
                       reqd)   // true if required
{
  msg(ifld, "warn", "");                         // Remove any old error messages
  var stat = commonCheck (vfld, ifld, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);
  if (((tfld.length == 1) ||                    // If the state code is only 1 letter
       (USStateCodes.indexOf(tfld.toUpperCase()) == -1)) &&  // or if it isn't a valid state code
      (tfld.indexOf("|") == -1)) {              // and it isn't just the "|" symbol
    msg(ifld, "error", "ERROR: not a valid state code");
    setfocus(vfld);
    return false;
  }
  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
                         ifld,   // id of element to receive info/error msg
                         reqd)   // true if required
{
  var stat = commonCheck (vfld, ifld, 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 (ifld, "error", "ERROR: not a valid e-mail address");
    setfocus(vfld);
    return false;
  }

  var email2 = /^[A-Za-z][\w.-]+@\w[\w.-]+\.[\w.-]*[A-Za-z][A-Za-z]$/
  if (!email2.test(tfld)) 
    msg (ifld, "warn", "Unusual e-mail address - check if correct");
  else
    msg (ifld, "warn", "");
  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
                         ifld,   // id of element to receive info/error msg
                         reqd)   // true if required
{
  var stat = commonCheck (vfld, ifld, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);  // value of field with whitespace trimmed off
  var telnr = /^\+?[0-9 Xx()-]+[0-9]$/
  if (!telnr.test(tfld)) {
    msg (ifld, "error", "ERROR: not a valid telephone number. Characters permitted are digits, space x()- and leading +");
    setfocus(vfld);
    return false;
  }

  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 (ifld, "error", "ERROR: " + numdigits + " digits - too short");
    setfocus(vfld);
    return false;
  }

  if (numdigits>14)
    msg (ifld, "warn", numdigits + " digits - check if correct");
  else { 
    if (numdigits<10)
      msg (ifld, "warn", "Only " + numdigits + " digits - check if correct");
    else
      msg (ifld, "warn", "");
  }
  return true;
};

// -----------------------------------------
//             validateAge
// Validate person's age
// Returns true if OK 
// -----------------------------------------

function validateAge    (vfld,   // element to be validated
                         ifld,   // id of element to receive info/error msg
                         reqd)   // true if required
{
  var stat = commonCheck (vfld, ifld, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);
  var ageRE = /^[0-9]{1,3}$/
  if (!ageRE.test(tfld)) {
    msg (ifld, "error", "ERROR: not a valid age");
    setfocus(vfld);
    return false;
  }

  if (tfld>=200) {
    msg (ifld, "error", "ERROR: not a valid age");
    setfocus(vfld);
    return false;
  }

  if (tfld>110) msg (ifld, "warn", "Older than 110: check correct");
  else {
    if (tfld<7) msg (ifld, "warn", "Bit young for this, aren't you?");
    else        msg (ifld, "warn", "");
  }
  return true;
};

// -----------------------------------------
//            commonCheck2
// Common code for checkbox validation routines to
// check for older / less-equipped browsers
// Returns true (validation passed) or
//         proceed (don't know yet)
// -----------------------------------------

var proceed = 2;  

function commonCheck2   (vfld,   // element to be validated
                         ifld)   // id of element to receive info/error msg
{
  if (!document.getElementById) 
    return true;  // not available on this browser - leave validation to the server
  var elem = document.getElementById(ifld);
  if (!elem.firstChild)
    return true;  // not available on this browser 
  if (elem.firstChild.nodeType != node_text)
    return true;  // ifld is wrong type of node  

  msg (ifld, "warn", "");  // clear any previous error message
  return proceed;
}



// -----------------------------------------
//            validateCheckbox
// Validate that the correct number of checkboxes has been checked.
// Returns true if valid (and also if could not be executed because 
// of old browser)
// -----------------------------------------

function validateCheckbox  (vfld,   // checkboxes to be validated
                            ifld,   // id of element to receive info/error msg
                            nr,     // number of checkboxes to be checked. >=1
                            cond)   // condition: -1 = less than or equal to nr
                                    //             0 = equal to nr (default)
                                    //             1 = greater than or equal to nr
{
  if (!nr || nr<1) {
    alert('Programming error in validateCheckbox: nr<2'); 
       // for nr=1 use radio buttons or validateConfirm
    return true;
  }
  if (!cond) cond = 0;

  var stat = commonCheck2(vfld, ifld);
  if (stat != proceed) return stat;

  // count how many boxes have been checked by the reader
  var count = 0;
  for (var j=0; j<vfld.length; j++)
     if (vfld[j].checked) count++;

  if (count==nr) return true;
  if (count<nr && cond==-1) return true;
  if (count>nr && cond==1)  return true;

  // if we get here then the validation has failed

  var suffix='';
  if (count>1) suffix='es';

  var errorMsg;

  if (count<nr) {
   if (cond == 1) {
   errorMsg = 'Only ' + count + ' box' + suffix + ' checked: at least ' + nr + ' required';
   }
   else
    errorMsg = 'Only ' + count + ' box' + suffix + ' checked: exactly ' + nr + ' required';
  }
  if (count>nr) {
   if (cond == -1) {
    errorMsg = '' + count + ' boxes checked: maximum ' + nr + ' allowed';
   }
   else
    errorMsg = '' + count + ' boxes checked: exactly ' + nr + ' allowed';
  }
  if (count==0) {
   if (cond != 0) {
    errorMsg = 'No boxes checked: at least ' + nr + ' required';
   }
   else
    errorMsg = 'No boxes checked: exactly ' + nr + ' required';
  }

  msg (ifld, "error", errorMsg);
  return false;
}


// -----------------------------------------
//            validateConfirm 
// Usually one doesn't want to validate if 1 checkbox of a set has been
// checked, because in this case one would use radio buttons instead.
// But sometimes one wants a reader to check a single box to confirm that 
// he or she agrees to something. That is covered by this routine.
//
// Returns true if valid (and also if could not be executed because 
			      // of old browser)
// -----------------------------------------

function validateConfirm   (vfld,   // checkbox to be validated
                            ifld)   // id of element to receive info/error msg
{
  var stat = commonCheck2(vfld, ifld);
  if (stat != proceed) return stat;

  if (vfld.checked) return true;

  // if we get here then the validation has failed

  var errorMsg = 'Please read the above message and confirm you agree to it';

  msg (ifld, "error", errorMsg);
  return false;
}


/*
///////////////////////
To look right, this requires:
///////////////////////
   <style type="text/css">
      @import url(formval.css);
   </style>


///////////////////////
formval.css:
///////////////////////

  .info   { color: black; background-color: transparent; font-weight: normal; }
  .warn   { color: rgb(120,0,0); background-color: transparent; font-weight: normal; }
  .error  { color: red; background-color: transparent; font-weight: bold }

  .formtab TD      
          { padding: 0.2em; height: 2.5em; vertical-align: top; font-size: 90%; 
}


  .checkcl    { text-align: right; }
  .checkbox   { clear: right; float: right; margin: 0 0.5em; }
  .formtab2 TD      
          { padding: 0.2em; height: 2.5em; vertical-align: top;  }

///////////////////////
Examples of using these validations:
///////////////////////
<NOSCRIPT>
   <P> Javascript is not currently enabled on your browser. Please enable it to use this form. </P>
</NOSCRIPT>

<SCRIPT TYPE="text/javascript">
 Only script specific to this form goes here.
 General-purpose routines are in a separate file.
  function RemoveErrors() {
   var taglist = new Array('inf_email','inf_nightphone','inf_dayphone','inf_zipcode','inf_state','inf_city','inf_address','inf_lastname','inf_firstname','inf_interested');
   resetErrors(taglist);
  }
    
  function validateOnSubmit(form) {
    var elem;
    var errs=0;
    // execute all element validations in reverse order, so focus gets
    // set to the first one in error.
    if (!validateEmail   (document.forms.inquiry.email,      'inf_email',       true))  errs += 1; 
    if (!validateTelnr   (document.forms.inquiry.nightphone, 'inf_nightphone',  false)) errs += 1; 
    if (!validateTelnr   (document.forms.inquiry.dayphone,   'inf_dayphone',    true))  errs += 1; 
    if (!validateZIP     (document.forms.inquiry.zipcode,    'inf_zipcode'))             errs += 1; 
    if (!validateState   (document.forms.inquiry.state,      'inf_state'))               errs += 1; 
    if (!validatePresent (document.forms.inquiry.city,       'inf_city'))                errs += 1; 
    if (!validatePresent (document.forms.inquiry.address,    'inf_address'))             errs += 1; 
    if (!validatePresent (document.forms.inquiry.lastname,   'inf_lastname'))            errs += 1; 
    if (!validatePresent (document.forms.inquiry.firstname,  'inf_firstname'))           errs += 1;
    if (!validateCheckbox(document.forms.inquiry.elements['interested[]'], 'inf_interested', 1, 1))    errs += 1;


    if (errs>1)  alert('There are fields which need correction before sending');
    if (errs==1) alert('There is a field which needs correction before sending');

    if (errs==0) form.submit();

    return (errs==0);
  };
</SCRIPT>
<form method="POST" name="inquiry" action="qwe.hmtl">
 <table>
  <tr>
   <TD class="style45">
    <input type="checkbox" name="interested[]" value="Private lessons with Academy Director, Dave Seeman"/>
   </TD>
   <TD>Private lessons with Academy Director, Dave Seeman</TD>
  </tr>
  <tr>
   <TD class="style45">
    <input type="checkbox" name="interested[]" value="Private lessons with Academy Instructor, Spencer Graham"/>
   </TD>
   <TD>Private lessons with Academy Instructor, Spencer Graham</TD>
  </tr>
  <tr>
   <TD>First Name:</TD>
   <TD><input type="TEXT" name="firstname" id="firstname" size="20" maxlength="40" onChange="validatePresent(this, 'inf_firstname');" /></TD>
   <TD id="inf_firstname">&nbsp;</TD>
  </tr>
...
  <tr>
   <TD>&nbsp;</TD>
   <TD>
    <table width="100%">
     <tr>
      <td align="left"><input type="button" value="Submit" onClick="validateOnSubmit(this.form)" /></td>
      <td align="right"><input type="reset" onClick="RemoveErrors()" /></td>
     </tr>
    </table>
   </TD>
  </tr>
 </table>
</form>
*/

