/*****************************************
*	VALIDATION OBJECT
*****************************************/

// ctor for validation object
function Validator()
{
	this.validate = vo_validate;
	if( arguments.length == 0 )
		this.errDisplay = "Please correct the following errors and resubmit the form:\n";
	else
		this.errDisplay = arguments[0];
	this.errMessages = "";
}

// the validate function called by the onSubmit of the form
// instance method
function vo_validate( rgoInputFields )
{
	this.errMessages = "";
	this.errMessages += this.errDisplay;
	var oError = new VError();
	bFirstError = false;
	
	for( i = 0; i < rgoInputFields.length; i++ )
	{	
		oError = rgoInputFields[i].check( rgoInputFields[i] );
		if( oError.raised )
		{
			rgoInputFields[i].valid = false;
			if( !bFirstError )
			{
				if (false == rgoInputFields[i].item.isdisabled)
				{
				    if( rgoInputFields[i].check != String(Validator.MHA_RADIO) )
				    {
					    rgoInputFields[i].item.focus();
					    if (rgoInputFields[i].item.type != "select-one" && rgoInputFields[i].item.type != "select-multiple")
						    rgoInputFields[i].item.select();
				    }
				    else
					    rgoInputFields[i].item[0].focus();
                }
                
				bFirstError = true;
			}
			this.errMessages += oError.message + "\n";
		}
		else
			rgoInputFields[i].valid = true;
	}
		
	if( bFirstError )
	{
		alert( this.errMessages );
		return false;
	}
	else
	{
		return true;
	}
}

/*****************************************
*	INPUT FIELD OBJECT
*****************************************/

// ctor for oInputField
function InputField( oItem, sName, oType, bRequired )
{
	assert( oItem, "Item is null" );
	this.required = bRequired;
	this.name = sName;
	this.item = oItem;
	this.check = oType;
	
	//***the following properties are type-specific and set by
	//***optional parameters/arguments
	//set min and max values, and size, to defaults/no setting flags
	this.item2 = null;
	this.name2 = null;
	this.minValue = Validator.NO_MIN_VALUE;
	this.maxValue = Validator.NO_MAX_VALUE;
	this.size = Validator.NO_SIZE;
	this.valid = true;
	//***
	
	//handle optional parameters/arguments
	//error message override is always last parameter
	if( arguments.length >= 5 )
	{
		switch ( String(oType) )
		{
			case String(Validator.MHA_TEXTAREA) :
				this.size = arguments[4];
				if ( arguments.length == 6) this.message = arguments[5];
				break;
			case String(Validator.MHA_NUMTEXTMAX) :
				this.maxValue = arguments[4];
				if ( arguments.length == 6) this.message = arguments[5];
				break;
			case String(Validator.MHA_NUMTEXTMIN) :
				this.minValue = arguments[4];
				if ( arguments.length == 6) this.message = arguments[5];
				break;
			case String(Validator.MHA_NUMTEXTMINMAX) :
			case String(Validator.MHA_MONEY) :
				this.minValue = arguments[4];
				this.maxValue = arguments[5];
				if ( arguments.length == 7) this.message = arguments[6];
				break;
			case String(Validator.MHA_YMM) :
				if ( arguments.length > 5 )
				{
					this.ymmMakes = arguments[4];
					this.message = arguments[5];
				}
				if (arguments[4].type)
					this.ymmMakes = arguments[4];
				else
					this.message = arguments[4];
				break;
			case String(Validator.MHA_DATE) :
				this.month = oItem;
				this.day = arguments[4];
				this.year = arguments[5];
				if ( arguments.length == 8 ) 
				{
					this.maxYear = arguments[6];
					this.message = arguments[7];
				}
				else if (arguments.length == 7 && !isNaN(arguments[6]))
					this.maxYear = arguments[6];
				else
				{
					if (arguments.length == 7)
						this.message = arguments[6];
					var oDate = new Date();
					this.maxYear = oDate.getFullYear();
				}
				break;
			case String(Validator.MHA_COMBOBOX) :
				if ( arguments.length == 5 )
				{
					if (isNaN(arguments[4]))
					{
						this.startIndex = 1; //first valid value is found at this index of array
						this.message = arguments[4];
					}
					else
						this.startIndex = arguments[4];
				}
				else
				{
					this.startIndex = arguments[4];
					this.message = arguments[5];
				}
				break;
			case String(Validator.MHA_COMBOEQUAL) :
			case String(Validator.MHA_GREATER) :
				this.item2 = arguments[4];
				this.name2 = arguments[5];
				if (arguments.length == 7)
					this.message = arguments[6];
				break;
			default :
				this.message = arguments[4];
		}
	}
}

