/***********************************************************************************
   validation.js
   
   DESCRIPTION:
        validations.js is a library of JavaScript data entry validation routines.      
        Input fields in a form include onChange event handlers that invoke
        the validate() function , passing as arguments:
        	window
        	this
        	a string of the name of the specific validation routine
        For example:

     <INPUT TYPE="text" NAME="exp_date" ... onChange="validate(window, this,'isPhone')">

        To include multiple validations for a given field, add more validation
        name strings as arguments to the event handler:

            <...onChange="validate(window, this,'<valName1>'[...'valNamen'])">
        
*************************************************************************************/

// global variables--reference to input field
// being validated set when validate() called

var gField
var gName
var gMsg = ""
var gSelect
/************************************
   Checkbox display for ungrouped field (name) 
************************************/
function displayCbox(field){
  var cField = eval("document.forms[0]." + field + ".checked")
  if (cField == 1){
	var cValue = eval("document.forms[0]." + field + ".value")
	gMsg += "\n " + field + " = " + cValue
  }
  return true
}

/************************************
   Special election routines
************************************/

function nameCheck() {
  var inputStr=gField.value;
  if (inputStr.length < 5) {
    alert("Please enter at least 5 characters in the \"" + gName +"\" field.")
    gField.focus()
  	gField.select()
    return false
  }

  var checkOK = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzƒŠŒŽšœžŸÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ \t\r\n\f";
  var numSp = 0;
  for (i = 0;  i < inputStr.length;  i++) {
    ch = inputStr.charAt(i);
    for (j = 0;  j < checkOK.length;  j++) {
      if (ch == " ") {
        numSp++
        break   
      }  
      if (ch == checkOK.charAt(j)) {
        break
      }
    }
    if (j == checkOK.length) {
      alert("Please enter only letter and whitespace characters in the \"" + gName +"\" field.")
      gField.focus()
      gField.select()
      return false
    }
  }  
  i = inputStr.length - numSp;
  if (i < 5) {
    alert("Please enter at least 5 significant characters in the \"" + gName +"\" field.")
    gField.focus()
    gField.select()
    return false
  }
  return true;
}

function displayChoice(field) {
  if (displayChecked(field)) {
    return true
  }
  else { //check for Writein entry
    cField = field + "_WriteIn" 
    cField = eval("document.forms[0]." + cField)
    var cValue = cField.value
    if (cValue == "" || cValue == null) { 
      cValue = "*** N O   E N T R Y * <=="
    }
  }
  gMsg += "\n " + field + " = " + cValue
  return true
}

// xedit the information entered
function xeditForm(window,form) {
  if (!validate(window,form.Category, 'isCategory')) return false;
  var cSw, eSw, msw, cpsw = -1
  var msg = ""
  
  var inputStr = gField.options[gField.selectedIndex].value
  var delim = inputStr.indexOf(" ")
  var Cat = inputStr.substring(0,delim)
  if (Cat.length < 3) Cat = "0" + Cat
  if (Cat.length < 3) Cat = "0" + Cat

  if (Cat > "24 ") {
	if (Cat < "27 ") {
	   msw  = 0
	}else{
	   cpsw = 0
	} 
  }

  validate(window,form.Recipe, 'isSelected')
  if ((cpsw == 0) && (gSelect != "Cider/Perry")) eSw = 0
  if ((cpsw != 0) && (gSelect == "Cider/Perry")) eSw = 0
  if ((msw == 0)  && (gSelect != "Mead")) eSw = 0
  if ((msw != 0)  && (gSelect == "Mead")) eSw = 0

  if (eSw == 0) { 
    alert("The Recipe is not consistent with the selected Category.")
    gField.focus()
   	return false;
  }  

  var sweetLen = 	getChecked("Sweetness").length
  var carbLen  = 	getChecked("Carbonation").length
  var strenLen =	getChecked("Strength").length  

  if ((cpsw != 0) && (msw != 0)) {
    if (sweetLen > 0) {
      eSw = 0
      resetChecked("Sweetness")
      msg = "\"Dry, Medium or Sweet\" choice RESET! (does not apply to the Recipe).\n "
    }  
    if (carbLen > 0) {
      eSw = 0
      resetChecked("Carbonation")
      msg += "\"Still, Petillant or Sparkling\" choice RESET! (does not apply to the Recipe).\n "
    }  
  }
  if (msw != 0) {
    if (strenLen > 0) {
      eSw = 0
      resetChecked("Strength")
      msg += "\"Hydromel, Standard or Sack Strength\" choice RESET! (does not apply to the Recipe).\n "
    }  
  }
  if (eSw == 0) { 
    alert(msg)
   	return false;
  }

  msg="Please give us the following information: "
  if ((cpsw = 0) || (msw = 0)) {
    if (!sweetLen > 0) {
      eSw = 0
      msg += " Is your mead, cider or perry dry, medium or sweet?\n"
    }  
    if (!carbLen > 0) {
      eSw = 0
      msg += " Is your mead, cider or perry still, pettilant or sparkling?\n"
    }
    if ((!strenLen > 0) && (msw == 0)) {
      eSw = 0
      msg += " Is your mead Hydromel, Standard or Sack Strength?\n"
    }  
  }
  
  switch (Cat) {
    case "17F": //Fruit Lambic
		cSw = 0;
		break;
    case "23A": //Specialty Beer 
		cSw = 0;
		break;
    case "20A": //Fruit Beer
		cSw = 0;
		break;
    case "21A": //Spice, Herb, or Vegetable Beer
    case "21B": //Christmas/Winter Specialty Spiced Beer
		cSw = 0;
		break;
    case "25C": //Other Fruit Melomel
    case "26A": //Metheglin
    case "26C": //Open Category Mead
		cSw = 0;
		break;
    default:
        break;
  }
  if (cSw == 0) {
      var splInfo = stripLead(document.forms[0].Special_Info.value," ") 
    if (splInfo == "") {
      eSw = 0
      msg += " List any special info...fruit, veggies, etc."
    }  
  }
  if (eSw == 0) {
    alert(msg);
    return false;
  }
  return true;
}

