﻿/* **************************************************************
Derived from
MegaScript form	validation by Charity Khan
2001-07-23 Modified to	handle more validation types and to make it user-friendlier
http://builder.cnet.com/webbuilding/pages/Programming/Kahn/050698/toolrei.html
************************************************************** */
var theForm;
// because Form	exists only once page is fully loaded, the formname must be provided at	page level!
var allAtOnce =	true	//default is all error messages show on the form
var beginRequestAlertForText, beginRequestAlertGeneric, endRequestAlert
var beginInvalidAlert, endInvalidAlert,	beginFormatAlert, endFormatAlert
var kAbove = "beforeBegin"
var kBelow = "afterEnd"
var kTypeDate = "D"
var kTypeNum =	"N"
var kTypeText = "T"

var kGT = ">"
var kGTE = ">="
var kLT = "<"
var kLTE = "<="
var kE = "=="
var kDiff = "!="

var errPos = kAbove			// error messages will show above by default
var alertText = ""
var validOK = false

var oElmPanel = "";			// panel object
var panelHeight = 0			// for all forms
var prevPanel = "panel0"	// to manage tab icons
var panelHasErr	= false;	// to manage tab icons
var activePanelNum = 0		// for non tabbed forms
var panelCount = 0 			// for non tabbed forms

// used with tab icons
var imgValOK		= "/assets/images/clear.gif"
var imgValRequired  = "/assets/images/bul/arrowdown.gif"
var imgValErr		= "/assets/images/bul/alert.gif"

var str= "";				// holding the element value
var elementName = "";		// string
var oElement = "";			// object
var firstMissingElt = null;	// array containing element object and type
var goToObj
var required = true;		// by default, so that validation functions can be used out of scope

// backwards compatibility
var curPanel, numPanel
/***************************************************************
** Define the elts array:
***************************************************************/
var elts = new Array();
var tabs = new Array();

/***************************************************************
** Define the QueryString array:
***************************************************************/
var qs = location.search.substr(1).split("&")
var blnDebug = 0
for (var i=0; i<qs.length; i++){
	tmp = qs[i].split("=")
	if (tmp[0].toUpperCase()=="DEBUG") blnDebug = 1
}



/***************************************************************
** unspecified field (text): "Please include [field name]."
** unspecified field (other): "Please choose [field name]."
** invalid text	field entries: "[field value] is an invalid [field name]!"
** help	with format: "Use this format: "
***************************************************************/
function setErrorStrings(usrLanguage){
	var lang = usrLanguage.toUpperCase()+"" == ""	? "EN": usrLanguage;

	if (lang == "FR") {
		beginRequestAlertForText = "Information requise"
		beginRequestAlertGeneric = "Veuillez effectuer votre sélection"
		beginInvalidAlert = "Valeur invalide!"
		//beginFormatAlert = "<br>Format : "
		beginFormatAlert = " "
		beginAlertSelectAtLeast = "Veuillez choisir au moins " 
		beginAlertSelectAtMost = "Veuillez choisir au plus " 
		endAlertSelectAtLeastMost = " option(s)."
	}else{
		beginRequestAlertForText = "Required information"
		beginRequestAlertGeneric = "Please make a selection"
		beginInvalidAlert = "Invalid value!"
//		beginFormatAlert =  " Valid format: "
		beginFormatAlert =  " "
		beginAlertSelectAtLeast = "Please select at least " 
		beginAlertSelectAtMost = "Please select at most " 
		endAlertSelectAtLeastMost = " option(s)."
	}
	endRequestAlert = "!"
	endInvalidAlert = ""
	endFormatAlert = ""
}


