//
// auto form validator
//
// Author : Tony Williams
//
// (c) 2008 Nissimo - provided AS-IS
//
// www.nissimo.co.uk
//


var errors = Array();  // array of errors 

//
// get an element in the document, by its ID
//
function getElement(psID) {
  if(document.all) {
    return document.all[psID];
  } else {
    return document.getElementById(psID);
  }
}

//
// get op-codes from the control name
//
// in : n = name string
//      ops = array of ops to push onto
//
// out : n = opcode removed from start of string
//
function parseCode(n, ops)
{
  switch(n.charAt(1)) {
    case 'v':
    case 'u':
      ops.push(n.substr(0, 5))
      n = n.substr(5)
      break

    default:  // not a known code, just skip the underscore for the caller
      n = n.substr(1)
      break
  }

  return n
}

//
// add an error
//
function addError(n, s, e)
{
  while(n.indexOf('-') >= 0) {
    n = n.replace('-', ' ') // '-' is used as a spacer in element names, which shouldn't contain spaces
  }
  while(n.indexOf('_') >= 0) {
    n = n.replace('_', ' ') // '_' is used as a spacer in element names, which shouldn't contain spaces
  }
  msg = (s != null) ? n + s : "Please Enter " + n
  errors.push(msg)
  e.focus()
}

//
// validate text
//
function validateText(n, e)
{
  // must be alphanumeric
  var alnum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ";

  if(e.value == null || e.value == "") {
    addError(n, null, e);
    return false;
  }

  for(var i = 0; i < e.value.length; i++) {
    if(alnum.indexOf(e.value.charAt(i)) < 0) {
      addError(n, " Must contain only A-Z, 0-9 or space", e);
      return false;
    }
  }

  return true;
}

//
// validate selection
//
function validateSelection(n, e)
{
  if(e.value == null || e.value == "") {
    addError(n, null, e);
    e.focus();
    return false;
  }

  return true;
}

//
// validate text - with punctuation allowed in it
//
function validateDesc(n, e)
{
  // must be alphanumeric
  var alnum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz .,;:\\/!\"'£$()+-_<>=\%\r\n";

  if(e.value == null || e.value == "") {
    addError(n, null, e);
    return false;
  }

  for(var i = 0; i < e.value.length; i++) {
    if(alnum.indexOf(e.value.charAt(i)) < 0) {
      addError(n, " Must contain only A-Z, 0-9 or punctuation", e);
      return false;
    }
  }

  return true;
}

//
// validate filename
//
function validateFile(n, e)
{
  // must be alphanumeric
  var alnum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz _-.\\/:";

  if(e.value == null || e.value == "") {
    addError(n, " Must be selected using the Browse button", e);
    return false;
  }

  for(var i = 0; i < e.value.length; i++) {
    if(alnum.indexOf(e.value.charAt(i)) < 0) {
      addError(n, " Must contain only A-Z, 0-9, _, -, ., \\, / or space", e);
      return false;
    }
  }

  return true;
}

//
// validate number
//
function validateNumber(n, e)
{
  // must be all digits 0-9
  var nums = "0123456789";

  if(e.value == null || e.value == "") {
    addError(n, null, e);
    return false;
  }

  for(var i = 0; i < e.value.length; i++) {
    if(nums.indexOf(e.value.charAt(i)) < 0) {
      addError(n, " Must contain only 0-9", e);
      return false;
    }
  }

  return true;
}

//
// validate phone number
//
function validatePhoneNumber(n, e)
{
  // must be all digits 0-9 or space (+ can only be first characetr)
  var nums = "0123456789 +";

  if(e.value == null || e.value == "") {
    addError(n, null, e);
    return false;
  }

  for(var i = 0; i < e.value.length; i++) {
    if(nums.indexOf(e.value.charAt(i)) < 0) {
      addError(n, " Must contain a valid phone number", e);
      return false;
    }
  }

  return true;
}

//
// validate a price in a string
//
function validPrice(str)
{
  var nums = "0123456789";
  var i = str.length - 1;

  if(i < 3) {
    return false;
  }

  // last char should be a number 
  if(nums.indexOf(str.charAt(i)) < 0) {
    return false;
  }
  
  // second-last char should be a number
  i--;
  if(nums.indexOf(str.charAt(i)) < 0) {
    return false;
  }

  // now a decimal point
  i--;
  if(str.charAt(i) != '.') {
    return false;
  }

  for (i--; i >= 0 ; i--) {
    if(nums.indexOf(str.charAt(i)) < 0) {
      return false;
    }
  }

  return true;
}

//
// validate decimal number as a price
//
function validatePrice(n, e)
{
  if(e.value == null || e.value == "") {
    addError(n, null, e);
    return false;
  }

  if(!validPrice(e.value)) {
    addError(n, " Must be a valid price - e.g. 2.05 ", e);
    return false;
  }

  return true;
}