// check the Beer Category for further edits
function isCategory() {
//var cField = eval("document.forms[0].Category")
  var cSw = -1
  var inputStr = gField.options[gField.selectedIndex].value
  var delim = inputStr.indexOf(" ")
  var Cat = inputStr.substring(0,delim)
  var CatMsg   = new Array() 
        CatMsg[0]="If you have brewed this beer to emulate a classic style don't forget to tell us what it is."
	  CatMsg[1]="If you brewed this beer to emulate a commercial example please tell us what it is."
        CatMsg[2]="If you used any spices in this beer please let us know."
	  CatMsg[3]="If you used any OTHER malt than pale malt please tell us."
 	  CatMsg[4]="You MUST specify if this is a Cider or a Perry in the 'Special Info' text box."
 	  CatMsg[5]="If you used any spices in this beer please let us know. And remember, you MUST specify if this is a Cider or a Perry in the 'Special Info' text box."
	  CatMsg[6]="Ciders and Perrys will not be judged this year. Sorry."
  switch (Cat) {
    case "23A": //Specialty Beer 
    case "20A": //Fruit Beer
    case "21A": //Spice, Herb, or Vegetable Beer
    case "22B": //Other Smoked Beer
    case "22C": //Wood Aged Beer
	    cSw = 0;
        break;
    case "16E": //Begian Specialty Ale
        cSw = 1;
        break;
    case "21B": //Christmas/Winter Specialty Spiced Beer 
    	cSw = 2;
        break;
    case "26B": //Braggot
		cSw = 3;
        break;
    case "27A": //Common Cider
    case "27B": //English Cider
    case "27C": //French Cider
    case "27D": //Common Perry
    case "27E": //Traditional Perry
		cSw = 4;
 		break;
    case "28A": //New England Cider
    case "28B": //Fruit Cider
    case "28C": //Applewine
    case "28D": //Other Specialty Cider/Perry
 		cSw = 5;
 		break;
//		cSw = 6;
//		gField.options.selectedIndex=0;
//		break;
    default:
        break;
  }
  if (cSw < 0) return true;
  alert(CatMsg[cSw]);
  if (cSw > 5) {
  	gField.focus()
	return false;
  }	
  return true;
}

/************************************
   Begin validation routines
************************************/
// display for confirmation
function displayField() {
//alert("displayField = (" + gField.value + ")")
	var inputStr = gField.value
	if (inputStr.length > 0) {
		gMsg += "\n " + gName + " = " + inputStr
	}
  return true
}

function displayTextbox() {
	var inputStr = gField.value
	if (inputStr.length > 0) {
		gMsg += "\n " + gName + " = "
		gMsg += "\n[" + inputStr + "]"
	}
  return true
}

function displaySelected() {
  gMsg += "\n " + gName + " = " + gField.value
  return true
}

function displayChecked(field) {
  var cValue = getChecked(field) 
  if (cValue.length > 0) {
    gMsg += "\n " + field + " = " + cValue 
    return true
  }
  return false    
}

function resetChecked(field) {
  var cField = eval("document.forms[0]." + field)
  var cValue = ""
  for (j = 0;  j < cField.length;  j++) {
    if (cField[j].checked) {
      cField[j].checked = false
      break
    }  
  }
  return true
}

function getChecked(field) {
  var cField = eval("document.forms[0]." + field)
  var cValue = ""
  for (j = 0;  j < cField.length;  j++) {
    if (cField[j].checked && cField[j].value != "--") {
      break
    }  
  }
  if (j != cField.length) {
    cValue = cField[j].value
  }
  return cValue
}

// check for empty field, capitalize words
function convertCase() {
//alert("convertCase = (" + gName + ", " + gField.value + ")")
  if (isNotEmpty()) {
	convertText()
	return true
  }
  return false	
}

