function Validate (Datatype, decimals, MinVal, MaxVal, MinValWarn, MaxValWarn, NotNull, tRegExp, Custom_ErrorMsg, NoCheckFutureDates, IsSearchMode)
// 10/24 - this function used to be in VBScript until we needed to add extra parameters to it, without having to break every
// previously generated form. javascript functions can take an arbitrary number of parameters. So this is simply a wrapper function
// that calls Validate_Core (the original function, renamed && modified to take extra parameters).
{ 
   if (arguments.length == 7 )
   { NoCheckFutureDates= false;
     Custom_ErrorMsg = "";
     tRegExp = "";
   }
  Validate_Core (Datatype, decimals, MinVal, MaxVal, MinValWarn, MaxValWarn, NotNull, tRegExp, Custom_ErrorMsg, NoCheckFutureDates,IsSearchMode);
}

function Nz(Value, ValueifNull)
{ if (Value == null) 
 return  ValueifNull
else
return  Value;
}

var SkipFlag	;
var CurrentElem, CurVal, Bypass_On_focus;
var DirtyBit ;//as Boolean, checks if changes have (!been saved
var Invalid_Data; //as Boolean
var SavedColor;  //saves the background color of the current field, 8/20/99

function Data_Error(SrcElem,ErrMsg)
{ SrcElem.focus;
SrcElem.value="";;
Invalid_Data=true;
alert ( ErrMsg);
}

//the next routine is similar to above, except that the SrcElem is (!blanked out
function Data_Error_DoNotBlank (SrcElem,ErrMsg)
{ SrcElem.focus;
Invalid_Data=true;
alert ( ErrMsg);
}

function getClockTime(dt)
{ return dt.getHours().toString() + ":"  +  dt.getMinutes().toString() + ":" +  dt.getSeconds().toString()
}

function Internat_Date(dt) 
{ //International Date function
 //ensures that the result is always returned as mmm-dd-yy, so works across both Britain, Canada and US
var mnth, dy ,yr, tm;
var MonthNames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" ];
if (dt == null || dt =="") return "";
mnth = MonthNames[dt.getMonth()]; dy = dt.getDate() ; yr = dt.getFullYear();
tm = getClockTime (dt);
if (tm == "0:0:0" ) 
	tm="" ;
else
	tm=" " & tm;
return  mnth + "-" + dy + "-" + yr + tm
}

function testRegExp ( RgXp,  InputDat)
{
//This function tests the string InputDat against the RegExp pattern RgXp.
//It returns true only if there is a single match, whose length is equal to the length of the input string
var Rg= new RegExp(RgXp);
var Mc = InputDat.match(Rg);
if (Mc == null) return false;
return (Mc[0].length == InputDat.length);
}

function IsNumeric(sText)
{
   var ValidChars = "0123456789.";
   var IsNumber=true;
   var Char;
   for (i = 0; i < sText.length && IsNumber == true; i++) 
      { 
      Char = sText.charAt(i); 
      if (ValidChars.indexOf(Char) == -1) 
         {
         IsNumber = false;
         }
      }
   return IsNumber;
   }
   
function round(number, rlength) {
var pw = Math.pow(10,rlength);
	var newNumber = Math.round(number * pw ) / pw;
	return newNumber;
}

function getYear(d) { 
  return (d < 1000) ? d + 1900 : d;
  }

var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
function LZ(x) {return(x<0||x>9?"":"0")+x}

// ------------------------------------------------------------------
// isDate ( date_string, format_string )
// Returns true if date string matches format of format string and
// is a valid date. Else returns false.
// It is recommended that you trim whitespace around the value before
// passing it to this function, as whitespace is NOT ignored!
// ------------------------------------------------------------------
function isDate(val) {
		return(parseDate(val) !=null);
	}

// -------------------------------------------------------------------
// compareDates(date1,date1format,date2,date2format)
//   Compare two date strings to see which is greater.
//   Returns:
//   1 if date1 is greater than date2
//   0 if date2 is greater than date1 of if they are the same
//  -1 if either of the dates is in an invalid format
// -------------------------------------------------------------------
function compareDates(date1,dateformat1,date2,dateformat2) {
	var d1=getDateFromFormat(date1,dateformat1);
	var d2=getDateFromFormat(date2,dateformat2);
	if (d1==0 || d2==0) {
		return -1;
		}
	else if (d1 > d2) {
		return 1;
		}
	return 0;
	}

