/* Validator Methods Below ******************************************/

fValidate.prototype.blank = function()
{
	if ( this.typeMismatch( 'text' ) ) return;
	if ( this.isBlank() )
	{
		this.throwError( 'Please enter the ' + this.elem.fName );
	}
}

/********************************************************************/
fValidate.prototype.number = function( type, lb, ub )
{
	if ( this.typeMismatch( 'text' ) ) return;
	var num  = ( type == 0 ) ? parseInt( this.elem.value, 10 ) : parseFloat( this.elem.value );
	lb       = this.setArg( lb, false );
	ub       = this.setArg( ub, false );
	var fail = Boolean( isNaN( num ) || num != this.elem.value );
	if ( !fail )
	{
		switch( true )
		{
			case ( lb != false && ub != false ) : fail = !Boolean( lb <= num && num <= ub ); break;
			case ( lb != false ) : fail = Boolean( num < lb ); break;
			case ( ub != false ) : fail = Boolean( num > ub ); break;
		}
	}
	if ( fail )
	{
		this.throwError( 'The number you entered for ' + this.elem.fName + ' is not valid' );
		return;
	}
	this.elemPass = true;
}

/********************************************************************/
fValidate.prototype.numeric = function( len )
{
	if ( this.typeMismatch( 'text' ) ) return;
	len = this.setArg( len, '*' );
	var regex = new RegExp( ( len == '*' ) ? "^\\d+$" : "^\\d{" + parseInt( len, 10 ) + "}\\d*$" );
	if ( !regex.test( this.elem.value ) )
	{
		var msg = ( len == '*' ) ?
			'Only numeric values are valid for the ' + this.elem.fName :
			'A minimum of ' + len + ' numeric values are required for the ' + this.elem.fName;
		this.throwError( msg );		
	}
}

/********************************************************************/
fValidate.prototype.length = function( len )
{
	if ( this.typeMismatch( 'text' ) ) return;
	if ( this.elem.value.length < parseInt( len, 10 ) )
	{
		this.throwError( 'The ' + this.elem.fName + ' must be at least ' + len + ' characters long' );
	}
}

/********************************************************************/
fValidate.prototype.equalto = function( oName )
{
	if ( this.typeMismatch( 'text' ) ) return;
	var otherElem = this.form.elements[oName];
	if ( this.elem.value != otherElem.value )
	{
		this.throwError( this.elem.fName + " must be equal to " + otherElem.fName );
	}
}

/********************************************************************/
fValidate.prototype.ssn = function()
{
	if ( this.typeMismatch( 'text' ) ) return;
	if ( !( /^\d{3}\-\d{2}\-\d{4}$/.test( this.elem.value ) ) )
		this.throwError( 'You need to enter a valid Social Security Number.\nYour SSN must be entered in \'XXX-XX-XXXX\' format.' );
}

/********************************************************************/
fValidate.prototype.select = function()
{
	if ( this.typeMismatch( 's1' ) ) return;
	if ( this.elem.selectedIndex == 0 )
	{
		this.throwError( 'Please select a valid option for ' + this.elem.fName );
	}
}

/********************************************************************/
fValidate.prototype.select2 = function()
{
	if ( this.typeMismatch( 's1' ) && this.typeMismatch( 'sm' ) ) return;
	var count = 0;	
	for ( var opt, i = 0; ( opt = this.elem.options[i] ); i++ )
		if ( opt.selected ) count++;
	if (count == 0) {
		this.throwError( 'Please select a valid option for ' + this.elem.fName );
	}
}

/********************************************************************/
fValidate.prototype.selectm = function( minS, maxS )
{
	if ( this.typeMismatch( 'sm' ) ) return;
	var count = 0;
	if ( maxS == 999 || maxS == '*' ) maxS = this.elem.length;
	for ( var opt, i = 0; ( opt = this.elem.options[i] ); i++ )
		if ( opt.selected ) count++;

	if ( count < minS || count > maxS )
	{
		this.throwError( 'Please select between ' + minS + ' and ' + maxS + ' options for ' + this.elem.fName + '.\nYou currently have ' + count + ' selected' );
	}
}

/********************************************************************/
fValidate.prototype.cazip = function()
{
	if ( this.typeMismatch( 'text' ) ) return;
	if ( !( /^[A-Z][0-9][A-Z] [0-9][A-Z][0-9]$/.test( this.elem.value ) ) )
	{
		this.throwError( 'Please enter a valid postal code.' );
	}
}

