// ----------------------------------------------------------------------
// 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 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
//
// Update Jun 2006: minor improvements to variable names and layout
// ----------------------------------------------------------------------

var nbsp = 160;		// non-breaking space char
var node_text = 3;	// DOM text node-type
var emptyString = /^\s*$/ ;
var global_valfield;	// retain valfield for timer thread

// --------------------------------------------
//                  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()
{
  global_valfield.focus();
}

function setfocus(valfield)
{
  // save valfield in global variable so value retained when routine exits
  global_valfield = valfield;
  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
{
	try {
		// 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
	}
	catch(err) {
		alert (message+"\r\nNo Info field found.\r\n"+err);
	}
}

// --------------------------------------------
//            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    (valfield,   // element to be validated
                         infofield,  // id of element to receive info/error msg
                         required)   // true if required
{
	try {
		if (!document.getElementById)
			return true;  // not available on this browser - leave validation to the server
		var elem = document.getElementById(infofield);
		if (!elem.firstChild) return true;  // not available on this browser
		if (elem.firstChild.nodeType != node_text) return true;  // infofield is wrong type of node

		if (emptyString.test(valfield.value)) {
			if (required) {
				msg (infofield, "error", "ERROR: required");
				setfocus(valfield);
				return false;
			}
			else {
				msg (infofield, "warn", "");   // OK
				return true;
			}
		}
		return proceed;
	}
	catch(err) {
		msg (infofield, "error", "ERROR: request to validate field failed.\r\n"+err);
		return false;
	}
}

// --------------------------------------------
//            validateNotEmpty
// Validate if something has been entered
// Returns true if so
// --------------------------------------------

function validateNotEmpty(valfield,   // element to be validated
                         infofield ) // id of element to receive info/error msg
{
  var stat = commonCheck (valfield, infofield, true);
  if (stat != proceed) return stat;

  msg (infofield, "warn", "");
  return true;
}

// --------------------------------------------
//            validateSize
// Validate if something has correct size
// Returns true if so
// --------------------------------------------

function validateSize(valfield,   // element to be validated
                         infofield, min_size, max_size ) // id of element to receive info/error msg
{
  var stat = commonCheck (valfield, infofield, true);
  if (stat != proceed) return stat;

  if (valfield.value.length < min_size) {
    msg (infofield, "error", "ERROR: field length must be a minimum of "+min_size+" characters");
    return false;
  }
  else if (valfield.value.length > max_size) {
    msg (infofield, "error", "ERROR: field length must be a maximum of "+max_size+" characters");
    return false;
  }
  else {
    msg(infofield,"warn","");
  }
  return true;
}

// --------------------------------------------
//            validateNumSize
// Validate if something has correct size
// Returns true if so
// --------------------------------------------

function validateNumSize(valfield,   // element to be validated
                         infofield, min_size, max_size ) // id of element to receive info/error msg
{
  var stat = commonCheck (valfield, infofield, true);
  if (stat != proceed) return stat;
  var str = /^[0-9\s\-]+$/  ;
  var tfld = trim(valfield.value);  // value of field with whitespace trimmed off

  if (valfield.value.length < min_size) {
    msg (infofield, "error", "ERROR: field length must be a minimum of "+min_size+" characters");
    return false;
  }
  else if (valfield.value.length > max_size) {
    msg (infofield, "error", "ERROR: field length must be a maximum of "+max_size+" characters");
    return false;
  }
  else if (!str.test(tfld)) {
    msg (infofield, "error", "ERROR: value must be numeric");
    return false;
  }
  else {
    msg(infofield,"warn","");
  }
  return true;
}


// --------------------------------------------
//               validateAlpha
// Validate if value is Alphabetic (spaces and periods ok)
// Returns true if so (and also if could not be executed because of old browser)
// --------------------------------------------

function validateAlpha  (valfield,   // element to be validated
                         infofield,  // id of element to receive info/error msg
                         required)   // true if required
{
  var stat = commonCheck (valfield, infofield, required);
  if (stat != proceed) return stat;

  var tfld = trim(valfield.value);  // value of field with whitespace trimmed off
  var str = /^[A-Za-z\s\.\-]+$/  ;
  if (!str.test(tfld))
    msg (infofield, "warn", "ERROR: value must be alphabetic (periods, hyphens, and blank spaces are Okay)");
  else
    msg (infofield, "warn", "");
  return true;
}

