/*
validate.js: Client-side input validation
Usage:alert

* Within a script tag in the <head> section on the page, include:
* language=JScript src=validate.js

	To apply keystroke constraint, include within the <form id=form1 name=form1> tag:
onKeyPress="return allow_keypress();"
onKeyPress="return top.allow_keypress( event.srcElement, event.keyCode );"

	To apply submit constraint, include within the <form id=form2 name=form2> tag:
onSubmit="return top.allow_submit( this );"

	To apply blur constriction, include within the <input id=text1 name=text1> tags:
onBlur="top.snip( this );"

	Within blur constriction, to return to the faulty text field:
onBlur="if(top.snip( this ) != true) this.focus();"
	...or to select (highlight) the text field upon return:
onBlur="if(top.snip( this ) != true) this.select();"

	Within every element you want to validate, include a type:
<input name=ZIP ZipC>
	Within every element you want to make required, include type 'Reqd':
<input name=SSnum Reqd>

Validation Types:

	numr:	number real, test for real numbers only nonnegative, allows decimals
	numsi:	number allows negatives and positive integers
	numi:	number integer does not allow negative numbers
	Tinyi:	tinyint( 1 byte), this is used to check that the field does not exceed 
			the SQL tinyint datatype limit of 255 and the same checking of numi
	Cur:	Currency, test for two decimal places
	Year:	4-digit positive integers for representing year (should not greater than current year??)
	SmMon:  currency, this is used to check that the field does
			not exceed the SQL Small Money datatype limit of 214,748.3647
			also does the same checking of numr
	
	ZipC:	checks to see if we pass a valid zipcode
	ZipL:	checks to see if we pass a proper list of zipcodes
	num3:   checks to see if we pass 3 numbers
	num4:	checks to see if we pass 4 numbers
	SSN:	checks for valid Social Security Number format
	Date:   checks for valid dates.
			MUST also include one of these secondary types:
			
			Past: when expecting a date in the past
			Futr: when expecting a date in the future
			PorF: when expecting a date that could be Past or Future
	
*/

var server_time = new Date();

//daysInMonth is an array used for checking valid dates
var daysInMonth = new Array();
daysInMonth[1]  = 31;
daysInMonth[2]  = 29;   // must check this
daysInMonth[3]  = 31;
daysInMonth[4]  = 30;
daysInMonth[5]  = 31;
daysInMonth[6]  = 30;
daysInMonth[7]  = 31;
daysInMonth[8]  = 31;
daysInMonth[9]  = 30;
daysInMonth[10] = 31;
daysInMonth[11] = 30;
daysInMonth[12] = 31;


//	L0	Typechecking
function is_str_empty( str )	{ return ! str.match( /\S/ ); }
function is_el_type( el, type )	{ return el.getAttribute( type ) != null; }


//	L1
function type_of_el( el ) {
		if( is_el_type( el, "Date" ) ) { return "Date"; }
	else if( is_el_type( el, "SmMon") ) { return "SmMon"; }
	else if( is_el_type( el, "NumR" ) ) { return "NumR"; }
	else if( is_el_type( el, "NumI" ) ) { return "NumI"; }
	else if( is_el_type( el, "NumL" ) ) { return "NumL"; }
	else if( is_el_type( el, "Date" ) ) { return "Date"; }
	else if( is_el_type( el, "ZipC" ) ) { return "ZipC"; }
	else if( is_el_type( el, "Feet" ) ) { return "Feet"; }
	else if (is_el_type( el, "ZipL" ) ) { return "ZipL"; }
	else if (is_el_type( el, "NumSI") ) { return "NumSI"; }
	else if (is_el_type( el, "Year") ) { return "Year"; }
	else if (is_el_type( el, "num3") ) { return "num3"; }
	else if (is_el_type( el, "num4") ) { return "num4"; }
	else if (is_el_type( el, "Tinyi") ) { return "Tinyi"; }
	else if (is_el_type( el, "SSN") ) { return "SSN"; }
	else if (is_el_type( el, "Cur") ) { return "Cur"; }
	
	
	else return "";
}


