// Globals for the validation system

var firstFormVRec = null;  // a list of FormVRec objects, one for each form to be validated
var validationFailedField; // a global buffer to pass the bad field to the timeout routine highlighting and focusing it



// validation onchange handler for must not be empty (required) HTML form input elements

function ecValidateFieldNotEmpty(actualValidationFunction, formField, hiddenName)
    {
    if (!formField || formField == null || formField.value == null || formField.value == "")
        {
        if (document.getElementById(formField.id + "_BeforeEdit")) formField.value = document.getElementById(formField.id + "_BeforeEdit").value;
        alert("The field must not be empty, its previous contents have been restored.");
        validationFailedField = formField;
        setTimeout("validationFailedField.focus();validationFailedField.select();", 20);
        return false;
        }
    else
        return ecValidateField(actualValidationFunction, formField, hiddenName);
    }

// focus handler for must not be empty (required) HTML form input elements

function ecFocusFieldNotEmpty(formField)
    {
    document.getElementById(formField.id + "_BeforeEdit").value = formField.value;
    }

// validation onchange handler for HTML form input elements

function ecValidateField(actualValidationFunction, formField, hiddenName)
    {
    var fvr = FormVRec_findOrMake(formField.form);
    if (!actualValidationFunction || fvr.validateField(actualValidationFunction, formField))
        {
        if (window.incModCount) incModCount();
        if (hiddenName != null) hiddenName.value = 1;
        return true;
        }
    else
        return false;
    }



// validation onsubmit handler for HTML form elements

function ecValidateForm(form)
    {
    return FormVRec_findOrMake(form).validate();
    }



// actual validation function for floating point numbers

function validateFloat(formField)
    {
    formField.value = trimStr(formField.value);
    if (formField.value == "") return true;
    var i = scanFloat(formField.value, 0);
    if (isNaN(parseFloat(formField.value)) || i < formField.value.length)
        {
        var failMsg =
            "The value \"" + formField.value + "\" is not valid for a (floating point) number.\n\n" +
            "Some examples of numbers in valid formats are:-\n" +
            "12 , -3.14 , .667 , -517.23e42 , .333E-26";
        alert(failMsg);
        return false;
        }
    else
        return true;
    }



// actual validation function for unsigned integers

function validateUnsignedInt(formField)
    {
    formField.value = trimStr(formField.value);
    if (formField.value == "") return true;
    var i = scanDigits(formField.value, 0);
    if (i < formField.value.length)
        {
        var failMsg =
            "The value \"" + formField.value + "\" is not valid for an unsigned whole number.\n\n" +
            "Some examples of numbers in valid formats are:-\n" +
            "0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21";
        alert(failMsg);
        return false;
        }
    else
        return true;
    }



// actual validation function for dates

function validateDate(formField)
    {
    formField.value = trimStr(formField.value);
    var errMsg = checkDate(formField.value);
    if (errMsg != null)
        {
		alert(errMsg)
		return false;
        }
    else
        return true;
    }



// FormVRec object -- responsible for validating a form

function FormVRec_findOrMake(form) // static method
    {
    for (var fvr = firstFormVRec; fvr != null; fvr = fvr.next) if (fvr.form === form) return fvr;
    firstFormVRec = new FormVRec(form, firstFormVRec);
    return firstFormVRec;
    }

function FormVRec(form, nextFormVRec)
    {
    this.indexField = FormVRec_indexField;
    this.findOrMakeField = FormVRec_findOrMakeField;
    this.validate = FormVRec_validate;
    this.validateField = FormVRec_validateField;
    this.form = form;
    this.fieldVRecs = null;
    this.next = nextFormVRec;
    var elements = form.elements;
    for (var j = 0; j < elements.length; j++) this.indexField(elements[j]);
    }

function FormVRec_indexField(formField)
    {
    if (!formField.onchange) return;
    var changeHandler = formField.onchange.toString();
    var i = changeHandler.indexOf("ecValidateField");
    if (i < 0 || i != changeHandler.lastIndexOf("ecValidateField")) return;
    while (i < changeHandler.length && changeHandler.charAt(i) != "(") i++;
    i++;
    while (i < changeHandler.length && changeHandler.charAt(i) == " ") i++;
    var start = i;
    while (i < changeHandler.length && " ,".indexOf(changeHandler.charAt(i)) == -1) i++;
    this.fieldVRecs = new FieldVRec(eval(changeHandler.slice(start, i)), formField, this.fieldVRecs);
    }

function FormVRec_findOrMakeField(actualValidationFunction, formField)
    {
    var f;
    for (f = this.fieldVRecs; f != null; f = f.next) if (f.field === formField) break;
    if (f == null)
        f = this.fieldVRecs = new FieldVRec(actualValidationFunction, formField, this.fieldVRecs);
    return f;
    }

function FormVRec_validate()
    {
    for (var v = this.fieldVRecs; v != null; v = v.next) if (!v.validate()) return false;
    return true;
    }

