//  Copyright (c) 1999-2002 by A Plus Consultants, L.L.C.
//  No part of this script may be used for any purpose without prior
//  written permission. Contact richard@aplusconsultants.com.
/*
	JavaScript Functions: (PREPARED FOR COMPRESSION.)
	These functions all contribute to scripting form validation.

	This is a list of functions contained in this script:

	ListElements(oForm)
	Rule(name, label, type, req, func, min, max, msg)
	ErrType()
	InputCtrl(name, type, state, value)
	BuildInputCtrl(oForm, sName, sWhat, bDebug)
	sDisallow(sText, sLabel, sChars)
	sValidEmail(sMail, sLabel)
	sValidFileName(sText, sLabel, sExt)
	sValidHTML(sText, sLabel)
	sValidImageDimensions(arValue, sLabel)
	sValidPhone(sPhone, sLabel, sCountry)
	sValidState(sState, sLabel, sCountry)
	sValidWords(sValue, sLabel, sLogic)
	sValidZip(sZip, sLabel, sCountry, sFlag)
	sIsDate(sString, sLabel, iLow, iHigh)
	sIsLength(sString, sLabel, sType, iLow, iHigh)
	sIsNumber(sNumb, sLabel, sLow, sHigh)
	sIsPassword(sPW, sLabel, sUser)
	sIsTime(sString, sLabel, sLow, sHigh)
	nOfGroup(oForm, lstNames, iMin, iMax, bDebug)
	bCheckFormRules(oForm, aRuleArray, bDebug)
*/

function ListElements(oForm) {
/*
	This is a debugging helper function that lists each form element,
	its type, and its value.

	Sample call in a page:
	<script type="text/javascript">ListElements()</script>
*/
	var ii = 0;
	var sElements = "";
	var sTitle = "";
//	alert(oForm)
	if (arguments.length == 0)
		var oForm = document.forms["0"];

	sTitle = oForm.name + " Elements:";
	for (ii = 0; ii < oForm.elements.length; ii++) {
		sElements += "\n" + (ii + 1) + ". " + oForm.elements[ii].name + " (" + oForm.elements[ii].type + ") " + oForm.elements[ii].value;
		if ((ii + 1) % 20 == 0) {
			alert(sTitle + sElements);
			sElements = "";
		}
	}
	alert(sTitle + sElements);
}

function Rule(name, label, type, req, func, min, max, msg) {
/*
	This object holds the validation rules for a form element. An array of
	these objects will contain all the rules for a single form or section
	of a form.
*/
	this.name = name;	//	name:		name of form element
	this.label = label;	//	label:		label for element on form
	this.type = type;	//	data type:	s = string, n = number, d = date
	this.req = req;		//	required:	true of false
	this.func = func;	//	function:	name of a validation function
	this.min = min;		//	minimum:		for numbers a value for strings a length
	this.max = max;		//	maximum:		for numbers a value for strings a length
	this.msg = msg;		//	message:		user message for validation failure
}

function ErrType() {
	alert("Error");
	sType = "undefined";
	return true;
}

function InputCtrl(name, type, state, value) {
/*
	This object holds all the information about a particular form element.
	It deals with the vagaries of each form element type by consolidating
	the state and values for all types of elements in an array.
*/
	this.name = name;	//	form element name
	this.type = type;	//	form element type
	this.state = state;	//	array of states
	this.value = value;	//	array of values
}

function BuildInputCtrl(oForm, sName, sWhat, bDebug) {
/*
	This builds a custom object for the form object passed to it.
	It's purpose is to simplify coding for validation checking by making
	all input controls appear similar in stucture. Because JavaScript
	creates arrays of elements when their names are the same, the checking
	to determine whether an element can be referenced directly
	or as an array is eliminated when referring to this object.

	Parameters:
		oForm:	Form object containing the element.
		sName:	Element name.
		sWhat:	What property of the element to check (text, value).
		bDebug:	Whether or not to display debugging messages.

	Key Features:
		name:	Each object can be referred to by name.
		type:	Each object's type can referred to directly.
		state:	An array of indices having a checked or selected status.
		value:	An array of values corresponding to the state array indices.
*/
	var aStates = new Array();
	var aValues = new Array();
	var ii = 0;
	var iSelected;
	var jj = 0;
	var sType = "";
	sWhat = sWhat.toLowerCase();

	if (bDebug) {alert("BuildInputCtrl:\n" + "document." + oForm.name + "." + sName);}
//	alert(eval("document." + oForm.name + "." + sName));
//	window.onError = ErrType;
	//	This handles the situation of a non-existent element.
	if (eval("document." + oForm.name + "." + sName) == "undefined") {
		sType = "undefined";
	}
	//	This handles an IE problem. IE does not assign the 'undefined'
	//	string to the variable, so we have to do it manually.
	else if (oForm[sName]) {
		//	Added another test for the IE problem 11/21/2000.
		if (oForm[sName].type) {
			sType = oForm[sName].type;
		}
		else {
			sType = "undefined";
		}
	}
	else {
		sType = "undefined";
	}
//	window.onError = jsError;

	if (bDebug) {alert("BuildInputCtrl:\n" + "Element: " + sName + "\nType: " + sType);}
	aValues[jj] = "";	//	Make sure there is one value element in array.
	if (sType == "undefined") {
		//	This is the case for arrays of elements with the same name.
		//	These are usually checkboxes and radio buttons, but could be
		//	other types as well.
		for (ii = 0; ii < oForm.elements.length; ii++) {
			if (oForm.elements[ii].name == sName) {
				//	There is a possibility that elements of the same name
				//	could be of different types, but we are not allowing
				//	for that here.
				sType = oForm.elements[ii].type;
				if (sType == "checkbox" || sType == "radio") {
					if (oForm.elements[ii].checked) {
						aStates[jj] = ii;
						//	Note: This was supposed to be handled below,
						//	but it apparently isn't.
						aValues[jj] = oForm.elements[ii].value;
					}
				}
				else if (sType == "text") {
					aStates[jj] = ii;
				}
				else if (sType == "select-one") {
					iSelected = oForm.elements[ii].selectedIndex;
					aStates[jj] = iSelected;
					if (iSelected == -1) {
						aValues[jj] = "";
					}
					else {
						if (sWhat == "text") {
							aValues[jj] = oForm.elements[ii].options[iSelected].text;
						}
						else {
							aValues[jj] = oForm.elements[ii].options[iSelected].value;
						}
					}
					aStates[jj] = ii;
					jj++;
				}
				else {
//					aStates[jj] = null;
				}
				if (aStates[jj] && sType != "select-one") {
					aValues[jj] = oForm.elements[ii].value;
					jj++;
				}
			}
		}
	}
	else {
		if (sType == "checkbox" || sType == "radio") {
			if (oForm[sName].checked) {
				aValues[jj] = oForm[sName].value;
			}
		}
		else if (sType == "select-one") {
			iSelected = oForm[sName].selectedIndex;
			if (bDebug) {alert("BuildInputCtrl:" + "\nSelect Element: " + sName + "\nSelected Index: " + iSelected);}
			aStates[jj] = iSelected;
			if (iSelected >= 0) {
				if (oForm[sName].options.length) {
					if (oForm[sName].selectedIndex == 0) {
						//	This is hard-coded because of a problem.
						//	In this case, the empty value will be correct.
						if (sName != "Category") {
							if (sWhat == "text") {
								aValues[jj] = oForm[sName].options[iSelected].text;
							}
							else {
								aValues[jj] = oForm[sName].options[iSelected].value;
							}
						}
					}
					else {
						if (oForm[sName].options[iSelected] == null) {
							alert(sName + " is null: 157x " + oForm[sName].selectedIndex + " " + iSelected + " of " + oForm[sName].options.length);
						}
						else {
							if (sWhat == "text") {
								aValues[jj] = oForm[sName].options[iSelected].text;
							}
							else {
								aValues[jj] = oForm[sName].options[iSelected].value;
							}
						}
					}
				}
				else {
					alert("BuildInputCtrl:" + "\nSelect Element: " + sName + " has no options.");
				}
			}
		}
		else if (sType == "select-multiple") {
			for (ii = 0; ii < oForm[sName].length; ii++) {
				if (oForm[sName].options[ii].selected) {
					aStates[jj] = ii;
					if (sWhat == "text") {
						aValues[jj] = oForm[sName].options[ii].text;
					}
					else {
						aValues[jj] = oForm[sName].options[ii].value;
					}
					jj++;
				}
			}
		}
		else {
			aStates[jj] = "";
			aValues[jj] = oForm[sName].value;
		}
		if (bDebug) {alert("BuildInputCtrl:" + "\nValue Array Length: " + aValues.length + "\nValue Array: " + aValues);}
	}
	return new InputCtrl(sName, sType, aStates, aValues);
}

