
(function($){
	
	$.fn.validate2 = function( opts ){
		
		var Plugin		= function( options ){

			var inst	= this;

			this.defaults		= {
				
				templates : {
					wrapper : '<div class="validation-tip-wrapper"></div>',
					tip		: '<div class="{class}" style="width:{width};">{msg}</div>'
				},

				validateOnBlur		: true,
				validateOnChange	: true,
				revalidateOnKey		: true,
				allowBlank			: false,
				inputTips			: true,
				msg					: 'Cannot be blank',
				successCls			: 'valPassed',
				failureCls			: 'valFailed',
				_onSubmitSuccess	: function(){
					return true;
				},
				_onSubmitFailed		: function(){
					return false;
				}
			};

			this.construct		= function(){
				
				this.setupVars();
				this.bind();
				
				return this;
				
			}

			this.setupVars		= function(){

				this.conf			= $.extend( true, {},  this.defaults, options, opts );

				this.parentClassID	= '_jsForm' + this.i;

				this.$form			= options.$form;
				this.$form.data( 'valid', false );

				this.$inputs		= this.$form.find("input[type!='radio']");
				this.$selects		= this.$form.find("select");
				this.$radioGroups	= this.$form.find("div.radioGroup");
				
				this.$formElements	= this.$inputs.add( this.$selects ).add( this.$radioGroups );

				
				
			}

			this.elementsBehaviour	= function(){				

				this.$formElements.each( function( i ){

					var $each		= $(this),
						rel			= $each.attr('rel'),
						params		= inst.publicMethod.evalRel( rel ),
						ignore		= $each.hasClass('_jsIgnore') || params.ignore,
						conf		= $.extend( true, {}, inst.conf, params );
						
					inst.publicMethod.setupElement( $each, conf, i );
					
					if( ignore ){
						return true;
					}
					
					if( conf.validateOnBlur ){
						inst.publicMethod.bindBlur( $each );
					}

					if( conf.validateOnChange ){
						inst.publicMethod.bindChange( $each );
					}

					if( conf.revalidateOnKey ){
						inst.publicMethod.bindRevalidateOnKey( $each );
					}

				});

				inst.publicMethod.bindSubmit();
				
			}

			this.validateForm	= function( $elements ){
				
				var x			= 0;

				if( !$elements ){
					$elements = this.$formElements;
				}

				$elements.each( function( i ){

					var $each		= $(this),
						conf		= $each.data('conf'),
						ignore		= $each.hasClass('_jsIgnore') || conf.ignore,
						disabled	= $each.is(':disabled'),
						hidden		= ( function(){
							
							var hidden	= false;
							$each.parents().each(function(){
								if( $(this).css('display') == 'none' ){
									hidden = true;
									return false;
								}
							});

							if( $each.css('display') == 'none' ){
								return true;
							}
							
							return hidden;
							
						})();
					
					if( ignore || hidden || disabled ){
						return true;
					}

					var state = inst.publicMethod.validate( $each );

					if(state == 1){
						inst.publicMethod.onValidTrue( $each );
					} else {
						inst.publicMethod.onValidFalse( $each );
						x++;
					}

				});

				return x == 0;

			}


			this.bind			= function(){
				
				this.elementsBehaviour();
				
			}

			this.publicMethod	= {
				
				evalRel	: function( rel ){
					if( typeof(rel) == 'string' ){
						var o	= $.parseJSON( rel );
						if( o.validator ){
							return o.validator
						}
						return o;
					}
					return false;
				},

				setupElement : function( $el, conf, i ){
					
					var tip		= inst.publicMethod.replace( inst.conf.templates.tip, {
						'msg'		: conf.msg,
						'class'		: '_jsFormElement' + i,
						'width'		: $el.outerWidth() + 'px'
					});

					var $wrap	= $( inst.conf.templates.wrapper );
					
					conf.$tip	= $wrap.html( tip );
					
					conf.i		= i;
					
					$el.data( 'conf', conf );

					if( conf.inputTips ){
						$el.after( conf.$tip );
					}
					
				},

				// Binding for an element's blur //
				bindBlur	: function( $el ){
					
					$el.blur(function(){
						
						var state = inst.publicMethod.validate( $el );

						if( state == 1 ){

							inst.publicMethod.onValidTrue( $el );

						} else if( state == 0 ){

							inst.publicMethod.onValidFalse( $el );
							
						}

					});

				},

				bindChange	: function( $el ){

					$el.change(function(){

						/* Change the class based on state */
						var state = inst.publicMethod.validate( $el );

						if( state == 1 ){

							inst.publicMethod.onValidTrue( $el );

						} else if( state == 0 ){

							inst.publicMethod.onValidFalse( $el );

						}
					});	

				},

				bindRevalidateOnKey : function( $el ){

					$el.keypress(function(){
						
						if( $el.data("state") == 0 ){

							var state = inst.publicMethod.validate( $el );
							
							if(state == 1){
								inst.publicMethod.onValidTrue( $el );
							}
							
						}

					});

				},

				bindFocus	: function( $el ){

					$el.focus(function(){

						if( $el.data("state") == 0 ){
							/* Display the tip */
							inst.publicMethod.showTip( $el );
						}

					});

				},

				bindSubmit	: function(){

					inst.$form.submit( function(){
						var state = inst.validateForm();
						inst.$form.data( 'valid', state );

						if( state ){
							return inst.conf._onSubmitSuccess();
						} else {
							inst.conf._onSubmitFailed( inst.$form );
							return false;
						}

					});

				},

				onValidTrue : function( $el ){
					
					var conf = $el.data('conf');
					inst.publicMethod.hideTip( $el );
					
					$el.addClass(conf.successCls);
					$el.removeClass(conf.failureCls);
				},

				onValidFalse : function( $el ){
					
					var conf	= $el.data('conf');
					inst.publicMethod.showTip( $el );

					$el.removeClass(conf.successCls);
					$el.addClass(conf.failureCls);
					
				},

				showTip		: function( $el ){
					
					var conf	= $el.data('conf');
					conf.$tip.show();

				},

				hideTip		: function( $el ){
					
					var conf	= $el.data('conf');
					conf.$tip.hide();
					
				},
				
				validate	: function( $el ){
					
					/* Intialise some objects and vars */
					var st			= [],
						i			= $el.data("i"),
						val			= $el.val(),
						params		= $el.data('conf'),
						parentID	= inst.parentClassID,
						opts		= $el.data('conf'),
						pattern;
					
					switch( opts.vtype ){

						/* Check if value is a valid email address. */
						case "email" :
							pattern = /^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+\.([a-zA-Z])+([a-zA-Z])+$/;
							if(validate(val, pattern, false) == false){
								/* State will be needed to determine key up validation */
								st.push(0);
							}

							/* Default message */
							!params.msg ? opts.msg = opts.messages.email:'';
							
							break;

						case "phone" :
							pattern = /^(\d+$)|^(\+\d+$)|^(\(\d{2,3}\)\d+$)/;
							if(validate(val, pattern, true) == false){
								st.push(0);
							}

							/* Default message */
							!params.msg ? opts.msg = opts.messages.phone:'';
						
							break;

						case "password" :
							pattern = /^[a-zA-Z\d]+/;
							if(validate(val, pattern, false) == false){
								st.push(0);
							}

							/* Default message */
							!params.msg ? opts.msg = opts.messages.password:'';

							break;

						case "date" :

							pattern = /^\d{1,2}([\-|\/|\.|\\])\d{1,2}([\-|\/|\.|\\])\d{4}$/;

							if(validate(val, pattern, false) == true){

								/* Get the length of the date string */
								var dateParts;
								if(val.indexOf("/") !== -1){
									dateParts = val.split("/");
								} else if (val.indexOf("\\") !== -1){
									dateParts = val.split("\\");
								} else if (val.indexOf("-") !== -1){
									dateParts = val.split("-");
								} else if (val.indexOf(".") !== -1){
									dateParts = val.split(".");
								}

								var d = new Array();

								d[0] = parseInt(dateParts[0]);
								d[1] = parseInt(dateParts[1]);
								d[2] = parseInt(dateParts[2]);

								switch(opts.dateFormat){

									case "mm/dd/yyyy" :

									if(parseDate(d[1], d[0], d[2]) == false){
										st.push(0);
									}

									break;

									case "dd/mm/yyyy" :

									if(parseDate(d[0], d[1], d[2]) == false){
										st.push(0);

									}

									break;
								}
							} else {
								st.push(0);
							}

							/* Default message */
							!params.msg ? opts.msg = opts.messages.date:'';

							break;

						case 'RSAID' :

							/* ************************ CODE FROM IGNITION DEVELOPERS **************************** */

							if(val.length != 13)//added check to see if id number is 13 digits long
							{
								st.push(0);
							} else {

								var mm = val.substring(2,2);
								var dd = val.substring(4,2);

								if((mm>12)||(dd>31))
								{
									st.push(0);
									break;
								}

								if(val.indexOf('000000') != -1)
								{
									st.push(0);
									break;
								}

								var ID = val.split('');
								var val1 = parseInt(ID[0]) + parseInt(ID[2]) + parseInt(ID[4]) + parseInt(ID[6]) + parseInt(ID[8]) + parseInt(ID[10]);

								var val2 = eval(ID[1]+ID[3]+ID[5]+ID[7]+ID[9]+ID[11]) * 2;
								val2 += "";
								var arr = val2.split('');

								var val3 = 0;
								for (var k = 0; k < arr.length; k++) {
									val3 += parseInt(arr[k]);
								}

								var val4 = val1 + val3;
								val4 += "";
								arr = val4.split('');

								var val5 = 10 - arr[1];

								if (val5 == 10)
									val5 = 0;

								if (val1==0 || val2==0)
								{
									st.push(0);
									break;
								}

								if (val5 != ID[12]){
									st.push(0);
								}
							}

							break;

					}

					/* Check for hint value */
					if(opts.hintValue){
						if(val == opts.hintValue){
						  st.push(0);
						}
					}

					/* Check for min length / max length parameters */
					if(opts.maxLength){
						if(val.length > params.maxLength){
							st.push(0);
						}

						/* Default message */
						!params.msg ? opts.msg = opts.messages.maxLength.replace("{x}", opts.maxLength):'';

					}

					if(opts.minLength){
						if(val.length < params.minLength){
							st.push(0);
						}

						/* Default message */
						!params.msg ? opts.msg = opts.messages.minLength.replace("{x}", opts.minLength):'';
					}

					if(opts.setLength){
						if(val.length != params.setLength){
							st.push(0);
						}

						/* Default message */
						!params.msg ? opts.msg = opts.messages.minLength.replace("{x}", opts.minLength):'';
					}

					/* Check for data type */
					if(opts.dataType){
						switch(opts.dataType){

							/* Only whitespace and alphabetic characters */
							case "alpha" :
								if(!validate(val, /^[a-zA-Z]+$/, true)){
									st.push(0);
								}

								/* Default message */
								!params.msg ? opts.msg = opts.messages.alpha:'';

							break;

							/* Only whitespace and integers */
							case "int" :
								if(!validate(val, /^[\d]+$/, true)){
									st.push(0);
								}

								/* Default message */
								!params.msg ? opts.msg = opts.messages.int:'';

							break;

							/* Only whitespace, integers, and alphabetic characters */
							case "alphanum" :
								if(!validate(val, /^[a-zA-Z0-9]+$/, true)){
									st.push(0);
								}

								/* Default message */
								!params.msg ? opts.msg = opts.messages.alphanum:'';

							break;

							/* Single word, can be hyphenated */
							case "oneword" :
								if(!validate(val, /^[a-zA-Z]+$|^[a-zA-Z]+\-?[a-zA-Z]+$/, false)){
									st.push(0);
								}

								/* Default message */
								!params.msg ? opts.msg = opts.messages.oneword:'';

							break;

						}
					}

					/* Custom regular expression */
					if(opts.regExp){
						pattern = new RegExp(params.regExp);
						if(!validate(val, pattern, true)){
							st[i] = 0;
						} else st[i] = 1;
					}

					/* Check extensions */
					if(opts.extensions && val > ""){
						var arr = params.extensions.split(" ");
						var cont = false;
						for(x in arr){
							var pattern = new RegExp(arr[x] + "$");

							if(pattern.test(val)){
								cont = true;
								break;
							}
						}

						if(cont == false) st[i] = 0;

						/* Default message */
						!params.msg ? opts.msg = opts.messages.extensions:'';
					}

					/* Check if allowing input to be blank */
					if(opts.allowBlank !== null){
						if(opts.allowBlank == true && val == ""){
							/* Unset the array */
							st = new Array();
						} else if(opts.allowBlank == false && val == ""){
							st.push(0);
						}
					}

					// If input hinting is on, check if hinting is currently active //
					if(opts.inputHints && $.fn.inputHints){
						if( $.fn.inputHints.hintActive( $el ) ){
							st.push(0);
						}
					}

					/* Exceptional Cases */
					/* Checkbox */
					if(opts.checked !== null){
						if(opts.checked == true){
							if($el.attr("checked") == false){
								st.push(0);
							}
						}
					}

					/* Intialize return state to true */
					if(!opts.preserveState){
						var returnState = 1;
						$el.data("state", returnState);
					} else {
						var returnState = $el.data("state");
					}

					/* Loop through states */
					for(var x in st){
						if(st[x] == 0){
							returnState = 0;
							$el.data("state", returnState);
							break;
						}
					}

					/* Set form message and slidedown tip elements */
					var messageEl = parentID + "-summary-" + i;

					/* Check where to place the message */
					var summaryEl = opts.summaryEl;
					
					/* OVERRIDES (radio group) */
					/* For radio group */
					if($el.hasClass('radioGroup')){
						var $radioGroup = $el;
						/* For changing class of group container to valid */
						var checkInputs = [];
						$radioGroup.find('input').each(function(){
							if($(this).attr('checked')){
								checkInputs.push(1);
							}
						});

						/* If a selection has not been made in the radio group */
						if(checkInputs.length == 0){
							returnState = 0;
						} else returnState = 1;

						$el.data("state", returnState);

					}

					/* Change the class based on state */
					if(returnState == 0){

						if(opts.useParent){
							$el.parent().removeClass(opts.successCls).addClass(opts.failureCls);
						} else {
							$el.removeClass(opts.successCls).addClass(opts.failureCls);
						}

						/* Store the message that's been set for later retrieval */
						$el.data("msg", opts.msg);

					} else if(returnState == 1){

						if(opts.useParent){
							$el.parent().removeClass(opts.failureCls).addClass(opts.successCls);
						} else {
							$el.removeClass(opts.failureCls).addClass(opts.successCls);
						}

					}
					return returnState;
				},

				replace : function( html, obj ){
					
					for( var x in obj ){
						var regexp		= new RegExp("{" + x + "}");
						html			= html.replace(regexp, obj[x]);
					}

					return html;
				}
				
			};

			return this;

		}

		function validate(str, p, strip){
			var str = strip ? str.replace(/\s|\-/g, '') : str;
			return p.test(str);
		}

		function parseDate(d, m, y){
			if(m == 0 || m > 12){
				return false;
			}

			if(m == 4 || m == 6 || m == 9 || m == 11){
				/* For April, June, September, November */
				if(d > 30 || d < 1){
					return false;
				}
				/* For February */
			} else if(m == 2){

				/* Check if it's a leap year */
				if(d > 28 || d < 1){
					if(d == 29){
						if(y%4 !==0){
							return false;
						} else if(y%100 == 0 && y%400 !== 0){
							return false;
						}
					} else return false;
				}

			/* All other days have 31 */
			} else {
				if(d > 31 || d < 1){
					return false;
				}
			}
		}

		return this.each( function( i ){
			var $this	= $(this);
			$this.data('validate', new Plugin({
				$form	: $this,
				i		: i
			}).construct() );
		});

	};

	

})(jQuery);
