<!--
//	Last modified: 2010-06-08
//	Do not steal our code

//2010-06-08 by CV - added global check !fnIsNull(formThis.elements[i].name) for formThis.elements[i].name is undefined

function fnIsEqual(str1, str2, boolCaseSens) {
	//By Cuong Vu
	//July 24, 2007
	//January 16, 2008 - Added checks for fnIsNull
	//February 28, 2008 - Bug in Javascript.
	//Compare two strings with case-sensitivity option
	
	if (fnIsNull(str1) && fnIsNull(str2)) return true;
	
	var str1Temp = new String(str1);
	var str2Temp = new String(str2);
	if (boolCaseSens == false) {
		str1Temp = str1Temp.toLowerCase();
		str2Temp = str2Temp.toLowerCase();
	}
	//CV on 2/28/2008 - DO NOT REMOVE THE .toString(). FRAKKING JAVASCRIPT BUG.
	if (str1Temp.toString() == str2Temp.toString()) {
		return true;
	} else {
		return false;
	}
}

function fnIsNumeric(strText) {
	//By Cuong Vu
	//April 7, 2005
	//March 8, 2008 - Fixed the problem where "70." is passed and returns false;
	//http://www.codetoad.com/javascript/isnumeric.asp
	/*
	^ == beginning of line
	-{0,1} == 0 or 1 minus signs
	\d* == 0 or more digits
	\.{0,1} == 0 or 1 decimal points
	\d+ == 1 or more digits
	$ == end of line
	*/
	if (fnIsNull(strText)) return false;
	
	var filter = /^-{0,1}(\d+\.){0,1}\d*$/;
	if (filter.test(strText)) {
		return true;
	} else {
		return false;
	}
}

function fnIsDollar(strText) {
	//By Cuong Vu
	//March 2, 2008
	/*
	^ == beginning of line
	-{0,1} == 0 or 1 minus signs
	\d* == 0 or more digits
	\.{0,1} == 0 or 1 decimal points
	\d+ == 1 or more digits
	$ == end of line
	*/
	if (fnIsNull(strText)) return false;
	
	var filter = /^\d+(\.\d{2})?$/;
	if (filter.test(strText)) {
		return true;
	} else {
		return false;
	}
}

function fnIsUnsignedNumeric(strText) {
	//By Cuong Vu
	//February 18, 2008
	//March 8, 2008 - Fixed the problem where "70." is passed and returns false;
	//http://www.codetoad.com/javascript/isnumeric.asp
	/*
	^ == beginning of line
	\d* == 0 or more digits
	\.{0,1} == 0 or 1 decimal points
	\d+ == 1 or more digits
	$ == end of line
	*/
	if (fnIsNull(strText)) return false;
	
	var filter = /^(\d+\.){0,1}\d*$/;
	if (filter.test(strText)) {
		return true;
	} else {
		return false;
	}
}

function fnIsAllNums(strText) {
	//By Cuong Vu
	//April 7, 2005
	//This is similar to the fnIsNumeric function but checks only numbers.
	if (fnIsNull(strText)) return false;
	
	var filter = /^\d+$/;
	if (filter.test(strText)) {
		return true;
	} else {
		return false;
	}
}

function fnIsIdValid(strText) {
	//Cuong Vu
	//February 18, 2008
	//Value must be unsigned whole integer greater than zero
	if (fnIsNull(strText)) return false;
	if (!fnIsAllNums(strText)) return false;
	var numID = new Number(strText);
	if (numID > 0) return true;
	return false;
}

function fnIsInteger(strText) {
	//By Cuong Vu
	//April 7, 2005
	//This is similar to the fnIsNumeric function but checks for positive and negative integers.
	if (fnIsNull(strText)) return false;
	
	var filter = /^-{0,1}\d+$/;
	if (filter.test(strText)) {
		return true;
	} else {
		return false;
	}
}

function fnGetParentName(strCtrlName, strParentPartID) {
	//By Cuong Vu
	//January 13, 2005
	
	//This function is handy for dotNet client side.  Takes the strCtrlName, goes up three levels,
	//and appends strParentPartID (dotNet server-side ID for the control).  For example:
	
	//Parent part - repeaterFeatures:_ctl0:repeaterParts:_ctl2:cboxPart
	//Child part - repeaterFeatures:_ctl0:repeaterParts:_ctl2:repeaterSCParts:_ctl0:cboxSizeChange
	
	if (fnIsNull(strCtrlName)) return ""; //CV on 1/31/2006
	if (fnIsNull(strParentPartID)) return ""; //CV on 1/31/2006
	
	var strReturn = fnLeftBack(fnLeftBack(fnLeftBack(strCtrlName, ":", true), ":", true), ":", true);
	if (fnIsNull(strReturn)) return ""; //CV on 1/31/2006
	strReturn = strReturn + ":" + strParentPartID.toString();
	return strReturn;
}

