
var fValidator = new Class({
	Implements: [Options, Events],
	options: {
		msgContainerTag: "span",
		msgClass: "fValidator-msg",

		styleNeutral: {},
		styleInvalid: {"background-color": "#fcc", "border-color": "#c00"},
		styleValid: {"background-color": "#cfc", "border-color": "#0c0"},

		language: "nl",
		languageConfig: {
			en: {	required:	"* This field is required.",
					alpha:		"* This field accepts alphabetic characters only.",
					alphanum:	"This field accepts alphanumeric characters only.",
					integer:	"Please enter a valid integer.",
					real:		"Please enter a valid number.",
					date:		"Please enter a valid date (mm/dd/yyyy).",
					dateISO8601:"Please enter a valid date (yyyy-mm-dd).",
					dateEU:		"Please enter a valid date (dd-mm-yyyy).",
					email:		"Please enter a valid email.",
					phone:		"Please enter a valid phone.",
					url:		"Please enter a valid url.",
					zip:		"Please enter a valid postal code",
					confirm:	"Confirm Password does not match original Password."
				},
			nl: {	required:	"* Dit veld is verplicht",
					alpha:		"* U kunt in dit veld alleen karakters uit het alphabet invoeren",
					alphanum:	"* U kunt in dit veld alleen alphanumerieke karakters invoeren",
					integer:	"* Voer een geheel getal in",
					real:		"* Voer een getal in",
					date:		"* Voer een geldige datum in (mm/dd/yyyy)",
					dateISO8601:"* Voer een geldige datum in (yyyy-mm-dd)",
					dateEU:		"* Voer een geldige datum in (dd-mm-yyyy)",
					email:		"* Voer een geldig e-mailadres in",
					phone:		"* Voer een geldig telefoonnummer in",
					url:		"* Voer een geldige url in",
					zip:		"* Voer een geldige postcode in (vb. 1234 XX)",
					confirm:	"* Het bevestigingswachtwoord komt niet overeen met het originele wachtwoord"
				}
		},

		required: {type: "required", re: /[^.*]/},
		alpha: {type: "alpha", re: /^[a-z ._-]+$/i},
		alphanum: {type: "alphanum", re: /^[a-z0-9 ._-]+$/i},
		integer: {type: "integer", re: /^\d+$/},
		real: {type: "real", re: /^[-+]?\d*\.?\d+$/},
		date: {type: "date", re: /^((((0[13578])|([13578])|(1[02]))[\/](([1-9])|([0-2][0-9])|(3[01])))|(((0[469])|([469])|(11))[\/](([1-9])|([0-2][0-9])|(30)))|((2|02)[\/](([1-9])|([0-2][0-9]))))[\/]\d{4}$|^\d{4}$/},
		dateISO8601: {type: "dateISO8601", re: /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/},
		dateEU: {type: "dateEU", re: /^(((([1-9])|([0-2][0-9])|(3[01]))[-]((0[13578])|([13578])|(1[02])))|((([1-9])|([0-2][0-9])|(30))[-]((0[469])|([469])|(11)))|((([1-9])|([0-2][0-9])))[-](2|02))[-]\d{4}$|^\d{4}$/},
		email: {type: "email", re: /^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i},
		phone: {type: "phone", re: /^[\d\s ().-]+$/},
		url: {type: "url", re: /^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&amp;%\$#\=~])*$/i},
		confirm: {type: "confirm"},
		zip: {type: "zip", re: /^[0-9]{4}\040?[a-z]{0,2}$/i},

		onValid: Class.empty,
		onInvalid: Class.empty
	},

	initialize: function(UpperDiv, fm,  options) {

		if(Browser.Engine.trident) {
			this.form = $(UpperDiv);
		} else {
			this.form = $(fm);
		}

		this.setOptions(options);

		this.fields = this.form.getElements("*[class^=fValidate]");
		this.validations = [];

		this.fields.each(function(element) {
			if(!this._isChildType(element)) {
				element.setStyles(this.options.styleNeutral);
			}
			
			var classes = element.getProperty("class").split(' ');
			classes.each(function(klass) {
				if(klass.match(/^fValidate(\[.+\])$/)) {
					var aFilters = eval(klass.match(/^fValidate(\[.+\])$/)[1]);
					for(var i = 0; i < aFilters.length; i++) {
						if(this.options[aFilters[i]]) {
							this.register(element, this.options[aFilters[i]]);
						}
						if(aFilters[i].charAt(0) == '=') {
							this.register(element, $extend(this.options.confirm, {idField: aFilters[i].substr(1)}));
						}
					}
				}
			}.bind(this));
		}.bind(this));

		// IE fix, voor mdgict
		if(Browser.Engine.trident) {
			temp = document.getElementById(fm);
			temp.onsubmit = function (e) {
				if (!e) var e = window.event
				return this._onSubmit(e,this.options).bind(this);
			}.bind(this);
		} else {
			this.form.addEvents({
				"submit": this._onSubmit.bind(this),
				"reset": this._onReset.bind(this)
			});
		}
	},

	register: function(field, options) {
		field.cbErr = 0;
		field = $(field);
		this.validations.push([field, options]);
		field.addEvent("blur", function() {
			this._validate(field, options);
		}.bind(this));
		
	},

	_isChildType: function(el) {
		var elType = el.type.toLowerCase();
		if((elType == "radio") || (elType == "checkbox")) {
			return true;
		}
		return false;
	},

	_validate: function(field, options) {
		
		if(isNaN(field.cbErr)) {
			field.cbErr = 0;
		}
		
		switch(options.type) {
			case "confirm":
				if($(options.idField).get('value') == field.get('value')) {
					this._msgRemove(field, options);
				} else {
					this._msgInject(field, options);
				}
				break;
			default:
				
				if(options.re.test(field.get('value'))) {
					this._msgRemove(field, options);
					this._chkStatus(field, options);
				} else { 
					this._msgInject(field, options);
				}
		}
	},

	_validateChild: function(child, options) {
		var nlButtonGroup = this.form[child.getProperty("name")];
		var cbCheckeds = 0;
		var isValid = true;
 		for(var i = 0; i < nlButtonGroup.length; i++) {
			if(nlButtonGroup[i].checked) {
				cbCheckeds++;
				if(!options.re.test(nlButtonGroup[i].get('value'))) {
					isValid = false;
					break;
				}
			}
		}
 		// Required
		if(cbCheckeds == 0 && options.type == "required") {
			isValid = false;
		}
		
		// Valid?
		if(isValid) {
			this._msgRemove(child, options);
		} else {
			this._msgInject(child, options);
		}
	},

	_msgInject: function(owner, options) {
		
		if(!$(owner.getProperty("id") + options.type +"_msg")) {
	
			var msgContainer = new Element(this.options.msgContainerTag, {"id": owner.get("id") + options.type +"_msg", "class": this.options.msgClass})
				.set('html', this.options.languageConfig[this.options.language][options.type])
				.setStyle("opacity", 0)
				.injectAfter(owner);
			owner.cbErr++;
				var myFx = new Fx.Tween(msgContainer, {
					duration: 500,
					transition: Fx.Transitions.linear
				}).start('opacity', 0, 1);
				
			this._chkStatus(owner, options);
		
		}
	},

	_msgRemove: function(owner, options, isReset) {
		isReset = isReset || false;
		if($(owner.getProperty("id") + options.type +"_msg")) {
			var el = $(owner.getProperty("id") + options.type +"_msg");
			var myFx = new Fx.Tween(el, {
					duration: 500,
					transition: Fx.Transitions.linear,
					onComplete: function() {el.destroy()}
				}).start('opacity', 1, 0);
			
			owner.cbErr--;
			this._chkStatus(owner, options);
			this._green(owner);
		}
	},
	_green : function (field) {
		
		var myFx = new Fx.Morph(field, {
			duration: 500,
			transition: Fx.Transitions.linear
		}).start(this.options.styleValid);
	},
	_chkStatus: function(field, options) {
		
		if(field.cbErr <= 0) {
			
			var myFx = new Fx.Morph(field, {
					duration: 500,
					transition: Fx.Transitions.linear
				}).start(this.options.styleValid);
			this.fireEvent("onValid", [field, options], 50);
		} else {
			var myFx = new Fx.Morph(field, {
					duration: 500,
					transition: Fx.Transitions.linear
				}).start(this.options.styleInvalid);
			this.fireEvent("onInvalid", [field, options], 50);
		}
	},

	_onSubmit: function(event) {
		
		var event = new Event(event);
		var isValid = true;
		this.validations.each(function(array) {
			if(this._isChildType(array[0])) {
				this._validateChild(array[0], array[1]);
			} else {
				
				this._validate(array[0], array[1]);
			}
			// Valid
			
			if(array[0].cbErr > 0) {
				isValid = false;
			}
		}.bind(this));
		
		if(!isValid) event.stop();

		return isValid;
	},

	_onReset: function() {
		this.validations.each(function(array) {
			if(!this._isChildType(array[0])) array[0].setStyles(this.options.styleNeutral);
			array[0].cbErr = 0;
			this._msgRemove(array[0], array[1], true);
		}.bind(this));
	}
});