// convert text to leading = Upper case, rest = Lower case
function convertText() {
  var inputStr = gField.value.toLowerCase()
  var ch, ch2 = ""
  result = inputStr.charAt(0).toUpperCase()
  for (var i = 1; i < inputStr.length; i++) {
    ch2 = ch = inputStr.charAt(i)
    result += ch
    while (i < inputStr.length && ch == " ") {
      ch = inputStr.charAt(++i)
    }
    if (ch != " " && ch2 == " ") result += ch.toUpperCase()
  }
  gField.value = result
  return true
}

// input field must not be empty
function isNotEmpty() {
    var inputStr = stripLead(gField.value," ") 
	if (inputStr == "" || inputStr == null) {
		alert("The \"" + gName +"\" field cannot be left empty.")
		gField.focus()
		gField.select()
		return false
	}
	gField.value = inputStr
//	gField.value = gField.value.toUpperCase()
	return true
}

// input selection must not be empty
function isSelected() {
    gSelect = stripLead(gField.options[gField.selectedIndex].value," ")
	if (gSelect == "" || gSelect == null) {
		alert("The \"" + gName +"\" field cannot be left empty.")
		gField.focus()
		return false
	}
	return true
}

// input value is a positive integer of any length
function isPositiveInteger() {
	var inputStr = gField.value
	if (inputStr.length == 0) return true
	for (var i = 0; i < inputStr.length; i++) {
		var oneChar = inputStr.charAt(i)
		if (oneChar < "0" || oneChar > "9") {
			alert("The \"" + gName +"\" field must be a positive, whole number (no letters or punctuation are allowed).")
			gField.focus()
			gField.select()
			return false
		}
	}
	return true
}

// input value is a positive integer, max 8 characters (and strip commas, periods, and "$")
// then re-insert commas for display.
function isDollarsOnly8Commas() {
	var inputStr = gField.value
	if (inputStr.length == 0) return true
	inputStr = stripChars(gField.value,"$,")
	inputStr = stripCents(inputStr)
	gField.value = inputStr

	if (isPositiveInteger()) {
		if (inputStr.length > 8) {
			alert("The \"" + gName +"\" field must be less than 100,000,000.")
			gField.focus()
			gField.select()
			return false
		}
		gField.value = reinsertCommas(inputStr)
		return true
	}
	alert("Check the dollar amount entered.")
	gField.focus()
	gField.select()
	return false
}

// input value must be user@domain
function isEmail() {
//alert("isEmail = (" + gField.value + ")")
  var inputStr = stripLead(gField.value," ") 
  if (inputStr.length == 0) return true
  var checkTLD=1; // check for addr to end in a 2 char country or well-known TLD.
  var knownDomsPat=/^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum)$/;
  var emailPat=/^(.+)@(.+)$/; // user@domain
  var specialChars="\\(\\)><@,;:\\\\\\\"\\.\\[\\]";
  var validChars="\[^\\s" + specialChars + "\]";
  var quotedUser="(\"[^\"]*\")"; // for quoted string 
  var ipDomainPat=/^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;
  var atom=validChars + '+'; // series of non-special characters.
  var word="(" + atom + "|" + quotedUser + ")"; // word in typical username.
  var userPat=new RegExp("^" + word + "(\\." + word + ")*$");
  var domainPat=new RegExp("^" + atom + "(\\." + atom +")*$");

  var matchArray=inputStr.match(emailPat); // break up user@domain
  if (matchArray==null) {
    alert(gName + " address seems incorrect (check @ and .'s)");
    gField.focus()
    gField.select()
    return false;
  }
  var user=matchArray[1];
  var domain=matchArray[2];
  for (i=0; i<user.length; i++) {
    if (user.charCodeAt(i)>127) {
      alert("The username contains invalid characters.");
      gField.focus()
      gField.select()
      return false;
    }
  }
  for (i=0; i<domain.length; i++) {
    if (domain.charCodeAt(i)>127) {
      alert("The domain name contains invalid characters.");
      gField.focus()
      gField.select()
      return false;
    }
  }
  if (user.match(userPat)==null) {
    alert("The username doesn't seem to be valid.");
    return false;
  }

  var IPArray=domain.match(ipDomainPat);
  if (IPArray!=null) {
    for (var i=1;i<=4;i++) {
      if (IPArray[i]>255) {
        alert("Destination IP address is invalid!");
        gField.focus()
        gField.select()
        return false;
      }
    }
    return true;
  }
  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.");
      gField.focus()
      gField.select()
      return false;
    }
  }
  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.");
    gField.focus()
    gField.select()
    return false;
  }
  if (len<2) {
    alert("This address is missing a hostname!");
    gField.focus()
    gField.select()
    return false;
  }
  return true;
}