//  L1	The bad_* functions:  single datatype checking
function bad_NumI( str ) {	return isNaN( parseInt( str ) ); }
function bad_NumR( str ) {	return isNaN( parseFloat( str ) ); }
function bad_Date( str ) {	return ! Date.parse( str ); }
function bad_ZipC( str ) {	return ! str.match( /(\d{5}(-\d{4})?)/ ); }
function bad_NumL( str ) {	return ! str.match( /(\d+(\s*,\s*\d+)*)/ ); }
function bad_Feet( str ) {  return ! (str.match( /((\d+)\s*'\s*(\d+))/ ) || str.match( /(\d+(\.\d*)?|(\.\d+))/ )); }
function bad_ZipL( str ) {	return ! str.match( /(\d+(\s*,\s*\d+)*)/ ); }
function bad_NumSI( str ) {  return ! str.match( /(^[-]?[0-9]*$)/ ); }
function bad_SmMon( str ) { return isNaN( parseFloat( str ) ); }
function bad_Year( str ) { return ! str.match ( /(\d{4})/ ); }
function bad_num3( str ) { return ! str.match ( /(\d{3})/ ); }
function bad_num4( str ) { return ! str.match ( /(\d{4})/ ); }
function bad_Tinyi( str ) {	return isNaN( parseInt( str ) ); }
function bad_SSN( str ) { return ! str.match( /(\d{3}-\d{2}-\d[4])/ ); }
function bad_Cur( str ) {	return isNaN( parseFloat( str ) ); }


//	L1	The snip_* functions:  try to find a qualifying value in the input
function snip_NumI( el ) {
	return el.value.match( /(\d+)/ ) ? RegExp.$1 : false;
}

function snip_NumR( el ) {
	return el.value.match( /(\d+(\.\d*)?|(\.\d+))/ ) ? RegExp.$1 : false;
}

// NumSI: positive and negetive integer without leading 0s 
function chop_leading_0s (el) {
	if (el.value.match(/(^[-][0]+[0-9]+$)/))
	{
		if (el.value.match(/(^[-][0]+$)/))
			return 0;
		else 
		{
			var result = el.value.replace(/(^[-]*[0]+)/, '' );
			return "-" + result;
		}
	}
	else if (el.value.match(/(^[0]+[0-9]+$)/))
	{
		var result = el.value.replace(/(^[-]*[0]+)/, '' );
		return result;
	}
	else if (el.value.match(/('')/))
	{
		return 0;
	}
	else
	{ 
		return el.value;
	}
}	

function snip_NumSI ( el ) {
	//return el.value.match( /(^[-]?[0-9]*)/)? RegExp.$1 : false;
	// no leading 0s
	if (el.value.match (/(^[-]?[1-9]+[0-9]*$)/)) 
		return el.value.match (/(^[-]?[1-9]+[0-9]*$)/) ? RegExp.$1 : false;
	else if (el.value.match ( /(^[-]?[0-9]*$)/))
		return el.value.match( /(^[-]?[0-9]*$)/)? chop_leading_0s( el) : false;
}

function snip_Year ( el ) {
	var thisyear = el.value;
	if( thisyear.match(/^\d\d\d\d\d+/) || thisyear.match(/^\d\d\d$/) || thisyear.match(/^\d$/) ) {
		//alert( thisyear + " is an invalid year" );
		return false;
	}
	else {
		if ( thisyear.match (/^\d\d$/) ) {
			if ( thisyear > 20 ) {
				var fullyear = "19" + thisyear;
			}
			else {
				var fullyear = "20" + thisyear;
			}
			return fullyear;
		}
		else {	
			return el.value.match( /(\d{4})/ ) ? RegExp.$1 : false;
		}
	}
}

function snip_Tinyi(el) {                       
	var tiny = el.value; 
	if (tiny <= 255	) {
		return tiny;     
	}                 
	else {                
		return false;     
	}                                       
}                         

function daysInFebruary( year ) {
    // February has 29 days in any year evenly divisible by four,
    // EXCEPT for centurial years not also divisible by 400.
    var fourth   = !(year % 4);
    var century  = !(year % 100);
    var fourhund = !(year % 400);

    return (fourth && !century || fourhund) ? 29 : 28;
}


function isDate( year, month, day ) {
	//  General check
	switch (month)
		{
			case "01":
					if( day > 31 )  return false;
					break;
			case "03":
					if( day > 31 )  return false;
					break;
			case "04":
					if( day > 30 )  return false;
					break;
			case "05":
					if( day > 31 )  return false;
					break;
			case "06":
					if( day > 30 )  return false;
					break;
			case "07":
					if( day > 31 )  return false;
					break;
			case "08":
					if( day > 31 )  return false;
					break;
			case "09":
					if( day > 30 )  return false;
					break;
			case "10":
					if( day > 31 )  return false;
					break;
			case "11":
					if( day > 30 )  return false;
					break;
			case "12":
					if( day > 31 )  return false;
					break;
		}
			
    //if( day > daysInMonth[month] ) return false;

	//  Leap year check
    if( (month == 2) && (day > daysInFebruary( year )) ) return false;

    return true;
}

function snip_SmMon(el) {
	
	var money = el.value;
	if (money <= 214748.36) {
		return money;
		}
	else {
		return false;
		}
	
}

function snip_date_replace( el ) {
    var chdate = /(\d\d?)/g;
	var result = el.value.match( chdate );
	var month = result[0];
	var day = result[1];

	if (result[2] > 20) {
		var year = "19"+result[2];
	} else {
		var year = "20"+result[2];
	}
	var enter_date = new Date( year, month - 1 , day );

	if( !(isDate( year, result[0], result[1] )) ) {
			el.value = result[0]+"/"+result[1]+"/"+year;
			alert( el.value+" is an invalid date" );
			return false;
	}

    if( is_el_type( el, "Futr" ) ) {
		if( Date.parse(enter_date) < Date.parse(server_time) ) {
			el.value = month+"/"+day+"/"+year;
			alert("Your date here must be a date in the future");
			return false;
		}
	}
	else if( is_el_type( el, "Past" ) ) {
		if( Date.parse(enter_date) > Date.parse(server_time) ) {
			el.value = month+"/"+day+"/"+year;

			alert("Your date here must be today's date or a date in the past");

			return false;
		}
	}
	else { }  //  Okay to have neither Past nor Futr

	if( result[2] != null ) {
		if( result[2].match( /\d{2}/ ) ) {
			return month+"/"+day+"/"+year;
		}
	}
	return false;
}



function snip_Date( el ) {

	var chdate = /(\d+)/g;
	var result = el.value.match( chdate );
	var enter_date = new Date( result[2], result[0] - 1, result[1] );

	if( (result[2] == null) || (result[0] == null) || (result[1] == null) ) {
		alert( el.value+" is an invalid date" );
		return false;
	}

	if( result[2].match(/\d\d\d\d\d+/) || result[2].match(/^\d\d\d$/) || result[2].match(/^\d$/) ) {
		alert( result[2] + " is an invalid year" );
		return false;
	}

	if( (result[0] < 1) || (result[0] > 12) ) {
		alert( result[0] + " is an invalid month" );
		return false;
	}

	if( (result[1] < 1) || (result[1] > 31) ) {
		alert( result[1] + " is an invalid day" );
		return false;
	}

	var year = result[2];
	if( year.match( /\d{4}/ ) ) {
		if( is_el_type( el, "Porf" ) ) { } // Okay to have neither Past nor Futr
		
		else if( is_el_type( el, "Futr" ) ) {
			if( Date.parse(enter_date) < Date.parse(server_time) ) {
				alert("Your date here must be a date in the future");
				return false;
			}
		}
		else if( is_el_type( el, "Past" ) ) {
			if( Date.parse(enter_date) > Date.parse(server_time) ) {
				alert("Your date here must be today's date or a date in the past");
				return false;
			}
		}
		else { }  //  Okay to have neither Past nor Futr

		if( !(isDate( year, result[0], result[1])) ) {
			alert( el.value+" is an invalid date" );
			return false;
		}
	}

	return el.value.match( /(\d\d?\/\d\d?\/\d{4})/ ) ? RegExp.$1 : snip_date_replace(el);
}

function snip_ZipC( el ) {
	return el.value.match( /(\d{5}(\-\d{4})?)/ ) ? RegExp.$1 : false;
}

function snip_num3( el ) {
	return el.value.match( /(\d{3})/ ) ? RegExp.$1 : false;
}

function snip_num4( el ) {
	return el.value.match( /(\d{4})/ ) ? RegExp.$1 : false;
}

function snip_ZipL( el ) {
	var val = el.value;
	var flag = true;
	var myval = "";
	var str = "";
	var zipcode = new Array();

	str = str + "," + val + ",";
	zipcode = str.split(/\s*,\s*/);

	for( mycount= 0; mycount < zipcode.length; mycount++ ) {
		str2 = zipcode[mycount];
		if( (str2.length > 5 ) || (str2.search(/\s+/)!= -1) ) {
			alert(zipcode[mycount] + " is an invalid zip code.");
			return false;
		}
		if( zipcode[mycount]!= null ) {
			myval = myval + zipcode[mycount] + ",";
		}
	}
	if( myval != "" ) {
		myval2 = myval.slice(0,-1);
		return myval2;
	}
}


function snip_NumL( el ) {
	var val = el.value;
	val = val.replace( /[ ,+]+/g, ", " );
	return val.match( /(\d+(\s*,\s*\d+)*)/ ) ? RegExp.$1 : false;
}


function snip_Feet( el ) {
	var val = el.value;
	var finalFeet;

	if( val.match( /^\s*$/ ) ) {
		return;
	}
	else if( val.match( /(\d+)\s*'\s*(\d+)/ ) ) {
		//  Can we get feet'inches" ?
		var feet = parseInt( RegExp.$1 );
		var inch = parseInt( RegExp.$2 );

		finalFeet = feet + ( inch / 12 );
	}
	else if( val.match( /(\d+(\.\d*)?|(\.\d+))/ ) ) {
		var finalFeet = RegExp.$1;
	}
	else {
		alert( "'" + val + "'" + " does not appear to be a valid number." );
		return;
	}

	return Math.round( 100 * finalFeet ) / 100;
}

function snip_SSN( el ) {
	return el.value.match( /(\d{3}-\d{2}-\d{4})/ ) ? RegExp.$1 : false;
}

function snip_Cur( el ) {
	var val = el.value;
	var amt;
	
	if( val.match( /(\d+(\.\d{1,2})?|(\.\d+))/ ) ) {
		amt = RegExp.$1;

		if( val.indexOf( "." ) != -1 ) {
			amt = amt.concat( "00" );
			return amt.match( /(\d+(\.\d{2}))/ ) ? RegExp.$1 : false;
		}
		else {
			return amt;
		}
	}
	else {
		return;
	}
	
	//return el.value.match( /(\d+(\.\d{1,2})?|(\.\d+))/ ) ? RegExp.$1 : false;
}


//	L2	Element-scope data type validation
function type_failed( el ) {
	var val = el.value;
	var type = type_of_el( el );
	var bad;

	if( type.length ) {
		eval( "bad = bad_" + type + "( val );" );
	}

	return bad? type : "";
}


//	L2	Descriptive "bad value" alert
function alert_val_type( val, type ) {
	var name = "value";
	switch( type ) {
		case "NumI": name = "whole number";	  break;
		case "NumR": name = "decimal number"; break;
		case "Year": name = "year";	  break;
		case "ZipC": name = "ZIP code";       break;
		case "NumL": name = "number list";    break;
		case "Date": name = "date";           break;
		case "Feet": name = "length";         break;
		case "ZipL": name = "Zip Code list";  break;
		case "num3": name = "Format";    break;
		case "num4": name = "Format";    break;
		case "SmMon": name = "Small Money Field";break;
		case "NumSI": name = "integer"; break;
		case "Tinyi": name = "Tiny Int Field. The number is too big"; break;
		case "SSN": name = "Social Security Number"; break;
		case "Cur": name = "dollar value"
	}
	alert( "Your entry:  \"" + val + "\" is not a valid " + name + "." );
}


//	L3	Make field contents fit type, if possible
function snip( el ) {
	if( is_str_empty( el.value ) ) { return true; }
	
	var type = type_of_el( el );
	eval( "var rv = snip_" + type + "( el );" );

	if( rv )                  { el.value = rv; return true; }
	else if( type == "Date" ) { return false; }
	else if( type == "ZipL" ) { return false; }
	else                      { alert_val_type( el.value, type ); return false; }
}


//  L3	Form-scope validation on submit
function allow_submit( the_form ) {
	var each_elem;
	var fail_type = "";
	var fail_reqd = "";
	var tot_elems = the_form.elements.length;
	var leasetype;
	var minvalue;
	var maxvalue;

	//  Check every element of the form
	for( each_elem=0; each_elem < tot_elems; each_elem++ ) {
		var cur_elem = the_form.elements[each_elem];




		//  If the field is empty
		if( is_str_empty( cur_elem.value ) ) {
			//  If it's required, that's an error
			if( is_el_type( cur_elem, "Reqd" ) ) {
				fail_reqd = cur_elem.title;
				if( !fail_reqd.length ) {
					fail_reqd = cur_elem.name;
				}
				alert( fail_reqd + " is a required field." );
				break;
			}
		}

		//  Else there's something to check for
		else {
			fail_type = type_failed( cur_elem );
			if( fail_type.length ) {
				alert_val_type( cur_elem.value, fail_type );
				break;
			}
		}

	}
	//  If a rule was broken, the form fails
	if( fail_reqd.length || fail_type.length ) {
		cur_elem.focus();
		return false;
	}
	else {
		return true;
	}
}


//	L3	Form-scope constraint on keypress
function allow_keypress( el, keyC ) {
	//var el = event.srcElement;
	var ok_keys = "";
	var CR = unescape("%0d");	//	Add <cr> to NumL for ie5 and quickMLS.

	//  If NumI is in the <input id=text2 name=text2> tag, it must be an integer.  Et c.
	if     ( is_el_type( el, "NumI" ) ) { ok_keys = "0123456789";  }
	else if( is_el_type( el, "NumR" ) ) { ok_keys = "0123456789."; }
	else if( is_el_type( el, "Num3" ) ) { ok_keys = "0123456789"; }
	else if( is_el_type( el, "Num4" ) ) { ok_keys = "0123456789"; }
	else if( is_el_type( el, "SmMon" ) ) { ok_keys = "0123456789."; }
	else if( is_el_type( el, "NumL" ) ) { ok_keys = "0123456789,+ " + CR; }
	else if( is_el_type( el, "ZipC" ) ) { ok_keys = "0123456789-"; }
	else if( is_el_type( el, "Date" ) ) { ok_keys = "0123456789/"; }
	else if( is_el_type( el, "Feet" ) ) { ok_keys = "0123456789.'\"" }
	else if( is_el_type( el, "ZipL" ) ) { ok_keys = "0123456789,+ " + CR; }
	else if( is_el_type( el, "NumSI") ) { ok_keys = "0123456789-"; }
	else if( is_el_type( el, "Tinyi" ) ) { ok_keys = "0123456789";  }
	//  If it is a constrained field, test it
	if( ok_keys.length ) {
		return ok_keys.indexOf( String.fromCharCode( keyC ) ) > -1;
	}
	return true;
}


//	L3	Set multi-select default
function ResetMultiSelect( chobj, defaultText ) {
	if( chobj.value == "" ) {
		chobj.options[0].selected = true;
	}

	if( chobj.options[chobj.selectedIndex].text == defaultText ) {
		for( var i=1; i < chobj.length; i++ ) {
			if( chobj.options[i].selected ) {
				chobj.options[0].selected = false;
			}
		}
	}
}

