var InputValidator = new Class({
    initialize: function(className, options){
        this.setOptions({
            errorMsg: 'Validation failed.',
            test: function(field){
                return true
            }
        }, options);
        this.className = className;
    },
    test: function(field){
        if ($(field))
            return this.options.test($(field), this.getProps(field));
        else
            return false;
    },
    getError: function(field){
        var err = this.options.errorMsg;
        if ($type(err) == "function")
            err = err($(field), this.getProps(field));
        return err;
    },
    getProps: function(field){
        if ($(field) && $(field).getProperty('validatorProps')) {
            try {
                return Json.evaluate($(field).getProperty('validatorProps'));
            }
            catch (e) {
                return {}
            }
        }
        else {
            return {}
        }
    }
});
InputValidator.implement(new Options);
var FormValidator = new Class({
    options: {
        fieldSelectors: "input[type='text'],input[type='checkbox'],input[type='radio'], input[type='password'],select,textarea",
        useTitles: false,
        evaluateOnSubmit: true,
        evaluateFieldsOnBlur: true,
        evaluateFieldsOnChange: true,
        serial: true,
        warningPrefix: "Warning: ",
        errorPrefix: "",
        onFormValidate: function(isValid, form){
        },
        onElementValidate: function(isValid, field){
        },
		useTips:false,
		help:null,
		makeAndShowAdvice:true
    },
    initialize: function(form, options){
        this.tips = new $A();
		this.setOptions(options);
        try {
            this.form = $(form);
            if (this.options.evaluateOnSubmit) {
				this.form.addEvent('submit', this.onSubmit.bind(this));
			}
            if (this.options.evaluateFieldsOnBlur) {
				this.watchFields();
			}
			this.addHelp();
        }
        catch (e) {
        }
    },
    getHelp:function(){
		if(this.options.help != null){
			return this.options.help;
		}
		else{
			return [];
		}
	},
	/*CHECK FOR tool-tip*/
	addHelp:function(){
        try {
			this.getHelp().each(function(msg){
				var addClasses = "";
				if(msg.classes != undefined) {addClasses = msg.classes;}

				var createMessage = function(el,classes){
					if(el.getStyle("visibility") != "hidden" && el.getStyle("display") != "none"){
						/*
						 * IE6 reports these as 'inherit' if the parent is hidden, FF2+ reports these as expected
						 */
						var className = el.getAttribute("name").replace(/[^a-zA-Z 0-9]+/g,'');
						var elementExists = false;
						var checkForElement = $ES("div[class^="+ className + "]","body");
						if(checkForElement.length < 1){
						   var boxSize = {x:20 , y:20};
                           var offset = 20;
                           if(window.ie) {
                               switch(el.nodeName){
                                case "TEXTAREA" :
                                    offset = 40;
                                    break;
                                case "INPUT" :
                                    offset = 20;
                                    break;
                               }
                           }

                           el.setStyle('padding-right', boxSize.x + 'px');

						   var cls = $$(className);
						   el.addClass(className);
                            
                           new Element('div',{	'class':className + ' helpmessage ' + classes,
							   					'events':{	'mouseover':function(e){
							   						var toolTip = $E("div.tool-tip","body");
							   						if(!toolTip){
							   							toolTip = new Element('div',{'class':'tool-tip '}).injectInside(document.body);
							   						}

							   						var toolDiv = toolTip.getElement("div");
							   						if(!toolDiv){
							   							toolDiv = new Element('div').injectInside(toolTip);
							   							toolDiv = toolTip.getElement("div");
							   						}

							   						var toolTitle = toolDiv.getElement(".tool-title");
							   						if(!toolTitle){
							   							toolTitle = new Element('div',{'class':'tool-title '}).injectInside(toolDiv);
							   							toolTitle = toolDiv.getElement(".tool-title");
							   						}
							   						toolTitle.setText("");

							   						var toolText = toolDiv.getElement(".tool-text");
							   						if(!toolText){
							   							toolText = new Element('div',{'class':'tool-text '}).injectInside(toolDiv);
							   							toolText = toolDiv.getElement(".tool-text");
							   						}

							   						toolText.setText(this.title);
							   						this.title = "";

							   						var thisCoords = this.getPosition();
							   						toolTip.setStyles({	position: 'absolute',
						   												top: thisCoords.y + 'px',
						   												left: (thisCoords.x + 20) + 'px',
						   												visibility: 'visible'});

						   						},'mouseout':function(e){
							   						var toolTip = $E("div.tool-tip","body");
							   						toolTip.setStyles({	position: 'absolute',
							   												top: -10000 + 'px',
							   												left: -10000 + 'px',
							   												visibility: 'hidden'});
							   						var toolDiv = toolTip.getElement("div");
							   						var toolTitle = toolDiv.getElement(".tool-title");
							   						toolTitle.setText("");
							   						var toolText = toolDiv.getElement(".tool-text");
							   						this.title = toolText.getText();
							   						toolText.setText("");
						   						}},
							   					'title':msg.message}
						   				).setStyles({	position:'absolute',
						   								top: el.getPosition().y + 'px' ,
						   								left: el.getPosition().x + el.getSize().size.x - offset  + 'px',
						   								width: '20px',
						   								height:'20px'}
						   				).injectInside(document.body);
						}

						if(checkForElement[0]){

							if((el.getPosition().y != checkForElement[0].getPosition().y) ||
							   ((el.getPosition().x + el.getSize().size.x) != checkForElement[0].getPosition().x)){
								checkForElement[0].x = el.getPosition().x;
								checkForElement[0].y = el.getPosition().y;
							}
						}
					}
				}

				if(msg.cls != undefined){
	            	   $$("." + msg.cls).each(function(er){
	            		   createMessage(er,addClasses);
	            	   });
				} else if(msg.id != undefined) {
					if($(msg.id)) {
						if(msg.classes != undefined) addClass = msg.classes;
						createMessage($(msg.id),addClasses);
					}
				}


            }, this);
        }
        catch (e) {
        }
	},
	getFields: function(){
        return this.fields = this.form.getElementsBySelector(this.options.fieldSelectors)
    },
    watchFields: function(){
        try {
            this.getFields().each(function(el){
				el.addEvent('blur', this.validateField.pass([el, false], this));
                if (this.options.evaluateFieldsOnChange) {
					el.addEvent('change', this.validateField.pass([el, true], this));
				}
            }, this);
        }
        catch (e) {
        }
    },
    onSubmit: function(event){
        if (!this.validate())
            new Event(event).stop();
        else {
            this.stop();
            this.reset();
        }
    },
    reset: function(){
        this.getFields().each(this.resetField, this);
    },
    validate: function(){
        var result = this.getFields().map(function(field){
            return this.validateField(field, true);
        }, this);
        result = result.every(function(val){
            return val;
        });
        this.fireEvent('onFormValidate', [result, this.form]);
        return result;
    },
    validateField: function(field, force){
        if (this.paused)
            return true;
        field = $(field);
        var result = true;
        var failed = this.form.getElement('.validation-failed');
        var warned = this.form.getElement('.warning');
        if (field && (!failed || force || (failed && !this.options.serial))) {
            var validators = field.className.split(" ").some(function(cn){
                return this.getValidator(cn);
            }, this);
            result = field.className.split(" ").map(function(className){
                return this.test(className, field);
            }, this);
            result = result.every(function(val){
                return val;
            });
            if (validators && !field.hasClass('warnOnly')) {
                if (result)
                    field.addClass('validation-passed').removeClass('validation-failed');
                else
                    field.addClass('validation-failed').removeClass('validation-passed');
            }
            if (!warned || force || (warned && !this.options.serial)) {
                var warnings = field.className.split(" ").some(function(cn){
                    if (cn.test('^warn-') || field.hasClass('warnOnly'))
                        return this.getValidator(cn.replace(/^warn-/, ""));
                }, this);
                field.removeClass('warning');
                var warnResult = field.className.split(" ").map(function(cn){
                    if (cn.test('^warn-') || field.hasClass('warnOnly'))
                        return this.test(cn.replace(/^warn-/, ""), field, true);
                }, this);
            }
        }
        return result;
    },
    getPropName: function(className){
        return '__advice' + className;
    },
    test: function(className, field, warn){
        if (field.hasClass('ignoreValidation'))
            return true;
        warn = $pick(warn, false);
        if (field.hasClass('warnOnly'))
            warn = true;
        field = $(field);
        var isValid = true;
        if (field) {
            var validator = this.getValidator(className);
            if (validator && this.isVisible(field)) {
                isValid = validator.test(field);
                if (!isValid && validator.getError(field)) {
                    if (warn)
                        field.addClass('warning');
                    if(this.options.makeAndShowAdvice){
	                    var advice = this.makeAdvice(className, field, validator.getError(field), warn);
	                    this.insertAdvice(advice, field);
	                    this.showAdvice(className, field);
                    }
                }
                else
                    this.hideAdvice(className, field);
                this.fireEvent('onElementValidate', [isValid, field]);
            }
        }
        if (warn)
            return true;
        return isValid;
    },
    showAdvice: function(className, field){
        var advice = this.getAdvice(className, field);
        if (advice && !field[this.getPropName(className)] && (advice.getStyle('display') == "none" || advice.getStyle('visiblity') == "hidden" || advice.getStyle('opacity') == 0)) {
            field[this.getPropName(className)] = true;
			if (advice.smoothShow) {
				advice.smoothShow();
			}
			else {
				advice.setStyle('display', 'block');
			}
		}
    },
    hideAdvice: function(className, field){
		var advice = this.getAdvice(className, field);
		if (advice && field[this.getPropName(className)]) {
			field[this.getPropName(className)] = false;
			if (advice.smoothHide)
				advice.smoothHide();
			else
				advice.setStyle('display', 'none');
		}
    },
    isVisible: function(field){
        while (field.tagName != 'BODY') {
            if ($(field).getStyle('display') == "none")
                return false;
            field = field.parentNode;
        }
        return true;
    },
    getAdvice: function(className, field){
        return $('advice-' + className + '-' + this.getFieldId(field))
    },
    makeAdvice: function(className, field, error, warn){
        var errorMsg = (warn) ? this.options.warningPrefix : this.options.errorPrefix;
        errorMsg += (this.options.useTitles) ? $pick(field.title, error) : error;
        var advice = this.getAdvice(className, field);
        if (!advice) {
            var cssClass = (warn) ? 'warning-advice' : 'validation-advice';
            advice = new Element('div').addClass(cssClass).setProperty('id', 'advice-' + className + '-' + this.getFieldId(field)).setStyle('display', 'none').appendText(errorMsg);
        }
        else {
            advice.setHTML(errorMsg);
        }
        return advice;
    },
    insertAdvice: function(advice, field){
		if(this.options.useTips) {
	        var pos = field.getPosition();
			var dim = field.getSize();
	        advice.setStyles({
	            'left': pos.x + dim.size.x,
	            'top': pos.y,
				'position':'absolute'
	        });
			advice.injectInside($(document.body));
		}
		else {
			switch (field.type.toLowerCase()) {
				case 'radio':
					var p = $(field.parentNode);
					if (p) {
						p.adopt(advice);
						break;
					}
				default:
					advice.injectAfter($(field));
	        };
		}
    },
    getFieldId: function(field){
        return field.id ? field.id : field.id = "input_" + field.name;
    },
    resetField: function(field){
        field = $(field);
        if (field) {
            var cn = field.className.split(" ");
            cn.each(function(className){
                if (className.test('^warn-'))
                    className = className.replace(/^warn-/, "");
                var prop = this.getPropName(className);
                if (field[prop])
                    this.hideAdvice(className, field);
                field.removeClass('validation-failed');
                field.removeClass('warning');
                field.removeClass('validation-passed');
            }, this);
        }
    },
    stop: function(){
        this.paused = true;
    },
    start: function(){
        this.paused = false;
    },
    ignoreField: function(field, warn){
        if (field = $(field)) {
            this.enforceField(field);
            if (warn)
                field.addClass('warnOnly');
            else
                field.addClass('ignoreValidation');
        }
    },
    enforceField: function(field){
        if (field = $(field)) {
            field.removeClass('warnOnly');
            field.removeClass('ignoreValidation');
        }
    }
});
FormValidator.implement(new Options);
FormValidator.implement(new Events);
FormValidator.adders = {
    validators: {},
    add: function(className, options){
        this.validators[className] = new InputValidator(className, options);
        if (!this.initialize) {
            this.implement({
                validators: this.validators
            });
        }
    },
    addAllThese: function(validators){
	        $A(validators).each(function(validator){
	            this.add(validator[0], validator[1]);
	        }, this);
    },
    getValidator: function(className){
        return this.validators[className];
    }
};
Object.extend(FormValidator, FormValidator.adders);
FormValidator.implement(FormValidator.adders);
FormValidator.add('IsEmpty', {
    errorMsg: false,
    test: function(element){
        if (element.type == "select-one" || element.type == "select")
            return !(element.selectedIndex >= 0 && element.options[element.selectedIndex].value != "");
        else
            return ((element.getValue() == null) || (element.getValue().length == 0));
    }
});
FormValidator.addAllThese([['required', {
    errorMsg: function(element){
        return 'This field is required.'
    },
    test: function(element){
        return !FormValidator.getValidator('IsEmpty').test(element);
    }
}], ['minLength', {
    errorMsg: function(element, props){
        if ($type(props.minLength))
            return 'Please enter at least ' + props.minLength + ' characters (you entered ' + element.getValue().length + ' characters).';
        else
            return '';
    },
    test: function(element, props){
        if ($type(props.minLength))
            return (element.getValue().length >= $pick(props.minLength, 0));
        else
            return true;
    }
}], ['maxLength', {
    errorMsg: function(element, props){
        if ($type(props.maxLength))
            return 'Please enter no more than ' + props.maxLength + ' characters (you entered ' + element.getValue().length + ' characters).';
        else
            return '';
    },
    test: function(element, props){
        return (element.getValue().length <= $pick(props.maxLength, 10000));
    }
}], ['validate-number', {
    errorMsg: 'Please enter a valid number in this field.',
    test: function(element){
        return FormValidator.getValidator('IsEmpty').test(element) || !/[^\d+$]/.test(element.getValue());
    }
}], ['validate-digits', {
    errorMsg: 'Please use numbers only in this field. Please avoid spaces or other characters such as dots or commas.',
    test: function(element){
        return FormValidator.getValidator('IsEmpty').test(element) || (!/[^a-zA-Z]/.test(element.getValue()) && /[\d]/.test(element.getValue()));
    }
}], ['validate-alpha', {
    errorMsg: 'Please use only letters (No numbers)',
    test: function(element){
        return FormValidator.getValidator('IsEmpty').test(element) || /^[a-zA-Z ]+$/.test(element.getValue())
    }
}], ['validate-alphanum', {
    errorMsg: 'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.',
    test: function(element){
        return FormValidator.getValidator('IsEmpty').test(element) || !/\W/.test(element.getValue())
    }
}], ['validate-date', {
    errorMsg: 'Please use this date format: mm/dd/yyyy. For example 03/17/2006 for the 17th of March, 2006.',
    test: function(element){
        if (FormValidator.getValidator('IsEmpty').test(element))
            return true;
        var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
        if (!regex.test(element.getValue()))
            return false;
        var d = new Date(element.getValue().replace(regex, '$1/$2/$3'));
        return (parseInt(RegExp.$1, 10) == (1 + d.getMonth())) && (parseInt(RegExp.$2, 10) == d.getDate()) && (parseInt(RegExp.$3, 10) == d.getFullYear());
    }
}], ['validate-email', {
    errorMsg: 'Please enter a valid email address. For example email@website.com.',
    test: function(element){
        return FormValidator.getValidator('IsEmpty').test(element) || /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/.test(element.getValue());
    }
}], ['validate-url', {
    errorMsg: 'Please enter a valid URL.',
    test: function(element){
        return FormValidator.getValidator('IsEmpty').test(element) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(element.getValue());
    }
}], ['validate-date-au', {
    errorMsg: 'Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.',
    test: function(element){
        if (FormValidator.getValidator('IsEmpty').test(element))
            return true;
        var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
        if (!regex.test(element.getValue()))
            return false;
        var d = new Date(element.getValue().replace(regex, '$2/$1/$3'));
        return (parseInt(RegExp.$2, 10) == (1 + d.getMonth())) && (parseInt(RegExp.$1, 10) == d.getDate()) && (parseInt(RegExp.$3, 10) == d.getFullYear());
    }
}], ['validate-currency-dollar', {
    errorMsg: 'Please enter a valid $ amount. For example $100.00 .',
    test: function(element){
        return FormValidator.getValidator('IsEmpty').test(element) || /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(element.getValue());
    }
}], ['validate-one-required', {
    errorMsg: 'Please enter something for at least one of the above options.',
    test: function(element){
        var p = element.parentNode;
        var options = p.getElements('input');
        return $A(options).some(function(el){
            return el.getValue();
        });
    }
}]]);