/***************************************************************
** object definition
Creates new validation objects
***************************************************************/
function validation() {

	// the object cannot be created if at the very least, an element name is not provided
	if (arguments[0] + "" == "undefined") {
		showHelpInfo()
		return false
	} else {
		this.elementName = arguments[0];
	}

	// testing element existence
	//alert(eval("theForm." + this.elementName))
	var tmpElm = eval("theForm." + this.elementName);
	if (tmpElm+"" == "undefined") {
		alert("ERROR: Cannot create validation object for [" + this.elementName + "] as there is no corresponding element in the form!")
		return false
	}

	// assigning args to object class
	// this argument used to have another purpose, hence this alert
	// may need some tweeking in case some old forms still have a value in there
	if (isNaN(arguments[1])){
		alert("ERROR: Cannot create validation object for [" + this.elementName + "] as the second argument should be a number!")
		return false
	}else{
		this.oElmPanel = (arguments[1] + "" == "undefined" || arguments[1] + "" == "null")? "panel999": "panel"+arguments[1];
	}
	this.elementValidation = (arguments[2] + "" == "undefined")? "isText(str)": arguments[2];
	this.validFormat = (arguments[3] + "" == "undefined")? "": arguments[3];
	this.required = (arguments[4] + "" == "undefined")? "true": arguments[4];
	this.errPos = (arguments[5] + "" == "undefined" || arguments[5] + "" == "null")? kAbove: arguments[5];


	// SETTING TYPES
	this.elementType = getElmType(tmpElm)

	/*
	Populate the array with each created object, adding it to the end of the array.
	If the object has already been created, we want to replace it with the new values.

	Make sure array elements are created in the same tab order as the form elements
	(when not using AllAtOnce, is more userfriendly)
	*/
	this.index = elts.length
	for (var i=0; i<elts.length; i++){
		if (this.elementName == elts[i].elementName) {
			this.index = i
		}
	}
	elts[this.index] = this

	// create adjacent SPANs for displaying error messages
	spanExists = (document.getElementById("err"+this.elementName) + "" == "null") ? false:true;

	if (!spanExists) {
		oErrElm = ((this.elementType == "checkbox" || this.elementType == "radio" ) && eval(theForm.name + "." + this.elementName).length > 0 )? eval(theForm.elements[this.elementName][0]): eval(theForm.name + "." + this.elementName);
		oErrElm.insertAdjacentHTML(this.errPos, "<SPAN class='errMsg' ID='err" + this.elementName + "'></span>")
	}
}


function removeValidation(){
/***********************
Desc: removes a validation object
IO: elementName
*/
	if (arguments[0] + "" == "undefined") {
	    alert("In order to remove a validation object, you must provide the element name!")
            return false
	} else {
	    this.elementName = arguments[0];
	}

	// if elementName is not found, nothing happens
	for (var i=0; i< elts.length; i++){
	    if (this.elementName == elts[i].elementName) {
		// get oElmPanel, val objects
		oElmPanel = elts[i].oElmPanel
		oTabIcon = document.getElementById(oElmPanel + "val")

		// reset the error spans and tabs
		eval("err"+elementName).innerHTML = "";
		//if (oElmPanel == "panel"+activePanelNum) oTabIcon.src = imgValOK

			if (blnDebug) alert(elts[i].elementName +" validation object removed successfully!")

			elts1 = elts.slice(0,i)
			elts = elts1.concat(elts.slice(i+1))
		}
	}
}


function showValidationArray(){
/************************
Desc: displays the content or the array
*/
	var	tmpStr = "Validation Objects for this form: \n"
		tmpStr += "[#]\t[panel###] [Name]\t[Validation]\t[Format]\t[required]\n"
	for (var i=0; i < elts.length; i++){
		tmpStr += "[" + elts[i].index + "]\t[" + elts[i].oElmPanel + "][" + elts[i].elementName + "]\t[" + elts[i].elementValidation + "]\t[" + elts[i].validFormat + "]\t[" + elts[i].required + "]\n"
	}
	alert(tmpStr)
}


function showHelpInfo() {
/***********************
Desc: takes the user to validation help page.
IO: N/A
*/
	var urlHelpPage = location.protocol + "//" + location.host
	var strHelpMsg = ""
	strHelpMsg += "Form Validation is not implemented correctly.\n"
	strHelpMsg += "Please go to the following page to get more information on how to implement it.\n\n"
	answer =	confirm(strHelpMsg + urlHelpPage)
	if (answer == true){
		oNewWindow = window.open(urlHelpPage, "_blank")
	}
}


function getElmType(oElm){
/***********************
Desc: returns the element type
	detecting element type automatically	(this is not supported in IE3)
	possible types are: select-one, select-multiple, text, textarea, hidden
IO: element object
*/
	var tmp
	if (oElm.type){
		tmp = (oElm.type.indexOf("select")>-1) ?	"select" : oElm.type;
	} else {
		// checkboxes and radios
		tmp = (oElm[0].type) ? oElm[0].type : "Unknown";
	}
	return tmp
}