// ------------------------------------------------------------------
// formatDate (date_object, format)
// Returns a date in the output format specified.
// The format string uses the same abbreviations as in getDateFromFormat()
// ------------------------------------------------------------------
function formatDate(date,format) {
	format=format+"";
	var result="";
	var i_format=0;
	var c="";
	var token="";
	var y=date.getYear()+"";
	var M=date.getMonth()+1;
	var d=date.getDate();
	var E=date.getDay();
	var H=date.getHours();
	var m=date.getMinutes();
	var s=date.getSeconds();
	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
	// Convert real date parts into formatted versions
	var value=new Object();
	if (y.length < 4) {y=""+(y-0+1900);}
	value["y"]=""+y;
	value["yyyy"]=y;
	value["yy"]=y.substring(2,4);
	value["M"]=M;
	value["MM"]=LZ(M);
	value["MMM"]=MONTH_NAMES[M-1];
	value["NNN"]=MONTH_NAMES[M+11];
	value["d"]=d;
	value["dd"]=LZ(d);
	value["E"]=DAY_NAMES[E+7];
	value["EE"]=DAY_NAMES[E];
	value["H"]=H;
	value["HH"]=LZ(H);
	if (H==0){value["h"]=12;}
	else if (H>12){value["h"]=H-12;}
	else {value["h"]=H;}
	value["hh"]=LZ(value["h"]);
	if (H>11){value["K"]=H-12;} else {value["K"]=H;}
	value["k"]=H+1;
	value["KK"]=LZ(value["K"]);
	value["kk"]=LZ(value["k"]);
	if (H > 11) { value["a"]="PM"; }
	else { value["a"]="AM"; }
	value["m"]=m;
	value["mm"]=LZ(m);
	value["s"]=s;
	value["ss"]=LZ(s);
	while (i_format < format.length) {
		c=format.charAt(i_format);
		token="";
		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
			token += format.charAt(i_format++);
			}
		if (value[token] != null) { result=result + value[token]; }
		else { result=result + token; }
		}
	return result;
	}
	
// ------------------------------------------------------------------
// Utility functions for parsing in getDateFromFormat()
// ------------------------------------------------------------------
function _isInteger(val) {
	var digits="1234567890";
	for (var i=0; i < val.length; i++) {
		if (digits.indexOf(val.charAt(i))==-1) { return false; }
		}
	return true;
	}
function _getInt(str,i,minlength,maxlength) {
	for (var x=maxlength; x>=minlength; x--) {
		var token=str.substring(i,i+x);
		if (token.length < minlength) { return null; }
		if (_isInteger(token)) { return token; }
		}
	return null;
	}
	
// ------------------------------------------------------------------
// getDateFromFormat( date_string , format_string )
//
// This function takes a date string and a format string. It matches
// if the date string matches the format string, it returns the 
// getTime() of the date. if it does not match, it returns 0.
// ------------------------------------------------------------------
function getDateFromFormat(val,format) {
	val=val+"";
	format=format+"";
	var i_val=0;
	var i_format=0;
	var c="";
	var token="";
	var token2="";
	var x,y;
	var now=new Date();
	var year=now.getFullYear();
	var month=now.getMonth()+1;
	var date=1;
	var hh=0;
	var mm=0;
	var ss=0;
	var ampm="";
	
	while (i_format < format.length) {
		// Get next token from format string
		c=format.charAt(i_format);
		token="";
		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
			token += format.charAt(i_format++);
			}
		// Extract contents of value based on format token
		if (token=="yyyy" || token=="yy" || token=="y") {
			if (token=="yyyy") { x=4;y=4; }
			if (token=="yy")   { x=2;y=2; }
			if (token=="y")    { x=2;y=4; }
			year=_getInt(val,i_val,x,y);
			if (year==null) { return 0; }
			i_val += year.length;
			if (year.length==2) {
				if (year > 70) { year=1900+(year-0); }
				else { year=2000+(year-0); }
				}
			}
		else if (token=="MMM"||token=="NNN"){
			month=0;
			for (var i=0; i<MONTH_NAMES.length; i++) {
				var month_name=MONTH_NAMES[i];
				if (val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) {
					if (token=="MMM"||(token=="NNN"&&i>11)) {
						month=i+1;
						if (month>12) { month -= 12; }
						i_val += month_name.length;
						break;
						}
					}
				}
			if ((month < 1)||(month>12)){return 0;}
			}
		else if (token=="EE"||token=="E"){
			for (var i=0; i<DAY_NAMES.length; i++) {
				var day_name=DAY_NAMES[i];
				if (val.substring(i_val,i_val+day_name.length).toLowerCase()==day_name.toLowerCase()) {
					i_val += day_name.length;
					break;
					}
				}
			}
		else if (token=="MM"||token=="M") {
			month=_getInt(val,i_val,token.length,2);
			if(month==null||(month<1)||(month>12)){return 0;}
			i_val+=month.length;}
		else if (token=="dd"||token=="d") {
			date=_getInt(val,i_val,token.length,2);
			if(date==null||(date<1)||(date>31)){return 0;}
			i_val+=date.length;}
		else if (token=="hh"||token=="h") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<1)||(hh>12)){return 0;}
			i_val+=hh.length;}
		else if (token=="HH"||token=="H") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<0)||(hh>23)){return 0;}
			i_val+=hh.length;}
		else if (token=="KK"||token=="K") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<0)||(hh>11)){return 0;}
			i_val+=hh.length;}
		else if (token=="kk"||token=="k") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<1)||(hh>24)){return 0;}
			i_val+=hh.length;hh--;}
		else if (token=="mm"||token=="m") {
			mm=_getInt(val,i_val,token.length,2);
			if(mm==null||(mm<0)||(mm>59)){return 0;}
			i_val+=mm.length;}
		else if (token=="ss"||token=="s") {
			ss=_getInt(val,i_val,token.length,2);
			if (ss==null||(ss<0)||(ss>59)){return 0;}
			i_val+=ss.length;}
		else if (token=="a") {
			if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
			else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
			else {return 0;}
			i_val+=2;}
		else {
			if (val.substring(i_val,i_val+token.length)!=token) {return 0;}
			else {i_val+=token.length;}
			}
		}
	// if there are any trailing characters left in the value, it doesn't match
	if (i_val != val.length) { return 0; }
	// Is date valid for month?
	if (month==2) {
		// Check for leap year
		if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year
			if (date > 29){ return 0; }
			}
		else { if (date > 28) { return 0; } }
		}
	if ((month==4)||(month==6)||(month==9)||(month==11)) {
		if (date > 30) { return 0; }
		}
	// Correct hours value
	if (hh<12 && ampm=="PM") { hh=hh-0+12; }
	else if (hh>11 && ampm=="AM") { hh-=12; }
	var newdate=new Date(year,month-1,date,hh,mm,ss);
	return newdate.getTime();
	}