/*****************************************
*	ERROR OBJECT
*****************************************/

// ctor for error object
function VError()
{
	this.message = "";
	this.raised = false;
	this.setError = ve_setError;
}

// error object function to turn on an error
function ve_setError( sMessage )
{
	if( !this.raised )
	{
		this.message = "  - " + sMessage;
		this.raised = true;
	}
}

/*****************************************
*	VALIDATION TYPES
*****************************************/

Validator.MHA_COMBOBOX = vo_isItemSelected;
Validator.MHA_COMBOEQUAL = vo_areSelectValuesDistinct;
Validator.MHA_NAME = vo_nameValidation;
Validator.MHA_EMAIL = vo_emailValidation;
Validator.MHA_TEXT = vo_textValidation;
Validator.MHA_VIN = vo_vinValidation;
Validator.MHA_ZIPCODE = vo_zipcodeValidation;
Validator.MHA_PHONE = vo_phoneValidation;
Validator.MHA_RADIO = vo_radioValidation;
Validator.MHA_YMM = vo_ymmValidation;
Validator.MHA_NUMTEXT = vo_numericValidation;
Validator.MHA_NUMTEXTMIN = vo_numericMinValidation;
Validator.MHA_NUMTEXTMAX = vo_numericMaxValidation;
Validator.MHA_NUMTEXTMINMAX = vo_numericRangeValidation;
Validator.MHA_MONEY = vo_moneyValidation;
Validator.MHA_TEXTAREA = vo_textAreaValidation;
Validator.MHA_DATE = vo_dateValidation;
Validator.MHA_GREATER = vo_compareGreater;

/*****************************************
*	VALIDATION CONSTS
*****************************************/
Validator.NO_MAX_VALUE	= "NOT SET";	//if MaxValue is this, then do not use max property
Validator.NO_MIN_VALUE	= "NOT SET";	//if MinValue is this, then do not use min property
Validator.NO_SIZE		= "NOT SET";	//if size is this, then do not use size property

/*************************************************************************
*	VALIDATION FUNCTIONS
*		all functions must return a string
*		a return value of "" is success
*		anything else is failure, and that string is the error message
*		that gets displayed to the user
*************************************************************************/

// validation function for drop downs to make sure a valid item is selected
function vo_isItemSelected( oInputField )
{
	var oError = new VError();
	if( oInputField.required ) // if its not required than it doesn't matter
	{
		if( oInputField.item.selectedIndex < oInputField.startIndex )
			oError.setError( "Please choose a " + oInputField.name + "." );
	}
	setDefaultError( oInputField, oError );
	return oError;
}

//ensure the selected values for two combo boxes are not the same
function vo_areSelectValuesDistinct( oInputField )
{
	var oError = new VError();
	var sValue1 = oInputField.item.options[oInputField.item.selectedIndex].value;
	var sValue2 = oInputField.item2.options[oInputField.item2.selectedIndex].value;

	if ( oInputField.item.selectedIndex > 0 && oInputField.item2.selectedIndex > 0 )
		if ( sValue1 == sValue2 )
			oError.setError(oInputField.name + " cannot have the same value as " + oInputField.name2 + ".");

	setDefaultError( oInputField, oError );
	return oError;
}

