/**************************************************************************************************
	validate_form.js...START

	Do not edit this file.  This file is dynamically generated by the validate_form.js
	ant build process
 **************************************************************************************************/

/**
	VALIDATE_FORM.JS
	Comprehensive solution for validating HTML forms.
	Copyright (C) 2002, Jeff Epstein, jeff_epstein@yahoo.com, http://www.jeffyjeffy.com#download

	This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 **/


/**
	See validate_form_documentation.html for information.

	NOTES TO SELF
		//alert("The CODE for getRCBSMTrackerArrIdx=" + getRCBSMTrackerArrIdx);

		I would like to add a function to get the number of elements that have a (any) value.
 **/



//GLOBAL VARIABLES...start  (do not alter this section)
	var sDBG_INDENT = "     ";
	var asRCBSM_TRACKER_NAME = '';
	var aiRCBSM_TRACKER_SEL_COUNT = '';
	var aiRCBSM_TRACKER_ARR_IDX = '';
	var iELEMENT_COUNT = -1;
	var bFIRST_DEBUG_SCREEN_SEEN = false;
	var sCURRENT_LENGTH_FOR_TA = '~CURRLEN~';
//GLOBAL VARIABLES...end

/**
	All messages must be at least one character in length.  If they are empty string, its as if they do not exist.
 **/
function getFormErrorMsgs(f_orm, s_userErrorPrefix, i_debugPerScreen)  {
	//This initialization function must be the very first line.
	initializeLocalVars();

	crashIfBadConfiguration(f_orm);

	if(hasCrashed())  {
		//For whatever reason utility.crash() was called, indicating
		//an error has occured.  Get out.  Get out while you still
		//can.
		return;
	}

	outputDebugging(f_orm, i_debugPerScreen);

	return getUserErrors(f_orm, s_userErrorPrefix);
}

