
/* formValidation.js */
/* Written from use with genericForm.asp only */
// How to call validation functions...
// validate(nStr, ElementName, ElementLabel)

// Where nStr is a string containing a number referring to the type of validation (Listed below)
// Where ElementName is the name of the form input element
// Where Label is the name used to identify the element to the user.

// Validation Codes.

// A negative validation code indicates a manditory form element.
//    1  = GENERAL (All characters allowed)
//    2  = ALPHA-NUMERIC (Only Characters in the Charset and whitespace are permitted)
//    3  = ALPHA ONLY (Upper or lowercase)
//    4  = NUMBER (positive integer)
//    5  = NUMBER (real)
//    6_min_max = NUMBER (range between min and max)
//    7  = EMAIL (a@b.c)
//    8  = ONLY ALPHA-NUMERIC (Leters and numbers only - no whitespace or symbols)
//    9  = DATE (Any UTC date, of format: YYYY-MM-DDThh:mm:ss.sssZ, or normal date of format: DD/MM/YYYY)
//    10 = TIME (TIMES of format: HH:MM, or, HH:MM:SS )
//    11 = FILE
//    12_XX/XX/XXXX_X = DATE (Check date is BEFORE, AFTER or EQUAL the specified date.  Check switch below.)
//    13 = currency

// Special Validation Codes.

//    50 = Compares 2 strings (Used with password when confirm = true)
//    51 = Compares a string with an array of strings. (The array must be the same name as the textbox)
//    55 = UK NATIONAL INSURANCE NUMBER (AB 12 34 56 C)
//    60 = DATE-TIME (ONLY DD/MM/YYYY:HH:MM:SS, SQL uses date-times of this format but uses whitespace at subString(10,11))

// VARIABLE DECLARATIONS
var specialchars = "";
var namechars        = "'"
var basicpunc        = ".,?£$()-!+=#@_&^~*{}[];:|\/";
var numbers          = "0123456789";
var alpha            = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
var whitespace       = " \t\n\r";
var invalidpathchars = "\"/*?<>|"

// This function is designed to check, and maybe change, the format of a submitted date
// you can have a standard ISO or UTC date (YYYY-MM-DDThh:mm:ss.sssZ), or all the possible permutations
// of a character from 'basicPunc' as the delimiter, with the date in british format.
function isDate(date) {
    var allValid;
    var delimitType = "";
    var dArr = new Array();
    if ((date.length == 25) && (date.substring(24,25) == "Z") && (date.substring(10,11) == "T")) {
      allValid = true;
    } else if ((date.length == 9) || (date.length == 10) || (date.length == 8)) {
      for(var aa=0;aa<basicpunc.length;aa++) {
         var ab=(aa+1);
         if(date.indexOf(basicpunc.substring(aa,ab))!= Number(-1)) {
         delimitType = basicpunc.substring(aa,ab);
         }
      }
      if (delimitType != "") {
         dArr = date.split(delimitType);
         if ((Number(dArr[0])<= 31) && (Number(dArr[1])<= 12) && (Number(dArr[2])<=9999) && (Number(dArr[0])> 0) && (Number(dArr[1])> 0) && (Number(dArr[2] > 0)))  {
            allValid = true;
         } else {
            allValid = false;
         }
      } else {
         allValid = false;
      }
    }
return allValid;

}
// Times can be passed as HH:MM, or HH:MM:SS, the delimiter MUST be a colon.
function isTime(time) {
   var arrTime = time.split(":")
   
   if( arrTime.length < 2 || arrTime.length > 3 ) return false;
   
   if( Number(arrTime[0]) >= 24 ) return false; // HOURS
   if( Number(arrTime[1]) >= 60 ) return false; // MINUTES
   if( arrTime.length == 3 ){  if( Number(arrTime[2]) >= 60 ) return false; } // SECONDS (optional)

   return true;
}
// Abuses the isDate and isTime functions because SQL server doesn't use any 'standard' delimiters
// to split the date from the time.  It's lovely really.
function isDateTime(dateTime) {
   var allValid = false;
   if(dateTime.length==19) {
      var strDate = dateTime.subSting(0,10);
      var strTime = dateTime.subString(11,19);
      if ((isDate(strDate)) && (isTime(strTime))) {
         allValid =  true;
      }
   }
return allValid;
}
// isEmpty returns true if TestString is empty or whitespace.
function isEmpty(TestString) {
	if ((TestString == null) || (TestString.length == 0)) return true
	else {
		for (i = 0; i < TestString.length; i++) {
		    // Check that current character isn't whitespace.
		    var c = TestString.charAt(i);
		    if (whitespace.indexOf(c) == -1) return false;
		}
	}
	return true;
}
// isValid returns true if all characters in TestString are also in ValidChars
function isValid(TestString, ValidChars) {
	for (i = 0; i < TestString.length; i++) {
		var c = TestString.charAt(i);
		if (ValidChars.indexOf(c) == -1) return false;
	}
	return true;
}
function isInRange(testValue, minValue, maxValue) {
	if(testValue == NaN) return false;
	if((testValue >= minValue) && (testValue <= maxValue)) return true;
	else return false;
}

