/*******
*	
*	FormHandler base class for checking required fields etc
*
* 	By: joacim.magnusson@orbitsville.com
*
* 	Currently dependent on mootools, http://www.mootools.net/ 
*
*/

/*****
* USAGE:
*
* 1. To make an input field required, add "required" to it's class. Ex: class="textinput required". Works with most elements except radio buttns.
* 2. Radio buttns needs to be enclosed in required spans, since there are always more than one radio button. The span checks that at least one of the buttons are checked
*		Example: <span class="required radiobuttons">[various HTML including radiobuttns to be checked]</span>
* 3. To validate type of data in a field, add email, date or number to the class. Ex: class="textinput required email"
* 4. The submit button will always issue a form validation before the form isFinite submitted. To override this, add "nocheck" to the submit buttons class.
*	 This will cause it to force-submit the form without any validation.
* 5. To specify fields that must match, supply an array of the fields that must match using the "identical" option when initializing an instance of of the class.
*		Example: new FormHandler({form: 'theFormId', identical:['field1ID', 'field2ID']});
*/

/********
*
* 	Version history
* 	0.1: initial version
* 	0.2: added support for specifying fields that must match
* 	0.3: added support for selects and email validation + added this fancy header
*	0.4: Can now handle "clusters" of input fields, enveloped in a span. This is useful for checking radiobuttons and checkboxes.
*		 Example usage <span class="required radiobuttons">[various HTML including radiobuttns to be checked]</span>
*		0.4.1: Added date and number validation
*		0.4.2: Cleaned up some code + added USAGE notes to the header.
*		0.4.3: Fixed an issue where multiple forms would conflict.
*/