/**
	PRIVATE FUNCTIONS...start
 **/
 	function crashIfBadConfiguration(f_orm)  {
		crashIfMissing(f_orm, 'f_orm', 'validate_form.getFormErrorMsgs');

		crashIfBadBSSA('f_orm.asGlobalBadSubStrs', f_orm.asGlobalBadSubStrs);
		f_orm.bsGlobalTrimSpaces = getOptBooleanStringVF(f_orm.bsGlobalTrimSpaces, "f_orm.bsGlobalTrimSpaces");

		if(hasCrashed())  {
			return;
		}

		for(var i = 0; i < f_orm.length; i++)  {

			if(f_orm[i].type == "text"  ||  f_orm[i].type == "password")  {
				crashIfBadCfg_text(f_orm, f_orm[i]);

			}  else if(f_orm[i].type == "textarea")  {
				crashIfBadCfg_textarea(f_orm, f_orm[i]);

			}  else if(f_orm[i].type == "checkbox"  ||  f_orm[i].type == "select-multiple")  {
				crashIfBadCfg_cbsm(f_orm, i);

			}  else if(f_orm[i].type == "radio"  ||  f_orm[i].type == "select-one")  {
				crashIfBadCfg_rso(f_orm, i);
			}
		}
	}


	function outputDebugging(f_orm, i_debugPerScreen)  {
		if(!i_debugPerScreen  ||  i_debugPerScreen == -1)  {
			//No debugging is requested
			return;
		}

		if(typeof(i_debugPerScreen) != "number"  ||  i_debugPerScreen < 1)  {
			return crashVF("i_debugPerScreen is not required (currently '" + i_debugPerScreen + "'), but when provided, it must be a number (currently '" + typeof(i_debugPerScreen) + "') either equal to -1, or greater than zero.");
		}

		var iDebugItemsTotal = 0;
		var iDebugItemsThisRound = 0;
		var sDebugBuffer = '';
		var sDebugThisItem = '';

		for(var i = 0; i < f_orm.length; i++)  {
			if(f_orm[i].type == "text"  ||  f_orm[i].type == "password")  {
				sDebugThisItem = getDebugging_text(f_orm, f_orm[i]);

			}  else if(f_orm[i].type == "textarea")  {
				sDebugThisItem = getDebugging_textarea(f_orm, f_orm[i]);

			}  else if(f_orm[i].type == "checkbox"  ||  f_orm[i].type == "select-multiple")  {
				sDebugThisItem = getDebugging_cbsm(f_orm, i);

			}  else if(f_orm[i].type == "radio"  ||  f_orm[i].type == "select-one")  {
				sDebugThisItem = getDebugging_rso(f_orm, i);
			}

			if(sDebugThisItem)  {
				iDebugItemsTotal++;
				iDebugItemsThisRound++;

				sDebugBuffer += sDebugThisItem + "\n";

				if(iDebugItemsThisRound >= i_debugPerScreen)  {
					sDebugBuffer = getGlobalDebugging(sDebugBuffer, f_orm);

					alert("---  validate_form.js  DEBUGGING  (" + (iDebugItemsTotal - iDebugItemsThisRound + 1) + " to " + iDebugItemsTotal + " of " + iELEMENT_COUNT + ")  ---\n\n" + sDebugBuffer);

					iDebugItemsThisRound = 0;
					sDebugBuffer = '';
				}

				sDebugThisItem = '';
			}
		}

		if(sDebugBuffer)  {
			sDebugBuffer = getGlobalDebugging(sDebugBuffer, f_orm);
			alert("---  validate_form.js  DEBUGGING  (" + (iDebugItemsTotal - iDebugItemsThisRound + 1) + " to " + iDebugItemsTotal + " of " + iELEMENT_COUNT + ")  ---\n\n" + sDebugBuffer);
		}
	}

	function getGlobalDebugging(s_debugBuffer, f_orm)  {
		if(!bFIRST_DEBUG_SCREEN_SEEN)  {
			bFIRST_DEBUG_SCREEN_SEEN = true;

			var sGlobalDebug = '';

			if(f_orm.asGlobalBadSubStrs)  {
				sGlobalDebug = sDBG_INDENT + "f_orm.asGlobalBadSubStrs=  [ " + f_orm.asGlobalBadSubStrs + " ]\n";
			}

			if(f_orm.bsGlobalTrimSpaces == 'true')  {
				sGlobalDebug += sDBG_INDENT + "f_orm.bsGlobalTrimSpaces=  'true'\n";
			}

			if(sGlobalDebug)  {
				return "---  GLOBAL SETTINGS  ---\n" + sGlobalDebug + "\n\n" + s_debugBuffer;
			}
		}

		//Either the first debugging screen has already been displayed, or
		//this is the first screen, but there is no global settings
		return s_debugBuffer;
	}

	function getUserErrors(f_orm, s_prefix)  {
		var sUserErrors = '';

		for(var i = 0; i < f_orm.length; i++)  {

			if(f_orm[i].type == "text"  ||  f_orm[i].type == "password")  {
				sUserErrors += getUserErrors_text(f_orm, i, s_prefix);

			}  else if(f_orm[i].type == "textarea")  {
				sUserErrors += getUserErrors_textarea(f_orm, i, s_prefix);

			}  else if(f_orm[i].type == "checkbox"  ||  f_orm[i].type == "select-multiple")  {
				sUserErrors += getUserErrors_cbsm(f_orm, i, s_prefix);

			}  else if(f_orm[i].type == "radio"  ||  f_orm[i].type == "select-one")  {
				sUserErrors += getUserErrors_rso(f_orm, i, s_prefix);
			}
		}

		return sUserErrors;
	}
	/**
		form_element must be provided, and a form element...START
	 **/
		function getDebuggingNameType(form_element)  {
			return "---  " + form_element.name + "  ---          [" + form_element.type + "]\n";
		}
		function getDebuggingMsgRequired(s_msgRequired)  {
			if(s_msgRequired)  {
				return sDBG_INDENT + "sMsgRequired:  '" + s_msgRequired + "'\n";
			}
			return '';
		}
		function getDebuggingValue(s_value)  {
			if(s_value)  {
				return sDBG_INDENT + "VALUE:  '" + s_value + "'\n";
			}
			return '';
		}
	/**
		form_element must be provided, and a form element...END
	 **/

	/**
		Do not call this function without first calling crashIfBadCfg_cbsm for the form-and-form-element.
	 **/
	function getUserErrors_cbsm(f_orm, i_arrIdx, s_prefix)  {
		var feCbsm = f_orm[i_arrIdx];

		var sMsgRequired = '';
		var sMsgMCRange = '';
		var iMCMin = '';
		var iMCMax = '';

		if(feCbsm.type == "checkbox")  {
			if(getFirstArrIdx(feCbsm.name) != i_arrIdx)  {
				//This checkbox has already had its user errors retrieved.
				return '';
			}
			//This checkbox has not yet had its user errors retrieved.

			sMsgRequired = f_orm[feCbsm.name + ".sMsgRequired"];
			sMsgMCRange = f_orm[feCbsm.name + ".sMsgMCRange"];
			iMCMin = f_orm[feCbsm.name + ".iMCMin"];
			iMCMax = f_orm[feCbsm.name + ".iMCMax"];

		}  else  {
			//this is a select-multiple type...which has not yet had its
			//user errors retrieved.

			sMsgRequired = feCbsm.sMsgRequired
			sMsgMCRange = feCbsm.sMsgMCRange
			iMCMin = feCbsm.iMCMin
			iMCMax = feCbsm.iMCMax
		}

		var iMCSelCount = getMCSelectedCount(feCbsm.name);
		if(sMsgRequired  &&  iMCSelCount < 1)  {
			return s_prefix + sMsgRequired + "\n";
		}
		//If its required, a value has been selected.

		if(iMCSelCount < 1)  {
			//Its not required, and no value has been selected,
			//which is okay.
			return '';
		}

		if((iMCMin  &&  iMCMin > iMCSelCount)  ||
		   (iMCMax  &&  iMCMax < iMCSelCount))  {
			return s_prefix + sMsgMCRange + "\n";
		}

		return '';
	}

	/**
		Do not call this function without first calling crashIfBadCfg_cbsm for the form-and-form-element.
	 **/
	function getDebugging_cbsm(f_orm, i_arrIdx)  {
		var feCbsm = f_orm[i_arrIdx];

		var sMsgRequired = '';
		var sMsgMCRange = '';
		var iMCMin = '';
		var iMCMax = '';

		if(feCbsm.type == "checkbox")  {
			if(getFirstArrIdx(feCbsm.name) != i_arrIdx)  {
				//This checkbox has already been debugged.
				return '';
			}
			//This checkbox has not yet been debugged

			sMsgRequired = f_orm[feCbsm.name + ".sMsgRequired"];
			sMsgMCRange = f_orm[feCbsm.name + ".sMsgMCRange"];
			iMCMin = f_orm[feCbsm.name + ".iMCMin"];
			iMCMax = f_orm[feCbsm.name + ".iMCMax"];

		}  else  {
			//this is a select-multiple type...which has not yet been debugged.

			sMsgRequired = feCbsm.sMsgRequired
			sMsgMCRange = feCbsm.sMsgMCRange
			iMCMin = feCbsm.iMCMin
			iMCMax = feCbsm.iMCMax
		}

		var s = getDebuggingNameType(feCbsm) + getDebuggingMsgRequired(sMsgRequired);
		if(sMsgMCRange)  {
			s += sDBG_INDENT + "sMsgMCRange:  '" + sMsgMCRange + "'\n";

			if(iMCMin)  {
				s += sDBG_INDENT + sDBG_INDENT + "iMCMin:  " + iMCMin + "\n";
			}

			if(iMCMax)  {
				s += sDBG_INDENT + sDBG_INDENT + "iMCMax:  " + iMCMax + "\n";
			}
		}
		var iMCSelCount = getMCSelectedCount(feCbsm.name);
		if(iMCSelCount > 0)  {
			s += sDBG_INDENT + "NUMBER OF CHOICES SELECTED:  " + iMCSelCount + "\n";
		}
		return s;
	}

	function crashIfBadCfg_cbsm(f_orm, i_arrIdx)  {
		conditionallyAddRCBSM(f_orm, i_arrIdx);

		var feCbsm = f_orm[i_arrIdx];
		var sMsgMCRange = '';
		var iMCMin = '';
		var iMCMax = '';
		var iTotalOptions = -1;
		var sErrVarPre = '';
		var sErrVarPost = '';

		if(feCbsm.type == "checkbox")  {
			if(getFirstArrIdx(feCbsm.name) != i_arrIdx)  {
				//This checkbox has already been validated.
				return '';
			}
			//This checkbox has not yet been validated

			sMsgMCRange = f_orm[feCbsm.name + ".sMsgMCRange"];
			iMCMin = f_orm[feCbsm.name + ".iMCMin"];
			iMCMax = f_orm[feCbsm.name + ".iMCMax"];
			iTotalOptions = f_orm[feCbsm.name].length;
			sErrVarPre = "f_orm['" + feCbsm.name;
			sErrVarPost = "']";

		}  else  {
			//this is a select-multiple type...which has not yet been validated.
			sMsgMCRange = feCbsm.sMsgMCRange;
			iMCMin = feCbsm.iMCMin;
			iMCMax = feCbsm.iMCMax;
			iTotalOptions = feCbsm.length;
			sErrVarPre = feCbsm.name;
			sErrVarPost = "";

		}

		if(sMsgMCRange)  {
			if(!iMCMin  &&  !iMCMax)  {
				return crashVF(sErrVarPre + ".sMsgMCRange" + sErrVarPost + " has been provided (currently '" + sMsgMCRange + "'), but neither " + sErrVarPre + ".iMCMin" + sErrVarPost + " nor " + sErrVarPre + ".iMCMax" + sErrVarPost + " have been provided.  At least one of these bounds are required.");
			}

			if(iMCMin)  {
				if(!isInteger(iMCMin))  {
					return crashVF(sErrVarPre + ".iMCMin" + sErrVarPost + " has been provided, but is not of type number.  Currently of type '" + typeof(iMCMin) + "' and equal to '" + iMCMin + "'.");
				}

				if(iMCMin < 1  ||  iMCMin > iTotalOptions)  {
					return crashVF(sErrVarPre + ".iMCMin" + sErrVarPost + " has been provided (currently " + iMCMin + "), but is either less than one, or greater than the total number of options (" + iTotalOptions + ").");
				}
			}

			if(iMCMax)  {
				if(!isInteger(iMCMax))  {
					return crashVF(sErrVarPre + ".iMCMax" + sErrVarPost + " has been provided, but is not of type number.  Currently of type '" + typeof(iMCMax) + "' and equal to '" + iMCMax + "'.");
				}

				if(iMCMax < 1  ||  iMCMax > iTotalOptions)  {
					return crashVF(sErrVarPre + ".iMCMax" + sErrVarPost + " has been provided (currently " + iMCMax + "), but is either less than one, or greater than the total number of options (" + iTotalOptions + ").");
				}
			}

			if(iMCMin  &&  iMCMax  &&  iMCMin > iMCMax)  {
				return crashVF(sErrVarPre + ".sMsgMCRange" + sErrVarPost + " has been provided (currently '" + sMsgMCRange + "'), and both " + sErrVarPre + ".iMCMin" + sErrVarPost + " (currently " + iMCMin + ") and " + sErrVarPre + ".iMCMax" + sErrVarPost + " (currently " + iMCMax + ") have been provided.  However, iMCMin must be less than or equal to iMCMax, which it is not.");
			}

		}  else if(iMCMin  ||  iMCMax)  {
			return crashVF(sErrVarPre + ".sMsgMCRange" + sErrVarPost + " has *not* been provided, but at least one of " + sErrVarPre + ".iMCMin" + sErrVarPost + " (currently '" + iMCMin + "') or " + sErrVarPre + ".iMCMax" + sErrVarPost + " (currently '" + iMCMax + "') has been provided.");
		}
	}


	/**
		Do not call this function without first calling crashIfBadCfg_rso for the form-and-form-element.
	 **/
	function getUserErrors_rso(f_orm, i_arrIdx, s_prefix)  {
		var feRso = f_orm[i_arrIdx];
		var sValue = '';
		var sMsgRequired = '';

		if(feRso.type == "radio")  {
			if(getFirstArrIdx(feRso.name) != i_arrIdx)  {
				//We already debugged this radio element.
				return '';
			}

			sMsgRequired = f_orm[feRso.name + ".sMsgRequired"];
			sValue = getRadioValue(f_orm, feRso.name);

		}  else if(feRso.selectedIndex > -1) {
			sMsgRequired = feRso.sMsgRequired;
			sValue = feRso[feRso.selectedIndex].value;
		}

		if(sMsgRequired  &&  !sValue)  {
			return s_prefix + sMsgRequired + "\n";
		}
		//If its required, a value has been provided.

		return '';
	}


	/**
		Do not call this function without first calling crashIfBadCfg_rso for the form-and-form-element.
	 **/
	function getDebugging_rso(f_orm, i_arrIdx)  {
		var feRso = f_orm[i_arrIdx];
		var sValue = '';
		var sMsgRequired = '';

		if(feRso.type == "radio")  {
			if(getFirstArrIdx(feRso.name) != i_arrIdx)  {
				//We already debugged this radio element.
				return '';
			}

			sMsgRequired = f_orm[feRso.name + ".sMsgRequired"];
			sValue = getRadioValue(f_orm, feRso.name);

		}  else if(feRso.selectedIndex > -1) {
			sMsgRequired = feRso.sMsgRequired;
			sValue = feRso[feRso.selectedIndex].value;
		}

		return getDebuggingNameType(feRso) + getDebuggingMsgRequired(sMsgRequired) + getDebuggingValue(sValue);
	}

	function getRadioValue(f_orm, s_radioName)  {
		var feRadio = f_orm[s_radioName];
		for(var i = 0; i < feRadio.length; i++)  {
			if(feRadio[i].checked)  {
				return feRadio[i].value;
			}
		}

		return '';
	}

	function crashIfBadCfg_rso(f_orm, i_arrIdx)  {
		var feRso = f_orm[i_arrIdx];
		if(feRso.type == "radio")  {
			conditionallyAddRCBSM(f_orm, i_arrIdx);

		}  else  {
			iELEMENT_COUNT++;
		}

		//Nothing to validate.  The only attribute that
		//can be associated to a radio or select-one type
		//is sMsgRequired...  Which either is or isn't...
		//doesn't matter which.
	}

	function conditionallyAddRCBSM(f_orm, i_arrIdx)  {
		var feCbsm = f_orm[i_arrIdx];
		if(wasRCBSMalreadyFound(feCbsm.name))  {
			//We already analyzed this element.
			return;
		}

		iELEMENT_COUNT++;

		addRCBSMtoTracker(f_orm, feCbsm, i_arrIdx);
	}

	/**
		Do not call this function without first calling crashIfBadCfg_textarea for the form-and-form-element.
	 **/
	function getUserErrors_textarea(f_orm, i_arrIdx, s_prefix)  {
		var feTA = f_orm[i_arrIdx];
		if(feTA.sMsgRequired  &&  !feTA.value)  {
			return s_prefix + feTA.sMsgRequired + "\n";
		}

		if(!feTA.value)  {
			return '';
		}

		if(needsToBeTrimmed(f_orm, feTA))  {
			feTA.value = trimSpaces(feTA.value);
		}

		if(feTA.sMsgBadLength  &&
		   (feTA.value.length < feTA.iMinLength  ||
		    feTA.value.length > feTA.iMaxLength))  {

			return s_prefix + getBadLengthMsg(feTA) + '\n';
		}

		return getBadSubStrMessage(feTA.value, f_orm, feTA.asBadSubStrs, feTA.bsNoBadSubStrings, s_prefix, feTA.sMsgBadSubStr);
	}

	function getBadLengthMsg(fe_tta)  {
		var sMsg = fe_tta.sMsgBadLength;

		var iCLArrIdx = sMsg.indexOf(sCURRENT_LENGTH_FOR_TA);
		if(iCLArrIdx != -1)  {
			sMsg = sMsg.substring(0, iCLArrIdx) + fe_tta.value.length + sMsg.substring((iCLArrIdx + sCURRENT_LENGTH_FOR_TA.length), (sMsg.length + 1));
		}

		return sMsg;
	}

	function getBadSubStrMessage(s_value, f_orm, as_badSubStrs, bs_noBadSubStrings, s_prefix, s_errorMessage)  {
		if(!s_value)  {
			//Theres no value, so it can't have any illegal sub-string.
			return '';
		}

		if(bs_noBadSubStrings == 'true')  {
			//This element is exempt.
			return '';
		}

		//"x" is important...1/2
		var asBadSubStrs = "x";

		if(f_orm.asGlobalBadSubStrs)  {
			asBadSubStrs = f_orm.asGlobalBadSubStrs;
		}

		if(as_badSubStrs)  {
			asBadSubStrs = as_badSubStrs;
		}

		//"x" is important...2/2
		if(asBadSubStrs == "x")  {
			//There are no sub-strings to consider illegal.
			return '';
		}
		//There *are* sub-strings to be considered illegal.

		for(var i = 0; i < asBadSubStrs.length; i++)  {
			if(s_value.indexOf(asBadSubStrs[i]) != -1)  {
				return s_prefix + s_errorMessage + "\n";
			}
		}

		return '';
	}


	/**
		Do not call this function without first calling crashIfBadCfg_textarea for the form-and-form-element.
	 **/
	function getDebugging_textarea(f_orm, fe_textarea)  {
		var s = getDebuggingNameType(fe_textarea) + getDebuggingMsgRequired(fe_textarea.sMsgRequired);

		if(fe_textarea.bsNoBadSubStrings == 'true')  {
			s += sDBG_INDENT + "bsNoBadSubStrings:  '" + fe_textarea.bsNoBadSubStrings + "'\n";

		}  else if(f_orm.asGlobalBadSubStrs  ||  fe_textarea.asBadSubStrs)  {
			if(fe_textarea.asBadSubStrs)  {
				s += sDBG_INDENT + "asBadSubStrs:  [" + fe_textarea.asBadSubStrs + "]\n";
			}  else if(f_orm.asGlobalBadSubStrs)  {
				s += sDBG_INDENT + "f_orm.asGlobalBadSubStrs:  [" + f_orm.asGlobalBadSubStrs + "]\n";
			}

			s += sDBG_INDENT + sDBG_INDENT + "sMsgBadSubStr:  '" + fe_textarea.sMsgBadSubStr + "'\n";
		}

		if(fe_textarea.sMsgBadLength)  {
			s += sDBG_INDENT + "sMsgBadLength:  '" + fe_textarea.sMsgBadLength + "'\n";

			if(fe_textarea.iMinLength)  {
				s += sDBG_INDENT + sDBG_INDENT + "iMinLength:  " + fe_textarea.iMinLength + "\n";
			}

			if(fe_textarea.iMaxLength)  {
				s += sDBG_INDENT + sDBG_INDENT + "iMaxLength:  " + fe_textarea.iMaxLength + "\n";
			}
		}

		s += getDebuggingValue(fe_textarea.value);

		return s;
	}

	function crashIfBadCfg_textarea(f_orm, fe_textarea)  {
		crashIfMissing(fe_textarea, 'fe_textarea', 'validate_form.crashIfBadCfg_textarea');

		if(fe_textarea.type != 'textarea')  {
			return crashVF("fe_textarea must be of type 'text'.  Currently '" + fe_textarea.type + "'...SANITY CHECK.  SHOULD NEVER HAPPEN.");
		}

		iELEMENT_COUNT++;

		var sName = sName;

		fe_textarea.bsNoBadSubStrings = getOptBooleanStringVF(fe_textarea.bsNoBadSubStrings, "f_orm." + sName + ".bsNoBadSubStrings");
		crashIfNBSSError(sName, fe_textarea.bsNoBadSubStrings, f_orm.asGlobalBadSubStrs, fe_textarea.asBadSubStrs);

		if(fe_textarea.bsNoBadSubStrings == 'false'  &&
			(f_orm.asGlobalBadSubStrs  ||  fe_textarea.asBadSubStrs)  &&
			!fe_textarea.sMsgBadSubStr)  {
			return crashVF("Either f_orm.asGlobalBadSubStrs (currently [" + f_orm.asGlobalBadSubStrs + "]) or f_orm." + sName + ".asBadSubStrs (currently [" + fe_textarea.asBadSubStrs + "]) have  been provided, AND f_orm." + sName + ".bsNoBadSubStrings equals 'false' (meaning there are sub-strings that are illegal for this field).  This means that f_orm." + sName + ".sMsgBadSubStr is required.  However, f_orm." + sName + ".sMsgBadSubStr has not been provided.");
		}

		crashIfBadSpaceTrimConfig(f_orm, fe_textarea);

		var iMin = fe_textarea.iMinLength;
		var iMax = fe_textarea.iMaxLength;
		if(fe_textarea.sMsgBadLength)  {

			if(!iMin  &&  !iMax)  {
				return crashVF("f_orm." + sName + ".sMsgBadLength has been provided (currently '" + fe_textarea.sMsgBadLength + "'), but neither f_orm." + sName + ".iMinLength nor f_orm." + sName + ".iMaxLength have been provided.");
			}

			if(iMin  &&  (!isInteger(iMin)  ||  iMin < 1))  {
				return crashVF("Both f_orm." + sName + ".sMsgBadLength (currently '" + fe_textarea.sMsgBadLength + "') and f_orm." + sName + ".iMinLength (currently " + iMin + ") have been provided, but iMinLength must be an integer, and at least equal to one.  Additional information:  f_orm." + sName + ".iMaxLength currently equals " + iMax + ".");
			}

			if(iMax  &&  (!isInteger(iMax)  ||  iMax < 1))  {
				return crashVF("Both f_orm." + sName + ".sMsgBadLength (currently '" + fe_textarea.sMsgBadLength + "') and f_orm." + sName + ".iMaxLength (currently '" + iMax + "') have been provided, but iMaxLength must be an integer, and at least equal to one.  Additional information:  f_orm." + sName + ".iMinLength currently equals " + iMin + ".");
			}

			if(iMin  &&  iMax  &&  iMin > iMax)  {
				return crashVF("f_orm." + sName + ".sMsgBadLength (currently '" + fe_textarea.sMsgBadLength + "'), f_orm." + sName + ".iMinLength (currently " + iMin + ") and f_orm." + sName + ".iMaxLength (currently " + iMax + ") have all been provided, but iMinLength must be less than or equal to iMaxLength.");
			}

		}  else if(iMin  ||  iMax)  {
			return crashVF("f_orm." + sName + ".sMsgBadLength has *not* been provided, but at least one of f_orm." + sName + ".iMinLength (currently " + iMin + ") and f_orm." + sName + ".iMaxLength (currently " + iMax + ") have been provided.");
		}
	}


	/**
		Do not call this function without first calling crashIfBadCfg_textarea for the form-and-form-element.
	 **/
	function getUserErrors_text(f_orm, i_arrIdx, s_prefix)  {
		var feText = f_orm[i_arrIdx];

		if(needsToBeTrimmed(f_orm, feText))  {
			feText.value = trimSpaces(feText.value);
		}

		if(feText.sMsgRequired  &&  !feText.value)  {
			return s_prefix + feText.sMsgRequired + "\n";
		}

		if(!feText.value)  {
			return '';
		}

		if(feText.sMsgTxtEmail)  {
			if(!isEmail(feText.value))  {
				return s_prefix + feText.sMsgTxtEmail + '\n';
			}

		}  else if(feText.sMsgTxtPhone)  {
			var sPhoneStripped = getValidPhoneNumber(feText.value);
			if(!sPhoneStripped)  {
				return s_prefix + feText.sMsgTxtPhone + '\n';
			}

			feText.value = sPhoneStripped;

		}  else if(feText.sMsgTxtZip)  {
			var sZipStripped = getValidZipCode(feText.value);
			if(!sZipStripped)  {
				return s_prefix + feText.sMsgTxtZip + '\n';
			}

			feText.value = sZipStripped;

		}  else if(feText.sMsgTxtInt)  {
			if(!isInteger(feText.value)  ||
			   (feText.iIntMin  &&  feText.iIntMin > feText.value)  ||
			   (feText.iIntMax  &&  feText.iIntMax < feText.value))  {
				return s_prefix + feText.sMsgTxtInt + '\n';
			}
		}

		if(feText.sMsgBadLength  &&  feText.value.length < feText.iMinLength)  {
			return s_prefix + getBadLengthMsg(feText) + '\n';
		}

		return getBadSubStrMessage(feText.value, f_orm, feText.asBadSubStrs, feText.bsNoBadSubStrings, s_prefix, feText.sMsgBadSubStr);
	}

	function needsToBeTrimmed(f_orm, fe_tta)  {
		if(!fe_tta.value)  {
			return false;
		}

		if(fe_tta.bsDontTrimSpaces == 'true')  {
			return false;
		}

		if(f_orm.bsGlobalTrimSpaces == 'true'  ||  fe_tta.bsTrimSpaces == 'true')  {
			return isSurroundedBySpace(fe_tta.value);
		}
	}


	/**
		Do not call this function without first calling crashIfBadCfg_text for the form-and-form-element.
	 **/
	function getDebugging_text(f_orm, fe_text)  {
		var sDBG_INDENT = "     ";
		var s = getDebuggingNameType(fe_text) + getDebuggingMsgRequired(fe_text.sMsgRequired);

		var bEM = fe_text.sMsgTxtEmail;
		var bInt = fe_text.sMsgTxtInt;
		var bZC = fe_text.sMsgTxtZip;
		var bPN = fe_text.sMsgTxtPhone;

		if(bEM)  {
			s += sDBG_INDENT + "sMsgTxtEmail:  '" + fe_text.sMsgTxtEmail + "'\n";
		}

		if(bZC)  {
			s += sDBG_INDENT + "sMsgTxtZip:  '" + fe_text.sMsgTxtZip + "'\n";
		}

		if(bPN)  {
			s += sDBG_INDENT + "sMsgTxtPhone:  '" + fe_text.sMsgTxtPhone + "'\n";
		}


		if(bInt)  {
			s += sDBG_INDENT + "sMsgTxtInt:  '" + fe_text.sMsgTxtInt + "'\n";

			if(fe_text.iIntMin)  {
				s += sDBG_INDENT + sDBG_INDENT + "iIntMin:  '" + fe_text.iIntMin + "'\n";
			}

			if(fe_text.iIntMax)  {
				s += sDBG_INDENT + sDBG_INDENT + "iIntMax:  '" + fe_text.iIntMax + "'\n";
			}
		}


		if(fe_text.bsNoBadSubStrings == 'true')  {
			s += sDBG_INDENT + "bsNoBadSubStrings:  '" + fe_text.bsNoBadSubStrings + "'\n";

		}  else if(!bInt  &&  !bZC  &&  !bPN  &&
		           (f_orm.asGlobalBadSubStrs  ||  fe_text.asBadSubStrs))  {

			var sSecondIndent = '';
			if(fe_text.asBadSubStrs)  {
				s += sDBG_INDENT + "asBadSubStrs:  [" + fe_text.asBadSubStrs + "]\n";
				sSecondIndent = sDBG_INDENT;
			}
		}

		if(fe_text.sMsgBadLength)  {
			s += sDBG_INDENT + "sMsgBadLength:  '" + fe_text.sMsgBadLength + "'\n" + sDBG_INDENT + sDBG_INDENT + "iMinLength:  " + fe_text.iMinLength + "\n";

			if(fe_text.maxLength)  {
				s += sDBG_INDENT + sDBG_INDENT + "MAXLENGTH:  " + fe_text.MAXLENGTH + "\n";
			}

		}

		s += getDebuggingValue(fe_text.value);

		return s;
	}

	function crashIfBadCfg_text(f_orm, fe_text)  {
		crashIfMissing(fe_text, 'fe_text', 'validate_form.crashIfBadCfg_text');

		if(fe_text.type != 'text'  &&  fe_text.type != 'password')  {
			return crashVF("fe_text must be of type 'text' or 'password'.  Currently '" + fe_text.type + "'...SANITY CHECK.  SHOULD NEVER HAPPEN.");
		}

		var sName = fe_text.name;

		iELEMENT_COUNT++;

		var bEM = fe_text.sMsgTxtEmail;
		var bInt = fe_text.sMsgTxtInt;
		var bZC = fe_text.sMsgTxtZip;
		var bPN = fe_text.sMsgTxtPhone;

		var iSpecialTypes = 0;
		var sSpecialTypes = '';
		if(bEM)  {
			iSpecialTypes++;
			sSpecialTypes += 'sMsgTxtEmail...';
		}

		if(bInt)  {
			iSpecialTypes++;
			sSpecialTypes += 'sMsgTxtInt...';

			if(fe_text.iIntMin  &&  !isInteger(fe_text.iIntMin))  {
				return crashVF("f_orm." + sName + ".sMsgTxtInt has been provided, and the sub-variable " + sName + ".iIntMin has also been provided.  However, iIntMin must be an integer.  Currently '" + fe_text.iIntMin + "'");
			}

			if(fe_text.iIntMax  &&  !isInteger(fe_text.iIntMax))  {
				return crashVF("f_orm." + sName + ".sMsgTxtInt has been provided, and the sub-variable " + sName + ".iIntMax has also been provided.  However, iIntMax must be an integer.  Currently '" + fe_text.iIntMax + "'");
			}

			if(fe_text.iIntMin  &&  fe_text.iIntMax  &&
				!isValidRange(fe_text.iIntMin, fe_text.iIntMax))  {
				return crashVF("f_orm." + sName + ".sMsgTxtInt has been provided, and both the sub-variables " + sName + ".iIntMin and " + sName + ".iIntMax have also been provided.  Both these variables are definitely legal integers (according to util_number.isInteger()), however, iIntMin and iIntMax are not a legal range.  Currently '" + fe_text.iIntMin + "' and '" + fe_text.iIntMax + "'");
			}

		}  else if(fe_text.iIntMin  ||  fe_text.iIntMax)  {
			return crashVF("f_orm." + sName + ".sMsgTxtInt has *not* been provided, but one or both of the sub-variables " + sName + ".iIntMin (currently '" + fe_text.iIntMin + "')/" + sName + ".iIntMax (currently '" + fe_text.iIntMax + "') have been provided.");
		}

		if(bZC)  {
			iSpecialTypes++;
			sSpecialTypes += 'sMsgTxtZip...';
		}

		if(bPN)  {
			iSpecialTypes++;
			sSpecialTypes += 'sMsgTxtPhone...';
		}

		if(iSpecialTypes > 1)  {
			return crashVF("f_orm." + sName + " is of type 'text', but has " + iSpecialTypes + " special text types associated to it.  Zero or one of the following special types may be used for a text form element:  sMsgTxtEmail, sMsgTxtInt, sMsgTxtZip, sMsgTxtPhone.  Actually existing special types found:  " + sSpecialTypes);
		}

		crashIfBadBSSA('f_orm.' + sName + '.asBadSubStrs', fe_text.asBadSubStrs);

		fe_text.bsNoBadSubStrings = getOptBooleanStringVF(fe_text.bsNoBadSubStrings, "f_orm." + sName + ".bsNoBadSubStrings");

		crashIfNBSSError(sName, fe_text.bsNoBadSubStrings, f_orm.asGlobalBadSubStrs, fe_text.asBadSubStrs);

		if(fe_text.bsNoBadSubStrings == 'false'  &&  !bInt  &&  !bZC  &&  !bPN  &&
			(f_orm.asGlobalBadSubStrs  ||  fe_text.asBadSubStrs)  &&
			!fe_text.sMsgBadSubStr)  {
			return crashVF("Either f_orm.asGlobalBadSubStrs (currently [" + f_orm.asGlobalBadSubStrs + "]) or f_orm." + sName + ".asBadSubStrs (currently [" + fe_text.asBadSubStrs + "]) have  been provided, AND f_orm." + sName + ".bsNoBadSubStrings equals 'false' (meaning there are sub-strings that are illegal for this field) AND this text/password field does not have a 'number' special type (sMsgTxtInt, sMsgTxtZip, sMsgTxtPhone).  This means that f_orm." + sName + ".sMsgBadSubStr is required.  However, f_orm." + sName + ".sMsgBadSubStr has not been provided.");
		}

		crashIfBadSpaceTrimConfig(f_orm, fe_text);

		var bSpecial = (bEM  ||  bInt  ||  bZC  ||  bPN);
		if(bSpecial  &&
			(fe_text.bsTrimSpaces == 'true'  ||  fe_text.bsDontTrimSpaces == 'true'))  {
			return crashVF("Either f_orm." + sName + ".bsTrimSpaces (currently '" + fe_text.bsTrimSpaces + "') or f_orm." + sName + ".bsDontTrimSpaces (currently '" + fe_text.bsTrimSpaces + "') equal 'true'.  But this is illegal, because the element has these special types associated to it:  " + sSpecialTypes);
		}

		var iMin = fe_text.iMinLength;
		if(fe_text.sMsgBadLength)  {
			if(!iMin)  {
				return crashVF("f_orm." + sName + ".sMsgBadLength has been provided (currently '" + fe_text.sMsgBadLength + "'), but f_orm." + sName + ".iMinLength has not been provided");
			}

			if(iMin  &&  (!isInteger(iMin)  ||  iMin < 1))  {
				return crashVF("Both f_orm." + sName + ".sMsgBadLength (currently '" + fe_text.sMsgBadLength + "') and f_orm." + sName + ".iMinLength (currently '" + iMin + "') have been provided, but iMinLength must be an integer, and at least equal to one.");
			}

			//NOTE:  When MAXLENGTH is an attribute of the element,
			//fe_text.MAXLENGTH is undefined, but fe_text.maxLength
			//is not.  Things that make you go hmmmmm.
			if(fe_text.maxLength  &&  fe_text.maxLength != -1  &&
			   isInteger(fe_text.maxLength)  &&  iMin > fe_text.maxLength)  {
				return crashVF("Both f_orm." + sName + ".sMsgBadLength (currently '" + fe_text.sMsgBadLength + "') and f_orm." + sName + ".iMinLength (currently " + iMin + ") have been provided, but iMinLength must be an integer between one and f_orm." + sName + ".MAXLENGTH (currently " + fe_text.maxLength + "), inclusive.");
			}

		}  else if(iMin)  {
			return crashVF("f_orm." + sName + ".sMsgBadLength has *not* been provided, but the sub-attribute f_orm." + sName + ".iMinLength (currently " + iMin + ") has been provided.");
		}

		if(fe_text.iMaxLength)  {
			return crashVF("f_orm." + sName + ".iMaxLength (currently " + fe_text.iMaxLength + ") is only a legal sub-attribute for text elements.  To enforce maximum length in a text or password element, use the standard MAXLENGTH attribute.");
		}
	}

	function crashIfBadSpaceTrimConfig(f_orm, fe_tta)  {
		fe_tta.bsTrimSpaces = getOptBooleanStringVF(fe_tta.bsTrimSpaces, "f_orm." + fe_tta.name + ".bsTrimSpaces");
		fe_tta.bsDontTrimSpaces = getOptBooleanStringVF(fe_tta.bsDontTrimSpaces, "f_orm." + fe_tta.name + ".bsDontTrimSpaces");

		if(f_orm.bsGlobalTrimSpaces == 'true')  {
			if(fe_tta.bsTrimSpaces == 'true')  {
				return crashVF("Both f_orm.bsGlobalTrimSpaces and f_orm." + fe_tta.name + ".bsTrimSpaces equal 'true'.  At a maximum, only one of these variables may be 'true'.");
			}

		}  else  if(fe_tta.bsDontTrimSpaces == 'true')  {
			return crashVF("f_orm.bsGlobalTrimSpaces has *not* been provided (or is equal to 'false'), but f_orm." + fe_tta.name + ".bsDontTrimSpaces equals 'true'.  ");
		}
	}

	function crashIfNBSSError(s_textPwName, b_noBadSubStrings, as_globalBadSubStrs, as_badSubStrs)  {
		if(b_noBadSubStrings == 'true'  &&
		   (as_badSubStrs  ||  !as_globalBadSubStrs))  {
			return crashVF("f_orm." + s_textPwName + ".bsNoBadSubStrings equals 'true', but either , f_orm.asGlobalBadSubStrs has *not* been provided (currently " + as_globalBadSubStrs + ") or f_orm." + s_textPwName + ".asBadSubStrs *has* been provided (currently " + as_badSubStrs + ").  When bsNoBadSubStrings equals 'true', it is required that or asBadSubStrs not be provided and f_orm.asGlobalBadSubStrs should be provided.");
		}
	}

	function getRqdMissingMsg(s_requiredMsg, s_value)  {
		crashIfMissing(s_value, 's_value ("missing" means empty string)', 'validate_form.getRqdMissingMsg');

		if(!s_requiredMsg)  {
			//This value is not required.
			return '';
		}
		//This value *is* required...

		if(s_value == '')  {
			//...but has not been provided.
			return s_requiredMsg;
		}

		//...and has been provided.
		return '';
	}

	/**
		Crash If the provided bad-sub-string-array is bad.
	 **/
	function crashIfBadBSSA(s_variableName, as_badSubStrs)  {
		if(!as_badSubStrs)  {
			return;
		}

		var sRule = " is not required, but when it is provided, it must be of type array, at least one element in length, and each element must be of type string, at least one character in length, and all elements must be unique.  Currently, ";

		if(!isArray(as_badSubStrs))  {
			return crashVF("crashIfBadBSSA:  " + s_variableName + sRule + "it is of type '" + typeof(as_badSubStrs) + "'.");
		}

		if(as_badSubStrs.length < 1)  {
			return crashVF("crashIfBadBSSA:  " + s_variableName + sRule + "it is zero elements in length.");
		}

		for(var i = 0; i < as_badSubStrs.length; i++)  {
			if(typeof(as_badSubStrs[i]) != "string")  {
				return crashVF("crashIfBadBSSA:  " + s_variableName + sRule + " s_variableName[" + i + "] is of type '" + typeof(as_badSubStrs[i]) + "'.");
			}

			if(as_badSubStrs[i].length < 1)  {
				return crashVF("crashIfBadBSSA:  " + s_variableName + sRule + " s_variableName[" + i + "] is zero characters in length.");
			}

			for(var j = i + 1; j < as_badSubStrs.length; j++)  {
				if(as_badSubStrs[i] == as_badSubStrs[j])  {
					return crashVF("crashIfBadBSSA:  " + s_variableName + sRule + " Element " + i + " (currently '" + as_badSubStrs[i] + "') is equal to element " + j + ".");
				}
			}
		}
		//All elements are definitely of type string.

		for(var i = 0; i < as_badSubStrs.length; i++)  {
			for(var j = i + 1; j < as_badSubStrs.length; j++)  {
				if(as_badSubStrs[i] == as_badSubStrs[j])  {
					return crashVF("crashIfBadBSSA:  " + s_variableName + sRule + " Element " + i + " (currently '" + as_badSubStrs[i] + "') is equal to element " + j + ".");
				}
			}
		}
	}

	function	wasRCBSMalreadyFound(s_elementName)  {
		return (getRCBSMTrackerArrIdx(s_elementName) != -1);
	}

	function	getFirstArrIdx(s_elementName)  {
		var iArrIdx = getAndValidateRCBSMTArrIdx("getFirstArrIdx", s_elementName);
		var iFirstArrIdx = aiRCBSM_TRACKER_ARR_IDX[iArrIdx];

		if(iFirstArrIdx == -1)  {
			return crashVF("getFirstArrIdx:  Element named '" + s_elementName + "' *was* found in the tracker array objects, but it is of type select-multiple, which does not have multiple instances, and is therefore invalid for this function.  SANITY CHECK.  SHOULD NEVER HAPPEN.");
		}

		return iFirstArrIdx;
	}

	function	getMCSelectedCount(s_elementName)  {
		var iArrIdx = getAndValidateRCBSMTArrIdx("getMCSelectedCount", s_elementName);
		var iSelCt = aiRCBSM_TRACKER_SEL_COUNT[iArrIdx];

		if(iSelCt == -1)  {
			return crashVF("getMCSelectedCount:  Element named '" + s_elementName + "' *was* found in the tracker array objects, but it is of type radio, which is a single-choice element, and is therefore invalid for this function.  SANITY CHECK.  SHOULD NEVER HAPPEN.");
		}
		return iSelCt;
	}

	function getAndValidateRCBSMTArrIdx(s_callingFunction, s_elementName)  {
		var iArrIdx = getRCBSMTrackerArrIdx(s_elementName);

		if(iArrIdx == -1)  {
			return crashVF("getAndValidateRCBSMTArrIdx ('" + s_callingFunction + "'):  Element named '" + s_elementName + "' was not found in the tracker array objects.  SANITY CHECK.  SHOULD NEVER HAPPEN.");
		}

		return iArrIdx;
	}


	function	getRCBSMTrackerArrIdx(s_elementName)  {
		for(var i = 0; i < asRCBSM_TRACKER_NAME.length; i++)  {
			if(asRCBSM_TRACKER_NAME[i] == s_elementName)  {
				return i;
			}
		}

		return -1;
	}


	function addRCBSMtoTracker(f_orm, fe_rcbsm, i_formArrIdx)  {
		if(asRCBSM_TRACKER_NAME.length < 1)  {
			//This is the first to be tracked.  Just create a new
			//array containing this name as the only element.
			asRCBSM_TRACKER_NAME = [fe_rcbsm.name];
			aiRCBSM_TRACKER_SEL_COUNT = [getMCSelectedCountForTracker(f_orm, fe_rcbsm)];
			aiRCBSM_TRACKER_ARR_IDX = [getRCBSMFirstArrIdx(fe_rcbsm, i_formArrIdx)];

			return;
		}
		//At least one element is already being tracked.  Add this
		//element to the existing tracking arrays.

		if(wasRCBSMalreadyFound(fe_rcbsm.name))  {
			return crashVF("addRCBSMtoTracker:  Element named '" + fe_rcbsm.name + "' already exists in the TRACKER arrays.  SANITY CHECK.  SHOULD NEVER HAPPEN.");
		}

		var asTrackerNames = new Array(asRCBSM_TRACKER_NAME.length + 1);
		for(var i = 0; i < asTrackerNames.length; i++)  {
			if(i < (asTrackerNames.length - 1))  {
				asTrackerNames[i] = asRCBSM_TRACKER_NAME[i];
			}  else  {
				asTrackerNames[i] = fe_rcbsm.name;
			}
		}
		asRCBSM_TRACKER_NAME = asTrackerNames;

		var aiTrackerCounts = new Array(aiRCBSM_TRACKER_SEL_COUNT.length + 1);
		for(var i = 0; i < aiTrackerCounts.length; i++)  {
			if(i < (aiTrackerCounts.length - 1))  {
				aiTrackerCounts[i] = aiRCBSM_TRACKER_SEL_COUNT[i];
			}  else  {
				aiTrackerCounts[i] = getMCSelectedCountForTracker(f_orm, fe_rcbsm);
			}
		}
		aiRCBSM_TRACKER_SEL_COUNT = aiTrackerCounts;

		var aiTrackerArrIdx = new Array(aiRCBSM_TRACKER_ARR_IDX.length + 1);
		for(var i = 0; i < aiTrackerArrIdx.length; i++)  {
			if(i < (aiTrackerArrIdx.length - 1))  {
				aiTrackerArrIdx[i] = aiRCBSM_TRACKER_ARR_IDX[i];
			}  else  {
				aiTrackerArrIdx[i] = getRCBSMFirstArrIdx(fe_rcbsm, i_formArrIdx);
			}
		}
		aiRCBSM_TRACKER_ARR_IDX = aiTrackerArrIdx;
	}

	function getRCBSMFirstArrIdx(fe_rcbsm, i_formArrIdx)  {
		if(fe_rcbsm.type == "select-multiple")  {
			//select-multiple only has one instance in the form.  -1 indicates that.
			return -1;
		}

		return i_formArrIdx;
	}

	function getMCSelectedCountForTracker(f_orm, fe_rcbsm)  {
		if(fe_rcbsm.type != "radio"  &&
		   fe_rcbsm.type != "checkbox"  &&
		   fe_rcbsm.type != "select-multiple")  {
			return crashVF("getMCSelectedCount:  Element named '" + fe_rcbsm.name + "' is not of type radio, checkbox or select-multiple.  Type of this element is '" + fe_rcbsm.type + "'.  SANITY CHECK.  SHOULD NEVER HAPPEN.");
		}

		if(fe_rcbsm.type == "radio")  {
			//This is not a multiple choice type.  -1 indicates that.
			return -1;
		}

		var iMCSelectedCount = 0;

		if(fe_rcbsm.type == "checkbox")  {
			for(var i = 0; i < f_orm[fe_rcbsm.name].length; i++)  {
				if(f_orm[fe_rcbsm.name][i].checked)  {
					iMCSelectedCount++;
				}
			}
			return iMCSelectedCount;
		}
		//Type is definitely select-multiple.

		for(var i = 0; i < fe_rcbsm.length; i++)  {
			if(fe_rcbsm.options[i].selected)  {
				iMCSelectedCount++;
			}
		}
		return iMCSelectedCount;
	}

	function getOptBooleanStringVF(bs_potential, s_variableName)  {
		return getOptBooleanString(bs_potential, s_variableName, 'validate_form.getFormErrorMsgs');
	}


	function crashVF(s_message)  {
		return crash("validate_form.getFormErrorMsgs:  " + s_message);
	}

	function initializeLocalVars()  {
		resetCrashedFlag();
		asRCBSM_TRACKER_NAME = [];
		aiRCBSM_TRACKER_SEL_COUNT = [];
		aiRCBSM_TRACKER_ARR_IDX = [];
		iELEMENT_COUNT = 0;
		bFIRST_DEBUG_SCREEN_SEEN = false;
	}


	/**
		For backwards compatibility only.  Only use getFormErrorMsgs.  This function will eventually be eliminated.
	 **/
	function validateForm(f_orm, s_userErrorPrefix, i_debugPerScreen)  {
		return getFormErrorMsgs(f_orm, s_userErrorPrefix, i_debugPerScreen);
	}