// ------------------------------------------------------------------
// parseDate( date_string [, prefer_euro_format] )
//
// This function takes a date string and tries to match it to a
// number of possible date formats to get the value. It will try to
// match against the following international formats, in this order:
// y-M-d   MMM d, y   MMM d,y   y-MMM-d   d-MMM-y  MMM d
// M/d/y   M-d-y      M.d.y     MMM-d     M/d      M-d
// d/M/y   d-M-y      d.M.y     d-MMM     d/M      d-M
// A second argument may be passed to instruct the method to search
// for formats like d/M/y (european format) before M/d/y (American).
// Returns a Date object or null if no patterns match.
// ------------------------------------------------------------------
function parseDate(val) {
	var preferEuro=(arguments.length==2)?arguments[1]:false;
	generalFormats=new Array('y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d');
	monthFirst=new Array('M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d');
	dateFirst =new Array('d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M');
	var checkList=new Array('generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst');
	var d=null;
	for (var i=0; i<checkList.length; i++) {
		var l=window[checkList[i]];
		for (var j=0; j<l.length; j++) {
			d=getDateFromFormat(val,l[j]);
			if (d!=0) { return new Date(d); }
			}
		}
	return null;
	}
 
function 	DaysAdd (whatDate, Increment)
// adds a number of days to a date
{ var oneDay = 86400000; // this many milliseconds in a day
return whatDate.getTime + (Increment * oneDay);
} 

function Validate_Contents_SearchMode(Text, Datatype)
    //this routine validates the contents of numeric or date fields
// to determine if they are valid for search patterns
    //the strategy is to use regular expressions
 {   var relOpSt = "(=|<>|>|>=|<|<=)?"
  var SrcElem = window.event.srcElement;;
    if (Text == "")  return true;
    switch (Datatype)
     { case "S" :
       case "T": 
      case "C" :
        return true; // no validation needed
      case "I" :
        if (! testRegExp(relOpSt + "[0-9]+", Text)) 
        { Data_Error(SrcElem, "Invalid Integer search expression")
           return false;
         }
      case "R":
        if (!  testRegExp(relOpSt + "[0-9]+(\.[0-9]+)?", Text)) 
        { Data_Error(SrcElem, "Invalid format for Real Number Search Expression");
        return false;
        }
      case "D":
      {  var pos, dval ;
        dval = Text;
        pos = 0
        while (! (dval.charAt(pos) >= "0" && dval.charAt(pos) <= "9") )
          pos ++
        var datePart  = dval.substring(pos); 
        if ( (pos) > 0 ) { 
          var relop = dval.substring(0, pos);
          if (!testRegExp (relOpSt, relop))
           Data_Error(SrcElem, "Invalid operator before date")
           return false;
         }
        if (! isDate (datePart)){
          Data_Error(SrcElem, "Invalid Date (must be mm/dd/yy or mm/dd/yyyy)")
          return false;
        }
        }
    }
    return true;
}

