	/***Functions for validating forms***/
	
	/********************************************************************************/
	//WB; Formats a text input for currency values and turns red when it is an invalid value.
	function checkCCNumber(roElement)
	{	
		var oValue = roElement.value;
		if (isCCNumber(oValue)) alert('This is a valid credit card number!');
		else  alert('This is NOT a valid credit card number!');
	}
	/********************************************************************************/
	//WB; Formats a text input for currency values and turns red when it is an invalid value.
	function checkCurrency(roElement)
	{	
		var oNumericType = new numericType('currency');
		var oValue = roElement.value;
		var oValue = oValue.replace('$', '');
		var oValue = oValue.replace(',', '');
		
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(roElement.value)) {
			if (roElement.style) roElement.style.color = 'black';
			return true;
		}
		//Make sure there is a leading zero for decimal numbers.
		if (0 == oValue.indexOf('.')) oValue = '0' + oValue;
		
		if (!isValidFormat(false, oNumericType.Mask, oValue)){
			delete oNumericType;
			if (roElement.style) roElement.style.color = 'red';
			return false;
		}
		delete oNumericType;
		
		if (roElement.style) roElement.style.color = 'black';
		roElement.value = formatDecimal(oValue, true, 2, false);
		return true;
	}
	/********************************************************************************/
	//WB; Formats a text input for date values and turns red when it is an invalid date.
	function checkDate(roElement)
	{	
		var dtToday = new Date();
		var iCentury = 0;
		var iPos = 0;
		var sChar = '';
		var sLastChar = '0';
		var sParts = '';
		var sText = roElement.value;
		
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(roElement.value)) {
			if (roElement.style) roElement.style.color = 'black';
			return true;
		}
		
		sText = sText.replace(/-/g,'/');
		sText = sText.replace(/\./g,'/');
		sText = sText.replace(/\ /g,'/');
		sText = sText.replace(/\\/g,'/');
		sParts = sText.split("/");
		
		switch (sParts.length) {
			case 2:   //This occurs if the user typed the month and day only.
				sParts[2] = dtToday.getFullYear().toString();
				break;
			case 1:   //This occurs if the user typed nothing but numbers.
				if (isNaN(sText)) { 
					if (roElement.style) roElement.style.color = 'red';
					return false;
				}
				sParts[0] = '';
				sParts[1] = '';
				sParts[2] = '';
				
				for (var iCtr = 0; iCtr < sText.length; iCtr++){
					sChar = sText.substr(iCtr, 1);
					
					if (isNumeric(sChar)) {
						switch (iPos) {
							case 0:   //Month
								if (sParts[0].length == 0 || (parseInt(sLastChar) < 2 && parseInt(sChar) < 3))
									sParts[0] += sChar;
								else if (sParts[0].length == 1 && parseInt(sLastChar) < 1) {
									sParts[0] += sChar;
									iPos = 1;
								}
								else {
									sParts[1] += sChar;
									iPos = 1;
								}
								if (sParts[0].length == 2) iPos = 1;
								break;
							case 1:   //Day
								if (sParts[1].length == 0 || (parseInt(sLastChar) < 4 && (parseInt(sChar) < 2 || parseInt(sLastChar) < 3)))
									sParts[1] += sChar;
								else {
									sParts[2] += sChar;
									iPos = 2;
								}
								if (sParts[1].length == 2) iPos = 2;
								break;
							case 2:   //Year
								sParts[2] += sChar;
								break;
						}
						sLastChar = sChar;
					}
					else if (sChar != '/') break;
				}
				if (sParts[2].length == 0) sParts[2] = dtToday.getFullYear().toString();
				if (sParts[1].length == 0) sParts[1] = '1';
				break;
		}
		if (sParts.length == 3) {
			//Make sure we have a four digit year.
			if (sParts[2].length < 4) {
				iCentury = Math.floor(dtToday.getFullYear()/100) * 100;
				if(parseInt(sParts[2]) > 30) iCentury -= 100;
				sParts[2] = (iCentury + parseInt(sParts[2])).toString();
			}
			
			//Create a date value from the separated parts.
			if (sParts[0].length < 2) sParts[0] = "0" + sParts[0];
			if (sParts[1].length < 2) sParts[1] = "0" + sParts[1];
			sText = sParts[0] + "/" + sParts[1] + "/" + sParts[2];
		}
		
		//Now see if this is a valid date.
		if (isDate(sText)) { 
			roElement.value = sText;
			if (roElement.style) roElement.style.color = 'black';
			return true;
		}
		else {
			if (roElement.style) roElement.style.color = 'red';
			return false;
		}
	}
	/********************************************************************************/
	//WB; Formats a text input for floating point values and turns red when it is an invalid value.
	function checkFloat(roElement, riDecimals)
	{	
		var oNumericType = new numericType('float');
		var oValue = roElement.value;
		var oValue = oValue.replace('$', '');
		var oValue = oValue.replace(',', '');
		
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(roElement.value)) {
			if (roElement.style) roElement.style.color = 'black';
			return true;
		}
		//Make sure there is a leading zero for decimal numbers.
		if (0 == oValue.indexOf('.')) oValue = '0' + oValue;
		
		if (!isValidFormat(false, oNumericType.Mask, oValue)){
			delete oNumericType;
			if (roElement.style) roElement.style.color = 'red';
			return false;
		}
		//Use the current number of decimals when not provided.
		if (null == riDecimals || 0 == riDecimals) {
			iPos = oValue.indexOf('.');
			if (iPos > 0) riDecimals = oValue.length - iPos - 1;
		}
		delete oNumericType;
		
		if (roElement.style) roElement.style.color = 'black';
		roElement.value = formatDecimal(oValue, true, riDecimals, true);
		return true;
	}
	/********************************************************************************/
	//WB; Formats a text input for integer values and turns red when it is an invalid value.
	function checkInt(roElement)
	{	
		var oNumericType = new numericType('int');
		var oValue = roElement.value;
		var oValue = oValue.replace('.', '');
		var oValue = oValue.replace(',', '');
		
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(roElement.value)) {
			if (roElement.style) roElement.style.color = 'black';
			return true;
		}
		if (!isValidFormat(false, oNumericType.Mask, oValue)){
			delete oNumericType;
			if (roElement.style) roElement.style.color = 'red';
			return false;
		}
		delete oNumericType;
		
		if (roElement.style) roElement.style.color = 'black';
		oValue = formatDecimal(oValue,true,0,false);
		roElement.value = oValue.replace('.', '');
		return true;
	}
	/********************************************************************************/
	//WB; Formats a text input for phone numbers and turns red when it is an invalid value.
	function checkPhone(roElement)
	{	
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(roElement.value)) {
			if (roElement.style) roElement.style.color = 'black';
			return true;
		}
		if (!isPhoneNumber(roElement.value)){
			
			var oValue = stripNumber(roElement.value, true);
			var sNewVal = '';
			
			//If the user entered all numbers, the length should be 10.
			if (oValue.length == 10) {
				sNewVal = oValue.substr(0, 3) + '-' + oValue.substr(3, 3) + '-' + oValue.substr(6, 4);
			}
			//This indicates a phone extension is included.
			else if (oValue.length > 10) {
				sNewVal = oValue.substr(0, 3) + '-' + oValue.substr(3, 3) + '-' + oValue.substr(6, 4) + ' x' + oValue.substr(10, oValue.length - 9);
			}
			if (isPhoneNumber(sNewVal.value) && sNewVal.length > 0)
				roElement.value = sNewVal;
			else {
				if (roElement.style) roElement.style.color = 'red';
				return false;
			}
		}
		if (roElement.style) roElement.style.color = 'black';
		return true;
	}
	/********************************************************************************/
	//Determines if a textarea has reached its maximum length.
	function checkTextLength(roElement, riMaxLen)
	{	
		var iLen = roElement.value.length;
		
		if (iLen > riMaxLen){
			window.status = "This field has been truncated from the length entered (" + iLen.toString() + ") to a maximum length of " + riMaxLen.toString() + ".";
			roElement.value = roElement.value.substr(0, riMaxLen);
			event.returnValue = false;
			return false;
		}
		else{
			window.status = '';
			return true;
		}
	}
	/********************************************************************************/
	//WB; Formats a text input for time values and turns red when it is an invalid time.
	function checkTime(roElement) 
	{	
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(roElement.value)) {
			if (roElement.style) roElement.style.color = 'black';
			return true;
		}
		if (!isTime(roElement.value)){
			
			var iDayLen = 0;
			var iMinLen = 0;
			var oValue = roElement.value;
			var sChar = '';
			var sLastChar = '';
			var sNewVal = '';
			
			for(var iCtr = 0; iCtr < oValue.length; iCtr++) {
				sChar = oValue.substr(iCtr, 1);
				
				if (isNumeric(sChar)) {
					switch (iCtr) {
						case 1:
							if (sLastChar > '1') {
								sNewVal += ':' + sChar;
								iMinLen = 2;
							}
							else if (sChar > '2') {
								sNewVal += ':' + sChar;
								iMinLen = 2;
							}
							else sNewVal += sChar;
							break;
						case 2:
							if (sLastChar > '2' && iMinLen == 0) {
								sNewVal += ':' + sChar;
								iMinLen = 2;
							}
							else { 
								if (iMinLen == 0) {
									sNewVal += ':'
									iMinLen++;
								}
								sNewVal += sChar;
								iMinLen++;
							}
							break;
						case 3:
						case 4:
						case 5:
							if (iMinLen == 3) break;
							iMinLen++;
						case 0:
							sNewVal += sChar;
							break;
						default:
					}
				}
				else {
					switch (sChar) {
						case 'A':
						case 'a':
						case 'P':
						case 'p':
							if (iMinLen == 0) {
								sNewVal += ':00';
								iMinLen = 3;
							}
							else if (iMinLen == 1) {
								sNewVal += '00';
								iMinLen = 3;
							}
							else if (iMinLen == 2) {
								sNewVal += '0';
								iMinLen = 3;
							}
							
							if(iDayLen == 0) sNewVal += ' ';
							sNewVal += sChar.toUpperCase() + 'M';
							iDayLen++;
							break;
						case ' ':
						case ':':
						case '.':
						case '-':
							if (iMinLen == 0) {
								sNewVal += ':';
								iMinLen++;
							}
							break;
						default:
					}
				}
				sLastChar = sChar;
			}
			if (iMinLen == 0) sNewVal += ':00';
			else if (iMinLen == 1) sNewVal += '00';
			else if (iMinLen == 2) sNewVal += '0';
			if (iDayLen == 0) sNewVal += ' AM';
			
			if (isTime(sNewVal) && sNewVal.length > 0)
				roElement.value = sNewVal;
			else {
				if (roElement.style) roElement.style.color = 'red';
				return false;
			}
		}
		if (roElement.style) roElement.style.color = 'black';
		return true;
	}
	/********************************************************************************/
	//WB; Formats a text input for phone numbers and turns red when it is an invalid value.
	function checkZipCode(roElement)
	{	
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(roElement.value)) {
			if (roElement.style) roElement.style.color = 'black';
			return true;
		}
		if (!isZipCode(roElement.value)){
			
			var oValue = stripNumber(roElement.value, true);
			var sNewVal = '';
			
			//If the user entered all numbers, the length should be 9.
			if (oValue.length == 9) {
				sNewVal = oValue.substr(0, 5) + '-' + oValue.substr(6, 4);
			}
			if (isZipCode(sNewVal.value) && sNewVal.length > 0)
				roElement.value = sNewVal;
			else {
				if (roElement.style) roElement.style.color = 'red';
				return false;
			}
		}
		if (roElement.style) roElement.style.color = 'black';
		return true;
	}
	/********************************************************************************/
	//WB; Compare the two dates provided. Returns 0 if 1 < 2, 1 if 1 = 2, and 2 if 1 > 2.
	function dateCompare(rsDate1, rsDate2)
	{
		var bWhite1 = isWhitespace(rsDate1);
		var bWhite2 = isWhitespace(rsDate2);
		var iDay1=0;
		var iMonth1=0;
		var iYear1=0;
		var iDay2=0;
		var iMonth2=0;
		var iYear2=0;
		var sParts = '';
		
		if (!bWhite1 && bWhite2)
			return 0;
		else if (bWhite1 && !bWhite2)
			return 2;
		else if (bWhite1 && bWhite2)
			return 1;
		else if (isDate(rsDate1) && isDate(rsDate2)) { 
			sParts = rsDate1.split("/");
			iMonth1=parseInt(sParts[0].replace(/^0/g,''));
			iDay1=parseInt(sParts[1].replace(/^0/g,''));		
			iYear1=parseInt(sParts[2]);
			
			sParts = rsDate2.split("/");
			iMonth2=parseInt(sParts[0].replace(/^0/g,''));
			iDay2=parseInt(sParts[1].replace(/^0/g,''));		
			iYear2=parseInt(sParts[2]);
			
			if (iYear1 < iYear2)
				return 0;
			else if (iYear1 == iYear2) {
				
				if (iMonth1 < iMonth2)
					return 0;
				else if (iMonth1 == iMonth2) {
					
					if (iDay1 < iDay2)
						return 0;
					else if (iDay1 == iDay2)
						return 1;
					else return 2;
				}
				else return 2;
			}
			else return 2;
		}
		if (rsDate1.value == rsDate2.value) return 1;
		else return 0;
	}
	/********************************************************************************/
	//WB; Launch a new window for the specified download file. Accomodates IE and Netscape.
	function downloadFile(rsFileName)
	{
		var oWindow;
				
		if (navigator.appName.substring(0,8) == "Netscape"){
			window.location = rsFileName;
		}
		else {
			oWindow = window.open(rsFileName, 'FileWindow', '');
			oWindow.focus();
		}
		return false;
	}
	/********************************************************************************/
	//WB; Forces a numeric currency value to be put in the value of this element when empty.
	//Used for textboxes that require a value of some sort, like a textbox array.
	function forceCurrency(roElement)
	{	
		if (!isNumeric(roElement.value)) 
		{
			roElement.value = "0.00";
			return false;
		}
		return checkCurrency(roElement);
	}
	/********************************************************************************/
	//WB; Forces a numeric integer value to be put in the value of this element when empty.
	//Used for textboxes that require a value of some sort, like a textbox array.
	function forceFloat(roElement, riDecimals)
	{	
		if (!isNumeric(roElement.value)) 
		{
			roElement.value = "0";
			return false;
		}
		return checkFloat(roElement, riDecimals);
	}
	/********************************************************************************/
	//WB; Forces a numeric integer value to be put in the value of this element when empty.
	//Used for textboxes that require a value of some sort, like a textbox array.
	function forceInt(roElement)
	{	
		if (!isNumeric(roElement.value)) 
		{
			roElement.value = "0";
			return false;
		}
		return checkInt(roElement);
	}
	/********************************************************************************/
	//Returns a formatted money string with the specified parameters.
	function formatDecimal(rsValue, rbAddZero, riDecimals, rbAddCommas) 
	{
		var iDecimalPlaces = (riDecimals == null) ? 2 : riDecimals;
		var bCommas = (rbAddCommas == null) ? false : true;
		var iNumber = 1;
		rsValue = rsValue.replace(/,/g,"")
		iNumber = Math.pow(10, iDecimalPlaces);
		rsValue = Math.round(parseFloat(rsValue) * iNumber) / iNumber;
		
		// If you're using IE3.x, you will get error with the following line.
		// rsValue = rsValue.toString();
		// It works fine in IE4.
		rsValue = "" + rsValue;
		
		if (rsValue.indexOf(".") == 0)
			rsValue = "0" + rsValue;

		if (rbAddZero == true) {
			if (rsValue.indexOf(".") == -1)
				rsValue = rsValue + ".";

			while ((rsValue.indexOf(".") + 1) > (rsValue.length - iDecimalPlaces))
				rsValue = rsValue + "0";
		}
		if( rbAddCommas == true ){
			var arglen = rsValue.length
			var numOfCommas
			//determine the number of digits to the left of the decimal
			if(rsValue.indexOf(".") != -1)
				arglen -= (arglen - rsValue.indexOf(".") )

			//put a comma in after every three digits
			numOfCommas = Math.floor(arglen / 3)
			if( arglen % 3 == 0 )
				numOfCommas -= 1

			if( numOfCommas != 0 ){
				var tempStr = new String()
				//grab the first low order group of three digits, the decimal, and the digits to the
				//right of the decimal (if present)
				var right = 3 + (rbAddZero == true ? 1 : 0 ) + riDecimals
				tempStr = rsValue.substr(rsValue.length - right, right)
				rsValue = rsValue.substr(0,rsValue.length - right)
				while(numOfCommas > 0){
					if(rsValue.length > 3){
						tempStr = rsValue.substr(rsValue.length - 3, 3) + "," + tempStr
						rsValue = rsValue.substr(0,rsValue.length - 3)
					}else{
						tempStr = rsValue + "," + tempStr
					}
					numOfCommas -= 1
				}
				rsValue = tempStr
			}
		}
		if (0 == riDecimals && rsValue.indexOf('.', 0) > -1) rsValue = rsValue.substr(0, rsValue.indexOf('.', 0));
		return rsValue;
	}
	/********************************************************************************/
	//WB; Returns a descritpion of the specified credit card type code.
	function getCreditCardType(riType)
	{
		var sReturn;
		
		switch(riType) {
			case '1':
				sReturn = 'Visa';
				break;
			case '2':
				sReturn = 'Mastercard';
				break;
			case '3':
				sReturn = 'Discover';
				break;
			case '4':
				sReturn = 'American Express';
				break;
			case '5':
				sReturn = 'Diners';
				break;
			default:
				sReturn = 'Unknown';
				break;
		}
		return sReturn;
	}
	/********************************************************************************/
	//WB; Returns true if the string represents a valid credit card expiration date in the format mm/yyyy.
	function isCCExpirationDate(rsText)
	{   
		var iCurMonth;
		var iMonth=0;
		var iYear=0;
		var sParts;
		var sToday = new Date()
		var iCurYear = sToday.getFullYear()
		var iCurMonth = sToday.getMonth() + 1
		
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(rsText)) return true;
		
		//First see if it matches the date pattern m/yyyy or mm/yyyy.
		if (!isValidFormat(false, '#0/0000', rsText)) return false;
		
		//If it's in the correct format, parse it into components and validate each one.
		sParts=rsText.split("/");
		iMonth=parseInt(sParts[0].replace(/^0/g,''));
		iYear=parseInt(sParts[1]);
		
		//Now validate the ranges.
		if (!isInRange(iYear, iCurYear, (iCurYear + 10))) return false;
		if (!isInRange(iMonth, 1, 12)) return false;
		if(((iYear == iCurYear) && (iMonth<iCurMonth)) || iYear < iCurYear){
			return false
		}
		//If it makes it to here, then all is well.
		return true;
	}
	/********************************************************************************/
	//WB; Returns true if the string represents a valid credit care number.
	function isCCNumber(rsText)
	{   
		var bEven = false;
		var sDigit = '';
		var iNum = 0;
		var iValid = 0;
		
		//White space does not constitute a valid credit card number.
		if (isWhitespace(rsText)) return false;
		
		//This checks the appropriate number of digits.
		if (!isValidFormat(true, '0000-0000-0000-0000', rsText) &&
			!isValidFormat(true, '0000 0000 0000 0000', rsText) &&
			!isValidFormat(true, '0000000000000000', rsText) &&
			!isValidFormat(true, '0000-000000-00000', rsText) &&
			!isValidFormat(true, '0000 000000 00000', rsText) &&
			!isValidFormat(true, '0000-0000-0000-000', rsText) &&
			!isValidFormat(true, '0000 0000 0000 000', rsText) &&
			!isValidFormat(true, '000000000000000', rsText) &&
			!isValidFormat(true, '0000-0000-0000-00', rsText) &&
			!isValidFormat(true, '0000 0000 0000 00', rsText) &&
			!isValidFormat(true, '00000000000000', rsText) &&
			!isValidFormat(true, '0000-0000-0000-0', rsText) &&
			!isValidFormat(true, '0000 0000 0000 0', rsText) &&
			!isValidFormat(true, '0000-0000-00000', rsText) &&
			!isValidFormat(true, '0000 0000 00000', rsText) &&
			!isValidFormat(true, '0000000000000', rsText)) {
				return false;
		}
		//Check for valid numbers using the LUHN algorithm.
		for (var iCtr = rsText.length-1; iCtr >= 0; iCtr--) {
			
			sDigit = rsText.charAt(iCtr);
			
			if (isDigit(sDigit)) {
				iNum = parseInt(sDigit, 10);
				if (bEven) {
					if ((iNum *= 2) > 9) {
						iNum -= 9;
					}
					iValid += iNum;
					bEven = !bEven;
				}
			}
			else if (sDigit != ' ' && sDigit != '.' && sDigit != '-') return false;
		}
		//If it makes it to here, then all is well.
		return (iValid % 10 == 0);
	}
	/********************************************************************************/
	//WB; Various fields are only validated if the country selected is the USA.
	function isCountry(rsCountryElement)
	{
		//Other countries support many different formats.
		var iIndex;
		var oCountry;
		
		if (null != rsCountryElement) 
			oCountry = document.getElementById(rsCountryElement);
		else oCountry = document.forms[0].Country;
		
		if ('undefined' != typeof(oCountry)) {
			iIndex = oCountry.selectedIndex
			
			if (iIndex > -1) {
				if (oCountry.options[iIndex].value == "31") {
					return true;
				}
			}
		}
		else return true;   //Default to USA when missing.
		return false;
	}
	/********************************************************************************/
	//WB; Only validate phone format if the country is USA.
	function isCountryPhone(roElement, rsCountryElement) 
	{
		if (isCountry(rsCountryElement)) {
			return checkPhone(roElement);
		}
		return true;
	}
	/********************************************************************************/
	//WB; Only validate selected State if the country is USA.
	function isCountryState(rsStateElement) 
	{
		var iIndex;
		var oState;
		
		if (null != rsStateElement)
			oState = document.getElementById(rsStateElement);
		else oState = document.forms[0].State;
		
		if ('undefined' != typeof(oState)) {
			iIndex = oState.selectedIndex;
			
			if (iIndex > -1) {
				if (oState.options[iIndex].value == "0") return false;
				else return true;
			}
		}
		return false;
	}
	/********************************************************************************/
	function isCountrySetState(rsStateElement, rsCountryElement)
	{
		//Disable the state if the country is not USA.
		var bDisabled = true;
		var oState;
		
		if (null != rsStateElement)
			oState = document.getElementById(rsStateElement);
		else oState = document.forms[0].State;
		
		if (isCountry(rsCountryElement)) {
			bDisabled = false;
		}
		if ('undefined' != typeof(oState)) {
			oState.disabled = bDisabled;
			if (bDisabled) oState.selectedIndex = 0;
		}
		return bDisabled;
	}
	/********************************************************************************/
	//WB; Only validate zip code format if the country is USA.
	function isCountryZip(roElement, rsCountryElement)
	{
		if (isCountry(rsCountryElement)) {
			return checkZipCode(roElement);
		}
		return true;
	}
	/********************************************************************************/
	//WB; Returns true if the string represents a valid date in the format mm/dd/yyyy.
	function isDate(rsText)
	{   
		var iDay=0;
		var iMonth=0;
		var iYear=0;
		var sParts;
		
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(rsText)) return true;
		
		//First see if it matches the date pattern m/d/yyyy or mm/dd/yyyy.
		if (!isValidFormat(false, '#0/#0/##00', rsText) &&
			!isValidFormat(false, '#0/#0/00', rsText) &&
			!isValidFormat(false, '0/0/00', rsText) &&
			!isValidFormat(false, '0/0/0000', rsText)) return false;
		
		//If it's in the correct format, parse it into components and validate each one.
		sParts=rsText.split("/");
		
		iMonth=parseInt(sParts[0].replace(/^0/g,''));
		iDay=parseInt(sParts[1].replace(/^0/g,''));		
		iYear=parseInt(sParts[2]);
		
		//Now validate the ranges.
		if (!isInRange(iYear, 1900, 2078)) return false;
		if (!isInRange(iMonth, 1, 12)) return false;
		switch (iMonth){     //Day range depends on the month.
			case 1:
			case 3:
			case 5:
			case 7:
			case 8:
			case 10:
			case 12:
				if (!isInRange(iDay, 1, 31)) return false;
				break;
			case 2:
				if (isLeapYear(iYear)) {
					if (!isInRange(iDay, 1, 29)) return false;
				}
				else if (!isInRange(iDay, 1, 28)) 
					return false;
				break;
			default:
				if (!isInRange(iDay, 1, 30)) return false;
		}
		
		//If it makes it to here, then all is well.
		return true;
	}
	/********************************************************************************/
	//WB; Determine if the supplied value is a number.
	function isDigit(rsValue)
	{
		var sDigits = "1234567890";
		return (sDigits.indexOf(rsValue) != -1);
	}
	/********************************************************************************/
	//WB; Determine if this form has been changed by the user.
	function isDirty(roForm)
	{	
		var iCtr;
		var iOpts;
		var oElement;
		var oOption;
		
		for (iCtr=0; iCtr < roForm.elements.length; iCtr++)
		{
			var oElement = roForm.elements[iCtr];
			
			switch(oElement.type.toLowerCase()){
				case 'hidden':
				case 'text':
				case 'textarea':
					//WB; Need to be able to ignore some form elements.
					if (oElement.className) {
						if ('ignore' == oElement.className.substr(0, 6).toLowerCase()) break;
						if ('barinput' == oElement.className.toLowerCase()) break;
					}
					if ('localtime' == oElement.name || 'bswtime' == oElement.name) break;
					if (oElement.disabled) break;
					if (oElement.value != oElement.defaultValue) return true;
					break;
				case 'checkbox':
				case 'radio':
					//WB; Need to be able to ignore some form elements.
					if (oElement.className) {
						if ('ignore' == oElement.className.substr(0, 6).toLowerCase()) break;
						if ('barinput' == oElement.className.toLowerCase()) break;
					}
					if (oElement.disabled) break;
					if (oElement.checked != oElement.defaultChecked) return true;
					break;
				case 'select':
				case 'select-multiple':
				case 'select-one':
					//WB; Need to be able to ignore some form elements.
					if (oElement.className) {
						if ('ignore' == oElement.className.substr(0, 6).toLowerCase()) break;
						if ('barinput' == oElement.className.toLowerCase()) break;
					}
					if (oElement.disabled) break;					
					oOption = oElement.options;
					
					//Check each option in this select element.
					for (iOpts=0; iOpts < oOption.length; iOpts++){
						if (oOption[iOpts].selected != oOption[iOpts].defaultSelected) {
							return true;
						}
					}
					break;
			}
		}
		//If it makes it to here, everything on the form is in its original state.
		return false;
	}
	/********************************************************************************/
	//WB; Checks to see if "rsText" is in a valid e-mail address format
	function isEmail(rsText) 
	{	
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(rsText)) return true;
		rsText = trim(rsText);    //Remove leading and trailing spaces.
		
        var iPosAt = rsText.indexOf('@'); 
        var iPosDot = rsText.lastIndexOf('.'); //NOTE: Last Index.
        var iPosSpace = rsText.indexOf(' '); 
        var iLen = rsText.length - 1; 
        
        if ((iPosAt < 1) || (iPosDot <= iPosAt + 1) || (iPosDot == iLen) || (iPosSpace > 0 && iPosSpace < iLen)) return false;
        return true; 
	}
	/********************************************************************************/
	//WB; Determine if this string is empty.
	function isEmpty(rsText)
	{   
		return ((rsText == null) || (rsText.length == 0));
	}
	/********************************************************************************/
	//WB; Determine if value is in acceptable range.
	function isInRange(riValue, riLow, riHigh) 
	{	
		if (riValue < riLow || riValue > riHigh) return false;
		return true;
	}
	/********************************************************************************/
	//WB; Determine if the specified year is a leap year.
	function isLeapYear(riYear)
	{	
		return (((riYear % 4 == 0) && (riYear % 100 != 0)) || (riYear % 400 == 0)) ? 1 : 0;
	}
	/********************************************************************************/
	//WB; Returns true if the string represents a valid number.
	function isNumeric(rsText) 
	{	
		var iDecCount = 0;
		var sChar = '';
		
		//In this case, white space is not considered a number.
		if (isWhitespace(rsText)) return false;
		
		for (var iCtr = 0; iCtr < rsText.length; iCtr++) {
			sChar = rsText.substr(iCtr, 1);
			
			//If it hits any non-numeric characters return false.
			switch (sChar) {
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					break;
				case '-':
					//Allow negative sign only if it's the first character.
					if (iCtr > 0) return false;
					break;
				case '.':
					//Allow decimals only if there is only one and it's not the only character.
					if (iDecCount > 0 || rsText.length == 1) return false;
					iDecCount += 1;
					break;
				default:
					return false;
			}
		}
		return true;
	}
	/********************************************************************************/
	//WB; Determine if the specified value is a valid phone number.
	function isPhoneNumber(rsText)
	{
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(rsText)) return true;
		
		//This mask is rigid, input must be exactly this format.
		if (!isValidFormat(true, '000-000-0000', rsText) &&
			!isValidFormat(true, '000-000-0000 x0000', rsText) &&
			!isValidFormat(true, '000-000-0000 x000', rsText) &&
			!isValidFormat(true, '000-000-0000 x00', rsText) &&
			!isValidFormat(true, '000-000-0000 x0', rsText) &&
			!isValidFormat(true, '(000) 000-0000 x0000', rsText) &&
			!isValidFormat(true, '(000) 000-0000 x000', rsText) &&
			!isValidFormat(true, '(000) 000-0000 x00', rsText) &&
			!isValidFormat(true, '(000) 000-0000 x0', rsText) &&
			!isValidFormat(true, '(000) 000-0000', rsText) &&
			!isValidFormat(true, '(000)000-0000 x0000', rsText) &&
			!isValidFormat(true, '(000)000-0000 x000', rsText) &&
			!isValidFormat(true, '(000)000-0000 x00', rsText) &&
			!isValidFormat(true, '(000)000-0000 x0', rsText) &&
			!isValidFormat(true, '(000)000-0000', rsText)){
				return false;
		}
		return true;
	}
	/********************************************************************************/
	//WB; Returns true if the string represents a file that is safe to upload.
	function isSafeUploadFile(rsText)
	{	
		var iPos = rsText.lastIndexOf('.');
		var sExt = null;
		
		//Ignoring periods that are beyond 6 places from the end and assuming it has no extension.
		if (iPos > -1 && rsText.length - iPos <= 6)
		{
			sExt = rsText.substr(iPos, rsText.length - iPos);
			if (sExt.toLowerCase() == '.bmp') return true;
			if (sExt.toLowerCase() == '.doc') return true;
			if (sExt.toLowerCase() == '.gif') return true;
			if (sExt.toLowerCase() == '.jpeg') return true;
			if (sExt.toLowerCase() == '.jpg') return true;
			if (sExt.toLowerCase() == '.pdf') return true;
			if (sExt.toLowerCase() == '.ppt') return true;
			if (sExt.toLowerCase() == '.rtf') return true;
			if (sExt.toLowerCase() == '.sit') return true;
			if (sExt.toLowerCase() == '.tif') return true;
			if (sExt.toLowerCase() == '.tiff') return true;
			if (sExt.toLowerCase() == '.txt') return true;
			if (sExt.toLowerCase() == '.wps') return true;
			if (sExt.toLowerCase() == '.xls') return true;
			if (sExt.toLowerCase() == '.zip') return true;
			if (sExt.toLowerCase() == '.rar') return true;
			if (sExt.toLowerCase() == '.enl') return true;
			if (sExt.toLowerCase() == '.org') return true;
			if (sExt.toLowerCase() == '.xml') return true;
			if (sExt.toLowerCase() == '.docx') return true;
			if (sExt.toLowerCase() == '.docm') return true;
			if (sExt.toLowerCase() == '.dotx') return true;
			if (sExt.toLowerCase() == '.dotm') return true;
			if (sExt.toLowerCase() == '.rels') return true;
			if (sExt.toLowerCase() == '.tbl') return true;
			if (sExt.toLowerCase() == '.tbe') return true;
			if (sExt.toLowerCase() == '.wll') return true;
			if (sExt.toLowerCase() == '.xlsx') return true;
			if (sExt.toLowerCase() == '.xlsm') return true;
			if (sExt.toLowerCase() == '.xltx') return true;
			if (sExt.toLowerCase() == '.xltm') return true;
			if (sExt.toLowerCase() == '.xlsb') return true;
			if (sExt.toLowerCase() == '.xlam') return true;
			if (sExt.toLowerCase() == '.pptx') return true;
			if (sExt.toLowerCase() == '.pptm') return true;
			if (sExt.toLowerCase() == '.potx') return true;
			if (sExt.toLowerCase() == '.potm') return true;
			if (sExt.toLowerCase() == '.ppam') return true;
			if (sExt.toLowerCase() == '.ppsx') return true;
			if (sExt.toLowerCase() == '.ppsm') return true;
			if (sExt.toLowerCase() == '.wmp') return true;
			if (sExt.toLowerCase() == '.wmv') return true;
			if (sExt.toLowerCase() == '.wmx') return true;
			if (sExt.toLowerCase() == '.wm') return true;
			if (sExt.toLowerCase() == '.qt') return true;
			if (sExt.toLowerCase() == '.wma') return true;
			if (sExt.toLowerCase() == '.wax') return true;
			if (sExt.toLowerCase() == '.wav') return true;
			if (sExt.toLowerCase() == '.m1v') return true;
			if (sExt.toLowerCase() == '.asf') return true;
			if (sExt.toLowerCase() == '.asr') return true;
			if (sExt.toLowerCase() == '.asx') return true;
			if (sExt.toLowerCase() == '.avi') return true;
			if (sExt.toLowerCase() == '.ivf') return true;
			if (sExt.toLowerCase() == '.lsf') return true;
			if (sExt.toLowerCase() == '.lsx') return true;
			if (sExt.toLowerCase() == '.mov') return true;
			if (sExt.toLowerCase() == '.movie') return true;
			if (sExt.toLowerCase() == '.mp2') return true;
			if (sExt.toLowerCase() == '.mp3') return true;
			if (sExt.toLowerCase() == '.mpa') return true;
			if (sExt.toLowerCase() == '.mpe') return true;
			if (sExt.toLowerCase() == '.mpeg') return true;
			if (sExt.toLowerCase() == '.mpg') return true;
			if (sExt.toLowerCase() == '.mpv2') return true;
			if (sExt.toLowerCase() == '.nsc') return true;
			if (sExt.toLowerCase() == '.wvx') return true;
			if (sExt.toLowerCase() == '.latex') return true;
			if (sExt.toLowerCase() == '.hwp') return true;
			return false;
		}
		else return true;   //Allow no extension
		
	}
	/********************************************************************************/
	//WB; Returns true if the string represents a valid time in the format hh:mm[AP]M
	function isTime(rsText)
	{
		var iHour=0;
		var iMinute=0;
		var sParts;
		
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(rsText)) return true;		
		rsText = rsText.toUpperCase()
		
		//First see if it matches the time pattern.
		if (!isValidFormat(false, '#0:00PM', rsText) &&
			!isValidFormat(false, '#0:00AM', rsText) &&
			!isValidFormat(false, '#0:00 AM', rsText) &&
			!isValidFormat(false, '#0:00 PM', rsText)) {
				return false;
		}
		//If it's in the correct format, parse it into components and validate each one.
		rsText = rsText.substr(0,rsText.length-2)
		sParts = rsText.split(":");	//now split the hours & minutes
		iHour = parseInt(sParts[0]);
		iMinute = parseInt(sParts[1]);
		
		//Now validate the ranges.
		if (!isInRange(iHour, 1, 12)) return false;
		if (!isInRange(iMinute, 0, 59)) return false;
		//If it makes it to here, then all is well.
		return true;
	}
    /********************************************************************************/ 
    //WB; Determine if this value contains a valid credit card number. 
	function isValidCreditCard(riType, rsCardNumber) 
	{
		var fCheckSum = 0;
		var iCtr = 0;
		var iDigit = 0;
		var oRegExp;
		
		//An empty string will cause an error in the regular expression test.
		if (isWhitespace(rsCardNumber)) return false;
		
		//Remove any dashes or spaces.
		rsCardNumber = stripNumber(rsCardNumber, true);
		
		if (riType == "1") {
			// Visa: length 16, prefix 4, dashes optional.
			oRegExp = /^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/;
		} else if (riType == "2") {
			// Mastercard: length 16, prefix 51-55, dashes optional.
			oRegExp = /^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/;
		} else if (riType == "3") {
			// Discover: length 16, prefix 6011, dashes optional.
			oRegExp = /^6011-?\d{4}-?\d{4}-?\d{4}$/;
		} else if (riType == "4") {
			// American Express: length 15, prefix 34 or 37.
			oRegExp = /^3[4,7]\d{13}$/;
		} else if (riType == "5") {
			// Diners: length 14, prefix 30, 36, or 38.
			oRegExp = /^3[0,6,8]\d{12}$/;
		}
		if (!oRegExp.test(rsCardNumber)) return false;
		
		// Perform checksum ("Mod 10")
		// Add even digits in even length strings or odd digits in odd length strings.
		for (iCtr = (2 - (rsCardNumber.length % 2)); iCtr <= rsCardNumber.length; iCtr += 2) {
			fCheckSum += parseInt(rsCardNumber.charAt(iCtr - 1));
		}
		// Analyze odd digits in even length strings or even digits in odd length strings.
		for (iCtr = (rsCardNumber.length % 2) + 1; iCtr < rsCardNumber.length; iCtr += 2) {
			iDigit = parseInt(rsCardNumber.charAt(iCtr - 1)) * 2;
			if (iDigit < 10) { fCheckSum += iDigit; } else { fCheckSum += (iDigit - 9); }
		}
		if ((fCheckSum % 10) == 0) return true; else return false;
	}
    /********************************************************************************/ 
    //WB; Determine if this field value is the proper format based on the provided mask. 
    function isValidFormat(rbCheckLength, rsMask, rsValue) 
    {
        var bDecimal = false; 
        var iPosMask; 
        var iPosVal=0; 
        var sCharMask; 
        var sCharVal; 
        
        //Numeric fields cannot be empty because it causes database errors. 
        if (isWhitespace(rsValue) && rbCheckLength) return true; 
        
        //If the length doesn't match and the caller requires it, then reject it. 
        if (rbCheckLength && rsMask.length != rsValue.length) return false; 
        iPosVal = rsValue.length - 1; 
        
        //Now check each charcter for compliance. 
        for (iPosMask = rsMask.length - 1; iPosMask >= 0; iPosMask--){ 
        
                sCharMask = rsMask.charAt(iPosMask); 
                if (iPosVal >= 0) { 
                        sCharVal = rsValue.charAt(iPosVal); 
                            
                        //If this is the floating decimal, ignore it. 
                        if (bDecimal && '.' == sCharVal && '.' != sCharMask) { 
								if (iPosVal >= 1) iPosVal--;
                                sCharVal = rsValue.charAt(iPosVal); 
                                    
                                //Since this is the decimal, must start the mask over. 
                                iPosMask = rsMask.indexOf('!') - 1; 
                                sCharMask = rsMask.charAt(iPosMask); 
                                    
                                bDecimal = false; //Only one allowed. 
                        } 
                        switch (sCharMask){ 
                                case '!':        //Indicates a floating decimal. Valid on end only. 
                                        bDecimal = true; 
                                        break; 
                                case '?':        //Indicates an optional character. 
                                        //The next character is the one thats optional. 
                                        sCharMask = rsMask.charAt(--iPosMask); 
                                        if (sCharMask == sCharVal) iPosVal--;
                                        break; 
                                case '#':       //Indicates an optional numeric charcter. 
                                        if (sCharVal >= '0' && sCharVal <= '9') iPosVal--; 
                                        break; 
                                case '0':       //Indicates a required numeric charcter. 
                                        if (sCharVal < '0' || sCharVal > '9') return false; 
                                        iPosVal--; 
                                        break; 
                                case '.':       //Above ! indicates a decimal is allowed anywhere. 
                                        if (bDecimal) { 
                                                 iPosVal--; 
                                                 bDecimal = false; //Only one allowed. 
                                                 break;            //Don't break if floating decimal is not allowed.

                                        } 
                                default:        //All other characters must match exactly. 
                                        if (sCharMask != sCharVal) return false; 
                                        iPosVal--; 
                                        break; 
                        } 
                } 
                else {  //The mask is longer than the value. See if the remainder is optional. 
                        switch (sCharMask) { 
                                case '?':    //This indicates an optional character, decrement to ignore it. 
                                        iPosMask-- 
                                case '#':    //This one is optional, ignore it. 
                                        break; 
                                default:     //All others are considered required. 
                                        return false; 
                        } 
                } 
        } 
        //This indicates it got stuck on a character in a mask of all optional numbers. 
        if (iPosVal >= 0) return false; 
        
        //If it makes it to here, then all is well. 
        return true; 
    }
	/********************************************************************************/
	//WB; Returns true if the string is empty or whitespace characters only.
	function isWhitespace(rsText)
	{   
		var iCtr;
		var sChar;
		var sWhitespace = " \t\n\r";
		
	    //Is rsText empty?
	    if (isEmpty(rsText)) return true;
		
	    //Search through string's characters one by one until we find a non-whitespace 
	    //character. When we do, return false; if we don't, return true.
	    for (iCtr = 0; iCtr < rsText.length; iCtr++)
	    {   
			//Check that current character isn't whitespace.
			sChar = rsText.charAt(iCtr);

			if (sWhitespace.indexOf(sChar) == -1) return false;
	    }
		
	    //All characters are whitespace.
	    return true;
	}
	/********************************************************************************/
	//WB; Determine if this field value is the proper format for a zip code.
	function isZipCode(rsText)
	{
		//Assumes that required fields are checked prior to this call. Therefore
		//empty fields are considered valid in here.
		if (isWhitespace(rsText)) return true;
		
		if (!isValidFormat(true, '00000', rsText) &&
			!isValidFormat(true, '00000-0000', rsText)){
				return false;
		}
		//If it makes it to here, then all is well.
		return true;
	}
	/********************************************************************************/
	//WB; Determine if this class is a numeric type and set up the mask, and range values if it is.
	function numericType(rsClass)
	{	
		var bReturn=true;
		this.Type=rsClass.toLowerCase();
		
		//To validate a number with a more limited range of values, it must have its own class name
		//and must be added to this switch block.
		switch (this.Type) {
			case 'blankfloat':
			case 'requiredfloat':
			case 'float':       //float (- 1.79E + 308 through 1.79E + 308)
			case 'requiredreal':
			case 'real':        //real (–3.40E + 38 through 3.40E + 38)
				this.Mask = '-?#,?###,?###,?###,?###,?###,?###!';
				this.Low = -9223372036854775808;
				this.High = 9223372036854775807;
				break;
			case 'blankbigint':
			case 'requiredbigint':
			case 'bigint':      //bigint (-9,223,372,036,854,775,808 through 9,223,372,036,854,775,807)
				this.Mask = '-?#,?###,?###,?###,?###,?###,?###';
				this.Low = -9223372036854775808;
				this.High = 9223372036854775807;
				break;
			case 'blankint':
			case 'requiredint':
			case 'requiredintlist':
			case 'requiredintsel':
			case 'int':         //int (-2,147,483,648 through 2,147,483,647)
				this.Mask = '-?#,?###,?###,?##0';
				this.Low = -2147483648;
				this.High = 2147483647;
				break;
			case 'blanksmallint':
			case 'requiredsmallint':
			case 'smallint':    //smallint (-32,768 through 32,767)
				this.Mask = '-?##,?##0';
				this.Low = -32768;
				this.High = 32767;
				break;
			case 'blanktinyint':
			case 'requiredtinyint':
			case 'tinyint':    //tinyint (0 through 255)
				this.Mask = '##0';
				this.Low = 0;
				this.High = 255;
				break;
			case 'blankcurrency':
			case 'blankmoney':
			case 'requiredcurrency':
			case 'currency':
			case 'requiredmoney':
			case 'money':       //money (-922,337,203,685,477.5808 through 922,337,203,685,477.5807)
				this.Mask = '-?$?###,?###,?###,?###,?###.?##';
				this.Low = -922337203685477.5808;
				this.High = 922337203685477.5807;
				break;
			default:            //This is NOT a numeric data type.
				this.Mask = '';
				this.Low = 0;
				this.High = 0;
				bReturn = false;
		}
		
		//Clean up the mask used for format validating for display to the user.
		if (bReturn) {
			this.DisplayMask = this.Mask;
			while (this.DisplayMask.indexOf('?') > 0){
				this.DisplayMask = this.DisplayMask.replace('?', '');
			}
			while (this.DisplayMask.indexOf('0') > 0){
				this.DisplayMask = this.DisplayMask.replace('0', '#');
			}
			this.DisplayMask = this.DisplayMask.replace('!', '');
			this.DisplayMask = this.DisplayMask.replace('-', '');
		}
		
		//Return true if this is a numeric data type.
		this.IsNumeric = bReturn;
		return bReturn;
	}
	/********************************************************************************/
	//WB; Set the disabled status of a radio button group.
	function radioStatusSet(roRadio, rbDisable)
	{
		if ('undefined' != roRadio && null != roRadio) {
			for (var iCtr = 0; iCtr < roRadio.length; iCtr++) {
				if (rbDisable) {
					roRadio[iCtr].checked = false;
					roRadio[iCtr].disabled = true;
				}
				else roRadio[iCtr].disabled = false;
			}
		}
	}
	/********************************************************************************/
	//WB; Get the value of the selected item in the list.
	function radioValueGet(roRadio)
	{
		if ('undefined' != roRadio && null != roRadio) {
			for (var iCtr = 0; iCtr < roRadio.length; iCtr++) {
				if (roRadio[iCtr].checked) return roRadio[iCtr].value;
			}
		}
		return "";
	}
	/********************************************************************************/
	//WB; Select a radio button in a set based on the value.
	function radioValueSet(roRadio, rvValue)
	{
		if ('undefined' != roRadio && null != roRadio) {
			for (var iCtr = 0; iCtr < roRadio.length; iCtr++) {
				if (roRadio[iCtr].value == rvValue) roRadio[iCtr].checked = true;
				else roRadio[iCtr].checked = false;
			}
		}
	}
	/********************************************************************************/
	//WB; Remove any spaces found in the specified string.
	function removeSpaces(rsValue)
	{
		var iCtr = 0;
		var sReturn = '';
		var sArray;
		
		rsValue = '' + rsValue;
		sArray = rsValue.split(' ');
		
		for(iCtr = 0; iCtr < sArray.length; iCtr++) {
			sReturn += sArray[iCtr];
		}
		return sReturn;
	}
	/********************************************************************************/
	//WB; If the specified select exists, return it to its original position.
	function resetSelect(roElement)
	{
		try {
			if ('undefined' != roElement && null != roElement) {
				for (var iCtr = 0; iCtr < roElement.length; iCtr++)
				{
					if (roElement.options[iCtr].defaultSelected) {
						roElement.selectedIndex = iCtr;
						break;
					}
				}
			}
		}
		catch (ex) { alert('Error in resetSelect: ' + ex); }
		return;
	}
	/********************************************************************************/
	//WB; Return the number of items selected in a multi-select listbox.
	function selectCountGet(roElement)
	{
		var iCtr = 0;
		var iCount = 0;
		
		for (iCtr = 0; iCtr < roElement.options.length; iCtr++) {
			if (roElement.options[iCtr].selected) {
				iCount++;
			}
		}
		return iCount;
	}
	/********************************************************************************/
	//WB; Return the value of the selected list item.
	function selectValueGet(roElement)
	{	
		if (null != roElement) {
			if (roElement.selectedIndex > -1) {
				return roElement.options[roElement.selectedIndex].value;
			}
		}
		return 0;
	}
	/********************************************************************************/
	//WB; Display a user message. Just keeps all messages in one place.
	function showMsg(rsMsgType, roElement)
	{	
		var iCtr;
		var sField="";
		var sNumMsg = "Invalid value! This field requires input in the format ";
		var sRngMsg = "Invalid value! This field must be within a ";
		var sReqMsg = " field cannot be empty. Please fill out all fields designated as required.";
		var sReqDefMsg = "Missing required field. Please fill out all fields designated as required.";
	    var bIE4 = (document.all)? true: false;
		
		//Determine which message to deliver based on the message type.
		switch(rsMsgType.toLowerCase()){
			case 'required':
				//Search the HTML for an associated label so the user can be
				//notified as to the specific field that failed.
				if (bIE4) {
					//The document.all object is not available in other browsers.
					for (iCtr = roElement.sourceIndex; iCtr > 0; iCtr--){
						if(document.all(iCtr).className.toLowerCase() == "required"){
							sField = document.all(iCtr).innerText;
							break;
						}
					}
				}
				if (sField.length > 0)
					alert(sField + sReqMsg);
				else 
					alert(sReqDefMsg);
				
				roElement.focus();
				break;
			//At this time all other types are assumed to be a numeric mask.
			default:
				if (rsMsgType.substring(0, 5) == 'range'){
					alert(sRngMsg + rsMsgType + ".");
				}
				else alert(sNumMsg + rsMsgType + ".");
				
				roElement.focus();
				roElement.select();
				break;
		}
		return true;
	}
	/********************************************************************************/
	//WB; Removes all formating characters from a numeric string value.
	function stripNumber(rsNumber, rbRemDecimal) 
	{	
		var oValue = rsNumber;
		
		//Decimal may not always be removed.
		if (rbRemDecimal) {
			do {
				oValue = oValue.replace(/\./g, '');		
			}while (oValue.indexOf(".") > -1);
		}
		//Always remove everything else.
		do {
			oValue = oValue.replace(/-/g, '');		
		}while (oValue.indexOf("-") > -1);
		do {
			oValue = oValue.replace(/\(/g, '');		
		}while (oValue.indexOf("(") > -1);
		do {
			oValue = oValue.replace(/\)/g, '');		
		}while (oValue.indexOf(")") > -1);
		do {
			oValue = oValue.replace(/\ /g, '');		
		}while (oValue.indexOf(" ") > -1);
		do {
			oValue = oValue.replace('x', '');		
		}while (oValue.indexOf("x") > -1);
		do {
			oValue = oValue.replace('X', '');		
		}while (oValue.indexOf("X") > -1);
		do {
			oValue = oValue.replace(',', '');		
		}while (oValue.indexOf(",") > -1);
		
		return oValue;
	}
	/********************************************************************************/
	function trim(rsString)
	{
		var oReturn = rsString;
		
		while (' ' == oReturn.substring(0, 1))
		{	
			oReturn = oReturn.substring(1, oReturn.length);
		}
		while (' ' == oReturn.substring(oReturn.length - 1, oReturn.length))
		{	
			oReturn = oReturn.substring(0, oReturn.length - 1);
		}
		return oReturn;
	}
	/********************************************************************************/
	//WB; Cycle through all form items and make sure they contain input when required,
	//and that they contain the correct data type. Uses the CLASS to identify both.
	function validateForm(roForm)
	{
		if(roForm != null)
		{	
			var bReturn=false;
			var oElement;
			var oNumber;
			var sClass='';
			var sName='';
			
			//First check all input tags for required values and data type.
			if(document.forms[0].elements.length > 0){
				for(var iCtr = 0; iCtr < document.forms[0].elements.length; iCtr++)
				{
					oElement = document.forms[0].elements[iCtr];
					
					//All required inputs should have a class prefixed with 'required', I.E.
					//or a name prefixed with 'required', all others.
					if (oElement.className) sClass = oElement.className.toLowerCase();
					else sClass = oElement.name.toLowerCase();
					
					//Check each input element to see if it's required.
					if(sClass.substring(0, 8) == 'required'){
						switch(oElement.type.toLowerCase()){
							case 'radio':
								sName = oElement.name;
								bReturn = false;
								
								//Need to find the end of this group even if one is checked.
								while (oElement.name == sName){
									if (oElement.checked) bReturn = true;
									oElement = eval("document.forms[0].elements[++iCtr];");
								}
								//Backup one so the no elements get skipped.
								oElement = eval("document.forms[0].elements[--iCtr];");							
								if (!bReturn){
									showMsg('REQUIRED', oElement);
									window.onbeforeunload = 'verifySave';
									return false;
								}
								break;
							case 'select':
							case 'select-multiple':
							case 'select-one':
								//This class prevents the zero(th) element from being selected.
								if(sClass.substring(0, 14) == 'requiredNoZero'){
									if (oElement.selectedIndex == 0){
										showMsg('REQUIRED', oElement);
										window.onbeforeunload = 'verifySave';
										return false;
									}
								}
								else {
									if (oElement.selectedIndex == -1){
										showMsg('REQUIRED', oElement);
										window.onbeforeunload = 'verifySave';
										return false;
									}
								}
								break;
							default:
								if (isWhitespace(oElement.value)){
									showMsg('REQUIRED', oElement);
									window.onbeforeunload = 'verifySave';
									return false;
								}
								break;
						}
					}
					
					//Next check each input for valid data types and ranges.
					switch (sClass){
						case 'requiredcreditcard':
						case 'creditcard':
							//This mask is rigid, input must be exactly this format.
							if (!isCCNumber(oElement.value)){
								showMsg('#### #### #### #### or #### ###### #####', oElement);
								window.onbeforeunload = 'verifySave';
								return false;
							}
							break;
						case 'requiredemail':
						case 'email':
							//Before validating an email, remove any spaces.
							oElement.value = removeSpaces(oElement.value);
							
							if (!isEmail(oElement.value)){
								showMsg('name@domain.ext', oElement);
								window.onbeforeunload = 'verifySave';
								return false;
							}
							break;
						case 'requireddate':
						case 'date':
							//Before validating a date, remove any spaces.
							oElement.value = removeSpaces(oElement.value);
							
							if (!isDate(oElement.value)){
								showMsg('mm/dd/yyyy', oElement);
								window.onbeforeunload = 'verifySave';
								return false;
							}
							break;
						case 'requiredccexpdate':
						case 'ccexpdate':
							//Before validating a credit card, remove any spaces.
							oElement.value = removeSpaces(oElement.value);
							
							if (!isCCExpirationDate(oElement.value)){
								showMsg('mm/yyyy\nand cannot be a date in the past.', oElement);
								window.onbeforeunload = 'verifySave';
								return false;
							}
							break;							
						case 'requiredtime':
						case 'time':
							if (!isTime(oElement.value)){
								showMsg('hh:mm AM or hh:mm PM', oElement);
								window.onbeforeunload = 'verifySave';
								return false;
							}
							break;
						//Put special cases here. Those that are not really numeric fields but must be
						//validated against a mask. Put numeric types in isNumericDataType().
						case 'requiredphone':
						case 'phone':
							//Only validate phone if the country is the USA.
							if (isCountry()) {
								//This mask is rigid, input must be exactly this format.
								if (!isPhoneNumber(oElement.value)){
									showMsg('(###) ###-#### or ###-###-#### x###', oElement);
									window.onbeforeunload = 'verifySave';
									return false;
								}
							}
							break;
						case 'requiredzip':
						case 'zip':
							//Only validate zip if the country is the USA.
							if (isCountry()) {
								//Before validating a zip, remove any spaces.
								oElement.value = removeSpaces(oElement.value);
								
								//This mask is rigid but, it can be in either of these two formats.
								if (!isZipCode(oElement.value)){
									showMsg('##### or #####-####', oElement);
									window.onbeforeunload = 'verifySave';
									return false;
								}
							}
							break;
						default:
							oNumber = new numericType(sClass);
							
							if (oNumber.IsNumeric){
								if(sClass.substring(0, 5) == 'blank' && isWhitespace(oElement.value)){
									//WB; Allow blanks for these class types.
								}
								else if (!isValidFormat(false, oNumber.Mask, oElement.value)){
									showMsg(oNumber.DisplayMask, oElement);
									window.onbeforeunload = 'verifySave';
									return false;
								}
								//Check the range for validity.
								else if (!isInRange(oElement.value, oNumber.Low, oNumber.High)) {
									showMsg('range of ' + oNumber.Low + ' to ' + oNumber.High, oElement);
									window.onbeforeunload = 'verifySave';
									return false;
								}
							}
							delete oNumber;
							break;
					}
				}
			}			
			//If it makes it here, all required fields must be filled in.
			return true;
		}
	}
	/********************************************************************************/
	//WB; Issue an alert indicating whether this card number is valid.
	function verifyCreditCard(riType, rsCardNumber)
	{
		var sCardType = getCreditCardType(riType);
		
		if (riType == '0') {
			alert('You must select a credit card type!');
			return;
		}
		if (!isValidCreditCard(riType, rsCardNumber)) {
			alert('Card number ' + rsCardNumber + ' is NOT a valid ' + sCardType + ' credit card number!');
		}
		else { 
			alert('Card number ' + rsCardNumber + ' IS a valid ' + sCardType + ' credit card number!'); 
		}
	}
	/********************************************************************************/
	//WB; If any forms have been changed, ask the user if they would like save.
	function verifySave()
	{	
		for (var iCtr=0; iCtr < document.forms.length; iCtr++){
			if (isDirty(document.forms[iCtr])) return "Your changes have not been saved. If you continue all changes will be lost!";
		}
	}