// input value is a U.S. state abbreviation; set entered value to all uppercase
// also set companion field (NAME="<xxx>_expand") to full state name
function isUSState() {
	var inputStr = gField.value.toUpperCase()
	if (inputStr.length > 0 && USStates[inputStr] == null) {
		var msg = ""
		var firstChar = inputStr.charAt(0)
		if (firstChar == "A") {
			msg += "\n(Alabama = AL; Alaska = AK; Arizona = AZ; Arkansas = AR)"
		}
		if (firstChar == "D") {
			msg += "\n(Delaware = DE; District of Columbia = DC)"
		}
		if (firstChar == "I") {
			msg += "\n(Idaho = ID; Illinois = IL; Indiana = IN; Iowa = IA)"
		}
		if (firstChar == "M") {
			msg += "\n(Maine = ME; Maryland = MD; Massachusetts = MA; Michigan = MI; Minnesota = MN; Mississippi = MS; Missouri = MO; Montana = MT)"
		}
		if (firstChar == "N") {
			msg += "\n(Nebraska = NE; Nevada = NV)"
		}
		alert("Check the spelling of the state abbreviation." + msg)
		gField.focus()
		gField.select()
		return false
	}
	gField.value = inputStr

	/* comment out next two lines to 'turn off' state expansion functionality */
	var expandField = eval("window.document.forms[0]." + gField.name + "_expand")
	expandField.value = (inputStr.length == 2)? USStates[inputStr] : ""
	/* end of turning off state expansion functionality */

	return true
}

// checks for strings of maximum length
function maxString15() {
	return maxString(15)
}
function maxString25() {
	return maxString(25)
}
function maxString30() {
	return maxString(30)
}
function maxString35() {
	return maxString(35)
}


// check input date format, and repair if possible
function isDateFormat() {
	var inputStr = gField.value
	// convert hyphen delimiters to slashes
	while (inputStr.indexOf("-") != -1) {
		inputStr = replaceString(inputStr,"-","/")
	}
	var delim1 = inputStr.indexOf("/")
	var delim2 = inputStr.lastIndexOf("/")
	if (delim1 != -1 && delim1 == delim2) {
		// there is only one delimiter in the string
		alert("The date entry is not in an acceptable format.\n\nYou can enter dates in the following formats: mmddyyyy, mm/dd/yyyy, or mm-dd-yyyy.  (If the month or date data is not available, enter \'01\' in the appropriate location.)")
		gField.focus()
		gField.select()
		return false
	}
	if (delim1 != -1) {
		// there are delimiters; extract component values
		var mm = parseInt(inputStr.substring(0,delim1),10)
		var dd = parseInt(inputStr.substring(delim1 + 1,delim2),10)
		var yyyy = parseInt(inputStr.substring(delim2 + 1, inputStr.length),10)
	} else {
		// there are no delimiters; extract component values
		var mm = parseInt(inputStr.substring(0,2),10)
		var dd = parseInt(inputStr.substring(2,4),10)
		var yyyy = parseInt(inputStr.substring(4,inputStr.length),10)
	}
	if (isNaN(mm) || isNaN(dd) || isNaN(yyyy)) {
		// there is a non-numeric character in one of the component values
		alert("The date entry is not in an acceptable format.\n\nYou can enter dates in the following formats: mmddyyyy, mm/dd/yyyy, or mm-dd-yyyy.")
		gField.focus()
		gField.select()
		return false
	}
	if (mm < 1 || mm > 12) {
		// month value is not 1 thru 12
		alert("Months must be entered between the range of 01 (January) and 12 (December).")
		gField.focus()
		gField.select()
		return false
	}
	if (dd < 1 || dd > 31) {
		// date value is not 1 thru 31
		alert("Days must be entered between the range of 01 and a maximum of 31 (depending on the month and year).")
		gField.focus()
		gField.select()
		return false
	}
	if (yyyy < 100) {
		// entered value is two digits, which we allow for 1930-2029
		if (yyyy >= 30) {
			yyyy += 1900
		} else {
			yyyy += 2000
		}
	}
	gField.value = monthDayFormat(mm) + "/" + monthDayFormat(dd) + "/" + yyyy
	return true
}


// Minus 5\Plus 10 years date advisory
function isM5_P10Date() {
	if (gField.value.length == 0) return true
	var thisYear = getYear()
	return isDate((thisYear - 5),(thisYear + 10))
}


// checks for single-letter Y, N, or U
function isNYU() {
	var inputStr = gField.value.toUpperCase()
	if (inputStr.length != 1 || "NYU".indexOf(inputStr) == -1) {
		alert("Enter one letter only:\n   N(o)\n   Y(es)\n   U(nknown)")
		gField.focus()
		gField.select()
		return false	
	}
	gField.value = inputStr
	return true
}

// checks for first five digits of a ZIP code
function isZip() {
	if (gField.value.length == 0) return true
	var inputStr = gField.value
	if (isPositiveInteger()) {
		if (inputStr.length != 5) {
			alert("Enter the first five digits of the ZIP code in this field.")
			gField.focus()
			gField.select()
			return false
		}
		return true
	}
	return false
}