function Validate_Core (Datatype, decimals, MinVal, MaxVal, MinValWarn, MaxValWarn, NotNull, 
   vRegExp, Custom_ErrorMsg, NoCheckFutureDates,IsSearchMode)
{
var MAXSTRLEN = 255;
var Value, SrcElem, tag, month, datearr, cmonth, pos;
var NullMinVal, NullMaxVal, NullMaxWarn, NullMinWarn; //all Booleans
//prevents this script from being fired after a Save && Resume button has been clicked
//"\n" = chr(13) + chr(10)
 SrcElem = window.event.srcElement;
Invalid_Data=false; 
//added to accomodate KM code 3/2 escapepressed
if (SkipFlag) {
	SkipFlag=false;
	SrcElem.style.backgroundcolor=SavedColor; 
	return;
}
if (Datatype != "D" && SavedColor == "gold" && SrcElem.value != "") {
	Data_Error (SrcElem, "Non-date fields previously designated as missing values must remain empty.");
	return;
}
if (! Invalid_Data && SrcElem.value==CurVal) { //no change in the contents of the field
	  SrcElem.style.backgroundcolor=SavedColor; 
	  return;
}
DirtyBit = true;
Value = Nz(SrcElem.value,"");
 var SrcName = SrcElem.name;
if ( Value == ""  && NotNull == true) {
  if ( SrcName.indexOf("_EE_") ==  -1 && SrcName.indexOf ("_ES_")== -1) { 
        //note - the LIKE operator does (!exist in VBscript
	      	Data_Error (SrcElem, "This value cannot be left blank.")
	       	return;
       //note - blank date errors are caught later in any case
	   } //srcName
} // value == ""

if (Value=="") {
  SrcElem.style.backgroundcolor=SavedColor;
  Invalid_Data=false;
  //Call Change(srcElem)
  return;
}  // value == ""
if (IsSearchMode) return Validate_Contents_SearchMode(Value, Datatype)
NullMinVal = (MinVal == null); 
NullMaxVal = (MaxVal == null);
NullMaxWarn = (MaxValWarn  == null); 
NullMinWarn = (MinValWarn == null);
if (Datatype == "C"  || Datatype == "T") return;  //C is used for choice sets, instance labels etc. which don//t require range validation
	switch (Datatype) {
	 case "S" : //added 9/19/02
	    if (Value.length > MAXSTRLEN) {
			     Data_Error (SrcElem, "The number of characters in this field cannot exceed " + MAXSTRLEN);
			    return; 
	    };
	    if (Value.indexOf ( "\n") != -1)  {			SrcElem.value = value.replace ("\n"," ")	    };
	    break;
	    // case S
	 case "I": 
	    if (Value.match(/^\d+$/) ) {
	        Value = parseInt(Value);
	        SrcElem.value = Value;
	       }
	    else {
	        Data_Error (SrcElem,  "Invalid format for Integer"); return;
	      } 
	    break;
	    // case I
	 case "R":
	    if (IsNumeric(Value) ) {
	        if (decimals == null) decimals = 2;
	        SrcElem.value = round(Value, decimals);
	        Value = parseFloat(Value); // will this have the same stupid conversion bug?
	       } 
	    else if (Value.indexOf ("%") != -1) {
	        Data_Error (SrcElem, "When changing a value containing //%//, please enter a new number without the % - e.g., to enter 25%, type in 0.25")
	       return; 
	       } 
	    else {
	         Data_Error (SrcElem,  "Invalid format for Real Number")
	         return;
	       }
	    break;
	    //case R
	 case "D" : 
		  //ani 7/14/2003 to allow "t", "t-1", "n" shortcuts to date fields
		  //for today, yesterday && now, etc.
		  var lendt, Increm, IncremSt;
		  lenDt = Value.length;
		  switch (Value.substring(0,1)) {
			  case "T": case "t" :
				  Increm = 0;
				  if (LenDt > 1) { 
					  Incremst = value.substring(1) //this should be of the form +n || -n
					  if (IsNumeric(Incremst)) { 
					  Increm = parseInt(Incremst)				
				  value = DaysAdd(Increm, Date()) //this is short format
			     }	
			  } // lendt > 1
			  break;	//case T, t
			  case "N": 	  case "n":
				  Value = Date();
			  } // switch	
	   Value = parseDate(Value);
    if (Value == null) {
       Data_Error (SrcElem,  "Invalid format for Date (must be mm/dd/yy, mm/dd/yyyy || mmm-dd-yyyy)")
       return;
      }
   if ( Value < new Date(1800, 1, 1) ) {
       Data_Error (SrcElem,  "Sorry, the earliest date you can enter is 1/1/1800.");
	      return;
     }
  var Now = new Date();
   if (!NoCheckFutureDates && Value.getTime() >Now.getTime()) { 
      Data_Error (SrcElem,  "The value entered cannot be a Date in the future.")
    	return;
   } // future dates
	    //7/17/02- special tests for an end-of-event date-
	 if (SrcName.indexOf( "_EE_") != -1) {
			 var StartEventFieldName, StartEventFieldValue;
			 StartEventFieldName = replace(SrcElem.name, "_EE_", "_ES_")
					StartEventFieldValue = window.document.all(StartEventFieldName).Value
			//two possibilities - if end-of-event is (!blank && start-of-event is blank { Error
			//if end-of-event date (!greater than the start of event date, ehen error
			if ( StartEventFieldValue == "") {
				Data_Error (SrcElem, "You cannot enter an end-of-event date if the start-of-event date is blank.")
				return;
			}
			StartEventFieldValue = parseDate(StartEventFieldValue);
			if (StartEventFieldValue > Value) {
				Data_Error (SrcElem, "End-of-event date-time must be greater than start-of-event date-time.")
				return;
			}
	} //special tests for end-of-event
} //switch
// now test for range errors	
	 SrcElem.value = Value;
   	//handle the range error logic
	if (NullMinVal && NullMaxVal && NullMaxWarn && NullMinWarn && !(Datatype == "S" && vRegExp != "") ) {
        SrcElem.style.backgroundcolor=SavedColor;
        Invalid_Data=false;
  } 

  if (Invalid_Data) return;
	if (! (NullMinVal && NullMaxVal)) { // test for absolute range errors 	
  if (Datatype == "D" ){
     if (!NullMinVal) MinVal = parseDate(MinVal) ; 
      if (!NullMaxVal) MaxVal = parseDate(MaxVal) ; 
     }  // date
  else if ( Datatype == "I" || Datatype =="R") {
     if (!NullMinVal) MinVal = parseFloat(MinVal) ; 
     if (!NullMaxVal)  MaxVal = parseFloat(MaxVal) ; 
  }	 // numeric
 if (NullMinVal && Value > MaxVal) {
	  Data_Error (SrcElem,  "value should (!be greater than " + MaxVal); 
	  return;
    } //lower bound only
 else if (NullMaxVal  && Value < MinVal) {
	    Data_Error (SrcElem,  "value should (!be less than " + MinVal)
	    return;
    } //upper bound only
 else
    if (Value < MinVal || Value > MaxVal) {
	    Data_Error (SrcElem,  "value should be between " + MinVal + " && " + MaxVal)
	    return;
    } // both bounds violated
 } // absolute range error check
	// now handle the warning logic- do (!exit, pass to Change anyway
	if (!(NullMaxWarn && NullMinWarn) ){
 if (Datatype =="D") {
   if (!NullMinWarn)  MinValWarn = parseDate(MinValWarn);
    if (!NullMaxWarn)  MaxValWarn = parseDate(MaxValWarn) ;
  } 
else if (Datatype == "I" || Datatype =="R" ) {
   if (!NullMinWarn)  MinValWarn = parseFloat(MinValWarn);
    if (!NullMaxWarn) MaxValWarn = parseFloat(MaxValWarn) ;
}		
	 if (NullMinWarn && Value > MaxValWarn)   
	 alert ( "Warning; value is expected to be less than " + MaxValWarn);
	   // upper bound warning only
	 else if (NullMaxWarn  && Value < MinValWarn)  
	   alert ( "Warning; value is expected to be greater than " + MinValWarn); 
	    // lower bound warning only
	 else if ( Value < MinValWarn || Value > MaxValWarn) 
	    alert ( "Warning; value expected to be between " + MinValWarn + " && " + MaxValWarn )
	   // both warning bounds
	} // end of warning logic

// regular expression check
if (vRegExp != "") {
  //perform regular Expression Checks- make sure that the entire string matches the RegExp
  if (!testRegExp (vRegExp, Value)) Data_Error_DoNotBlank  (SrcElem, Custom_ErrorMsg);
} // regular expression check 
SrcElem.style.backgroundcolor=SavedColor;
Invalid_Data=false;

//if (Datatype== "D") { SrcElem.value = Internat_Date(Value) }

} // end function