// ----------------------------------------------------------------------
// 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.
// ----------------------------------------------------------------------
emptyString = /^\s*$/

// -----------------------------------------
//                  trim
// Trim leading/trailing whitespace off string
// -----------------------------------------

function trim(str)
{
  return str.replace(/^\s+|\s+$/g, '')
};

// -----------------------------------------
// Function   : isEmpty
// Description: Validates that a string is not ' '
// Arguments  : string
// Returns    : Boolean. TRUE or FALSE
// -----------------------------------------
function isEmpty(sValue) 
{
 empty = true
 for (z=0; z < sValue.length;z++) 
  {
    tempStr = sValue
    str = tempStr.substr(z, 1)
     if (str != ' ') 
    {
      empty = false
    }
  }
   return empty	
}

// -----------------------------------------
// Function   : isNumber
// Description: Validates that value passed is a number
// Arguments  : value
// Returns    : Boolean. TRUE or FALSE
// -----------------------------------------
function isNumber(value) {
  if (isNaN(value)) {
    return false
  }
  return true
}


// -----------------------------------------
// Function   : isPosInt
// Description: Validates if a string is a positive int
// Arguments  : string
// Returns    : Boolean. TRUE or FALSE
// -----------------------------------------
function isPosInt(stringValue) {
  var inputValue = parseInt(stringValue)
  if (! isNumber(inputValue) ) {
    return false
  }

  for (var i =0; i < stringValue.length; i++) {
    var oneChar = stringValue.charAt(i)
    if (oneChar <"0" || oneChar > "9") {
      return false
    }
  }

  return true
}


// -----------------------------------------
// 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 slip in an nbsp if needed
  var dispmessage;
  if (emptyString.test(message)) 
    dispmessage = "&nbsp;";    
  else  
    dispmessage = message;

  var elem = document.getElementById(fld);
  if (elem.firstChild && (elem.firstChild.nodeType == 1))
    elem.firstChild.nodeValue = dispmessage;  // DOM method
  else 
    elem.innerHTML = dispmessage; // IE method
  
  elem.className = msgtype;
};

// -----------------------------------------
// 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 && (typeof elem.innerHTML != "string"))
    return true;  // not available on this browser 

  if (emptyString.test(vfld.value)) {
    if (reqd) {
      msg (ifld, "error", "Entry required.");  
      vfld.focus();
      return false;
    }
    else {
      msg (ifld, "warn", "");   // OK
      return true;  
    }
  }
  return proceed;
}

// -----------------------------------------
// validateIsNotBlank
// Validate if something has been entered
// Returns true if so 
// -----------------------------------------
function validateIsNotBlank(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;
};

