var FormValidator = Class.create({
    initialize: function(element, options) {
        this.options = {
            onSubmit: null,
            submitForm: true
        };
        Object.extend(this.options, options || { });
        this.element = $(element);
        this.regExps = {
            // accept only a-z and 0-9
            ALPHA_NUMERIC_EXP:/^[a-zA-Z0-9]+$/,
            // accept only 0-9
            NUMERIC_EXP:/^[0-9]+$/,
            //accept letters, numbers space and parens to handle (123)555-1234 ext 1234
            PHONE_EXP: /^[-a-zA-Z0-9\s\(\)]+$/,
            //Email addresses
            EMAIL_EXP:/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))$/,
            //accept only a-z, 0-9, space, -, _,
            ALPH_NUM_BLANK_HYP_UNDSCOR_EXP: /^[-a-zA-Z0-9\s_]+$/,
            //accept only a-z, 0-9, space, - and '
            ALPH_NUM_BLANK_HYP_QUOT_EXP: /^[-a-zA-Z0-9\s']+$/,
            //accept letters, space, numbers, single quote and the characters !@#$%^&*()_+=-{}|\][:;~`,./?><
            ALL_NODBLQUOTES_EXP: /^[]\[a-zA-Z0-9\s\(\)~`!@#\$%\^&\*_\+=\{\}\\\|:;\<\>,\.\?\/\'-]+$/,
            //accept letters, space, numbers, single quote and the characters ()!@#$_+=|:;,.?''-
            SAFE_CHARACTERS_EXP: /^[a-zA-Z0-9\s\(\)!@#\$_\+=\|:;,\.\?\/\'-]+$/
        };
        
        //initilize validators
        this.fieldValidators = [];
        this.fieldWatchers = [];
        this.element.select('*.required').each(function (input){
            var classNames = ['validate_minlength_', 'validate_minnums_', 'validate_minalpha_'];
            var values = [1, 0, 0];
            
            for(var i=0; i<classNames.length; i++){
                var name = input.className
                var index = name.indexOf(classNames[i]);
                if(index != -1)
                    values[i] = new Number(name.substr(name.indexOf(classNames[i])+classNames[i].length, 1));
            }
            this.add(new FieldValidator(input, {minLength:values[0], minNums:values[1], minAlpha:values[2]}));
        }.bind(this));

        this.element.select('[class*=validate_match_]').each(function (input){
            var classes = input.className;
            var name = 'validate_match_';
            var index = classes.indexOf(name);
            var spaceIndex = classes.indexOf(' ',index);
            if (spaceIndex == -1)
                spaceIndex = classes.length;
            var id = classes.substring(index+name.length, spaceIndex);
            this.add(new FieldValidator(input, {type: 'match', matchElement: id}));
        }.bind(this));

        this.element.select('.validate_email').each(function (input){
            this.add(new FieldValidator(input, {type:'regExp', regExp: this.regExps.EMAIL_EXP}));
        }.bind(this));

        this.element.select('.validate_phone').each(function (input){
            this.add(new FieldValidator(input, {type:'regExp', regExp: this.regExps.PHONE_EXP}));
        }.bind(this));
        
        this.element.select('.validate_number').each(function (input){
            this.add(new FieldValidator(input, {type:'regExp', regExp: this.regExps.NUMERIC_EXP}));
        }.bind(this));

        this.element.select('input.validate_pradate').each(function (input){
            this.add(new FieldValidator(input, {type:'pradate'}));
        }.bind(this));
        
        this.element.observe('submit',this.onSubmit.bind(this));
        this.element.select('.validate_clear').each(function(el){
            el.observe('click',this.clear.bindAsEventListener(this));
        }.bind(this));

    },
    add: function(fieldValidator){
        this.fieldValidators[this.fieldValidators.size()] = fieldValidator;
    },
    valid: function(){
        this.clear();
        var errCount = 0;
        for (i=0; i < this.fieldValidators.size(); i++){
            var field = this.fieldValidators[i];
            if (!field.valid()){
                errCount = errCount + 1;
                var id = field.element.identify();
                field.element.addClassName('validate_error');
                this.element.select('[for="'+id+'"]').invoke('addClassName', 'validate_error');
                this.element.select('.validate_hint.for_'+id).invoke('show');
                this.fieldWatchers[this.fieldWatchers.size()] = new PeriodicalExecuter(function(pe){
                    var form = this.element.up('form');
                    var id = this.element.identify();
                    if(this.valid()){
                        this.element.removeClassName('validate_error');
                        form.select('[for="'+id+'"]').invoke('removeClassName', 'validate_error');
                        form.select('.validate_hint.for_'+id).invoke('hide');
                        pe.stop();
                    }else{
                        this.element.addClassName('validate_error');
                        form.select('[for="'+id+'"]').invoke('addClassName', 'validate_error');
                        form.select('.validate_hint.for_'+id).invoke('show');
                    }
                }.bind(field), 0.3);
            }
        }
        return (errCount == 0);
    },
    onSubmit: function(ev){
        var isValid = this.valid();
        
        if(Object.isFunction(this.options.onSubmit))
        	try{
        		this.options.onSubmit(isValid);
        	}catch(er){}
        if(!isValid || !this.options.submitForm)
            ev.stop();
    },
    clear: function(ev){
        if (ev != null){ ev.stop(); this.element.reset();}
        this.element.select('.validate_error').invoke('removeClassName', 'validate_error');
        this.element.select('.validate_hint').invoke('hide');
        this.killWatchers();
    },
    killWatchers: function(){
    	this.fieldWatchers.invoke('stop');
    }
});

var FieldValidator = Class.create({
    initialize: function(element, options) {
        this.options = {
            type: 'required',
            errorMessage: 'Field is invalid.',
            label: null,
            minLength: 1,
            minNums: 0,
            minAlpha: 0,
            regExp: null,
            matchElement: null
        };
        Object.extend(this.options, options || { });
        this.element = $(element);
        if (this.options.label == null)
            this.options.label = this.element.previous('label');
        else
            this.options.label = $(this.options.label);
        if (this.options.matchElement != null)
            this.options.matchElement = $(this.options.matchElement);
    },
    valid: function(){
        if(this.element.disabled || !this.element.visible() || !this.element.ancestors().invoke('visible').all())
        	return true;
        var val = this.element.getValue();
        var retVal;
        if (this.options.type == 'required')
            retVal = (this.element.present() && val != null && val.strip().length >= this.options.minLength && this.testNums(val) && this.testAlpha(val))
        else if (this.options.type == 'match')
            retVal = (this.options.matchElement.getValue() == val);
        else if (this.options.type == 'pradate'){
            retVal = (!isNaN(Date.parse(Date.parsePRAString(val))));
        }
        else if (this.options.type == 'regExp')
            if (this.options.regExp == null)
                retVal = false;
            else
                retVal = this.options.regExp.test(val.strip());
        else
            retVal = false;
        return retVal;
    },
    testNums: function(val){
        var results = val.match(/[0-9]/);
        if(results == null) results = [];
        return (results.size() >= this.options.minNums);
    },
    testAlpha: function(val){
        var results = val.match(/[a-zA-Z]/);
        if(results == null) results = [];
        return (results.size() >= this.options.minAlpha);
    }
});
document.observe('dom:loaded',
            function() {
                $$('form.validate').each(function(form){
                    new FormValidator(form);
                });
            }
        );