/********************************************************************/
fValidate.prototype.email = function( level )
{
	if ( this.typeMismatch( 'text' ) ) return;
	if ( typeof level == 'undefined' ) level = 0;
	var emailPatterns = [
		/.+@.+\..+$/i,
		/^\w.+@\w.+\.[a-z]+$/i,
		/^\w[-_a-z~.]+@\w[-_a-z~.]+\.[a-z]{2}[a-z]*$/i,
		/^\w[\w\d]+(\.[\w\d]+)*@\w[\w\d]+(\.[\w\d]+)*\.[a-z]{2,7}$/i
		];
	if ( ! emailPatterns[level].test( this.elem.value ) )
	{
		this.throwError( 'Please enter a valid email address' );
	}	
}	

/********************************************************************/
fValidate.prototype.ip = function( portMin, portMax )
{
	if ( this.typeMismatch( 'text' ) ) return;
	portMin = this.setArg( portMin, 0 );
	portMax = this.setArg( portMax, 99999 );
	if ( !( /^\d{1,3}(\.\d{1,3}){3}(:\d+)?$/.test( this.elem.value ) ) )
	{
		this.throwError( "Please enter a valid IP" );
	}
	else
	{
		var part, i = 0, parts = this.elem.value.split( /[.:]/ );
		while ( part = parts[i++] )
		{
			if ( i == 5 ) // Check port
			{
				if ( part < portMin || part > portMax )
				{
					this.throwError( 1, 1, "The port number you specified, " + part + ",  is out of range.\nIt must be between " + portMin + " and " + portMax );
				}
			}
			else if ( part < 0 || part > 255 )
			{
				this.throwError( "Please enter a valid IP" );
			}
		}
	}
}

/********************************************************************/
fValidate.prototype.decimal = function( lval, rval )
{
	if ( this.typeMismatch( 'text' ) ) return;
	var regex = '';
	if ( lval != '*' ) lval = parseInt( lval, 10 );
	if ( rval != '*' ) rval = parseInt( rval, 10 );
	
	if ( lval == 0 )
		regex = "^\\.[0-9]{" + rval + "}$";	
	else if ( lval == '*' )
		regex = "^[0-9]*\\.[0-9]{" + rval + "}$";
	else if ( rval == '*' )
		regex = "^[0-9]{" + lval + "}\\.[0-9]+$";
	else
		regex = "^[0-9]{" + lval + "}\\.[0-9]{" + rval + "}$";
		
	regex = new RegExp( regex );

	if ( !regex.test( this.elem.value ) )
	{
		this.throwError( 'The data you entered,' + this.elem.value + ' is not valid.  Please re-enter the ' + this.elem.fName );
	}	
}

/********************************************************************/
fValidate.prototype.decimalr = function( lmin, lmax, rmin, rmax )
{
	if ( this.typeMismatch( 'text' ) ) return;
	lmin = ( lmin == '*' )? 0 : parseInt( lmin, 10 );
	lmax = ( lmax == '*' )? '': parseInt( lmax, 10 );
	rmin = ( rmin == '*' )? 0 : parseInt( rmin, 10 );
	rmax = ( rmax == '*' )? '': parseInt( rmax, 10 );
	var	decReg = "^[0-9]{"+lmin+","+lmax+"}\\.[0-9]{"+rmin+","+rmax+"}$"
	var regex = new RegExp(decReg);
	if ( !regex.test( this.elem.value ) )
	{
		this.throwError( this.elem.fName + ' is not a valid. Please re-enter.' );
	}
	return true;
}

/********************************************************************/
fValidate.prototype.zip = function( sep )
{
	if ( this.typeMismatch( 'text' ) ) return;
	sep = this.setArg( sep, "- " );
	var regex = new RegExp( "^[0-9]{5}(|[" + sep.toPattern() + "][0-9]{4})?$" );
	if ( ! regex.test( this.elem.value ) )
	{
		this.throwError( 'Please enter a valid 5 or 9 digit Zip code.' );
	}
}

/********************************************************************/
fValidate.prototype.phone = function( format )
{
	if ( this.typeMismatch( 'text' ) ) return;
	format       = this.setArg( format, 0 );
	var patterns = [
		/^(\(?\d\d\d\)?)?[ -]?\d\d\d[ -]?\d\d\d\d$/,
		/^(\(\d\d\d\) )?\d\d\d[ -]\d\d\d\d$/
		];
	if ( !patterns[format].test( this.elem.value ) )
	{
		return this.throwError( "Please enter a valid phone number" + ( ( format == 1 ) ? " plus Area Code." : " - seven or ten digits." ) );
	}
}

