// Note: Date.setFullYear() requires JavaScript 1.2.
// This file containts code for client-side validations.
// When validating dates, dates.js is also required.
// Constants, configurable values, and values that 
// would change for internationalization appear at the top.
// JavaScript names of input field types; 
// we only validate these types of fields:

var htmlTypeSingleSelect = "select-one";
var htmlTypeRadio = "radio";
var htmlTypeText = "text";
var htmlTypeTextArea = "textarea";
var htmlTypePassword = "password"; // treat password like text.
//var htmlTypeHidden = "Hidden"; //enviromental, session, variables storage.
//var htmlTypeCheckbox = "Checkbox"; //many shopping carts deploy this style
//var htmlTypeForm = "form"; //All form elements available

// Errors only for now; In case we later want 
// to add valMsgType's of info or warning...
var valMsgTypeError = "1";
//var valMsgTypeWarning = "2";
//var valMsgTypeInfo = "3";

// For floats, money:
var decimalPointDelimiter = "."
var thousandsDelimiter = ",";
var digitsAfterDecimalPoint = 2;
// For Zip Code format:
var zipCodePics =new Array();
    zipCodePics[0]= "99999";
    zipCodePics[1]= "99999-9999";
    zipCodePics[2]= "33069-2741";
// For Tax ID format:
var taxIDPics = new Array();
    taxIDPics[0] = "999-99-9999";
    taxIDPics[1] = "99-9999999";
var taxIDForMsg = "999-99-9999 or 99-9999999";
// Phone format accept pictures with optional extensions of varying
// lengths like these 999-999-9999 or (999)999-9999 
var phonePics = new Array();
    phonePics[0] = "999-999-9999";
    phonePics[1] = "999-999-9999 x9";
    phonePics[2] = "999-999-9999 x99";
    phonePics[3] = "999-999-9999 x999";
    phonePics[4] = "999-999-9999 x9999";
    phonePics[5] = "999-999-9999 x99999";
    phonePics[6] = "(999)999-9999";
    phonePics[7] = "(999)999-9999 x9";
    phonePics[8] = "(999)999-9999 x99";
    phonePics[9] = "(999)999-9999 x999";
    phonePics[10] = "(999)999-9999 x9999";
    phonePics[11] = "(999)999-9999 x99999";
var phoneForMsg = "999-999-9999 or (999)999-9999 or 999-999-9999 x9999";

// Messages displayed to the user:
// Token represent el.title or el.name, used once per message.
// If changed, change the token in all messages below.
var valMsgFieldToken = "%s";
var topMsgBdr = "============================================\n";
var botMsgBdr = "\n============================================\n";
// Messages; make the format consistent; 
// 1) error, warning, or info; 
// 2) in which field; 
// 3) colon; 
// 4) message on what to do (start with verb); 
// 5) include format or description for valid value.

var valMsgSelectRequired = "Error in %s field:  Select one of the items in the list";
var valMsgRequired = "Error in %s field:  Enter a value in this required field";
var valMsgNumericPositive = "Error in %s field:  Enter a positive whole number";
var valMsgNumericPositiveOrZero = "Error in %s field:  Enter a positive whole number or zero";
var valMsgEmail = "Error in %s field:  Enter an email address in the form 'a@b.c'";
var valMsgMoney = "Error in %s field:  Enter a dollar amount, for example 1500 or 1500.00. Do not include commas";
var valMsgMinMaxNumber = "Error in %s field:  Enter a number"; 

// Number is either a whole number or decimal.
// Other more specific messages already used.
// Can now be less specific about the type.
var valMsgMinMaxGreater = " greater than or equal to ";
var valMsgMinMaxLess = " less than or equal to ";
var valMsgMinMaxAndLess = " and less than or equal to ";
var valMsgTaxID = "Error in %s field:  Enter a Tax ID# number in the form '" + taxIDForMsg + "'";
var valMsgPhone = "Error in %s field:  Enter a phone number in the form '" + phoneForMsg + "'"
var valMsgPicture = "Error in %s field:  Enter a picture in the form ";