// --------------------------------------------
//               validateAlphaNum
// Validate if value is Alpha-Numeric (spaces and periods ok)
// Returns true if so (and also if could not be executed because of old browser)
// --------------------------------------------

function validateAlphaNum  (valfield,   // element to be validated
                         infofield,  // id of element to receive info/error msg
                         required)   // true if required
{
  var stat = commonCheck (valfield, infofield, required);
  if (stat != proceed) return stat;

  var tfld = trim(valfield.value);  // value of field with whitespace trimmed off
  var str = /^[0-9A-Za-z\s\.\-]+$/  ;
  if (!str.test(tfld))
    msg (infofield, "warn", "ERROR: value must be alpha-numeric (periods, hyphens, and blank spaces are Okay)");
  else
    msg (infofield, "warn", "");
  return true;
}


// --------------------------------------------
//               validateFloat
// Validate if value is Numeric Float with the specified number of decimal places
// Returns true if so (and also if could not be executed because of old browser)
// --------------------------------------------
function validateFloat  (valfield,   // element to be validated
			    infofield,  // id of element to receive info/error msg
			    places, // number of decimal places
			    required)   // true if required
{
	var stat = commonCheck (valfield, infofield, required);
	if (stat != proceed) return stat;
	
	var tfld = trim(valfield.value);  // value of field with whitespace trimmed off
	var str = eval("/^[0-9]+\.[0-9]{"+places+"}$/");
	if (!str.test(tfld)) {
		msg (infofield, "error", "ERROR: value must be numeric with "+places+" decimal places");
		return false;
	}
	else
		msg (infofield, "warn", "");
	return true;
}
			    
// --------------------------------------------
//               validateNumeric
// Validate if value is Numeric (spaces and hyphens ok)
// Returns true if so (and also if could not be executed because of old browser)
// --------------------------------------------

