jQuery.fn.formvalidation = function(options) {

	// default settings
	var options = jQuery.extend( {
		mainErrorMsg: 'Sorry there was a problem submitting your form. Please check that there are no errors',
		mandErrorSuffixText: ' must be completed',
		emailErrorText: 'You must enter a valid email address',
		numericErrorSuffixText: ' must only contain numeric characters (0-9)',
		checkboxErrorText: 'You must tick the box to proceed',
		radioErrorText: 'You must select one of the options',
		focusBorderColor: '#FF7B1A',
		defaultFieldBorderColor: '#DEDEDE',
		errorFieldBorderColor: '#AD0000',
		errorMsgBGColor: '#f8E8E8',
		errorMsgColor: '#AD0000',
		errorMsgBorderColor: '#EABEBE',
		errorFieldClass: 'errorField',
		mandatoryFields: $(':input:enabled:not(:submit, :button, :file, :reset, :image).mand'),
		emailFields: $('input:enabled.emailaddr'),
		numericFields: $('input:enabled.numeric'),
		checkFields: $(':checkbox:enabled.mand'),
		radioGroupClass: 'radioGroup',
		errorMsgArray: [],
		submitForm: true,
		compareEmailFields: [],
		errorFieldPadding: '10px',
		allFieldRows: $('.fieldRow'),
		formId: ''
	}, options);
	 
	return this.each(function() {
		options.errorMsgArray = new Array();
		options.formId = $(this).attr('id');
		options.mandatoryFields = $('#' + options.formId + ' :input:enabled:not(:submit, :button, :file, :reset, :image).mand');
		options.checkFields = $('#' + options.formId + ' :checkbox:enabled.mand');
		addFieldFocus();
		$(this).submit(function () {
			clearErrors();
			validateMandatory();
			validateEmail();
			validateCheckFields();
			validateRadioFields();
			validateNumeric();
			if (jQuery.isArray(options.compareEmailFields) && options.compareEmailFields.length > 0) validateCompareEmails();
			if (! options.submitForm) {
				setErrors();
				setCursorFocus();
			}			
			return options.submitForm;
		});
	});
  
	function validateMandatory() { 
    	$(options.mandatoryFields).each(function () {
    		if ($(this).attr('value') == '' || $(this).attr('value') == undefined || $(this).attr('value') == null) {
    			var fieldLabel = getLabel($(this).attr('id'));
    			options.errorMsgArray.push($(this).attr('id') + ':::' + fieldLabel + options.mandErrorSuffixText);
    			options.submitForm = false;
    		}
    	}); 
    }
    
    function validateEmail() {
    	$(options.emailFields).each(function () {
    		if (! $(this).attr('value').match( /^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z0-9_\-\.]+)$/)) {
        		options.errorMsgArray.push($(this).attr('id') + ':::' + options.emailErrorText);
				options.submitForm = false;
			}
    	});
    }
    
    function validateNumeric() {
    	$(options.numericFields).each(function () {
    		if (! isNumeric($(this).attr('value')) && $(this).attr('value') != '') {
    			var fieldLabel = getLabel($(this).attr('id'));
    			options.errorMsgArray.push($(this).attr('id') + ':::' + fieldLabel + options.numericErrorSuffixText);
				options.submitForm = false;
    		}
    	});
    }
    
    function validateCompareEmails() {
    	jQuery.each(options.compareEmailFields, function( ind, objVal ) {
    		if ($('#' + objVal[0]).attr('value') != $('#' + objVal[1]).attr('value')) {
    			var label0 = getLabel(objVal[0]);
    			var label1 = getLabel(objVal[1]);
    			options.errorMsgArray.push(objVal[0] + ':::' + label0 + ' does not match ' + label1);
    			options.errorMsgArray.push(objVal[1] + ':::' + label1 + ' does not match ' + label0);
				options.submitForm = false;
    		}
    	});
    }
    
    function validateCheckFields() {
    	$(options.checkFields).each(function () {
    		if (! $(this).attr('checked')) {
	    		options.errorMsgArray.push($(this).attr('id') + ':::' + options.checkboxErrorText);
				options.submitForm = false;
			}
    	});
    }
    
    function validateRadioFields() {
    	$('.' + options.radioGroupClass).each(function() {
    		if ($(this).children(":radio:checked").length == 0) {
    			options.errorMsgArray.push($(this).children(":radio:last").eq(0).attr('id') + ':::' + options.radioErrorText);
				options.submitForm = false;
    		}
    	});
    }
    
    function setErrors() {
    	$('#' + options.formId).prepend('<p class="error"><strong>' + options.mainErrorMsg + '</strong></p>');
		var elmsArr = new Array();
		jQuery.each(options.errorMsgArray, function( ind, objVal ) {
			var vals = objVal.split(':::');
			if (jQuery.inArray(vals[0], elmsArr) == -1) {
    			$('#' + vals[0]).after("<p class='error'>" + vals[1] + "</p>");
    			elmsArr.push(vals[0]);
    		} else {
    			$('#' + vals[0] + ' ~ p.error').append('<br />' + vals[1]);
    		}
		});
		highlightErrorFields();
	}
	
	function highlightErrorFields() {
		jQuery.each(options.errorMsgArray, function( ind, objVal ) {
			var vals = objVal.split(':::');
			$('#' + vals[0]).css({borderColor: options.errorFieldBorderColor}).addClass(options.errorFieldClass);
			$('#' + vals[0]).parent().css({
				border: '1px solid ' + options.errorMsgBorderColor,
				backgroundColor: options.errorMsgBGColor,
				padding: options.errorFieldPadding
			});
		});
	}
	
	function clearErrors() {
		$('.error').html('').remove();
		jQuery.each(options.errorMsgArray, function( ind, objVal ) {
			var vals = objVal.split(':::');
			$('#' + vals[0]).parent().css({
				border: 'none',
				backgroundColor: 'transparent'
			});
		});
		options.allFieldRows.css({
			border: 'none',
			backgroundColor: 'transparent'
		});
		$(':input:enabled:not(:submit, :button, :file, :reset, :image).errorField').css({
			borderColor: options.defaultFieldBorderColor
		}).removeClass('errorField');
		options.errorMsgArray = [];
		options.submitForm = true;
	}
	
	function addFieldFocus() {
		$(':input:enabled:not(:submit, :button, :file, :reset, :image)').focus(function () {
			if (! $(this).hasClass(options.errorFieldClass)) {
				$(this).css({borderColor: options.focusBorderColor});
			}
		});
		
		$(':input:enabled:not(:submit, :button, :file, :reset, :image)').blur(function () {
			if (! $(this).hasClass(options.errorFieldClass)) {
				$(this).css({borderColor: options.defaultFieldBorderColor});
			}
		});
	}
	
	function setCursorFocus() {
		if ($(':input.errorField').length > 0) {
			$(':input.errorField').eq(0).focus();
		}
	}
	
	function getLabel(id) {
		var label = '';
		$('#' + id).siblings('label[for=' + id + ']').eq(0).contents().each(function () {
			if(this.nodeType == 3) label = this.nodeValue;
		});
		return label;
	}
	
	function isNumeric(num) {
		var numRG = /(^\d+$)/
		if (numRG.test(num))
			return true;

		return false;
	}
};