$.extend({
	ALPHAS : "[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00C5\u00C8-\u00CB\u00CC-\u00CF\u00E0-\u00E5\u00D2-\u00D6\u00F2-\u00F6\u00E8-\u00EB\u00EC-\u00EF\u00D9-\u00DC\u00F9-\u00FC\u00C7\u00D1\u00D8\u00E7\u00F1\u00F8\u00FF]",
	NOALPHAS : "[^\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00C5\u00C8-\u00CB\u00CC-\u00CF\u00E0-\u00E5\u00D2-\u00D6\u00F2-\u00F6\u00E8-\u00EB\u00EC-\u00EF\u00D9-\u00DC\u00F9-\u00FC\u00C7\u00D1\u00D8\u00E7\u00F1\u00F8\u00FF]",
	LATIN : "[\u0041-\u005A\u0061-\u007A\u00C0-\u00C5\u00C8-\u00CB\u00CC-\u00CF\u00E0-\u00E5\u00D2-\u00D6\u00F2-\u00F6\u00E8-\u00EB\u00EC-\u00EF\u00D9-\u00DC\u00F9-\u00FC\u00C7\u00D1\u00D8\u00E7\u00F1\u00F8\u00FF]",
	NOLATIN : "[^\u0041-\u005A\u0061-\u007A\u00C0-\u00C5\u00C8-\u00CB\u00CC-\u00CF\u00E0-\u00E5\u00D2-\u00D6\u00F2-\u00F6\u00E8-\u00EB\u00EC-\u00EF\u00D9-\u00DC\u00F9-\u00FC\u00C7\u00D1\u00D8\u00E7\u00F1\u00F8\u00FF]",
	DECIMALS : "[0-9.]",
	NODECIMALS : "[^0-9.]",
	NUMBERS : "[0-9]",
	NONUMBERS : "[^0-9]"
});

