// JScript File

// takes arguments, which should be objects created from json, and creates relevant validator class instances
ValidatorParser = Class.create({
    initialize: function(){
    },
    // gets argument on latter half of - of className, from elementClass
    getValidatorArgument: function(elementClass, className){
        var validatorArgument;
        if(!elementClass) return null;
        if(!elementClass.indexOf) return null;
        if(elementClass.indexOf(className) == -1) return null;
        // find argument on second side of dash
        // do this by first just finding starting point of validator class until last space
        var validatorStartingPoint = elementClass.indexOf(className);
        var tailHalfString = elementClass.substr(validatorStartingPoint, elementClass.length - validatorStartingPoint);
        var validatorEndingPoint = tailHalfString.indexOf(' ');
        var validatorString;
        if(validatorEndingPoint != -1)
            validatorString = tailHalfString.substr(0, validatorEndingPoint);
        else
            validatorString = tailHalfString;
        // now split validator class into classname on left and arguments on right side of dash.
        var validatorSections =  validatorString.split('-');
        if(validatorSections.length > 0)
            validatorArgument = validatorSections[1];
        else
            validatorArgument = null;
            
        // now replace &nbsp; with whitespaces in validatorArgument
        if(validatorArgument){
            validatorArgument = this.removeEscapedWhiteSpaces(validatorArgument);
        }
        
            
        return validatorArgument;  
    },
    removeEscapedWhiteSpaces: function(stringToFix){
        while(stringToFix.indexOf('&nbsp;') != -1)
            stringToFix = stringToFix.replace('&nbsp;', ' ');
        return stringToFix;
    },
    parseFieldValidator: function(validationSpan, form){
        var validationClass = validationSpan.className;
        var fieldAccessorArgumentJson = this.getValidatorArgument(validationClass, 'fieldToAccess');
        if(!fieldAccessorArgumentJson) return null;
        var fieldAccessorArgument = fieldAccessorArgumentJson.evalJSON();
         
        var fieldAccessor = this.parseFieldAccessor(fieldAccessorArgument, form);
        
        var valueValidator = this.parseValueValidator(validationClass);
        var validationError;
        var validationErrorArgument = this.getValidatorArgument(validationClass, 'validatorError');
        if(validationErrorArgument){
            var validationErrorArgumentObject = validationErrorArgument.evalJSON();
            validationError = this.parseValidationError(validationErrorArgumentObject);
        }
        else if(valueValidator)
            validationError = valueValidator.getDefaultValidationError();
            
        return new FieldValidator(validationSpan, fieldAccessor, valueValidator, validationError);
    },
    // returns either a single field or multi-field accessor
    parseFieldAccessor: function(accessorArgumentInfo, form){
        if(!accessorArgumentInfo) return false;
        // if type is single field validator, fieldId and fieldName will be here
        if(accessorArgumentInfo.singleField){
            return new FieldAccessor(accessorArgumentInfo.singleField, form);
        }
        // otherwise, go through fields and reparse
        else if(accessorArgumentInfo.multiField){
            var multiFieldArguments = accessorArgumentInfo.multiField;
            if(!multiFieldArguments.fields) return null;
            var fieldAccessors = new Array();
            var methodType = multiFieldArguments.methodType;
            for(var i = 0; i < multiFieldArguments.fields.length; i++){
                var fieldAccessor = this.parseFieldAccessor(multiFieldArguments.fields[i], form);
                fieldAccessors.push(fieldAccessor);
            }
            // create MultiFieldAccessor with all fieldAccessors
            return new MultiFieldAccessor(fieldAccessors, methodType);
        }
        else
            return null;
    },
    parseValueValidator: function(validationClass){
        if(!validationClass) return null;
        var valueValidator = null;
        
        if(validationClass.indexOf('required') != -1)
            valueValidator = new RequiredValidator();
        else if(validationClass.indexOf('integer') != -1)
            valueValidator = new IntegerValidator(); 
        else if(validationClass.indexOf('email') != -1)
            valueValidator = new EmailValidator();
        else if (validationClass.indexOf('date') != -1)
            valueValidator = new DateValidator();
        else if (validationClass.indexOf('today') != -1)
            valueValidator = new DateGreaterThanTodayValidator();
        else if (validationClass.indexOf('dob') != -1)
            valueValidator = new DOBValidator();
        else if (validationClass.indexOf('aba') != -1)
            valueValidator = new ABAValidator();
        else if(validationClass.indexOf('length') != -1){        
            var lengthArgument = this.getValidatorArgument(validationClass, 'length');
            if(!lengthArgument) 
                valueValidator = null;
            else
                valueValidator = new LengthValidator(lengthArgument);
        }
        else if(validationClass.indexOf('compare') != -1){        
            var comparisonArgument = this.getValidatorArgument(validationClass, 'compare');
            if(!comparisonArgument) 
                valueValidator = null;
            else
                valueValidator = new ComparisonValidator(comparisonArgument);
        }  
           
        return valueValidator;
    },
    parseValidationError: function(validationErrorArgumentObject){
        if(!validationErrorArgumentObject) return null;
        return new ValidationError(validationErrorArgumentObject.errorTypeName, 
            validationErrorArgumentObject.errorMessage, 
            validationErrorArgumentObject.errorClassName, 
            validationErrorArgumentObject.validatorSymbol)
    }
});