function fnIsElementChecked(formThis, strCtrlName) {
	//By Cuong Vu
	//January 13, 2005
	//Checks the form to see if this element is checked.  The strCtrlName must match EXACTLY!
	//February 11, 2005 - Changed parameter from intFormNum to formThis.  You can only run this after the form and elements have been loaded.
	//Therefore, you can't call it in the <HEAD> section.  Pass formThis as either document.FormName or document.forms[0].
	if (fnIsNull(strCtrlName)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	//loop thru all elements and find if this element is checked
	for (i = 0; i < intLength; i++) {
		if (fnIsEqual(formThis.elements[i].name, strCtrlName, true)) {
			if (formThis.elements[i].type == "checkbox") {
				if (formThis.elements[i].checked == true) {
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		}
	}
	return false;
}

function fnIsChildChecked(formThis, strCtrlName) {
	//By Cuong Vu
	//January 13, 2005
	//Loops thru all elements that match the strCtrlName (case-sensitive) wildcard, to see if any
	//are checked.  Handy for nested dotNet elements where the parent and child elements have the
	//following names.  Returns true if ANY child is checked, returns false if ALL children are unchecked.
	
	//Parent part - repeaterFeatures:_ctl0:repeaterParts:_ctl2:cboxPart
	//Child part - repeaterFeatures:_ctl0:repeaterParts:_ctl2:repeaterSCParts:_ctl0:cboxSizeChange
	
	//So strCtrlName could be 'repeaterFeatures:_ctl0:repeaterParts:_ctl2'
	//February 11, 2005 - Changed parameter from intFormNum to formThis.  You can only run this after the form and elements have been loaded.
	//Therefore, you can't call it in the <HEAD> section.  Pass formThis as either document.FormName or document.forms[0].
	if (fnIsNull(strCtrlName)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	//dotNet appends the server-side ID as the last item
	var strParentName = fnLeftBack(strCtrlName, ":", true);
	if (strParentName == "") { return false; }
	
	//loop thru all elements and find if this element is checked
	for (i = 0; i < intLength; i++) {
		//CV on 2005/02/23 - Fixed a bug where strParentName needed the ":", else it looked for 10, 11, 12 when it is 1
		if (!fnIsNull(formThis.elements[i].name) && (formThis.elements[i].name.indexOf(strParentName + ":") > -1) && (formThis.elements[i].name != strCtrlName)) {
			//We have a match
			if (formThis.elements[i].type == "checkbox") {
				if (formThis.elements[i].checked == true) {
					return true;
				}
			}
		}
	}
	return false;
}

function fnRight(strToParse, strToFind, boolCaseSens) {
	//By Cuong Vu
	//June 30, 2004
	//This will find parse the string to the right of strToFind.  If not found, returns a null.
	//December 20, 2004 - Fixed bug where strToFind was more than one character
	//February 1, 2006 - Fixed bug where boolCaseSens wasn't even used.
	if (fnIsNull(strToParse) || fnIsNull(strToFind))
		return ""; //CV on 1/31/2006
	
	var str1 = new String();
	var str2 = new String();
	strToParse = new String(strToParse);
	strToFind = new String(strToFind);
	//CV on 2007-07-24 - Fixed bug in boolCaseSens logic
	if (boolCaseSens == false) {
		str1 = strToParse.toLowerCase();
		str2 = strToFind.toLowerCase();
	} else {
		str1 = strToParse;
		str2 = strToFind;
	}
	
	var intPos;
	intPos = str1.indexOf(str2); //CV on 2/1/2006
	
	if (intPos == -1) {
		return ""; //CV on 1/31/2006
	} else {
		return strToParse.slice(intPos + strToFind.length); //CV on 12/20/2004
	}
}

function fnLeft(strToParse, strToFind, boolCaseSens) {
	//By Cuong Vu
	//July 1, 2004
	//This will find parse the string to the left of strToFind.  If not found, returns a null.
	//February 1, 2006 - Fixed bug where boolCaseSens wasn't even used.
	if (fnIsNull(strToParse) || fnIsNull(strToFind))
		return ""; //CV on 1/31/2006
	
	var str1 = new String();
	var str2 = new String();
	strToParse = new String(strToParse);
	strToFind = new String(strToFind);
	//CV on 2007-07-24 - Fixed bug in boolCaseSens logic
	if (boolCaseSens == false) {
		str1 = strToParse.toLowerCase();
		str2 = strToFind.toLowerCase();
	} else {
		str1 = strToParse;
		str2 = strToFind;
	}
	
	var intPos;
	intPos = str1.indexOf(str2); //CV on 2/1/2006
	
	if (intPos == -1) {
		return ""; //CV on 1/31/2006
	} else {
		return strToParse.substr(0, intPos);	
	}
}

function fnRightBack(strToParse, strToFind, boolCaseSens) {
	//By Cuong Vu
	//January 13, 2005
	//This will find parse the string to the right of strToFind.  If not found, returns a null.
	//December 20, 2004 - Fixed bug where strToFind was more than one character
	//February 1, 2006 - Fixed bug where boolCaseSens wasn't even used.
	if (fnIsNull(strToParse) || fnIsNull(strToFind))
		return ""; //CV on 1/31/2006
	
	var str1 = new String();
	var str2 = new String();
	strToParse = new String(strToParse);
	strToFind = new String(strToFind);
	//CV on 2007-07-24 - Fixed bug in boolCaseSens logic
	if (boolCaseSens == false) {
		str1 = strToParse.toLowerCase();
		str2 = strToFind.toLowerCase();
	} else {
		str1 = strToParse;
		str2 = strToFind;
	}
	
	var intPos;
	intPos = str1.lastIndexOf(str2); //CV on 2/1/2006
	
	if (intPos == -1) {
		return ""; //CV on 1/31/2006
	} else {
		return strToParse.slice(intPos + strToFind.length); //CV on 12/20/2004
	}
}

function fnLeftBack(strToParse, strToFind, boolCaseSens) {
	//By Cuong Vu
	//January 13, 2005
	//This will find parse the string to the left of strToFind.  If not found, returns a null.
	//February 1, 2006 - Fixed bug where boolCaseSens wasn't even used.
	if (fnIsNull(strToParse) || fnIsNull(strToFind))
		return ""; //CV on 1/31/2006
	
	var str1 = new String();
	var str2 = new String();
	strToParse = new String(strToParse);
	strToFind = new String(strToFind);
	//CV on 2007-07-24 - Fixed bug in boolCaseSens logic
	if (boolCaseSens == false) {
		str1 = strToParse.toLowerCase();
		str2 = strToFind.toLowerCase();
	} else {
		str1 = strToParse;
		str2 = strToFind;
	}
	
	var intPos;
	intPos = str1.lastIndexOf(str2); //CV on 2/1/2006
	
	if (intPos == -1) {
		return ""; //CV on 1/31/2006
	} else {
		return strToParse.substr(0, intPos);	
	}
}

function fnTrim(str) {
  //Cuong Vu
  //April 4, 2009
  //http://blog.stevenlevithan.com/archives/faster-trim-javascript
  if (null == str || "" == str) return "";
  var strNew = new String(str);
  strNew = strNew.replace(/^\s\s*/, '');
  var ws = /\s/;
  var i = strNew.length;
  while (ws.test(strNew.charAt(--i)));
  return strNew.slice(0, i + 1);
}

function fnGetItemValue(formThis, strItemName) {
	//By Cuong Vu
	//January 28, 2005
	//March 23, 2006 - Return a semicolon delimited string when element type is select-multiple
	//May 24, 2007 - Added select-one and return the value if it exists or return the text
	//July 24, 2007 - Added checkbox and radio ability
	//February 16, 2008 - Fully tested radio ability
	//March 9, 2008 - removed boolSelectedYet
	if (fnIsNull(strItemName)) return ""; //CV on 1/31/2006
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return "";
	}
	
	var strReturn = new String();
	var i;
	var j;
	
	for (i=0; i < intLength; i++) {
		if (fnIsEqual(formThis.elements[i].name, strItemName, true)) {
			if (formThis.elements[i].type == "select-one") {
				if (fnIsNull(formThis.elements[i].options[formThis.elements[i].selectedIndex].value)) {
					return formThis.elements[i].options[formThis.elements[i].selectedIndex].text;
				} else {
					return formThis.elements[i].options[formThis.elements[i].selectedIndex].value;
				}
			} else if (formThis.elements[i].type == "select-multiple") {
				for (j=0; j < formThis.elements[i].length; j++) {
					if (formThis.elements[i].options[j].selected) {
						if (!fnIsNull(strReturn)) {
							strReturn = strReturn + ";";
						}
						//alert(formThis.elements[i].options[j].value);
						if (fnIsNull(formThis.elements[i].options[j].value)) {
							strReturn = strReturn + formThis.elements[i].options[j].text;
						} else {
							strReturn = strReturn + formThis.elements[i].options[j].value;
						}
					}
				}
			} else if (formThis.elements[i].type == "radio") {
				//CV on 2007-07-24
				if (formThis.elements[i].checked) {
					return formThis.elements[i].value;
				}
			} else if (formThis.elements[i].type == "checkbox") {
				//CV on 2008-02-29
				if (formThis.elements[i].checked) {
					if (!fnIsNull(strReturn)) {
						strReturn = strReturn + ";";
					}
					strReturn = strReturn + formThis.elements[i].value;
				}
			} else {
				return formThis.elements[i].value;
			}
		}
	}
	return strReturn;
}

function fnGetItemValue2(strItemName) {
	//By Cuong Vu
	//January 28, 2005
	//March 23, 2006 - Return a semicolon delimited string when element type is select-multiple
	//May 24, 2007 - Added select-one and return the value if it exists or return the text
	//July 24, 2007 - Added checkbox and radio ability
	//February 16, 2008 - Fully tested radio ability
	//March 9, 2008 - fnGetItemValue2 uses the document object and not the form
	if (fnIsNull(strItemName)) return ""; //CV on 1/31/2006
	
	var strReturn = new String();
	var j;
	var objItem;
	
	try {
		objItem = document.getElementById(strItemName);
	} catch(er) {
		return "";
	}
	
	if (objItem.type == "select-one") {
		if (fnIsNull(objItem.options[objItem.selectedIndex].value)) {
			return objItem.options[objItem.selectedIndex].text;
		} else {
			return objItem.options[objItem.selectedIndex].value;
		}
	} else if (objItem.type == "select-multiple") {
		for (j=0; j < objItem.length; j++) {
			if (objItem.options[j].selected) {
				if (!fnIsNull(strReturn)) {
					strReturn = strReturn + ";";
				}
				//alert(objItem.options[j].value);
				if (fnIsNull(objItem.options[j].value)) {
					strReturn = strReturn + objItem.options[j].text;
				} else {
					strReturn = strReturn + objItem.options[j].value;
				}
			}
		}
	} else if (objItem.type == "radio") {
		//CV on 2007-07-24
		if (objItem.checked) {
			return objItem.value;
		}
	} else if (objItem.type == "checkbox") {
		//CV on 2008-02-29
		if (objItem.checked) {
			if (!fnIsNull(strReturn)) {
				strReturn = strReturn + ";";
			}
			strReturn = strReturn + objItem.value;
		}
	} else {
		return objItem.value;
	}
	return strReturn;
}

function fnSetItemValue(formThis, strItemName, strItemValue) {
	//By Cuong Vu
	//January 28, 2005
	//March 23, 2006 - Allow a semicolon delimited string for strItemValue when element type is select-multiple
	//June 7, 2007 - Added select-one and set the value if it exists or set the text. Tested new changes.
	//July 24, 2007 - Added checkbox and radio ability. Pass strItemValue == "on" to check a checkbox element.
	//January 16, 2008 - If you use the checkbox and pass a value, pass that value to strItemValue. Added support to uncheck.
	//February 29, 2008 - for checkbox, pass strItemValue as a semicolon delimited string. If you use this onload,
	//	make sure you PUT IT AFTER THE FORM. E.g. fnSetItemValue(document.leagues_config, "chk_dayofweek", "3;4;5")
	//E.g. fnSetItemValue(formThis, "cblSponsors:1452", "on");
	if (fnIsNull(strItemName)) return false; //CV on 3/9/2008
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var boolChangedOne = false;
	var boolReturn = false;
	var i;
	var j;
	var k;
	
	for (i=0; i < intLength; i++) {
		if (fnIsEqual(formThis.elements[i].name, strItemName, true)) {
			try
			{
				if (formThis.elements[i].type == "select-multiple" || formThis.elements[i].type == "select-one") {
					var arrayItemValues = new Array();
					arrayItemValues = fnExplode(strItemValue, ";");
					for (k=0; k < arrayItemValues.length; k++) {
						for (j=0; j < formThis.elements[i].length; j++) {
							if (fnIsNull(formThis.elements[i].options[j].value)) {
								if (fnIsEqual(formThis.elements[i].options[j].text, arrayItemValues[k], false)) {
									formThis.elements[i].options[j].selected = true;
									boolChangedOne = true;
								}
							} else {
								if (fnIsEqual(formThis.elements[i].options[j].value, arrayItemValues[k], false)) {
									formThis.elements[i].options[j].selected = true;
									boolChangedOne = true;
								}
							}
						}
					}
					return boolChangedOne;
				} else if (formThis.elements[i].type == "radio") {
					//CV on 2007-07-24, not case-sensitive
					if (fnIsEqual(formThis.elements[i].value, strItemValue, false)) {
						formThis.elements[i].checked = true;
						return true;
					} else {
						//CV on 2008-01-16
						formThis.elements[i].checked = false;
						return true;
					}
				} else if (formThis.elements[i].type == "checkbox") {
					//CV on 2008-02-29, allow semicolon delimited string for strItemValue
					if (fnCompare(formThis.elements[i].value, fnExplode(strItemValue, ";"), true, false)) {
						if (formThis.elements[i].checked == false) boolReturn = true;
						formThis.elements[i].checked = true;
					} else {
						if (formThis.elements[i].checked == true) boolReturn = true;
						formThis.elements[i].checked = false;
					}
				} else {
					formThis.elements[i].value = strItemValue;
					return true;
				}
			} catch(er) {
				self.status = "fnSetItemValue: An error occurred on item " + strItemName;
				return false;
			}
		}
	}
	return boolReturn;
}

function fnSetItemValue2(strItemName, strItemValue) {
	//By Cuong Vu
	//January 28, 2005
	//March 23, 2006 - Allow a semicolon delimited string for strItemValue when element type is select-multiple
	//June 7, 2007 - Added select-one and set the value if it exists or set the text. Tested new changes.
	//July 24, 2007 - Added checkbox and radio ability. Pass strItemValue == "on" to check a checkbox element.
	//January 16, 2008 - If you use the checkbox and pass a value, pass that value to strItemValue. Added support to uncheck.
	//February 29, 2008 - for checkbox, pass strItemValue as a semicolon delimited string. If you use this onload,
	//	make sure you PUT IT AFTER THE FORM. E.g. fnSetItemValue(document.leagues_config, "chk_dayofweek", "3;4;5")
	//E.g. fnSetItemValue(formThis, "cblSponsors:1452", "on");
	//March 9, 2008 - fnSetItemValue2 does uses the document object
	if (fnIsNull(strItemName)) return false; //CV on 3/9/2008
	
	var boolChangedOne = false;
	var boolReturn = false;
	var j;
	var k;
	var objItem;
	
	try {
		objItem = document.getElementById(strItemName);
	} catch(er) {
		return false;
	}
	
	try
	{
		if (objItem.type == "select-multiple" || objItem.type == "select-one") {
			var arrayItemValues = new Array();
			arrayItemValues = fnExplode(strItemValue, ";");
			for (k=0; k < arrayItemValues.length; k++) {
				for (j=0; j < objItem.length; j++) {
					if (fnIsNull(objItem.options[j].value)) {
						if (fnIsEqual(objItem.options[j].text, arrayItemValues[k], false)) {
							objItem.options[j].selected = true;
							boolChangedOne = true;
						}
					} else {
						if (fnIsEqual(objItem.options[j].value, arrayItemValues[k], false)) {
							objItem.options[j].selected = true;
							boolChangedOne = true;
						}
					}
				}
			}
			return boolChangedOne;
		} else if (objItem.type == "radio") {
			//CV on 2007-07-24, not case-sensitive
			if (fnIsEqual(objItem.value, strItemValue, false)) {
				objItem.checked = true;
				return true;
			} else {
				//CV on 2008-01-16
				objItem.checked = false;
				return true;
			}
		} else if (objItem.type == "checkbox") {
			//CV on 2008-02-29, allow semicolon delimited string for strItemValue
			if (fnCompare(objItem.value, fnExplode(strItemValue, ";"), true, false)) {
				if (objItem.checked == false) boolReturn = true;
				objItem.checked = true;
			} else {
				if (objItem.checked == true) boolReturn = true;
				objItem.checked = false;
			}
		} else {
			objItem.value = strItemValue;
			return true;
		}
	} catch(er) {
		self.status = "fnSetItemValue2: An error occurred on item " + strItemName;
		return false;
	}
	return boolReturn;
}

function fnGetItemType(formThis, strItemName) {
	//By Cuong Vu
	//February 4, 2005
	if (fnIsNull(strItemName)) return "";
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return "";
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (fnIsEqual(formThis.elements[i].name, strItemName, true)) {
			return formThis.elements[i].type;
		}
	}
	return "";
}

function fnGetItemType2(strItemName) {
	//By Cuong Vu
	//February 4, 2005
	//March 18, 2008 - Uses the document object instead of the form object
	if (fnIsNull(strItemName)) return "";
	
	try {
		objItem = document.getElementById(strItemName);
		return objItem.type;
	} catch(er) {
		return "";
	}
}

function fnAnyItemEqual(formThis, strItemValue, strItemType) {
	//By Cuong Vu
	//February 4, 2005
	//January 16, 2008 - Changed comparison to case-insensitive
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (fnIsNull(strItemType) || fnIsEqual(formThis.elements[i].type, strItemType, false)) {
			if (fnIsEqual(formThis.elements[i].value, strItemValue, false)) {
				return true;
			}
		}
	}
	return false;
}

function fnAnyItemContain(formThis, strItemValue, strItemType) {
	//By Cuong Vu
	//February 4, 2005
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (fnIsNull(strItemType) || fnIsEqual(formThis.elements[i].type, strItemType, false)) {
			if (formThis.elements[i].value.indexOf(strItemValue) > -1) {
				return true;
			}
		}
	}
	return false;
}

function fnAnyItemNotEqualTo(formThis, strItemValue, strItemType) {
	//By Cuong Vu
	//February 4, 2005
	//January 16, 2008 - Changed comparison to case-insensitive
	//January 16, 2008 - Modified fnAnyItemEqual
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (fnIsNull(strItemType) || fnIsEqual(formThis.elements[i].type, strItemType, false)) {
			if (!fnIsEqual(formThis.elements[i].value, strItemValue, false)) {
				return true;
			}
		}
	}
	return false;
}

function fnAnyItemEqualByName(formThis, strItemNamePartial, strItemValue, strItemType) {
	//By Cuong Vu
	//February 4, 2005
	//January 16, 2008 - Changed comparison to case-insensitive
	//January 16, 2008 - Modified fnAnyItemEqual
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			if (fnIsNull(strItemType) || fnIsEqual(formThis.elements[i].type, strItemType, false)) {
				if (fnIsEqual(formThis.elements[i].value, strItemValue, false)) {
					return true;
				}
			}		
		}
	}
	return false;
}

function fnAnyItemContainByName(formThis, strItemNamePartial, strItemValue, strItemType) {
	//By Cuong Vu
	//February 4, 2005
	//January 16, 2008 - Changed comparison to case-insensitive
	//January 16, 2008 - Modified fnAnyItemContain
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			if (fnIsNull(strItemType) || fnIsEqual(formThis.elements[i].type, strItemType, false)) {
				if (formThis.elements[i].value.indexOf(strItemValue) > -1) {
					return true;
				}
			}		
		}
	}
	return false;
}