/***************************************************************
** VALIDATION FUNCTIONS
** these functions validate the	string or form object passed,
** and return true or false based on whether the test succeeds or fails
**
**
** validate text in text input or textarea matches pattern
**   isEmail(str): verifies email address (contains "@"	and ".")
**   isState(str): verifies U.S. State Code
**   isZipCode(str): verifies zip code of form xxxxx or	xxxxx-xxxx
**   isPhoneNum(str): verifies phone number of form xxx-xxx-xxxx
**   isDate(str): verifies date	specified format
***************************************************************/

function isText(str,minL,maxL) {
/***********************
Desc: verifies that text field or textarea is not empty, and optionally check minimum length
IO:
	str = sting to evaluate
	minLen = minimum length of string
	maxLen = maximum length of string
Known issue: if passing null or empty set of commas as parameters, will choke or not validate properly
*/
//	var blnrequired = elts[i].required


	var blnrequired = required
	var strLen = str.length

	var minLen = (arguments[1] + "" == "undefined")? 0: arguments[1];
	var maxLen = (arguments[2] + "" == "undefined")? 0: arguments[2];
	// because required string must be at least 1 long
	minLen = (blnrequired && minLen==0)?1:minLen;

	//	alert("-Field is "+ ((blnrequired)?"":"not ") +"required\n-Min. length: "+arguments[1]+":"+minLen+"\tMax. length: "+arguments[2]+":"+maxLen)
	//alert(minLen + maxLen == 0 && str != "")

	if (minLen + maxLen == 0 && str != "") { return true }      // required string of any length
	if (minLen > maxLen && strLen >= minLen) { return true }    // minonly
	if (minLen <= maxLen && strLen >= minLen && strLen <= maxLen) { return true }	// minmax
	return false
}


function isEmpty(str){
/***********************
Desc: verifies that text field or textarea IS EMPTY
	Although this is not used in validation per se, it is useful to process string removal.
IO:
	str = sting to evaluate
*/
	if (str == null || str == ""){
		return true
	}else{
		return false
	}
}


function isNum(str, NumberofDecimals, pminLen, pminVal,	pmaxVal){
/***********************
 validates that the value is numeric and that
 there are no more than the required number of decimals
*/

	var strDec = NumberofDecimals+"" == "undefined"?"":(NumberofDecimals+"" == 0?"":"\\.?\\d{0," + NumberofDecimals + "}");
	var minLen = pminLen+""	== "undefined"?0:pminLen;	//min int digits before decimals
	var minVal = (isNaN(pminVal) == true || pminVal == "" )?0:parseFloat(pminVal);
	var maxVal = (isNaN(pmaxVal) == true || pmaxVal == "" )?0:parseFloat(pmaxVal);
	if (blnDebug) alert(pminVal + ":"+isNaN(pminVal) + " -> [" + minVal + "]\n"+ pmaxVal+":"+isNaN(pmaxVal) + " -> [" + maxVal + "]")


	// 2002-10-31 FIX: validating negative numbers
	var re = new RegExp("^-?\\d{"+minLen+",}" + strDec + "$")
	//alert(elementName + "; " + re + "; " + minVal)

	var	isValid	= re.test(str)

	if (isValid){
		//convert string to number; (1.00 = 1)
		var myNumber = parseFloat(str)

		// as long as both range values are different
		if (maxVal != minVal){
			if (maxVal >= minVal){
				return (myNumber >= minVal && myNumber <= maxVal)
			}else if (maxVal == 0){
				return (myNumber >= minVal)
			}else{
				return (myNumber <= minVal && myNumber >= maxVal)
			}
		}else{
			return isValid
		}
	}else{
		//false
		return isValid
	}

}


function isSelect(oElement,skipFirst,minS, maxS) {
/***********************
Desc: makes sure at least one item is selected, with optional non selectable first item
IO:
	skipFirst: true when first item is not a selectable value
	minS: select at least
	maxS: select at most
*/

	var atLeast = (arguments[2] + "" == "undefined")? 1: arguments[2]
	var atMost = (arguments[3] + "" == "undefined")? ((oElement.multiple)? oElement.length:atLeast): arguments[3]


	// SELECT required AND FIRST DISABLED
	//if (oElement.selectedIndex < 0 || (skipFirst && oElement.selectedIndex == 0)) {

	var numSelected = 0;
	for (var i = 0;  i < oElement.length;  i++){
	if (oElement.options[i].selected)
		numSelected++;
	}

	if (skipFirst && oElement.selectedIndex == 0){
		alertText = beginRequestAlertGeneric 
		return false;
	}
	if (numSelected < atLeast){
		alertText = beginAlertSelectAtLeast + atLeast + endAlertSelectAtLeastMost
		return false;
	}
	if (numSelected > atMost) {
		alertText = beginAlertSelectAtMost + atMost + endAlertSelectAtLeastMost
		return false;
	}
	return true
}