// validation function for a first or last name text box
function vo_nameValidation( oInputField )
{
	leftRightTrim(oInputField);
	var oError = checkIfRequired( oInputField );
	if( oInputField.item.value.search(/[^a-z\x20'-]/i) != -1 ) // stricter than script injection checking function so i dont have to call it
	{
		oError.setError( oInputField.name + " contains invalid characters." );
	}
	else if( oInputField.item.value.length > 255 )
	{
		oError.setError( "The " + oInputField.name + " can contain a maximum of 255 characters." );
	}
	setDefaultError( oInputField, oError );
	return oError;
}

// validation function for email address
function vo_emailValidation( oInputField )
{
	leftRightTrim(oInputField);
	var oError = checkIfRequired( oInputField );
	if( oInputField.item.value.length > 0 &&
		(oInputField.item.value.search(/^[\w-\.]+[@]{1}[\w-\.]+\.[a-z]{2,}$/i) == -1 ||
		oInputField.item.value.indexOf("..") != -1 || 
		oInputField.item.value.indexOf("--")) != -1 ) // dont need script injection checking this is strict enough
	{
		oError.setError( "Invalid E-mail address. E-mail address must be of the format someone@example.com." );
	}
	setDefaultError( oInputField, oError );
	return oError;
}

// validation function for standard text box
function vo_textValidation( oInputField )
{	
	leftRightTrim(oInputField);
	var oError = checkIfRequired( oInputField );
	setDefaultError( oInputField, oError );
	return oError;
}

//validation for VIN, must be 17 alphanumerics, not including I, O or Q
function vo_vinValidation( oInputField )
{
	leftRightTrim(oInputField);
	var oError = checkIfRequired( oInputField );
	if ( oInputField.item.value.length > 0)
	{
		if ( oInputField.item.value.search(/^[a-hj-npr-z\d]{17}$/i) == -1 )
			oError.setError( "Invalid VIN. The VIN must be 17 characters long and contain only letters or numbers; and cannot include the letters \'I\', \'O\', or \'Q\'." );
	}
	setDefaultError( oInputField, oError );
	return oError;
}

// validation function for 5 digit zip code
function vo_zipcodeValidation( oInputField )
{
	leftRightTrim(oInputField);
	var oError = checkIfRequired( oInputField );
	if( oInputField.item.value.length > 0 && oInputField.item.value.search(/^[\d]{5}$/i) == -1 )
		oError.setError( "Invalid ZIP Code.  The ZIP Code must be 5 characters long and contain only numbers." );
	setDefaultError( oInputField, oError );
	return oError;
}

// validation function for phone numbers
// if the phone number is valid it is reformatted to nnn-nnn-nnnn
function vo_phoneValidation( oInputField )
{
	leftRightTrim(oInputField);
	var oError = checkIfRequired( oInputField );
	var sTemp = "";
	if( !oError.raised && oInputField.item.value.length < 1) // Field is empty and not required
		return oError;
	if( !oError.raised )
	{
		// find non white spaces, hyphens, slashes, parentheses, period, plus signs and strip them out
		var sNum = oInputField.item.value;
		var cChar;
		for( g = 0; g < sNum.length; g++ )
		{
			cChar = sNum.charAt( g );
			if( cChar != " " && cChar != "\t" && cChar != "(" && cChar != ")" && cChar != "." && cChar != "-" && cChar != "+" && cChar != "/" && cChar != "\\" )
				sTemp += cChar;
		}
		if( sTemp.search(/^[\d]{10}$/i) == -1 ) // must be 10 digits
		{
			oError.setError( "Invalid phone number." );
		}
	}
	if( !oError.raised )
	{
		var arrInvalidArea = new Array();
		arrInvalidArea[0] = "900";
		arrInvalidArea[1] = "911";
		arrInvalidArea[2] = "411";
		arrInvalidArea[3] = "456";
		arrInvalidArea[4] = "500";
		arrInvalidArea[5] = "555";
		arrInvalidArea[6] = "0";
		arrInvalidArea[7] = "1";
		var arrInvalidPrefix = new Array();
		arrInvalidPrefix[0] = "0";
		arrInvalidPrefix[1] = "1";
		arrInvalidPrefix[2] = "911";
		arrInvalidPrefix[3] = "411";
		arrInvalidPrefix[4] = "555";
		
		for( nCount = 0; nCount <= arrInvalidArea.length; nCount++ )
			if( sTemp.indexOf( arrInvalidArea[ nCount ] ) == 0 )
				oError.setError( "Phone number contains invalid area code." );
		
		if( !oError.raised )
		{		
			for( nCount = 0; nCount <= arrInvalidPrefix.length; nCount++ )
				if( sTemp.indexOf( arrInvalidPrefix[ nCount ], 3 ) == 3)
					oError.setError( "Phone number contains invalid prefix." );
		}
	}
	if( !oError.raised ) // reformat properly
	{
		oInputField.item.value = sTemp;
	}
	setDefaultError( oInputField, oError );
	return oError;	
}

// validator function for a group of radio buttons
function vo_radioValidation( oInputField )
{
	var oError = new VError();
	if( oInputField.required )
	{
		var bChecked = false;
		var i = 0;
		for( i = 0; i < oInputField.item.length; i++ )
		{
			if( oInputField.item[i].checked )
				bChecked = true;
		}
		
		if( !bChecked )
		{
			oError.setError( "A " + oInputField.name + " must be selected." );
		}
	}
	setDefaultError( oInputField, oError );
	return oError;
}

// validation function for drop downs to make sure a valid item is selected for ymm control
function vo_ymmValidation( oInputField )
{
	var oError = new VError();

	if( oInputField.required || (oInputField.ymmMakes && oInputField.ymmMakes.selectedIndex > 0)) // if its not required than it doesnt matter
	{
		if( oInputField.item.options[ oInputField.item.selectedIndex ].value == 0 )
			oError.setError( "Please choose a " + oInputField.name + "." );
	}
	setDefaultError( oInputField, oError );
	return oError;
}

// validation function for text field that should only contain numbers
function vo_numericValidation( oInputField )
{
	leftRightTrim(oInputField);
	oInputField.item.value = oInputField.item.value.replace( /,/g, "" );
	var oError = checkIfRequired( oInputField );
	if (!oError.raised && oInputField.item.value.length == 0)
		return true;
	if( oInputField.item.value.search( /\D/i ) != -1 )
		oError.setError( oInputField.name + " must be in numeric digits." );
	if( !oError.raised && oInputField.item.value.length > 6 )
		oError.setError( oInputField.name + " currently contains " + oInputField.item.value.length + " characters. " + oInputField.name + " has a limit of 6 characters." );
	setDefaultError( oInputField, oError );
	return oError;
}

//validation function for text field that contains an numeric value
//that falls within a certain range.
function vo_numericRangeValidation( oInputField )
{
	leftRightTrim(oInputField);
	oInputField.item.value = oInputField.item.value.replace( /,/g, "" );
	var oError = checkIfRequired( oInputField );
	if (!oError.raised && oInputField.item.value.length == 0)
		return true;
	if( isNaN(oInputField.item.value) )
		oError.setError( oInputField.name + " must be in numeric digits." );
	else
	{
		if (oInputField.item.value.length > 0)
			oInputField.item.value = eval(oInputField.item.value); //strip leading 0s if value not 0
		//check if value falls within valid range
		if ( (Validator.NO_MIN_VALUE != oInputField.minValue) && (oInputField.minValue > oInputField.item.value) ) 
			oError.setError( oInputField.name + " must must have a value greater than or equal to " + oInputField.minValue + "." );
		else if ( (Validator.NO_MAX_VALUE != oInputField.maxValue) && (oInputField.maxValue < oInputField.item.value) ) 
			oError.setError( oInputField.name + " must must have a value less than or equal to " + oInputField.maxValue + "." );
	}
	setDefaultError( oInputField, oError );
	return oError;
}

function vo_moneyValidation( oInputField )
{
	oInputField.item.value = vo_cleanMoney(oInputField.item.value);
	return vo_numericRangeValidation( oInputField );
}

//min/max have wrappers so they validate as different types, do not remove wrappers
function vo_numericMinValidation( oInputField )
{
	return vo_numericRangeValidation( oInputField );
}

// validation function for numerix text box with a max value
function vo_numericMaxValidation( oInputField )
{
	return vo_numericRangeValidation( oInputField );
}

function vo_textAreaValidation( oInputField )
{
	leftRightTrim( oInputField );
	var oError = checkIfRequired( oInputField );
	if( oInputField.item.value.length > oInputField.size )
		oError.setError( oInputField.name + " has a limit of " + oInputField.size + " characters. Current length is " + oInputField.item.value.length + " characters." );
	setDefaultError( oInputField, oError );
	return oError;
}

//is day/month/year a valid date?  
//these text boxes would be ideal candidates to employ vo_NumericKeyDown
function vo_dateValidation( oInputField )
{
	vo_Trim(oInputField.year);
	vo_Trim(oInputField.month);
	vo_Trim(oInputField.day);

	var oError = new VError();
	
	if(oInputField.required)
	{
		if (oInputField.month.value.toUpperCase() == 'MM' || oInputField.month.value.length == 0)
		{
			oInputField.item = oInputField.month;
			oError.setError("Complete " + oInputField.name + " is required.");
		}
		else if (oInputField.day.value.toUpperCase() == 'DD' || oInputField.day.value.length == 0)
		{
			oInputField.item = oInputField.day;
			oError.setError("Complete " + oInputField.name + " is required.");
		}
		else if (oInputField.year.value.toUpperCase() == 'YYYY' || oInputField.year.value.length == 0)
		{
			oInputField.item = oInputField.year;
			oError.setError("Complete " + oInputField.name + " is required.");
		}
	}

	if (!oError.raised)
	{
		var m = oInputField.month.value;
		var d = oInputField.day.value;
		var y = oInputField.year.value;
		
		if (m.length != 0 || d.length != 0 || y.length != 0)
		{
			if (isNaN(m) || m < 1 || m > 12)
			{
				oInputField.item = oInputField.month;
				oError.setError("Invalid month for " + oInputField.name + ".");
			}
			else if (m.length < 2)
				oInputField.month.value = '0' + m;
				
			if (isNaN(d) || d < 1 || d > 31)
			{
				if (!oError.raised)
				{
					oInputField.item = oInputField.day;
					oError.setError("Invalid day for " + oInputField.name + ".");
				}
				else
					oError.message += "\n  - Invalid day for " + oInputField.name + ".";
			}
			else if (d.length < 2)
				oInputField.day.value = '0' + d;
			
			if (isNaN(y) || y < 1880 || y > this.maxYear)
			{
				if (!oError.raised)
				{
					oInputField.item = oInputField.year;
					oError.setError("Invalid year for " + oInputField.name + ".  Year should be four digits; e.g., 1970.");
				}
				else
					oError.message += "\n  - Invalid year for " + oInputField.name + ".  Year should be four digits; e.g., 1970.";
			}
		}
	}
	
	setDefaultError( oInputField, oError );
	return oError;
}

//if item.value > item2.value, then set error
function vo_compareGreater( oInputField )
{
	vo_Trim(oInputField.item);
	vo_Trim(oInputField.item2);
	var oError = new VError();
	var nCompare = vo_CompareItems(oInputField);
	if (2 == nCompare)
	{
		oError.setError(oInputField.name + " may not be greater than " + oInputField.name2 + ".");
		setDefaultError( oInputField, oError );
	}
	return oError;
}

/*****************************************
*	UTILITY FUNCTIONS
*****************************************/

// checks to see if a text item is required is filled out
function checkIfRequired( oInputField )
{
	var oError = new VError();
	if( oInputField.required && oInputField.item.value.length < 1 )
	{	
		oError.setError( oInputField.name + " is currently blank and is required." );
	}
	return oError;
}

function setDefaultError( oInputField, oError )
{
	if( oInputField.message )
		oError.message = oInputField.message;
}

function leftRightTrim( oInputField )
{
	vo_Trim( oInputField.item );
}

function vo_Trim( oItem )
{
	//strip out whitespace at beginning and end
	oItem.value = oItem.value.replace(/^\s+|\s+$/, "");
}

//returns 1 if equal, -1 if unequal (non-numeric), 0 if less than, 2 if greater than
function vo_CompareItems(oInputField)
{
	if (oInputField.item2 == null)
		return -1;

	var oValue1 = oInputField.item.value;
	var oValue2 = oInputField.item2.value;

	if (isNaN(oValue1) || isNaN(oValue2))
		return -1;
	if (parseFloat(oValue1) == parseFloat(oValue2))
		return 1;
	if (parseFloat(oValue1) < parseFloat(oValue2))
		return 0;
	if (parseFloat(oValue1) > parseFloat(oValue2))
		return 2;
}

function vo_cleanMoney(sNum)
{
	if (!sNum) return sNum;
	return sNum.replace(/\$|,/i, "" );
}

function assert( sExpression )
{
}

function ValidateZip (oInputField, onValidAction)
{
    var oValid = new Validator();
    var vncbs_rgoItems = new Array();
    vncbs_rgoItems[0] = new InputField( oInputField, "ZIP Code", Validator.MHA_ZIPCODE, true );

	if (oValid.validate(vncbs_rgoItems))
	{
		onValidAction(oInputField.value);
		return true;
	}
	else
		return false;
}