function fnAnyItemNotEqualToByName(formThis, strItemNamePartial, strItemValue, strItemType) {
	//By Cuong Vu
	//February 4, 2005
	//January 16, 2008 - Changed comparison to case-insensitive
	//January 16, 2008 - Modified fnAnyItemNotEqualTo
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			if (fnIsNull(strItemType) || fnIsEqual(formThis.elements[i].type, strItemType, false)) {
				if (!fnIsEqual(formThis.elements[i].value, strItemValue, false)) {
					return true;
				}
			}		
		}
	}
	return false;
}

function fnSetItemFocus(formThis, strItemName) {
	//By Cuong Vu
	//February 7, 2005
	if (fnIsNull(strItemName)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (fnIsEqual(formThis.elements[i].name, strItemName, true)) {
			formThis.elements[i].focus();
			return true;
		}
	}
	return false;
}

function fnSetItemFocus2(strItemName) {
	//By Cuong Vu
	//October 21, 2008 - fnSetItemFocus2 does uses the document object
	if (fnIsNull(strItemName)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	var objItem;
	
	try {
		objItem = document.getElementById(strItemName);
		objItem.focus();
		return true;
	} catch(er) {
		return false;
	}
}

function fnSetAnyItemFocus(formThis, strItemNamePartial) {
	//By Cuong Vu
	//February 11, 2005
	if (fnIsNull(strItemNamePartial)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			formThis.elements[i].focus();
			return true;
		}
	}
	return false;
}

function fnSetAnyItemFocusEqualTo(formThis, strItemNamePartial, strItemValue) {
	//By Cuong Vu
	//January 17, 2008
	
	if (fnIsNull(strItemNamePartial)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			if (fnIsEqual(formThis.elements[i].value, strItemValue, false)) {
				formThis.elements[i].focus();
				return true;			
			}
		}
	}
	return false;
}

function fnSetAnyItemFocusNotEqualTo(formThis, strItemNamePartial, strItemValue) {
	//By Cuong Vu
	//January 17, 2008
	
	if (fnIsNull(strItemNamePartial)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			if (!fnIsEqual(formThis.elements[i].value, strItemValue, false)) {
				formThis.elements[i].focus();
				return true;			
			}
		}
	}
	return false;
}

function fnSetItemSelect(formThis, strItemName) {
	//By Cuong Vu
	//April 22, 2007
	if (fnIsNull(strItemName)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (fnIsEqual(formThis.elements[i].name, strItemName, true)) {
			formThis.elements[i].select();
			return true;
		}
	}
	return false;
}

function fnSetItemSelect2(strItemName) {
	//By Cuong Vu
	//October 21, 2008 - fnSetItemSelect2 does uses the document object
	if (fnIsNull(strItemName)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	var objItem;
	
	try {
		objItem = document.getElementById(strItemName);
		objItem.select();
		return true;
	} catch(er) {
		return false;
	}
}

function fnSetAnyItemSelect(formThis, strItemNamePartial) {
	//By Cuong Vu
	//April 22, 2007
	if (fnIsNull(strItemNamePartial)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			formThis.elements[i].select();
			return true;
		}
	}
	return false;
}

function fnSetAnyItemSelectEqualTo(formThis, strItemNamePartial, strItemValue) {
	//By Cuong Vu
	//January 17, 2008
	
	if (fnIsNull(strItemNamePartial)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			if (fnIsEqual(formThis.elements[i].value, strItemValue, false)) {
				formThis.elements[i].select();
				return true;			
			}
		}
	}
	return false;
}

function fnSetAnyItemSelectNotEqualTo(formThis, strItemNamePartial, strItemValue) {
	//By Cuong Vu
	//January 17, 2008
	
	if (fnIsNull(strItemNamePartial)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			if (!fnIsEqual(formThis.elements[i].value, strItemValue, false)) {
				formThis.elements[i].select();
				return true;			
			}
		}
	}
	return false;
}

function fnCountChecked(formThis, strItemNamePartial) {
	//By Cuong Vu
	//February 11, 2005
	//January 16, 2008 - Added suport for type = radio
	if (fnIsNull(strItemNamePartial)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return -1;
	}
	
	var intCounter = 0;
	var i;
	
	//loop thru all elements and find if this element is checked
	for (i = 0; i < intLength; i++) {		
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			//CV on 1-16-2008
			if (formThis.elements[i].type == "checkbox" || formThis.elements[i].type == "radio") {
				if (formThis.elements[i].checked == true) {
					intCounter++;
				}
			}
		}
	}
	return intCounter;
}

function fnMaxChecked(ctrlThis, strItemNamePartial, intMaxChecked) {
	//By Cuong Vu
	//February 11, 2005
	if (fnIsNull(strItemNamePartial)) return false;
	if (fnIsAllNums(intMaxChecked) == false) { return false; }
	if (intMaxChecked < 1) { return true; } //0 is unlimited
	
	var formThis = ctrlThis.form;
	
	if (ctrlThis.type == "checkbox") {
		if (ctrlThis.checked == true) {
			//Only validate when trying to check the box
			if (fnCountChecked(formThis, strItemNamePartial) > intMaxChecked) {
				window.alert('You can only check a maximum of ' + intMaxChecked + ' boxes.  Please deselect one in order to check this one.');
				return false;
			}
		}
	} else {
		//We have another control that's adding to the total that isn't a checkbox.  For example, a textbox to be validated client side.
		var strError = 'You are trying to add an option to this list that will give you more than ' + intMaxChecked + ' selected options.  Please deselect a checkbox in order to add this new option.';
		if ((fnCountChecked(formThis, strItemNamePartial) + 1) > intMaxChecked) {
			window.alert(strError);
			fnSetAnyItemFocus(formThis, strItemNamePartial);
			return false;
		} else {
			fnSetItemFocus(formThis, ctrlThis.name);
		}
	}
	
	return true;
}

function fnGetRadioValue(formThis, strRadioName) {
	//Cuong Vu
	//January 23, 2005
	var i;
	
	for (i=0; i < formThis.elements.length; i++) {
		if (formThis.elements[i].type == "radio") {
			if (fnIsEqual(formThis.elements[i].name, strRadioName, true)) {
				if (formThis.elements[i].checked) {
					return formThis.elements[i].value;
				}
			}
		}
	}
	return "";
}

function fnAddQS(strURL, strQSName, strQSValue) {
	//Cuong Vu
	//April 7, 2005
	//January 21, 2008 - Switched to use fnReplaceQueryStringNoNulls

	//if a query string already exists use ampersand, else question mark
	if (fnIsNull(strURL)) return ""; //CV on 1/31/2006
	if (fnIsNull(strQSName)) return strURL;
	if (fnIsNull(strQSValue)) return strURL;
	
	return fnReplaceQueryStringNoNulls(strURL, strQSName, strQSValue);
}

function fnAddQSInteger(strURL, strQSName, strQSValue) {
	//Cuong Vu
	//April 7, 2005
	//January 21, 2008 - Switched to use fnReplaceQueryStringNoNulls
	
	//Same as fnAddQS but checks that the strQSValue is a positive integer
	//i.e. the strQSValue passes fnIsAllNums()

	//if a query string already exists use ampersand, else question mark
	if (fnIsNull(strURL)) return ""; //CV on 1/31/2006
	if (fnIsNull(strQSName)) return strURL;
	if (fnIsNull(strQSValue)) return strURL;
	if (fnIsAllNums(strQSValue) == false) return strURL;
	
	return fnReplaceQueryStringNoNulls(strURL, strQSName, strQSValue);
}

function fnAddQSBoolean(strURL, strQSName, strQSValue) {
	//Cuong Vu
	//April 7, 2005
	//January 21, 2008 - Switched to use fnReplaceQueryStringNoNulls
	
	//Same as fnAddQS but checks that the strQSValue is a "1" or "0"

	//if a query string already exists use ampersand, else question mark
	if (fnIsNull(strURL)) return ""; //CV on 1/31/2006
	if (fnIsNull(strQSName)) return strURL;
	if (fnIsNull(strQSValue)) return strURL;
	if ((strQSValue != "1") && (strQSValue !="0")) return strURL;
	
	return fnReplaceQueryStringNoNulls(strURL, strQSName, strQSValue);
}