// checks for valid phone number; expands numbers-only with hyphens
function isPhone() {
	var inputStr = gField.value
	if (inputStr.length == 0) return true
	inputStr = stripChars(inputStr,"-./() ")
	for (var i = 0; i < inputStr.length; i++) {
		var oneChar = inputStr.charAt(i)
		if (oneChar < "0" || oneChar > "9") {
			alert("A phone number must be numbers only, including Area Code.  The entry format is \'212-555-1212\' or \'2125551212\'.")
			gField.focus()
			gField.select()
			return false
		}
	}
	if (inputStr.length != 10) {
			alert("Your phone number entry contains " + inputStr.length + " numbers instead of the required 10. Be sure to include Area Code.")
			gField.focus()
			gField.select()
			return false
	}
	inputStr = restorePhoneDelims(inputStr,"-")
	gField.value = inputStr
	return true
}


// Check components of Social Security and Employment ID Numbers
// First component of SSN requires range check
function isSSN1() {
	var inputStr = gField.value
	if (inputStr == null || inputStr == "") {return true}
	if (isNum(3)) {
		if (parseInt(inputStr) >= 800) {
			alert("The first three digits of a Social Security number must be below 800.")
			gField.focus()
			gField.select()
			return false
		}
		if (inputStr == "000") {
			alert("All zeros are not allowed in any Social Security number field.")
			gField.focus()
			gField.select()
			return false
		}
		return true
	}
	return false
}

// Other components can be generalized
function isSSN2() {
	return isTINComponent(2, "Social Security")
}
function isSSN3() {
	return isTINComponent(4, "Social Security")
}

// checks an entry against a parallel, duplicate entry to
// confirm that correct data has been entered
// sets up for redirect to self
function isChanged() {
	var inputStr = stripLead(gField.value," ").toLowerCase()
//	var inputStr = gField.value
	var xcfmField = eval("window.document.forms[0]." + gField.name.substring(0,(gField.name.length-5)))
	var xcfmStr = xcfmField.value
	var actField = eval("window.document.forms[0]." + "Active_Sw")
	var actStr = actField.value	
//alert ("inputSTr,xcfmStr,actStr =(" + inputStr + "," + xcfmStr + "," + actStr + ")")
    if (inputStr.length == 0) {
		inputStr = xcfmStr
	}	
	else {
		switch (inputStr) {
		case xcfmStr:
			return true;
		case ">":
		case "+": 
			inputStr = (parseInt(xcfmStr) + 1) + '';
			break;
		case "<": 
		case "-":
			inputStr = (parseInt(xcfmStr) - 1) + '';
			break;
		case "activate":
			inputStr = xcfmStr;
			if (actStr != "1") {
				window.location="reg_m_stat.asp?sid=" + inputStr + "&stat=1";
				return false;
			}
		case "delete":
			inputStr = xcfmStr;
			if (actStr == "1") {
				window.location="reg_m_stat.asp?sid=" + inputStr + "&stat=0";
				return false;
			}
		case "list":
		case "pick":
			window.location="displayall.asp";
			return false;
		default: //check for numeric
			var strINTCh = "0123456789";
			var strCh;
			var isINT = 1;
			for (i = 0; i < inputStr.length && isINT == 1; i++) {
				strCh = inputStr.charAt(i);
				if (strINTCh.indexOf(strCh) == -1) isINT = 0;
			}
			if (isINT = 0) inputStr = xcfmStr;
		}
	}
	window.location="reg_m_form.asp?sid=" + inputStr;
	return false;
}

// checks an entry against a parallel, duplicate entry to
// confirm that correct data has been entered
// Parallel field name must be the main field name plus "_xcfm"
function isConfirmed() {
	var inputStr = gField.value
	// flag for whether field under test is primary (true) or confirmation field
	var primary = (gField.name.indexOf("_xcfm") == -1)
	if (primary) {
		// clear the confirmation field if primary field is changed
		var xcfmField = eval("window.document.forms[0]." + gField.name + "_xcfm")
		var xcfmValue = xcfmField.value
		if (inputStr != xcfmValue) {
			xcfmField.value = ""
			return true
		}
	} else {
		var xcfmField = eval("window.document.forms[0]." + gField.name.substring(0,(gField.name.length-5)))
	}
	var xcfmStr = xcfmField.value
	if ((primary && xcfmStr != "") || (!primary)) {
		if (inputStr != xcfmStr) {
			alert("The main and confirmation entry field contents do not match. Both fields must have EXACTLY the same content to be accepted by the database.")
			gField.focus()
			gField.select()
			return false	
		}
	}
	return true
}

/************************************************
   Begin utility validation and support routines
************************************************/

// generic maximum string length checker (called by dispatched validation functions)
function maxString(len) {
	var inputStr = gField.value.toUpperCase()
	if (inputStr.length > len) {
		if (!confirm("This field is limited to " + len + " characters long. Should the system accept the first " + len + " characters of your entry, and ignore the rest?")) {
			gField.focus()
			gField.select()
			return false
		}
		inputStr = inputStr.substring(0,len)
	}
	gField.value = inputStr
	return true
}