$.extend({
	HAS_MIN: function(value, min) {
		return ($.trim(value).length >= min);
	},
	HAS_MAX: function(value, max) {
		return ($.trim(value).length <= max);
	},
	IS_IN_RANGE: function(value, min, max) {
		return ($.trim(value).length >= min && $.trim(value).length <= max);
	},
	IS_EMAIL: function(value) {
		return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
	},
	IS_URL: function(value) {
		return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
	},
	IS_DATE: function(value) {
		return !/Invalid|NaN/.test(new Date(value));
	},
	IS_DATE_ISO: function(value) {
		return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
	},
	ONLY_NUMBERS: function(value) {
		var r = new RegExp($.NUMBERS, 'g');
		return r.test(value);
	},
	ONLY_DECIMALS: function(value) {
		var r = new RegExp($.DECIMALS, 'g');
		return r.test(value);
	},
	ONLY_ALPHAS: function(value) {
		var r = new RegExp($.ALPHAS, 'g');
		return r.test(value);
	},
	ONLY_LATIN: function(value) {
		var r = new RegExp($.LATIN, 'g');
		return r.test(value);
	},
	IS_CREDIT_CARD: function(value) {
		// accept only digits and dashes
		if (/[^0-9-]+/.test(value)) { return false; }
		var nCheck = 0, nDigit = 0, bEven = false, value = value.replace(/\D/g, "");
		for (n = value.length - 1; n >= 0; n--) {
			var cDigit = value.charAt(n);
			var nDigit = parseInt(cDigit, 10);
			if (bEven && (nDigit *= 2) > 9) { nDigit -= 9; }
			nCheck += nDigit;
			bEven = !bEven;
		}
		return (nCheck % 10) == 0;
	},
	HAS_MIN_WEIGHT: function(value) {
		var points = 0;
		if (value.length >= 10) { points++; }
		if (/\W/.test(value)) { points++; }
		if (/[A-Z]/.test(value)) { points++; }
		if (/[a-z]/.test(value)) { points++; }
		if (/[0-9]/.test(value)) { points++; }
		return points;
	},
	IN_VALID: function(id, className, MSG) {
		$('#'+id).removeClass('jv-valid jv-invalid').addClass('jv-'+className);
		$('#'+id+'-text').removeClass().addClass(className).html(MSG);
		$('#'+id+'-icon').removeClass().addClass('field-'+className);
	}
});
(function($) {
	var methods = {
		HAS_MIN: function(min, invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.HAS_MIN(o.val(), min)) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});			
		},
		HAS_MAX: function(max, invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			if (o.attr('type') == 'text' || o.attr('type') == 'password') { o.attr({'maxlength':max}); }
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.HAS_MAX(o.val(), max)) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		HAS_EXACT: function(length, invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			if (o.attr('type') == 'text' || o.attr('type') == 'password') { o.attr({'maxlength':length}); }
			o.bind({
				'keyup.validate blur.validate': function(){
					if ($.trim(o.val()).length != length) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		IS_IN_RANGE: function(min, max, invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			if (o.attr('type') == 'text' || o.attr('type') == 'password') { o.attr({'maxlength':max}); }
			o.bind({
				'keyup.validate blur.validate': function(){
					if (!$.IS_IN_RANGE(o.val(), min, max)) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		IS_EMAIL: function(exists, invalidMSG, validMSG, verifyingMSG, registeredMSG, availableMSG, exception) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({
				'keyup.validate blur.validate' : function(){
					if (!$.IS_EMAIL(o.val())) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else  if ($.HAS_MIN(verifyingMSG, 1) && $.HAS_MIN(registeredMSG, 1) && $.HAS_MIN(availableMSG, 1)){
						$(o).emailExists(
							function() {
								$.IN_VALID(id, 'unverified', verifyingMSG);
								$.data(e, 'valid', false);
							},
							function(response) {
								if (exception && response && (o.val() == $.data(e, 'startupValue'))) {
									$.IN_VALID(id, 'valid', validMSG);
									$.data(e, 'valid', true);
								} else if ((response && !exists) || (!response && exists)) {
									$.IN_VALID(id, 'invalid', registeredMSG);
									$.data(e, 'valid', false);
								} else if ((response && exists) || (!response && !exists)) {
									$.IN_VALID(id, 'valid', availableMSG);
									$.data(e, 'valid', true);
								}
							}
						);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});	
			
		},
		IS_URL: function(invalidMSG, validMSG) {			
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.IS_URL(o.val())) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		IS_DATE: function(invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.IS_DATE(o.val())) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		IS_DATE_ISO: function(invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.IS_DATE_ISO(o.val())) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		ONLY_NUMBERS: function(invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.ONLY_NUMBERS(o.val())) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		ONLY_DECIMALS: function(invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.ONLY_DECIMALS(o.val())) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		ONLY_ALPHAS: function(invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.ONLY_ALPHAS(o.val())) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		ONLY_LATIN: function(invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.ONLY_LATIN(o.val())) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		IS_CREDIT_CARD: function(invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = this.attr('id');
			
			o.bind({			
				'keyup.validate blur.validate': function(){
					if (!$.IS_CREDIT_CARD(o.val())) {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', validMSG);
						$.data(e, 'valid', true);
					}
				}
			});
		},
		HAS_MIN_WEIGHT: function(element, level, levelCero, levelOne, levelTwo, levelThree, levelFour, levelFive, minWeight) {
			var power, weight;
			var o = this;
			var e = o[0];
			var id = o.attr('id');
			
			o.bind({
				'keyup.validate blur.validate' : function(){
					weight = $.HAS_MIN_WEIGHT(o.val());
					switch (weight) {
						default : power = levelCero; break;
						case 1 : power = levelOne; break;
						case 2 : power = levelTwo; break;
						case 3 : power = levelThree; break;
						case 4 : power = levelFour; break;
						case 5 : power = levelFive; break;
					}
					if (minWeight > weight) {
						$.IN_VALID(id, 'invalid', level+" "+weight+": "+power);
						$.data(e, 'valid', false);
					} else {
						$.IN_VALID(id, 'valid', level+" "+weight+": "+power);
						$.data(e, 'valid', true);
					}
					$(element).blur();
				}
			});
		},
		IS_EQUAL_TO: function(element, emptyMSG, invalidMSG, validMSG) {			
			var o = this;
			var e = o[0];
			var id = o.attr('id');
			
			o.bind({
				'keyup.validate blur.validate' : function(){
					var a = $.trim(o.val());
					var b = $.trim($(element).val());
					if (a == b) {
						if (a == '' || b == ' ') {
							$.IN_VALID(id, 'invalid', emptyMSG);
							$.data(e, 'valid', false);
						} else {
							$.IN_VALID(id, 'valid', validMSG);
							$.data(e, 'valid', true);
						}
					} else {
						$.IN_VALID(id, 'invalid', invalidMSG);
						$.data(e, 'valid', false);
					}
				}
			});			
		},
		NO_DEFAULT: function(invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = o.attr('id');
			
			o.bind('change.validate', function(){
				var opt = o.find('option')[o.attr('selectedIndex')];
				if (opt.value == '::default::') {
					$.IN_VALID(id, 'invalid', invalidMSG);
					$.data(e, 'valid', false);
				} else {
					$.IN_VALID(id, 'valid', validMSG);
					$.data(e, 'valid', true);
				}
			});
		},
		AT_LEAST_ONE: function(invalidMSG, validMSG) {
			var o = this;
			var e = o[0];
			var id = o.attr('id');
			
			o.bind('change.validate', function(){
				var selected = 0;
				$.each(o.find('option'), function(index, opt){
					if (opt.selected) { selected++; }
				});
				
				if (selected < 1) {
					$.IN_VALID(id, 'invalid', invalidMSG);
					$.data(e, 'valid', false);
				} else {
					$.IN_VALID(id, 'valid', validMSG);
					$.data(e, 'valid', true);
				}
			});
		}
	};
	$.fn.validate = function(method) {
		method = method.toUpperCase();
		$.data($(this)[0], 'startupValue', $(this).val());		
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments,1));
		} else {
			return false;
		}
	};
})( jQuery );

(function($) {
	var methods = {
		LATIN : function() {
			$(this).bind({'keyup.restrict blur.restrict': function() {
				var r = new RegExp($.NOLATIN, 'g');
				$(this).val($(this).val().replace(r, ''));
			}});
		},
		DECIMALS : function() {
			$(this).bind({'keyup.restrict blur.restrict': function() {
				var r = new RegExp($.NODECIMALS, 'g');
				$(this).val($(this).val().replace(r, ''));
			}});
		},
		NUMBERS : function() {
			$(this).bind({'keyup.restrict blur.restrict': function() {
				var r = new RegExp($.NONUMBERS, 'g');
				$(this).val($(this).val().replace(r, ''));
			}});
		}		
	};
	$.fn.restrict = function(range) {
		range = range.toUpperCase();
		if (methods[range]) {
			return methods[range].apply(this, Array.prototype.slice.call(arguments,1));
		} else {
			return false;
		}
	};
})($);