function fnSelectAll(formThis, strItemName, strItemValuePartial) {
	//Cuong Vu
	//February 15, 2005
	//With dotNet, the CheckBoxList has a bunch of ListItem children.  If a CheckBoxList name
	//is passed, this function will check for the parent name and its children's name (appended with a ":").
	//For <SELECT>, it will only work if object.type == "select-multiple".
	//Only change values that match strItemValuePartial.  If you want to match all, pass a "" string.
	//2009-03-17 by CV - strItemName now matches not case-sensitive

	if (fnIsNull(strItemName)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var boolChangedOne = false;
	var i;
	var j;
	var strName = new String();
	
	for (i=0; i < intLength; i++) {
		var strType = new String(formThis.elements[i].type);
		if (strType == "checkbox") {
			//Match partial child matches and if explicitly called
			strName = new String(formThis.elements[i].name);
			if (fnIsEqual(strName, strItemName, false) ||
				(strName.indexOf(strItemName + ":") > -1)) {
				if (fnIsNull(strItemValuePartial) ||
					formThis.elements[i].value.indexOf(strItemValuePartial) > -1) {
					formThis.elements[i].checked = true;
					boolChangedOne = true;
				}
			}
		} else if (strType == "select-multiple") {
			strName = new String(formThis.elements[i].name);
			if (fnIsEqual(strName, strItemName, false)) {
				for (j=0; j < formThis.elements[i].length; j++) {
					if (fnIsNull(strItemValuePartial) ||
						formThis.elements[i].options[j].value.indexOf(strItemValuePartial) > -1) {
						formThis.elements[i].options[j].selected = true;
						boolChangedOne = true;
					}
				}
			}
		}
	}
	return boolChangedOne;
}

function fnUnSelectAll(formThis, strItemName, strItemValuePartial) {
	//Cuong Vu
	//February 15, 2005
	//With dotNet, the CheckBoxList has a bunch of ListItem children.  If a CheckBoxList name
	//is passed, this function will check for the parent name and its children's name (appended with a ":").
	//For <SELECT>, it will only work if object.type == "select-multiple".
	//Only change values that match strItemValuePartial.  If you want to match all, pass a "" string.
	//2009-03-17 by CV - strItemName now matches not case-sensitive

	if (fnIsNull(strItemName)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var boolChangedOne = false;
	var i;
	var j;
	var strName = new String();
	
	for (i=0; i < intLength; i++) {
		var strType = new String(formThis.elements[i].type);
		if (strType == "checkbox") {
			//Match partial child matches and if explicitly called
			strName = new String(formThis.elements[i].name);
			if (fnIsEqual(strName, strItemName, false) ||
				(strName.indexOf(strItemName + ":") > -1)) {
				if (fnIsNull(strItemValuePartial) ||
					formThis.elements[i].value.indexOf(strItemValuePartial) > -1) {
					formThis.elements[i].checked = false;
					boolChangedOne = true;
				}
			}
		} else if (strType == "select-multiple") {
			strName = new String(formThis.elements[i].name);
			if (fnIsEqual(strName, strItemName, false)) {
				for (j=0; j < formThis.elements[i].length; j++) {
					if (fnIsNull(strItemValuePartial) ||
						formThis.elements[i].options[j].value.indexOf(strItemValuePartial) > -1) {
						formThis.elements[i].options[j].selected = false;
						boolChangedOne = true;
					}
				}
			}
		}
	}
	return boolChangedOne;
}

function fnCheckMail(strEmail)
{
	//Cuong Vu
	//April 4, 2009
	//http://www.quirksmode.org/js/mailcheck.html
	if (null == strEmail || "" == strEmail) return false;
	
	var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
	if (filter.test(strEmail)) {
		return true;
	} else {
		return false;
	}
}

function fnTrimAllTextBoxes(formThis) {
	//By Cuong Vu
	//February 21, 2005
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return -1;
	}
	
	var intCounter = 0;
	var filter  = /\S+/;
	var i;
	
	//loop thru all elements and find if this element is checked
	for (i = 0; i < intLength; i++) {
		if (formThis.elements[i].type == "text") {
			if (filter.test(formThis.elements[i].value)) {
				if (formThis.elements[i].value != fnTrim(formThis.elements[i].value)) {
					formThis.elements[i].value = fnTrim(formThis.elements[i].value);
					intCounter++;
				}
			} else {
				//All whitespace only should be null
				formThis.elements[i].value = "";
				intCounter++;
			}
		}
	}
	return intCounter;
}

/**
 * Removes duplicates in the array 'a'
 * @author Johan Känngård, http://dev.kanngard.net
 */
function fnUnique(a) {
	//CV on 3/2/2008
	var i;
	
	if (!fnIsArray(a)) return fnExplode(a, "CUONGHOLDER");
	
	tmp = new Array(0);
	for(i = 0; i < a.length; i++){
		if(!fnContains(tmp, a[i])){
			tmp.length += 1;
			tmp[tmp.length - 1] = a[i];
		}
	}
	return tmp;
}

function fnDuplicates(a) {
	//CV on 3/2/2008
	if (!fnIsArray(a)) return "";
	
	var tmp = new Array(0);
	var arrayReturn = new Array(0);
	var i;
	
	for(i = 0; i < a.length; i++){
		if(!fnContains(tmp, a[i])){
			tmp.length += 1;
			tmp[tmp.length - 1] = a[i];
		} else if (tmp.length > 0) {
			arrayReturn.length += 1;
			arrayReturn[arrayReturn.length - 1] = a[i];
		}
	}
	
	if (arrayReturn.length == 0) {
		return "";
	} else {
		return fnUnique(arrayReturn);
	}
}

/**
 * Returns true if 's' is contained in the array 'a'
 * @author Johan Känngård, http://dev.kanngard.net
 */
function fnContains(a, e) {
	if (fnIsArray(a) == false) return false;
	var j;
	
	for(j=0;j<a.length;j++)if(a[j]==e)return true;
	return false;
}

function fnContains2(a, e, boolCaseSens) {
	if (fnIsArray(a) == false) return false;
	var j;
	
	for(j=0;j<a.length;j++)if(fnIsEqual(a[j], e, boolCaseSens))return true;
	return false;
}

function fnCompare(obj1, obj2, boolFullMatch, boolCaseSens) {
	//Cuong Vu
	//February 28, 2008
	//You can pass strings or arrays to obj1 and obj2.
	var i;
	var j;
	
	if (!fnIsArray(obj1) && !fnIsArray(obj2)) {
		if (boolFullMatch == true) {
			return fnIsEqual(obj1, obj2, boolCaseSens);
		} else {
			if (fnIndexOf(obj1, obj2, boolCaseSens) >= 0) return true;
		}
	} else if (fnIsArray(obj1) && !fnIsArray(obj2)) {
		if (boolFullMatch == true) {
			for (i = 0; i < obj1.length; i++) if (fnIsEqual(obj1[i], obj2, boolCaseSens)) return true;
		} else {
			for (i = 0; i < obj1.length; i++) if (fnIndexOf(obj1[i], obj2, boolCaseSens) >= 0) return true;
		}
	} else if (!fnIsArray(obj1) && fnIsArray(obj2)) {
		if (boolFullMatch == true) {
			for (j = 0; j < obj2.length; j++) if (fnIsEqual(obj1, obj2[j], boolCaseSens)) return true;
		} else {
			for (j = 0; j < obj2.length; j++) if (fnIndexOf(obj1, obj2[j], boolCaseSens) >= 0) return true;
		}
	} else {
		//both are arrays
		for (i = 0; i < obj1.length; i++) {
			for (j = 0; j < obj2.length; j++) {
				if (boolFullMatch == true) {
					if (fnIsEqual(obj1[i], obj2[j], boolCaseSens)) return true;
				} else {
					if (fnIndexOf(obj1[i], obj2[j], boolCaseSens) >= 0) return true;
				}
			}
		}
	}
	return false;
}

function fnStartsWith(obj1, obj2, boolCaseSens) {
	//Cuong Vu
	//March 3, 2008
	//You can pass strings or arrays to obj1 and obj2.
	var i;
	var j;
	
	if (!fnIsArray(obj1) && !fnIsArray(obj2)) {
		if (fnIndexOf(obj1, obj2, boolCaseSens) == 0) return true;
	} else if (fnIsArray(obj1) && !fnIsArray(obj2)) {
		for (i = 0; i < obj1.length; i++) if (fnIndexOf(obj1[i], obj2, boolCaseSens) == 0) return true;
	} else if (!fnIsArray(obj1) && fnIsArray(obj2)) {
		for (j = 0; j < obj2.length; j++) if (fnIndexOf(obj1, obj2[j], boolCaseSens) == 0) return true;
	} else {
		//both are arrays
		for (i = 0; i < obj1.length; i++) {
			for (j = 0; j < obj2.length; j++) {
				if (fnIndexOf(obj1[i], obj2[j], boolCaseSens) == 0) return true;
			}
		}
	}
	return false;
}

function fnArrayRemove(obj1, obj2, boolFullMatch, boolCaseSens) {
	//Cuong Vu
	//March 4, 2008
	//You can pass strings or arrays to obj1 and obj2.
	//Remove obj2 from obj1
	var i;
	
	var arrReturn = new Array();
	if (!fnIsArray(obj1)) {
		if (!fnCompare(obj1, obj2, boolFullMatch, boolCaseSens)) {
			arrReturn[0] = obj1;
		}
	} else {
		for (i = 0; i < obj1.length; i++) {
			if (!fnCompare(obj1[i], obj2, boolFullMatch, boolCaseSens)) {
				arrReturn.length += 1;
				arrReturn[arrReturn.length - 1] = obj1[i];
			}
		}
	}
	return arrReturn;
}

function fnArrayCombine(obj1, obj2) {
	//Cuong Vu
	//March 4, 2008
	//You can pass strings or arrays to obj1 and obj2.
	//Always returns an array. Does not do a fnUnique. If you want it, use that function.
	var j;
	
	var arrReturn = new Array();
	if (!fnIsArray(obj1) && !fnIsArray(obj2)) {
		arrReturn[0] = obj1;
		arrReturn[1] = obj2;
	} else if (fnIsArray(obj1) && !fnIsArray(obj2)) {
		arrReturn = obj1;
		arrReturn.length += 1;
		arrReturn[arrReturn.length - 1] = obj2;
	} else if (!fnIsArray(obj1) && fnIsArray(obj2)) {
		arrReturn[0] = obj1;
		for (j = 0; j < obj2.length; j++) {
			arrReturn.length += 1;
			arrReturn[arrReturn.length - 1] = obj2[j];
		}
	} else {
		//both are arrays
		arrReturn = obj1;
		for (j = 0; j < obj2.length; j++) {
			arrReturn.length += 1;
			arrReturn[arrReturn.length - 1] = obj2[j];
		}
	}
	return arrReturn;
}

function fnIsArray(obj) {
	//http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256C720080D723
	if (fnIsNull(obj)) return false;
	if (obj.constructor.toString().indexOf("Array") == -1)
		return false;
	else
		return true;
}

function fnIsUnique(a) {
	//By Cuong Vu
	//February 22, 2005
	
	if (fnIsArray(a) == false) return false;
	var numBefore = a.length;
	var numAfter = fnUnique(a).length;
	
	if (numBefore == numAfter) {
		return true;
	} else {
		return false;
	}
}

function fnIsAllUnique(formThis, strItemNamePartial) {
	//By Cuong Vu
	//February 22, 2005

	if (fnIsNull(strItemNamePartial)) return false;
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	arrTemp = new Array(0);
	for (i = 0; i < intLength; i++) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
			arrTemp.length+=1;
			arrTemp[arrTemp.length-1]=formThis.elements[i].value;
		}
	}
	return fnIsUnique(arrTemp);
}

function fnReset(formThis) {
	//By Cuong Vu
	//March 9, 2008
	
	if (confirm('Are you sure you want to reset this form returning it to its original state?  This action will clear all your values and cannot be undone.')) {
		formThis.reset();
		return true;
	} else {
		return false;
	}
}

function fnIsValidDate(intYear, intMonth, intDay) {
	//By Cuong Vu
	//March 28, 2005
	//Javascript is very forgiving and will return an adjusted date even if not valid.  For example, Feb 31, 2006 works.
	//This function will catch that it is not valid.
	//intMonth is 0 to 11 instead of 1-12.  January is month 0.
	var dateToCheck;
	try {
		dateToCheck = new Date(intYear, intMonth, intDay);
	} catch(er) {
		return false;
	}
	return ((intDay == dateToCheck.getDate()) && (intMonth == dateToCheck.getMonth()) && (intYear == dateToCheck.getFullYear()));
}

function fnDayOfWeek(intYear, intMonth, intDay) {
	//By Cuong Vu
	//March 12, 2008
	//Javascript is very forgiving and will return an adjusted date even if not valid.  For example, Feb 31, 2006 works.
	//This function will catch that it is not valid.
	//intMonth is 0 to 11 instead of 1-12.  January is month 0.
	var dateToCheck;
	try {
		dateToCheck = new Date(intYear, intMonth, intDay);
	} catch(er) {
		return -1;
	}
	if ((intDay == dateToCheck.getDate()) && (intMonth == dateToCheck.getMonth()) && (intYear == dateToCheck.getFullYear())) {
		return dateToCheck.getDay();
	} else {
		return -1;
	}
}

// http://javascript.about.com/library/scripts/blquerystring.htm
// Cuong Vu on Sept. 8, 2004
// There was a bug in this function where the QS needed to be unescaped.  I also fixed a bug for
// matching case.  This function now compares against the key case-insensitive.
// Usage: QueryString("keyname")

function QueryString(key)
{
	// Updated by CV on January 31, 2006
	// Instead of returning a null, return an empty string "".  Apparently, when you new String(null), you get "null".
	var value = new String("");
	for (var i=0;i<QueryString.keys.length;i++)
	{
		if (QueryString.keys[i].toLowerCase()==key.toLowerCase())
		{
			value = QueryString.values[i];
			break;
		}
	}
	return value;
}

QueryString.keys = new Array();
QueryString.values = new Array();

function QueryString_Parse()
{
	var query = fnUrlDecode(window.location.search.substring(1));
	var pairs = query.split("&");
	
	for (var i=0;i<pairs.length;i++)
	{
		var pos = pairs[i].indexOf('=');
		if (pos >= 0)
		{
			var argname = pairs[i].substring(0,pos);
			var value = pairs[i].substring(pos+1);
			QueryString.keys[QueryString.keys.length] = argname;
			QueryString.values[QueryString.values.length] = value;		
		}
	}

}

QueryString_Parse();

function fnImplode(objArray, strDelimiter) {
	//Cuong Vu
	//April 7, 2005
	if (fnIsArray(objArray) == false) return objArray;
	var strReturn = new String();
	for (var i=0; i < objArray.length; i++) {
		if (i > 0) {
			strReturn = strReturn + strDelimiter.toString();
		}
		strReturn = strReturn + objArray[i];
	}
	return strReturn;
}

function fnExplode(strToExplode, strDelimiter) {
	//Cuong Vu
	//March 23, 2006
	if (fnIsNull(strToExplode)) return null;
	if (fnIsNull(strDelimiter)) return null;
	var arrayBeforeCleanup = strToExplode.toString().split(strDelimiter.toString());
	var arrayReturn = new Array();
	var i;
	
	for (i=0; i<arrayBeforeCleanup.length; i++) {
		arrayReturn[i] = fnTrim(arrayBeforeCleanup[i]);
	}
	return arrayReturn;
}

function fnIsValidMonth(intMonth) {
	//Cuong Vu
	//April 7, 2005
	//Check that the month is between 0 and 11 inclusive.  Javascript uses 0-11.  January is 0.
	if (fnIsAllNums(intMonth) == false) return false;
	if (intMonth < 0) return false;
	if (intMonth > 11) return false;
	return true;
}

function fnIsValidMonthAdjusted(intMonth) {
	//Cuong Vu
	//April 7, 2005
	//Check that the month is between 1 and 12 inclusive.  This does not use the JS format of 0-11.
	if (fnIsAllNums(intMonth) == false) return false;
	if (intMonth < 0) return false;
	if (intMonth > 11) return false;
	return true;
}

function fnIsNull(strToCheck) {
	//Cuong Vu
	//April 7, 2005
	//Do not compare strToCheck == "", apparently if you pass the number zero (not the string) it breaks.
	if (strToCheck == null) return true;
	var strTest = new String(strToCheck);
	if (strTest.length == 0) return true;
	return false;
}

function fnGotoMonth(formThis, strMonthFieldName, strYearFieldName) {
	//by Cuong Vu
	//April 7, 2005
	//Use for dropdown, radio, textbox, etc. that uses the number alias (1 for January,
	//12 for December) for month and the full year (i.e. 2005).  This function requires
	//fnMoveToMonth
	
	//NOTE: I'm adjusting the Javascript months to match January is month 1.  Therefore, pass
	//this function 1 for January.
	var strMonthValue = fnGetItemValue(formThis, strMonthFieldName);
	var strYearValue = fnGetItemValue(formThis, strYearFieldName);
	if (fnIsInteger(strMonthValue) && fnIsInteger(strYearValue)) {
		fnMoveToMonth(strMonthValue, strYearValue, 0);
	} else {
		return;
	}
}

