FormValidator = Class.create();
FormValidator.prototype = {
	initialize: function(options){
		this.options = Object.extend({
			errorClass: 'form_error',
			baseInputClass: 'form_input',
			baseSelectClass: 'form_input',
			srcElement: null,
			persistantLabels: false,
			focusFirstError: true
		}, options);
		
		this.messageElements = [];
		this.eventListeners  = [];
		
		this.bindListeners();
	},
	
	bindListeners: function(){
		//loop inputs & bind listeners if needed
		var inputs = $A($(this.options.srcElement).getElementsByTagName('input'));
		inputs = inputs.concat($A($(this.options.srcElement).getElementsByTagName('textarea')));
		for(var i=0; i<inputs.length; i++){
			var input = inputs[i];
			if(input.getAttribute('type')!="checkbox" && input.getAttribute('type')!="radio")
				Element.addClassName(input, this.options.baseInputClass);
			var required = input.getAttribute('required');
			var validators = input.getAttribute('validate');
			if(required || validators)
				Event.observe(input, "keyup", this.validate.bind(this));
		}
		var inputs = $A($(this.options.srcElement).getElementsByTagName('select'));
		for(var i=0; i<inputs.length; i++){
			var input = inputs[i];
			Element.addClassName(input, this.options.baseSelectClass);
			var required = input.getAttribute('required');
			var validators = input.getAttribute('validate');
			if(required || validators)
				Event.observe(input, "change", this.validate.bind(this));
		}
		this.validate();
	},
	
	validate: function(){
		this.clearMessages();
		
		var inputs = $A($(this.options.srcElement).getElementsByTagName('input'));
		inputs = inputs.concat($A($(this.options.srcElement).getElementsByTagName('textarea')));
		inputs = inputs.concat($A($(this.options.srcElement).getElementsByTagName('select')));
		var valid  = true;
		
		for(var i=0; i<inputs.length; i++){
			var input = inputs[i];
			var required = input.getAttribute('required');
			var requiredLength = input.getAttribute('requiredLength') ? input.getAttribute('requiredLength') : 1;
			
			if(required == 'true' && input.value.length < requiredLength){
				this.setErrorMessage(input, 'This field is required.');
				valid = false;
				continue;
			}	
			
			var validators = input.getAttribute('validate');
			try {
				if(input.value.length > 0 && validators)
					validators.split(';').each(function(validator){
						if(!this._validators[validator])
							throw $continue;
						
						var args = validator.split(":");
						args[0] = input.value;
						var message = this._validators[validator].apply(this, args);
						if(message){
							this.setErrorMessage(input, message);
							valid = false;
							throw $break;
						}
					}.bind(this));
			} catch (e) {
				if(e != $continue) throw e;
			}
		}
		
		if(!valid && this.options.focusFirstError)
			this.eventListeners[0][0].focus();
		
		return valid;
	},
	
	clearMessages: function(){
		this.messageElements = $(this.options.srcElement).getElementsByClassName('warningImage');
		for(var i=0; i<this.messageElements.length; i++)
			Element.remove(this.messageElements[i]);
		this.messageElements = [];

		for(var i=0; i<this.eventListeners.length; i++)
			try {Event.stopObserving(this.eventListeners[i][0], this.eventListeners[i][1]);} catch (e){}
		this.eventListeners = [];
		
		var inputs = $A($(this.options.srcElement).getElementsByTagName('input'));
		inputs = inputs.concat($A($(this.options.srcElement).getElementsByTagName('textarea')));
		inputs = inputs.concat($A($(this.options.srcElement).getElementsByTagName('select')));
		
		for(var i=0; i<inputs.length; i++)
			Element.removeClassName(inputs[i], this.options.errorClass);
	},
	
	setErrorMessage: function(input, msg){
		Element.addClassName(input, this.options.errorClass);
		var id = Number(new Date());
		new Insertion.After(input, '<img id="'+id+'" class="warningImage" src="/images/exclamation.png" align="absmiddle" title="'+msg+'">');
		input.title = msg;
		
	},
	
	_showMessage: function(input, message){
		Element.show(message);
		this._positionMessage(input, message);
	},
	
	_hideMessage: function(message){
		Element.hide(message);
	},
	
	_positionMessage: function(input, message){
		var pos = Position.positionedOffset(input);
		var absPos = Position.realOffset(input);
		pos[0] -= absPos[0];
		pos[1] -= absPos[1];
		message.style.top  = pos[1] + 'px';
		message.style.left = pos[0] + input.offsetWidth + 'px';
	},
	
	_validators: {
		numeric: function(value) {
			if(isNaN(value))
				return "Must be numeric";
		},
		
		notBlank: function(value) {
			if(value.length<4)
				return "Must be at least 4 characters long.";
		},
		
		date: function(value) {
			if(!value.match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4}/))
				return "Must be a date of the format MM/DD/YYYY";
		},
		
		email: function(value){   
		       if(!value.match(/^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/))  
		        return "Must be a valid email address";  
		},  
		    
		phone: function(value){  
		       if(!value.match(/\(?(\d{3})\)?[\s.-]?(\d{3})[\s.-]?(\d{4}).*(\d*)/))  
		        return "Must be a valid phone number";  
		},  
		    
		zip: function(value){  
		       if(!value.match(/^[0-9]{5}(-[0-9]{4})?/))  
		        return "Must be a valid zip code";  
		}
		
	}
}