var validatorParser = new ValidatorParser();

var Validator = Class.create({
    initialize: function(validationElement, form){
        this.validationElement = validationElement;
        this.form = form;
        this.elementToValidate = null;
        this.validationOrder = 0;
    },
    validate: function(){
        return true;
    }, 
    reflectValidation: function(){
    }
});

var FormValidator = Class.create(Validator, {
    initialize: function($super, form) {
        // field validators will be validated
        // first build validation elements then call base constructor to set properties
        var validationElements = new Array();
        var validatorSpans = $$('span.validator');
        for (var i = 0; i < validatorSpans.length; i++) {
            var fieldValidator = validatorParser.parseFieldValidator(validatorSpans[i], form);
            validationElements.push(fieldValidator);
        }
        $super(validationElements, form);
        this.validationErrors = new Hash();
    },
    // old method
    validate: function() {
        var valid = true;
        var elementValid;
        this.clearValidationStatus();
        // cycle through each element and validate it
        for (var i = 0; i < this.validationElement.length; i++) {

            var currentElement = this.validationElement[i];
            var shouldskip = false;
            try {
                if (document.forms[0][currentElement.fieldAccessor.fieldToAccess.id].length > 0) {
                    var quicktempcheck = (currentElement.fieldAccessor.fieldToAccess.type.toString().indexOf("select") > -1);
                    if (quicktempcheck) {
                        if (currentElement.fieldAccessor.fieldToAccess.offsetHeight == 0)
                            shouldskip = true;
                    }
                    else {
                        if (document.forms[0][currentElement.fieldAccessor.fieldNameToAccess][0].offsetHeight == 0)
                            shouldskip = true;
                    }
                }
            }
            catch (e) {
                shouldskip = false;
            }
            // check for method existence
            if (currentElement.validate && !shouldskip) {
                // set valid to false if element not valid and add to validation message
                elementValid = currentElement.validate();
                currentElement.reflectValidation(elementValid);
                if (!elementValid) {
                    valid = false;
                    var validationError = currentElement.getValidationError();
                    // add validator error to list if not there already
                    if (!this.validationErrors.get(validationError.name))
                        this.validationErrors.set(validationError.name, validationError);
                }
            }
        }
        return valid;
    },
    reflectValidation: function() {
        var validationMessage = "";
        // need to hardcode keys cause of failure of foreach
        this.validationErrors.each(function(pair) {
            validationMessage += pair.value.errorMessage + '\n';
        });

        if (validationMessage != "")
            alert(validationMessage);
    },
    clearValidationStatus: function() {
        var validationErrorKeys = this.validationErrors.keys();
        for (var i = 0; i < validationErrorKeys.length; i++)
            this.validationErrors.unset(validationErrorKeys[i]);

    }
});

