(function($) {
	
	nV_failCount = 0;
	
	nV_debug = true;
	
	$.fn.needsValidation = function(options) {

		var defaults = {
			// misc
			classBase: 'validate', // base of validated field classname
			// rules
			customRules: null, // object containing custom rules, regex or functions
			customMessages: null, // object containing custom messages
			// default classes
			failureClass: 'formError', // default class for field and label of error
			failureMessageClass: 'formErrorMessage', // default class for span containing error messages
			// callbacks
			preValidate: null, // called on form submit before validation is done.  Clean up from previous validation here
			failure: null, // called if any validations failed
			success: null // called if all validations passed. return false to prevent form submit (ex: for submitting via ajax)
		};
		
		var options = $.extend(defaults, options);

		var rules = {
			required: /\S/,
			length: nV_length,
			email: /([0-9a-zA-Z]+([_.-]?[0-9a-zA-Z]+)*@[0-9a-zA-Z]+[0-9,a-z,A-Z,.,-]*(.){1}[a-zA-Z]{2,4})+|^$/,
			phone: /[2-9]\d{2}-\d{3}-\d{4}|^$/,
			url: /(http[s]?:\/\/|ftp:\/\/)?(www\.)?[a-zA-Z0-9-\.]+\.([a-zA-Z.]+)|^$/,
			number: /\d+|^$/
		}
		
		var failures = new Array();
		
		if(options.customRules) rules = $.extend(rules, options.customRules);
			
		var errorMessages = {
			required: 'is required',
			email: 'must be a valid email address',
			phone: 'must be a valid phone number',
			url: 'must be a valid url, including "http://"',
			number: 'must be a number'
		}
		
		if(options.customMessages) errorMessages = $.extend(errorMessages, options.customMessages);
		
		$(this).submit(function(){
						
			failures = new Array();
			
			if(options.preValidate) {
				options.preValidate();
			} else {
				$('.nvErrorField').removeClass(options.failureClass).removeClass('nvErrorField');
				$('.nvErrorFieldLabel').removeClass(options.failureClass).removeClass('nvErrorField');
				$('.nvErrorMessage').hide();
			}
			
			$(this).find(':input[class*='+options.classBase+'-]').each(function(){
				var classes = $(this).attr('class').split(' ');
				for(var i=0; i<classes.length; i++) {
					if(classes[i].indexOf(options.classBase+'-') > -1) {
						// This is a class that needs validation
						var parts = classes[i].split('-');
						if(rules[parts[1]] instanceof RegExp) {
							result = rules[parts[1]].test($(this).val());
						} else if(typeof(rules[parts[1]]) == 'function') {
							result = rules[parts[1]](parts[0], $(this).val(), parts.slice(2, parts.length));
						} else {
							continue;
						}
						if((result !== true)) {
							elementFail($(this).attr('id'), parts[1], result);
						}
					}
				}
			});
			if(failures.length) {
				if(options.failure) {
					options.failure(failures);
				} else {
					stdFail(failures);
				}
				return false;
			} else {
				if(options.success) {
					return options.success();
				} else {
					return true;
				}
			}
		});
	
		var elementFail = function(id, rule, msg) {
			if(!msg) msg = (errorMessages[rule]) ? errorMessages[rule] : '';	
			var label = $('label[for='+id+']').text();
			failures.push({id: id, rule: rule, message: msg.toString(), label: label});
		}
		
		var stdFail = function(failures) {
			for(var i=0; i<failures.length; i++) {
				var failure = failures[i];
				var label = $('label[for='+failure.id+']').text().replace(':','');
				$('#'+failure.id).addClass(options.failureClass).addClass('nvErrorField').after('<span class="'+options.failureMessageClass+' nvErrorMessage">'+label+' '+failure.message+'</span> ');
				if(!nV_failCount) $('#'+failure.id).focus();
				$('label[for='+failure.id+']').addClass(options.failureClass).addClass('nvErrorFieldLabel');;
				nV_failCount++;
			}
		}
						
	}
	
	var nV_length = function(className, content, args) {
		nV_log(args);
		if(args[1] && parseInt(args[1]) > parseInt(args[0])) {
			if (content.length >= args[0] && content.length <= args[1]) return true;
				else return 'must be between '+args[0]+' and '+args[1]+' characters long.';
		}
		if(content.length >= args[0]) return true;
			else return 'must be at least '+args[0]+' characters long.';
	}
	
	var nV_log = function(msg) {
		try { if(nV_debug) console.log(msg) } catch(e){}
	}
	
})(jQuery);


