function CheckNum(source, args)
{                   
    if (isNaN(args.Value) || (args.Value < 0))
    {
        args.IsValid = false;
    }              
    else
    {
        args.IsValid = true;
    }
}

function TALimit()
{
    var taObj=event.srcElement;	    
    if (taObj.value.length > 1000) return false;
}

function TAStrip()
{ 
    var taObj=event.srcElement;
    if (taObj.value.length > 1000) taObj.value=taObj.value.substring(0,1000);	    
} 

function TAStrip(max)
{ 
    var targetObj;
    if(event.srcElement) targetObj = event.srcElement; else targetObj = event.target;
    
    if(targetObj != null)
	    if (targetObj.value.length > max) 
			targetObj.value = targetObj.value.substring(0,max);	    
} 

function TAStrip2000()
{ 
    var taObj=event.srcElement;
    if (taObj.value.length > 2000) taObj.value=taObj.value.substring(0,2000);	    
}  

function MaxCheck(source, args)
{       
    var x = args.Value;
                
    if (x.length > 1000)
    {
        args.IsValid = false;
    }              
    else
    {
        args.IsValid = true;
    }
}

function MaxCheck500(source, args)
{       
    var x = args.Value;
                
    if (x.length > 500)
    {
        args.IsValid = false;
    }              
    else
    {
        args.IsValid = true;
    }
}

function trim() //Removes whitespace from the beginning and end of a string
{
	var targetObj;
    if(event.srcElement) targetObj = event.srcElement; else targetObj = event.target;
    
    if(targetObj != null)
    {
		var strValue = targetObj.value;
		while(strValue.charAt(0) == ' ' || strValue.charAt(0) == '\n' || strValue.charAt(0) == '\t') strValue = strValue.substring(1, strValue.length);
		while(strValue.charAt(strValue.length-1) == ' ' || strValue.charAt(strValue.length-1) == '\n' || strValue.charAt(strValue.length-1) == '\t') strValue = strValue.substr(0, strValue.length-1);
		
		if(strValue.length < targetObj.value.length)	//Changing the value gives the HTML element focus, only change focus if the input failed to meet restrictions.
			targetObj.value = strValue;
    }
}

function CharsRemaining(id, max)
{
	var el = document.getElementById(id);
	var src; if(event.srcElement) src = event.srcElement; else src=event.target;
	
	try
	{
		var charsLeft = max - src.value.length;
		el.innerHTML = charsLeft + ' characters remaining.';
	}
	catch(err)
	{}
}
	
//Used to automatically shift focus between related fields.
function ShiftFocus(charCount, strShiftTargetID, blnNumericOnly)
{
	var blnNumericCheck = true;
	var sourceRef;
    if(event.srcElement) sourceRef = event.srcElement; else sourceRef = event.target;
    
    if(sourceRef != null)
    {
		//Numeric keys on the keyboard have codes 48 through 57, numeric keys on the numpad have codes 96 through 105
		if(blnNumericOnly && (!(event.keyCode > 95 && event.keyCode < 106) && !(event.keyCode > 47 && event.keyCode < 58)))
			blnNumericCheck = false;
		
		if(sourceRef.value.length == charCount && blnNumericCheck)
		{
			var shiftTargetRef = document.getElementById(strShiftTargetID);
			if(shiftTargetRef != null)
				shiftTargetRef.select();
		}
    }
}

//Clears the target element of the characters that match the passed in regular expression
//The /[^ ...]/g syntax can be used to express which symbols to keep. 
function RestrictInput(disallowedRegExp)
{
	var targetObj;
    if(event.srcElement) targetObj = event.srcElement; else targetObj = event.target;
    
    if(targetObj != null && disallowedRegExp != null)
    {
		var strValue = targetObj.value.replace(disallowedRegExp, '');
		
		if(strValue.length < targetObj.value.length)	//Changing the value gives the HTML element focus, only change focus if the input failed to meet restrictions.
			targetObj.value = strValue;
    }
}

