text: /**
   * @license 
   * jQuery Tools Validator 1.2.5 - HTML5 is here. Now use it.
   * 
   * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
   * 
   * http://flowplayer.org/tools/form/validator/
   * 
   * Since: Mar 2010
   * Date:    Wed Sep 22 06:02:10 2010 +0000 
   */
  /*jslint evil: true */ 
  (function($) {	
  
  	$.tools = $.tools || {version: '1.2.5'};
  		
  	// globals
  	var typeRe = /\[type=([a-z]+)\]/, 
  		numRe = /^-?[0-9]*(\.[0-9]+)?$/,
  		dateInput = $.tools.dateinput,
  		
  		// http://net.tutsplus.com/tutorials/other/8-regular-expressions-you-should-know/
  		emailRe = /^([a-z0-9_\.\-\+]+)@([\da-z\.\-]+)\.([a-z\.]{2,6})$/i,
  		urlRe = /^(https?:\/\/)?[\da-z\.\-]+\.[a-z\.]{2,6}[#&+_\?\/\w \.\-=]*$/i,
  		v;
  		 
  	v = $.tools.validator = {
  		
  		conf: {   
  			grouped: false, 				// show all error messages at once inside the container 
  			effect: 'default',			// show/hide effect for error message. only 'default' is built-in
  			errorClass: 'invalid',		// input field class name in case of validation error		
  			
  			// when to check for validity?
  			inputEvent: null,				// change, blur, keyup, null 
  			errorInputEvent: 'keyup',  // change, blur, keyup, null
  			formEvent: 'submit',       // submit, null
  
  			lang: 'en',						// default language for error messages 
  			message: '<div/>',
  			messageAttr: 'data-message', // name of the attribute for overridden error message
  			messageClass: 'error',		// error message element's class name
  			offset: [0, 0], 
  			position: 'center right',
  			singleError: false, 			// validate all inputs at once
  			speed: 'normal'				// message's fade-in speed			
  		},
  
  
  		/* The Error Messages */
  		messages: {
  			"*": { en: "Please correct this value" }		
  		},
  		
  		localize: function(lang, messages) { 
  			$.each(messages, function(key, msg)  {
  				v.messages[key] = v.messages[key] || {};
  				v.messages[key][lang] = msg;		
  			});
  		},
  		
  		localizeFn: function(key, messages) {
  			v.messages[key] = v.messages[key] || {};
  			$.extend(v.messages[key], messages);
  		},
  		
  		/** 
  		 * Adds a new validator 
  		 */
  		fn: function(matcher, msg, fn) {
  			
  			// no message supplied
  			if ($.isFunction(msg)) { 
  				fn = msg; 
  				
  			// message(s) on second argument
  			} else {
  				if (typeof msg == 'string') { msg = {en: msg}; }
  				this.messages[matcher.key || matcher] = msg;
  			}
  
  			// check for "[type=xxx]" (not supported by jQuery)
  			var test = typeRe.exec(matcher);                                    
  			if (test) { matcher = isType(test[1]); }				
  			
  			// add validator to the arsenal
  			fns.push([matcher, fn]);		 
  		},
  
  		/* Add new show/hide effect */
  		addEffect: function(name, showFn, closeFn) {
  			effects[name] = [showFn, closeFn];
  		}
  		
  	};
  	
  	/* calculate error message position relative to the input */  	
  	function getPosition(trigger, el, conf) {	
  		
  		// get origin top/left position 
  		var top = trigger.offset().top, 
  			 left = trigger.offset().left,	 
  			 pos = conf.position.split(/,?\s+/),
  			 y = pos[0],
  			 x = pos[1];
  		
  		top  -= el.outerHeight() - conf.offset[0];
  		left += trigger.outerWidth() + conf.offset[1];
  		
  		
  		// iPad position fix
  		if (/iPad/i.test(navigator.userAgent)) {
  			top -= $(window).scrollTop();
  		}
  		
  		// adjust Y		
  		var height = el.outerHeight() + trigger.outerHeight();
  		if (y == 'center') 	{ top += height / 2; }
  		if (y == 'bottom') 	{ top += height; }
  		
  		// adjust X
  		var width = trigger.outerWidth();
  		if (x == 'center') 	{ left -= (width  + el.outerWidth()) / 2; }
  		if (x == 'left')  	{ left -= width; }	 
  		
  		return {top: top, left: left};
  	}	
  	
  
  	
  	// $.is("[type=xxx]") or $.filter("[type=xxx]") not working in jQuery 1.3.2 or 1.4.2
  	function isType(type) { 
  		function fn() {
  			return this.getAttribute("type") == type;  	
  		} 
  		fn.key = "[type=" + type + "]";
  		return fn;
  	}	
  
  	
  	var fns = [], effects = {
  		
  		'default' : [
  			
  			// show errors function
  			function(errs) {
  				
  				var conf = this.getConf();
  				
  				// loop errors
  				$.each(errs, function(i, err) {
  						
  					// add error class	
  					var input = err.input;					
  					input.addClass(conf.errorClass);
  					
  					// get handle to the error container
  					var msg = input.data("msg.el"); 
  					
  					// create it if not present
  					if (!msg) { 
  						msg = $(conf.message).addClass(conf.messageClass).appendTo(document.body);
  						input.data("msg.el", msg);
  					}  
  					
  					// clear the container 
  					msg.css({visibility: 'hidden'}).find("p").remove();
  					
  					// populate messages
  					$.each(err.messages, function(i, m) { 
  						$("<p/>").html(m).appendTo(msg);			
  					});
  					
  					// make sure the width is not full body width so it can be positioned correctly
  					if (msg.outerWidth() == msg.parent().width()) {
  						msg.add(msg.find("p")).css({display: 'inline'});		
  					} 
  					
  					// insert into correct position (relative to the field)
  					var pos = getPosition(input, msg, conf); 
  					 
  					msg.css({ visibility: 'visible', position: 'absolute', top: pos.top, left: pos.left })
  						.fadeIn(conf.speed);     
  				});
  						
  				
  			// hide errors function
  			}, function(inputs) {
  
  				var conf = this.getConf();				
  				inputs.removeClass(conf.errorClass).each(function() {
  					var msg = $(this).data("msg.el");
  					if (msg) { msg.css({visibility: 'hidden'}); }
  				});
  			}
  		]  
  	};
  
  	
  	/* sperial selectors */
  	$.each("email,url,number".split(","), function(i, key) {
  		$.expr[':'][key] = function(el) {
  			return el.getAttribute("type") === key;
  		};
  	});
  	
  
  	/* 
  		oninvalid() jQuery plugin. 
  		Usage: $("input:eq(2)").oninvalid(function() { ... });
  	*/
  	$.fn.oninvalid = function( fn ){
  		return this[fn ? "bind" : "trigger"]("OI", fn);
  	};
  	
  	
  	/******* built-in HTML5 standard validators *********/
  	
  	v.fn(":email", "Please enter a valid email address", function(el, v) {
  		return !v || emailRe.test(v);
  	});
  	
  	v.fn(":url", "Please enter a valid URL", function(el, v) {
  		return !v || urlRe.test(v);
  	});
  	
  	v.fn(":number", "Please enter a numeric value.", function(el, v) {
  		return numRe.test(v);			
  	});
  	
  	v.fn("[max]", "Please enter a value smaller than $1", function(el, v) {
  			
  		// skip empty values and dateinputs
  		if (v === '' || dateInput && el.is(":date")) { return true; }	
  		
  		var max = el.attr("max");
  		return parseFloat(v) <= parseFloat(max) ? true : [max];
  	});
  	
  	v.fn("[min]", "Please enter a value larger than $1", function(el, v) {
  
  		// skip empty values and dateinputs
  		if (v === '' || dateInput && el.is(":date")) { return true; }
  
  		var min = el.attr("min");
  		return parseFloat(v) >= parseFloat(min) ? true : [min];
  	});
  	
  	v.fn("[required]", "Please complete this mandatory field.", function(el, v) {
  		if (el.is(":checkbox")) { return el.is(":checked"); }
  		return !!v; 			
  	});
  	
  	v.fn("[pattern]", function(el) {
  		var p = new RegExp("^" + el.attr("pattern") + "$");  
  		return p.test(el.val()); 			
  	});
  
  	
  	function Validator(inputs, form, conf) {		
  		
  		// private variables
  		var self = this, 
  			 fire = form.add(self);
  
  		// make sure there are input fields available
  		inputs = inputs.not(":button, :image, :reset, :submit");			 
  
  		// utility function
  		function pushMessage(to, matcher, returnValue) {
  			
  			// only one message allowed
  			if (!conf.grouped && to.length) { return; }
  			
  			// the error message
  			var msg;
  			
  			// substitutions are returned
  			if (returnValue === false || $.isArray(returnValue)) {
  				msg = v.messages[matcher.key || matcher] || v.messages["*"];
  				msg = msg[conf.lang] || v.messages["*"].en;
  
  				// substitution
  				var matches = msg.match(/\$\d/g);
  				
  				if (matches && $.isArray(returnValue)) {
  					$.each(matches, function(i) {
  						msg = msg.replace(this, returnValue[i]);
  					});
  				} 					 
  				
  			// error message is returned directly
  			} else {
  				msg = returnValue[conf.lang] || returnValue;
  			}
  			
  			to.push(msg);
  		}
  		
  		
  		// API methods  
  		$.extend(self, {
  
  			getConf: function() {
  				return conf;	
  			},
  			
  			getForm: function() {
  				return form;		
  			},
  			
  			getInputs: function() {
  				return inputs;	
  			},		
  			
  			reflow: function() {
  				inputs.each(function()  {
  					var input = $(this),
  						 msg = input.data("msg.el");
  						 
  					if (msg) {						
  						var pos = getPosition(input, msg, conf);
  						msg.css({ top: pos.top, left: pos.left });
  					}
  				});
  				return self;
  			},
  			
  			/* @param e - for internal use only */
  			invalidate: function(errs, e) {
  				
  				// errors are given manually: { fieldName1: 'message1', fieldName2: 'message2' }
  				if (!e) {
  					var errors = [];
  					$.each(errs, function(key, val) {
  						var input = inputs.filter("[name='" + key + "']");
  						if (input.length) {
  							
  							// trigger HTML5 ininvalid event
  							input.trigger("OI", [val]);
  							
  							errors.push({ input: input, messages: [val]});				
  						}
  					});
  
  				  	errs = errors; 
  					e = $.Event();
  				}
  				
  				// onFail callback
  				e.type = "onFail";					
  				fire.trigger(e, [errs]); 
  				
  				// call the effect
  				if (!e.isDefaultPrevented()) {						
  					effects[conf.effect][0].call(self, errs, e);													
  				}
  				
  				return self;
  			},
  			
  			reset: function(els) {
  				els = els || inputs;
  				els.removeClass(conf.errorClass).each(function()  {
  					var msg = $(this).data("msg.el");
  					if (msg) {
  						msg.remove();
  						$(this).data("msg.el", null);
  					}
  				}).unbind(conf.errorInputEvent || '');
  				return self;
  			},
  			
  			destroy: function() { 
  				form.unbind(conf.formEvent + ".V").unbind("reset.V"); 
  				inputs.unbind(conf.inputEvent + ".V").unbind("change.V");
  				return self.reset();	
  			}, 
  			
  			
  //{{{  checkValidity() - flesh and bone of this tool
  						
  			/* @returns boolean */
  			checkValidity: function(els, e) {
  				
  				els = els || inputs;    
  				els = els.not(":disabled");
  				if (!els.length) { return true; }
  
  				e = e || $.Event();
  
  				// onBeforeValidate
  				e.type = "onBeforeValidate";
  				fire.trigger(e, [els]);				
  				if (e.isDefaultPrevented()) { return e.result; }				
  					
  				// container for errors
  				var errs = [];
   
  				// loop trough the inputs
  				els.not(":radio:not(:checked)").each(function() {
  						
  					// field and it's error message container						
  					var msgs = [], 
  						 el = $(this).data("messages", msgs),
  						 event = dateInput && el.is(":date") ? "onHide.v" : conf.errorInputEvent + ".v";					
  					
  					// cleanup previous validation event
  					el.unbind(event);
  					
  					
  					// loop all validator functions
  					$.each(fns, function() {
  						var fn = this, match = fn[0]; 
  					
  						// match found
  						if (el.filter(match).length)  {  
  							
  							// execute a validator function
  							var returnValue = fn[1].call(self, el, el.val());
  							
  							
  							// validation failed. multiple substitutions can be returned with an array
  							if (returnValue !== true) {								
  								
  								// onBeforeFail
  								e.type = "onBeforeFail";
  								fire.trigger(e, [el, match]);
  								if (e.isDefaultPrevented()) { return false; }
  								
  								// overridden custom message
  								var msg = el.attr(conf.messageAttr);
  								if (msg) { 
  									msgs = [msg];
  									return false;
  								} else {
  									pushMessage(msgs, match, returnValue);
  								}
  							}							
  						}
  					});
  					
  					if (msgs.length) { 
  						
  						errs.push({input: el, messages: msgs});  
  						
  						// trigger HTML5 ininvalid event
  						el.trigger("OI", [msgs]);
  						
  						// begin validating upon error event type (such as keyup) 
  						if (conf.errorInputEvent) {							
  							el.bind(event, function(e) {
  								self.checkValidity(el, e);		
  							});							
  						} 					
  					}
  					
  					if (conf.singleError && errs.length) { return false; }
  					
  				});
  				
  				
  				// validation done. now check that we have a proper effect at hand
  				var eff = effects[conf.effect];
  				if (!eff) { throw "Validator: cannot find effect \"" + conf.effect + "\""; }
  				
  				// errors found
  				if (errs.length) {					 
  					self.invalidate(errs, e); 
  					return false;
  					
  				// no errors
  				} else {
  					
  					// call the effect
  					eff[1].call(self, els, e);
  					
  					// onSuccess callback
  					e.type = "onSuccess";					
  					fire.trigger(e, [els]);
  					
  					els.unbind(conf.errorInputEvent + ".v");
  				}
  				
  				return true;				
  			}
  //}}} 
  			
  		});
  		
  		// callbacks	
  		$.each("onBeforeValidate,onBeforeFail,onFail,onSuccess".split(","), function(i, name) {
  				
  			// configuration
  			if ($.isFunction(conf[name]))  {
  				$(self).bind(name, conf[name]);	
  			}
  			
  			// API methods				
  			self[name] = function(fn) {
  				if (fn) { $(self).bind(name, fn); }
  				return self;
  			};
  		});	
  		
  		
  		// form validation
  		if (conf.formEvent) {
  			form.bind(conf.formEvent + ".V", function(e) {
  				if (!self.checkValidity(null, e)) { 
  					return e.preventDefault(); 
  				}
  			});
  		}
  		
  		// form reset
  		form.bind("reset.V", function()  {
  			self.reset();			
  		});
  		
  		// disable browser's default validation mechanism
  		if (inputs[0] && inputs[0].validity) {
  			inputs.each(function()  {
  				this.oninvalid = function() { 
  					return false; 
  				};		
  			});
  		}
  		
  		// Web Forms 2.0 compatibility
  		if (form[0]) {
  			form[0].checkValidity = self.checkValidity;
  		}
  		
  		// input validation               
  		if (conf.inputEvent) {
  			inputs.bind(conf.inputEvent + ".V", function(e) {
  				self.checkValidity($(this), e);
  			});	
  		} 
  	
  		// checkboxes, selects and radios are checked separately
  		inputs.filter(":checkbox, select").filter("[required]").bind("change.V", function(e) {
  			var el = $(this);
  			if (this.checked || (el.is("select") && $(this).val())) {
  				effects[conf.effect][1].call(self, el, e); 
  			}
  		});		
  		
  		var radios = inputs.filter(":radio").change(function(e) {
  			self.checkValidity(radios, e);
  		});
  		
  		// reposition tooltips when window is resized
  		$(window).resize(function() {
  			self.reflow();		
  		});
  	}
  
  	
  	// jQuery plugin initialization
  	$.fn.validator = function(conf) { 
  		
  		var instance = this.data("validator");
  		
  		// destroy existing instance
  		if (instance) { 
  			instance.destroy();
  			this.removeData("validator");
  		} 
  		
  		// configuration
  		conf = $.extend(true, {}, v.conf, conf);		
  		
  		// selector is a form		
  		if (this.is("form")) {
  			return this.each(function() {			
  				var form = $(this); 
  				instance = new Validator(form.find(":input"), form, conf);	 
  				form.data("validator", instance);
  			});
  			
  		} else {
  			instance = new Validator(this, this.eq(0).closest("form"), conf);
  			return this.data("validator", instance);
  		}     
  		
  	};   
  		
  })(jQuery);
  			
  
  