function fnGotoMonth2(formThis, strMonthFieldName, strYearFieldName, strRelativeURL) {
	//by Cuong Vu
	//April 11, 2005
	//Use for dropdown, radio, textbox, etc. that uses the number alias (1 for January,
	//12 for December) for month and the full year (i.e. 2005).  This function requires
	//fnMoveToMonth
	
	//NOTE: I'm adjusting the Javascript months to match January is month 1.  Therefore, pass
	//this function 1 for January.
	
	//This function will allow to move to a different script strRelativeURL
	if (fnIsNull(strRelativeURL)) return;
	
	var strMonthValue = fnGetItemValue(formThis, strMonthFieldName);
	var strYearValue = fnGetItemValue(formThis, strYearFieldName);
	if (fnIsInteger(strMonthValue) && fnIsInteger(strYearValue)) {
		fnMoveToMonth2(strMonthValue, strYearValue, 0, strRelativeURL);
	} else {
		return;
	}
}

function fnMoveToMonth(intThisMonth, intThisYear, intAddMonths) {
	//by Cuong Vu
	//April 7, 2005
	//Remember that Javascript uses month as 0-11 and not 1-12.  January is month 0.
	//This function will change the "month" and "year" QueryString parameters of the existing
	//URL to the intDeltaMonths.
	//NOTE: I'm adjusting the Javascript months to match January is month 1.  Therefore, pass
	//this function 1 for January.
	
	if (fnIsInteger(intAddMonths) == false) return false;
	
	//If not passed to this function, use the QueryString
	if (fnIsNull(intThisMonth)) {
		intThisMonth = new Number(QueryString('month'));
	}
	if (fnIsInteger(intThisMonth)) {
		intThisMonth = intThisMonth - 1; //adjust for JS
	}
	if (fnIsNull(intThisYear)) {
		intThisYear = new Number(QueryString('year'));
	}
	
	//For the current month, the URL may not have the month and year QS
	var dateThis = new Date();
	if (fnIsNull(intThisMonth) || fnIsNull(intThisYear)) {
		//do nothing
	} else {
		try {
			dateThis = new Date(intThisYear, intThisMonth, 1);
		} catch(er) {
			dateThis = new Date();
		}
	}

	dateThis = fnAdjust(dateThis, 0, intAddMonths, 0, 0, 0, 0);
	var intMonth = dateThis.getMonth();
	var intMonthAdjusted = intMonth + 1;
	var intYear = dateThis.getFullYear();
	var boolFoundMonth = false;
	var boolFoundYear = false;
	
	var strQueryString = fnUrlDecode(location.search.substring(1));
	if (fnIsNull(strQueryString)) {
		location.href = location.pathname + "?month=" + intMonthAdjusted + "&year=" + intYear;
		return;
	}
	var arrPairs = strQueryString.split("&");
	var arrKeys = new Array();
	var arrValues = new Array();
	
	for (var i = 0; i < arrPairs.length; i++)
	{
		var intPos = arrPairs[i].indexOf('=');
		if (intPos >= 0)
		{
			var strKey = arrPairs[i].substring(0, intPos);
			var strValue = arrPairs[i].substring(intPos + 1);
			if (strKey.toLowerCase() == "month") {
				arrKeys[arrKeys.length] = strKey;
				arrValues[arrValues.length] = intMonthAdjusted;
				boolFoundMonth = true;
			} else if (strKey.toLowerCase() == "year") {
				arrKeys[arrKeys.length] = strKey;
				arrValues[arrValues.length] = intYear;
				boolFoundYear = true;
			} else {
				arrKeys[arrKeys.length] = strKey;
				arrValues[arrValues.length] = strValue;	
			}
		}
	}
	
	if (arrKeys.length == 0) {
		location.href = location.pathname + "?month=" + intMonthAdjusted + "&year=" + intYear;
		return;
	}
	if (boolFoundMonth == false) {
		arrKeys[arrKeys.length] = "month";
		arrValues[arrValues.length] = intMonthAdjusted;
	}
	if (boolFoundYear == false) {
		arrKeys[arrKeys.length] = "year";
		arrValues[arrValues.length] = intYear;
	}
	var strURL = new String();
	strURL = location.pathname + "?";
	for (var j = 0; j < arrKeys.length; j++) {
		if (j > 0) {
			strURL = strURL + "&";
		}
		strURL = strURL + arrKeys[j] + "=" + fnUrlEncode(arrValues[j]);
	}
	location.href = strURL;
}

function fnAdjust(dateThis, intAddYears, intAddMonths, intAddDays, intAddHours, intAddMinutes, intAddSeconds) {
	//Cuong Vu
	//April 7, 2005
	//Take dateThis and add the needed times.  JS only allows for the setFullYear, setMonth,
	//setDate, setHours, setMinutes, setSeconds methods.
	//February 1, 2006 - bug with the function.  Only call the set* method if the intAdd* is not zero.  Also,
	//Fixed bug in intAddMonth where it would apparently adds days instead of month values around Feb and other
	//short months.
	if (fnIsNull(dateThis)) return new Date();
	
	var dateNew = new Date(dateThis);
	var intDateOriginal = new Number(dateThis.getDate());
	var intDateLast;
	
	if (intAddYears != 0) dateNew.setFullYear(dateNew.getFullYear() + intAddYears);
	if (intAddMonths != 0) {
		//CV on 02/01/2006
		//I always want the day of the month to be the same when you add months or if a month doesn't have
		//that new day (such as Feb 31) roll back to the last day of that month (Feb 28 or 29).  Fixing the
		//problem where fnAdjust(new Date("01/31/2006"),0,1,0,0,0,0) returns 03/03/2006 instead of 02/28/2006.
		//Not sure where JS gets the number of days for months.  Wrote code on IE6.0.  Firefox may behave differently.
		
		/*
		Remember that this function performs the operations left to right (add year, add month, etc.).  Therefore, you
		may have to split your logic into two lines to compensate for the order of operations.  For example, the first
		line here is not equivalent to the second set of lines.
		
		dateEnd = fnAdjust(dateEnd, 0, 1, 0 - dateEnd.getDate(), 0, 0, 0);
		
		dateEnd = fnAdjust(dateEnd, 0, 0, 0 - dateEnd.getDate(), 0, 0, 0);
		dateEnd = fnAdjust(dateEnd, 0, 1, 0, 0, 0, 0);
		*/
		dateNew.setMonth(dateNew.getMonth() + intAddMonths);
		if (dateNew.getDate() != intDateOriginal) {
			for (var i = 0; i < 31; i++) {
				intDateLast = new Number(dateNew.getDate());
				dateNew.setDate(dateNew.getDate() - 1);
				if (dateNew.getDate() > intDateLast) break;
			}
		}
	}
	if (intAddDays != 0) dateNew.setDate(dateNew.getDate() + intAddDays);
	if (intAddHours != 0) dateNew.setHours(dateNew.getHours() + intAddHours);
	if (intAddMinutes != 0) dateNew.setMinutes(dateNew.getMinutes() + intAddMinutes);
	if (intAddSeconds != 0) dateNew.setSeconds(dateNew.getSeconds() + intAddSeconds);
	return dateNew;
}

function fnMoveToMonth2(intThisMonth, intThisYear, intAddMonths, strRelativeURL) {
	//by Cuong Vu
	//April 12, 2005
	//Remember that Javascript uses month as 0-11 and not 1-12.  January is month 0.
	//This function will change the "month" and "year" QueryString parameters of the existing
	//URL to the intDeltaMonths.
	//NOTE: I'm adjusting the Javascript months to match January is month 1.  Therefore, pass
	//this function 1 for January.
	
	//This function will allow to move to a different script strRelativeURL
	
	if (fnIsInteger(intAddMonths) == false) return false;
	if (fnIsNull(strRelativeURL)) return false;
	
	//If not passed to this function, use the QueryString
	if (fnIsNull(intThisMonth)) {
		intThisMonth = new Number(QueryString('month'));
	}
	if (fnIsInteger(intThisMonth)) {
		intThisMonth = intThisMonth - 1; //adjust for JS
	}
	if (fnIsNull(intThisYear)) {
		intThisYear = new Number(QueryString('year'));
	}
	
	//For the current month, the URL may not have the month and year QS
	var dateThis = new Date();
	if (fnIsNull(intThisMonth) || fnIsNull(intThisYear)) {
		//do nothing
	} else {
		try {
			dateThis = new Date(intThisYear, intThisMonth, 1);
		} catch(er) {
			dateThis = new Date();
		}
	}

	dateThis = fnAdjust(dateThis, 0, intAddMonths, 0, 0, 0, 0);
	var intMonth = dateThis.getMonth();
	var intMonthAdjusted = intMonth + 1;
	var intYear = dateThis.getFullYear();
	var boolFoundMonth = false;
	var boolFoundYear = false;
	
	var strQueryString = fnUrlDecode(location.search.substring(1));
	if (fnIsNull(strQueryString)) {
		location.href = strRelativeURL + "?month=" + intMonthAdjusted + "&year=" + intYear;
		return;
	}
	var arrPairs = strQueryString.split("&");
	var arrKeys = new Array();
	var arrValues = new Array();
	
	for (var i = 0; i < arrPairs.length; i++)
	{
		var intPos = arrPairs[i].indexOf('=');
		if (intPos >= 0)
		{
			var strKey = arrPairs[i].substring(0, intPos);
			var strValue = arrPairs[i].substring(intPos + 1);
			if (strKey.toLowerCase() == "month") {
				arrKeys[arrKeys.length] = strKey;
				arrValues[arrValues.length] = intMonthAdjusted;
				boolFoundMonth = true;
			} else if (strKey.toLowerCase() == "year") {
				arrKeys[arrKeys.length] = strKey;
				arrValues[arrValues.length] = intYear;
				boolFoundYear = true;
			} else {
				arrKeys[arrKeys.length] = strKey;
				arrValues[arrValues.length] = strValue;	
			}
		}
	}
	
	if (arrKeys.length == 0) {
		location.href = strRelativeURL + "?month=" + intMonthAdjusted + "&year=" + intYear;
		return;
	}
	if (boolFoundMonth == false) {
		arrKeys[arrKeys.length] = "month";
		arrValues[arrValues.length] = intMonthAdjusted;
	}
	if (boolFoundYear == false) {
		arrKeys[arrKeys.length] = "year";
		arrValues[arrValues.length] = intYear;
	}
	var strURL = new String();
	strURL = strRelativeURL + "?";
	for (var j = 0; j < arrKeys.length; j++) {
		if (j > 0) {
			strURL = strURL + "&";
		}
		strURL = strURL + arrKeys[j] + "=" + fnUrlEncode(arrValues[j]);
	}
	location.href = strURL;
}

function fnGotoURLWithQueryString(strRelativeURL) {
	//By Cuong Vu
	//April 12, 2005
	//Keep the QueryString of the current location.pathname, but goto new strRelativeURL
	if (fnIsNull(strRelativeURL)) return false;
	var strQueryString = new String(location.search);
	if (strQueryString.length > 0) {
		location.href = strRelativeURL + strQueryString;
	} else {
		location.href = strRelativeURL;
	}
	return true;
}

