function is1AlphaNumeric(s) {
  return alphaCount(s,1,true);
}

function is1Alpha(s) {
  return alphaCount(s,1,false);
}

function is2Alphas(s) {
  return alphaCount(s,2,false);
}

function is4Alphas(s) {
  return alphaCount(s,4,false);
}

function alphaCount(s, n, numbersOk) {
  if (isEmpty(s)) return false;

  var cnt = 0;
  for (var i = 0; i < s.length; i++) {   
    var c = s.charAt(i);

    if (isLetter(c)) cnt++;
    if (numbersOk && (c >= "0" && c <= "9")) cnt++;
  }
  return (cnt >= n);
}


function isEmail(s) {
  if (isEmpty(s)) return emptyReturn(isEmail.arguments);
  if (isWhitespace(s)) return false;
  var i = 1;
  var sLength = s.length;
  while ((i < sLength) && (s.charAt(i) != "@")) {
    i++
  }
  if ((i >= sLength) || (s.charAt(i) != "@")) return false;
  else i += 2;
  while ((i < sLength) && (s.charAt(i) != ".")) {
    i++
  }
  if ((i >= sLength - 1) || (s.charAt(i) != ".")) return false;
  else return true;
}

// Info
var infodigits = "0123456789";
var infoletters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

function stripInfoCharsNotInBag (s, bag) {
  var i;
  var returnString = "";

  for (i = 0; i < s.length; i++) {   
    var c = s.charAt(i);
    if (bag.indexOf(c) != -1) returnString += c;
  }
  return returnString;
}

function isInfoInteger(s) {
  if (isEmpty(s)) return emptyReturn(isInfoInteger.arguments);

  for (var i = 0; i < s.length; i++) {   
    var c = s.charAt(i);
    if ( !((c >= "0") && (c <= "9")) ) return false;
  }
  return true;
}

var phoneNumberDelimiters = "()- ";
var validUSPhoneChars = infodigits + phoneNumberDelimiters;
var validWorldPhoneChars = infodigits + phoneNumberDelimiters + "+";
var digitsInUSPhoneNumber = 10;

function isUSPhoneNumber(s) {
  if (isEmpty(s)) return emptyReturn(isUSPhoneNumber.arguments);
  var s2 = stripInfoCharsNotInBag(s,infodigits);
  return (isInfoInteger(s2) && s2.length == digitsInUSPhoneNumber)
}

function isInternationalPhoneNumber(s) {
  if (isEmpty(s)) return emptyReturn(isInternationalPhoneNumber.arguments);
  var s2 = stripInfoCharsNotInBag(s,infodigits);
  return (isInfoInteger(s2));
}

function isPhoneNumber(s) {
  if (isEmpty(s)) return emptyReturn(isPhoneNumber.arguments);
  return (isUSPhoneNumber(s) || isInternationalPhoneNumber(s));
}


// String checking
function isLetter(c) {
  return ( ((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")) )
}


// Validator
var defaultEmptyOK = false
var whitespace = " \t\n\r";

function isEmpty(s) {
  return ((s == null) || (s.length == 0));
}

function emptyReturn() {
  var argv = emptyReturn.arguments;
  if (argv.length == 1) return defaultEmptyOK;
  else return (argv[0] == true);
}

function isWhitespace (s) {
    if (isEmpty(s)) return true;

    for (var i = 0; i < s.length; i++) {   
      var c = s.charAt(i);
      if (whitespace.indexOf(c) == -1) return false;
    }
    return true;
}

function FormField() {
  this.elmt = null;  this.name = null;
  this.func = null;  this.mesg = null;
}

function validateForm(form) {
  var missing = "";
  var error = "";

  if (setArrays(form) == false) return false;

  if (fields == null) return true;

  for (var j = 0; j < fields.length; j++) {
    if (fields[j] == null) continue;
    var elmt = fields[j].elmt;
    var name = fields[j].name;
    var func = fields[j].func;
    var type = "";
    if (elmt.length > 0 && elmt.options == null) {
      type = elmt[0].type;
    } else {
      type = elmt.type;
    }
    type = type.toLowerCase()

    if (type == "checkbox" || type == "radio") {
      var isChecked = false;
      var checkElmt = null;
      if (elmt.length > 0) {
	for (var i = 0; i < elmt.length; i++) {
	  if (elmt[i].checked) {
	    isChecked = true;
	    checkElmt = elmt[i];
	    break;
	  }
	}
      } else {
	if (elmt.checked) {
	  isChecked = true;
	  checkElmt = elmt;
	}
      }
      if (!isChecked) {
	if (alwaysShowMsgs) {
	  error += name+":  "+fields[j].mesg+"\n\n";
	} else {
	  missing += name+"\n";
	}
      } else if (func != null && checkElmt != null) {
	if (!func(checkElmt.value)) error += name+":  "+fields[j].mesg+"\n\n";
      }
    } else if (type == "select-one") {
      if (func != null) {
	if (!func(elmt.options[elmt.selectedIndex].value))
	  error += name+":  "+fields[j].mesg+"\n\n";
      }
    } else if (type == "select-multiple") {
      var isChecked = false;
      var checkElmt = null;
      for (var i = 0; i < elmt.length; i++) {
	if (elmt[i].selected) {
	  isChecked = true;
	  checkElmt = elmt[i];
	  break;
	}
      }
      if (!isChecked) {
	if (alwaysShowMsgs) {
	  error += name+":  "+fields[j].mesg+"\n\n";
	} else {
	  missing += name+"\n";
	}
      } else if (func != null && checkElmt != null) {
	if (!func(checkElmt.value)) error += name+":  "+fields[j].mesg+"\n\n";
      }
    } else if (type == "text" || type == "password"
	    || type == "textarea" || type == "file") {
      if (elmt.value.length < 1 || isWhitespace(elmt.value)) {
	if (alwaysShowMsgs) {
	  error += name+":  "+fields[j].mesg+"\n\n";
	} else {
	  missing += name+"\n";
	}
      } else if (func != null) {
	if (!func(elmt.value)) error += name+":  "+fields[j].mesg+"\n\n";
      }
    } else if (type == "hidden") { // ignore hiddens
    } else if (type == "reset") {  // ignore the buttons
    } else if (type == "submit") {
    } else if (type == "button") {
    } else {
      missing += 'Unsupported form element type: '+type+' ('+j+')\n';
    }
  }

  if (missing.length == 0 && error.length == 0) return true;

  // we have missing fields and/or errors
  var alertStr = "";
  if (missing.length != 0 && error.length != 0) {
    alertStr = "The following required fields are missing:\n\n"+missing
	     + "\n\nAdditionally, the following errors occured:\n\n"+error;
  } else if (missing.length == 0 && error.length != 0) {
    alertStr = error;
  } else if (missing.length != 0 && error.length == 0) {
    alertStr = "The following required fields are missing:\n\n"+missing;
  }
  alert(alertStr);
  return false;
}
