
	
	var FormValidator = {

		fieldRequiredClassName: 'required',
		fieldEmailClassName: 'email',
		strictFormatClassName: 'strictFormat',
		strictExtensionsClassName: 'strictExtensions',
		fieldErrorClassName: 'fieldError',
		labelErrorClassName: 'labelError',
		fieldEmptyOnClickClassName: 'emptyOnClick',
		fieldCanBeDefaultClassName: 'canBeDefault',
		showNames: false,
		stripSuffixes: false, // strip the suffix that is  appended to the name with an underscore
		stripUnderscores: true, // strip the underscores of names

		initialize: function()
		{
			// set submit event of each form
			var form;
			for (var i = 0; form = document.getElementsByTagName('form')[i]; i++)
			{
				this.addEvent(form, 'submit', (function (scope, form) {
					return function(e)
					{
						(function(){
							this.submit(form);
						}).call(scope);
					};
				})(this, form));
			}


			// set form elements' click and blur events
			var element, elements = this.getElementsByClassName(this.fieldEmptyOnClickClassName, 'input');
			for (var i = 0; element = elements[i]; i++)
			{
				element.onclick = function(){if(this.defaultValue == this.value && this.type.toLowerCase() != 'submit'){this.value = ''}};
				element.onblur = function(){if(this.value.trim() == ''){this.value = this.defaultValue}};
			}
		},


		// submit form
		submit: function(form)
		{
			var errorMssg;

			if (errorMssg = this.check(form))
			{
				alert(errorMssg);

				return false;
			}

			return true;
		},


		// check whether the required fields have been filled in
		check: function(form)
		{
			var
				errorMssg,
				requiredInput, requiredInputs = this.getElementsByClassName(this.fieldRequiredClassName, 'input', form),
				inputCollection, collectionInput, inputLabel, alternatives, alternative, alternativeIsSet = false,
				names = '\n';

			for (i = 0; requiredInput = requiredInputs[i]; i++)
			{
				// no value in radio or checkbox collection selected
				// or no value filled in or value is the default value and this is not allowed
				if (
					((requiredInput.type == 'radio' || requiredInput.type == 'checkbox') && !this.getInputCollectionValue(inputCollection = this.getInputCollection(requiredInput.name, requiredInput.type, form)))
					||
					(requiredInput.type != 'radio' && requiredInput.type != 'checkbox' && (requiredInput.value.trim() == '' || (requiredInput.value == requiredInput.defaultValue && !this.hasClassName(requiredInput, this.fieldCanBeDefaultClassName))))
				)
				{
					if (requiredInput.type == 'radio' || requiredInput.type == 'checkbox')
					{
						for (j = 0; collectionInput = inputCollection[j]; j++)
						{
							if (inputLabel = this.getLabel(collectionInput.id)) this.addClassName(inputLabel, this.labelErrorClassName);
							errorMssg = 'U heeft nog niet alle relevante velden ingevuld.';
						}
						for (j = 0; collectionInput = inputCollection[j]; j++)
						{
							if (collectionInput.name) names += '\n' + (this.stripSuffixes ? collectionInput.name.replace(/_[^_\s]*$/ ,'') : collectionInput.name);
							break;
						}
					}
					else
					{
						alternatives = requiredInput.className.match(/.*alternatives=\[([^\]]*)\].*/);
						if (alternatives)
						{
							alternatives = alternatives[1].split(',');

							for (j = 0; alternative = document.getElementById(alternatives[j]); j++)
							{
								if (!(alternative.value.trim() == '' || (alternative.value == alternative.defaultValue && !this.hasClassName(alternative, this.fieldCanBeDefaultClassName))))
								{
									alternativeIsSet = true;
								}
							}

							if (alternativeIsSet)
							{
								this.removeClassName(requiredInput, this.fieldErrorClassName);
							}
							else
							{
								this.addClassName(requiredInput, this.fieldErrorClassName);
								errorMssg = 'U heeft nog niet alle relevante velden ingevuld.';
								names += '\n' + (this.stripSuffixes ? requiredInput.name.replace(/_[^_\s]*$/ ,'') : requiredInput.name);
							}
						}
						else
						{
							this.addClassName(requiredInput, this.fieldErrorClassName);
							errorMssg = 'U heeft nog niet alle relevante velden ingevuld.';
							names += '\n' + (this.stripSuffixes ? requiredInput.name.replace(/_[^_\s]*$/ ,'') : requiredInput.name);
						}
					}
				}
				else
				{
					if (requiredInput.type == 'radio' || requiredInput.type == 'checkbox')
					{
						for (j = 0; collectionInput = inputCollection[j]; j++)
						{
							if (inputLabel = this.getLabel(collectionInput.id)) this.removeClassName(inputLabel, this.labelErrorClassName);
						}
					}
					else
					{
						this.removeClassName(requiredInput, this.fieldErrorClassName);
					}
				}
			}

			if (!errorMssg)
			{
				var emailInput, emailInputs = this.getElementsByClassName(this.fieldEmailClassName, 'input', form);

				for (var i = 0; emailInput = emailInputs[i]; i++)
				{
					if (emailInput.value != "" && emailInput.value.search(/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/) == -1)
					{
						this.addClassName(emailInput, this.fieldErrorClassName);
						errorMssg = 'Het opgegeven e-mail adres is ongeldig.';
						names += '\n' + (this.stripSuffixes ? emailInput.name.replace(/_[^_\s]*$/ ,'') : emailInput.name);
					}
					else
					{
						this.removeClassName(emailInput, this.fieldErrorClassName);
					}
				}
			}

			if (!errorMssg)
			{
				var format, strictFormatInput, strictFormatInputs = this.getElementsByClassName(this.strictFormatClassName, 'input', form);

				for (i = 0; strictFormatInput = strictFormatInputs[i]; i++)
				{
					if (strictFormatInput.value != '')
					{
						format = strictFormatInput.className.replace(/.*format=([^\s$]*).*/, '$1');
						if (new RegExp(format).test(strictFormatInput.value) == false)
						{
							errorMssg = (strictFormatInput.type == 'file' ? 'Het bestandstype is niet juist.' : 'De notatie is niet correct.');
							this.addClassName(strictFormatInput, this.fieldErrorClassName);
							names += '\n' + (this.stripSuffixes ? strictFormatInput.name.replace(/_[^_\s]*$/ ,'') : strictFormatInput.name);
						}
						else
						{
							this.removeClassName(strictFormatInput, this.fieldErrorClassName);
						}
					}
				}
			}

			if (!errorMssg)
			{
				var extension, extensions, strictExtensionsInput, strictExtensionsInputs = this.getElementsByClassName(this.strictExtensionsClassName, 'input', form);

				for (i = 0; strictExtensionsInput = strictExtensionsInputs[i]; i++)
				{
					if (strictExtensionsInput.value != '')
					{
						eval(strictExtensionsInput.className.replace(/.*(extensions=\[[^\]]*\]).*/, '$1'));

						for (j = 0; extension = extensions[j]; j++)
						{
							if (strictExtensionsInput.value.replace(/.*\.([^\.]*)/, '$1').toLowerCase() == extension.toLowerCase())
							{
								this.removeClassName(strictExtensionsInput, this.fieldErrorClassName);
								break;
							}
						}
						if (j == extensions.length)
						{
							errorMssg = 'Alleen bestanden met de extensie \'' + extensions.join('\', \'').replace(/,([^,]*$)/, ' of$1') + '\' zijn toegestaan.';
							this.addClassName(strictExtensionsInput, this.fieldErrorClassName);
							names +=  '\n' + (this.stripSuffixes ? strictExtensionsInput.name.replace(/_[^_\s]*$/ ,'') : strictExtensionsInput.name);
							break;
						}
					}
				}
			}

			return (errorMssg ? (this.showNames ? errorMssg + (this.stripUnderscores ? names.replace(/_/g, ' ') : names) : errorMssg) : false);
		},


		// get radio button or checkbox collection; optionally by name and in container
		// for checkboxes only the alphabetical start of the name is compared to include them in a collection (to exclude suffixed details in their name that make them unique)
		getInputCollection: function(name, type, container)
		{
			var
				input, allInputs = (container || document).getElementsByTagName('input'),
				collection = [];

			for (var i = 0; input = allInputs[i]; i++)
			{
				if ((name && (type == 'radio' ? input.name : input.name.replace(/(^[a-zA-Z]*).*/, '$1')) == name) || (!name && input.type == type))
				{
					collection.push(input);
				}
			}

			return collection;
		},


		// get radio button or checkbox collection value; optionally from input element collection, or collection by name whereby optionally the elements should be found in container
		// dependancies getInputCollection
		getInputCollectionValue: function(collection, type, container)
		{
			if (!collection || typeof(collection) == 'string') collection = getInputCollection(collection, type, container);

			for (var i = 0; i < collection.length; i++)
			{
				if (collection[i].checked)
				{
					return collection[i].value;
				}
			}
		},


		// get label by for value
		getLabel: function(forValue, container)
		{
			var labels = (container || document).getElementsByTagName('label');

			for (var i = 0; i < labels.length; i++)
			{
				if (labels[i].getAttribute('for') == forValue || (document.all && labels[i].getAttribute('htmlFor') == forValue))
				{
					return labels[i];
				}
			}
		},


		// standalone getElementsByClassName function from http://www.robertnyman.com/index.php?p=256
		getElementsByClassName: function(className, tag, container)
		{
			var
				testClass = new RegExp("(^|\\s)" + className + "(\\s|$)"),
				tag = tag || '*', container = container || document,
				elements = (tag == '*' && container.all)? container.all : container.getElementsByTagName(tag),
				returnElements = [], current, length = elements.length;

			for(var i=0; i<length; i++){
				current = elements[i];
				if(testClass.test(current.className)){
					returnElements.push(current);
				}
			}

			return returnElements;
		},


		// standalone addClassName from http://particletree.com/features/javascript-basics-for-prototyping/
		// dependancies: String.prototype.trim
		addClassName: function(element, className)
		{
			this.removeClassName(element, className);
			element.className = (element.className + " " + className).trim();
		},


		// standalone removeClassName from http://particletree.com/features/javascript-basics-for-prototyping/
		// dependancies: String.prototype.trim
		removeClassName: function(element, className)
		{
			element.className = element.className.replace(className, "").trim();
		},


		// check whether an element has a specific class
		hasClassName: function(element, name)
		{
			var i, list = element.className.split(" ");

			for(i = 0; i < list.length; i++)
				if(list[i] == name)
					return true;
			
			return false;
		},


		// better addEvent function from http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html
		addEvent: function(element, type, fn)
		{
			if (element.addEventListener)
			{
				element.addEventListener(type, fn, false);
			}
			else if (element.attachEvent)
			{
				element['e' + type + fn] = fn;
				element[type + fn] = function() {
					element['e' + type + fn](window.event);
				};
				element.attachEvent('on' + type, element[type + fn]);
			}
		}


	};


	// string trim function from http://www.somacon.com/p355.php
	String.prototype.trim = function() {
		return this.replace(/^\s+|\s+$/g,"");
	}