// restore commas for big dollar amounts
function reinsertCommas(inputStr) {
	var out = ""
	var len = inputStr.length
	for (var i = 0; i < len; i++) {
		if (i != 0 && i%3 == 0) {
			out = "," + out
		}
		out = inputStr.charAt(len-i-1) + out
	}
	return out
}

// strip decimal and trailing cents
function stripCents(inputStr) {
	var marker = inputStr.indexOf(".")
	if (marker != -1) {
		return inputStr.substring(0,marker)
	}
	return inputStr
}

// strip characters
function stripChars(inputStr,stripChar) {
  for (var i = 0;  i < stripChar.length;  i++) {
    var ch = stripChar.charAt(i) 
    var j = inputStr.indexOf(stripChar.charAt(i))
    while (j != -1) {
	  inputStr = inputStr.substring(0,j) + inputStr.substring(j+1,inputStr.length)
	  j = inputStr.indexOf(ch)
	}  
  }
  return inputStr
}

// strip leading characters
function stripLead(inputStr,stripChar) {
  for (var i = 0;  i < stripChar.length;  i++) {
    var ch = stripChar.charAt(i)
	var result = inputStr
	while (result.substring(0,1) == ch) {
		result = result.substring(1,result.length)
	}
  }	
  return result
}

// insert hyphens into phone number
function restorePhoneDelims(inputStr,delim) {
	var n1 = inputStr.substring(0,3)
	var n2 = inputStr.substring(3,6)
	var n3 = inputStr.substring(6,10)
	return (n1 + delim + n2 + delim + n3)
}

// perform range checking on SSN
function isSSNRange(inputStr) {
	if (inputStr.substring(0,3) == "000" ||
		inputStr.substring(4,6) == "00"  ||
		inputStr.substring(7,11) == "0000") {
			alert("No segment of a valid Social Security Number can be all zeros.")
			gField.focus()
			gField.select()
			return false
	}
	if (parseInt(inputStr.substring(0,3), 10) >= 800) {
			alert("The first three digits of a valid Social Security Number must be less than \'800\'.")
			gField.focus()
			gField.select()
			return false
	}
	return true
}

// utility for isNum, isSSN, and isEIN validations
function isNum(n) {
	if (isPositiveInteger()) {
		var inputStr = gField.value
		if (inputStr.length != n) {
			alert("The \"" + gName +"\" field requires a number " + n + " digits long.")
			gField.focus()
			gField.select()
			return false
		}
		return true
	}
	return false
}

// States array
var USStates = new Array(53)
USStates["AL"] = "ALABAMA"
USStates["AK"] = "ALASKA"
USStates["AZ"] = "ARIZONA"
USStates["AR"] = "ARKANSAS"
USStates["CA"] = "CALIFORNIA"
USStates["CO"] = "COLORADO"
USStates["CT"] = "CONNECTICUT"
USStates["DE"] = "DELAWARE"
USStates["DC"] = "DISTRICT OF COLUMBIA"
USStates["FL"] = "FLORIDA"
USStates["FR"] = "FOREIGN"
USStates["GA"] = "GEORGIA"
USStates["HI"] = "HAWAII"
USStates["ID"] = "IDAHO"
USStates["IL"] = "ILLINOIS"
USStates["IN"] = "INDIANA"
USStates["IA"] = "IOWA"
USStates["KS"] = "KANSAS"
USStates["KY"] = "KENTUCKY"
USStates["LA"] = "LOUISIANA"
USStates["ME"] = "MAINE"
USStates["MD"] = "MARYLAND"
USStates["MA"] = "MASSACHUSETTS"
USStates["MI"] = "MICHIGAN"
USStates["MN"] = "MINNESOTA"
USStates["MS"] = "MISSISSIPPI"
USStates["MO"] = "MISSOURI"
USStates["MT"] = "MONTANA"
USStates["NE"] = "NEBRASKA"
USStates["NV"] = "NEVADA"
USStates["NH"] = "NEW HAMPSHIRE"
USStates["NJ"] = "NEW JERSEY"
USStates["NM"] = "NEW MEXICO"
USStates["NY"] = "NEW YORK"
USStates["NC"] = "NORTH CAROLINA"
USStates["ND"] = "NORTH DAKOTA"
USStates["OH"] = "OHIO"
USStates["OK"] = "OKLAHOMA"
USStates["OR"] = "OREGON"
USStates["PA"] = "PENNSYLVANIA"
USStates["PR"] = "PUERTO RICO"
USStates["RI"] = "RHODE ISLAND"
USStates["SC"] = "SOUTH CAROLINA"
USStates["SD"] = "SOUTH DAKOTA"
USStates["TN"] = "TENNESSEE"
USStates["TX"] = "TEXAS"
USStates["UT"] = "UTAH"
USStates["VT"] = "VERMONT"
USStates["VA"] = "VIRGINIA"
USStates["WA"] = "WASHINGTON"
USStates["WV"] = "WEST VIRGINIA"
USStates["WI"] = "WISCONSIN"
USStates["WY"] = "WYOMING"

