var Fat = {};

Fat.addEvent = function (elm,evt,fn) {
	var oldFunc = elm[evt];
	if (typeof oldFunc !== "function") {
		elm[evt] = fn;	
	} else {
		elm[evt] = function() {
			fn();
			oldFunc();
		};
	};
};

Fat.validationConfig = {
	errors: true,
	verboseErrors: true,
	elmCallback: null,
	formCallback: null
};

Fat.classAttribute = (function(){
	var ie7 = /MSIE 7.0/g.test(navigator.userAgent),
	ie6 = /MSIE 6.0/g.test(navigator.userAgent),
	docMode = document.documentMode ? document.documentMode.toString() : "",
	classAttr = "class";
	if(docMode === "7" || docMode === "5") {
		classAttr = "className";
	} else if(docMode === "") {
		classAttr = (ie7 || ie6) ? "className" : "class";
	};
	return classAttr;
})();

Fat.validate = (function(){
	var classes = {
		required: /required/i,
		email: /email/i,
		phone: /phone/i,
		postcode: /postcode/i,
		group: /group/i,
		max: /max/i,
		min: /min/i,
		match: /match/i
	},
	msgs = {
		defaultMsg: "This field cannot be left blank",
		radioGroup: "Please choose one option",
		radio: "You have not chosen the correct option",
		check: "Please make sure to check this box",
		checkGroup: "Please choose at least one option",
		select: "Please choose an option",
		
		phone: "This field must contain a valid phone number",
		postcode: "This field must contain a valid postcode",
		email: "This field must contain a valid email address",
		
		min: "This field has too few characters",
		max: "This field has too many characters",
		match: "This field must match the other"
	},
	regs = {
		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
	},
	types = {
		text: /(text|email|password)/,
		ignore: /(hidden|submit|image|button|reset)/i
	},
	hasValue = function(elm) { // -- basic value test
		if(typeof elm.value !== "undefined" && elm.value !== "") {
			return true;
		};
		return false;
	},
	hasEmail = function(elm){ // -- email test
		return regs.email.test(elm.value);
	},
	hasPostcode = function(elm){ // -- postcode test
		return regs.postcode.test(elm.value);
	},
	hasPhone = function(elm){ // -- phone test
		return regs.phone.test(elm.value);
	},
	hasGroup = function(elm) { // -- test that 1 radio button in a group is checked
		var group = elm.form.elements[elm.name],
		i = group.length;
		while(i--) {
			if(group[i].checked) {
				return true;
			};
		};
		return false;
	},
	hasChecked = function(elm) { //  -- test for checked item
		return (typeof elm.checked !== "undefined" ? elm.checked : false);
	},
	hasOption = function(elm) { // -- test select box for option
		return (typeof elm.selectedIndex !== "undefined" ? elm.selectedIndex > 0 : false);
	},
	hasMax = function(elm,str){ // -- test for a maximum number of characters
		var limit = parseInt(str.split("-")[1].split(" ")[0]);
		return (elm.value.length <= limit);
	},
	hasMin = function(elm,str){ // -- test for a minimum number of characters
		var limit = parseInt(str.split("-")[1].split(" ")[0]);
		return (elm.value.length >= limit);
	},
	hasMatch = function(elm,str){ // -- test value matches value of another element
		var targetName = str.split("-")[1].split(" ")[0],
		target = elm.form.elements[targetName];
		return elm.value === target.value;
	},
	createError = function(elm,errStr){
	    if(elm.formError) {
			removeError(elm);
		};
		if(Fat.validationConfig.elmCallback) {
			Fat.validationConfig.elmCallback(elm, elm.form);
		};
		if(Fat.validationConfig.errors) {
			var customError = elm.getAttribute("data-error"),
			error;
			elm.style.boxShadow = "inset 1px 1px 3px #c00";
			if(Fat.validationConfig.verboseErrors) {
				error =  document.createElement("span");
				error.innerHTML = customError || errStr;
				error.setAttribute(Fat.classAttribute,"errorStyle");
				$(elm).after(error);
				elm.formError = error;
			};
		};
	},
	removeError = function(elm){
		elm.removeAttribute("style");
		if(elm.formError) {
			$(elm.formError).css("display","none").remove();
		};
	};
	return function(formName,handler) {
					
		if(formName.jquery) {
			formName.each(function(){
				Fat.validate(this,handler);
			});
			return false;
		} else if(formName === document.forms) {
			var len = formName.length, i = 0;
			while(i < len) {
				Fat.validate(formName[i],handler)
				i++;
			};
			return false;
		};
		var form;
		if(formName.nodeName) {
			form = formName
		} else if(typeof document.forms[formName] !== "undefined") {
			form = document.forms[formName];
		} else {
			return Fat.validate($(formName),handler);
		};
		if(typeof form !== "undefined") {
			var elemList = form.elements || $(form).find("input,select,textarea").toArray(),
			doValidation = function(elms) {
				var len = elms.length,
				name = 0,
				isValidForm = true,
				isValidElem;
				while(name < len) {
					isValidElem = true;
					var elem = elms[name],
					inpValue = typeof elem.value !== "undefined" ? elem.value  : "",
					nodeName = elem.nodeName.toLowerCase(),
					nodeType = typeof elem.type !== "undefined" ? elem.type.toLowerCase()  : "",
					className = elem.getAttribute(Fat.classAttribute),
					required = classes.required.test(className),
					errorStr = "";
					
					if(required && !types.ignore.test(nodeType)) {
						if(nodeName === "input" || nodeName === "textarea") { // -- test all inputs/textareas
							elem.onkeyup = elem.onkeyup || function(){
								doValidation([this])
							};
							if(types.text.test(nodeType) || nodeName === "textarea") { // -- test "typeable" inputs
								
								if(!hasValue(elem)) { // -- basic value test
									isValidElem = false;
									errorStr = msgs.defaultMsg;
								} else if(classes.email.test(className) && !hasEmail(elem)) { // email test
									isValidElem = false;
									errorStr = msgs.email;
								} else if(classes.postcode.test(className) && !hasPostcode(elem)) { // postcode test
									isValidElem = false;
									errorStr = msgs.postcode;
								} else if(classes.phone.test(className) && !hasPhone(elem)) { // phone no. test
									isValidElem = false;
									errorStr = msgs.phone;
								} else if(classes.max.test(className) && !hasMax(elem,className)) { // -- test for too many chars
									isValidElem = false;
									errorStr = msgs.max;
								} else if(classes.min.test(className) && !hasMin(elem,className)) { // -- test for too few chars
									isValidElem = false;
									errorStr = msgs.min;
								} else if(classes.match.test(className) && !hasMatch(elem,className)) { // -- test for matching values
									isValidElem = false;
									errorStr = msgs.match;
								};
								
							} else if(nodeType === "radio" || nodeType === "checkbox") { // test radio/checkbox groups
								elem.onclick = elem.onclick || function(){
									doValidation([this])
								};
								if(classes.group.test(className) && !hasGroup(elem)) { // any is checked?
									isValidElem = false;
									errorStr = nodeType === "checkbox" ? msgs.checkGroup : msgs.radioGroup;
								} else if(!classes.group.test(className) && !hasChecked(elem)) { // specific is checked
									isValidElem = false;
									errorStr = nodeType === "checkbox" ? msgs.check : msgs.radio;
								};
							};
						} else if(nodeName === "select" && !hasOption(elem)) { // -- test select boxes
							elem.onchange = elem.onchange || function(){
								doValidation([this])
							};
							errorStr = msgs.select;
							isValidElem = false;
						};
					};
					if(!isValidElem) {
						createError(elem, errorStr);
						isValidForm = false;
					} else {
						removeError(elem);
					};
					name++;
				};
				if(!isValidForm && Fat.validationConfig.formCallback) {
					Fat.validationConfig.formCallback(elem.form);
				};
				return isValidForm;
			};
			if(typeof handler !== "undefined") {
				$(form).find(handler).each(function(){
                    var oldHref = $(this).attr("href");
                    $(this).removeAttr("href").bind("click",function(){
                        var isValidated = doValidation(elemList);
                        if(isValidated) {
                            $(this).attr("href", oldHref);
                        };
                        return isValidated;
                    });
                });
			} else {
				Fat.addEvent(form,"onsubmit",function(){
					return doValidation(elemList);
				});
			};
		};
	};
})();
