/**
 * @author WW
 */

/**
 * Class Validation
 * 
 * Simplifies "live" form validation.
 * First: Add each field you want to validate with "addField"
 * Finally: bindtoForm(formname/formref) to add validation
 * 
 * isValid performs validation on the entire form. Useful prior
 * to a submit, will highlight all invalid fields based on validation.
 * 
 * cancelSubmit = true/false;
 * When the form submit button is pressed, the actual submittal of
 * the form will be cancelled. This should be used with the onsubmit
 * property to send the form via AJAX or do whatever you want to do
 * instead of submitting the form.
 * 
 * onsubmit = function();
 * function to execute if the form passes validation. Should only be used
 * if cancelSubmit = true, otherwise this function will be executed as the 
 * browser starts to submit.
 * 
 * 
 * isCommandKey is there if needed to check if a keypress is 
 * a keyboard function key. NOT exhaustive!
 */
var Validation = new Class({
	
	/**
	 * Initilization of Validation class
	 */
	initialize: function() {
		Validation.implement(new Events);
		this.validation_timers = new Object;
		this.commandkeys = [3,8,9,13,16,17,18,19,20,27,33,34,35,36,37,38,39,40,44,45,46,112,113,114,115,116,117,118,119,120,121,122,123,144,145];
		this.form = null;
		this.elements = new Object;
		this.failedvalidation = false;
		this.cancelsubmit = false;
	},
	
	/**
	 * cancelSubmit(cancel)
	 * 
	 * Should the Validation object cancel submittal of the form
	 * 
	 * @param {Boolean} cancel
	 */
	cancelSubmit: function(cancel){
		this.cancelsubmit = cancel;
	},
	
	/**
	 * setOnSubmit(function)
	 * 
	 * Function to execute if the form passes validation. Should only be used
	 * if cancelSubmit = true, otherwise this function will be executed as the 
	 * browser starts to submit.
	 * 
	 * @param {function} func
	 */
	setOnSubmit: function(func) {
			this.onsubmit = func;
	},
	
	/**
	 * isCommandKey(key)
	 * 
	 * Checks if the key pressed is a keyboard command
	 * @param {Integer} key
	 */
	isCommandKey: function(key) {
		if(this.commandkeys.indexOf(key) == -1)
			return false;
		else
			return true;
	},
	
	/**
	 * validateForm()
	 * 
	 * Performs validation across every element in the form
	 * calling the specified functions on each and returning
	 * true if the form passed validation, false if not.
	 */
	validateForm: function() {
		if(this.form) {
			var elements = this.getElements();
			this.failedvalidation = false;
			elements.each(function(el, i) {
				if(this.elements[el.name] !=undefined)
					this.validateField(el, true)
			}.bind(this));
			return !this.failedvalidation;
		}
		return null;
	},
	
	/**
	 * lockForm()
	 * 
	 * Either disables or enables all the form fields
	 * (input, select, textarea) in the form this Validation
	 * is bound to
	 * 
	 * @param {Boolean} lock
	 */
	lockForm: function(lock) {
		if (this.form) {
			var elements = this.getElements();
			elements.each(function(el, i){
				el.disabled = lock
			}.bind(this));
		}
	},
	
	/**
	 * validateField(element)
	 * 
	 * Internal function to validate a field and call the relevant functions
	 * when the field is invalid/valid/empty
	 * 
	 * @param {Object} element
	 */
	validateField: function(element) {
							var validation_info = this.elements[element.name];

							if(validation_info.required != true && element.value.length == 0 && validation_info.onempty != undefined) {
								validation_info.onempty(element);
							} else {	
								if(validation_info.type.constructor.toString().indexOf('function Array') == -1)
									validation_info.type = new Array(validation_info.type);
								validation_info.type.each(function(t, i) {
									switch (t) {
										case 'email':
											var reg = /^([A-Za-z0-9_\-\.\+])+\@([A-Za-z0-9_\-\.]){2,100}\.([A-Za-z]{2,4})$/;
											if(reg.test(element.value) == false) {
												this.failedvalidation = true;
												validation_info.oninvalid(element, 'email'); return false;
											} else {
												validation_info.onvalid(element);
											}
											break;
										case 'integer':
											if(parseInt(element.value) == element.value - 0) {
												validation_info.onvalid(element);
											} else {
												this.failedvalidation = true;
												validation_info.oninvalid(element, 'integer'); return false;
											}
											break;
										case 'float':
											if(parseFloat(element.value) == element.value - 0) {
												validation_info.onvalid(element);
											} else {
												this.failedvalidation = true;
												validation_info.oninvalid(element, 'float'); return false;
											}
											break;
										case 'alpha':
											var reg = /^([A-Za-z\s]+)$/;
											if(reg.test(element.value) == false) {
												this.failedvalidation = true;
												validation_info.oninvalid(element, 'alpha'); return false;
											} else {
												validation_info.onvalid(element);
											}
											break;
										case 'alphaextended':
											var reg = /^([A-Za-z\.\,\?\!\"\(\)\'\;\:\-\`\s]+)$/;
											if (reg.test(element.value) == false) {
												this.failedvalidation = true;
												validation_info.oninvalid(element, 'alphaextended'); return false;
											} else {
												validation_info.onvalid(element);
											}
											break;
										case 'alphanumeric':
											var reg = /^([A-Za-z0-9\s]+)$/;
											if(reg.test(element.value) == false) {
												this.failedvalidation = true;
												validation_info.oninvalid(element, 'alphanumeric'); return false;
											} else {
												validation_info.onvalid(element);
											}
											break;
										case 'alphanumericextended':
											var reg = /^([A-Za-z0-9\.\,\?\!\"\(\)\'\;\:\-\`\s]+)$/;
											if (reg.test(element.value) == false) {
												this.failedvalidation = true;
												validation_info.oninvalid(element, 'alphanumericexpanded');
												return false;
											} else {
												validation_info.onvalid(element);
											}
											break;
										case 'regex':
											if(validation_info.regex != undefined) {
												if (validation_info.regex.test(element.value) == false) {
													this.failedvalidation = true;
													validation_info.oninvalid(element, 'regex');
													return false;
												} else {
													validation_info.onvalid(element);
												}
											}
											break;
										case 'custom':
											if (validation_info.custom != undefined) {
												if (validation_info.custom(element.value) == false) {
													this.failedvalidation = true;
													validation_info.oninvalid(element, 'custom');
													return false;
												}
												else {
													validation_info.onvalid(element);
												}
											}
											break;
									}
									if(validation_info.minlength != undefined && validation_info.minlength != null && parseInt(validation_info.minlength) == validation_info.minlength - 0) {
										if (element.value.length < validation_info.minlength) {
											this.failedvalidation = true;
											validation_info.oninvalid(element, 'minlength');
											return false;
										} else {
											validation_info.onvalid(element);
										}
									}
									
									if(validation_info.maxlength != undefined && validation_info.maxlength != null && parseInt(validation_info.maxlength) == validation_info.maxlength - 0) {
										if (element.value.length > validation_info.maxlength) {
											this.failedvalidation = true;
											validation_info.oninvalid(element, 'maxlength');
											return false;
										} else {
											validation_info.onvalid(element);
										}
									}
								}.bind(this));
							}
	},
	
	/**
	 * bindToForm(form)
	 * 
	 * Binds this validation object to a specific form.
	 * Ensure all fields are added with addFields prior to
	 * calling this!
	 * 
	 * @param {Object} form
	 */
	bindToForm: function(form) {
							form = $(form);
							if (form) {
								form.addEvent('submit', function(event){
									if(this.cancelsubmit == true) {
										if (!event.preventDefault) {
											this.returnValue = false;
										} else {
											event.preventDefault()
										}
									}
									if (this.validateForm()) {
											this.onsubmit();
									}
								}.bind(this));
								this.form = form;
								var elements = this.getElements();
								elements.each(function(el, i) {
									if(el.name.length > 0 && this.elements[el.name] != undefined) {
										el.addEvent('keypress', function(event){
											if(this.validation_timers[el.name] == undefined)
												this.validation_timers[el.name] = 0;
											if(this.validation_timers[el.name] > 0)
												$clear(this.validation_timers[el.name]);
											this.validation_timers[el.name] = this.validateField.pass([el], this).delay(1000);
										}.bind(this));
										el.addEvent('blur', function(event){
											if(this.validation_timers[el.name] == undefined)
												this.validation_timers[el.name] = 0;
											if(this.validation_timers[el.name] > 0)
												$clear(this.validation_timers[el.name]);
											this.validation_timers[el.name] = this.validateField.pass([el], this).delay(1000);
										}.bind(this));
									}
								}.bind(this));
							}
	},
	
	getElements: function() {
		return this.form.getElements('input').extend(this.form.getElements('select')).extend(this.form.getElements('textarea'));
	},
	
	fillForm: function(t) {
		if(typeof t == 'string')
			t = Json.evaluate(t);
		var elements = this.getElements();
	},
	
	/**
	 * addField(fieldname, type, minlength, maxlength, required, onvalid, oninvalid, onempty, custom, regex)
	 * 
	 *  definitions of type fields:
	 * 		email - validates an email
	 * 		integer - must be an integer (no decimal)
	 * 		float - must be a valid number (possibly decimal)
	 * 		alpha - can contain letters and spaces/CR's only
	 * 		alphaextended - alpha + most standard punctuation
	 * 		alphanumeric - can contain letters/spaces/CR's and numbers
	 * 		alphanumericextended - alphanumeric + most standard punctuation
	 * 
	 * 	regex - requires regex variable be set
	 * 	custom - requires custom variable be a function returning true/false
	 * minlength - null or an int for minimum length
	 * maxlength - null or an int for maximum length
	 * required - used during submit, is this field required for submit?
	 * onvalid - function to call when field is good
	 * oninvalid - function to call when field is bad
	 * onempty - function to call when field is empty
	 * custom - function to call when type=custom. must return true or false.
	 * regex - function to call when type=regex. must be a valid regex.
	 * @param {String} fieldname - the NAME of the input/select/textarea
	 * @param {String} type - one of the following validation types:
	 * 		email, integer, float, alpha, alphaextended, alphanumeric, alphanumericextended
	 * @param {Integer} minlength - null or minimum field length
	 * @param {Integer} maxlength - null or maximum field length
	 * @param {Boolean} required - field must be entered to submit form
	 * @param {function} onvalid - function to call if the field is valid
	 * @param {function} oninvalid - function to call if the field is invalid
	 * @param {function} onempty - function to call if the field is empty
	 * @param {function} custom - function to call under type=custom
	 * @param {RegExp} regex - regular expression to test against under type=regex
	 */
	addField: function(fieldname, type, minlength, maxlength, required, onvalid, oninvalid, onempty, custom, regex) {
		element = new Object;
		element.type = type;
		element.minlength = minlength;
		element.maxlength = maxlength;
		element.required = required;
		element.onvalid = onvalid;
		element.oninvalid = oninvalid;
		element.onempty = onempty;
		element.custom = custom;
		element.regex = regex;
		this.elements[fieldname] = element;
	}
});