(function(){
	String.prototype.trim = function () {
		return this.replace(/[\s\0\n\t\r\f\v]+/g," ").replace(/(^\s*|\s*$)/g,"");
	};
	var ie7 = /MSIE 7.0/g.test(navigator.userAgent),
	ie6 = /MSIE 6.0/g.test(navigator.userAgent),
	classAttr = (ie7 || ie6) ? "className":"class";

	var s = {
		defaultError: "This is a required field",
		radioError: "You must choose one of these options",
		checkError: "This box must be ticked",
		emailError: "This must be a valid email address",
		phoneError: "This must be a valid phone number",
		postError: "This must be a valid postcode",
		matchError: "This field still requires correct confirmation"
	},
	regs = {
		required: /required/i,
		classEmail: /email/i,
		classPhone: /phone/i,
		classPcode: /postcode/i,
		classDepends: /depends/i,
		dependsNext: /dependsNext/i,
		dependsPrev: /dependsPrev/i,
		dependsName: /depends-/i,
		dependsChecked: /dependsChecked/i,
		tryChecked: /tryChecked/i,
		email: /([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})/,
		phone: /[0-9\+]/,
		postcode: /(^[A-Z]{1,2}[0-9]{1,2})(\s|)([0-9][A-Z]{2}$)/i,
		inputs: /(hidden|submit|image|button|reset)/i
	},
	createError = function(elm,msg){
		removeError(elm);
		var errorAlert = $(document.createElement("span")).addClass("formError"),
		position = $(elm).position(),
		leftVal = position.left + $(elm).outerWidth() + 10,
		topVal = position.top,
		errorHtml = msg;
		errorAlert.html(errorHtml).css({"top":topVal,"left":leftVal}).insertAfter(elm);
	},
	removeError = function(elm) {
		$(elm).next(".formError").remove();
	},
	radioObj = {},
	check = function(formArray){
		var $return = true,
		len = formArray.length;
		while (len--) {
			var isValid = true,
			errorMsg = "",
			elm = formArray[len],
			className = elm.getAttribute(classAttr),
			tagName = elm.nodeName.toLowerCase(),
			inpType = elm.getAttribute("type"),
			inpValue = elm.value;
			if(inpValue) {inpValue = inpValue.trim() };
			if(className) { className = className.toLowerCase(); };
			if(inpType) { inpType = inpType.toLowerCase(); };
			
			if(!regs.inputs.test(inpType) && tagName != "fieldset" && regs.required.test(className)) {				
				if(inpType == "text" || inpType == "password" || tagName == "textarea") {
					// perform live checking
					elm.onkeyup = function(){
						check([this]);
					};
					
					if(inpValue == '' || inpValue == null) {
						isValid = false;
						errorMsg = s.defaultError;
					} else if(regs.classEmail.test(className)) {
						isValid = regs.email.test(inpValue);
						errorMsg = s.emailError;
					} else if(regs.classPhone.test(className)) {
						isValid = regs.phone.test(inpValue);
						errorMsg = s.phoneError;
					} else if(regs.classPcode.test(className)) {
						isValid = regs.postcode.test(inpValue);
						errorMsg = s.postError;
					};
					if(isValid) {
						if(regs.dependsNext.test(className) || regs.dependsPrev.test(className)) {
							var dependentElem = regs.dependsNext.test(className) ? $(elm).nextAll(".dependsPrev")[0] : $(elm).prevAll(".dependsNext")[0],
							doReturn = true;
						} else if(regs.dependsName.test(className)) {
							var nameAttr = className.split("depends-")[1].split(" ")[0],
							dependentElem = $("[name="+nameAttr+"]")[0],
							doReturn = true;;
						};
						if(doReturn) {
							isValid = (inpValue == dependentElem.value);
							if(isValid) {
								removeError(dependentElem);
							} else {
								createError(dependentElem,s.matchError)
							};
							errorMsg = s.matchError;
						};
					};
				
					
				} else if (inpType == "radio") {
					elm.onclick = function(){
						check([this.form.elements[this.name]][0]);
					};
					var group = elm.getAttribute("name");
					if(group) {
						group = group.toLowerCase();
						if(typeof radioObj[group] === "undefined") {
							radioObj[group] = {
								firstElement: elm,
								valArray: []
							};
						};
						radioObj[group].valArray.push(elm.checked);
					};
				} else if (inpType == "checkbox") {
					elm.onclick = function(){
						check([this]);
					};
					if(!elm.checked) {
						isValid = false;
						errorMsg = s.checkError
					};
				};
				if(regs.dependsChecked.test(className)) {
					var theID = className.split("dependschecked-")[1].split(" ")[0],
					tryElem = $(".tryChecked-" + theID)[0];
					$return = isValid = tryElem.checked ? inpValue != "" : true;
					errorMsg = s.defaultError;
				} else if(regs.tryChecked.test(className)) {
					var theID = className.split("trychecked-")[1].split(" ")[0],
					dependsElem = $(".dependsChecked-" + theID)[0];
					$return = isValid = elm.checked ? dependsElem.value != "" : true;
					errorMsg = s.defaultError;
					check([dependsElem]);
				};
				if(isValid) { removeError(elm); }
				else {
					createError(elm,errorMsg);
					$return = false;
				};
			};
		};
		for(var x in radioObj) {
			var radioValid = false,
			valArray = radioObj[x].valArray;
			for(var y in valArray) {
				if(valArray[y] === true) { radioValid = true; };
			};
			if(radioValid) { removeError(radioObj[x].firstElement); }
			else {
				createError(radioObj[x].firstElement,s.radioError);
				$return = false;
			};
		}; 
		return $return;
	};
	(function($){  
		jQuery.fn.fatValidate = function(o) {  
			s = $.extend(s, o);  
			return this.each(function() {  
				$(this).submit(function(){
					return check(this.elements);
				});
			});  
		};  
	})(jQuery);
})();