function isRadio(oElement) {
/***********************
Desc: verifies one of a group of radio buttons is chosen
In a group, only one button can be selected
*/

	if (oElement.length > 0 ) {
		//alert(oElement[0].name + ": " + oElement.length)
		for (j=0; j < oElement.length; j++) {
	    	if (oElement[j].checked) {
				return true;
			}
		}
	}else if (oElement.checked) {
		// when a radio group contains only 1 radio button, the length is undefined
		//alert(oElement.name + ": " + oElement.checked)
		return true;
	}
	alertText = beginRequestAlertGeneric + endRequestAlert;
	return false;
}



function isCheck(oElement, minC, maxC) {
/***********************
Desc: verifies at least one of a group of checkboxes is checked
IO:
	minC: mininum number af check boxes that can be selected.
	maxC: maximum number of check boxes that can be selected.
*/

	var atLeast = (arguments[1] + "" == "undefined")? 1: arguments[1]
	var atMost = (arguments[2] + "" == "undefined")? oElement.length: arguments[2]

	var numChecked = 0;
	if (oElement.length > 0 ) {
		for (var i = 0;	i < oElement.length; i++) {
	    	if (oElement[i].checked) {
				numChecked++;
			}
		}
	}else if (oElement.checked) {
		// when a checkbox group contains only 1 checkbox, the length is undefined
		numChecked++;
	}

	if (numChecked < atLeast){
		alertText = beginAlertSelectAtLeast + atLeast + endAlertSelectAtLeastMost
		return (false);
	}
	if (numChecked > atMost) {
		alertText = beginAlertSelectAtMost + atMost + endAlertSelectAtLeastMost
		return (false);
	}
	return true
}


function isEmail() {
/***********************
Desc: validates that the single or multiple emails are correctly formatted
*/

	// the object cannot be created if at the very least, an element name is not provided
	if (arguments[0] + "" == "undefined") {
		alert("ERROR: Cannot validate isEmail as no value was provided!")
		return false
	}
	//get args
	var str = arguments[0]
	var blnList = (arguments[1] + "" == "undefined")? false: arguments[1];


	//replace all commas with ; for testing
	var re = /,/
	str = str.replace(re,";")

	//email validation regexp
	//re = /^\w+(\.{1}\w+)*@\w+(\.{1}\w+)*[com|ca]$/
	// to support hyphens in the name
	re = /^\w+((\.|\-){1}\w+)*@\w+((\.|\-){1}\w+)*\.{1}(com|ca|fr)$/

	if (blnList) {
		var aList = str.split(";")
		for (var i = 0; i < aList.length; i++) {
			if (!re.test(trim(aList[i]))) return false;
		}
	}else{
		return(re.test(str))
	}
	return true
}


function isState(str,arg1) {
/***********************
Desc:
*/
	var str = arguments[0].toUpperCase();
	var strCountry = arguments[1]+"" == "undefined" ? "CA" : arguments[1];
	var validStates	= "";
	if (strCountry.indexOf("CA") >-1) {
		validStates += "|AB|BC|LB|MB|NB|NF|NS|NT|ON|PE|QC|SK|YT|"
	}
	if (strCountry.indexOf("US") >-1) {
		validStates += "|AK|AL|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL|IN|KS|KY|LA|"
		validStates += "|MA|MD|ME|MI|MN|MO|MS|MT|NB|NC|ND|NH|NJ|NM|NV|NY|OH|OK|"
		validStates += "|OR|PA|RI|SC|SD|TN|TX|UT|VA|VT|WA|WI|WV|WY|"
	}
	if (strCountry.indexOf("AU") >-1) {
		validStates += "|ACT|NT|ST|NSW|VIC|TAS|QLD|WT|"
	}
	if (validStates.indexOf("|"+str+"|") >-1) return true;
}