/**
	PRIVATE FUNCTIONS...end
 **/


/**************************************************************************************************
	util_string.js...START

	Do not edit this file.  This file is dynamically generated by the validate_form.js
	ant build process
 **************************************************************************************************/

/**
	VALIDATE_FORM.JS
	Comprehensive solution for validating HTML forms.
	Copyright (C) 2002, Jeff Epstein, jeff_epstein@yahoo.com, http://www.jeffyjeffy.com#download

	This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 **/



/**
	Does the provided string start or end with a space?

	EQUAL TO
		isSurroundedByChar(s_tring, ' ');
 **/
function isSurroundedBySpace(s_tring)  {
	return isSurroundedByChar(s_tring, ' ');
}

/**
	Does the provided string start or end with the provided character?

	PARAMETERS
		s_tring  The string to analyze.
		c_har    The character that is determined to exist at the start or end of s_tring.

	RETURNS
		true   If the first or last character in s_tring is c_har.
		false  If s_tring is null or zero characters in length.
				 If both the first and last characters in s_tring are *not* c_har.
 **/
function isSurroundedByChar(s_tring, char_toTrim)  {
	if(!s_tring)  {
		return false;
	}

	if(!char_toTrim  ||  char_toTrim.length != 1)  {
		return crash('isSurroundedByChar:  char_toTrim parameter must be provided, and be exactly one character in length.');
	}

	if(s_tring.length < 1)  {
		return false;
	}

	if(s_tring.substring(0, 1) == char_toTrim)  {
		return true;
	}

	return (s_tring.substring(s_tring.length - 1, s_tring.length) == char_toTrim);
}



