/*
FormValidation Class

- Validates a form on its values
- Needed element properties	
	[required="true|false"] --> false will only validate when value is entered (not explicitely needed element)
	[subtype="phone|integer|zipcode|email|general"]
	[minlength="[1-n]"]
	[maxlength="[1-n]"]	
- possible element properties
	[regexp]
	[allowfirst] -> for select elements
	[confirm] -> sets password confirmation element (not needed)
- Possible elements
	<input type="text|password" />
	<input type="radio|checkbox" />
	<select></select>
	<textarea></textarea>	
	
If the "required" attribute on an element is not specified, then the validator will exclude the element from validation. If the "required"
attribute is "false", validation will only occur on the element *if* it has a value.

Selects are validated if an option is selected. The only other validation control for a Select is if the "allowfirst" attribute
is present and its value is "true". If this is the case, the first Option (at index zero) will not be valid for selection.

Radios/Checkboxes work in a similar fashion to Selects. If a Radio is checked, then it is valid - likewise with Checkboxes.

Textareas/Text/Password inputs have multiple validation types. See the functions after Validation._validateSelect in this js
file to get an idea of what's available.

*/
var FormValidation = new Class({
	options: {
		onElementValidating: Class.empty,
		onElementValid: Class.empty,
		onElementInvalid: Class.empty,
		onFormValid: Class.empty,
		onFormInvalid: Class.empty
	},
	initialize: function (oForm, options) {
		this.setOptions(this.options, options);
		this.form = oForm;
		this.elements = $$("form#" + oForm.id + " input, select, textarea").filterByAttribute("required");
		this.validators = {
			"password": this._password,
			"phone": this._phone,
			"integer": this._integer,
			"float": this._float,
			"zipcode": this._zipcode,
			"email": this._email,
			"general": this._general,
			"regex": this._regex,
			"address": this._address
		};		
		this.messages = [];
		this._failures = [];
	},
	validate: function () {
		var bValid = false;
		
		this._failures = [];
		this.messages = [];
		
		this.elements.each(function(el) {
			var bRequired = (el.getProperty("required") == "true") ? true : false;
			
			switch(el.getTag()) {				
				case "input":
				case "textarea":					
					switch(el.type) {
						case "radio":
						case "checkbox":
							this._validateRadioCheckbox(el, bRequired)
							break;
						default:
							this._validateTextArea(el, bRequired);
							break;
					}
					break;
				case "select":
					this._validateSelect(el, bRequired);
					break;		
			}
			
		}, this);
		
		if (this._failures.contains(false)) {
			return this._onFormInvalid();
		}
		
		return this._onFormValid();
	},
	// If a named Radio has one of its elements checked, it is implicitly valid, since the user does not have control over the value
	// of a Radio. Same deal with Checkboxes, except obviously you can check multiple boxes if they have the same name
	_validateRadioCheckbox: function(rc, bRequired) {
		var bChecked = false;
		
		$$("form#" + rc.form.id + " input").filterByAttribute("name", "=", rc.name).filterByAttribute("type", "=", rc.type).each(function(el) {
			if (el.checked) bChecked = true;
		});
				
		if (!bChecked) {
			this._onElementInvalid(rc, "Element '" + rc.name + "' must be selected");
		} else {
			this._onElementValid(rc);
		}
	},
	// Validate a Text input or Textarea against its defined subtype (default to "general" if subtype is missing)
	// Will also pitch a fit if subtype="regex" and no regex attribute is found
	_validateTextArea: function(it, required) {
		var subType = ($defined(it.getProperty("subtype"))) ? it.getProperty("subtype") : "general";
		
		if (required || (!required && it.getValue().length > 0)) {
			if (subType == "regex" && !$defined(it.getProperty("regex"))) {
				subType = "general";

				this._onElementInvalid(it, "Element: '" + it.name + "' attempting to use regex without regex pattern attribute");
			}
			
			if (!this._validateFilter(subType, it)) {
				this._onElementInvalid(it, "Element: '" + it.name + "' is not a valid " + subType);
			} else {
				var minLength = (it.getProperty("minlength") != -1) ? parseInt(it.getProperty("minlength")) : null;
				var maxLength = (it.getProperty("maxlength") != -1) ? parseInt(it.getProperty("maxlength")) : null;
				
				if (it.type == "textarea") {
					// textareas deny they have a maxlength attribute unless you beat it out of them
					maxLength = ($chk(it.attributes.maxlength)) ? it.attributes.maxlength.nodeValue : maxLength;
				}
				
				var validMinMax = this._testMinMax(it.getValue().length, minLength, maxLength);
				
				if ([validMinMax.vMin, validMinMax.vMax, validMinMax.vEqual].contains(false)) {
					var errMinMax = "between " + minLength + " and " + maxLength;

					if ($chk(minLength) && !$chk(maxLength)) errMinMax = "at least " + minLength;
					if (!$chk(minLength) && $chk(maxLength)) errMinMax = "less than or equal to " + maxLength;
					if (!validMinMax.vEqual) errMinMax = "exactly " + minLength;

					this._onElementInvalid(it, "Element: '" + it.name + "' must be " + errMinMax + " characters in length");
				} else {
					this._onElementValid(it);
				}
			}
		} else if (!required && it.getValue().length <= 0) {
			this._onElementValid(it);
		}
	},
	// Validate a Select. Is the first option allowed? No? Then error. Otherwise, the Select is implicitly valid
	_validateSelect: function(s, required) {
		var allowFirst = ($defined(s.getProperty("allowfirst")) && s.getProperty("allowfirst") == "true") ? true : false;
		
		if (required && (!allowFirst && s.options.selectedIndex == 0)) {
			this._onElementInvalid(s, "Element: '" + s.name + "' does not allow the first option to be selected");
		} else {
			this._onElementValid(s);
		}
	},
	_validateFilter: function(subType, el) {
		if (subType == "regex") return this.validators["regex"](el);
		else if (subType == "password") return this.validators["password"](el)
		return this.validators[subType](el.getValue());
	},
	
	// Test whether or not the element contains a value which is greater than <minlength> and less than or equal to <maxlength>
	// (providing one or both are defined). If minlength and maxlength are equal, then the length of the value in the element
	// must be *exactly* the number of characters that both minlength and maxlength are set to.
	_testMinMax: function(valueLength, minLength, maxLength) {
		var validMinMax = { vMin: true, vMax: true, vEqual: true };
		
		if ($chk(minLength) && $chk(maxLength)) {
			if (minLength == maxLength) {
				if (valueLength != maxLength) validMinMax.vEqual = false;
				return validMinMax;
			}
		}
		
		if ($chk(minLength) && (valueLength < minLength)) validMinMax.vMin = false;
		if ($chk(maxLength) && (valueLength > maxLength)) validMinMax.vMax = false;
		
		return validMinMax;
	},
	_general : function(val) {
		return /\b[a-zA-Z0-9.&_%+-]{1,}\b/i.test(val);
	},
	_password : function(el) {						
		var oConfirm;
				
		if (!/[a-zA-Z0-9.&_%+-]{1,}\b/i.test(el.getValue())) return false;		
		if ($defined($(el.getProperty("confirm")))) {
			oConfirm = $(el.getProperty("confirm"));			
			if (!(el.getValue() == oConfirm.getValue())) return false;		
		}
		
		return true;
	},
	_zipcode : function(val) {
		return /\d{4}\s?[a-z]{2}/i.test(val);
	},
	_address : function(val) {
		return /\w{1,50}?\s?\w{1,50}?\s?\w{1,50}?\s?[0-9]{1,20}\s?\w{0,50}/i.test(val);
	},
	_email : function(val) {
		return /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i.test(val);
	},
	_phone : function(val) {
		return /^(([0-9]{1})*[- .(]*([0-9]{3})[- .)]*[0-9]{3}[- .]*[0-9]{4})+$/i.test(val);
	},
	_integer : function(val) {
		return /[0-9-]{1,}/i.test(val);
	},
	_float : function(val) {
		return /^([0-9]\d*\.|0\.)[0-9]*\d+$/.test(val);
	},
	_regex: function(el) {
		var sValue = el.getValue();
		var sRegex = el.getProperty("regex");
		return sRegex.test(sValue);
	},
	_onElementValidating: function(el) {		
		this.fireEvent("onElementValidating", el);	
	},
	_onElementValid: function(el) {
		this.fireEvent("onElementValid", el);	
	},
	_onElementInvalid: function(el, err) {		
		this._failures.include(false);
		this.messages.include(err);
		this.fireEvent("onElementInvalid", [el, err]);	
	},
	_onFormValid: function() {		
		this.fireEvent("onFormValid", this.form);	
		return true;
	},
	_onFormInvalid: function() {		
		this.fireEvent("onFormInvalid", [this.form, this.messages]);
		return false;
	},
	submit: function() {		
		this.form.submit();
	}
});
FormValidation.implement(new Options, new Events);

var switchRequired = function(id,elem){
	var el = $(id);
	if(elem.options[elem.selectedIndex].value != "NL"){
		el.setProperty('required','false');
		el.setProperty('subtype','general');
	}else{
		el.setProperty('required','true');
		el.setProperty('subtype','zipcode');		
	}
	
}