//Under default security settings, ASP.NET does not allow form input containing the angle bracket characters
//('<' and '>') because submitting scripts as input fields is a common attack approach for users with ill intent.
//If angle brackets are submitted as input an exception will be thrown and the error page will be displayed
//as a safety precaution. Angle brackets can be submitted as input when properly encoded as HTML escape sequences
//(&lt; and &gt;). The following function escapes the angle brackets for a single form element or for all form
//elements at once. When dealing with an entire form this function relies on a consitent naming structure for
//the form elements on a page (ex. the names of text inputs begin with the prefix 'txt').
function EncodeInput(blnAll)
{
	var i;
	var id_parts, type;
	var blnReplaced = false;
	var txtBox, strCleanValue;
    
    if(blnAll == true)
    {
		for(i = 0; i < document.forms[0].elements.length; i++)
		{
			id_parts = document.forms[0].elements[i].id.split("_");	//WebControls have their ID combined with that of the parent ContentPlaceholder
			if(id_parts.length > 2)									//if the split yielded 3 or more elements, assume element comes from a ContentPlaceholder
			{
				type = id_parts[2].substring(0, 3);
				if(type == 'txt')
				{
					txtBox = document.forms[0].elements[i];
					if(txtBox.value != null)
					{
						strCleanValue = txtBox.value.replace(/</g, '&lt;');	//the 'g' flag indicates a global search, i.e. all occurrences of that target pattern are found and replaced.
						strCleanValue = strCleanValue.replace(/>/g, '&gt;');
						if(txtBox.value != strCleanValue)
						{
							txtBox.value = strCleanValue;
							blnReplaced  = true;
						}
					}
				}
			}
		}
	}
	else
	{
		if(event.srcElement) txtBox = event.srcElement; else txtBox = event.target;
		if(txtBox != null)
		{
			strCleanValue = txtBox.value.replace(/</g, '&lt;');
			strCleanValue = strCleanValue.replace(/>/g, '&gt;');
			if(txtBox.value != strCleanValue)
			{
				txtBox.value = strCleanValue;
				blnReplaced  = true;
			}
		}
	}
	
	return blnReplaced;
}

//
// The following four functions are used in combination
//

//This function depends on a consistent naming scheme for Web Controls: textbox and textarea names begin with the
//prefix 'txt' and dropdown box names begin with the prefix 'sel'.
function EnableDetectChanges()
{
	var i;
    var id_parts;
    var type;
    
    for(i = 0; i < document.forms[0].elements.length; i++)
    {
		id_parts = document.forms[0].elements[i].id.split("_");	//WebControls have their ID combined with that of the parent ContentPlaceholder
        if(id_parts.length > 2)									//if the split yielded 3 or more elements, assume element comes from a ContentPlaceholder
        {
            type = id_parts[2].substring(0, 3);
            switch (type)
            {
                case "txt": document.forms[0].elements[i].onchange = ChangesOccurred;
                            break;
                case "sel": document.forms[0].elements[i].onselectedchanged = ChangesOccurred;
                            break;
            }
        }
    }
}

function CheckForChanges()
{
	//relies on the existence of a hidden input tag with the id "ChangesOccurred" and on the existence
	//of a Submit input tag with the id "cpl00_ContentPlaceholder_btnSubmit"	
	if(document.getElementById("ChangesOccurred").Value == "true")
		if(confirm("Would you like to Save before leaving?"))
		{
			document.getElementById("ctl00_ContentPlaceHolder_btnSubmit").click();
			alert("Your changes have been saved."); //Block for user I/O, this appapently frees the client to 
													//do a postback, which otherwise does not occur since this
													//function is used when a page is unloading. Alert box can
													//be dismissed by the user anytime, there is no need to wait
													//once the postback is underway.
		
		}
}

function ChangesOccurred()
{
	//relies on the existence of a hidden input tag with the id "ChangesOccurred"
	document.getElementById("ChangesOccurred").Value = "true";
}

//Used to avoid the save prompt when the user click on the Save/Submit button
function ClearChangesFlag()
{
	//relies on the existence of a hidden input tag with the id "ChangesOccurred"
	document.getElementById("ChangesOccurred").Value = "";
}

//This function is the clientside code for a .NET Custom Validator 
//val is the html control that initiated the event
//arg is the incoming value that is allegedly a date
function ValidateDate(val, arg) 
{   
    // use regular expression to determine if date entered is valid
    var RegExPattern = /^(?=\d)(?:(?:(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})|(?:0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))|(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2}))($|\ (?=\d)))?(((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))|([01]\d|2[0-3])(:[0-5]\d){1,2})?$/;
    if ((arg["Value"].match(RegExPattern)) && (arg["Value"]!='')) 
    {
        arg["IsValid"] = true;  
    } 
    else 
    {
        arg["IsValid"] = false;    
    } 
}