/********************************************************************/
fValidate.prototype.checkbox = function( minC, maxC )
{
	if ( this.typeMismatch( 'cb' ) ) return;
	if ( this.elem == this.form.elements[this.elem.name] && !this.elem.checked )
	{
		this.throwError( "Please check " + this.elem.fName + " before continuing" );
	}
	else
	{
		this.elem = this.form.elements[this.elem.name];
		var len   = this.elem.length;
		var count = 0;
		
		if ( maxC == 999 || maxC == '*' )
		{
			maxC == len;
		}
		var i = len;
		while( i-- > 0 )
		{
			if ( this.elem[i].checked )
			{
				count++;
			}
		}
		if ( count < minC || count > maxC )
		{
			this.throwError( 'Please select between ' + minC + ' and ' + maxC + ' options for ' + this.elem[0].fName + '.\nYou currently have ' + count + ' selected' );
		}			
	}
}

/********************************************************************/
fValidate.prototype.radio = function()
{
	if ( this.typeMismatch( 'rg' ) ) return;
	this.elem2 = this.form.elements[this.elem.name];
	
	for ( var i = 0; i < this.elem2.length; i++ )
	{
		if ( this.elem2.item( i ).checked )
		{
			return;
		}
	}
	this.throwError( 'Please select an option for ' + this.elem2[0].fName );
}

/********************************************************************/
fValidate.prototype.eitheror = function()
{
	if ( this.typeMismatch( 'hidden' ) ) return;
	var arg, i  = 0;
	var fields  = new Array();
	var nbCount = 0;
	
	while ( arg = arguments[i++] )
	{
		fields.push( this.form.elements[arg].fName );
		if ( !this.isBlank( arg ) )
		{
			nbCount++;
		}
	}
	if ( nbCount != 1 )
	{
		this.throwError( "One and only one of the following fields must be filled in:\n\n" + fields.join( "\n" ) );
	}
}

/********************************************************************/
fValidate.prototype.atleast = function()
{
	if ( this.typeMismatch( 'hidden' ) ) return;
	var arg, i  = 1;
	var fields  = new Array();
	var nbCount = 0;
	
	while ( arg = arguments[i++] )
	{
		fields.push( this.form.elements[arg].fName );
		if ( !this.isBlank( arg ) )
		{
			nbCount++;
		}
	}
	if ( nbCount < arguments[0] )
	{
		this.throwError( "At least " + arguments[0] + " of the following fields must be filled in:\n\n" + fields.join( "\n" ) + "\n\nYou have only " + nbCount + " filled in" );
	}
}

/********************************************************************/
fValidate.prototype.file = function( extensions, cSens )
{
	if ( this.typeMismatch( 'file' ) ) return;
	cSens = ( cSens ) ? "" : "i";
	var regex = new RegExp( "^.+\\.(" + extensions.replace( /,/g, "|" ) + ")$", cSens );
	if ( ! regex.test( this.elem.value ) )
	{
		this.throwError( "The file must be one of the following types:\n" + extensions.replace( /,/g, "\n" ) + "\nNote: File extention may be case-sensitive" );
	}
}

/********************************************************************/
fValidate.prototype.custom = function( flags )
{
	if ( this.typeMismatch( 'text' ) ) return;
	flags     = ( flags ) ? flags.replace( /[^gim]/ig ) : "";
	var regex = new RegExp( this.elem.getAttribute( this.config.pattern ), flags );
	if ( !regex.test( this.elem.value ) )
	{
		this.throwError( this.elem.fName + " is invalid." );
	}	
}