var formErrorsMsg = ""+topMsgBdr+"Please correct the following error(s) then try again:"+botMsgBdr+"";

var radioSetMsg = "Select one of the radio buttons corresponding to your choice";
var noRadioSetMsg = "There are no rows available to select so you cannot proceed here";

// Form types:
var typeFormRadioList = "FormRadioList";  // The form is a radio button list
//var typeFormCheckboxList = "";  // The form is a checkbox list
//var typeFormQuarySearch = "";  // Quary Search form
//var typeFormHiddenValue = "";  // HiddenValue type for enviromental or session storage
//var typeFormAllElement = "";  // Standard type all form elements avaliabe

// Text/textarea field subtypes:
var typeText = "Text"; // treat password like text.
var typeDate = "Date";
var typeNumericPositive = "NumericPositive";
var typeNumericPositiveOrZero = "NumericPositiveOrZero";
var typeEmail = "Email";
var typeTaxID = "TaxID";
var typePhone = "Phone";
var typePicture = "Picture";
var typeMoney = "Money";

// Caption specification here
var txt;
var note;
var requiredColorName = "<b>red</b>";
var requiredImg = "<IMG src='./images/required_alt.gif'>";
var requiredColor = "<font color ='#cc0033'>";
var requiredColorEnd = "</font>";
var captionFontTag = "<div class='clickable' id='captNormal'>";
var captionFontTagEnd = "</div>";
//var captionAnchor = "<a href='#' onClick='alert(" + note + "); return false;'>";
var captionAnchorEnd = "</a>";


function writeNewCaption(title, required, note){
	if (required){
		document.write(captionFontTag + requiredImg);
		document.write("<a href=\"#\" onClick=\"alert('" +note+ "'); return false;\">");
		document.write(title + captionAnchorEnd + captionFontTagEnd);
	} else {
		document.write(captionFontTag, title, captionFontTagEnd);
	}
}

function writeCaption(title, required){
	if (required){
		document.write(captionFontTag, requiredImg, title, captionFontTagEnd);
	} else {
		document.write(captionFontTag, title, captionFontTagEnd);
	}
}


function writeRequiredNote(){
	document.write("Captions precided by " + requiredColor + "<b>red</b>");
	document.write(requiredColorEnd + "(" + requiredImg + ")");
	document.write("indicate required fields");
}