//This validation function creates a custom message tailored to the input being validated. The
//custom message is displayed by assigning it sender.errormessage. JavaScript is case sensitive.
//(Unlike args.IsValid and args.Value, sender.errormessage is all lower case.)
//The ControlToValidate property of CustomValidator controls should not be specified as this is
//known to prevent the validation message from showing up in the ValidationSummary on the page.
//(Making the CustomValidator and ValidationSummary controls belong to the same validation group
//does not correct this behavior). Instead, this function expects the client ID of the Control
//that will be validated to be available as a global variable whose name is mangled with the client
//ID of the CustomValidator that will 
function CheckFilterWords2(sender, args)
{
	//Utilizes five global variables:
	//"strCheckFilterWords_ControlToValidate"  -- The client ID of the HTML element to be validated. The client ID is the ID of the server control mangled with the IDs of its naming containers.
	//"astrCheckFilterWords_FilterWords"       -- An array of strings containing the filter words
	//"astrCheckFilterWords_Redirects"         -- An array of strings, each associated with a filter word
	//"ablnCheckFilterWords_EncounteredFlags"  -- An array of boolean flags, each indicating whether a warning for the filter word at the same index has already been displayed
	//"blnCheckFilterWords_FilterWarningDisplayed" -- A flag used to indicate whether a warning has already been displayed. Warnings are displayed only once. The flag is reset if a previously unencountered filter word is found.
	//These globals are prepared at runtime by the page that contains the validator that will
	//call this funciton.
	var i;
	var strList = '';
	var regEx = new RegExp();
	var txt = document.getElementById(strCheckFilterWords_ControlToValidate);
	
	if(txt != null)
	{	
		for(i = 0; i < astrCheckFilterWords_FilterWords.length; i++)
		{
			regEx.compile('\\b' + astrCheckFilterWords_FilterWords[i] + '\\b', 'im'); //'\b' matches word boundaries, this avoids matching word parts
																 //escape the backslash so the resulting symbol is '\b'
																 //flags: i = Ignore Case, m = Multiline 
			if(regEx.test(txt.value) && ablnCheckFilterWords_EncounteredFlags[i] == undefined)
			{
				blnCheckFilterWords_FilterWarningDisplayed = false;
				ablnCheckFilterWords_EncounteredFlags[i] = 1;
				strList = strList + '<li>' + astrCheckFilterWords_FilterWords[i] + ' (<a href="http://www.fta.dot.gov/relatedlinks"><u>' + astrCheckFilterWords_Redirects[i] + '</u></a>)';
			}
		}
	}

	if(strList != '')
	{
		strList = '<div>The following words are associated with agencies other than FTA:<br /><ul>' + strList + '</ul><br />FTA may not have sufficient information regarding these topics to provide you a helpful response.</div>';
		sender.errormessage = strList;
		
		if(!blnCheckFilterWords_FilterWarningDisplayed)
		{
			blnCheckFilterWords_FilterWarningDisplayed = true;	//Validation interferes with form submission only once unless new filter words are added as part of an attempted correction.
			args.IsValid = false;
		}
		else
			args.IsValid = true;
	}
	else
	{
		args.IsValid = true;
	}
	
	return args.IsValid;
}