function sDisallow(sText, sLabel, sChars) {
	var aCharList = aBreakApart(sChars, "~");
	var ii = 0;
	for (ii = 0; ii < aCharList.length; ii++) {
		if (sText.indexOf(aCharList[ii]) >= 0) {
			return sLabel + " may not contain the following words or characters:\n\t '" + aCharList + "'";
		}
	}
	return "";
}

function sValidEmail(sMail, sLabel) {
	var reEmail = /^[\w_-]+(\.[\w_-]+)?@[\w_-]+(\.[\w_-]{2,16})+$/gi;
	//	! reEmail.test(sMail)
	if (sMail.search(reEmail) == -1) {
		return sLabel + " is not a valid format for internet e-mail addresses.";
	}
	if (sMail.length < 7) {
		return sLabel + " must be at least 7 characters.";
	}
	if (sMail.indexOf("@") == -1 || sMail.lastIndexOf(".") < sMail.indexOf("@")) {
		return sLabel + " is not in a valid format.";
	}
	return "";
}

function sValidFileName(sText, sLabel, sExt) {
/*
	02/16/2002:289 Added new validation function for file uploads.

	When used as a validation function, this prevents unauthoized file
	types from being uploaded to the server. It also enforces a rule
	against spaces and the hash character appearing in file names.

	An extension class is a predefined set of file extensions. Using the
	class allows a change to the class definition in one place without
	changing the code in server pages. An extension class is enclosed in
	braces. There are three classes currently defined: {doc}, {img}, and
	{media}. You cannot mix a class and loose extensions, nor can you
	have a list of classes, but I might allow those in a later version.

	Parameters:
	sText		: A complete file specification.
	sLabel		: User-friendly element label.
	sExt		: List of permissible extensions or an extension class.
*/

	var sFolderChar = "\\";
	var sMsg = "";

	//	02/17/2002:311 Change from a literal to a RegExp object. The
	//	literal caused Netscape 4.0x to cough on loading this js file.
	var re = new RegExp("[/]", "gi");
	if (sText.search(re) >= 0) {
		sFolderChar = "/";
	}

	if (sExt.substr(0, 1) == "{") {
		if (sExt == "{doc}") {
			sExt = "doc,pdf,txt,xls";
		}
		else if (sExt == "{img}") {
			sExt = "gif,jpeg,jpg,png";
		}
		else if (sExt == "{media}") {
			sExt = "ram";
		}
		else {
			sMsg = "Developer: Invalid file extension class.";
		}
	}
	var ay = sText.split(sFolderChar);
	//	The file name is the last element of the array.
	var sFileName = ay[ay.length - 1];
	if (sFileName.search(/[.]/gi) > 0) {
		ay = sFileName.split(".");
		sFileExt = ay[ay.length - 1];
		//	Recompose the file name.
		sFileName = "";
		for (ii = 0; ii < ay.length - 1; ii++) {
			sFileName = sFileName + ay[ii] + ".";
		}

		var reExt = new RegExp(sFileExt, "gi");
		if (sExt.search(reExt) == -1) {
			sMsg = sMsg + sFileExt + " is not a permitted " + sLabel + " extenstion." + "\n";
		}
		if (sFileName.search(/[ #]/gi) >= 0) {
			sMsg = sMsg + "Spaces and hashes (#) are not permitted in " + sLabel + " names." + "\n";
		}
	}
	else {
		sMsg = sLabel + "s must have extensions.";
	}
//	if (sMsg != "")
//		alert(sMsg);

	return sMsg;
}

function sValidHTML(sText, sLabel) {
	//	Permit an HTML subset only.
	//	Define an array of HTML tags that are permitted.
	var sTagList = "B,BIG,BR,DIV,FONT,H1,H2,H3,H4,HR,I,LI,OL,P,PRE";
	sTagList += "SMALL,SPAN,STRONG,SUB,SUP,TABLE,TD,TH,TR,U,UL";
	var aTags = sTagList.split(",");
	//	This locates any HTML tags in the string.
	var reTag = /\<\/?[A-Z]+[^>]*\>/gi;
	var sTag = "";
	var sTagX = "";
	var sRet = "";
	var ii = 0;
	var sBadTags = ",";
	sTag = sText.match(reTag);
	if (sTag == null) {
		//	There were no HTML tags in the string.
		return "";
	}
	for (ii = 0; ii < sTag.length; ii++) {
		sTagx = sTag[ii].substring(1, sTag[ii].length - 1);
		if (sTagx.indexOf("\n") != -1) {
			sTagx = sTagx.substr(0, sTagx.indexOf("\n"));
		}
		if (sTagx.indexOf(" ") != -1) {
			sTagx = sTagx.substr(0, sTagx.indexOf(" "));
		}
		if (sTagx.substr(0, 1) == "/") {
			sTagx = sTagx.substr(1, sTagx.length - 1);
		}
		var bOk = false;
		for (var jj = 0; jj < aTags.length; jj++) {
			//	If tag is in the list, exit the loop.
			if (sTagx.toLowerCase() == aTags[jj].toLowerCase()) {
				bOk = true;
				jj = aTags.length;
			}
		}
		//	Tag is not in the list.
		if (! bOk) {
			if (sRet.length == 0) {
				sRet = sLabel + " may not contain the following HTML tags:\n\t";
			}
			var sTest = "," + sBadTags + ",";
			if (sTest.indexOf("," + sTagx + ",") == -1) {
				sBadTags = sBadTags + sTagx + ",";
				sRet += " <" + sTagx + "> ";
			}
		}
	}
	return sRet;
}

function sValidImageDimensions(arValue, sLabel) {
/*
	This is tightly coupled with the elements on the form. There should be
	both a select-one list and a text box with the same element name. The
	text box is for the custom dimension.

	The allowable text box values must be a pair of two- or three-digit
	numbers separated by an 'x'. This also enforces an arbitray maximum
	480 pixel width and 320 pixel height.

	Parameters:
	arValue		: Array of select-one value and text box value.
	sLabel		: User-friendly element label.
*/

	var reDimension = /^[1-9][0-9]{1,2}x[1-9][0-9]{1,2}$/gi;
	if (arValue.length == 1) {
		//	There was no custom dimension on the form.
		return "";
	}
//	alert(arValue[0] + ", " + arValue[1]);
	var sVal = arValue[arValue.length - 1];
//	alert(sVal + ", " + sVal.search(reDimension));
	if (sVal == "") {
		//	A custom dimension was not passed.
		return "";
	}

//	alert(sVal + ", " + sVal.search(reDimension));
	if (sVal.search(reDimension) == -1) {
		return  "'" + sVal + "' is not valid for " + sLabel + " dimensions.";
	}
	var arVals = sVal.split("x");
	if (arVals[0] > 480 && arVals[1] > 320) {
		return sLabel + " dimensions are too large. Maximum 480 wide by 320 high.";
	}
	if (arVals[0] > 480) {
		return sLabel + " width is too large. Maximum width is 480.";
	}
	if (arVals[1] > 320) {
		return sLabel + " height is too large. Maximum height is 320.";
	}
	return "";
}

function sValidPhone(sPhone, sLabel, sCountry) {
/*
	Phone format for US and Canada is (999)999-9393 x12345. The
	first digit of the area code and the first digit of the exchange
	must be two or greater. The extension is optional, but must be
	separated from the number by a space and contain an 'x' followed
	by between one and five numbers.
	For international numbers, the format is (9999)9 999 999 999 or
	something similar. The country code must be enclosed in parentheses
	and begin with 1 or greater. The following number which is usually a
	city code made be from 1 to 4 digits in length. Following that are sets
	of numbers with from 2 to four digits preceded by a space, a period,
	or a dash. There can be anywhere from 2 to 6 of these sets and they
	can be followed by an extension in the same format as that in North
	America.
*/
	var sMsg = "";
	var reFax = /^\([2-9][0-9]{2}\)[2-9][0-9]{2}-[0-9]{4}$/gi;
//	var rePhone = /^\([2-9][0-9]{2}\)[2-9][0-9]{2}-[0-9]{4}( x[0-9]{1,5})?$/gi;
	var rePhone = /^[(]?[2-9][0-9]{2}[ )\/\.-]?[2-9][0-9]{2}[ \.-]{1}[0-9]{4}( x[0-9]{1,5})?$/gi;
	var reNonNA = /^\([1-9][0-9]{1,3}\)[0-9]{1,4}([ .-][0-9]{2,4}){2,6}( x[0-9]{1,5})?$/gi;
	var sChar = "";
	var sNum = "0123456789()-";

	//	Default to US, if country is not provided.
	if (arguments.length == 2) {
		sCountry = "US";
	}

	if (sPhone == "") {
		return "";
	}

	if (sLabel == "Fax") {
		if (sCountry != "US" && sCountry != "CA") {
			//	reNonNA.test(sPhone);
			if (sPhone.search(reNonNA) == 0) {
				return "";
			}
			else {
				sMsg = sLabel + " numbers outside the US and Canada must "
					+ "include a country code in parentheses followed by "
					+ "a pattern of numbers (in pairs or more) separated "
					+ "by either spaces or dashes.";
				return sMsg;
			}
		}
		else {
			//	reFax.test(sPhone);
			if (sPhone.search(reFax) == 0) {
				return "";
			}
			else {
				sMsg = "North American fax numbers must be formatted as (999)999-9999.";
				return sMsg;
			}
		}
	}
	else {
		if (sCountry != "US" && sCountry != "CA") {
			//	reNonNA.test(sPhone);
			if (sPhone.search(reNonNA) == 0) {
				return "";
			}
			else {
				sMsg = sLabel + " numbers outside the US and Canada must "
					+ "include a country code in parentheses followed by "
					+ "a pattern of numbers (in pairs or more) separated "
					+ "by either spaces or dashes.";
				return sMsg;
			}
		}
		else {
			//	rePhone.test(sPhone);
			if (sPhone.search(rePhone) == 0) {
				return "";
			}
			else {
				sMsg = "North American phone numbers must be formatted "
					+ "as (999)999-9999 x99999 where the extension is optional.";
				return sMsg;
			}
		}
	}

	if (sPhone.length < 13) {
		return sLabel + " must be at least 13 characters.";
	}
	if (sPhone.indexOf("(") != 0 || sPhone.indexOf(")") != 4 || sPhone.indexOf("-") != 8) {
		return sLabel + " is not in a valid format.";
	}
	for (var ii = 0; ii < 13; ii++) {
		sChar = sPhone.substring(ii, ii + 1);
		if (sNum.indexOf(sChar) == -1) {
			return "Only numbers and ()- are allowed in a " + sLabel + " number.";
		}
	}
	return "";
}

function sValidState(sState, sLabel, sCountry) {
/*
	For the United States and Canada, the State or Province is validated.
	Right now, there the state name is being saved in the table and not
	the code. Until this changes, we have to check against the name to
	determine the corresponding country.
*/
	var sCA = "AB,BC,MB,NB,NF,NS,NT,ON,PE,PQ,SK,YT";
	var sUS = "AL,AK,AR,AZ,CA,CO,CT,DE,DC,GA,FL,HI,ID,IL,IN,IA,KS,KY,LA,MA,ME,MI,MN,MT,MO,MS,MD,NC,ND,NE,NH,NJ,NM,NY,NV,OH,OR,OK,PA,RI,SC,SD,TN,TX,UT,VT,VA,WA,WI,WV,WY";
	var sCAName = "Alberta,British Columbia,Manitoba,New Brunswick,Newfoundland,Northwest Territories,Nova Scotia,Nunavit,Ontario,Prince Edward Island,Quebec,Saskatchewan,Yukon Territory";
	var sUSName = "Alabama,Alaska,Arkansas,Arizona,California,Colorado,Connecticut,Delaware,Florida,Georgia,Hawaii,Idaho,Illinois,Indiana,Iowa,Kansas,Kentucky,Louisiana,Maine,Maryland,Massachusetts,Michigan,Minnesota,Mississippi,Missouri,Montana,Nebraska,Nevada,New Hampshire,New Jersey,New Mexico,New York,North Carolina,North Dakota,Ohio,Oklahoma,Oregon,Pennsylvania,Rhode Island,South Carolina,South Dakota,Tennessee,Texas,Utah,Vermont,Virginia,Washington,West Virginia,Wisconsin,Wyoming,District of Columbia";

	if (arguments.length != 3) {
		sCountry = "US";
	}

	if (sCountry == "CA") {
		if (sCAName.indexOf(sState) == -1 || sState == "") {
			return "A province must be entered for Canada.";
		}
	}

	if (sCountry == "US") {
		if (sUSName.indexOf(sState) == -1 || sState == "") {
			return "A state must be entered for the United States.";
		}
	}

	if (sCountry != "US" && sCountry != "CA" && sState != "") {
		return "State must be blank, except for the United States and Canada.";
	}
	return "";
}

function sValidWords(sValue, sLabel, sLogic) {
/*
	When searching for words, we want to prevent certain short, common, or
	other words from being used. This handles the various types of
	situations.

	It works in conjunction with sLogic, which is the boolean logic
	selected by the visitor. Words that would not be allowed when searched
	for as individual words are allowed when being searched in a phrase.

	REMINDER: We want to parse out most punctuation from the value passed.
*/
	var aWords = sValue.split(" ");
	var ii = 0;
	var iInvalidLength = 1;
	var jj = 0;
	//	These words should probably be ordered in terms of likelihood so as
	//	to reduce processing time.
	var sCommonWords = "an,and,are,be,but,by,for,have,he,her,here,him,his,how,is,it,me,my,of,on,or,our,she,that,the,there,their,they,this,to,we,what,when,where,who,why,with,you,your";
	var sInvalidWords = "fuck.,shit.";
	var sTest = "";
	var sWord = "";

	//	If search logic has not been provided, default to the 'any' logic.
	if (arguments.length == 2) {
		sLogic = "OR";
	}

	if (sValue == "") {
		return "";
	}

	//	All words are allowed when 'Phrase' logic is selected and more than
	//	one word has been entered.
	if (sLogic == "Phrase" && aWords.length > 1) {
		return "";
	}

	//	Deal with short words, single characters.
	for (ii = 0; ii < aWords.length; ii++) {
		if (aWords[ii].length == 1) {
			return sLabel + " may not contain single characters, such as '" + aWords[ii] + "', unless you choose 'Exact Phrase' with more than one word.";
		}
	}

	//	Turn the word lists into arrays.
	var aCommonWords = sCommonWords.split(",");
	var aInvalidWords = sInvalidWords.split(",");
	for (ii = 0; ii < aWords.length; ii++) {
		sWord = aWords[ii].toLowerCase();
		//	Deal with common words from the list.
		for (jj = 0; jj < aCommonWords.length; jj++) {
			sTest = aCommonWords[jj].toLowerCase();
			if (sWord == sTest) {
				return sLabel + " may not contain common words, such as '" + sTest + "', unless you choose 'Exact Phrase' with more than one word.";
			}
		}
		//	Deal with other words that we don't want to allow.
		for (jj = 0; jj < aInvalidWords.length; jj++) {
			sTest = aInvalidWords[jj].toLowerCase();
			if (sTest.indexOf(".") >= 0) {
//				alert("Test: " + sTest + " " + sTest.indexOf("."));
				var re = new RegExp(sTest + "*", "gi");
//				alert("Test: " + sTest + " " + sWord.search(re));
				if (sWord.search(re) >= 0) {
					return sLabel + " may not contain words containing strings, such as '" + sTest + "', unless you choose 'Exact Phrase' with more than one word.";
				}
			}
			else {
				if (sWord == sTest) {
					return sLabel + " may not contain common words, such as '" + sTest + "', unless you choose 'Exact Phrase' with more than one word.";
				}
			}
		}
	}
	return "";
}

function sValidZip(sZip, sLabel, sCountry, sFlag) {
	var sChar = "";
	var sNum = "0123456789-";
	var reZip = /^([0-9]{5})(-[0-9]{4})?$/gi;
	var reCanada = /^[A-Z][0-9][A-Z] ?[0-9][A-Z][0-9]$/gi;
	var reOther = /^[A-Z 0-9,-]{3,10}$/gi;
	var sEurope = "AL,AD,AT,BE,BA,BG,HR,CZ,DK,EE,FI,FR,DE,GR,IS,IE,IT,LV,LI,LT,LU,MK,MT,MC,NL,NO,PL,PT,RO,RU,SM,SK,SI,ES,SE,CH,TR,UA,GB,YU";

	if (arguments.length < 3) {
		sCountry = "US";
	}

	if (arguments.length < 4) {
		sFlag = "noblank";
	}

	if (sZip == "" && sFlag == "blank") {
		return "";
	}

	if (sCountry == "CA") {
		//	reCanada.test(sZip);
		if (sZip.search(reCanada) == 0) {
			return "";
		}
		else {
			return "Canadian Postal Codes must contain numbers, letters, and spaces in a pattern of Z9Z 9Z9.";
		}
	}

	if (sCountry == "US") {
		//	reZip.test(sZip);
		if (sZip.search(reZip) == 0) {
			return "";
		}
		else {
			return "US ZIP Codes must be in the pattern 99999-9999. The dash and last four digits are optional.";
		}
	}

	if (sCountry != "US" && sCountry != "CA") {
		//	reOther.test(sZip);
		if (sZip.search(reOther) == 0) {
			return "";
		}
		else {
			//	Postal codes are not required outside North America and Europe.
			if (sZip == "") {
				if (sEurope.indexOf(sCountry) == -1) {
					return "";
				}
				else {
					return "You must enter a postal code for a European country.";
				}
			}
			return "Postal codes may contain numbers, letters, spaces, dashes, and commas.";
		}
	}

	//	This is older code which we should never reach.
	if (sZip.length != 5 & sZip.length != 10)
		return "Zip code must be either 5 or 10 characters in length.";
	if (sZip.length == 10 && sZip.indexOf("-") != 5)
		return "Zip code is not in a valid format.";
	for (var ii = 0; ii < sZip.length; ii++) {
		sChar = sZip.substring(ii, ii + 1);
		if (sNum.indexOf(sChar) == -1)
			return "Only numbers and - are allowed in a zip code.";
	}
	return "";
}

function sIsDate(sString, sLabel, iLow, iHigh) {
//	NOTE:	getMonth returns values between 0 and 11.
	var reDateTime = /^[0-1][0-9](\/|-)[0-3][0-9](\/|-)([1-2][90])?[0-9]{2}$/gi;
	var arParts = aBreakApart(sString, "/");
	var dtCheck;
	var dtNow = new Date;
	var iDate;
	var sDate = "";
//	alert(Date.parse(sString));
	if (Date.parse(sString) != "NaN") {
		iDate = Date.parse(sString);
		dtCheck = new Date(sString);
//		alert(parseInt(dtCheck.getDate(), 10) + " " + parseInt(arParts[1], 10) + " " + arParts[1]);
//		alert(parseInt(dtCheck.getMonth(), 10) + " " + parseInt(arParts[0], 10) + " " + arParts[0]);
		if (parseInt(dtCheck.getDate(), 10) != parseInt(arParts[1], 10)) {
			return sLabel + " must be a valid date / time.";
		}
		//	Added 11/21/2000 to allow for two-digit year input.
		if (dtCheck.getYear() < 100) {
			if (dtCheck.getYear() < 30) {
				sDate = (dtCheck.getMonth() + 1) + "/" + dtCheck.getDate() + "/" + (dtCheck.getYear() + 2000);
			}
			else {
				sDate = (dtCheck.getMonth() + 1) + "/" + dtCheck.getDate() + "/" + (dtCheck.getYear() + 1900);
			}
			iDate = Date.parse(sDate);
		}
		else if (dtCheck.getYear() < 1875) {
			return "Dates prior to 1875 are not permitted.";
		}
/*
		//	Normally, this should work, but getMonth does not return value expected in 4.04.
		sDate = dtCheck.getMonth() + "/" + dtCheck.getDate() + "/" + dtCheck.getYear();
		if (sString != sDate) {
			return sLabel + " must be a valid date / time.";
		}
*/
	}
	else {
		return sLabel + " must be a date / time value.";
	}
	if (iLow != "" && iHigh != "") {
//		alert(sDate + " " + iDate + " " + iLow + " " + Date.parse(iLow) + " " + iHigh + " " + Date.parse(iHigh));
		if (iDate < Date.parse(iLow) || iDate > Date.parse(iHigh)) {
//			alert(iDate + " " + Date.parse(iLow) + " " + Date.parse(iHigh));
			return sLabel + " must be between " + iLow + " and " + iHigh + ".";
		}
	}
	if (iLow == "" && iHigh != "") {
		if (iDate > Date.parse(iHigh))
			return sLabel + " may not be later than " + iHigh + ".";
	}
	if (iLow != "" && iHigh == "") {
		if (iDate < Date.parse(iLow))
			return sLabel + " may not be earlier than " + iLow + ".";
	}
	return "";
}

function sIsLength(sValue, sLabel, sType, iLow, iHigh) {
/*
	Determine that the input string conforms to a specified minimum or
	maximum length.

	Parameters:
		sValue		: Element value to test.
		sLabel		: User-friendly name of element.
		sType		: Type of value expected.
		iLow		: Minimum length of value.
		iHigh		: Maximum length of value.
*/
	var iLen = sValue.length;
	var sTail = "";

	//	Neither minimum and maximum are specified.
	if (iLow == "" && iHigh == "")
		return "";

	//	Both minimum and maximum are specified and are the same.
	if (iLow == iHigh && iLen != iLow)
		return sLabel + " must be exactly " + iLow + " characters long.";

	//	Determine a descriptive message depending on whether or not
	//	both minimum and maximum are specified.
	if (iLow != "" && iHigh != "")
		sTail = "must be between " + iLow + " and " + iHigh + " characters.";
	else if (iLow != "")
		sTail = "may not be less than " + iLow + " characters.";
	else if (iHigh != "")
		sTail = "may not more than " + iHigh + " characters.";

	//	When value is empty, don't mention zero characters.
	if (iLen > 0)
		sTail = "is " + iLen + " characters long, but " + sTail;

	//	Maximum is specified and value is too long or
	//	minimum is specified and value is too short.
	if ((iHigh != "" && iLen > iHigh) || (iLow != "" && iLen < iLow))
		return sLabel + " " + sTail;

	return "";
}

function sIsNumber(sNumb, sLabel, sLow, sHigh) {
	var reZero = /^[-]?[0-9]*$/gi;
//	var reZero = new RegExp("^[0-9]*$", "gi");
//	reZero.compile("^[0-9]*$", "gi");
	//	This provides for decimal numbers and dollar amounts.
//	var reNoZero = /^[$-]?[1-9][0-9]*[.]*[0-9]*$/gi;
	//	Added ability to include commas in sets of three numbers.
	var reNoZero = /^[$-]?[1-9]+[0-9]{0,2}([,]?[0-9]{3})*[.]*[0-9]*$/gi;
//	var reNoZero = new RegExp("^[1-9][0-9]*$", "gi");
//	reNoZero.compile("^[1-9][0-9]*$", "gi");
//	alert(sNumb + " " + iLow + " " + iHigh);
	var iLow = parseInt(sLow, 10);
	var iHigh = parseInt(sHigh, 10);
/*
	if (sNumb.length == 0) {
		return sLabel + " must have a value.";
	}
*/
	//	First test for whether the expected characters are present.
	if (iLow == 0) {
//		! reZero.test(sNumb);
		if (sNumb.search(reZero) == -1)
			return sLabel + " must be a number.";
	}
	else {
//		alert(sNumb + " x " + sNumb.search(reNoZero) + " test " + reNoZero.test(sNumb));
//		! reNoZero.test(sNumb);
		if (sNumb.search(reNoZero) == -1)
			return sLabel + " must be a number without leading zeros.";
	}
	//	Remove dollar signs and commas from the number.
	sNumb = sNumb.replace(/[,$]/gi, "");
	//	Now test for values in relation to the range specified.
//	alert(sNumb);
	//	Changed parseInt to parseFloat.
	iNumb = parseFloat(sNumb, 10);
//	alert(parseFloat(sNumb))
	if (sLow != "" && sHigh != "") {
		if (iNumb < iLow || iNumb > iHigh)
			return sLabel + " must be between " + sLow + " and " + sHigh + ".";
	}
	if (sLow == "" && sHigh != "") {
		if (iNumb > iHigh)
			return sLabel + " may not be greater than " + sHigh + ".";
	}
	if (sLow != "" && sHigh == "") {
		if (iNumb < iLow)
			return sLabel + " may not be lesser than " + sLow + ".";
	}
	return "";
}

function sIsPassword(sPW, sLabel, sUser) {
/*
	This function validates the content of a password. To enhance security
	some common security holes are prevented here.
*/
	rePW = /^\S+$/gi;
	if (arguments.length == 1)
		return "";
	if (sPW.toLowerCase() == "password") {
		return "'" + sPW + "' may not be used as a " + sLabel + ".";
	}
	if (sPW.search(rePW) == -1) {
		return "Spaces are not permitted in a " + sLabel + ".";
	}
	if (sPW.indexOf(sUser) != -1) {
		return "Your User Name may not be part of your " + sLabel + ".";
	}
	if (sUser.indexOf(sPW) != -1) {
		return "Your " + sLabel + " may not be part of your User Name.";
	}
	return "";
}

function sIsTime(sString, sLabel, sLow, sHigh) {
	var reTime = new RegExp("([0-1]?[0-9]+):?([0-5]?[0-9]?) ?([AaPp]m?)", "i");
//	var reDateTime = /^[0-1][0-9](\/|-)[0-3][0-9](\/|-)([1-2][90])?[0-9]{2}$/gi;
	var dtNow = new Date;
	var dtValue = new Date;
	var iLow = 0;
	var iHigh = 0;
	var sTime = "";

	//	Try a first pass at validating the string.
	if (Date.parse("01/01/2000 " + sString) > 0)
		dtValue = Date.parse("01/01/2000 " + sString);
	else {
		//	Failing on the first pass, use our regular expression for time.
//		alert("Fail 1 " + sString.search(reTime));
		if (sString.search(reTime) != 0) {
			return sLabel + " must be a time value.";
		}
	}
	if (sLow != "" && sHigh != "") {
/*
		if (dtValue < Date.parse(iLow) || dtValue > Date.parse(iHigh))
			return sLabel + " must be between " + iLow + " and " + iHigh + ".";
*/
	}
	if (sLow == "" && sHigh != "") {
//		alert(sString + " L:" + sLow + " H:" + sHigh);
		if (sHigh.search(reTime) == 0) {
			iHigh = sTimeToFloat(sHigh);
			iTime = sTimeToFloat(sString);
			if (iTime > iHigh) {
				return sLabel + " may not be later than " + sHigh + ".";
			}
		}
	}
	if (sLow != "" && sHigh == "") {
//		alert(sString + " L:" + sLow + " H:" + sHigh);
		if (sLow.search(reTime) == 0) {
			iLow = sTimeToFloat(sLow);
			iTime = sTimeToFloat(sString);
			if (iTime < iLow) {
				return sLabel + " may not be earlier than " + sLow + ".";
			}
		}
	}
	return "";
}

function nOfGroup(oForm, lstNames, iMin, iMax, bDebug) {
/*
	This takes a referenced form and a list of fields that are to be
	checked. If the count of fields that have values is between iMin and
	iMax, the validation has succeeded.

	Parameters:
		oForm
		lstNames	:	rule object's 'name' property.
		iMin		:	rule object's 'min' property.
		iMax		:	rule object's 'max' property.
		bDebug		:	Whether or not to show debug code alerts.

	The rule object's name property is expected to be a list of form
	element names separated by '|'. For select-one and select-multiple
	elements, the element name may be followed by a caret (^) and the
	string 'text' or 'value' to designate whether to evaluate the contents
	of the option object's text or value property.

	Usage Example:
	aRules[1] = new Rule("City^text|ZipCode^text","Limit To","s",true,"nOfGroup","1","2","You must select a City Name or Zip Code.")
*/
	var aNames = lstNames.split("|");
	var ii = 0;
	var iCnt = 0;
	var oElement;
	var sValue = "";
	//	Check each field in the list for a value.
	for (ii = 0; ii < aNames.length; ii++) {
		aPart = aNames[ii].split("^");
		if (aPart.length == 1) {
			aPart[1] = "value";
		}
		oElement = BuildInputCtrl(oForm, aPart[0], aPart[1], false);
		if (bDebug) {alert("nOfGroup:" + "\nElement Name: " + oElement.name + "\nElement Values: " + oElement.value);}
		sValue = RTrim(LTrim(oElement.value[0]));
		if (sValue != "") {
			iCnt++;
		}
	}
	if (bDebug) {alert("nOfGroup:" + "\nCount of Values: " + iCnt + "\nCount Range: " + iMin + "," + iMax);}
	if (iCnt >= iMin && iCnt <= iMax) {
		return true;
	}
	else {
		return false;
	}
}

function bCheckFormRules(oForm, aRuleArray, bDebug) {
/*
	This function processes the array of form validation rules in a generic
	manner. It handles validation on the most common types of form elements:
	checkbox, password, radio, select-one, select-multiple, text, textarea
*/
	var aEN = new Array(0);	//	Array holding parts of element name.
	var aParms = new Array(0);
	var bCheckElement = false;
	var bRet = false;
	var ii = 0;
	var jj = 0;
	var kk = 0;
	var oElement;
	var oES;	//	Element style object.
	var sAlert = "";
	var sClrClearFlag = "None White";
	var sClrSetFlag = "None Red";
	var sParm = "";
	var sValue = "";

	if (arguments.length == 1) {
		aRuleArray = aRules;
	}

	if (arguments.length <= 2) {
		bDebug = false;
	}

	//	This is a new check based on using the zero element of the rules
	//	array. It performs a special function that normally spans more
	//	than one differently named form element.
	if (aRuleArray[ii] != null) {
		sFunc = aRuleArray[ii].func;
		if (sFunc == "nOfGroup") {
			if (! nOfGroup(oForm, aRuleArray[ii].name, aRuleArray[ii].min, aRuleArray[ii].max, bDebug)) {
				jj++; sAlert += "\n" + jj + ". " + aRuleArray[ii].msg;
			}
		}
	}

	for (ii = 1; ii < aRuleArray.length; ii++) {
		//	01/21/2002 Provide for a sparse rules array.
		//	Skip over missing rules.
		while (! (aRuleArray[ii])) {
			ii++;
		}
		aEN = aRuleArray[ii].name.split("^");
		if (document.all) {
//			document.all[aEN].style.background = sClrClearFlag;
			oES = document.all[aEN[0]].style;
		}
		else if (document.layers) {
//			document[aEN].style.background = sClrClearFlag;
			//	REMINDER: This doesn't work in Netscape 4.04.
			oES = oForm[aEN[0]].style;
		}
		else if (document.getElementById) {
//			document.getElementById(aEN).style.background = sClrClearFlag;
			oES = document.getElementById(aEN[0]).style;
		}
		if (oES) {
			oES.background = sClrClearFlag;
		}

		sFunc = aRuleArray[ii].func;
		if (sFunc == "nOfGroup") {
			//	We need to by-pass normal validation testing.
			bCheckElement = true;
			sValue = "***";
		}
		else {
			if (bDebug) {alert("bCheckFormRules1:" + "\nRule Name: " + aRuleArray[ii].name);}
			oElement = BuildInputCtrl(oForm, aRuleArray[ii].name, "value", bDebug);
			if (bDebug) {alert("bCheckFormRules2:" + "\nElement Name: " + oElement.name + "\nElement Type: " + oElement.type + "\nElement State: " + oElement.state + "\nElement Value: " + oElement.value);}
			if (bDebug) {alert("bCheckFormRules3:" + "\nElement Matches Rule Array? " + (oElement == oForm[aRuleArray[ii].name]));}
//			sFunc = aRuleArray[ii].func;
			sValue = RTrim(LTrim(oElement.value[0]));
			//	02/16/2002:1092 Added handling for array of fields where
			//	the first one is blank, specifically for ImageOrient.
			if (sValue == "" && oElement.value[oElement.value.length - 1] != "") {
				sValue = oElement.value[oElement.value.length - 1];
			}
			if (bDebug) {alert(oElement.name + ": '" + sValue + "'");}
			//	For elements that must have values, make sure they do.
			if (aRuleArray[ii].req) {
//			&& (oElement.type == "text" || oElement.type == "password" || oElement.type == "textarea" || oElement.type == "select-one" || oElement.type == "hidden"))
				if (sValue == "") {
					jj++; sAlert += "\n" + jj + ". " + aRuleArray[ii].msg;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			//	02/16/2002:1108 Included 'file' type in list to check.
			bCheckElement = (oElement.type == "text" || oElement.type == "password" || oElement.type == "textarea" || oElement.type == "select-one" || oElement.type == "hidden" || oElement.type == "file");
		}
		//	If a value exists, run it through the validation functions.
		if ((aRuleArray[ii].req || sValue != "") && bCheckElement) {
			if (sFunc.indexOf("nOfGroup") == 0) {
				if (! nOfGroup(oForm, aRuleArray[ii].name, aRuleArray[ii].min, aRuleArray[ii].max, bDebug)) {
					jj++; sAlert += "\n" + jj + ". " + aRuleArray[ii].msg;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc.indexOf("sValidWords") == 0) {
				if (sFunc.indexOf(",") >= 0) {
					aParms = aBreakApart(sFunc, ",");
				}
				else {
					aParms[0] = sFunc;
				}
				if (aParms.length == 1) {
					sRet = sValidWords(sValue, aRuleArray[ii].label);
				}
				else {
					var oNew = BuildInputCtrl(oForm, aParms[1], "value", bDebug);
					sRet = sValidWords(sValue, aRuleArray[ii].label, oNew.value[0]);
				}
				if (sRet != "") {
					jj++; sAlert += "\n" + jj + ". " + sRet;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc.indexOf("sValidEmail") == 0) {
				sRet = sValidEmail(sValue, aRuleArray[ii].label);
				if (sRet != "") {
					jj++; sAlert += "\n" + jj + ". " + sRet;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc.indexOf("sValidHTML") == 0) {
				sRet = sValidHTML(sValue, aRuleArray[ii].label);
				if (sRet != "") {
					jj++; sAlert += "\n" + jj + ". " + sRet;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc.indexOf("sValidPhone") == 0) {
				if (sFunc.indexOf(",") >= 0) {
					aParms = aBreakApart(sFunc, ",");
				}
				else {
					aParms[0] = sFunc;
				}
				if (aParms.length == 1) {
					sRet = sValidPhone(sValue, aRuleArray[ii].label);
				}
				else {
					if (oForm[aParms[1]].type == "select-one") {
						sParm = oForm[aParms[1]].options[oForm[aParms[1]].selectedIndex].value;
					}
					else {
						sParm = oForm[aParms[1]].value;
					}
					sRet = sValidPhone(sValue, aRuleArray[ii].label, sParm);
				}
				if (sRet != "") {
					jj++; sAlert += "\n" + jj + ". " + sRet;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc == "sIsNumber") {
				sRet = sIsNumber(sValue, aRuleArray[ii].label, aRuleArray[ii].min, aRuleArray[ii].max);
				if (sRet != "") {
					jj++; sAlert += "\n" + jj + ". " + sRet;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc == "sIsDate") {
				sRet = sIsDate(sValue, aRuleArray[ii].label, aRuleArray[ii].min, aRuleArray[ii].max);
				if (sRet != "") {
					jj++; sAlert += "\n" + jj + ". " + sRet;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc == "sIsTime") {
				sRet = sIsTime(sValue, aRuleArray[ii].label, aRuleArray[ii].min, aRuleArray[ii].max);
				if (sRet != "") {
					jj++; sAlert += "\n" + jj + ". " + sRet;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc.indexOf("sDisallow") == 0) {
				if (sFunc.indexOf(",") >= 0) {
					aParms = aBreakApart(sFunc, ",");
				}
				else {
					aParms[0] = sFunc;
				}
				if (aParms.length == 1) {
					sRet = sDisallow(sValue, aRuleArray[ii].label);
				}
				else {
					sRet = sDisallow(sValue, aRuleArray[ii].label, aParms[1]);
				}
				if (sRet != "") {
					jj++; sAlert += "\n" + jj + ". " + sRet;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc.indexOf("sIsPassword") == 0) {
				if (sFunc.indexOf(",") >= 0) {
					aParms = aBreakApart(sFunc, ",");
				}
				else {
					aParms[0] = sFunc;
				}
				if (aParms.length == 1) {
					sRet = sIsPassword(sValue, aRuleArray[ii].label);
				}
				else {
					if (oForm[aParms[1]].type == "select-one") {
						sParm = oForm[aParms[1]].options[oForm[aParms[1]].selectedIndex].value;
					}
					else {
						sParm = oForm[aParms[1]].value;
					}
					sRet = sIsPassword(sValue, aRuleArray[ii].label, sParm);
				}
				if (sRet != "") {
					jj++; sAlert += "\n" + jj + ". " + sRet;
					if (oES) {
						oES.background = sClrSetFlag;
					}
				}
			}
			if (sFunc.indexOf("nOfGroup") != 0) {
				if (aRuleArray[ii].min != "" || aRuleArray[ii].max != "") {
					if (aRuleArray[ii].type == "s") {
						sRet = sIsLength(sValue, aRuleArray[ii].label, aRuleArray[ii].type, aRuleArray[ii].min, aRuleArray[ii].max);
						if (sRet != "") {
							jj++; sAlert += "\n" + jj + ". " + sRet;
							if (oES) {
								oES.background = sClrSetFlag;
							}
						}
					}
				}
			}
		}
		//	02/16/2002:1273 Added check for new sValidFileName function.
		if (sFunc.indexOf("sValidFileName") == 0 && sValue != "") {
			if (sFunc.indexOf(",") >= 0) {
				aParms = aBreakApart(sFunc, ",");
			}
			else {
				aParms[0] = sFunc;
			}
			if (aParms.length == 1) {
				sRet = sValidFileName(sValue, aRuleArray[ii].label);
			}
			else {
				sParm = "";
				for (ll = 1; ll < aParms.length; ll++) {
					sParm += (ll == aParms.length - 1) ? "" : ",";
					sParm += aParms[ll];
				}
				sRet = sValidFileName(sValue, aRuleArray[ii].label, sParm);
			}
			if (sRet != "") {
				jj++; sAlert += "\n" + jj + ". " + sRet;
				if (oES) {
					oES.background = sClrSetFlag;
				}
			}
		}
		//	State validation is dependent on the country choice and
		//	may be blank, so we need to process it even when it is blank.
		if (sFunc.indexOf("sValidState") == 0) {
			if (sFunc.indexOf(",") >= 0) {
				aParms = aBreakApart(sFunc, ",");
			}
			else {
				aParms[0] = sFunc;
			}
			if (aParms.length == 1) {
				sRet = sValidState(sValue, aRuleArray[ii].label);
			}
			else {
				if (oForm[aParms[1]].type == "select-one") {
					sParm = oForm[aParms[1]].options[oForm[aParms[1]].selectedIndex].value;
				}
				else {
					sParm = oForm[aParms[1]].value;
				}
				sRet = sValidState(sValue, aRuleArray[ii].label, sParm);
			}
			if (sRet != "") {
				jj++; sAlert += "\n" + jj + ". " + sRet;
				if (oES) {
					oES.background = sClrSetFlag;
				}
			}
		}
		//	Postal code validation is dependent on the country choice and
		//	may be blank, so we need to process it even when it is blank.
		if (sFunc.indexOf("sValidZip") == 0) {
			if (sFunc.indexOf(",") >= 0) {
				aParms = aBreakApart(sFunc, ",");
			}
			else {
				aParms[0] = sFunc;
			}
			if (aParms.length == 1) {
				sRet = sValidZip(sValue, aRuleArray[ii].label);
			}
			else if (aParms.length == 2) {
				if (oForm[aParms[1]].type == "select-one") {
					sParm = oForm[aParms[1]].options[oForm[aParms[1]].selectedIndex].value;
				}
				else {
					sParm = oForm[aParms[1]].value;
				}
				sRet = sValidZip(sValue, aRuleArray[ii].label, sParm);
			}
			else {
				if (oForm[aParms[1]].type == "select-one") {
					sParm = oForm[aParms[1]].options[oForm[aParms[1]].selectedIndex].value;
				}
				else {
					sParm = oForm[aParms[1]].value;
				}
//				alert(sFunc + " " + sParm + " " + aParms[2]);
				sRet = sValidZip(sValue, aRuleArray[ii].label, sParm, aParms[2]);
			}
			if (sRet != "") {
				jj++; sAlert += "\n" + jj + ". " + sRet;
				if (oES) {
					oES.background = sClrSetFlag;
				}
			}
		}
		if (sFunc == "sValidImageDimensions") {
			sRet = sValidImageDimensions(oElement.value, aRuleArray[ii].label);
			if (sRet != "") {
				jj++; sAlert += "\n" + jj + ". " + sRet;
				if (oES) {
					oES.background = sClrSetFlag;
				}
			}
		}
	}
	if (sAlert != "") {
		if (sAlert != "\n1. blank") {
			alert(sAlert);
		}
		return false;
	}
	return true;
}

/*
**	This is example code to place in the page with the form.
<script type="text/javascript"><!--
	var aRules = new Array;
	aRules[1] = new Rule("user_name","User Name","s",true,"","5","","You must enter a User Name.");
	aRules[2] = new Rule("password","Password","s",true,"sIsPassword,user_name","5","","You must enter a Password.");
	aRules[3] = new Rule("email","E-mail Address","s",true,"sValidEmail","","","You must enter the e-mail address on file with us.");

	function bFormValid(oForm, aRule) {
		if (bFormValid.arguments.length > 1) {
			if (! bCheckFormRules(oForm, aRule, false)) {
				return false;
			}
		}
		return true;
	}
	//-->
</script>

**	This is example calling code for the form element.
	<form action="NextPage.cfm" method="Post" name="Search"
	onSubmit="return bFormValid(this, aRules)">
	</form>
*/

function AllowHTML(sText) {
	//	Permit an HTML subset only.
	//	Define an array of HTML tags that are permitted.
	var sTagList = "B,BIG,BR,DIV,FONT,H1,H2,H3,H4,HR,I,LI,OL,P,PRE";
	sTagList += "SMALL,SPAN,STRONG,SUB,SUP,TABLE,TD,TH,TR,U,UL";
	var aTags = sTagList.split(",");
	//	This locates any HTML tags in the string.
	var reTag = /\<\/?[A-Z]+[^>]*\>/gi;
	var sTag = "";
	var sTagX = "";
	var sMsg = "The following tags:\n\n";
	var sRet = "";
	var ii = 0;
	var sBadTags = ",";
	sTag = sText.match(reTag);
	if (sTag == null) {
		//	There were no HTML tags in the string.
		return "";
	}
	for (ii = 0; ii < sTag.length; ii++) {
		sTagx = sTag[ii].substring(1, sTag[ii].length - 1);
		if (sTagx.indexOf("\n") != -1) {
			sTagx = sTagx.substr(0, sTagx.indexOf("\n"));
		}
		if (sTagx.indexOf(" ") != -1) {
			sTagx = sTagx.substr(0, sTagx.indexOf(" "));
		}
		if (sTagx.substr(0, 1) == "/") {
			sTagx = sTagx.substr(1, sTagx.length - 1);
		}
		var bOk = false;
		for (var jj = 0; jj < aTags.length; jj++) {
			//	If tag is in the list, exit the loop.
			if (sTagx.toLowerCase() == aTags[jj].toLowerCase()) {
				bOk = true;
				jj = aTags.length;
			}
		}
		//	Tag is not in the list.
		if (! bOk) {
			if (sRet.length == 0) {
				sRet = sMsg;
			}
			var sTest = "," + sBadTags + ",";
			if (sTest.indexOf("," + sTagx + ",") == -1) {
				sBadTags = sBadTags + sTagx + ",";
				sRet += "\t<" + sTagx + ">\n";
			}
		}
	}
	if (sRet.length > 0) {
		sRet += "\nare not allowed.";
//				document.writeln(sRet.replace(/\</gi, "&lt;"));
		return sRet;
	}
	return "";
}
/*
Change History:
02/16/2002:	Standardized coding and comments for compression.
			Most of this was already done, but needed to modify two
			literal regular expressions with spaces in them to avoid
			losing the space in the compression.
			289 Added new validation function for file uploads.
			1092 Added handling for array of fields where
			the first one is blank, specifically for ImageOrient.
			1108 Included 'file' type in list to check.
			1273 Added check for new sValidFileName function.
02/17/2002:	311 Change from a literal to a RegExp object. The
			literal caused Netscape 4.0x to cough on loading this js file.
*/