/**
	Is the provided string a legal integer?

	PARAMETERS
		s_potentialInteger
			The string to analyze.  Required.

	RETURNS
		true
			If s_potentialInteger is a legal integer.  A legal integer is a string that...
				...contains one or more zeros.  Note that 0 equals 0000 equals 0000000000 equals ...
				...starts with zero or one dashes (indicating negative, followed by only digits (0-9), where at least one is greater than zero.

			Here are some legal examples:
				-1
				-1
				0
				1
				-50
				8750328754
				00008750328754
				08750328754
				-487584758475235
				-0000000487584758475235
				-0487584758475235

		false
			If the above rules are violated.  Here are some illegal examples:
				4875847-58475235
				487584758475235-
				[space]
				a
				0-
				-0
				1.1
				Not a number!!!
				Some 123 numbers 456
				123 456
 **/
function isInteger(s_potentialInteger)  {
	crashIfMissing('util_string.isInteger', s_potentialInteger, 's_potentialInteger');

	if(s_potentialInteger.length < 1)  {
		return false;
	}

	if(s_potentialInteger == '-')  {
		//Negative only makes no sense.
		return false;
	}

	if(/^-[0]+$/.test(s_potentialInteger))  {
		//Negative zero makes no sense.
		return false;
	}

	if(/^[0]+$/.test(s_potentialInteger))  {
		//Zero is a legal number:
		//0  ==  0000000000000  ==  000
		return true;
	}
	//It is definitely not zero.

	//^         The start of the line.
	//[-]{0,1}  Zero or one dashes.
	//[0-9]+    Zero or more of any digit.
	//$         The end of the line.
	if(/^[-]{0,1}[0-9]+$/.test(s_potentialInteger))  {
		return true;
	}
	//It has not fulfilled the requirements for a number.

	return false;
}