// date field validation (called by other validation functions that specify minYear/maxYear)
function isDate(minYear,maxYear,minDays,maxDays) {
	var inputStr = gField.value
	// convert hyphen delimiters to slashes
	while (inputStr.indexOf("-") != -1) {
		inputStr = replaceString(inputStr,"-","/")
	}
	var delim1 = inputStr.indexOf("/")
	var delim2 = inputStr.lastIndexOf("/")
	if (delim1 != -1 && delim1 == delim2) {
		// there is only one delimiter in the string
		alert("The date entry is not in an acceptable format.\n\nYou can enter dates in the following formats: mmddyyyy, mm/dd/yyyy, or mm-dd-yyyy.  (If the month or date data is not available, enter \'01\' in the appropriate location.)")
		gField.focus()
		gField.select()
		return false
	}
	if (delim1 != -1) {
		// there are delimiters; extract component values
		var mm = parseInt(inputStr.substring(0,delim1),10)
		var dd = parseInt(inputStr.substring(delim1 + 1,delim2),10)
		var yyyy = parseInt(inputStr.substring(delim2 + 1, inputStr.length),10)
	} else {
		// there are no delimiters; extract component values
		var mm = parseInt(inputStr.substring(0,2),10)
		var dd = parseInt(inputStr.substring(2,4),10)
		var yyyy = parseInt(inputStr.substring(4,inputStr.length),10)
	}
	if (isNaN(mm) || isNaN(dd) || isNaN(yyyy)) {
		// there is a non-numeric character in one of the component values
		alert("The date entry is not in an acceptable format.\n\nYou can enter dates in the following formats: mmddyyyy, mm/dd/yyyy, or mm-dd-yyyy.")
		gField.focus()
		gField.select()
		return false
	}
	if (mm < 1 || mm > 12) {
		// month value is not 1 thru 12
		alert("Months must be entered between the range of 01 (January) and 12 (December).")
		gField.focus()
		gField.select()
		return false
	}
	if (dd < 1 || dd > 31) {
		// date value is not 1 thru 31
		alert("Days must be entered between the range of 01 and a maximum of 31 (depending on the month and year).")
		gField.focus()
		gField.select()
		return false
	}

	// validate year, allowing for checks between year ranges
	// passed as parameters from other validation functions
	if (yyyy < 100) {
		// entered value is two digits, which we allow for 1930-2029
		if (yyyy >= 30) {
			yyyy += 1900
		} else {
			yyyy += 2000
		}
	}

	var today = new Date()
	if (!minYear) {
		// function called with specific day range parameters
		var dateStr = new String(monthDayFormat(mm) + "/" + monthDayFormat(dd) + "/" + yyyy)
		var testDate = new Date(dateStr)
		if (testDate.getTime() < (today.getTime() + (minDays * 24 * 60 * 60 * 1000))) {
			alert("The most likely range for this entry begins " + minDays + " days from today.")
		}
		if (testDate.getTime() > today.getTime() + (maxDays * 24 * 60 * 60 * 1000)) {
			alert("The most likely range for this entry ends " + maxDays + " days from today.")
		}
	} else if (minYear && maxYear) {
		// function called with specific year range parameters
		if (yyyy < minYear || yyyy > maxYear) {
			// entered year is outside of range passed from calling function
			alert("The most likely range for this entry is between the years " + minYear + " and " + maxYear + ".  If your source data indicates a date outside this range, then enter that date.")
		}
	} else {
		// default year range (now set to (this year - 100) and (this year + 25)
		var thisYear = today.getYear()
		if (thisYear < 100) {
			thisYear += 1900
		}
		minYear = thisYear - 100
		maxYear = thisYear + 25
		if (yyyy < minYear || yyyy > maxYear) {
			alert("It is unusual for a date entry to be before " + minYear + " or after " + maxYear + ". Please verify this entry.")
		}
	}
	if (!checkMonthLength(mm,dd)) {
		gField.focus()
		gField.select()
		return false
	}
	if (mm == 2) {
		if (!checkLeapMonth(mm,dd,yyyy)) {
			gField.focus()
			gField.select()
			return false
		}
	}

	// put the Informix-friendly format back into the field
	gField.value = monthDayFormat(mm) + "/" + monthDayFormat(dd) + "/" + yyyy
	return true
}

// get JS-friendly year
function getYear() {
	var thisYear = (new Date()).getYear()
	thisYear = (thisYear < 1900) ? thisYear + 1900: thisYear
	return thisYear
}

// check the entered month for too high a value
function checkMonthLength(mm,dd) {
	var months = new Array("","January","February","March","April","May","June","July","August","September","October","November","December")
	if ((mm == 4 || mm == 6 || mm == 9 || mm == 11) && dd > 30) {
		alert(months[mm] + " has only 30 days.")
		return false
	} else if (dd > 31) {
		alert(months[mm] + " has only 31 days.")
		return false
	}
	return true
}