// -----------------------------------------
// isValidEmail
// Validate if entered email address is valid
// Returns true if so 
// -----------------------------------------
function isValidEmail(emailStr) {
	/* The following variable tells the rest of the function whether or not
	to verify that the address ends in a two-letter country or well-known
	TLD.  1 means check it, 0 means don't. */
	var checkTLD=1;

	/* The following is the list of known TLDs that an e-mail address must end with. */
	var knownDomsPat=/^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum)$/;

	/* The following pattern is used to check if the entered e-mail address
	fits the user@domain format.  It also is used to separate the username
	from the domain. */
	var emailPat=/^(.+)@(.+)$/;

	/* The following string represents the pattern for matching all special
	characters.  We don't want to allow special characters in the address. 
	These characters include ( ) < > @ , ; : \ " . [ ] */
	var specialChars="\\(\\)><@,;:\\\\\\\"\\.\\[\\]";

	/* The following string represents the range of characters allowed in a 
	username or domainname.  It really states which chars aren't allowed.*/
	var validChars="\[^\\s" + specialChars + "\]";

	/* The following pattern applies if the "user" is a quoted string (in
	which case, there are no rules about which characters are allowed
	and which aren't; anything goes).  E.g. "jiminy cricket"@disney.com
	is a legal e-mail address. */
	var quotedUser="(\"[^\"]*\")";

	/* The following pattern applies for domains that are IP addresses,
	rather than symbolic names.  E.g. joe@[123.124.233.4] is a legal
	e-mail address. NOTE: The square brackets are required. */
	var ipDomainPat=/^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;

	/* The following string represents an atom (basically a series of non-special characters.) */
	var atom=validChars + '+';

	/* The following string represents one word in the typical username.
	For example, in john.doe@somewhere.com, john and doe are words.
	Basically, a word is either an atom or quoted string. */
	var word="(" + atom + "|" + quotedUser + ")";

	// The following pattern describes the structure of the user
	var userPat=new RegExp("^" + word + "(\\." + word + ")*$");

	/* The following pattern describes the structure of a normal symbolic
	domain, as opposed to ipDomainPat, shown above. */
	var domainPat=new RegExp("^" + atom + "(\\." + atom +")*$");

	/* Finally, let's start trying to figure out if the supplied address is valid. */

	/* Begin with the coarse pattern to simply break up user@domain into
	different pieces that are easy to analyze. */
	var matchArray=emailStr.match(emailPat);

	if (matchArray==null) {
		/* Too many/few @'s or something; basically, this address doesn't even fit the 
			general mould of a valid e-mail address. */
		return false;
	}
	var user=matchArray[1];
	var domain=matchArray[2];

	// Start by checking that only basic ASCII characters are in the strings (0-127).
	for (i=0; i<user.length; i++) {
		if (user.charCodeAt(i)>127) {
			//alert("Ths username contains invalid characters.");
			return false;
	   }
	}
	for (i=0; i<domain.length; i++) {
		if (domain.charCodeAt(i)>127) {
			//alert("Ths domain name contains invalid characters.");
			return false;
	   }
	}

	// See if "user" is valid 

	if (user.match(userPat)==null) {
		// user is not valid
		//alert("The username doesn't seem to be valid.");
		return false;
	}

	/* if the e-mail address is at an IP address (as opposed to a symbolic
	host name) make sure the IP address is valid. */
	var IPArray=domain.match(ipDomainPat);
	if (IPArray!=null) {
		// this is an IP address

		for (var i=1;i<=4;i++) {
			if (IPArray[i]>255) {
				//alert("Destination IP address is invalid!");
				return false;
			}
		}
		return true;
	}
	
	// Domain is symbolic name.  Check if it's valid.
	var atomPat=new RegExp("^" + atom + "$");
	var domArr=domain.split(".");
	var len=domArr.length;
	for (i=0;i<len;i++) {
	if (domArr[i].search(atomPat)==-1) {
		//alert("The domain name does not seem to be valid.");
		return false;
	   }
	}

	/* domain name seems valid, but now make sure that it ends in a
	known top-level domain (like com, edu, gov) or a two-letter word,
	representing country (uk, nl), and that there's a hostname preceding 
	the domain or country. */
	if (checkTLD && domArr[domArr.length-1].length!=2 && domArr[domArr.length-1].search(knownDomsPat)==-1) {
			//alert("The address must end in a well-known domain or two letter " + "country.");
			return false;
	}
	// Make sure there's a host name preceding the domain.
	if (len<2) {
		//alert("This address is missing a hostname!");
		return false;
	}
	// If we've gotten this far, everything's valid!
	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
  if (!isValidEmail(tfld)) {
    msg (ifld, "error", "Not a valid e-mail address");
    vfld.focus();
    return false;
  }
  msg (ifld, "warn", ""); 
  return true;
};

// -----------------------------------------
// validateCheckboxGroup
// Validate if e-mail address
// Returns true if so (and also if could not be executed because of old browser)
// -----------------------------------------
function validateCheckBoxGroup(chkbxgrp,  // name of checkbox group to validate
                               ifld, // id of element to receive info/error msg
							   reqd) // true if required
{
	var isChecked = false;
	// Use the length property to iterate through each Checkbox
	// to determine if a selection has been made
	for (var counter=0; counter<chkbxgrp.length; counter++)
		if (chkbxgrp[counter].checked == true) 
		   isChecked = true;

	if (isChecked == false)
	{
		 msg (ifld, "error", "Please select at least one option."); 
		 return false;
	}

	msg (ifld, "warn", "");   // OK
	return true;
}