function isEmail(s_potentialEmail)  {
	if(!s_potentialEmail  ||  s_potentialEmail.length < 1)  {
		return crash("isValidNumber:  s_potentialEmail must be defined, and at least one character in length.");
	}

	if(s_potentialEmail.indexOf(" ") != -1  ||
	   s_potentialEmail.indexOf("/") != -1  ||
	   s_potentialEmail.indexOf("\\") != -1  ||
	   s_potentialEmail.indexOf(",") != -1)  {
		//A space, slash or comma was found.
		return false;
	}

	if(/[@.][@.]/.test(s_potentialEmail))  {
		//An @ or dot is followed by an @ or dot.
		return false;
	}

	if(/^[@.]/.test(s_potentialEmail))  {
		//The first character is an @ or dot.
		return false;
	}

	if(/[@.]$/.test(s_potentialEmail))  {
		//The last character may not be an @ or dot.
		return false;
	}

	if(!/^[^@]+@[^@]+$/.test(s_potentialEmail))  {
		//Only one @ allowed
		//		(Negative of:
		//		 Must have one or more non-@ starting the line,
		//		 exactly one @, and then
		//		 one or more non-@ ending the line.)
		return false;
	}
	//There is exactly one @.

	if(!/@.*[.]/.test(s_potentialEmail))  {
		//At least one dot must follow the (single) @.
		return false;
	}
	//At least one dot follows the @.  We already determined
	//above that it does not immediately follow it,


	//No negative conditions were met.  This is a legal email address.
	//Cool, eh?  : )
	return true;
}