function fnReplaceQueryStringNoNulls(strCurrentURL, strNewKey, strNewValue) {
	//By Cuong Vu
	//September 28, 2005
	//October 5, 2007 - Don't show the strNewKey if strNewValue is empty or null.
	//Will replace the strCurrentURL passed to it with the new strKey and strValue pair.  If it currently exists
	//in the strCurrentURL, it will overwrite it.  If not, it will add it.  You can use location.href or
	//location.pathname to assign to strCurrentURL.

	if (fnIsNull(strCurrentURL)) return '';
	if (fnIsNull(strNewKey)) return strCurrentURL;
	var strNativePath = new String();
	var strQueryString = new String();
	var boolFoundIt = false;
	var boolAppendAmp = false;
	var strNewQueryString = new String();
	
	try {
		var arrSplitURL = new Array();
		arrSplitURL = strCurrentURL.split("?");
		strNativePath = arrSplitURL[0].toString();
		if (arrSplitURL.length > 0) {
			strQueryString = fnUrlDecode(arrSplitURL[1].toString());
		}
	} catch(er) {
	}
	
	if (strQueryString.length == 0) {
		if (fnIsNull(strNewValue)) {
			return (strNativePath);
		} else {
			return (strNativePath + "?" + strNewKey + "=" + fnUrlEncode(strNewValue));
		}
	}
	var arrPairs = strQueryString.split("&");
	var arrKeys = new Array();
	var arrValues = new Array();
	
	for (var i = 0; i < arrPairs.length; i++)
	{
		var intPos = arrPairs[i].indexOf('=');
		if (intPos >= 0)
		{
			var strKey = arrPairs[i].substring(0, intPos);
			var strValue = arrPairs[i].substring(intPos + 1);
			if (strKey.toLowerCase() == strNewKey.toLowerCase()) {
				arrKeys[arrKeys.length] = strNewKey;
				arrValues[arrValues.length] = strNewValue;
				boolFoundIt = true;
			} else {
				arrKeys[arrKeys.length] = strKey;
				arrValues[arrValues.length] = strValue;	
			}
		}
	}
	
	if (arrKeys.length == 0) {
		if (fnIsNull(strNewValue)) {
			return (strNativePath);
		} else {
			return (strNativePath + "?" + strNewKey + "=" + fnUrlEncode(strNewValue));
		}
	}
	if (boolFoundIt == false) {
		arrKeys[arrKeys.length] = strNewKey;
		arrValues[arrValues.length] = strNewValue;
	}

	var strURL = new String();
	strURL = strNativePath;
	strNewQueryString = "";
	for (var j = 0; j < arrKeys.length; j++) {
		if (!fnIsNull(arrValues[j])) {
			if (boolAppendAmp == true) {
				strNewQueryString = strNewQueryString + "&";
			}
			strNewQueryString = strNewQueryString + arrKeys[j] + "=" + fnUrlEncode(arrValues[j]);
			boolAppendAmp = true;
		}
	}
	if (!fnIsNull(strNewQueryString)) {
		strURL = strURL + "?" + strNewQueryString;
	}
	return strURL;
}

function fnReplaceQueryString(strCurrentURL, strNewKey, strNewValue) {
	//By Cuong Vu
	//September 28, 2005
	//Will replace the strCurrentURL passed to it with the new strKey and strValue pair.  If it currently exists
	//in the strCurrentURL, it will overwrite it.  If not, it will add it.  You can use location.href or
	//location.pathname to assign to strCurrentURL.

	if (fnIsNull(strCurrentURL)) return '';
	if (fnIsNull(strNewKey)) return strCurrentURL;
	var strNativePath = new String();
	var strQueryString = new String();
	var boolFoundIt = false;
	
	try {
		var arrSplitURL = new Array();
		arrSplitURL = strCurrentURL.split("?");
		strNativePath = arrSplitURL[0].toString();
		if (arrSplitURL.length > 0) {
			strQueryString = fnUrlDecode(arrSplitURL[1].toString());
		}
	} catch(er) {
	}
	
	if (strQueryString.length == 0) {
		return (strNativePath + "?" + strNewKey + "=" + fnUrlEncode(strNewValue));
	}
	var arrPairs = strQueryString.split("&");
	var arrKeys = new Array();
	var arrValues = new Array();
	
	for (var i = 0; i < arrPairs.length; i++)
	{
		var intPos = arrPairs[i].indexOf('=');
		if (intPos >= 0)
		{
			var strKey = arrPairs[i].substring(0, intPos);
			var strValue = arrPairs[i].substring(intPos + 1);
			if (strKey.toLowerCase() == strNewKey.toLowerCase()) {
				arrKeys[arrKeys.length] = strNewKey;
				arrValues[arrValues.length] = strNewValue;
				boolFoundIt = true;
			} else {
				arrKeys[arrKeys.length] = strKey;
				arrValues[arrValues.length] = strValue;	
			}
		}
	}
	
	if (arrKeys.length == 0) {
		return (strNativePath + "?" + strNewKey + "=" + fnUrlEncode(strNewValue));
	}
	if (boolFoundIt == false) {
		arrKeys[arrKeys.length] = strNewKey;
		arrValues[arrValues.length] = strNewValue;
	}

	var strURL = new String();
	strURL = strNativePath + "?";
	for (var j = 0; j < arrKeys.length; j++) {
		if (j > 0) {
			strURL = strURL + "&";
		}
		strURL = strURL + arrKeys[j] + "=" + fnUrlEncode(arrValues[j]);
	}
	return strURL;
}

function fnGotoYear(formThis, strYearFieldName) {
	//by Cuong Vu
	//September 28, 2005
	//Changes the "Year" QS parameter.  All other parameters are retained.
	
	var strYearValue = fnGetItemValue(formThis, strYearFieldName);
	
	if (fnIsInteger(strYearValue)) {
		location.href =  fnReplaceQueryString(location.href, "year", strYearValue);
		return true;
	} else {
		return false;
	}
}

function fnShortDateString(dateThis) {
	//by Cuong Vu
	//January 31, 2006
	//returns m/d/yyyy format
	if (fnIsNull(dateThis)) return new Date();
	
	try {
		var strMonth = new String(dateThis.getMonth() + 1);
		var strDay = new String(dateThis.getDate());
		var strYear = new String(dateThis.getFullYear());
		return strMonth + "/" + strDay + "/" + strYear;
	} catch(er) {
		return "fnShortDateString: " + er.toString();
	}
}

function fnLeadingZeros(intThis, intDesiredNumberOnlyLength) {
	//by Cuong Vu
	//January 31, 2006
	//Returns a two digit number with leading zeros, the "length" of the negative sign is not included.
	//I don't know if the negative number will ever be used, but I added it anyway.
	//fnLeadingZeros(-5, 3) will return -005
	//fnLeadingZeros(5, 3) will return 005
	if (fnIsInteger(intDesiredNumberOnlyLength) == false) return "";
	if (fnIsInteger(intThis) == false) return "";
	if (intDesiredNumberOnlyLength < 1) return "";
	
	//we should have a positive or negative integer here.
	var intNew = new Number(intThis);
	intNew = Math.abs(intNew);
	if (intNew.toString().length >= intDesiredNumberOnlyLength) return intThis.toString();
	
	var strReturn = new String("");
	strReturn = fnRepeatString("0", (intDesiredNumberOnlyLength - intNew.toString().length)) + intNew.toString();
	if (intThis < 0) {
		return "-" + strReturn;
	} else {
		return strReturn;
	}
}

function fnRepeatString(strToRepeat, intRepeatTimes) {
	//by Cuong Vu
	//January 31, 2006
	//Simply repeats a character or string a number of times.
	if (fnIsNull(strToRepeat)) return "";
	if (!fnIsInteger(intRepeatTimes)) return "";
	if (intRepeatTimes < 1) return "";
	
	var strReturn = new String("");
	for (var i = 0; i < intRepeatTimes; i++) {
		strReturn = strReturn + strToRepeat;
	}
	return strReturn;
}

function fnDateFormat(dateThis, strFormatToReturn) {
	//by Cuong Vu
	//January 31, 2006
	//Somewhat similar to the MS DateTimeFormatInfo class
	//Here's what's available to you.

	/*
	ms-help://MS.MSDNQTR.2005APR.1033/cpref/html/frlrfsystemglobalizationdatetimeformatinfoclasstopic.htm
	Format Pattern Description 

	d The day of the month. Single-digit days will not have a leading zero. 
	dd The day of the month. Single-digit days will have a leading zero. 
	ddd The abbreviated name of the day of the week, as defined in AbbreviatedDayNames. 
	dddd The full name of the day of the week, as defined in DayNames. 

	M The numeric month. Single-digit months will not have a leading zero. 
	MM The numeric month. Single-digit months will have a leading zero. 
	MMM The abbreviated name of the month, as defined in AbbreviatedMonthNames. 
	MMMM The full name of the month, as defined in MonthNames. 

	y The year without the century. If the year without the century is less than 10, the year is displayed with no leading zero. 
	yy The year without the century. If the year without the century is less than 10, the year is displayed with a leading zero. 
	yyyy The year in four digits, including the century. 

	h The hour in a 12-hour clock. Single-digit hours will not have a leading zero. 
	hh The hour in a 12-hour clock. Single-digit hours will have a leading zero. 
	H The hour in a 24-hour clock. Single-digit hours will not have a leading zero. 
	HH The hour in a 24-hour clock. Single-digit hours will have a leading zero. 

	m The minute. Single-digit minutes will not have a leading zero. 
	mm The minute. Single-digit minutes will have a leading zero. 

	s The second. Single-digit seconds will not have a leading zero. 
	ss The second. Single-digit seconds will have a leading zero. 

	t The first character in the AM/PM designator defined in AMDesignator or PMDesignator, if any. 
	tt The AM/PM designator defined in AMDesignator or PMDesignator, if any.
	*/
	
	//I'm also keeping the shortcuts
	
   /** Output.
    *
    * d :08/17/2000
    * D :Thursday, August 17, 2000
    * f :Thursday, August 17, 2000 16:32
    * F :Thursday, August 17, 2000 16:32:32
    * g :08/17/2000 16:32
    * G :08/17/2000 16:32:32
    * m :August 17
    * r :Thu, 17 Aug 2000 23:32:32 UTC
    * s :2000-08-17T16:32:32
    * t :16:32
    * T :16:32:32
    * u :2000-08-17 23:32:32Z
    * U :Thursday, August 17, 2000 23:32:32
    * y :August, 2000
    * dddd, MMMM dd yyyy :Thursday, August 17 2000
    * ddd, MMM d "'"yy :Thu, Aug 17 '00
    * dddd, MMMM dd :Thursday, August 17
    * M/yy :8/00
    * dd-MM-yy :17-08-00
    */

	if (fnIsNull(strFormatToReturn)) return "";
	try {
		var dateHere = new Date(dateThis);
	} catch(er) {
		return "fnDateFormat: " + er.toString();
	}
	var stryyyy = new String(dateHere.getFullYear());
	var stryy = new String(stryyyy.substr(2, 2));
	var stry = stryy;
	
	var strd = new String(dateHere.getDate());
	var strdd = fnLeadingZeros(strd, 2);
	var strddd = fnDayShort(dateHere.getDay());
	var strdddd = fnDay(dateHere.getDay());
	
	var strM = new String(dateHere.getMonth() + 1); //remember that Javascript uses 0 for January
	var strMM = fnLeadingZeros(strM, 2);
	var strMMM = fnMonthShort(dateHere.getMonth()); //The called function uses 0 for January
	var strMMMM = fnMonth(dateHere.getMonth()); //The called function uses 0 for January
	
	var int12Hour = new Number(dateHere.getHours());
	var strt = "A"
	var strtt = "AM"
	if (int12Hour > 12) {
		int12Hour = int12Hour - 12;
		strt = "P"
		strtt = "PM"
	}
	var strh = new String(int12Hour);
	var strhh = fnLeadingZeros(strh, 2);
	var strH = new String(dateHere.getHours());
	var strHH = fnLeadingZeros(strH, 2);
	
	var strm = new String(dateHere.getMinutes());
	var strmm = fnLeadingZeros(strm, 2);
	
	var strs = new String(dateHere.getSeconds());
	var strss = fnLeadingZeros(strs, 2);
	
	var strUTCyyyy = new String(dateHere.getUTCFullYear());
	var strUTCyy = new String(strUTCyyyy.substr(2, 2));
	var strUTCy = strUTCyy;
	
	var strUTCd = dateHere.getUTCDate();
	var strUTCdd = fnLeadingZeros(strUTCd, 2);
	var strUTCddd = fnDayShort(dateHere.getUTCDay());
	var strUTCdddd = fnDay(dateHere.getUTCDay());
	
	var strUTCM = new String(dateHere.getUTCMonth() + 1); //remember that Javascript uses 0 for January
	var strUTCMM = fnLeadingZeros(strUTCM, 2);
	var strUTCMMM = fnMonthShort(dateHere.getUTCMonth()); //The called function uses 0 for January
	var strUTCMMMM = fnMonth(dateHere.getUTCMonth()); //The called function uses 0 for January
	
	var int12HourUTC = new Number(dateHere.getUTCHours());
	var strUTCt = "A"
	var strUTCtt = "AM"
	if (int12HourUTC > 12) {
		int12HourUTC = int12HourUTC - 12;
		strUTCt = "P"
		strUTCtt = "PM"
	}
	var strUTCh = new String(int12HourUTC);
	var strUTChh = fnLeadingZeros(strUTCh, 2);
	var strUTCH = new String(dateHere.getUTCHours());
	var strUTCHH = fnLeadingZeros(strUTCH, 2);
	
	var strUTCm = new String(dateHere.getUTCMinutes());
	var strUTCmm = fnLeadingZeros(strUTCm, 2);
	
	var strUTCs = new String(dateHere.getUTCSeconds());
	var strUTCss = fnLeadingZeros(strUTCs, 2);
	
	var intOffsetSeconds = dateHere.getTimezoneOffset();
	var strz = new String(intOffsetSeconds / 60);
	var strzz = new String();
	var strzzz = new String();
	if (strz.indexOf(".", 0) > 0) {
		strz = fnInteger(strz);
	}
	strzzz = fnLeadingZeros(strz, 2) + ":" + fnLeadingZeros(Math.abs(intOffsetSeconds % 60), 2);
	strzz = fnLeadingZeros(strz, 2);
	if (intOffsetSeconds > 0) {
		strz = "+" + strz;
		strzz = "+" + strzz;
		strzzz = "+" + strzzz;
	}
	
	switch (strFormatToReturn)
	{
		case "d" :
			return strMM + "/" + strdd + "/" + stryyyy;
		case "D" :
			return strdddd + ", " + strMMMM + " " + strd + ", " + stryyyy;
		case "f" :
			return strdddd + ", " + strMMMM + " " + strd + ", " + stryyyy + " " + strH + ":" + strmm;
		case "F" :
			return strdddd + ", " + strMMMM + " " + strd + ", " + stryyyy + " " + strH + ":" + strmm + ":" + strss;
		case "g" :
			return strMM + "/" + strdd + "/" + stryyyy + " " + strHH + ":" + strmm;
		case "G" :
			return strMM + "/" + strdd + "/" + stryyyy + " " + strHH + ":" + strmm + ":" + strss;
		case "m" :
			return strMMMM + " " + strd;
		case "r" :
			return strUTCddd + ", " + strUTCd + " " + strUTCMMM + " " + strUTCyyyy + " " + strUTCHH + ":" + strUTCmm + ":" + strUTCss + " UTC";
		case "s" :
			return stryyyy + "-" + strMM + "-" + strdd + "T" + strHH + ":" + strmm + ":" + strss;
		case "t" :
			return strHH + ":" + strmm;
		case "T" :
			return strHH + ":" + strmm + ":" + strss;
		case "u" :
			return strUTCyyyy + "-" + strUTCMM + "-" + strUTCdd + " " + strUTCHH + ":" + strUTCmm + ":" + strUTCss + "Z"; //No idea what the Z means.
		case "U" :
			return strUTCdddd + ", " + strUTCMMMM + " " + strUTCd + ", " + strUTCyyyy + " " + strUTCHH + ":" + strUTCmm + ":" + strUTCss;
		case "y" :
			return strMMMM + ", " + stryyyy;
		default :
			var strReturn = new String(strFormatToReturn);
			strReturn = strReturn.replace(/\byyyy\b/g, stryyyy).replace(/\byy\b/g, stryy).replace(/\by\b/g, stry);
			strReturn = strReturn.replace(/\bHH\b/g, strHH).replace(/\bH\b/g, strH);
			strReturn = strReturn.replace(/\bhh\b/g, strhh).replace(/\bh\b/g, strh);
			strReturn = strReturn.replace(/\bmm\b/g, strmm).replace(/\bm\b/g, strm);
			strReturn = strReturn.replace(/\bss\b/g, strss).replace(/\bs\b/g, strs);
			strReturn = strReturn.replace(/\btt\b/g, strtt).replace(/\bt\b/g, strt);
			strReturn = strReturn.replace(/\bzzz\b/g, strzzz).replace(/\bzz\b/g, strzz).replace(/\bz\b/g, strz);
			strReturn = strReturn.replace(/\bMM\b/g, strMM).replace(/\bM\b/g, strM);
			strReturn = strReturn.replace(/\bdd\b/g, strdd).replace(/\bd\b/g, strd);
			strReturn = strReturn.replace(/\bMMMM\b/g, strMMMM).replace(/\bMMM\b/g, strMMM);
			strReturn = strReturn.replace(/\bdddd\b/g, strdddd).replace(/\bddd\b/g, strddd);
			return strReturn;
	}
}