// -----------------------------------------
// validatePhone
// 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 validatePhone  (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 ()-]+[0-9]$/
  if (!telnr.test(tfld)) {
    msg (ifld, "error", "Not a valid telephone number. Characters permitted are digits, space ()- and leading +");
    vfld.focus();
    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", numdigits + " digits - too short");
    vfld.focus();
    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", "Not a valid age");
    vfld.focus();
    return false;
  }

  if (tfld>=200) {
    msg (ifld, "error", "Not a valid age");
    vfld.focus();
    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;
};

// -----------------------------------------
// Function   : validateDate
// Description: Validates if a string is a valid date
// Returns    : Boolean. TRUE or FALSE
// -----------------------------------------
function validateDate    (vfld,   // element to be validated
                         ifld,   // id of element to receive info/error msg
                         reqd)   // true if required
{
  var stat = commonCheck (vfld, ifld, reqd);
  var sErr = ""
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);
  msg (ifld, "warn", "");
  if (!isDateMMddyyyy(tfld,ifld)) {
    vfld.focus();
    return false;
  }
  msg (ifld, "warn", "");
  return true;
};


// -----------------------------------------
// Function   : isDateMMddyyyy
// Description: Validates if a date is mm/dd/yyyy format
// Arguments  : string
// Returns    : Boolean. TRUE or FALSE
// -----------------------------------------
function isDateMMddyyyy(sDate,ifld) {

  // Check if string is empty... if so then simply return true
  if (isEmpty(sDate)) return true;

  // first check out the delimiters
  var delim1 = sDate.indexOf("/");
  var delim2 = sDate.lastIndexOf("/");
  if (delim1 == -1 || delim1 == delim2)
  {
	msg (ifld, "error", "Not a valid date, no delimiters. Date must be entered in mm/dd/yyyy format");  
    return false;
  }

  // delimiters found checking out day, month and year
  var mm = sDate.substring(0,delim1)
  if (mm.substring(0,1) == "0")
  {
	mm = mm.substring(1,2)
  }	
        
  var dd = sDate.substring(delim1+1,delim2)
  if (dd.substring(0,1) == "0")
  {
	dd = dd.substring(1,2);
  }	

  var yyyy =  sDate.substring(delim2+1,sDate.length);
  // make sure that the year is 4 digits
  if (yyyy.length != 4) {
	msg (ifld, "error", "Invalid date, year must be 4 characters long.");   
    return false;
  }
    
  if ( !isPosInt(yyyy) ) {
    msg (ifld, "error", "Invalid date, year is not valid.");   
    return false;
  }

  
  var yyyyNum = parseInt(yyyy);
  // Check that month is a valid integer
  if ( !isPosInt(mm) ) {
	msg (ifld, "error", "Invalid date, month is not valid"); 	  
    return false;
  }

  // Check that month is between 1 and 12
  var mmNum = parseInt(mm);

  if ( mmNum <1 || mmNum >12 ) {
	msg (ifld, "error", "Invalid date, month needs to be between 1 and 12"); 
    return false;
  }

  // Check that day is a valid integer
  if ( !isPosInt(dd) ) {
	msg (ifld, "error", "Invalid date, day is not valid");	  
    return false
  }

  // Check that day is between 1 and 31
  var ddNum = parseInt(dd)
  if ( ddNum <1 || ddNum >31 ) {
	msg (ifld, "error", "Invalid date, day range must be between 1 and 31.");	  
    return false
  }

  // Check that date entered is valid of month provided
  if ((mm==4 || mm==6 || mm==9 || mm==11 ) && dd >30 ) {
	msg (ifld, "error", "Invalid date, month can have only 30 days.");
    return false
  }

  // Perform leap year calculations to determine valide date
  // for February
  if (mmNum == 2 && ddNum > 29 ) {
	msg (ifld, "error", "Inalid date, February can not have more than 29 days.");		  
    return false
  }

  if (mmNum == 2 && yyyyNum % 4 > 0 && ddNum > 28 ) {
	msg (ifld, "error", "Inalid date, February of this year can not have more than 28 days.");	  
    return false
  }
  return true
}