var FieldValidator = Class.create(Validator, {
    initialize: function(validationSpan, fieldAccessor, valueValidator, validationError){
        this.validationSpan = validationSpan;
        this.fieldAccessor = fieldAccessor;
        this.valueValidator = valueValidator;
        this.validationError = validationError;
    },
    initializeOld: function($super, validationElement, form){
        $super(validationElement, form);
        
        //valueValidator is what validates the value
        this.valueValidator=null;
        
        // now see if custom validation error appears in class.  if so, use it.  Otherwise, use
        // default value passed in.
        var validationError = null;
        var customValidationErrorArgument = this.getValidatorArgument(validationElement.className, 'validatorError');
        if(customValidationErrorArgument){
            var customValidationError = customValidationErrorArgument.evalJSON();
            validationError = new ValidationError(customValidationError.errorTypeName, 
                customValidationError.errorMessage.unescapeHTML(), customValidationError.errorClassName, customValidationError.validatorSymbol);
        }
        
        
        
        //find label which points to this element
        //this.label = $$('label[for="'+ validationElement.id +'"]');
    },
    validate: function(){
        var valid = true;
        if(!this.fieldAccessor || !this.valueValidator) return true;
        if(!this.fieldAccessor.isEnabled()) return true;
        var value = this.fieldAccessor.getValue();
        valid = this.valueValidator.validate(value);
        return valid;
    },
    validateOld: function(){
        var valid = true;
        //this.clearValidationStatus();
        if(this.valueValidator)
            valid =  this.valueValidator.validate();
        return valid;
    },
    reflectValidation: function(valid){
        if(!this.fieldAccessor || !this.validationError) return false;
        if(valid){
            this.clearValidationStatus();
        }
        else{
            // set fields to invalid
            this.fieldAccessor.setInvalid(this.validationError.errorClass);
            // add error symbol to validation span
            this.validationSpan.innerHTML = this.validationError.errorSymbol;
        }
        
    },
    clearValidationStatus: function(){        
        // set fields to valid
        this.fieldAccessor.setValid();
        // remove error symbol from validation span
        this.validationSpan.innerHTML = '';
    },  
    getValidationError: function(){
        return this.validationError;
    }
});

//fieldToAccess should be an object with properties name and id
var FieldAccessor = Class.create({
    initialize: function(fieldToAccess, form){
        this.form = form;
        this.fieldNameToAccess = fieldToAccess.name;
        this.fieldToAccess = $(fieldToAccess.id);
    },
    getValue: function(){
        if(!this.form[this.fieldNameToAccess]) return null;
        var value = null;
        // if checkbox, return if checked.  Otherwise, get value from form (use this method
        // because with radio buttons cannot get value of element id)
        if(this.fieldToAccess.type == "checkbox")
            value = this.fieldToAccess.checked;
        else {
            // if has value, then get value.  Otherwise try to go through elements and get checked
            // value
            var formElementToAccess = this.form[this.fieldNameToAccess];
            if(formElementToAccess.value)
                value = formElementToAccess.value;
            else if(formElementToAccess.length){
                for(var i = 0; i < formElementToAccess.length; i++){
                    if(formElementToAccess[i].checked){
                        value = formElementToAccess[i];
                        break;
                    }
                }
            }
        }
        return value;
    },
    isEnabled: function(){
        if(!this.fieldToAccess) return true;
        var enabled;
        if(this.fieldToAccess.disabled)
            enabled = false;
        else
            enabled = true;
        return enabled;
    },
    setInvalid: function(errorClassName){
        if(!this.fieldToAccess) return false;
        this.fieldToAccess.addClassName(errorClassName);
        this.errorClassName = errorClassName;
    },
    setValid: function(){
        if(!this.errorClassName || !this.fieldToAccess) return false;
        this.fieldToAccess.removeClassName(this.errorClassName);
    }
});