function removeNonNumbers(s_tring)  {
	if(!s_tring  ||  s_tring.length < 1)  {
		return crash("removeNonNumbers:  s_tring must be defined, and at least one character in length.");
	}

	var reNonNumbers = /^\D$/;
	if(/^\D$/.test(s_tring))  {
		//There are no numbers at all.
		return "";
	}
	//There is at least one number.

	var asNumberParts = s_tring.split(/\D/);

	var sNumber = asNumberParts[0];
	for(var i = 1; i < asNumberParts.length; i++)  {
		sNumber += asNumberParts[i];
	}

	return sNumber;
}


function getValidPhoneNumber(s_potentialPhoneNumber)  {
	if(!s_potentialPhoneNumber  ||  s_potentialPhoneNumber.length < 1)  {
		return crash("isValidNumber:  s_potentialPhoneNumber must be defined, and at least one character in length.");
	}

	var iPhoneNumber = removeNonNumbers(s_potentialPhoneNumber);

	if(!iPhoneNumber  ||  iPhoneNumber.length < 1)  {
		return "";
	}

	if(isValidNumber(iPhoneNumber, 1000000000, 9999999999))  {
		return iPhoneNumber;
	}
	//It is not a valid phone number.

	return "";
}


function getValidZipCode(s_potentialZipCode)  {
	if(!s_potentialZipCode  ||  s_potentialZipCode.length < 1)  {
		return crash("isValidNumber:  s_potentialZipCode must be defined, and at least one character in length.");
	}

	var iZipCode = removeNonNumbers(s_potentialZipCode);

	if(!iZipCode)  {
		return "";
	}

	if(!iZipCode  ||  (iZipCode.length != 9  &&  iZipCode.length != 5))  {
		return "";
	}

	var bZerosOnly = true;
	for(var i = 0; i < iZipCode.length  &&  bZerosOnly; i++)  {
		var cDigit = iZipCode.substring(i, i + 1);
		if(cDigit != 0)  {
			bZerosOnly = false;
		}
	}

	if(bZerosOnly)  {
		//There must be at least one non-zero digit.
		return "";
	}

	//It is exactly five (or nine) digits.  No non-digit characters.
	return iZipCode;
}


