/* Validates form fields
 * -----------------------------------------------
 * Input : 1) array of strings
 *		   An a array holding the names of the field names to be validated
 *		   Format of array item
 *		   <form name>[<type>][<requirement>]
 *		   2) string
 * 		   name of the form
 * Output : boolean
 *			Whether the form passes the validation checks
 */
function validateFormFields(fieldArray, formName)
{

	// If arguments not provided, assume they are on the current document
	if (!fieldArray)
		fieldArray = FieldsToValidate;
	if (!formName)
		formName = document.forms[0].name;
		
	Passed = true;	// Default to form as being OK
	
	// The messages that gets displayed upon failed validation
	Msg = "\tFORM ERROR\n";
	Msg += "------------------------------------\n";
	AlphaMsg = "\n";
	AlphaNumMsg = "\n";
	CurrencyMsg = "\n";
	NumericMsg = "\n";
	TextMsg = "\n";
	EmailMsg = "\n";
	UrlMsg = "\n";
	RequiredMsg = "\n";
	
	// The number of errors that occur for each type of validation
	AlphaErrs = 0;
	AlphaNumErrs = 0;
	CurrencyErrs = 0;
	NumericErrs = 0;
	TextErrs = 0;
	RequiredErrs = 0;
	EmailErrs = 0;
	UrlErrs = 0;
	
	// Arrays that hold the corresponding fields for validation
	AlphaFields = new Array();
	AlphaNumFields = new Array();
	CurrencyFields = new Array();
	NumFields = new Array();
	TextFields = new Array();
	EmailFields = new Array();
	UrlFields = new Array();
	RequiredFields = new Array();


	// Place the passed in fields into their appropriate validation arrays
	currType = "";
	for (i = 0; i < fieldArray.length; i++)
	{
		// Make note of the required fields
		currReq = fieldArray[i].charAt(fieldArray[i].length - 1);
		if (currReq == '!')
		{
			if (fieldArray[i].lastIndexOf("*") > 0)
			{
				currType = fieldArray[i].substring(fieldArray[i].length - 3, fieldArray[i].length - 1);
				currField = fieldArray[i].substring(0, fieldArray[i].length - 3);
			}
			else
			{
				currType = "*Z";		// Dummy i.e. no current type
				currField = fieldArray[i].substring(0, fieldArray[i].length - 1);
			}
			RequiredFields[RequiredFields.length] = currField;
			//alert(currField);
		}
		else
		{
			currType = fieldArray[i].substr(fieldArray[i].length - 2);
			currField = fieldArray[i].substring(0, fieldArray[i].length - 2);
		}
		
		switch(currType)
		{
			case "*A": case "*a":
				AlphaFields[AlphaFields.length] = currField;
				break;
			case "*B": case "*b":
				AlphaNumFields[AlphaNumFields.length] = currField;
				break;
			case "*C": case "*c":
				CurrencyFields[CurrencyFields.length] = currField;
				break;
			case "*N": case "*n":
				NumFields[NumFields.length] = currField;
				break;
			case "*T": case "*t":
				TextFields[TextFields.length] = currField;		
				break;
			case "*E": case "*e":
				EmailFields[EmailFields.length] = currField;		
				break;
			case "*U": case "*u":
				UrlFields[UrlFields.length] = currField;		
				break;
		}
	}
	

	// Validate Alphabetic fields
	for (i = 0; i < AlphaFields.length; i++)
	{
		field = eval(formName + "." + AlphaFields[i]);
		field.value = trim(field.value);
		
		nonValidChars = /[^a-zA-Z_\s\.\/-]/gi;
		if (field.value.search(nonValidChars) >= 0)
		{
			if (AlphaErrs++ == 0)
			{
				Msg += AlphaMsg;
				Passed = false;	
			}
			
			Msg += "      " + AlphaErrs + ") " + field.name + "\n";
		}
	}

	// Validate AlphaNumeric fields
	for (i = 0; i < AlphaNumFields.length; i++)
	{
		field = eval(formName + "." + AlphaNumFields[i]);
		field.value = trim(field.value);
		
		nonValidChars = /[^\da-zA-Z-_\s\.\/\(\)]/gi;
		if (field.value.search(nonValidChars) >= 0)
		{
			if (AlphaNumErrs++ == 0)
			{
				Msg += AlphaNumMsg;
				Passed = false;	
			}
			
			Msg += "      " + AlphaNumErrs + ") " + field.name + "\n";
		}
	}

	// Validate Currency fields
	for (i = 0; i < CurrencyFields.length; i++)
	{
		field = eval(formName + "." + CurrencyFields[i]);
		field.value = trim(field.value);
		
		nonValidChars = /[^\$\s\.0-9]/gi;
		if (field.value.search(nonValidChars) >= 0)
		{
			if (CurrencyErrs++ == 0)
			{
				Msg += CurrencyMsg;
				Passed = false;
			}
			
			Msg += "      " + CurrencyErrs + ") " + field.name + "\n";
		}
		else
			field.value = fixCurrency(field.value);		// Fix up string to be acceptable to database type (decimal, scale 2)
	}

	// Validate Numeric fields
	for (i = 0; i < NumFields.length; i++)
	{

		field = eval(formName + "." + NumFields[i]);
		field.value = trim(field.value);  
		
		nonValidChars = /[^0-9\s\(\)-]/gi;
		if (field.value.search(nonValidChars) >= 0)
		{
			if (NumericErrs++ == 0)
			{
				Msg += NumericMsg;
				Passed = false;	
			}
			Msg += "      " + NumericErrs + ") " + field.name + "\n";
		}
	}

	// Validate Text fields
	for (i = 0; i < TextFields.length; i++)
	{
		field = eval(formName + "." + TextFields[i]);
		field.value = trim(field.value);
		
		nonValidChars = /[^\w\s\.\~\!\@\#\$\%\^\&\*\#\(\)\`\"\'\:\;\,\/\?\|\\\-\|\[\]\{\}\<\>\+\=]/gi;
		if (field.value.search(nonValidChars) >= 0)
		{
			if (TextErrs++ == 0)
			{
				Msg += TextMsg;
				Passed = false;	
			}
			
			Msg += "      " + TextErrs + ") " + field.name + "\n";
		}
	}

	// Validate Email fields
	for (i = 0; i < EmailFields.length; i++)
	{
		field = eval(formName + "." + EmailFields[i]);
		field.value = trim(field.value);
		
		nonValidChars = /[^\da-zA-Z-_\.\@]/gi;
		uncleanAtPre = /[^\w]\@/gi;
		uncleanAtPost = /\@[^\da-zA-Z]/gi;
		duplicateAt = /[\w\.]*\@[\w\.]*\@/gi;
		duplicateDot = /\.\./gi;
		badAtPosition = field.value.indexOf("@") == 0 || field.value.indexOf("@") > field.value.length - 3;
		noDot = field.value.lastIndexOf(".") < field.value.indexOf("@");
		badDotPosition = field.value.lastIndexOf(".") == field.value.length - 1;
		noAt = field.value.indexOf("@") < 0;
		if ((field.value.length > 0) && ((field.value.search(nonValidChars) >= 0) || (field.value.search(duplicateAt) >= 0) || (field.value.search(uncleanAtPre) >= 0) || 
			(field.value.search(duplicateDot) >= 0) || (field.value.search(uncleanAtPost) >= 0) || badAtPosition || noDot || noAt || badDotPosition))
		{
			if (EmailErrs++ == 0)
			{
				Msg += EmailMsg;
				Passed = false;	
			}
			
			Msg += "      " + EmailErrs + ") " + field.name + "\n";
		}
	}

	// Validate Url fields
	for (i = 0; i < UrlFields.length; i++)
	{
		field = eval(formName + "." + UrlFields[i]);
		field.value = trim(field.value);
		tmp = trim(field.value);
		
		ProtocolSize = tmp.indexOf("://");
		Protocol = "";
		if (ProtocolSize != -1)
		{
			Protocol = tmp.substring(0, ProtocolSize + 3);
			tmp = tmp.substr(ProtocolSize + 3);
		}
			
		HostSize = tmp.indexOf("/");
		Host = "";
		if ( HostSize != -1 )
		{
			Host = tmp.substring(0, HostSize);
			tmp = tmp.substr(HostSize);
		}
		else
		{
			Host = tmp;
			tmp = "";
		}
			
		PathSize = tmp.indexOf("?");
		Path = "";
		if ( PathSize != -1 )
		{
			Path = tmp.substring(0, PathSize);
			tmp = tmp.substr(PathSize);
		}
		else
		{
			Path = tmp;
			tmp = "";
		}
		
		Query = tmp;

		improperProtocol = Protocol.length > 0 && ( Protocol.toString() != "http://" && Protocol.toString() != "https://" ) ? true : false;
			
		validHost = /^[^\.\W]+[\w-\.]+[^\.\W]+$/gi;
		invalidDots = /\.{2,}/gi;
		improperHost = false;
		if( Host.search(validHost) == -1 || Host.search(invalidDots) != -1 || Host.indexOf("://") != -1)
			improperHost = true;
			
		validPath = /^\/[\W\w ]*$/gi;
		improperPath = Path.length > 0 && Path.search(validPath) == -1;

		validQueryChars = /^\?[\w-\.=&%]+/gi;
		improperQuery = Query.length > 0 && Query.search(validQueryChars) == -1;
		
		if ( (field.value.length > 0) && ( improperProtocol || improperHost || improperPath || improperQuery) )
		{
			if (UrlErrs++ == 0)
			{
				Msg += UrlMsg;
				Passed = false;	
			}
			
			Msg += "      " + UrlErrs + ") " + field.name + "\n";
		}
		
		
	}
	
	// Check for Required fields
	for (i = 0; i < RequiredFields.length; i++)
	{
		
		field = eval(formName + "." + RequiredFields[i]);
		
		if (field.type == "select-one")	// a single select object
		{
		//alert("type = select-one");
			if (field.selectedIndex == 0)
			{
				if (RequiredErrs++ == 0)
				{
					Msg += RequiredMsg;
					Passed = false;	
				}
				
				Msg += "      " + RequiredErrs + ") " + field.name + "\n";
			}
				
		}
		else if (field.type == "select-multiple")	// a multiple select object
		{
	//alert("type = select-multiple");
			if (field.selectedIndex == -1)
			{
				if (RequiredErrs++ == 0)
				{
					Msg += RequiredMsg;
					Passed = false;	
				}
				
				Msg += "      " + RequiredErrs + ") " + field.name + "\n";
			}
		}
		else
		{
		//alert("type = normal");
			field.value = trim(field.value);
			
//alert(field.name);
//alert(field.value);

			if (field.value.length == 0)
			{
				if (RequiredErrs++ == 0)
				{
					Msg += RequiredMsg;
					Passed = false;	
				}
				
				Msg += "      " + RequiredErrs + ") " + field.name + "\n";
			}
		}

	}

	
	if (!Passed)
		alert(Msg);
		
	return Passed;
}


/* Trims the white space before and after a string 
 * -----------------------------------------------
 * Input : string 
 * 		   The string to trim
 * Output : string 
 *			The string trimmmed or all pre-fixed and post-fixed white space
 */
function trim(val)
{
	PreWhite = /^\s+/gi;
	PostWhite = /\s+$/gi;
	
	newVal = val.replace(PreWhite, "");
	newVal = newVal.replace(PostWhite, "");
	
	return newVal;
}


/* Fixes up a value as much as possible to conform to a DB decimal type of scale 2
 * -------------------------------------------------------------------------------
 * Input : string
 * 		   The string that represents a a currency value
 * Output : newFloat : string 
 *			A "vanilla" currency value i.e rounded to 2 decimal places and no $
 */
function fixCurrency(val)
{
	nonValidChars = /[^\d\.]/gi;
	
	// Get rid of non-valid chars
	newVal = val.replace(nonValidChars, "");
	
	// Find the first dot (.)
	dotIndex = -1;
	for (i = 0; i < newVal.length; i++)
		if (newVal.charAt(i) == '.')
		{
			dotIndex = i;
			break;
		}
	
	// Get Rid of duplicate dots
	if (dotIndex >= 0)
	{
		tmp1 = newVal.substring(0, dotIndex);
		tmp2 = newVal.subbstr(dotIndex + 1);
		dots = /\./gi;
		tmp2 = tmp2.replace(dots, "");
				
		newVal = tmp1 + "." + tmp2;
	}
	
	return roundOffCents(newVal);
}


/* Rounds off a float value to 2 decimal places
 * -----------------------------------------------
 * Input : string 
 * 		   The string that represents a float
 * Output : newFloat : string 
 *			The string representing the float rounded off to 2 decimal places
 */
function roundOffCents(val)
{
	if(!parseFloat(val))
		return "0";
	
	integer = "";
	decimal = "";
	leftOfDec = true;
	
	for(i = 0; i < val.length; i++)
	{
		if(val.charAt(i) != ".")
			if(leftOfDec)
				integer += val.charAt(i);
			else
				decimal += val.charAt(i);
		else
			leftOfDec = false;
	}

	newint = "";
	newdec = "";
	carry = false;
	
	if(decimal.length > 2)
	{
		if(parseInt(decimal.charAt(2)) >= 5 && parseInt(decimal.charAt(1)) + 1 < 10)
			newdec = decimal.charAt(0) + (parseInt(decimal.charAt(1)) + 1).toString();
		else if(parseInt(decimal.charAt(2)) >= 5 && parseInt(decimal.charAt(1)) + 1 >= 10)
		{
			newdec = "0";
			
			if(parseInt(decimal.charAt(0)) + 1 < 10)
				newdec = (parseInt(decimal.charAt(0)) + 1) + newdec;
			else
			{
				newdec = "0" + newdec;
				carry = true;
			}
		}
		else
			newdec = decimal.substring(0,2);
	}
	else if(decimal.length > 1)
		newdec = decimal;
	else if(decimal.length > 0) 
		newdec = decimal + "0";
	else
		newdec = "00";
	
	if (integer == "")
		integer = "0";
	for(i = integer.length - 1; i >= 0; i--)
	{
		if(carry)
		{
			curr = parseInt(integer.charAt(i)) + 1;
			
			if(curr >= 10)
				newint = newint + "0";
			else
			{
				newint = curr.toString() + newint;
				carry = false;
			}
		}
		else
			newint = parseInt(integer.charAt(i)) + newint;
	}
	
	return (newint + "." + newdec);
}


/* Gets rid of any decimals present in a string
 * -----------------------------------------------
 * Input : string 
 * 		   The string to truncate the decimals
 * Output : newFloat : string 
 *			The string without the decimal places
 */
function chop(val)
{
	if(!parseFloat(val))
		return "0";
		
	if((i = val.indexOf(".")) != - 1)
		return val.substring(0, i);
	else
		return val;
}