function constructDate(year, month, day)  {
//pass me the individual elements and I give you the full date you can perform operations against
//it appears the date object numbers it's months 0-11, so i have reduced the month variable - MW 03/03/2003
  var name = new Date(parseFloat(year), parseFloat(month-1), parseFloat(day));
  return name
// Oh dear, it appears that abs only returns a positive number!!!!
// Removed for error553, JMM 3/3/04, God knows why it was there in the first place.  Answers on a postcard...
//  var objMath = Math.abs(name.valueOf());
//  return objMath
}

function isEmail(TestString){
	i = 1;
    StringLength = TestString.length;
    while ((i < StringLength) && (TestString.charAt(i) != "@")){ i++ }
    if ((i >= StringLength) || (TestString.charAt(i) != "@")) return false;
    else i += 2;
    while ((i < StringLength) && (TestString.charAt(i) != ".")){ i++ }
    if ((i >= StringLength - 1) || (TestString.charAt(i) != ".")) return false;
    else return true;
}
// Don't you just love regular expressions?  The format of a NI is specified in TSR2524. JMM 13/10/04
// Altered to check 1st and 2nd letters individually as wasnt working to the spec. AH 23/11/04
function isValidNINumber(NINum){
	/*
	NINum = NINum.replace(/ /g,"");
	var NIreg = /^\D{2}\d{6}(\D$|$)/i // Simple Format of a NI number (aa111111a).
	*/
	var NIformatReg = /^\D{2} ?\d{2} ?\d{2} ?\d{2} ?([ABCD]$|$)/i; // NI Format (allows spaces)
	var NIinvalidFirstLetters = /^(gb|bg|nk|kn|tn|nt|zz)/i; // NI first two letters
	var NIinvalidFirst = /^[DFIOQUV]/i; // NI first letter
	var NIinvalidSecond = /^[DFIOQUV]|[DFIOQUV]/i; // NI second letter
	
	if(!NIformatReg.test(NINum)) return false;
	if(NIinvalidFirstLetters.test(NINum)) return false;
	if(NIinvalidFirst.test(NINum)) return false;
	if(NIinvalidSecond.test(NINum)) return false;
	
	return true;
}
// This function is used to show 'non-mandatory' validation errors
function displaySubErrorMsg() {
// alert(SubErrorMsg)
	ProblemField = document.forms[0].elements[SubErrorMsg[0]]
	ProblemField.className = "formItemInvalid";

	if(ProblemField.type != "hidden")
	   {
	   ProblemField.select();
	   ProblemField.focus();
	   }
	alert("The value in \""+ SubErrorMsg[1] +"\" field is invalid because\n"+ SubErrorMsg[2]);
}
// ----------------------------------------- VALIDATE STARTS HERE -----------------
function validate(CodeInput, ElementName, ElementLabel) {
	CodeNumber = parseInt(CodeInput);
	InputField = document.forms[0].elements[ElementName];
	InputField.className = "formItem";
	if (isNaN(CodeNumber)) {
		alert("formValidation Error:\n\""+ CodeInput +"\" is not a valid code number");
		return false;
	}
	
// ------------- This bit checks all required fields are completed --------------------------
	if(CodeNumber < 0) {
		CodeNumber = Math.abs(CodeNumber)
		if(isEmpty(InputField.value)) {
			ErrorMsg = "Please enter an email address.";
			InputField.className = "formItemInvalid";
			InputField.focus();
			return false;
			}
		}
// ------------- This bit checks that inputs are correct to their validation type ----------
	if((!isEmpty(InputField.value)) && (ErrorMsg == "") && (SubErrorMsg[2] == "")) {
		switch(CodeNumber)
			{
			case 0 : 
			         break;
			case 1 : 
			         break;
			case 2 : if(!isValid(InputField.value, alpha + numbers + whitespace + basicpunc)) SubErrorMsg[2] = "this field cannot contain special characters.";
			         break;
			case 3 : if(!isValid(InputField.value, alpha+ whitespace)) SubErrorMsg[2] = "this field can only contain alphabetical characters.";
			         break;
			case 4 : if(!isValid(InputField.value, numbers)) SubErrorMsg[2] = "this field must be a whole number greater than 0.";
			         break;
			case 5 : if(isNaN(Number(InputField.value))) SubErrorMsg[2] = "this field must be a number.";
			         break;
			case 6 : CodeInput = CodeInput.split("_");
			         minValue = parseFloat(CodeInput[1]);
			         maxValue = parseFloat(CodeInput[2]);
			         if(!isInRange(parseFloat(InputField.value), minValue, maxValue)) SubErrorMsg[2] = "this field must be a number between "+ minValue +" and "+ maxValue +".";
			         break;
			case 7 : if(!isEmail(InputField.value)) SubErrorMsg[2] = "this field must be a valid e-mail address."
			         break;
			case 8 : if(!isValid(InputField.value, alpha + numbers)) SubErrorMsg[2] = "this field must not contain whitespace or symbols.";
			         break;
			case 9 : if(!isDate(InputField.value)) SubErrorMsg[2] = "this field must be a valid date, ie: 02/04/1983.";
			         break;
			case 10 : if(!isTime(InputField.value)) SubErrorMsg[2] = "this field must be a valid 24 hour time, ie: 17:30:00.";
			         break;
            case 11 : if(isValid(InputField.value, invalidpathchars)) SubErrorMsg[2] = "this field must be a valid file path.";
                     break;
            case 12 : //get individual elements from user
                      CodeInput = CodeInput.split("_");
                      checkDate = CodeInput[1].split("/");
                      checkStatus = parseFloat(CodeInput[2]); //integer that states whether you want to check date is before, after or equal.  See switch below.
                        
                      //alert("user input: " + InputField.value);
                      inputFieldDate = InputField.value.split("/") //user input
                        
                      //construct the two dates for comparison
                      specifiedDate = constructDate(checkDate[2], checkDate[1], checkDate[0]);
                      inputFieldDate = constructDate(inputFieldDate[2], inputFieldDate[1], inputFieldDate[0]);

                    //alert(specifiedDate.getUTCFullYear())

                    /*              //Left in if needed for debugging later.
                                    var sd = new Date(specifiedDate)
                                    var ifd = new Date(inputFieldDate)
                                    alert("specifiedDate: " + sd.toDateString())
                                    alert("inputFieldDate: " + ifd.toDateString())
                    */
                    //check status               
                   switch(checkStatus) {
                    case 0: //if date specified comes after or is equal to the input field date
                      if (specifiedDate <= inputFieldDate)  {
                          SubErrorMsg[2] = "the date given has to be before " + CodeInput[1];
                      }
                    break;
                    case 1: //if date specified comes before or is equal to the input field date
                      //alert("specifiedDate: " + specifiedDate);
                      //alert("inputFieldDate: " + inputFieldDate);
                      
                      if (specifiedDate >= inputFieldDate)  {
                          SubErrorMsg[2] = "the date given has to be after " + CodeInput[1];
                      }
                    break;
                    case 2: //if date specified is equal to the input field date
                      if (specifiedDate != inputFieldDate)  {
                          SubErrorMsg[2] = "the date given has to be equal to " + CodeInput[1];
                      }
                    break;
                    case 3: //if date specified comes after the input field date
                      if (specifiedDate < inputFieldDate)  {
                          SubErrorMsg[2] = "the date given has to be on or before " + CodeInput[1];
                      }
                    break;
                    case 4: //if date specified comes before the input field date
                      if (specifiedDate > inputFieldDate)  {
                          SubErrorMsg[2] = "the date given has to be on or after " + CodeInput[1];
                      }
                    break;
                    }
                   break;
      case 13 : 
                if (!isValid(InputField.value,numbers))
                {
                  currencyName = InputField.name.substring(0,InputField.name.length - 5);
                  currencyUnit = InputField.name.substring(InputField.name.length - 5);
                  //document.forms[0].elements[currencyName + "pound"].className = "formItemInvalid";
                  //document.forms[0].elements[currencyName + "pence"].className = "formItemInvalid";
                  ElementName = currencyName + currencyUnit;
                  SubErrorMsg[2] = "it has to be a number";
                }
               break;
         case 14 : if(isNaN(InputField.value) || ((InputField.value.indexOf(".") + 3 < InputField.value.length) && (InputField.value.indexOf(".") > 0))) {
                  SubErrorMsg[2] = "this field must be a number with no more than 2 decimal places.";
                }
              break;
          case 15: if (!isValid(InputField.value, alpha + whitespace + namechars)) SubErrorMsg[2] = "this field cannot contain special characters.";
              break;
          case 16: if (!isValid(InputField.value, alpha + whitespace + namechars + numbers)) SubErrorMsg[2] = "this field cannot contain special characters.";
              break;
			case 50 : if(InputField.value != document.forms[0].elements["Confirm" + ElementName].value) {
			            SubErrorMsg[2] = "this field and Confirm "+ ElementLabel +" are not the same value.";
			            document.forms[0].elements["Confirm" + ElementName].className = "formItemInvalid";
			          }
			          break;
			case 51 : CheckArray = eval(ElementName);
			          for(GFcount=0; GFcount < CheckArray.length; GFcount++)
			             {
			             if(InputField.value.toLowerCase() == CheckArray[GFcount].toLowerCase())
			                {
			                SubErrorMsg[2] = "\"" + InputField.value + "\"" + " already exists in the database.";
			                break;
			                }
			             }
			          break;
			case 55 : if(!isValidNINumber(InputField.value)) SubErrorMsg[2] = "this field must be a valid national insurance number in the following format:\n\tAANNNNNNA, where A = letter and N = numeral.\n\tE.g. AB12 34 56C";
			          break;
			case 60 : if(!isDateTime(InputField.value)) SubErrorMsg[2] = "this field must be a valid date-time\nie: 02/04/1983:17:30:00.";
			         break;
			}
		if(SubErrorMsg[2] != "") {
			SubErrorMsg[0] = ElementName;
			SubErrorMsg[1] = ElementLabel.replace(/<[^<]+>/gi,""); // Regular expression to strip HTML from the label.
			}
		}
	else return false;
}
function isFormValid() {
if ((ErrorMsg == "") && (SubErrorMsg[0] == ""))	return true;
else {
	if(ErrorMsg == "") displaySubErrorMsg();
	else alert(ErrorMsg);
	return false;
	}
}
//  ASPX form validation by IR 26/3/MMIV
//  form items to be validated contain the attribute, "validation=true"
//  and also the attribute "validationCode=[ValidationCode]"....
//  wait! there's also "validationName=[name]"
function validateASPX()
{
  ErrorMsg = "";
  SubErrorMsg = new Array ("","","");

  for (var i=0; i<document.all.length; i++)
    if (document.all[i].validation == "True")
      validate(document.all[i].validationCode, document.all[i].name, document.all[i].validationName);

  return isFormValid();
}