function isZipCode(str)	{
/***********************
Desc:
*/
  var l	= str.length;
  if ((l != 5) && (l !=	10)) { return false }
  for (j=0; j<l; j++) {
    if ((l == 10) && (j	== 5)) {
      if (str.charAt(j)	!= "-")	{ return false }
    } else {
      if ((str.charAt(j) < "0")	|| (str.charAt(j) > "9")) { return false }
    }
  }
  return true;
}


function isPostalCode(oElement, arg1){
/***********************
Desc: validates Canadian postal codes (for the time being)
*/
	var str = oElement.value.toUpperCase()
	//var str = formatPostalCode(oElement)
	//oElement.value = str
	var strCountry = arguments[1]+"" == "undefined" ? "CA" : arguments[1];
	switch (strCountry){
	case "CA":
	case "US":
	case "HK":
	case "FR":
		// accepts (555) 555-1212 or 555-555-1212
		var re = /^[A-Z][0-9][A-Z] ?[0-9][A-Z][0-9]$/i;
	}
	return(re.test(str))
}





function isPhone(str, arg1) {
/***********************
Desc: validates the phone number format
*/
//

	var strCountry = arguments[1]+"" == "undefined" ? "CA" : arguments[1];
	switch (strCountry){
	case "CA":
	case "US":
	case "HK":
	case "FR":
		// accepts (555) 555-1212 or 555-555-1212
		var re = /^\(?\d{1,3}\)?\s|-\d{3,4}-\d{4}$/;
	}

	return(re.test(str))
}



function isDate(str,pFormat) {
/***********************
Desc: Validates that a string contains only
    valid dates with 2 digit month, 2 digit day,
    4 digit year. Date separator can be ., -, or /.
    Uses combination of regular expressions and
    string parsing to validate date.
    Ex. mm/dd/yyyy or mm-dd-yyyy or mm.dd.yyyy
	Ex. yyyy/mm/dd or yyyy-mm-dd or yyyy.mm.dd

PARAMETERS:
   str - String to be tested for validity
   pFormat - (optional) SI (default) or US

RETURNS:
   True if valid, otherwise false.

REMARKS:
   Avoids some of the limitations of the Date.parse()
   method such as the date separator character.

ORIGINAL AUTHOR: Karen Gayda, KGayda@yahoo.com
*************************************************/
	// validate numeric format
	var format = pFormat+""	== "undefined"?"SI":pFormat;
	if (format=="US"){
		var re = /^\d{1,2}(\-|\/)\d{1,2}\1\d{4}$/;
	}else{
	    var re = /^\d{2,4}(\-|\/)\d{1,2}\1\d{1,2}$/;
	}

	// then check for correct values
	if(!re.test(str)){
		return false; //doesn't match pattern, bad date
	}else{
		var arrayDate = str.split(RegExp.$1); //split date into month, day, year
		if (format=="US"){
			// MM/DD/YYYY
			var intYear = parseInt(arrayDate[2],10);
			var intMonth = parseInt(arrayDate[0],10);
			var intDay = parseInt(arrayDate[1],10);
		}else{
			// YYYY/MM/DD
			var intYear = parseInt(arrayDate[0],10);
			var intMonth = parseInt(arrayDate[1],10);
			var intDay = parseInt(arrayDate[2],10);
		}
		//alert(intYear + ":" + intMonth + ":" + intDay)

		//check for valid month
		if(intMonth > 12 || intMonth < 1) {
			return false;
		}

		//create a lookup for months not equal to Feb.
		//check if month value and day value agree
		var arrayLookup = {1:31, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31}
		if(arrayLookup[intMonth] != null) {
			if(intDay <= arrayLookup[intMonth] && intDay != 0) {
				return true; //found in lookup table, good date
			}
		}

		//check for February
		var blnLeapYear = (intYear % 4 == 0 && (intYear % 100 != 0 || intYear % 400 == 0));
		if( ((blnLeapYear && intDay <= 29) || (!blnLeapYear && intDay <=28)) && intDay !=0) {
			return true; //Feb. had valid number of days
		}
		return false; //any other values, bad date
	}
}