function validateNumeric  (valfield,   // element to be validated
                         infofield,  // id of element to receive info/error msg
                         required)   // true if required
{
  var stat = commonCheck (valfield, infofield, required);
  if (stat != proceed) return stat;

  var tfld = trim(valfield.value);  // value of field with whitespace trimmed off
  var str = /^[0-9\s\-]+$/  ;
  if (!str.test(tfld)) {
    msg (infofield, "error", "ERROR: value must be numeric (hyphens and blank spaces are Okay)");
    return false;
  }
  else
    msg (infofield, "warn", "");
  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  (valfield,   // element to be validated
                         infofield,  // id of element to receive info/error msg
                         required)   // true if required
{
  var stat = commonCheck (valfield, infofield, required);
  if (stat != proceed) return stat;

  var tfld = trim(valfield.value);  // value of field with whitespace trimmed off
  var email = /^[^@]+@[^@.]+\.[^@]*\w\w$/  ;
  if (!email.test(tfld)) {
    msg (infofield, "error", "ERROR: not a valid e-mail address");
    setfocus(valfield);
    return false;
  }

  var email2 = /^[A-Za-z][\w.-]+@\w[\w.-]+\.[\w.-]*[A-Za-z][A-Za-z]$/  ;
  if (!email2.test(tfld))
    msg (infofield, "warn", "Unusual e-mail address - check if correct");
  else
    msg (infofield, "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  (valfield,   // element to be validated
                         infofield,  // id of element to receive info/error msg
                         required)   // true if required
{
  var stat = commonCheck (valfield, infofield, required);
  if (stat != proceed) return stat;

  var tfld = trim(valfield.value);  // value of field with whitespace trimmed off
  var telnr = /^\+?[0-9 ()-]+[0-9]$/  ;
  if (!telnr.test(tfld)) {
    msg (infofield, "error", "ERROR: not a valid telephone number. Characters permitted are digits, space ()- and leading +");
    setfocus(valfield);
    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 (infofield, "error", "ERROR: " + numdigits + " digits - too short");
    setfocus(valfield);
    return false;
  }

  if (numdigits>14)
    msg (infofield, "warn", numdigits + " digits - check if correct");
  else {
    if (numdigits<10)
      msg (infofield, "warn", "Only " + numdigits + " digits - check if correct");
    else
      msg (infofield, "warn", "");
  }
  return true;
}

// --------------------------------------------
//             validateAge
// Validate person's age
// Returns true if OK
// --------------------------------------------

function validateAge    (valfield,   // element to be validated
                         infofield,  // id of element to receive info/error msg
                         required)   // true if required
{
  var stat = commonCheck (valfield, infofield, required);
  if (stat != proceed) return stat;

  var tfld = trim(valfield.value);
  var ageRE = /^[0-9]{1,3}$/
  if (!ageRE.test(tfld)) {
    msg (infofield, "error", "ERROR: not a valid age");
    setfocus(valfield);
    return false;
  }

  if (tfld>=200) {
    msg (infofield, "error", "ERROR: not a valid age");
    setfocus(valfield);
    return false;
  }

  if (tfld>110) msg (infofield, "warn", "Older than 110: check correct");
  else {
    if (tfld<7) msg (infofield, "warn", "Bit young for this, aren't you?");
    else        msg (infofield, "warn", "");
  }
  return true;
}

// --------------------------------------------
//               validateIPHost
// Validate if value is IPv4 Address or hostname
// (alpha, number, hyphen, underscore, and periods ok)
// Returns true if so (and also if could not be executed because of old browser)
// --------------------------------------------

function validateIPHost (valfield,   // element to be validated
                         infofield,  // id of element to receive info/error msg
                         required)   // true if required
{
  var stat = commonCheck (valfield, infofield, required);
  if (stat != proceed) return stat;

  var tfld = trim(valfield.value);  // value of field with whitespace trimmed off
  var str = /^[0-9A-Za-z_\.\-]+$/  ;
  if (!str.test(tfld))
    msg (infofield, "warn", "ERROR: value must be an IP address or Hostname (periods, hyphens, and underscores are Okay)");
  else
    msg (infofield, "warn", "");
  return true;
}

// --------------------------------------------
//               validateClean
// Make sure string doesn't contain any quotes or backtics
// Returns false if so, true otherwise
// --------------------------------------------

function validateClean (valfield,   // element to be validated
                         infofield,  // id of element to receive info/error msg
                         required)   // true if required
{
  var stat = commonCheck (valfield, infofield, required);
  if (stat != proceed) return stat;

  var tfld = trim(valfield.value);  // value of field with whitespace trimmed off
  //var str = /\;|#|\$|%|\^|&|\*|\(|\)|\+|\\|\/|\?|>|<|\{|\}|\,|\[|\]|\`|\|/;
  var str = /\$|\^|\\|\`|\|/;
  if (str.test(tfld)) {
  alert(tfld);
    msg (infofield, "warn", "ERROR: value may NOT contain any of the following characters $ ^ \\ ` |");
    return false;
  }
  else {
    msg (infofield, "warn", "");
    return true;
  }
}

/*
Purpose: return true if the date is valid, false otherwise
Arguments: day integer representing day of monthmonth integer representing month of yearyear integer representing year
Variables: dteDate - date object
*/
function validateDayMonthYear(infofield,day,month,year)
{
	//set up a Date object based on the day, month and year arguments
	//javascript months start at 0 (0-11 instead of 1-12)
	month -= 1;
	var dteDate = new Date(year,month,day);
	
	/*
	   Javascript Dates are a little too forgiving and will change the date to a reasonable guess if it's invalid. 
  	   We'll use this to our advantage by creating the date object and then comparing it to the details we put it. 
	   If the Date object is different, then it must have been an invalid date to start with...
	*/

	if ((day==dteDate.getDate()) && (month==dteDate.getMonth()) && (year==dteDate.getFullYear())) {
		msg (infofield, "warn", "");
		return true;
	}
	else {
		msg (infofield, "error", "ERROR: invalid date.");
		return false;
	}
}