function FormVRec_validateField(actualValidationFunction, formField)
    {
    var f = this.findOrMakeField(actualValidationFunction, formField);
    return f.validate();
    }



// FieldVRec object -- responsible for validating one form field

function FieldVRec(actualValidationFunction, formField, nextFieldVRec)
    {
    this.validate = FieldVRec_validate;
    this.field = formField;
    this.func = actualValidationFunction;
    this.next = nextFieldVRec;
    }

function FieldVRec_validate()
    {
    if (this.func == null || this.func(this.field))
        return true;
    else
        {
        validationFailedField = this.field;
        setTimeout("validationFailedField.focus();validationFailedField.select();", 20);
        return false;
        }
    }



// subroutines used by the validation functions

function trimStr(str)
    {
    if (str == null || str.length == 0) return "";
    var start = 0;
    while (start < str.length && " \t".indexOf(str.charAt(start)) >= 0) start++;
    var end = str.length;
    while (end > 0 && " \t".indexOf(str.charAt(end - 1)) >= 0) --end;
    return str.slice(start, end);
    }

function scanFloat(str, i)
    {
    i = scanSignOpt(str, i);
    if (i < 0) return i;
    i = scanMantissa(str, i);
    if (i < 0) return i;
    return scanExponentOpt(str, i);
    }

function scanSignOpt(str, i)
    {
    if (i < str.length)
        switch (str.charAt(i))
            {
            case "+": case "-": i++; break;
            default:                 break;
            }
    return i;
    }

function scanMantissa(str, i)
    {
    if (i >= str.length) return -str.length;
    if (str.charAt(i) == ".") return scanDigits(str, i + 1);
    i = scanDigits(str, i);
    if (i < 0) return i;
    if (i < str.length && str.charAt(i) == ".") i = scanDigitsOpt(str, i + 1);
    return i;
    }

function scanExponentOpt(str, i)
    {
    if (i >= str.length) return i;
    switch (str.charAt(i))
        {
        case "e": case "E": i++; break;
        default:                 return i;
        }
    i = scanSignOpt(str, i);
    if (i < 0) return i;
    return scanDigits(str, i);
    }

function scanDigits(str, i)
    {
    var nextI = scanDigitsOpt(str, i);
    return nextI == i ? -nextI : nextI;
    }

function scanDigitsOpt(str, i)
    {
    while (i < str.length && "0123456789".indexOf(str.charAt(i)) >= 0) i++;
    return i;
    }

function checkDate(dateStr)
    {
    if (dateStr == null || dateStr == "") return null;
    var code_0 = "0".charCodeAt(0);
    var code_sep_offset = "/".charCodeAt(0) - code_0;
    var i = 0;
    var month = 0;
    var digitCount = 0;
    while (i < dateStr.length)
        {
        var c = dateStr.charCodeAt(i++) - code_0;
        if (c == code_sep_offset) break;
        if (c < 0 || c > 9) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy.";
        digitCount++;
        month = 10*month + c;
        }
    if (digitCount < 1 || digitCount > 2) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy.";
    if (i >= dateStr.length || dateStr.charAt(i - 1) != "/") return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy.";
    var day = 0;
    digitCount = 0;
    while (i < dateStr.length)
        {
        var c = dateStr.charCodeAt(i++) - code_0;
        if (c == code_sep_offset) break;
        if (c < 0 || c > 9) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy.";
        digitCount++;
        day = 10*day + c;
        }
    if (digitCount < 1 || digitCount > 2) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy.";
    if (i >= dateStr.length || dateStr.charAt(i - 1) != "/") return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy.";
    var year = 0;
    digitCount = 0;
    while (i < dateStr.length)
        {
        var c = dateStr.charCodeAt(i++) - code_0;
        if (c == code_sep_offset) break;
        if (c < 0 || c > 9) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy.";
        digitCount++;
        year = 10*year + c;
        }
    if (digitCount != 4) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy. There must be 4 digits in the year.";
    if (year < 1900 || year > 9999) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy. The year must be between 1900 and 9999.";
    if (month < 1 || month > 12) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy. The month must be between 1 and 12.";
    var daysInMonth;
    switch (month)
        {
        case 2:                          daysInMonth = ((year%4 == 0) && ((!(year%100 == 0)) || (year%400 == 0))) ? 29 : 28; break;
        case 4: case 6: case 9: case 11: daysInMonth = 30;                                                                   break;
        default:                         daysInMonth = 31;                                                                   break;
        }
    if (day < 1) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy. The day of the month must be at least 1.";
    var monthNames = [ "", "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" ];
    if (day > daysInMonth) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy. The day of the month can't be greater than " + daysInMonth + " (the last day of " + monthNames[month] + " " + year + ").";
    while (i < dateStr.length) if (" \t".indexOf(dateStr.charAt(i++)) < 0) return "The value \"" + dateStr + "\" is not in the required date format: mm/dd/yyyy.";
    return null;
    }