//
// validate email address
//
function validateEmail(n, e)
{
  var str = e.value;
	var iat = str.indexOf('@');     // index of '@' char
	var ub  = str.length - 1;       // upper bound of the string
	var idt = str.lastIndexOf('.'); // index of last '.' char

  if(ub < 6) {
    addError(n, null, e);
    return false;
  }

  var retval = true;

	if (iat <= 0 || iat >= ub)          { retval = false; }   // fail if no '@' char
	if (idt <= 0 || idt >= ub)          { retval = false; }   // fail if no '.'
  if (str.indexOf('@',(iat+1)) != -1) { retval = false; }   // fail if two 'a' chars
  if (idt < iat)                      { retval = false; }   // fail if last '.' before the '@'
	if (str.indexOf(" ") != -1)         { retval = false; }   // fail if contains space char
  if ((ub - idt) > 4)                 { retval = false; }   // fail if last section is more than four chars

  if(!retval) { addError(n, " Must be a valid email address", e); }
  return retval;
}

//
// validate a URL
//
function validateURL(n, e)
{
  var data = e.value

  // SCHEME
  var urlregex = "^(https?|ftp):\/\/"

  // USER AND PASS (optional)
  urlregex = urlregex + "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?"

  // HOSTNAME OR IP
  //urlregex = urlregex + "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*";  // http://x = allowed (ex. http://localhost, http://routerlogin)
  //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)+";  // http://x.x = minimum
  urlregex = urlregex + "([a-z0-9+\$_-]+\.)*[a-z0-9+\$_-]{2,3}";  // http://x.xx(x) = minimum
  //use only one of the above

  // PORT (optional)
  urlregex = urlregex + "(\:[0-9]{2,5})?"
  // PATH  (optional)
  urlregex = urlregex + "(\/([a-z0-9+\$_-]\.?)+)*\/?"
  // GET Query (optional)
  //urlregex = urlregex + "(\?[a-z+&\$_.-][a-z0-9;:@/&%=+\$_.-]*)?"
  // ANCHOR (optional)
  //urlregex = urlregex + "(#[a-z_.-][a-z0-9+\$_.-]*)?\$"

  // check
  var regex = new RegExp(urlregex)
  var retval = regex.test(data)

  if(!retval) { addError(n, " Must be a valid URL (web address)", e); }
  return retval
}

//
// validate a uk postcode
//
function validatePostcode(n, e)
{
  var data = e.value;
  var regex = new RegExp("^[A-Z]{1,2}[0-9]{1,2}[A-Z]{0,1} ?[0-9][A-Z]{2}$")
  retval = regex.test(data.toUpperCase())
  if(!retval) { addError(n, " Must be a valid UK Postcode", e); }
  return retval
}

//
// validate Yes or No field
//
function validateYesOrNo(n, e)
{
  var str = e.value
  str.toLowerCase()
  var retval = (str == 'y' || str == 'n' || str == 'yes' || str == 'no')
  if(!retval) addError(n, " should be only 'Yes' or 'No'", e)
  return retval
}

//
// validate a single element
//
// in : e = element
//      op = opcode to validate
function validateElement(n, e, op)
{
  var retval = true;

  switch(op) {
    case "_vtxt":  retval = validateText(n, e);        break;
    case "_vdsc":  retval = validateDesc(n, e);        break;
    case "_vprc":  retval = validatePrice(n, e);       break;
    case "_vfle":  retval = validateFile(n, e);        break;
    case "_vnum":  retval = validateNumber(n, e);      break;
    case "_vphn":  retval = validatePhoneNumber(n, e); break
    case "_veml":  retval = validateEmail(n, e);       break;
    case "_vurl":  retval = validateURL(n, e);         break;
    case "_vukp":  retval = validatePostcode(n, e);    break;
    case "_vtho":  retval = validateThousands(n, e);   break;
    case "_vsel":  retval = validateSelection(n, e);   break;
    case "_vpwd":  retval = validatePassword(n, e, false); break;
    case "_vpwc":  retval = validatePassword(n, e, true); break;
    case "_vdte":  retval = validateDate(n, e);         break;
    case "_vyon":  retval = validateYesOrNo(n, e);      break;

    case "_ueml": // check email address but don't require it if it's blank
      if(e.value.length > 0) {
        retval = validateEmail(n, e)
      }
      break;

    default:
      break
  }

  return retval
}

//
// main entry point
//
function validate_form(f)
{
  var ops = Array()

  errors = Array()  // clears any previous errors

  for(var i = 0; i < f.elements.length; i++) {
    e = f.elements[i]
    n = e.name

    // create an array of ops required on this element
    while(n.charAt(0)=='_') {
      n = parseCode(n, ops)
    }

    // now operate on it
    while(ops.length > 0) {
      validateElement(n, e, ops.pop())
    }
  }

  // output any errors
  if(errors.length > 0) {
    html = "<ul>\n";
    for (e in errors) {
      html = html + "<li>" + errors[e] + "</li>\n"
    }
    html = html + "</ul>\n";
    var d = getElement("errors")
    if(null != d) {
      d.innerHTML = html
    }

    return false    
  }

  // all ok
  return true;
}