var FormHandler = new Class({
/* v0.4.3 */

	Implements: Options,
	
	options: {
		required: 'required',
		output: 'FHOutput',
		errorMsg: 'Please fill out all required fields.',
		
		/* Default regexps */
		reEmail: /^[!-'*+=?{-~\/-9A-Z^-z-]+(\.[!-'*+={-~\/-9A-Z^-z-]+)*@[!#$%&'*+"\/=?^_`{|}~0-9A-Za-z-]+\.[!-'*+={-~\/-9A-Z^-z-]{2,}/,
		reDate: /^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d/,
		reNumber: /^[0-9]+$/
	},
	
	initialize: function(options)
	{
		this.setOptions(options);
		this._output = $(this.options.output);
		this._form = $(this.options.form);
		this._form.onsubmit = this.testSubmit.bind(this);
		this._clsRequired = this.options.required;
		this._identical = this.options.identical;
		this._arrErrors = new Array;
		this._arrMsgs = new Array;
		
		// Register ovverride buttons that will force-submit the form without
		// checking the fields
		$$("input[type=submit]").each(function(btn,index)
		{
			if(btn.hasClass("nocheck")){
				btn.addEvent('click',this.forceSubmit.bind(this));
			}
		}.bind(this));
		
		// Default messages
		if(this.options.errorMsg) {
			this._errorMsg = this.options.errorMsg;
		} else {
			this._errorMsg = "Please fill out all required fields";
		}
	},
	
	testSubmit: function()
	{	
		// Clean up previous run
		if(this.dirty == true) {
			this._arrErrors.each(function(objErr, index){
				objErr.elmInput.removeClass('error');
			});
			this._arrMsgs.each(function(elm, index){
				elm.remove();
			});
			
			if(this._output) {this._output.setText("");}
			this._arrErrors = new Array;
			this._arrMsgs = new Array;
		}
		
		if(this.hasErrors()) {
			if(this._output) {
				this._output.setText(this._errorMsg);
			}
			
			this._arrErrors.each(function(objErr, index){
				if(objErr.strMsg != null) {
					var errorText = new Element('span',{'class':'error'}).injectAfter(objErr.elmInput);
					errorText.setText(objErr.strMsg);
					this._arrMsgs.include(errorText);
				}
			}.bind(this));
			
			this.dirty = true;
			return false;
		} else {
			return true;
		}
	},
	
	hasErrors: function()
	{
		this._arrErrors = new Array;
		var inputs = this._form.getElements("input,textarea,select,span");
		inputs.each(function(elm,index){
			
			/* Only check field if it has the reuired class */
			if(elm.hasClass(this._clsRequired) && elm.style.display != "none") {
				switch(elm.getProperty("type")){
				case "text":
				case "password":
					
					/* Check that the field is not empty */
					if(elm.value == "") {
						elm.addClass('error');
						this._arrErrors.include({elmInput:elm,strMsg:null});
						
					} else if(elm.hasClass('email')) {
						
						/* This field has the email class, so check for valid email */
						if(!this.options.reEmail.test(elm.value)) {
							elm.addClass('error');
							this._arrErrors.include({elmInput:elm,strMsg:"This email is not valid"});
						}
					} else if(elm.hasClass('date')) {
						
						/* This field has the email class, so check for valid email */
						if(!this.options.reDate.test(elm.value)) {
							elm.addClass('error');
							this._arrErrors.include({elmInput:elm,strMsg:"This date is not valid (use dd/mm/yyyy)"});
						}
					} else if(elm.hasClass('number')) {
						
						/* This field has the email class, so check for valid email */
						if(!this.options.reNumber.test(elm.value)) {
							elm.addClass('error');
							this._arrErrors.include({elmInput:elm,strMsg:"This number is not valid"});
						}
					}
					
					break;
				
				case "radio":
					// All your radio buttons are belong to spans.
					// See the handling of spans below
				break;
				
				case "checkbox":
					
					if(!elm.checked) {
						elm.addClass('error');
						this._arrErrors.include({elmInput:elm,strMsg:null});
					}
					
					break;
				
				/* Handles stuff like selects and textareas */
				default:
				if(elm.getTag() == "select") {
					//alert("Name: "+elm.name+" Value: '"+elm.value+"'")
					if(elm.value == "" || elm.value == "0") {
						elm.addClass('error');
						this._arrErrors.include({elmInput:elm,strMsg:null});
					}
				}
				if(elm.getTag() == "textarea") {
					if(elm.value == "") {
						elm.addClass('error');
						this._arrErrors.include({elmInput:elm,strMsg:null});
					}
				}
				
				// Required spans are used for groups of checkboxes or radiobuttons
				if(elm.getTag() == "span") {
					
					if(elm.hasClass("radiobuttons") || elm.hasClass("checkboxes")) {
						this.foundChecked = false;
						elm.getElements("input").each(function(elmRb)
						{
							if(elmRb.checked)
								this.foundChecked = true;
						}.bind(this));
						
						if(!this.foundChecked) {
							elm.addClass('error');
							this._arrErrors.include({elmInput:elm,strMsg:null});
						}
						
						this.foundChecked = null;
					}
					
				}
				break;
				}
			}
			
			/* Also check valid email on fields that are not required, and doesn't already have an error */
			if(elm.hasClass('email') && !elm.hasClass('error') && elm.style.display != "none") {
				if(!this.options.reEmail.test(elm.value)) {
					elm.addClass('error');
					this._arrErrors.include({elmInput:elm,strMsg:"This email is not valid"});
				}
			}
			
		}.bind(this));
		
		if(this._identical) {
			this.tmpVal = $(this._identical[0]).value;
			this.tmpDiffer = false;
			
			this._identical.each(function(elm,index,val1) {
				if($(elm).value != this.tmpVal){
					this.tmpDiffer = true;
					$(elm).addClass('error');
					this._arrErrors.include({elmInput:$(elm),strMsg:"These fields must match"});
				}
			}.bind(this));
			
			if(this.tmpDiffer == true){
				
				$(this._identical[0]).addClass('error');
				this._arrErrors.include({elmInput:$(this._identical[0]),strMsg:"These fields must match"});
			
			}
			
			/* Clean up the mess */
			this.tmpVal = null;
			this.tmpDiffer = null;
		}
		
		if(this._arrErrors.length < 1) {
			return false;
		} else {
			return true;
		}
	},
	
	/* Removes the onsubmit event, so that the checking will not occur */
	forceSubmit: function()
	{
		this._form.onsubmit = null;
	},
	
	validates: function() { return this.testSubmit() }
	
});

