
/* 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 certainchars = "&'@-"
var basicpunc = ".,?£$()-!+=#@_&^~*{}[];:|\/";
var internationalPhone = "+";
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]);
    fcnShowPopupBySize2('ifPopup', 330, 150, '/Alert.aspx?PopupName=ifPopup&Msg=The value in \'' + SubErrorMsg[1] + '\' field is invalid because[br]' + 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 fill in all required fields.";
            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 17: if (!isValid(InputField.value, internationalPhone + whitespace + numbers)) SubErrorMsg[2] = "this field must be a valid Phone/Fax number.";
                break;
            case 18: if (!isValid(InputField.value, alpha + whitespace + namechars + numbers + certainchars)) SubErrorMsg[2] = "this field cannot contain certain 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);
            fcnShowPopupBySize2('ifPopup', 330, 150, '/Alert.aspx?PopupName=ifPopup&Msg=' + 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();
}