//contains an array of field accessors
var MultiFieldAccessor = Class.create({
    initialize: function(fieldAccessors, joinMethod){
        this.fieldAccessors = fieldAccessors;
        this.joinMethod = joinMethod;
    },
    getValue: function(){
        var value = null;
        if(!this.fieldAccessors) return false;
        
        var fieldValues = new Array();
        for(var i = 0; i < this.fieldAccessors.length; i++){
            var fieldValue = this.fieldAccessors[i].getValue();
            fieldValues.push(fieldValue);
        }
        
        if(this.joinMethod == 'concat'){
            value = "";
            for(var i = 0; i < fieldValues.length; i++){
                if(fieldValues[i] != null && fieldValues[i] != ''){                     
                    value += fieldValues[i];
                }else{  
                    if (fieldValues[i] == null || fieldValues[i] == ""){
                        value = null;
                        break;
                    }
                    var nextVal = i+1; 
                    /* Charles Taylor (01.13.2009) - Hack to fix built-in multi-field validation bug */
                    if (nextVal < fieldValues.length){                                       
                        if (fieldValues[nextVal] == null || fieldValues[nextVal] == ""){
                            value = null;
                            break;   
                        }
                    }                    
                }
            }            
        }
        else if(this.joinMethod == 'equal' || this.joinMethod == 'notEqual'){
            var same = true;
            // first make sure fields arent empty
            if(!this.isBlank(fieldValues)){    
                for(var i = 0; i < fieldValues.length - 1; i++){
                    if(fieldValues[i] != fieldValues[i + 1]){
                        same = false;
                        break;
                    }
                }   
                if(this.joinMethod == 'equal')
                    value = same;
                else
                    value = !same;
            }
            else
                value = true;
        }
        
        return value;
    },
    isBlank: function(fieldValues){
        if(!fieldValues.length) return false;
        var blank = true;
        for(var i = 0; i < fieldValues.length; i++){
            if(fieldValues[i].blank)
                if(!fieldValues[i].blank()){
                    blank = false;
                    break;
                }
        }
        return blank;
    },
    isEnabled: function() {
        if(!this.fieldAccessors) return true;
        if(!this.fieldAccessors.length) return true;
        var enabled = true;
        for(var i = 0; i < this.fieldAccessors.length; i++){
            if(!this.fieldAccessors[i].isEnabled){
                enabled = false;
                break;
            }                
        }
        return enabled;
    },
    setInvalid: function(errorClassName){
        if(!this.fieldAccessors) return false;        
        for(var i = 0; i < this.fieldAccessors.length; i++){
            this.fieldAccessors[i].setInvalid(errorClassName);
        }
    },
    setValid: function(){
        if(!this.fieldAccessors) return false;        
        for(var i = 0; i < this.fieldAccessors.length; i++){
            this.fieldAccessors[i].setValid();
        }
    }
});

// validates a value
var ValueValidator = Class.create(Validator, {
    initialize: function(defaultValidationError){
        this.defaultValidationError = defaultValidationError;
    },
    validate: function(value){
        return true;
    },
    getDefaultValidationError: function(){
        return this.defaultValidationError;
    }
});

var RequiredValidator = Class.create(ValueValidator, {
    initialize: function($super){
        $super(new RequiredValidationError());
    },
    validate: function(value) {
        var valid;
        if(!value)
            valid = false;
        // if blank method appears then it is a string, check if empty
        // otherwise it is a boolean value so it is true.
        else if(value.blank){
            if(!value.blank())
                valid = true;
            else
                valid = false;
        }
        else
            valid = true;
        return valid;
    }
});