function fnMonth(intMonth) {
	//by Cuong Vu
	//January 31, 2006
	//This function follows the Javascript rule that 0 is January
	if (!fnIsInteger(intMonth)) return "";
	try {
		var intHere = new Number(intMonth);
		if (intHere == 0) {
			return "January";
		} else if (intHere == 1) {
			return "February";
		} else if (intHere == 2) {
			return "March";
		} else if (intHere == 3) {
			return "April";
		} else if (intHere == 4) {
			return "May";
		} else if (intHere == 5) {
			return "June";
		} else if (intHere == 6) {
			return "July";
		} else if (intHere == 7) {
			return "August";
		} else if (intHere == 8) {
			return "September";
		} else if (intHere == 9) {
			return "October";
		} else if (intHere == 10) {
			return "November";
		} else if (intHere == 11) {
			return "December";
		} else {
			return "";
		}
	} catch(er) {
		return "fnMonth: " + er.toString();
	}
}

function fnMonthShort(intMonth) {
	//by Cuong Vu
	//January 31, 2006
	//This function follows the Javascript rule that 0 is January
	if (!fnIsInteger(intMonth)) return "";
	try {
		var intHere = new Number(intMonth);
		if (intHere == 0) {
			return "Jan";
		} else if (intHere == 1) {
			return "Feb";
		} else if (intHere == 2) {
			return "Mar";
		} else if (intHere == 3) {
			return "Apr";
		} else if (intHere == 4) {
			return "May";
		} else if (intHere == 5) {
			return "Jun";
		} else if (intHere == 6) {
			return "Jul";
		} else if (intHere == 7) {
			return "Aug";
		} else if (intHere == 8) {
			return "Sep";
		} else if (intHere == 9) {
			return "Oct";
		} else if (intHere == 10) {
			return "Nov";
		} else if (intHere == 11) {
			return "Dec";
		} else {
			return "fnMonthShort: No month exists for the value = " + intMonth.toString();
		}
	} catch(er) {
		return "fnMonth: " + er.toString();
	}
}

function fnDay(intDayOfTheWeek) {
	//by Cuong Vu
	//January 31, 2006
	//Will return the text of the day of the week passed.  0 for Sunday thru 6 for Saturday.
	if (!fnIsInteger(intDayOfTheWeek)) return "";
	try {
		var intHere = new Number(intDayOfTheWeek);
		if (intHere == 0) {
			return "Sunday";
		} else if (intHere == 1) {
			return "Monday";
		} else if (intHere == 2) {
			return "Tuesday";
		} else if (intHere == 3) {
			return "Wednesday";
		} else if (intHere == 4) {
			return "Thursday";
		} else if (intHere == 5) {
			return "Friday";
		} else if (intHere == 6) {
			return "Saturday";
		} else {
			return "fnDay: No day of the week exists for the value = " + intDayOfTheWeek.toString();
		}
	} catch(er) {
		return "fnDay: " + er.toString();
	}
}

function fnDayShort(intDayOfTheWeek) {
	//by Cuong Vu
	//January 31, 2006
	//Will return the text of the day of the week passed.  0 for Sunday thru 6 for Saturday.
	if (!fnIsInteger(intDayOfTheWeek)) return "";
	try {
		var intHere = new Number(intDayOfTheWeek);
		if (intHere == 0) {
			return "Sun";
		} else if (intHere == 1) {
			return "Mon";
		} else if (intHere == 2) {
			return "Tue";
		} else if (intHere == 3) {
			return "Wed";
		} else if (intHere == 4) {
			return "Thu";
		} else if (intHere == 5) {
			return "Fri";
		} else if (intHere == 6) {
			return "Sat";
		} else {
			return "fnDayShort: No day of the week exists for the value = " + intDayOfTheWeek.toString();
		}
	} catch(er) {
		return "fnDayShort: " + er.toString();
	}
}

function fnInteger(strToParse) {
	//Cuong Vu
	//March 9, 2008
	return fnInt(strToParse);
}

function fnRound(numToRound, intNumPlaces) {
	//by Cuong Vu
	//March 3, 2006
	//Similar to this code, but made it into a nice function
	//http://www.mediacollege.com/internet/javascript/number/round.html
	if (fnIsNull(numToRound)) return "";
	if (fnIsNull(intNumPlaces)) return "";
	if (!fnIsNumeric(numToRound)) return "";
	if (!fnIsInteger(intNumPlaces)) return "";
	
	var numThisPlaces = new Number(intNumPlaces);
	var numThis = new Number(numToRound);
	var numNew = new Number();
	
	//Apparently, the subtraction of 5000 is a bug fix for Javascript per article.
	if (numThis > 8191 && numThis < 10485) {
		numThis = numThis - 5000;
		numNew = Math.round(numThis*Math.pow(10,numThisPlaces))/Math.pow(10,numThisPlaces);
		numNew = numNew + 5000;
	} else {
		numNew = Math.round(numThis*Math.pow(10,numThisPlaces))/Math.pow(10,numThisPlaces);
	}
	return numNew;
}

function fnTruncate(numToTruncate, intNumPlaces) {
	//by Cuong Vu
	//March 9, 2008
	//Similar to this code, but made it into a nice function
	//http://www.mediacollege.com/internet/javascript/number/round.html
	if (fnIsNull(numToTruncate)) return "";
	if (fnIsNull(intNumPlaces)) return "";
	if (!fnIsNumeric(numToTruncate)) return "";
	if (!fnIsInteger(intNumPlaces)) return "";
	
	var numThisPlaces = new Number(intNumPlaces);
	var numThis = new Number(numToTruncate);
	var numNew = new Number();
	
	//Apparently, the subtraction of 5000 is a bug fix for Javascript per article.
	if (numThis > 8191 && numThis < 10485) {
		numThis = numThis - 5000;
		numNew = (fnInt(numThis*Math.pow(10,numThisPlaces)))/Math.pow(10,numThisPlaces);
		numNew = numNew + 5000;
	} else {
		numNew = (fnInt(numThis*Math.pow(10,numThisPlaces)))/Math.pow(10,numThisPlaces);
	}
	return numNew;
}

function fnAnyPairChangedNumeric(formThis, strPrevName, strControlNameStart, strControlNameEnd) {
	//By Cuong Vu
	//March 23, 2006
	//This function is used to compare text (or other controls) fields on the form with
	//their corresponding hidden text values.  For example, for each datagriditem, two elements
	//exist <input name="gridTimeEntry:_ctl2:txtTime" type="text" value="0.10" id="gridTimeEntry__ctl2_txtTime"
 	// onChange="return fnUpdateTotal(this, 'txtHoursPrev2');" /> and
	//<input type="hidden" name="txtHoursPrev2" value="0.10">.  Both are created and assigned values via
	//a DataGrid_ItemBound event.  This function will check all pairs to see if any have changed.  The reason
	//the control name is split into two parts is because the number for the dotNet control is in the middle of the field name.

	//Usage:

	//<input type="button" onClick="javascript: fnAnyPairChangedNumeric(this.form, 'txtHoursPrev', 'gridTimeEntry:_ctl', ':txtTime');">
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i = 0;
	
	//There's a bug in the for loop, i wouldn't increment for some reason.  Switched to a while loop.
	while (i < intLength) {
		if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strPrevName) > -1) {
			try {
				if (fnIsNumeric(formThis.elements[i].value) == false) {
					return true;
				}
				var strNumber = fnRight(formThis.elements[i].name, strPrevName);
				var intNumber = new Number(strNumber);
				var numPrev = new Number(formThis.elements[i].value);
				var strOldFieldName = strControlNameStart + intNumber + strControlNameEnd;
				var numNew = new Number(fnGetItemValue(formThis, strOldFieldName));
				var numDiff = new Number(numPrev - numNew);
				if (numDiff > 0 || numDiff < 0) {
					return true;
				}
			} catch(er1) {
				return true; //All errors mean something has changed.
			}
		}
		i++;
	}
	return false;
}

function fnQueryString() {
	//Cuong Vu
	//November 16, 2007
	//Returns the entire QueryString. I can never remember it.
	return new String(window.location.search.substring(1));
}

function fnBaseUrl() {
	//Cuong Vu
	//November 16, 2007
	//Returns the base URL without the QueryString.
	var strURL = new String(window.location.href);
	if (strURL.indexOf("?") == -1) {
		return strURL;
	} else {
		return fnLeft(strURL, "?", true);
	}
}

function fnRemoveLineBreaks(strSearch) {
	//Cuong Vu
	//February 12, 2008
	//Strips all line breaks
	return fnReplaceRepeatingChar(strSearch.replace(new RegExp( "\\n", "g" ), " "), " ", true);
}