//Returns a Boolean value based on the presence of at least one Filter Word in the specified HTML
//element. Once encountered and displayed to the user, a Filter Word no longer causes the validation
//check to return false.
//This function can be called from dynamically emitted wrapper function that is generated at runtime
//with the parameters expected by this function. Alternatively, the Boolean return value can be used
//to prevent form submission by setting the onclick attribute of the submit button as follows:
//onclick=" if(!CheckFilterWords(...)) return false; "
// strTextAreaClientID			-> The id of the HTML element that will be checked for the presence of Filter Words
// strValidationSummaryClientID	-> The id of the HTML element that will display a validation message to the user
//								   if validation fails. Ex. A Label ASP.NET control.
// strValidationLabelClientID	-> The id of the HTML element used to aid the user to locate the HTML element 
//								   that failed validation. It is located next to the HTML element checked for 
//								   the presence of Filter Words
function CheckFilterWords(strTextAreaClientID, strValidationSummaryClientID, strValidationLabelClientID)
{
	//Utilizes four global variables:
	//"astrCheckFilterWords_FilterWords"       -- An array of strings containing the filter words
	//"astrCheckFilterWords_Redirects"         -- An array of strings, each associated with a filter word
	//"ablnCheckFilterWords_EncounteredFlags"  -- An array of boolean flags, each indicating whether a warning for the filter word at the same index has already been displayed
	//"blnCheckFilterWords_FilterWarningDisplayed" -- A flag used to indicate whether a warning has already been displayed. Warnings are displayed only once. The flag is reset if a previously unencountered filter word is found.
	//These globals are prepared at runtime by the page that contains the validator that will
	//call this funciton.
	var blnIsValid;
	var i;
	var list = '';
	var regEx = new RegExp(); 
	var txta       = document.getElementById(strTextAreaClientID);
	var valSummary = document.getElementById(strValidationSummaryClientID);
	var lbl        = null;
	
	if((strValidationLabelClientID != null) && (strValidationLabelClientID != ''))
		lbl = document.getElementById(strValidationLabelClientID);
		
	for(i = 0; i < astrCheckFilterWords_FilterWords.length; i++)
	{
		regEx.compile('\\b' + astrCheckFilterWords_FilterWords[i] + '\\b', 'im'); //'\b' matches word boundaries, this avoids matching word parts
															 //escape the backslash so the resulting symbol is '\b'
															 //flags: i = Ignore Case, m = Multiline 
		if(regEx.test(txta.value) && ablnCheckFilterWords_EncounteredFlags[i] == undefined)
		{
			blnCheckFilterWords_FilterWarningDisplayed = false;
			ablnCheckFilterWords_EncounteredFlags[i] = 1;
			list = list + '<li>' + astrCheckFilterWords_FilterWords[i] + ' (<a href="http://www.fta.dot.gov/relatedlinks"><u>' + astrCheckFilterWords_Redirects[i] + '</u></a>)';
		}
	}

	if(list != '')
	{
		list = '<br>The following words are associated with agencies other than FTA:<br><ul>' + list + '</ul><br> FTA may not have sufficient information regarding these topics to provide you a helpful response.<br>';
		valSummary.innerHTML = list;
		if(lbl != null) lbl.innerHTML = '<font size="+1">*</font>';
		
		if(!blnCheckFilterWords_FilterWarningDisplayed)
		{
			blnCheckFilterWords_FilterWarningDisplayed = true;	//Validation interferes with form submission only once unless new filter words are added as part of an attempted correction.
			blnIsValid = false;
		}
		else
			blnIsValid = true;
	}
	else
	{
		valSummary.innerHTML = '';
		if(lbl != null) lbl.innerHTML = '';
		blnIsValid = true;
	}
	return blnIsValid
}

//===============================================================================
//Client-side highlighting:
//Unmodified version available at http://www.kryogenix.org/code/browser/searchhi/
function highlightWord(node, objRegex, strStyleName, strToolTip, intTabIndex)
{
	var intCount = 0;
	
	// Iterate into this nodes childNodes
	if (node.hasChildNodes)
	{
		var hi_cn;
		for (hi_cn = 0; hi_cn < node.childNodes.length; hi_cn++)
		{
			intCount += highlightWord(node.childNodes[hi_cn],objRegex, strStyleName, strToolTip, intTabIndex);
		}
	}

	// And do this node itself
	if (node.nodeType == 3) // text node
	{
		var intMatchIndex = node.nodeValue.search(objRegex);
		var astrMatchText = node.nodeValue.match(objRegex);
		var nv, strMatchText;
		var pn, nodeBefore, nodeAfter, nodeHiWord, nodeHiWordText;
		
		if (intMatchIndex != -1)
		{
			strMatchText = astrMatchText[0];
			pn = node.parentNode;
			
			// If the word has not already been highlighted and is not a drop-down option
			if (pn.className != strStyleName && pn.nodeName.toLowerCase() != 'option') 
			{
				nv = node.nodeValue;
				
				// Create a set of replacement nodes
				nodeBefore     = document.createTextNode(nv.substr(0,intMatchIndex));
				nodeAfter      = document.createTextNode(nv.substr(intMatchIndex+strMatchText.length));
				nodeHiWordText = document.createTextNode(strMatchText);
				nodeHiWord     = document.createElement('span');
				
				nodeHiWord.className = strStyleName;
				if(strToolTip != null && strToolTip != '')
					nodeHiWord.title = strToolTip;
				if(intTabIndex > 0)
					nodeHiWord.tabIndex = intTabIndex;
				nodeHiWord.appendChild(nodeHiWordText);
				
				pn.insertBefore(nodeBefore, node);
				pn.insertBefore(nodeHiWord, node);
				pn.insertBefore(nodeAfter, node); 
				pn.removeChild(node);
				
				intCount++;
			}
		}
	}
	
	return intCount;
}