// abstract class that validates a value in a field to be correct
var CorrectValueValidator = Class.create(ValueValidator, {
    initialize: function($super, validationError){
        if(validationError)
            $super(validationError)
        else
            $super(new InvalidValueValidationError());
    },
    validate: function(value){
        // first make sure value is filled.  If it is, then perform value validation
        //var value = this.getValue();
        var valid;
        if(value != '' && value != null)
            valid = this.validateValue(value);
        else
            valid = true;
        return valid;
    },
    validateValue: function(value){
        // should be overwritten by subclasses
        return true;
    }
});

var IntegerValidator = Class.create(CorrectValueValidator, {
    validateValue: function(value) {
        var valid;
        var integerFilter=/^\$?\d{1,3}(,?\d{3})*(\.\d{1,2})?$/
        if(integerFilter.test(value)) 
            valid = true;
        else
            valid = false;
        return valid;
    }
});


var EmailValidator = Class.create(CorrectValueValidator, {
    validateValue: function(value) {
        var valid;
        var emailFilter=/^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
        if(emailFilter.test(value)) 
            valid = true;
        else
            valid = false;
            
        return valid;
    }
});

//New Date Validator - Charles Taylor (02/03/2009)
var DateValidator = Class.create(CorrectValueValidator, {
    initialize: function($super){
        $super(new RequiredValidationError());
    },
    validate: function(value){
        // first make sure value is filled.  If it is, then perform value validation
        //var value = this.getValue();
        var valid;
        if(value != '' && value != null)
            valid = this.validateValue(value);
        else
            valid = false;
        return valid;
    },
    validateValue: function(value) {     
    if (value == "mm/dd/yyyy" || value == "") { return false; }
       var re = /^\d{1,2}\/\d{1,2}\/\d{4}$/
       if (re.test(value)) {
          var dArr = value.split("/");
          var d = new Date(value);
          return d.getMonth() + 1 == dArr[0] && d.getDate() == dArr[1] && d.getFullYear() == dArr[2];
       }
       else {
          return false;
       }   
    }
});

// New ABA Validator - Chris V. (04/08/2009)
var ABAValidator = Class.create(CorrectValueValidator, {
    initialize: function($super){
        $super(new ABAValidationError());
    },
    validate: function(value) 
    {
        var valid;
        if(value != '' && value != null)
            valid = this.validateValue(value);
        else
            valid = false;
        return valid;
    },
    validateValue: function(value) 
    {
        if (value == "mm/dd/yyyy" || value == "") { return false; }
        if (value.length != 9) { return false; }
        var n = 0; var i = 0;
        for (i = 0; i < value.length; i += 3)
        {
        n += parseInt(value.charAt(i),     10) * 3
          +  parseInt(value.charAt(i + 1), 10) * 7
          +  parseInt(value.charAt(i + 2), 10);
        }
        // If the resulting sum is an even multiple of ten (but not zero),
        // the aba routing number is good.
        if(n != 0 && n % 10 == 0)
            return true;
        else
            return false;
    }
});

//New Date Validator - Charles Taylor (02/19/2009)
var DateGreaterThanTodayValidator = Class.create(CorrectValueValidator, {
    initialize: function($super) {
        $super(new DateNotGreaterThanTodayValidationError());
    },
    validate: function(value) {
        // first make sure value is filled.  If it is, then perform value validation
        //var value = this.getValue();
        var valid;
        if (value != '' && value != null)
            valid = this.validateValue(value);
        else
            valid = false;
        return valid;
    },
    validateValue: function(value) {
        if (value == "mm/dd/yyyy" || value == "") { return false; }
        var dateToday = new Date();
        value = value.replace(/-/g, "/")
        date = value.split('/');
        var d = new Date();
        d.setYear(date[2]);
        var convertMonth = 0;
        if (date[0].indexOf('0') == 0)
            convertMonth = parseInt(date[0].substring(1)) - 1;
        else
            convertMonth = parseInt(date[0]) - 1;
        d.setMonth(convertMonth);
        d.setDate(date[1]);
        return ((d < dateToday) ? false : true);
    }
});