function trimSpaces(s_tring)  {
	return trimChar(' ', s_tring);
}

function trimChar(c_harToTrim, s_tring)  {
	if(!c_harToTrim  ||  c_harToTrim.length != 1)  {
		return crash("util_sting.trimChar:  c_harToTrim must be provided, and must be exactly one character in length.  Currently '" + c_harToTrim + "'.");
	}

	crashIfMissing('util_string.trimSpaces', s_tring, 's_tring');

	var i = 0;
	var iStartingSpaces = 0;
	while(s_tring.substring(i, (i + 1)) == c_harToTrim)  {
		iStartingSpaces++
		i++;
	}

	if(iStartingSpaces == s_tring.length)  {
		return '';
	}

	i = 0;
	var iEndingSpaces = 0;
	while(s_tring.substring((s_tring.length - i), (s_tring.length - (i + 1))) == c_harToTrim)  {
		iEndingSpaces++
		i++;
	}

	if(iStartingSpaces == 0  &&  iEndingSpaces == 0)  {
		return s_tring;
	}

	return s_tring.substring(iStartingSpaces, (s_tring.length - iEndingSpaces));
}

/**************************************************************************************************
	util_number.js...START

	Do not edit this file.  This file is dynamically generated by the validate_form.js
	ant build process
 **************************************************************************************************/

/**
	VALIDATE_FORM.JS
	Comprehensive solution for validating HTML forms.
	Copyright (C) 2002, Jeff Epstein, jeff_epstein@yahoo.com, http://www.jeffyjeffy.com#download

	This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 **/


function isValidRangeOpt(i_min, bs_enforceMin, i_max, bs_enforceMax)  {
	crashIfBadBooleanString(bs_enforceMin, 'bs_enforceMin', 'util_number.isValidRangeOpt');
	crashIfBadBooleanString(bs_enforceMax, 'bs_enforceMax', 'util_number.isValidRangeOpt');

	if(bs_enforceMin == 'false'  ||  bs_enforceMax == 'false')  {
		//At least one of the bounds are not being enforced.
		return true;
	}

	return !(i_min > i_max);
}

function isValidRange(i_min, i_max)  {
	return isValidRangeOpt(i_min, 'true', i_max, 'true');
}

