/* http://xserve.local/projects/LoudDog/wiki/FormValidator */

FormValidator = Class.create();
FormValidator.prototype = {
	tests: false,
	onSuccess: false,
	onFailure: false,
	header: '',
	listType: 'unordered',
	errors: new Array(),
	submit: true,
	errorClass: 'invalid',
	
	initialize: function(options) {
		if (!FormUtils) alert('ERROR - FormValidator[initialize]\nformUtils.js is required');
		else Event.observe(window, 'load', function() {
			this.form = document.forms[options.form];
			if (this.form) {
				this.form.onsubmit = function() {
					if (this.checkForm())
						this.form.originalSubmit();
					return false;
				}.bind(this);

				this.form.originalSubmit = this.form.submit;
				this.form.submit = this.form.onsubmit;

				this.form.onreset = function() { this.reset(); }

				if (options.output) this.output = $(options.output);
				if (options.tests) this.tests = options.tests;
				if (options.onSuccess) this.onSuccess = options.onSuccess;
				if (options.onFailure) this.onFailure = options.onFailure;
				if (options.submit != null) this.submit = options.submit;
				if (options.errorClass) this.errorClass = options.errorClass;
				if (options.header) this.header = options.header;
				if (options.listType) this.listType = options.listType;

				if (this.output) this.output.hide();

				FormUtils.fixFormLabels();
			} else alert('ERROR - FormValidator[initialize]\nUnknown form: ' + options.form);	
		}.bind(this));
	},
	
	checkForm: function() {
		this.reset();
	
		if (this.tests)  {
			try { this.tests();	}
			catch(error) { alert('ERROR - FormValidator[tests]\n' + error); }
		}
	
		if (this.errors.length) {
			var response = '';
		
			if (this.output) {
				response += this.header;
				if (this.listType == 'ordered') response += '<ol>';
				else response += '<ul>';
				for (var i = 0; i < this.errors.length; i++) response += '<li>' + this.errors[i] + '</li>';
				if (this.listType == 'ordered') response += '</ol>';
				else response += '</ul>';
				this.output.update(response);
				this.output.style.display = "block";
				this.output.scrollTo();
			} else {
				for (var i = 0; i < this.errors.length; i++)
					response += this.errors[i] + '\n';
				alert(response);
			}
		
			if (this.onFailure) {
				try { this.onFailure();	}
				catch(error) { alert("ERROR - FormValidator[onFailure]\n" + error);	}
			}
		} else if (this.onSuccess) {
			try { this.onSuccess(); }
			catch(error) { alert("ERROR - FormValidator[onSuccess]\n" + error);	}
		}
	
		return this.submit && this.errors.length == 0;
	},
	
	reset: function() {
		this.errors = new Array();
		
		document.getElementsByClassName(''); // This somehow allows IE7 to use gEBCN on this.form
		var elements = this.form.getElementsByClassName(this.errorClass);
		for (var i = 0; i < elements.length; i++)
			elements[i].removeClassName(this.errorClass);
			
		if (this.output) {
			this.output.style.display = 'none';
			this.output.update('');
		}
	},
	
	runTest: function(errorMessage, test) {
		if (!test) {
			var args = this.runTest.arguments;
			
			var invalidated = false;
			for (var i = 2; i < args.length; i++) {
				if (!$(args[i])) alert('ERROR - FormValidator[runTest]\nInvalid field name during test,\n"'+errorMessage+'"'); 
				else if (this.invalidate(args[i])) invalidated = true;
			}
		
			if (invalidated) this.errors.push(errorMessage);
		}		
		
		return test;
	},
	
	required: function() {
		var args = this.required.arguments;
		
		if (args.length % 2 == 0) {
			for (var i = 0; i < args.length; i += 2) {
				if (!$(args[i+1])) alert('ERROR - FormValidator[required]\nInvalid field name during test,\n' + args[i] + ' is required');
				else this.runTest(
					args[i] + " is required",
					FormUtils.value(args[i+1]) && FormUtils.value(args[i+1]) != '',
					args[i+1]
				);
			}
		} else alert("ERROR - FormValidator[required]\nRequired takes an even number of parameters.");		
	},
	
	equals: function(msg, field, value) { return this.runTest(msg, FormUtils.value(field) == value, field); },
	validEmail: function(msg, field) { return this.runTest(msg, this.regExp(/^.+@.+$/, FormUtils.value(field)), field); },
	validPassword: function(msg, field) { return this.runTest(msg, this.regExp(/^\S+$/, FormUtils.value(field)), field); },
	validPhone: function(msg, field) { return this.runTest(msg, this.regExp(/^\d{10}$/, FormUtils.value(field).replace(/\D+/g,'')), field); },
	validZipCode: function(msg, field) { return this.runTest(msg, this.regExp(/^\d{5}([\ -]\d{4})?$/, FormUtils.value(field)), field); },
	numeric: function(msg, field) { return this.runTest(msg, this.regExp(/^\d+$/, FormUtils.value(field)), field); },
	
	regExp: function(regexp, value) { return regexp.test(value.replace(/^\s+/, '').replace(/\s+$/, '')); },
		
	invalidate: function(element) {
		var elements = new Array();
		
		switch (FormUtils.fieldType(element)) {
			case 'radio' :
				for(var i = 0; i < element.length; i++)
					elements.push(FormUtils.labelFor(element[i]));
				break;
			default:
				elements.push(FormUtils.labelFor(element));
				elements.push(element);
				break;
		}
		
		var invalidated = false;
		for (var i = 0; i < elements.length; i++) {
			if (!elements[i]) invalidated = true;
			else if (!elements[i].hasClassName(this.errorClass)) {
				elements[i].addClassName(this.errorClass);
				invalidated = true;
			}
		}
		
		return invalidated;
	}
}