//New Date Of Birth Validator - Charles Taylor (02/04/2009)
var DOBValidator = Class.create(CorrectValueValidator, {
    initialize: function($super) {
        $super(new DOBValidationError());
    },
    validate: function(value) {
        // first make sure value is filled.  If it is, then perform value validation
        //var value = this.getValue();
        var valid;
        if (value != '' && value != null)
            valid = this.validateValue(value);
        else
            valid = false;
        return valid;
    },
    validateValue: function(value) {
        if (value == "mm/dd/yyyy" || value == "") { return false; }
        value = value.replace(/-/g, "/")
        var testDateLength = (/^\d{1,2}\/?\d{1,2}\/\d{4}$/.test(value));
        if (!testDateLength) { return false; }

        var dateEighteenYearsAgo = new Date();
        dateEighteenYearsAgo.setFullYear(dateEighteenYearsAgo.getFullYear() - 18);



        birthDate = value.split('/');
        var bDate = new Date();
        bDate.setYear(birthDate[2]);
        bDate.setMonth((parseInt(birthDate[0]) - 1));
        bDate.setDate(birthDate[1]);
        //alert("birthdate: " + birthDate + " Eighteen Years Ago: " + dateEighteenYearsAgo);
        var isLegalDate = ((bDate > dateEighteenYearsAgo) ? false : true);
        if (isLegalDate) {
            if (document.getElementById("birth_month")) {
                document.getElementById("birth_month").value = birthDate[0];
            }
            if (document.getElementById("birth_year")) {
                document.getElementById("birth_year").value = birthDate[2];
            }
            if (document.getElementById("birth_date")) {
                document.getElementById("birth_date").value = birthDate[1];
            }
        }
        return isLegalDate;
    }
});



var LengthValidator = Class.create(CorrectValueValidator, {
    initialize: function($super, validationLengthArgument){
        if(!validationLengthArgument) return false;
        $super(new LengthValidationError());
        this.validLength = parseInt(validationLengthArgument);
    },
    validateValue: function(value) {
        var valid;
        if(value.length == this.validLength){
            valid = true;
        }
        else
            valid = false;
            
        return valid;
    }
});

var ComparisonValidator = Class.create(CorrectValueValidator, {
    initialize: function($super, comparisonArgument){
        if(!comparisonArgument) return false;
        $super();
        if(comparisonArgument == "true")
            this.shouldBeSame = true;
        else
            this.shouldBeSame = false;
    },
    // value will be true if the same, false if different
    validateValue: function(value) {
        // comparison built into field accessor
        if(value)
            return true;
        else
            return false;
    }
});


var ValidationError = Class.create({
    initialize: function(name, errorMessage, errorClass, errorSymbol){
        this.name = name;
        this.errorMessage = errorMessage;
        this.errorClass = errorClass;
        this.errorSymbol = errorSymbol;
    }
});

var RequiredValidationError = Class.create(ValidationError,{
    initialize: function($super){
        $super("required", "* Please fill out all required fields (marked in red)", "errorRequired", "*");
    }
});

var DOBValidationError = Class.create(ValidationError,{
    initialize: function($super){
        $super("invalid", "* You must be at least 18 to apply \n * Dates must be in the format mm/dd/yyy", "errorInvalid", "*");
    }
});

var DateNotGreaterThanTodayValidationError = Class.create(ValidationError,{
    initialize: function($super){
        $super("invalid", "* The date must be greater than or equal to today", "errorInvalid", "*");
    }
});
var ABAValidationError = Class.create(ValidationError,{
    initialize: function($super){
        $super("invalidaba", "* The ABA Number is not valid", "errorInvalid", "*");
    }
});
var InvalidValueValidationError = Class.create(ValidationError,{
    initialize: function($super){
        $super("invalid", "* Please correct all invalid fields (marked in yellow)", "errorInvalid", "*");
    }
});

var LengthValidationError = Class.create(ValidationError,{
    initialize: function($super){
        $super("length", "* Please make sure all fields have a valid length (marked in yellow)", "errorLength", "*");
    }
});