function fnRemoveWhiteSpace(strSearch) {
	//Cuong Vu
	//February 12, 2008
	//Strips all white space characters
	if (fnIsNull(strSearch)) return "";
	strSearch = fnReplace(strSearch, "\r", " ", true);
	strSearch = fnReplace(strSearch, "\n", " ", true);
	strSearch = fnReplace(strSearch, "\t", " ", true);
	strSearch = fnReplace(strSearch, "\v", " ", true);
	return fnReplaceRepeatingChar(strSearch, " ", true);
}

function fnReplaceWhiteSpace(strSearch, strToReplaceWith, boolCaseSens) {
	//Cuong Vu
	//February 12, 2008
	//Strips all white space characters
	if (fnIsNull(strSearch)) return "";
	strSearch = fnReplace(strSearch, "\r", strToReplaceWith, boolCaseSens);
	strSearch = fnReplace(strSearch, "\n", strToReplaceWith, boolCaseSens);
	strSearch = fnReplace(strSearch, "\t", strToReplaceWith, boolCaseSens);
	strSearch = fnReplace(strSearch, "\v", strToReplaceWith, boolCaseSens);
	return fnReplaceRepeatingChar(strSearch, strToReplaceWith, boolCaseSens);
}

function fnReplaceRepeatingChar(strSearch, strDuplicate, boolCaseSens) {
	//Cuong Vu
	//February 12, 2008
	//Will find and replace all consecutive instances of strDuplicate with a single instance of it. You can use for
	//spaces, carriage returns, or any characters.  You must enter double back slash in double quotes to represent a
	//single back slash.  For example:

	//fnReplaceRepeatingChar("What\\\\\'s my cat\\\\\'s name?", "\\") returns "What\'s my cat\'s name?"
	
	if (fnIsNull(strSearch)) return '';
	if (fnIsNull(strDuplicate)) return strSearch;
	if (fnIndexOf(strSearch, strDuplicate + strDuplicate, boolCaseSens) < 0) return strSearch;
	var strReturn = new String(strSearch);
	while (fnIndexOf(strReturn, strDuplicate + strDuplicate, boolCaseSens) >= 0)
	{
		strReturn = fnReplace(strReturn, strDuplicate + strDuplicate, strDuplicate, boolCaseSens);
	}
	return strReturn;
}

function fnReplace(strSearch, strToFind, strToReplace, boolCaseSens) {
	//Cuong Vu
	//February 12, 2008
	
	if (fnIsNull(strSearch)) return '';
	if (fnIsNull(strToFind)) return strSearch;
	if (fnIsNull(strToReplace)) return strSearch;
	
	var strOptions;
	if (boolCaseSens == false) {
		strOptions = "gi";
	} else {
		strOptions = "g";
	}
	return strSearch.replace(new RegExp(strToFind, strOptions), strToReplace);
}

function fnIndexOf(strSearch, strToFind, boolCaseSens) {
	//Cuong Vu
	//February 12, 2008
	//Zero-based return. Same as string.indexOf.
	
	//Example: "".indexOf("cuong") returns -1
	//Example: "Cuong".indexOf("Cu") returns 0
	
	var str1 = new String(strSearch);
	var str2 = new String(strToFind);
	
	if (boolCaseSens == false) {
		str1 = str1.toLowerCase();
		str2 = str2.toLowerCase();
	}
	
	return str1.indexOf(str2);
}

function fnLastIndexOf(strSearch, strToFind, boolCaseSens) {
	//Cuong Vu
	//February 12, 2008
	//Zero-based return. Same as string.lastIndexOf.
	
	//Example: "".lastIndexOf("cuong") returns -1
	//Example: "Cuong".lastIndexOf("Cu") returns 0
	
	var str1 = new String(strSearch);
	var str2 = new String(strToFind);
	
	if (boolCaseSens == false) {
		str1 = str1.toLowerCase();
		str2 = str2.toLowerCase();
	}
	
	return str1.lastIndexOf(str2);
}

function fnInt(strToParse) {
	//Cuong Vu
	//February 18, 2008
	//Will return the integer portion of a number
	strToParse = fnTrim(strToParse);
	if (!fnIsNumeric(strToParse)) return "";
	return parseInt(strToParse);
}

function fnFrac(strToParse) {
	//Cuong Vu
	//February 18, 2008
	//Will return the decimal portion of a number
	//FRAKKING JAVASCRIPT! The following does not work. It returns some funky number when you pass "7.999"
	/*
	var numToParse = new Number(strToParse);
	var numInt = new Number(parseInt(strToParse));
	return (numToParse - numInt);
	*/
	strToParse = fnTrim(strToParse);
	if (!fnIsNumeric(strToParse)) return "";
	var numToParse = new Number(strToParse);
	var strSign = new String();
	if (numToParse < 0) {
		strSign = "-";
	}
	var strReturn = new String(strToParse);
	if (strReturn.indexOf(".") < 0) {
		return 0;
	} else {
		strReturn = fnRight(strReturn, ".", true);
		return parseFloat(strSign + "0.".toString() + strReturn);
	}
}

function fnNumDecimalPlaces(strToParse) {
	//Cuong Vu
	//February 18, 2008
	//Will return the number of decimal places used
	strToParse = fnTrim(strToParse);
	if (!fnIsNumeric(strToParse)) return -1;
	
	var i = 0;
	var numToParse = new Number(strToParse);
	while (parseFloat(fnFrac(numToParse)) != 0.0) {
		numToParse = numToParse * 10;
		i++;
	}
	return i;
}

function Left(strToParse, numPlaces) {
	//Cuong Vu
	//February 18, 2008
	//Returns the left numPlaces characters in strToParse
	if (fnIsNull(strToParse)) return "";
	return strToParse.substr(0, numPlaces);
}

function Right(strToParse, numPlaces) {
	//Cuong Vu
	//February 18, 2008
	//Returns the left numPlaces characters in strToParse
	if (fnIsNull(strToParse)) return "";
	if (numPlaces > strToParse.length) return strToParse;
	return strToParse.slice(strToParse.length - numPlaces);
}

function fnSumFields(formThis, strItemNamePartial, numMin, numMax) {
	//Cuong Vu
	//February 18, 2008
	//Sums all the field values on the form that match strItemNamePartial.
	//The field name is the concatenation of strItemNamePartial and the number
	if (fnIsNull(strItemNamePartial)) return 0;
	if (numMin > numMax) return 0;
	
	var strFieldValue = new String();
	var numReturn = 0;
	var i;
	
	for (i = numMin; i <= numMax; i++) {
		strFieldValue = fnGetItemValue(formThis, strItemNamePartial + i.toString());
		if (fnIsNumeric(strFieldValue)) {
			numReturn = fnAdd(numReturn, strFieldValue);
		}
	}
	return numReturn;
}

function fnAdd(str1, str2) {
	//Cuong Vu
	//February 18, 2008
	//FRAKKING JAVASCRIPT! Weird number when adding decimals
	if (!fnIsNumeric(str1) || !fnIsNumeric(str2)) return "";
	var num1a = new Number(fnInt(str1));
	var num1b = new Number(fnFrac(str1));
	var num2a = new Number(fnInt(str2));
	var num2b = new Number(fnFrac(str2));
	var numReturnInt = new Number(num1a + num2a);
	var numReturnFrac = new Number(num1b + num2b);
	return (numReturnInt + numReturnFrac);
}

function fnDropLow(arr, intDrop) {
	//Cuong Vu
	//March 9, 2008
	if (intDrop <= 0) return arr;
	var arrReturn = new Array();
	if (fnIsNull(arr) || !fnIsArray(arr) || intDrop >= arr.length) return arrReturn;
	arr = arr.sort(numOrderAsc);
	var i;
	
	for (i = 0; i < arr.length; i++) {
		if (i >= intDrop) {
			arrReturn.length += 1;
			arrReturn[arrReturn.length - 1] = arr[i];
		}
	}
	return arrReturn;
}

function fnDropHigh(arr, intDrop) {
	//Cuong Vu
	//March 9, 2008
	if (intDrop <= 0) return arr;
	var arrReturn = new Array();
	if (fnIsNull(arr) || !fnIsArray(arr) || intDrop >= arr.length) return arrReturn;
	arr = arr.sort(numOrderAsc);
	var i;
	
	for (i = 0; i < arr.length; i++) {
		if ((arr.length - i) > intDrop) {
			arrReturn.length += 1;
			arrReturn[arrReturn.length - 1] = arr[i];
		}
	}
	return arrReturn;
}

//http://javascript.about.com/library/blsort.htm
function numOrderAsc(a, b){ return (a-b); }
function numOrderDesc(a, b){ return (b-a); }

function fnAverage(arr) {
	//Cuong Vu
	//March 9, 2008
	if (fnIsNull(arr)) return "";
	if (!fnIsArray(arr) && fnIsNumeric(arr)) return arr.toString();
	var numTotal = 0;
	var numValid = 0;
	var i;
	
	for (i = 0; i < arr.length; i++) {
		if (fnIsNumeric(arr[i])) {
			numTotal = fnAdd(numTotal, arr[i]);
			numValid++;
		}
	}
	if (numValid > 0) {
		return numTotal / numValid;
	} else {
		return "";
	}
}

function fnFindEmailAddresses(StrObj) {
	//http://javascript.internet.com/forms/extract-email.html
	var separateEmailsBy = "; ";
	var email = ""; // if no match, use this
	var emailsArray = StrObj.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi);
	if (emailsArray) {
		email = "";
		for (var i = 0; i < emailsArray.length; i++) {
			if (i != 0) email += separateEmailsBy;
			email += emailsArray[i];
		}
	}
	return email;
}

function fnGetScrollXY() {
	//http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
	var scrOfX = 0, scrOfY = 0;
	if( typeof( window.pageYOffset ) == 'number' ) {
		//Netscape compliant
		scrOfY = window.pageYOffset;
		scrOfX = window.pageXOffset;
	} else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
		//DOM compliant
		scrOfY = document.body.scrollTop;
		scrOfX = document.body.scrollLeft;
	} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
		//IE6 standards compliant mode
		scrOfY = document.documentElement.scrollTop;
		scrOfX = document.documentElement.scrollLeft;
	}
  
	return [ scrOfX, scrOfY ];
}

function fnToggleLayer( whichLayer ) {
	//http://www.netlobo.com/div_hiding.html
	var elem;
	var vis;
	
	if( document.getElementById ) // this is the way the standards work
		elem = document.getElementById( whichLayer );
	else if( document.all ) // this is the way old msie versions work
		elem = document.all[whichLayer];
	else if( document.layers ) // this is the way nn4 works
		elem = document.layers[whichLayer];
		vis = elem.style;
		// if the style.display value is blank we try to figure it out here
		if(vis.display==''&&elem.offsetWidth!=undefined&&elem.offsetHeight!=undefined)
		vis.display = (elem.offsetWidth!=0&&elem.offsetHeight!=0)?'block':'none';
		vis.display = (vis.display==''||vis.display=='block')?'none':'block';
}

function fnUrlEncode(strUrl) {
	//http://xkr.us/articles/javascript/encode-compare/
	if (fnIsNull(strUrl)) return '';
	
	var strReturn = new String(strUrl);
	
	if (strReturn.indexOf("=") >= 0) {
		strReturn = encodeURI(strReturn);
		strReturn = strReturn.replace("?", "%3F");
		strReturn = strReturn.replace("#", "%23");
		strReturn = strReturn.replace("&", "%26");
	} else {
		strReturn = encodeURIComponent(strReturn);
	}
	strReturn = strReturn.replace('\'', "%27");
	return strReturn;
}

function fnUrlDecode(strUrl) {
	//2009-03-17 by CV - no tricks yet
	return unescape(strUrl);
}

function fnDisableAll(formThis, strItemNamePartial) {
	//By Cuong Vu
	//March 18, 2009
	//Will disable all buttons that match the strItemNamePartial
	if (fnIsNull(strItemNamePartial)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		try {
			if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
				formThis.elements[i].disabled = true;
				return true;
			}
		} catch(er2) {

		}
	}
	return false;
}

function fnUnDisableAll(formThis, strItemNamePartial) {
	//By Cuong Vu
	//March 18, 2009
	//Will disable all buttons that match the strItemNamePartial
	if (fnIsNull(strItemNamePartial)) return false;
	
	//Catch errors when the form hasn't been loaded yet.
	try {
		var intLength = formThis.elements.length;
	} catch(er) {
		return false;
	}
	
	var i;
	
	for (i=0; i < intLength; i++) {
		try {
			if (!fnIsNull(formThis.elements[i].name) && formThis.elements[i].name.indexOf(strItemNamePartial) > -1) {
				formThis.elements[i].disabled = false;
				return true;
			}
		} catch(er2) {

		}
	}
	return false;
}
// -->