// check the entered February date for too high a value 
function checkLeapMonth(mm,dd,yyyy) {
	if (yyyy % 4 > 0 && dd > 28) {
		alert("February of " + yyyy + " has only 28 days.")
		return false
	} else if (dd > 29) {
		alert("February of " + yyyy + " has only 29 days.")
		return false
	}
	return true
}

// convert month or day number to string, padding with leading zero if needed
function monthDayFormat(val) {
	if (isNaN(val) || val == 0) {
		return "01"
	} else if (val < 10) {
		return "0" + val	
	}
	return "" + val
}

// Generic TIN component validator
function isTINComponent(len, type) {
	var inputStr = gField.value
	if (inputStr == null || inputStr == "") {return true}
	if (isNum(len)) {
		inputStr = parseInt(stripLead(inputStr,"0"),10)
		if (inputStr == 0 || isNaN(inputStr)) {
			alert("All zeros are not allowed in any " + type + " number field.")
			gField.focus()
			gField.select()
			return false
		}
		return true
	}
	return false
}
// String utilities for search/replace functions
// extract front part of string prior to searchString
function getFront(mainStr,searchStr){
        foundOffset = mainStr.indexOf(searchStr)
        if (foundOffset == -1) {
                return null
        }
        return mainStr.substring(0,foundOffset)
}

// extract back end of string after searchString
function getEnd(mainStr,searchStr) {
        foundOffset = mainStr.indexOf(searchStr)
        if (foundOffset == -1) {
                return null
        }
        return mainStr.substring(foundOffset+searchStr.length,mainStr.length)
}

// insert insertString immediately before searchString
function insertString(mainStr,searchStr,insertStr) {
        var front = getFront(mainStr,searchStr)
        var end = getEnd(mainStr,searchStr)
        if (front != null && end != null) {
                return front + insertStr + searchStr + end
        }
        return null
}

// remove deleteString
function deleteString(mainStr,deleteStr) {
        return replaceString(mainStr,deleteStr,"")
}

// replace searchString with replaceString

function replaceString(mainStr,searchStr,replaceStr) {
        var front = getFront(mainStr,searchStr)
        var end = getEnd(mainStr,searchStr)
        if (front != null && end != null) {
                return front + replaceStr + end
        }
        return null
}

/******************************************
   Begin validation dispatching mechanism
******************************************/
// validation function object constructor
function dispatcher(validationFunc) {
   this.doValidate = validationFunc
}
var dispatchLookup = new Array
    dispatchLookup["displayField"] = new dispatcher(displayField)
    dispatchLookup["displayTextbox"] = new dispatcher(displayTextbox)
    dispatchLookup["displaySelected"]= new dispatcher(displaySelected)
    dispatchLookup["nameCheck"]  = new dispatcher(nameCheck)
    dispatchLookup["convertCase"]  = new dispatcher(convertCase)
    dispatchLookup["convertText"]  = new dispatcher(convertText)
    dispatchLookup["isNotEmpty"] = new dispatcher(isNotEmpty)
    dispatchLookup["isSelected"] = new dispatcher(isSelected)    
    dispatchLookup["isPositiveInteger"] = new dispatcher(isPositiveInteger)
    dispatchLookup["isDollarsOnly8Commas"] = new dispatcher(isDollarsOnly8Commas)
    dispatchLookup["isEmail"] = new dispatcher(isEmail)
    dispatchLookup["isUSState"] = new dispatcher(isUSState)
    dispatchLookup["maxString15"] = new dispatcher(maxString15)
    dispatchLookup["maxString25"] = new dispatcher(maxString25)
    dispatchLookup["maxString30"] = new dispatcher(maxString30)
    dispatchLookup["maxString35"] = new dispatcher(maxString35)
    dispatchLookup["isNYU"] = new dispatcher(isNYU)
    dispatchLookup["isConfirmed"] = new dispatcher(isConfirmed)
    dispatchLookup["isChanged"] = new dispatcher(isChanged)
    dispatchLookup["isZip"] = new dispatcher(isZip)
    dispatchLookup["isPhone"] = new dispatcher(isPhone)
    dispatchLookup["isSSN1"] = new dispatcher(isSSN1)
    dispatchLookup["isSSN2"] = new dispatcher(isSSN2)
    dispatchLookup["isSSN3"] = new dispatcher(isSSN3)
    dispatchLookup["isM5_P10Date"] = new dispatcher(isM5_P10Date)
    dispatchLookup["isDateFormat"] = new dispatcher(isDateFormat)
    dispatchLookup["isCategory"] = new dispatcher(isCategory)    


// main validation function called by form event handlers
function validate(frame, field, method) {
    gName = field.name
	gField = eval("document.forms[0]." + field.name)
	var args = validate.arguments
	for (var i = 2; i < args.length; i++) {
		if (!dispatchLookup[args[i]].doValidate()) {
			return false
		}
	}
	return true
}