/********************************************************************/
fValidate.prototype.cc = function()
{
	if ( this.typeMismatch( 'text' ) ) return;
	var typeElem = this.form.elements[this.config.ccType];
	if ( !typeElem )
	{
		this.devError( "You must include a SELECT item with Credit Card type choices" )
		return;
	}
	var ccType   = typeElem.options[typeElem.selectedIndex].value.toUpperCase();
	var types    = {
		'VISA'    : /^4\d{12}(\d{3})?$/,
		'MC'      : /^5[1-5]\d{14}$/,
		'DISC'    : /^6011\d{12}$/,
		'AMEX'    : /^3[4|7]\d{13}$/,        
		'DINERS'  : /^3[0|6|8]\d{12}$/,
		'ENROUTE' : /^2[014|149]\d{11}$/,
		'JCB'     : /^3[088|096|112|158|337|528]\d{12}$/,
		'SWITCH'  : /^(49030[2-9]|49033[5-9]|49110[1-2]|4911(7[4-9]|8[1-2])|4936[0-9]{2}|564182|6333[0-4][0-9]|6759[0-9]{2})\d{10}(\d{2,3})?$/,
		'DELTA'   : /^4(1373[3-7]|462[0-9]{2}|5397[8|9]|54313|5443[2-5]|54742|567(2[5-9]|3[0-9]|4[0-5])|658[3-7][0-9]|659(0[1-9]|[1-4][0-9]|50)|844[09|10]|909[6-7][0-9]|9218[1|2]|98824)\d{10}$/,
		'SOLO'    : /^(6334[5-9][0-9]|6767[0-9]{2})\d{10}(\d{2,3})?$/
		};
	if ( typeof types[ccType] == 'undefined' )
	{
		this.devError( "Credit Card type (" + ccType + ") not found." );
		return;
	}
	this.elem.value = this.elem.value.replace( /\D/g, "" );
	if ( !types[ccType].test( this.elem.value ) || !this.elem.value.luhn() )
	{
		this.throwError( 'The ' + this.elem.fName + ' you entered is not valid. Please check again and re-enter' );
	}
}

String.prototype.luhn = function()
{
	var i = this.length;
	var checkSum = "", digit;
	while ( digit = this.charAt( --i ) )
	{
		checkSum += ( i % 2 == 0 ) ? digit * 2 : digit;
	}
	checkSum = eval( checkSum.split('').join('+') );
	return ( checkSum % 10 == 0 );
}