// Removes all characters which appear in string bag from string s.
// Loop through characters one by one.
// If character is not in bag, append to returnString.
// Check that current character isn't in bag.
function stripCharsInBag(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 isLetter(c){   
	return ( ((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")) )
}
// Email address must be of form a@b.c -- in other words:
// * one character before @ so we start looking at character position 1
// * one character before and after .
// *  @ and . are required
function isEmail(s){
    var i = 1;
    var sLength = s.length;

    // look for @
    while ((i < sLength) && (s.charAt(i) != "@")){ 
			i++
    }
    if ((i >= sLength) || (s.charAt(i) != "@")) return false;
    else i += 2;
    // look for .
    while ((i < sLength) && (s.charAt(i) != ".")){ 
			i++
    }
    // there must be at least one character after the .
    if ((i >= sLength - 1) || (s.charAt(i) != ".")) return false;
    else return true;
}
function isAlphabetic(s){
   var i;
    // Search through string's characters one by one
    // until we find a non-alphabetic character.
    // When we do, return false; if we don't, return true.
    for (i = 0; i < s.length; i++){   
        // Check that current character is letter.
        var c = s.charAt(i);
        if (!isLetter(c))
        return false;
    }
    // All characters are letters.
    return true;
}
function isDigit(c){   
	return ((c >= "0") && (c <= "9"))
}
// Returns true if all characters in string s are numbers.
// Accepts non-signed integers only. Does not accept floating 
// point, exponential notation, etc.
// Ignores commas (thousandsDelimiter).
// We don't use parseInt because that would accept a string
// with trailing non-numeric characters.
function isInteger(s){   
	var i;
    // Search through string's characters one by one
    // until we find a non-numeric character.
    // When we do, return false; if we don't, return true.
    for (i = 0; i < s.length; i++){  
        // Check that current character is number.
        var c = s.charAt(i);
        if ( (!isDigit(c)) && (c != thousandsDelimiter) ) return false;
    }
    // All characters are numbers.
    return true;
}
function isNonnegativeInteger(s){
	return ((isInteger(s)) && (parseInt (stripLeadZero(s)) >= 0));
}
function isPositiveInteger(s){
    return ((isInteger(s)) && (parseInt (stripLeadZero(s)) > 0));
}
function isIntegerInRange(s, a, b){
		// Catch non-integer strings to avoid creating a NaN below,
		// which isn't available on JavaScript 1.0 for Windows.
    if (!isInteger(s)) return false;
		// Now, explicitly change the type to integer via parseInt
		// so that the comparison code below will work both on 
		// JavaScript 1.2 (which typechecks in equality comparisons)
		// and JavaScript 1.1 and before (which doesn't).
    var num = parseInt (stripLeadZero(s));
    return ((num >= a) && (num <= b));
}
function isFloat(s, money){   
	var i;
    var decimalPointPos = 0;
	if (money){
		if (s.charAt(0) == decimalPointDelimiter) return false;
	} else {
		if (s == decimalPointDelimiter) return false;
	}
    // Search through string's characters one by one
    // until we find a non-numeric character.
    // When we do, return false; if we don't, return true.
    for (i = 0; i < s.length; i++){   
        // Check that current character is number.
        var c = s.charAt(i);
        if ((c == decimalPointDelimiter) && !decimalPointPos) decimalPointPos = i;
        else if ( (!isDigit(c)) && (c != thousandsDelimiter) ) return false;
    }
    // All characters are numbers.
	if ((money) && (decimalPointPos)){
		if ((s.length - decimalPointPos) != (digitsAfterDecimalPoint + 1)){
			return false;
		}
	}
    return true;
}
// Picture characters that may be used follow.
// Any other character is taken to be a literal and must appear in the subject string.
// Pictures specify character-by-character what the subject string should look like;
// This means that the subject string must match the length of the picture.
var picAnyChar = '?';
var picNumChar = '9';
var picAlphaChar = 'a';
function valPicture(str, picArray){
	var pic;
	var validPic;
	for (var picIx = 0; picIx < picArray.length; picIx++){
		pic = picArray[picIx];
		if (str.length != pic.length){
			continue;
		}
		validPic = true;
		for (var i = 0; i < str.length; i++){
			strChar = str.charAt(i);
			picChar = pic.charAt(i);
			if (picChar == picAnyChar){
				continue;
			}
			if (picChar == picNumChar){
				if (!isDigit(strChar)){
					validPic = false;
					break;
				} else {
					continue;
				}
			}
			if (picChar == picAlphaChar){
				if (!isAlphabetic(strChar)){
					validPic = false;
					break;
				} else {
					continue;
				}
			}
			if (picChar != strChar){  // if not one of the above characters, need an exact match
				validPic = false;
				break;
			} else {
				continue;
			}
		}
		if (validPic){
			return true;
		}
	}
    return false;
}
function valEmail(str){
	return (isEmail(str));
}
function valTaxID(str){
	return (valPicture(str, taxIDPics));
}
function valPhone(str){
	return (valPicture(str, phonePics));
}
function valFloat(str){
	return (isFloat(str, false));
}
function valMoney(str){
	return (isFloat(str, true));
}
function valNumericPositive(str){
	return (isPositiveInteger(str));
}
function valNumericPositiveOrZero(str){
	return (isNonnegativeInteger(str));
}
function stripHtml(s){
	var stripping = false;
	var rtn = "";
    for (var i = 0; i < s.length; i++){
		if (s.charAt(i) == '<'){
			stripping = true;
		} else {
			if (stripping){
				if (s.charAt(i) == '>'){
					stripping = false;
				}
			} else {
				rtn += s.charAt(i);
			}
		}
	}
	return rtn;
}
function toMoney(val){
	var rtn = val + "";
	var decpos, decimals;
	decpos = rtn.indexOf(decimalPointDelimiter);
	if (decpos != -1){
		decimals = ((rtn.length - decpos) - 1);
		if (decimals == 1){
			rtn = rtn + "0";
		} else {
			if (decimals > 2){
				rtn = rtn.substring(0, (decpos + 3));
			}
		}
	}
	return rtn;
}
function stripThousandsDelimiter(str){
	return (stripCharsInBag(str, thousandsDelimiter));
}
function valPrepMsg(el){
	var tokenStartPos = el.valMsg.indexOf(valMsgFieldToken);
	if (tokenStartPos == -1){
		return el.valMsg;
	} else {
		var msg = el.valMsg.substring(0,tokenStartPos)
			+ (((el.valTitle != null) && (el.valTitle != "")) ? stripHtml(el.valTitle) : "'" + el.name + "'")
			+ el.valMsg.substring((tokenStartPos + valMsgFieldToken.length), el.valMsg.length);
		return msg;
	}
}
// TODO this causes both field and URL to be selected in IE 4 
function valFocusAndSelect(el){		
	el.focus(); 
	if ((el.type == htmlTypeText) || (el.type == htmlTypeTextArea) || (el.type == htmlTypePassword)){
		el.select(); // select fields text  
	}
}
//COULD USE THIS TO GET ERRORS
//function passErrors(field){
//	for(i = 0; i < field.legnth; i++){
//		alert(document.frm.field[i].name);
//	}
//}

function valDisplayFormErrors(msg, formErrors, firstField){
	
	if (firstField != null){
		valFocusAndSelect(formErrors[firstField]);
	}
	alert(msg);
}
function valDisplayFieldError(el){
	
	 valFocusAndSelect(el);
   msg = valPrepMsg(el);
alert(msg);
}
function formErrorsSortFunc(a, b){
	if ((a.valSeq != null) && (b.valSeq != null)){
		return (a.valSeq - b.valSeq);
	} else {
		return 0;
	}
}
function valForm(f){
	var formErrors = new Array();
	var formErrorsIndex = 0;
	var formErrorsAllMsgs = formErrorsMsg;
	var radioSet = new Array();
	var radioFound = false;
	for (var i = 0; i < f.length; i++){
		var el = f.elements[i];
		if (el.type == htmlTypeRadio){
			var radioFound = true;
			if (!radioSet[el.name]){
				radioSet[el.name] = 0;
			}
			if (el.checked){
				radioSet[el.name]++;
			}
		} else {
			var fieldOk = valField(el);
			if (!fieldOk){
				formErrors[formErrorsIndex++] = el;
			}
		}
	}
	for (rSet in radioSet){
		if (radioSet[rSet] != 1){
			var pseudoEl = new Object();
			pseudoEl.valSeq = -1;
			valSetError(pseudoEl, radioSetMsg);
			formErrors[formErrorsIndex++] = pseudoEl;
			break;
		}
	}
//	if ((!radioFound) && (f.valFormType == typeFormRadioList)){ 
//		var pseudoEl = new Object();
//		pseudoEl.valSeq = -1;
//		valSetError(pseudoEl, noRadioSetMsg);
//		formErrors[formErrorsIndex++] = pseudoEl;
//	}
	if (formErrorsIndex > 0){
		formErrors.sort(formErrorsSortFunc);
		var firstField = null;
		for (var i = 0; i < formErrors.length; i++){
			formErrorsAllMsgs += "\n" + valPrepMsg(formErrors[i]);
			if (formErrors[i].valSeq >= 0){
				if (firstField == null){
					firstField = i;
				}
			}
		}
		valDisplayFormErrors(formErrorsAllMsgs, formErrors, firstField);
		return false;
	} else {
		return true;
	}
}
function valSetError(el, msg){
	el.valMsg = msg;
	el.valMsgType = valMsgTypeError;
	//alert(valMsgTypeError);
	return false;
}
function valField(el){
	if (el.valIgnore){
		return true;
	}
	//if (el.type == htmlTypeSingleSelect){
    	// Navigator 4 incorrectly sets element.type to select-multiple 
		// for "<select size="2">" so can't do this edit
		// on that browser because real select-multiple 
        //lists ("<select MULTIPLE>") do not require any items to be selected
		//setBrowser();
		//if ((isIE) && (el.selectedIndex == "-1")){
			//return valSetError(el, valMsgSelectRequired);
		//}
	//}
	if ((el.type == htmlTypeText) || (el.type == htmlTypeTextArea) || (el.type == htmlTypePassword)){
		var valueIsBlank = (isEmpty(el.value));
		if ((el.valRequired) && (valueIsBlank)){
			return valSetError(el, valMsgRequired);
		}
		if (valueIsBlank){
			return true;
		}
		if (el.valType == typeDate){
			if (!valDate(el.value)){
				return valSetError(el, valMsgDateFormat);
			} else {
				return true;
			}
		}
		if (el.valType == typeNumericPositive){
			if (!valNumericPositive(el.value)){
				return valSetError(el, valMsgNumericPositive);
			} else {
				// continue; could also have min/max edit
			}
		}
		if (el.valType == typeNumericPositiveOrZero){
			if (!valNumericPositiveOrZero(el.value)){
				return valSetError(el, valMsgNumericPositiveOrZero);
			} else {
				// continue; could also have min/max edit
			}
		}
		if (el.valType == typeEmail){
			if (!valEmail(el.value)){
				return valSetError(el, valMsgEmail);
			} else {
				return true;
			}
		}
		if (el.valType == typeTaxID){
			if (!valTaxID(el.value)){
				return valSetError(el, valMsgTaxID);
			} else {
				return true;
			}
		}
		if (el.valType == typePhone){
			if (!valPhone(el.value)){
				return valSetError(el, valMsgPhone);
			} else {
				return true;
			}
		}
		if (el.valType == typePicture){
			if (!valPicture(el.value, el.valPictureArray)){
				valSetError(el, valMsgPicture);
				el.valMsg += ("'" + el.valPictureForMsg + "'");
				return false;
			} else {
				return true;
			}
		}
		if (el.valType == typeMoney){
			if (!valMoney(el.value)){
				return valSetError(el, valMsgMoney);
			} else {
				// continue; could also have min/max edit
			}
		}
		if ((el.valMin) || (el.valMax)){
			var num = parseFloat(el.value);
			if ((isNaN(num))
				|| (!valFloat(el.value))
				|| ((el.valMin) && (num < el.valMin))
				|| ((el.valMax) && (num > el.valMax))){
				valSetError(el, valMsgMinMaxNumber);
				if (el.valMin){
					el.valMsg += (valMsgMinMaxGreater + el.valMin);
				}
				if ((el.valMax) && (el.valMin)){
					el.valMsg += (valMsgMinMaxAndLess + el.valMax);
				} else {
					if (el.valMax){
						el.valMsg += (valMsgMinMaxLess + el.valMax);
					}
				}
				return false;

			} else {
				return true;
			}
		}
	}
	return true;
}
// handler for any field to be validated onChange
function onChangeField(el){
	var fieldOk = valField(el);
	if (!fieldOk){
		//alert(el);
		valDisplayFieldError(el);
	}
}

//onFocus event handler highlight data in field
function selectContents(el){
	el.select(); 
}