/*
function isDateDiff(){
	// validate the difference between two dates is equal to a specified datepart
	/*
	var one_day = 1000*60*60*24

	this.max = (arguments[2] + "" == "undefined")? 0: parseInt(arguments[2],19);
	this.ms1 = Date.parse(arguments[0])
	this.ms2 = Date.parse(arguments[1])
	this.d = getDateDiff(arguments[0],arguments[1],"d")


	if (this.d <= this.max){
		alert(this.d + "::" + this.max)
		return true
	}else{
		return false
	}
}
*/


function isCompareDate(){
	// this function will compare two dates based on an operand
	//alert(arguments[0] + "] [" +arguments[1] + "] [" +arguments[2] + "]" )
	if (arguments[0] + "" == "undefined" || arguments[0] + "" == "") {
		alert("ERROR: Cannot compare without a value!")
		return false
	}
	if (arguments[1] + "" == "undefined" || arguments[1] + "" == "") {
		alert("ERROR: Cannot compare without a value to compare it to!")
		return false
	}
	this.date1 = new Date(arguments[0])
	this.date2 = new Date(arguments[1])
	this.type = (arguments[2] + "" == "undefined")? kE: arguments[2];

	if (isDate(this.date1) && isDate(this.date2) ){
		return false
	}else if (this.date1 == "NaN" || this.date2 == "NaN"){
		return false
	}

	// testing with appropriate operator
	switch (this.type) {
	case kGT:
		return (this.date1.getTime() > this.date2.getTime())
	case kGTE:
		return (this.date1.getTime() >= this.date2.getTime())
	case kLT:
		return (this.date1.getTime() < this.date2.getTime())
	case kLTE:
		return (this.date1.getTime() <= this.date2.getTime())
	case kE:
		return (this.date1.getTime() == this.date2.getTime())
	case kDiff:
		return (this.date1.getTime() != this.date2.getTime())
	}

	// it should have returned already
	return false
}


function isRange(str,pMin,pMax){
/***********************
Desc: verify if str is in acceptable range
From: Javascript Bible , p592
*/

	var num = parseFloat(str)
	if (num < pMin || num > pMax){
		return false
	}
	return true
}


function isAmount(str,pDecimals){
/***********************
Desc:
*/
}

function isCurrency(str,pDecimals){
/***********************
Desc:
*/
}