/* Old code below for reference

function validateAlnum(formObj, minLen, tCase, numbers, spaces, puncs) {
	var objName = formatName(formObj);
	if (fv['bok'] && checkBlank(formObj))
		{ fv['bok']=false; return true; }
	var arrE = new Array();
	arrE[0] = (minLen == "*") ? "None" : minLen;
	var okChars = "";
	switch (tCase.toUpperCase()) {
		case 'U'	:	okChars = "A-Z"; arrE[1] = "UPPER"; break
		case 'L'	:	okChars = "a-z"; arrE[1] = "lower"; break;
		case 'C'	:	okChars = "A-Z][a-z"; if (minLen != "*") minLen--; arrE[1]="Initial capital"; break;
		default		:	okChars = "a-zA-Z"; arrE[1]="Any"; break;
		}
	if (parseInt(numbers)) { okChars += "0-9"; arrE[2] = "Yes"; } else arrE[2] = "No";
	if (parseInt(spaces)) { okChars += " "; arrE[3] = "Yes"; } else arrE[3] = "No";
	if (puncs == "all") { okChars += "."; arrE[4] = "All"; }
	if (puncs == "all") { okChars += puncStr("!@#$%^&*()_+-={}|[]:\";'<\\>?,.?~`"); arrE[4] = "All"; }
	else if (puncs == "none") arrE[4] = "None";
	else { okChars += puncStr(puncs); arrE[4] =  puncStr(puncs).replace(/\\/g,""); }
	var length = (minLen == "*") ? "+" : "{"+minLen+",}";
	var alnumReg = "^["+okChars+"]"+length+"$";
	var regex = new RegExp(alnumReg);
	if (!regex.test(formObj.value) ) {
		return errorProcess2(formObj,1,1,"The data you entered ("+formObj.value+") does not match the requested format for the "+objName+"\nMinimum Length: "+arrE[0]+"\nCase: "+arrE[1]+"\nNumbers allowed: "+arrE[2]+"\nSpaces allowed: "+arrE[3]+"\nPunctuation characters allowed: "+arrE[4]);
		}
	return true;
	}	

function validateMoney(formObj, ds, grp, dml) {
	var objName = formatName(formObj);	
	if (fv['bok'] && checkBlank(formObj))
		{ fv['bok']=false; return true; }
	
	var moneySyntax;
	if (ds && grp && dml)		// Dollar sign, grouping, and decimal
		{ moneyReg = "^\\$(?:(?:[0-9]{1,3},)(?:[0-9]{3},)*[0-9]{3}|[0-9]{1,3})(\\.[0-9]{2})$";	moneySyntax = "$XX,XXX.XX"; }
	if (ds && grp && !dml)		// Dollar sign and grouping
		{ moneyReg="^\\$(?:(?:[0-9]{1,3},)(?:[0-9]{3},)*[0-9]{3}|[0-9]{1,3})$"; moneySyntax="$XX,XXX"; }
	if (ds && !grp && dml)		// Dollar sign and decimal
		{ moneyReg="^\\$[0-9]*(\\.[0-9]{2})$"; moneySyntax="$XXXXX.XX"; }
	if (!ds && grp && dml)		// Grouping and decimal
		{ moneyReg="^(?:(?:[0-9]{1,3},)(?:[0-9]{3},)*[0-9]{3}|[0-9]{1,3})(\\.[0-9]{2})?$"; moneySyntax="XX,XXX.XX"; }
	if (ds && !grp && !dml)		// Dollar sign only
		{ moneyReg="^\\$[0-9]*$"; moneySyntax="$XXXXX"; }
	if (!ds && grp && !dml)		// Grouping only
		{ moneyReg="^(?:(?:[0-9]{1,3},)(?:[0-9]{3},)*[0-9]{3}|[0-9]{1,3})$"; moneySyntax="XX,XXX"; }
	if (!ds && !grp && dml)		// Decimal only
		{ moneyReg="^[0-9]*(\\.[0-9]{2})$"; moneySyntax="XXXXX.XX"; }
	if (!ds && !grp && !dml)	// No params set, all special chars become optional
		{ moneyReg="^\\$?(?:(?:[0-9]{1,3},?)(?:[0-9]{3},?)*[0-9]{3}|[0-9]{1,3})(\\.[0-9]{2})?$"; moneySyntax="[$]XX[,]XXX[.XX]"; }
	var regex = new RegExp(moneyReg);
	if (!regex.test(formObj.value)) {
		return errorProcess2(formObj,1,1,formObj.value+' does not match the required format of '+moneySyntax+' for '+objName+'.');
		}
	return true;
	}

function validateSelectI(formObj, indexes) {
	var objName = formatName(formObj);
	var arrIndexes =indexes.split(/[,]/);
	var selectOK = true;
	for (var i=0; i<arrIndexes.length; i++)
		if (formObj.selectedIndex == arrIndexes[i])
			selectOK = false;
	if (!selectOK) {
		return errorProcess2(formObj,0,1,"Please select a valid option for "+objName);
		}
	return true;
	}
		

function validateDate(formObj, dateStr, delim, code, specDate) {
	var objName = formatName(formObj);	
	if (fv['bok'] && checkBlank(formObj))
		{ fv['bok']=false; return true; }
	
	var months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
	var vDate = formObj.value;
	var mPlace = dateStr.indexOf("m");
	var dPlace = dateStr.indexOf("d");
	var yPlace = dateStr.indexOf("y");
	var yLength = dateStr.lastIndexOf("y") - yPlace + 1;
	var dateReg = dateStr.replace(/\w/g,"\\d");
	delim = puncStr(delim);
	dateReg = dateReg.replace(/-/g,"[" + delim + "]");
	dateReg = "^" + dateReg + "$";
	var day = vDate.substring(dPlace, dPlace+2);
	var month = vDate.substring(mPlace, mPlace+2);
	var year = vDate.substring(yPlace, yPlace + yLength);
	var regex = new RegExp(dateReg);
	var d = new Date(months[month-1] + " " + day + ", " + year);
	var today = (specDate == 'today') ? new Date() : new Date(specDate);
	today.setHours(0);
	today.setMinutes(0);
	today.setSeconds(0);
	today.setMilliseconds(0);
	var timeDiff = today.getTime() - d.getTime();
	var dateOk = false;
	switch (parseInt(code)) {
		case 1 : // Before specDate
			dateOk = (timeDiff > 0);
			break;
		case 2 : // Before or on specDate
			dateOk = ((timeDiff + 86400000) > 0);
			break;
		case 3 : // After specDate
			dateOk = (timeDiff < 0);
			break;
		case 4 : // After or on specDate
			dateOk = ((timeDiff - 86400000) < 0);
			break;
		default : dateOk = true;
		}
	if (!regex.test(vDate) || d == 'NaN' || !dateOk) {
		return errorProcess2(formObj,1,1,"Please enter a valid date");
		}
	return true;
	}
	
*/

function groupAlert()
{
	var a = new Array();
	for ( var i = 0; ( arg = arguments[i] ); i++ )
		a.push( arg );
	alert( a.join( "\n" ) );
}

function addDebug( val )
{
	window.status += val + " | ";
}