function isValidNumber(i_number, i_min, i_max)  {
	return isValidNumberOpt(i_number, i_min, 'true', i_max, 'true');
}

/**
	It is assumed that the range is valid.  You will get unpredictable results if it is not.
 **/
function isValidNumberOpt(i_number, i_min, bs_enforceMin, i_max, bs_enforceMax)  {
	crashIfMissing("util_number.isValidNumber", i_number, "i_number");

			//	alert("isValidRangeOpt(" + i_number + ", " + i_min + ", " + bs_enforceMin + ", " + i_max + ", " + bs_enforceMax + ")...\n...isValidRangeOpt(" + i_min + ", " + bs_enforceMin + ", " + i_number + ", 'true')=" + isValidRangeOpt(i_min, bs_enforceMin, i_number, 'true') + "\n...isValidRangeOpt(" + i_number + ", 'true', " + i_max + ", " + bs_enforceMax + ")=" + isValidRangeOpt(i_number, 'true', i_max, bs_enforceMax));

	return (isValidRangeOpt(i_min, bs_enforceMin, i_number, 'true')  &&
	        isValidRangeOpt(i_number, 'true', i_max, bs_enforceMax));
}



/**************************************************************************************************
	utility.js...START

	Do not edit this file.  This file is dynamically generated by the validate_form.js
	ant build process
 **************************************************************************************************/

/**
	VALIDATE_FORM.JS
	Comprehensive solution for validating HTML forms.
	Copyright (C) 2002, Jeff Epstein, jeff_epstein@yahoo.com, http://www.jeffyjeffy.com#download

	This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 **/


//GLOBAL VARIABLES...start

	//A secret public global variable.  For testing purposes, you may want to
	//avoid re-displaying the error after the alert box (within H1 tags).
	//
	//This prevents the crash function from re-presenting the
	//error, after the alert box, within H1 tags.  It makes it so you
	//don't have to press the back button between each error, in order
	//to get back to this page.
	var bCRASH_ONLY_ALERT_BOX = false;

	//This is automatically manipulated by this js file.  It is private
	//and should never be altered.
	var bCrashed = false;
//GLOBAL VARIABLES...end

/**
	Present an error message in an alert box, and also print the provided error message to screen, surrounded by H1 tags.  This function is intended for programming errors, not user errors.

	Although execution is not truly interrupted, this should be significant enough to alert the programmer to an error.

	PARAMETERS
		s_callingFuncAndError
			The function name, followed by the error message.

	RETURNS
		false
			Always.
 **/
function crash(s_callingFuncAndError)  {
	alert("-------ERROR-------\n\nERROR in " + s_callingFuncAndError);

	if(!bCRASH_ONLY_ALERT_BOX)  {
		document.writeln("<h1>ERROR in " + s_callingFuncAndError + "</h1>");
	}

	bCrashed = true;

	return false;
}

/**
	Was crash() called?

	RETURNS
		true
			If crash() was called.
		false
			If crash() was never called...or when it was, but you since called resetCrashedFlag().
 **/
function hasCrashed()  {
	return bCrashed;
}

/**
	Make it appear as if crash() was never called, regardless of whether or not it actually was.  This is probably only useful for testing purposes.
 **/
function resetCrashedFlag()  {
	bCrashed = false;
}

/**
	Conditionally print the provided message in an alert box.

	PARAMETERS
		b_condition
			If true, present s_message in an alert box.  If false, do nothing.
		s_message
			The message to present in an alert box, should b_condition be true.  If b_condition is false, this parameter is ignored.

	RETURNS
		true
			If b_condition is true.
		false
			If b_condition is false.
 **/
function calert(b_condition, s_message)  {
	if(b_condition)  {
		alert(s_message);
		return false;
	}

	return true;
}


/**
	Convenience function to print an error message, should a required parameter not be provided.

	PARAMETERS
		s_callingFileFunc
			Passed directly to crash, if !o_param.  If o_param is true, this parameter is ignored.
		s_paramName
			The name of o_param, only for the potential error message.  If o_param is true, this parameter is ignored.
		o_param
			The required parameter.  If "false" (!o_param) the an error message is printed.

	RETURNS
		true
			If o_param is true.
		false
			If o_param is false.
 **/
function crashIfMissing(s_callingFileFunc, s_paramName, o_param)  {
	if(!o_param)  {
		return crash(s_callingFileFunc + ':  Required parameter ' + s_paramName + ' not provided.');
	}

	return true;
}

/**
	A single unit test.  If the actual result differs from the actual, an error alert box is presented.

	PARAMETERS
		s_nameOfTest
			The name of the calling JavaScript file and function name, currently being tested.  Required.
		i_testNumber
			The number of the test, for potential error messages only.  Required.
		s_expected
			The expected response from this test.
		s_actual
			The actual response from this test.

	RETURNS
		true
			If s_expected equals s_actual.
		false
			If s_expected does not equal s_actual.
 **/
function test(s_nameOfTest, i_testNumber, s_expected, s_actual)  {
	crashIfMissing(s_nameOfTest, 's_nameOfTest', 'utility.test');
	crashIfMissing(i_testNumber, 'i_testNumber', 'utility.test');

	if(s_expected != s_actual)  {
		alert("---ERROR---\n\n\tTest name:\t" + s_nameOfTest + '\n\tTest number:\t' + i_testNumber + '\n\tExpected:\t"' + s_expected + '"\n\tActual:\t\t"' + s_actual + '"');
		return false;
	}

	return true;
}

function crashIfBadBooleanString(bs_potential, s_variableName, s_callingFunc)  {
	if(!bs_potential)  {
		return crash(s_callingFunc + ":  " + s_variableName + " must be a *string*, provided, and equal to either 'true' or 'false'.  It is currently a *boolean* equal to 'false'.");
	}

	if(bs_potential != 'true'  &&  bs_potential != 'false')  {
		var sValue = "string equal to '" + bs_potential;
		if(typeof(bs_potential) == 'boolean')  {
			//It is equal to *boolean* true.  It's not a string.
			//Note that it's NOT false, because the first line of this
			//function eliminated this possibility.
			sValue = "*boolean* equal to '" + bs_potential;
		}
		return crash(s_callingFunc + ":  " + s_variableName + " must be a string, provided, and equal to either 'true' or 'false'.  It is currently a " + sValue + "'.");
	}
}

function crashIfBadBooleanStringOpt(bs_potential, s_variableName, s_callingFunc)  {
	if(!bs_potential)  {
		return;
	}
	//bs_potential has been provided.

	if(bs_potential != 'true'  &&  bs_potential != 'false')  {
		var sValue = "string equal to '" + bs_potential + "";
		if(typeof(bs_potential) == 'boolean')  {
			//It is equal to *boolean* true.  It's not a string.
			//Note that it's NOT false, because the first line of this
			//function eliminated this possibility.
			sValue = "*boolean* equal to '" + bs_potential + "";
		}
		return crash(s_callingFunc + ":  " + s_variableName + " is not required, but when provided it must be a string, and equal to either 'true' or 'false'.  It is currently a " + sValue + "'.");
	}
}


function getOptBooleanString(bs_potential, s_variableName, s_callingFunc)  {
	if(bs_potential)  {
		crashIfBadBooleanStringOpt(bs_potential, s_variableName, s_callingFunc);
		return bs_potential;
	}  else  {
		return 'false';
	}
}




/**
	Is the provided object an array?

	This concept comes from Kas Thomas, at
	http://www.planetpdf.com/mainpage.asp?webpageid=1144

	PARAMETERS
		o_potentialArray
			The object that is analyzed to see if it is an array.  Required.

	RETURNS
		true
			If o_potentialArray is an array.
		false
			If o_potentialArray is anything but an array.

 **/
function isArray(o_potentialArray)  {
	crashIfMissing(o_potentialArray, 'o_potentialArray', 'utility.isArray');

	if(typeof o_potentialArray != 'object')  {
		return false;
	}
	//It is definitely an object.

	return (o_potentialArray.constructor.toString().match(/array/i) != null);
}


/**************************************************************************************************
	Do not edit this file.  This file is dynamically generated by the validate_form.js
	ant build process

	utility.js...END
 **************************************************************************************************/



/**************************************************************************************************
	Do not edit this file.  This file is dynamically generated by the validate_form.js
	ant build process

	util_number.js...END
 **************************************************************************************************/



/**************************************************************************************************
	Do not edit this file.  This file is dynamically generated by the validate_form.js
	ant build process

	util_string.js...END
 **************************************************************************************************/




/**************************************************************************************************
	Do not edit this file.  This file is dynamically generated by the validate_form.js
	ant build process

	validate_form.js...END
 **************************************************************************************************/