function unhighlightWord(node, word, strStyleName)
{
	// Iterate into this nodes childNodes
	if (node.hasChildNodes)
	{
		var hi_cn;
		for (hi_cn = 0; hi_cn < node.childNodes.length; hi_cn++)
		{
			highlightWord(node.childNodes[hi_cn],word, strStyleName);
		}
	}

	// And do this node itself
	if (node.nodeType == 3) // text node
	{
		tempNodeVal = node.nodeValue.toLowerCase();
		tempWordVal = word.toLowerCase();
		if (tempNodeVal.indexOf(tempWordVal) != -1)
		{
			pn = node.parentNode;
			if (pn.className == strStyleName)
			{
				prevSib           = pn.previousSibling;
				nextSib           = pn.nextSibling;
				nextSib.nodeValue = prevSib.nodeValue + node.nodeValue + nextSib.nodeValue;
				prevSib.nodeValue = '';
				node.nodeValue    = '';
			}
		}
	}
}

function unhighlight(node, strStyleName)
{
	// Iterate into this nodes childNodes
	if (node.hasChildNodes)
	{
		var hi_cn;
		for (hi_cn = 0; hi_cn < node.childNodes.length ; hi_cn++)
		{
			unhighlight(node.childNodes[hi_cn], strStyleName);
		}
	}

	// And do this node itself
	if (node.nodeType == 3) // text node
	{ 
		pn = node.parentNode;
		
		if( pn.className == strStyleName )
		{
			prevSib           = pn.previousSibling;
			nextSib           = pn.nextSibling;
			nextSib.nodeValue = prevSib.nodeValue + node.nodeValue + nextSib.nodeValue;
			prevSib.nodeValue = '';
			node.nodeValue    = '';
		}
	}
}

function localSearchHighlight(searchStr, strStyleName, strRootNode_ClientID, strCountOutput_ClientID, strToolTip, intTabIndex) 
{
	var i = 0, intCount = 0;
	var objBody, objCountLabel;
	var objRegex;
	
	if (!document.createElement) return; //If the document contents (in memory as an XML tree) cannot be modified, client-side highlighting will not work.
	if (searchStr == '') return;
	
	if(strRootNode_ClientID != null)
		objBody = document.getElementById(strRootNode_ClientID);
	else
		objBody = document.getElementsByTagName("body")[0];
	
	objCountLabel = document.getElementById(strCountOutput_ClientID);
	
	if(objBody != null)
	{
		unhighlight(objBody, strStyleName);
		
		//Split input into search phrases based on double quotes, split operation produces some empty elements that will be skipped.
		qsa = searchStr.split(/\"/);

		for (i = 0; i < qsa.length; i++)
		{
			qsa[i] = qsa[i].replace(/\s+/gm, ' ');				//Replace all repeating whitespace with a single space
			qsa[i] = qsa[i].replace(/(^\s*)|(\s*$)/gm, '');		//Trim leading and trailing whitespace
			qsa[i] = qsa[i].replace(/[^a-z0-9]/gmi, ' ');		//Replace all non-letter and non digit characters with spaces, i.e. puntuation etc.
			qsa[i] = qsa[i].replace(/ /gm, '(( )|( ?[^a-z0-9] ?)|(://))');//Replace single spaces with the pattern for an optional punctuation char and/or a single space or the "://" separator between the protocol name and domain name in a URI
			if (qsa[i].length == 0) continue;					//Skip blank phrases
			objRegex = new RegExp(qsa[i], 'mi');
			intCount += highlightWord(objBody, objRegex, strStyleName, strToolTip, intTabIndex);
		}
	}
	
	if(intCount == 0)
		objCountLabel.innerHTML = 'There are no matches.';
	else if(intCount == 1)
		objCountLabel.innerHTML = 'There is 1 match highlighted.';
	else
		objCountLabel.innerHTML = 'There are ' + intCount + ' matches highlighted.';
}
//End Of Client-side highlighting
//===============================================================================