/***************************************************************
** VALIDATION OF THE FORM ITSELF
** The validateForm() function validates the form elements
** previously defined as validation objects and	as members of
** the elements array. We loop through the elements array, testing each
** element in turn, and alerting the user when they've missed
** a required field.
***************************************************************/
function validateForm(form) {

	if (elts.length <= 0) {
		// there are no validation objects, therefore provide help information
		showHelpInfo()
		return false
	}

	// preparation

	// FOR BACKWARDS COMPATIBILITY (OLD NAMES)
	curPanel = (curPanel + "" == "undefined")? 0: curPanel;
	numPanel = (numPanel + "" == "undefined")? 0: numPanel;

	// for non tabbed forms
	// TODO: when new names have been used for a while, rearrange this area
	activePanelNum = (activePanelNum+""=="undefined")?curPanel:activePanelNum;
	panelCount = (panelCount+""=="undefined")?numPanel:panelCount;

	//resetTabIcons()		// to their original state

	// go ahead with validation
	// go through all validation objects
	for (i=0; i<elts.length; i++) {
		elementName = elts[i].elementName
		oElement = eval("theForm."+elementName)
		oElmType = elts[i].elementType
		oElmPanel = elts[i].oElmPanel
		oTab = document.getElementById(oElmPanel + "tab")
		oTabIcon = document.getElementById(oElmPanel + "val")
		strFormat = elts[i].validFormat
		required = eval(elts[i].required)
		errPos = elts[i].errPos
		BRbefore = (errPos == kAbove)?"":"<BR>";
		BRafter  = (errPos == kAbove)? "<BR>":"";


		var tmpElm = eval("theForm." + elementName);
		if (tmpElm+"" == "undefined") {
			alert("ERROR: Cannot validate this object [" + elementName + "] as is not on the form!")
			continue;
		}

		// reset the error spans
		eval("err"+elementName).innerHTML = "";

//		alert("Validation on " + theForm.name + "." + elementName +": [" + i + "] " + elts[i].elementValidation + ": " + eval(elts[i].elementValidation))
		if (blnDebug) alert("Validation on " + theForm.name + "." + elementName +": [" + i + "] " + elts[i].elementValidation + ": " + eval(elts[i].elementValidation))



		// TEXT VALIDATION
		// sometimes a disabled hidden field is uses for combined validations
		// for type=file, we can validate the value as a string, not as a file
		if (oElmType == "text" || oElmType == "textarea" || oElmType == "hidden" || oElmType == "file") {

			str= trim(oElement.value);

			if (required==false && str== "") continue;  	// skips to the next validation object (For loop)

			if (eval(elts[i].elementValidation)) continue;

			// value is missing, valid format is not shown
			if (str== "") {

				// show Required only if valid format string is empty
				alertText = (strFormat == null)? beginRequestAlertForText + endRequestAlert: beginFormatAlert + strFormat + endFormatAlert;
			} else {

				// string is not empty but format may not be valid
				alertText = beginInvalidAlert + endInvalidAlert
				// add Format string if one was provided with the validation object
				if (strFormat != null) alertText += beginFormatAlert + strFormat + endFormatAlert

			}

		} else {
			// ALL OTHER validations: checkbox, radio, select list...
			if (eval(elts[i].elementValidation)) continue;
			// alertText is set in each validation function because it cannot be reset during validation: isCheck, isRadio, isSelect
		}


		// ERRORS FOUND (valid tests bypass to next For loop)

		// display error text and icon (only if preloaded)
		eval("err"+elementName).innerHTML += BRbefore + alertText + BRafter;
		//if (oTabIcon) oTabIcon.src = imgValErr;
		//setTabIcon(oTabIcon, -1)

		// for all forms the firstMissingElt needs to be initialized only once
		if (allAtOnce && firstMissingElt == null) {
			var firstMissingElt = new Array(oElement,elementName,oElmType,oElmPanel);
		}


		// set the object to get focused to either the first missing element or the current one.
		goToObj = (allAtOnce) ? firstMissingElt : new Array(oElement,elementName,oElmType,oElmPanel);


		if (!allAtOnce && alertText != "") {
			setFocus(goToObj)
			return false;
		}


	} // end validation FOR


	// reset error text and scoll up to show focused error
	if (allAtOnce && alertText != "") {
		setFocus(goToObj)
		alertText="";
		return false;
	}


//	if (blnDebug) alert("I am valid!"); //remove this when you use the code)
//	return false; //for debuggin purposes...
	return true; //change this to true to send the form
}




function scrollUp(){
	// because the focus can only be set on form elements, not layers
	if (errPos == kAbove) document.body.scrollTop = document.body.scrollTop - 20;
}

function setFocus(){
	var tmpElm = arguments[0][0]		//oElement
	var tmpElmName = arguments[0][1]	//oElement
	var tmpType = arguments[0][2]		//oElmType
	var tmpPanel = arguments[0][3]		//oElmPanel

	// give focus to the first element in error
	// on tabbed forms, this means on the visible panel
	// on non tabbed forms, this means on the form (activePanelNum==0)
	if (blnDebug) alert(tmpElmName + " active panel: " + (tmpPanel == "panel"+activePanelNum) +"\nactivePanelNum=0: "+ (activePanelNum == 0))

	if (tmpPanel == "panel"+activePanelNum || activePanelNum == 0){
		switch (tmpType){
		case "hidden":
			window.scrollTo(0,eval("err"+tmpElmName).offsetTop + 50)
			break;
		case "checkbox":
		case "radio":
			// when a radio or checkbox group contains only 1 item , the length is undefined
			if (tmpElm.length > 0){
				if (tmpElm[0].disabled){
					window.scrollTo(0,theForm.offsetTop + 50)
				}else{
					tmpElm[0].focus()
				}
				break;
			}else{
				//only 1 in the set
				//will be caught by default case
			}
		default:
			if (tmpElm.disabled) {
				window.scrollTo(0,theForm.offsetTop + 50)
			}else{
				tmpElm.focus()
			}
		}

		scrollUp()
	}else{
		window.scrollTo(0,theForm.offsetTop + 50)
	}
}




function ltrim ( s ){
/************************
returns clean string leaving out leading blanks
*/

	return s.replace( /^\s*/, "" );
}

function rtrim ( s2 ){
/************************
returns clean string leaving out trailing blanks
*/

	return s2.replace( /\s*$/, "" );
}


function trim ( s ){
/************************
returns clean string after left and right trims
*/
	return rtrim(ltrim(s));
}




if (blnDebug) alert("formValidator loaded");
