text: /*
   * Inline Form Validation Engine 2.6.2, jQuery plugin
   *
   * Copyright(c) 2010, Cedric Dugas
   * http://www.position-absolute.com
   *
   * 2.0 Rewrite by Olivier Refalo
   * http://www.crionics.com
   *
   * Form validation engine allowing custom regex rules to be added.
   * Licensed under the MIT License
   */
   (function($) {
  
  	"use strict";
  
  	var methods = {
  
  		/**
  		* Kind of the constructor, called before any action
  		* @param {Map} user options
  		*/
  		init: function(options) {
  			var form = this;
  			if (!form.data('jqv') || form.data('jqv') == null ) {
  				options = methods._saveOptions(form, options);
  				// bind all formError elements to close on click
  				$(document).on("click", ".formError", function() {
  					$(this).fadeOut(150, function() {
  						// remove prompt once invisible
  						$(this).parent('.formErrorOuter').remove();
  						$(this).remove();
  					});
  				});
  			}
  			return this;
  		 },
  		/**
  		* Attachs jQuery.validationEngine to form.submit and field.blur events
  		* Takes an optional params: a list of options
  		* ie. jQuery("#formID1").validationEngine('attach', {promptPosition : "centerRight"});
  		*/
  		attach: function(userOptions) {
  
  			var form = this;
  			var options;
  
  			if(userOptions)
  				options = methods._saveOptions(form, userOptions);
  			else
  				options = form.data('jqv');
  
  			options.validateAttribute = (form.find("[data-validation-engine*=validate]").length) ? "data-validation-engine" : "class";
  			if (options.binded) {
  
  				// delegate fields
  				form.on(options.validationEventTrigger, "["+options.validateAttribute+"*=validate]:not([type=checkbox]):not([type=radio]):not(.datepicker)", methods._onFieldEvent);
  				form.on("click", "["+options.validateAttribute+"*=validate][type=checkbox],["+options.validateAttribute+"*=validate][type=radio]", methods._onFieldEvent);
  				form.on(options.validationEventTrigger,"["+options.validateAttribute+"*=validate][class*=datepicker]", {"delay": 300}, methods._onFieldEvent);
  			}
  			if (options.autoPositionUpdate) {
  				$(window).bind("resize", {
  					"noAnimation": true,
  					"formElem": form
  				}, methods.updatePromptsPosition);
  			}
  			form.on("click","a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick);
  			form.removeData('jqv_submitButton');
  
  			// bind form.submit
  			form.on("submit", methods._onSubmitEvent);
  			return this;
  		},
  		/**
  		* Unregisters any bindings that may point to jQuery.validaitonEngine
  		*/
  		detach: function() {
  
  			var form = this;
  			var options = form.data('jqv');
  
  			// unbind fields
  			form.find("["+options.validateAttribute+"*=validate]").not("[type=checkbox]").off(options.validationEventTrigger, methods._onFieldEvent);
  			form.find("["+options.validateAttribute+"*=validate][type=checkbox],[class*=validate][type=radio]").off("click", methods._onFieldEvent);
  
  			// unbind form.submit
  			form.off("submit", methods._onSubmitEvent);
  			form.removeData('jqv');
              
  			form.off("click", "a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick);
  			form.removeData('jqv_submitButton');
  
  			if (options.autoPositionUpdate)
  				$(window).off("resize", methods.updatePromptsPosition);
  
  			return this;
  		},
  		/**
  		* Validates either a form or a list of fields, shows prompts accordingly.
  		* Note: There is no ajax form validation with this method, only field ajax validation are evaluated
  		*
  		* @return true if the form validates, false if it fails
  		*/
  		validate: function() {
  			var element = $(this);
  			var valid = null;
  
  			if (element.is("form") || element.hasClass("validationEngineContainer")) {
  				if (element.hasClass('validating')) {
  					// form is already validating.
  					// Should abort old validation and start new one. I don't know how to implement it.
  					return false;
  				} else {				
  					element.addClass('validating');
  					var options = element.data('jqv');
  					var valid = methods._validateFields(this);
  
  					// If the form doesn't validate, clear the 'validating' class before the user has a chance to submit again
  					setTimeout(function(){
  						element.removeClass('validating');
  					}, 100);
  					if (valid && options.onSuccess) {
  						options.onSuccess();
  					} else if (!valid && options.onFailure) {
  						options.onFailure();
  					}
  				}
  			} else if (element.is('form') || element.hasClass('validationEngineContainer')) {
  				element.removeClass('validating');
  			} else {
  				// field validation
  				var form = element.closest('form, .validationEngineContainer'),
  					options = (form.data('jqv')) ? form.data('jqv') : $.validationEngine.defaults,
  					valid = methods._validateField(element, options);
  
  				if (valid && options.onFieldSuccess)
  					options.onFieldSuccess();
  				else if (options.onFieldFailure && options.InvalidFields.length > 0) {
  					options.onFieldFailure();
  				}
  			}
  			if(options.onValidationComplete) {
  				// !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing
  				return !!options.onValidationComplete(form, valid);
  			}
  			return valid;
  		},
  		/**
  		*  Redraw prompts position, useful when you change the DOM state when validating
  		*/
  		updatePromptsPosition: function(event) {
  
  			if (event && this == window) {
  				var form = event.data.formElem;
  				var noAnimation = event.data.noAnimation;
  			}
  			else
  				var form = $(this.closest('form, .validationEngineContainer'));
  
  			var options = form.data('jqv');
  			// No option, take default one
  			form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each(function(){
  				var field = $(this);
  				if (options.prettySelect && field.is(":hidden"))
  				  field = form.find("#" + options.usePrefix + field.attr('id') + options.useSuffix);
  				var prompt = methods._getPrompt(field);
  				var promptText = $(prompt).find(".formErrorContent").html();
  
  				if(prompt)
  					methods._updatePrompt(field, $(prompt), promptText, undefined, false, options, noAnimation);
  			});
  			return this;
  		},
  		/**
  		* Displays a prompt on a element.
  		* Note that the element needs an id!
  		*
  		* @param {String} promptText html text to display type
  		* @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
  		* @param {String} possible values topLeft, topRight, bottomLeft, centerRight, bottomRight
  		*/
  		showPrompt: function(promptText, type, promptPosition, showArrow) {
  			var form = this.closest('form, .validationEngineContainer');
  			var options = form.data('jqv');
  			// No option, take default one
  			if(!options)
  				options = methods._saveOptions(this, options);
  			if(promptPosition)
  				options.promptPosition=promptPosition;
  			options.showArrow = showArrow==true;
  
  			methods._showPrompt(this, promptText, type, false, options);
  			return this;
  		},
  		/**
  		* Closes form error prompts, CAN be invidual
  		*/
  		hide: function() {
  			 var form = $(this).closest('form, .validationEngineContainer');
  			 var options = form.data('jqv');
  			 var fadeDuration = (options && options.fadeDuration) ? options.fadeDuration : 0.3;
  			 var closingtag;
  			 
  			 if($(this).is("form") || $(this).hasClass("validationEngineContainer")) {
  				 closingtag = "parentForm"+methods._getClassName($(this).attr("id"));
  			 } else {
  				 closingtag = methods._getClassName($(this).attr("id")) +"formError";
  			 }
  			 $('.'+closingtag).fadeTo(fadeDuration, 0.3, function() {
  				 $(this).parent('.formErrorOuter').remove();
  				 $(this).remove();
  			 });
  			 return this;
  		 },
  		 /**
  		 * Closes all error prompts on the page
  		 */
  		 hideAll: function() {
  
  			 var form = this;
  			 var options = form.data('jqv');
  			 var duration = options ? options.fadeDuration:300;
  			 $('.formError').fadeTo(duration, 300, function() {
  				 $(this).parent('.formErrorOuter').remove();
  				 $(this).remove();
  			 });
  			 return this;
  		 },
  		/**
  		* Typically called when user exists a field using tab or a mouse click, triggers a field
  		* validation
  		*/
  		_onFieldEvent: function(event) {
  			var field = $(this);
  			var form = field.closest('form, .validationEngineContainer');
  			var options = form.data('jqv');
  			options.eventTrigger = "field";
  			// validate the current field
  			window.setTimeout(function() {
  				methods._validateField(field, options);
  				if (options.InvalidFields.length == 0 && options.onFieldSuccess) {
  					options.onFieldSuccess();
  				} else if (options.InvalidFields.length > 0 && options.onFieldFailure) {
  					options.onFieldFailure();
  				}
  			}, (event.data) ? event.data.delay : 0);
  
  		},
  		/**
  		* Called when the form is submited, shows prompts accordingly
  		*
  		* @param {jqObject}
  		*            form
  		* @return false if form submission needs to be cancelled
  		*/
  		_onSubmitEvent: function() {
  			var form = $(this);
  			var options = form.data('jqv');
  			
  			//check if it is trigger from skipped button
  			if (form.data("jqv_submitButton")){
  				var submitButton = $("#" + form.data("jqv_submitButton"));
  				if (submitButton){
  					if (submitButton.length > 0){
  						if (submitButton.hasClass("validate-skip") || submitButton.attr("data-validation-engine-skip") == "true")
  							return true;
  					}
  				}
  			}
  
  			options.eventTrigger = "submit";
  
  			// validate each field 
  			// (- skip field ajax validation, not necessary IF we will perform an ajax form validation)
  			var r=methods._validateFields(form);
  
  			if (r && options.ajaxFormValidation) {
  				methods._validateFormWithAjax(form, options);
  				// cancel form auto-submission - process with async call onAjaxFormComplete
  				return false;
  			}
  
  			if(options.onValidationComplete) {
  				// !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing
  				return !!options.onValidationComplete(form, r);
  			}
  			return r;
  		},
  		/**
  		* Return true if the ajax field validations passed so far
  		* @param {Object} options
  		* @return true, is all ajax validation passed so far (remember ajax is async)
  		*/
  		_checkAjaxStatus: function(options) {
  			var status = true;
  			$.each(options.ajaxValidCache, function(key, value) {
  				if (!value) {
  					status = false;
  					// break the each
  					return false;
  				}
  			});
  			return status;
  		},
  		
  		/**
  		* Return true if the ajax field is validated
  		* @param {String} fieldid
  		* @param {Object} options
  		* @return true, if validation passed, false if false or doesn't exist
  		*/
  		_checkAjaxFieldStatus: function(fieldid, options) {
  			return options.ajaxValidCache[fieldid] == true;
  		},
  		/**
  		* Validates form fields, shows prompts accordingly
  		*
  		* @param {jqObject}
  		*            form
  		* @param {skipAjaxFieldValidation}
  		*            boolean - when set to true, ajax field validation is skipped, typically used when the submit button is clicked
  		*
  		* @return true if form is valid, false if not, undefined if ajax form validation is done
  		*/
  		_validateFields: function(form) {
  			var options = form.data('jqv');
  
  			// this variable is set to true if an error is found
  			var errorFound = false;
  
  			// Trigger hook, start validation
  			form.trigger("jqv.form.validating");
  			// first, evaluate status of non ajax fields
  			var first_err=null;
  			form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each( function() {
  				var field = $(this);
  				var names = [];
  				if ($.inArray(field.attr('name'), names) < 0) {
  					errorFound |= methods._validateField(field, options);
  					if (errorFound && first_err==null)
  						if (field.is(":hidden") && options.prettySelect)
  							first_err = field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix);
  						else {
  
  							//Check if we need to adjust what element to show the prompt on
  							//and and such scroll to instead
  							if(field.data('jqv-prompt-at') instanceof jQuery ){
  								field = field.data('jqv-prompt-at');
  							} else if(field.data('jqv-prompt-at')) {
  								field = $(field.data('jqv-prompt-at'));
  							}
  							first_err=field;
  						}
  					if (options.doNotShowAllErrosOnSubmit)
  						return false;
  					names.push(field.attr('name'));
  
  					//if option set, stop checking validation rules after one error is found
  					if(options.showOneMessage == true && errorFound){
  						return false;
  					}
  				}
  			});
  
  			// second, check to see if all ajax calls completed ok
  			// errorFound |= !methods._checkAjaxStatus(options);
  
  			// third, check status and scroll the container accordingly
  			form.trigger("jqv.form.result", [errorFound]);
  
  			if (errorFound) {
  				if (options.scroll) {
  					var destination=first_err.offset().top;
  					var fixleft = first_err.offset().left;
  
  					//prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10)
  					var positionType=options.promptPosition;
  					if (typeof(positionType)=='string' && positionType.indexOf(":")!=-1)
  						positionType=positionType.substring(0,positionType.indexOf(":"));
  
  					if (positionType!="bottomRight" && positionType!="bottomLeft") {
  						var prompt_err= methods._getPrompt(first_err);
  						if (prompt_err) {
  							destination=prompt_err.offset().top;
  						}
  					}
  					
  					// Offset the amount the page scrolls by an amount in px to accomodate fixed elements at top of page
  					if (options.scrollOffset) {
  						destination -= options.scrollOffset;
  					}
  
  					// get the position of the first error, there should be at least one, no need to check this
  					//var destination = form.find(".formError:not('.greenPopup'):first").offset().top;
  					if (options.isOverflown) {
  						var overflowDIV = $(options.overflownDIV);
  						if(!overflowDIV.length) return false;
  						var scrollContainerScroll = overflowDIV.scrollTop();
  						var scrollContainerPos = -parseInt(overflowDIV.offset().top);
  
  						destination += scrollContainerScroll + scrollContainerPos - 5;
  						var scrollContainer = $(options.overflownDIV + ":not(:animated)");
  
  						scrollContainer.animate({ scrollTop: destination }, 1100, function(){
  							if(options.focusFirstField) first_err.focus();
  						});
  
  					} else {
  						$("html, body").animate({
  							scrollTop: destination
  						}, 1100, function(){
  							if(options.focusFirstField) first_err.focus();
  						});
  						$("html, body").animate({scrollLeft: fixleft},1100)
  					}
  
  				} else if(options.focusFirstField)
  					first_err.focus();
  				return false;
  			}
  			return true;
  		},
  		/**
  		* This method is called to perform an ajax form validation.
  		* During this process all the (field, value) pairs are sent to the server which returns a list of invalid fields or true
  		*
  		* @param {jqObject} form
  		* @param {Map} options
  		*/
  		_validateFormWithAjax: function(form, options) {
  
  			var data = form.serialize();
  									var type = (options.ajaxFormValidationMethod) ? options.ajaxFormValidationMethod : "GET";
  			var url = (options.ajaxFormValidationURL) ? options.ajaxFormValidationURL : form.attr("action");
  									var dataType = (options.dataType) ? options.dataType : "json";
  			$.ajax({
  				type: type,
  				url: url,
  				cache: false,
  				dataType: dataType,
  				data: data,
  				form: form,
  				methods: methods,
  				options: options,
  				beforeSend: function() {
  					return options.onBeforeAjaxFormValidation(form, options);
  				},
  				error: function(data, transport) {
  					if (options.onFailure) {
  						options.onFailure(data, transport);
  					} else {
  						methods._ajaxError(data, transport);
  					}
  				},
  				success: function(json) {
  					if ((dataType == "json") && (json !== true)) {
  						// getting to this case doesn't necessary means that the form is invalid
  						// the server may return green or closing prompt actions
  						// this flag helps figuring it out
  						var errorInForm=false;
  						for (var i = 0; i < json.length; i++) {
  							var value = json[i];
  
  							var errorFieldId = value[0];
  							var errorField = $($("#" + errorFieldId)[0]);
  
  							// make sure we found the element
  							if (errorField.length == 1) {
  
  								// promptText or selector
  								var msg = value[2];
  								// if the field is valid
  								if (value[1] == true) {
  
  									if (msg == ""  || !msg){
  										// if for some reason, status==true and error="", just close the prompt
  										methods._closePrompt(errorField);
  									} else {
  										// the field is valid, but we are displaying a green prompt
  										if (options.allrules[msg]) {
  											var txt = options.allrules[msg].alertTextOk;
  											if (txt)
  												msg = txt;
  										}
  										if (options.showPrompts) methods._showPrompt(errorField, msg, "pass", false, options, true);
  									}
  								} else {
  									// the field is invalid, show the red error prompt
  									errorInForm|=true;
  									if (options.allrules[msg]) {
  										var txt = options.allrules[msg].alertText;
  										if (txt)
  											msg = txt;
  									}
  									if(options.showPrompts) methods._showPrompt(errorField, msg, "", false, options, true);
  								}
  							}
  						}
  						options.onAjaxFormComplete(!errorInForm, form, json, options);
  					} else
  						options.onAjaxFormComplete(true, form, json, options);
  
  				}
  			});
  
  		},
  		/**
  		* Validates field, shows prompts accordingly
  		*
  		* @param {jqObject}
  		*            field
  		* @param {Array[String]}
  		*            field's validation rules
  		* @param {Map}
  		*            user options
  		* @return false if field is valid (It is inversed for *fields*, it return false on validate and true on errors.)
  		*/
  		_validateField: function(field, options, skipAjaxValidation) {
  			if (!field.attr("id")) {
  				field.attr("id", "form-validation-field-" + $.validationEngine.fieldIdCounter);
  				++$.validationEngine.fieldIdCounter;
  			}
  
             if (!options.validateNonVisibleFields && (field.is(":hidden") && !options.prettySelect || field.parent().is(":hidden")))
  				return false;
  
  			var rulesParsing = field.attr(options.validateAttribute);
  			var getRules = /validate\[(.*)\]/.exec(rulesParsing);
  
  			if (!getRules)
  				return false;
  			var str = getRules[1];
  			var rules = str.split(/\[|,|\]/);
  
  			// true if we ran the ajax validation, tells the logic to stop messing with prompts
  			var isAjaxValidator = false;
  			var fieldName = field.attr("name");
  			var promptText = "";
  			var promptType = "";
  			var required = false;
  			var limitErrors = false;
  			options.isError = false;
  			options.showArrow = true;
  			
  			// If the programmer wants to limit the amount of error messages per field,
  			if (options.maxErrorsPerField > 0) {
  				limitErrors = true;
  			}
  
  			var form = $(field.closest("form, .validationEngineContainer"));
  			// Fix for adding spaces in the rules
  			for (var i = 0; i < rules.length; i++) {
  				rules[i] = rules[i].replace(" ", "");
  				// Remove any parsing errors
  				if (rules[i] === '') {
  					delete rules[i];
  				}
  			}
  
  			for (var i = 0, field_errors = 0; i < rules.length; i++) {
  				
  				// If we are limiting errors, and have hit the max, break
  				if (limitErrors && field_errors >= options.maxErrorsPerField) {
  					// If we haven't hit a required yet, check to see if there is one in the validation rules for this
  					// field and that it's index is greater or equal to our current index
  					if (!required) {
  						var have_required = $.inArray('required', rules);
  						required = (have_required != -1 &&  have_required >= i);
  					}
  					break;
  				}
  				
  				
  				var errorMsg = undefined;
  				switch (rules[i]) {
  
  					case "required":
  						required = true;
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._required);
  						break;
  					case "custom":
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._custom);
  						break;
  					case "groupRequired":
  						// Check is its the first of group, if not, reload validation with first field
  						// AND continue normal validation on present field
  						var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]";
  						var firstOfGroup = form.find(classGroup).eq(0);
  						if(firstOfGroup[0] != field[0]){
  
  							methods._validateField(firstOfGroup, options, skipAjaxValidation); 
  							options.showArrow = true;
  
  						}
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._groupRequired);
  						if(errorMsg)  required = true;
  						options.showArrow = false;
  						break;
  					case "ajax":
  						// AJAX defaults to returning it's loading message
  						errorMsg = methods._ajax(field, rules, i, options);
  						if (errorMsg) {
  							promptType = "load";
  						}
  						break;
  					case "minSize":
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minSize);
  						break;
  					case "maxSize":
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxSize);
  						break;
  					case "min":
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._min);
  						break;
  					case "max":
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._max);
  						break;
  					case "past":
  						errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._past);
  						break;
  					case "future":
  						errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._future);
  						break;
  					case "dateRange":
  						var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]";
  						options.firstOfGroup = form.find(classGroup).eq(0);
  						options.secondOfGroup = form.find(classGroup).eq(1);
  
  						//if one entry out of the pair has value then proceed to run through validation
  						if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) {
  							errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateRange);
  						}
  						if (errorMsg) required = true;
  						options.showArrow = false;
  						break;
  
  					case "dateTimeRange":
  						var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]";
  						options.firstOfGroup = form.find(classGroup).eq(0);
  						options.secondOfGroup = form.find(classGroup).eq(1);
  
  						//if one entry out of the pair has value then proceed to run through validation
  						if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) {
  							errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateTimeRange);
  						}
  						if (errorMsg) required = true;
  						options.showArrow = false;
  						break;
  					case "maxCheckbox":
  						field = $(form.find("input[name='" + fieldName + "']"));
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxCheckbox);
  						break;
  					case "minCheckbox":
  						field = $(form.find("input[name='" + fieldName + "']"));
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minCheckbox);
  						break;
  					case "equals":
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._equals);
  						break;
  					case "funcCall":
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._funcCall);
  						break;
  					case "creditCard":
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._creditCard);
  						break;
  					case "condRequired":
  						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._condRequired);
  						if (errorMsg !== undefined) {
  							required = true;
  						}
  						break;
  
  					default:
  				}
  				
  				var end_validation = false;
  				
  				// If we were passed back an message object, check what the status was to determine what to do
  				if (typeof errorMsg == "object") {
  					switch (errorMsg.status) {
  						case "_break":
  							end_validation = true;
  							break;
  						// If we have an error message, set errorMsg to the error message
  						case "_error":
  							errorMsg = errorMsg.message;
  							break;
  						// If we want to throw an error, but not show a prompt, return early with true
  						case "_error_no_prompt":
  							return true;
  							break;
  						// Anything else we continue on
  						default:
  							break;
  					}
  				}
  				
  				// If it has been specified that validation should end now, break
  				if (end_validation) {
  					break;
  				}
  				
  				// If we have a string, that means that we have an error, so add it to the error message.
  				if (typeof errorMsg == 'string') {
  					promptText += errorMsg + "<br/>";
  					options.isError = true;
  					field_errors++;
  				}	
  			}
  			// If the rules required is not added, an empty field is not validated
  			//the 3rd condition is added so that even empty password fields should be equal
  			//otherwise if one is filled and another left empty, the "equal" condition would fail
  			//which does not make any sense
  			if(!required && !(field.val()) && field.val().length < 1 && rules.indexOf("equals") < 0) options.isError = false;
  
  			// Hack for radio/checkbox group button, the validation go into the
  			// first radio/checkbox of the group
  			var fieldType = field.prop("type");
  			var positionType=field.data("promptPosition") || options.promptPosition;
  
  			if ((fieldType == "radio" || fieldType == "checkbox") && form.find("input[name='" + fieldName + "']").size() > 1) {
  				if(positionType === 'inline') {
  					field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:last"));
  				} else {
  				field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:first"));
  				}
  				options.showArrow = false;
  			}
  
  			if(field.is(":hidden") && options.prettySelect) {
  				field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix);
  			}
  
  			if (options.isError && options.showPrompts){
  				methods._showPrompt(field, promptText, promptType, false, options);
  			}else{
  				if (!isAjaxValidator) methods._closePrompt(field);
  			}
  
  			if (!isAjaxValidator) {
  				field.trigger("jqv.field.result", [field, options.isError, promptText]);
  			}
  
  			/* Record error */
  			var errindex = $.inArray(field[0], options.InvalidFields);
  			if (errindex == -1) {
  				if (options.isError)
  				options.InvalidFields.push(field[0]);
  			} else if (!options.isError) {
  				options.InvalidFields.splice(errindex, 1);
  			}
  				
  			methods._handleStatusCssClasses(field, options);
  	
  			/* run callback function for each field */
  			if (options.isError && options.onFieldFailure)
  				options.onFieldFailure(field);
  
  			if (!options.isError && options.onFieldSuccess)
  				options.onFieldSuccess(field);
  
  			return options.isError;
  		},
  		/**
  		* Handling css classes of fields indicating result of validation 
  		*
  		* @param {jqObject}
  		*            field
  		* @param {Array[String]}
  		*            field's validation rules            
  		* @private
  		*/
  		_handleStatusCssClasses: function(field, options) {
  			/* remove all classes */
  			if(options.addSuccessCssClassToField)
  				field.removeClass(options.addSuccessCssClassToField);
  			
  			if(options.addFailureCssClassToField)
  				field.removeClass(options.addFailureCssClassToField);
  			
  			/* Add classes */
  			if (options.addSuccessCssClassToField && !options.isError)
  				field.addClass(options.addSuccessCssClassToField);
  			
  			if (options.addFailureCssClassToField && options.isError)
  				field.addClass(options.addFailureCssClassToField);		
  		},
  		
  		 /********************
  		  * _getErrorMessage
  		  *
  		  * @param form
  		  * @param field
  		  * @param rule
  		  * @param rules
  		  * @param i
  		  * @param options
  		  * @param originalValidationMethod
  		  * @return {*}
  		  * @private
  		  */
  		 _getErrorMessage:function (form, field, rule, rules, i, options, originalValidationMethod) {
  			 // If we are using the custon validation type, build the index for the rule.
  			 // Otherwise if we are doing a function call, make the call and return the object
  			 // that is passed back.
  	 		 var rule_index = jQuery.inArray(rule, rules);
  			 if (rule === "custom" || rule === "funcCall") {
  				 var custom_validation_type = rules[rule_index + 1];
  				 rule = rule + "[" + custom_validation_type + "]";
  				 // Delete the rule from the rules array so that it doesn't try to call the
  			    // same rule over again
  			    delete(rules[rule_index]);
  			 }
  			 // Change the rule to the composite rule, if it was different from the original
  			 var alteredRule = rule;
  
  
  			 var element_classes = (field.attr("data-validation-engine")) ? field.attr("data-validation-engine") : field.attr("class");
  			 var element_classes_array = element_classes.split(" ");
  
  			 // Call the original validation method. If we are dealing with dates or checkboxes, also pass the form
  			 var errorMsg;
  			 if (rule == "future" || rule == "past"  || rule == "maxCheckbox" || rule == "minCheckbox") {
  				 errorMsg = originalValidationMethod(form, field, rules, i, options);
  			 } else {
  				 errorMsg = originalValidationMethod(field, rules, i, options);
  			 }
  
  			 // If the original validation method returned an error and we have a custom error message,
  			 // return the custom message instead. Otherwise return the original error message.
  			 if (errorMsg != undefined) {
  				 var custom_message = methods._getCustomErrorMessage($(field), element_classes_array, alteredRule, options);
  				 if (custom_message) errorMsg = custom_message;
  			 }
  			 return errorMsg;
  
  		 },
  		 _getCustomErrorMessage:function (field, classes, rule, options) {
  			var custom_message = false;
  			var validityProp = /^custom\[.*\]$/.test(rule) ? methods._validityProp["custom"] : methods._validityProp[rule];
  			 // If there is a validityProp for this rule, check to see if the field has an attribute for it
  			if (validityProp != undefined) {
  				custom_message = field.attr("data-errormessage-"+validityProp);
  				// If there was an error message for it, return the message
  				if (custom_message != undefined) 
  					return custom_message;
  			}
  			custom_message = field.attr("data-errormessage");
  			 // If there is an inline custom error message, return it
  			if (custom_message != undefined) 
  				return custom_message;
  			var id = '#' + field.attr("id");
  			// If we have custom messages for the element's id, get the message for the rule from the id.
  			// Otherwise, if we have custom messages for the element's classes, use the first class message we find instead.
  			if (typeof options.custom_error_messages[id] != "undefined" &&
  				typeof options.custom_error_messages[id][rule] != "undefined" ) {
  						  custom_message = options.custom_error_messages[id][rule]['message'];
  			} else if (classes.length > 0) {
  				for (var i = 0; i < classes.length && classes.length > 0; i++) {
  					 var element_class = "." + classes[i];
  					if (typeof options.custom_error_messages[element_class] != "undefined" &&
  						typeof options.custom_error_messages[element_class][rule] != "undefined") {
  							custom_message = options.custom_error_messages[element_class][rule]['message'];
  							break;
  					}
  				}
  			}
  			if (!custom_message &&
  				typeof options.custom_error_messages[rule] != "undefined" &&
  				typeof options.custom_error_messages[rule]['message'] != "undefined"){
  					 custom_message = options.custom_error_messages[rule]['message'];
  			 }
  			 return custom_message;
  		 },
  		 _validityProp: {
  			 "required": "value-missing",
  			 "custom": "custom-error",
  			 "groupRequired": "value-missing",
  			 "ajax": "custom-error",
  			 "minSize": "range-underflow",
  			 "maxSize": "range-overflow",
  			 "min": "range-underflow",
  			 "max": "range-overflow",
  			 "past": "type-mismatch",
  			 "future": "type-mismatch",
  			 "dateRange": "type-mismatch",
  			 "dateTimeRange": "type-mismatch",
  			 "maxCheckbox": "range-overflow",
  			 "minCheckbox": "range-underflow",
  			 "equals": "pattern-mismatch",
  			 "funcCall": "custom-error",
  			 "creditCard": "pattern-mismatch",
  			 "condRequired": "value-missing"
  		 },
  		/**
  		* Required validation
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @param {bool} condRequired flag when method is used for internal purpose in condRequired check
  		* @return an error string if validation failed
  		*/
  		_required: function(field, rules, i, options, condRequired) {
  			switch (field.prop("type")) {
  				case "text":
  				case "password":
  				case "textarea":
  				case "file":
  				case "select-one":
  				case "select-multiple":
  				default:
  					var field_val      = $.trim( field.val()                               );
  					var dv_placeholder = $.trim( field.attr("data-validation-placeholder") );
  					var placeholder    = $.trim( field.attr("placeholder")                 );
  					if (
  						   ( !field_val                                    )
  						|| ( dv_placeholder && field_val == dv_placeholder )
  						|| ( placeholder    && field_val == placeholder    )
  					) {
  						return options.allrules[rules[i]].alertText;
  					}
  					break;
  				case "radio":
  				case "checkbox":
  					// new validation style to only check dependent field
  					if (condRequired) {
  						if (!field.attr('checked')) {
  							return options.allrules[rules[i]].alertTextCheckboxMultiple;
  						}
  						break;
  					}
  					// old validation style
  					var form = field.closest("form, .validationEngineContainer");
  					var name = field.attr("name");
  					if (form.find("input[name='" + name + "']:checked").size() == 0) {
  						if (form.find("input[name='" + name + "']:visible").size() == 1)
  							return options.allrules[rules[i]].alertTextCheckboxe;
  						else
  							return options.allrules[rules[i]].alertTextCheckboxMultiple;
  					}
  					break;
  			}
  		},
  		/**
  		* Validate that 1 from the group field is required
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_groupRequired: function(field, rules, i, options) {
  			var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]";
  			var isValid = false;
  			// Check all fields from the group
  			field.closest("form, .validationEngineContainer").find(classGroup).each(function(){
  				if(!methods._required($(this), rules, i, options)){
  					isValid = true;
  					return false;
  				}
  			}); 
  
  			if(!isValid) {
  		  return options.allrules[rules[i]].alertText;
  		}
  		},
  		/**
  		* Validate rules
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_custom: function(field, rules, i, options) {
  			var customRule = rules[i + 1];
  			var rule = options.allrules[customRule];
  			var fn;
  			if(!rule) {
  				alert("jqv:custom rule not found - "+customRule);
  				return;
  			}
  			
  			if(rule["regex"]) {
  				 var ex=rule.regex;
  					if(!ex) {
  						alert("jqv:custom regex not found - "+customRule);
  						return;
  					}
  					var pattern = new RegExp(ex);
  
  					if (!pattern.test(field.val())) return options.allrules[customRule].alertText;
  					
  			} else if(rule["func"]) {
  				fn = rule["func"]; 
  				 
  				if (typeof(fn) !== "function") {
  					alert("jqv:custom parameter 'function' is no function - "+customRule);
  						return;
  				}
  				 
  				if (!fn(field, rules, i, options))
  					return options.allrules[customRule].alertText;
  			} else {
  				alert("jqv:custom type not allowed "+customRule);
  					return;
  			}
  		},
  		/**
  		* Validate custom function outside of the engine scope
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_funcCall: function(field, rules, i, options) {
  			var functionName = rules[i + 1];
  			var fn;
  			if(functionName.indexOf('.') >-1)
  			{
  				var namespaces = functionName.split('.');
  				var scope = window;
  				while(namespaces.length)
  				{
  					scope = scope[namespaces.shift()];
  				}
  				fn = scope;
  			}
  			else
  				fn = window[functionName] || options.customFunctions[functionName];
  			if (typeof(fn) == 'function')
  				return fn(field, rules, i, options);
  
  		},
  		/**
  		* Field match
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_equals: function(field, rules, i, options) {
  			var equalsField = rules[i + 1];
  
  			if (field.val() != $("#" + equalsField).val())
  				return options.allrules.equals.alertText;
  		},
  		/**
  		* Check the maximum size (in characters)
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_maxSize: function(field, rules, i, options) {
  			var max = rules[i + 1];
  			var len = field.val().length;
  
  			if (len > max) {
  				var rule = options.allrules.maxSize;
  				return rule.alertText + max + rule.alertText2;
  			}
  		},
  		/**
  		* Check the minimum size (in characters)
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_minSize: function(field, rules, i, options) {
  			var min = rules[i + 1];
  			var len = field.val().length;
  
  			if (len < min) {
  				var rule = options.allrules.minSize;
  				return rule.alertText + min + rule.alertText2;
  			}
  		},
  		/**
  		* Check number minimum value
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_min: function(field, rules, i, options) {
  			var min = parseFloat(rules[i + 1]);
  			var len = parseFloat(field.val());
  
  			if (len < min) {
  				var rule = options.allrules.min;
  				if (rule.alertText2) return rule.alertText + min + rule.alertText2;
  				return rule.alertText + min;
  			}
  		},
  		/**
  		* Check number maximum value
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_max: function(field, rules, i, options) {
  			var max = parseFloat(rules[i + 1]);
  			var len = parseFloat(field.val());
  
  			if (len >max ) {
  				var rule = options.allrules.max;
  				if (rule.alertText2) return rule.alertText + max + rule.alertText2;
  				//orefalo: to review, also do the translations
  				return rule.alertText + max;
  			}
  		},
  		/**
  		* Checks date is in the past
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_past: function(form, field, rules, i, options) {
  
  			var p=rules[i + 1];
  			var fieldAlt = $(form.find("*[name='" + p.replace(/^#+/, '') + "']"));
  			var pdate;
  
  			if (p.toLowerCase() == "now") {
  				pdate = new Date();
  			} else if (undefined != fieldAlt.val()) {
  				if (fieldAlt.is(":disabled"))
  					return;
  				pdate = methods._parseDate(fieldAlt.val());
  			} else {
  				pdate = methods._parseDate(p);
  			}
  			var vdate = methods._parseDate(field.val());
  
  			if (vdate > pdate ) {
  				var rule = options.allrules.past;
  				if (rule.alertText2) return rule.alertText + methods._dateToString(pdate) + rule.alertText2;
  				return rule.alertText + methods._dateToString(pdate);
  			}
  		},
  		/**
  		* Checks date is in the future
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_future: function(form, field, rules, i, options) {
  
  			var p=rules[i + 1];
  			var fieldAlt = $(form.find("*[name='" + p.replace(/^#+/, '') + "']"));
  			var pdate;
  
  			if (p.toLowerCase() == "now") {
  				pdate = new Date();
  			} else if (undefined != fieldAlt.val()) {
  				if (fieldAlt.is(":disabled"))
  					return;
  				pdate = methods._parseDate(fieldAlt.val());
  			} else {
  				pdate = methods._parseDate(p);
  			}
  			var vdate = methods._parseDate(field.val());
  
  			if (vdate < pdate ) {
  				var rule = options.allrules.future;
  				if (rule.alertText2)
  					return rule.alertText + methods._dateToString(pdate) + rule.alertText2;
  				return rule.alertText + methods._dateToString(pdate);
  			}
  		},
  		/**
  		* Checks if valid date
  		*
  		* @param {string} date string
  		* @return a bool based on determination of valid date
  		*/
  		_isDate: function (value) {
  			var dateRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|1\d|2[0-8]))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(0?2(\/|-)29)(\/|-)(?:(?:0[48]00|[13579][26]00|[2468][048]00)|(?:\d\d)?(?:0[48]|[2468][048]|[13579][26]))$/);
  			return dateRegEx.test(value);
  		},
  		/**
  		* Checks if valid date time
  		*
  		* @param {string} date string
  		* @return a bool based on determination of valid date time
  		*/
  		_isDateTime: function (value){
  			var dateTimeRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1}$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^((1[012]|0?[1-9]){1}\/(0?[1-9]|[12][0-9]|3[01]){1}\/\d{2,4}\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1})$/);
  			return dateTimeRegEx.test(value);
  		},
  		//Checks if the start date is before the end date
  		//returns true if end is later than start
  		_dateCompare: function (start, end) {
  			return (new Date(start.toString()) < new Date(end.toString()));
  		},
  		/**
  		* Checks date range
  		*
  		* @param {jqObject} first field name
  		* @param {jqObject} second field name
  		* @return an error string if validation failed
  		*/
  		_dateRange: function (field, rules, i, options) {
  			//are not both populated
  			if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) {
  				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  			}
  
  			//are not both dates
  			if (!methods._isDate(options.firstOfGroup[0].value) || !methods._isDate(options.secondOfGroup[0].value)) {
  				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  			}
  
  			//are both dates but range is off
  			if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) {
  				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  			}
  		},
  		/**
  		* Checks date time range
  		*
  		* @param {jqObject} first field name
  		* @param {jqObject} second field name
  		* @return an error string if validation failed
  		*/
  		_dateTimeRange: function (field, rules, i, options) {
  			//are not both populated
  			if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) {
  				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  			}
  			//are not both dates
  			if (!methods._isDateTime(options.firstOfGroup[0].value) || !methods._isDateTime(options.secondOfGroup[0].value)) {
  				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  			}
  			//are both dates but range is off
  			if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) {
  				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
  			}
  		},
  		/**
  		* Max number of checkbox selected
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_maxCheckbox: function(form, field, rules, i, options) {
  
  			var nbCheck = rules[i + 1];
  			var groupname = field.attr("name");
  			var groupSize = form.find("input[name='" + groupname + "']:checked").size();
  			if (groupSize > nbCheck) {
  				options.showArrow = false;
  				if (options.allrules.maxCheckbox.alertText2)
  					 return options.allrules.maxCheckbox.alertText + " " + nbCheck + " " + options.allrules.maxCheckbox.alertText2;
  				return options.allrules.maxCheckbox.alertText;
  			}
  		},
  		/**
  		* Min number of checkbox selected
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_minCheckbox: function(form, field, rules, i, options) {
  
  			var nbCheck = rules[i + 1];
  			var groupname = field.attr("name");
  			var groupSize = form.find("input[name='" + groupname + "']:checked").size();
  			if (groupSize < nbCheck) {
  				options.showArrow = false;
  				return options.allrules.minCheckbox.alertText + " " + nbCheck + " " + options.allrules.minCheckbox.alertText2;
  			}
  		},
  		/**
  		* Checks that it is a valid credit card number according to the
  		* Luhn checksum algorithm.
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return an error string if validation failed
  		*/
  		_creditCard: function(field, rules, i, options) {
  			//spaces and dashes may be valid characters, but must be stripped to calculate the checksum.
  			var valid = false, cardNumber = field.val().replace(/ +/g, '').replace(/-+/g, '');
  
  			var numDigits = cardNumber.length;
  			if (numDigits >= 14 && numDigits <= 16 && parseInt(cardNumber) > 0) {
  
  				var sum = 0, i = numDigits - 1, pos = 1, digit, luhn = new String();
  				do {
  					digit = parseInt(cardNumber.charAt(i));
  					luhn += (pos++ % 2 == 0) ? digit * 2 : digit;
  				} while (--i >= 0)
  
  				for (i = 0; i < luhn.length; i++) {
  					sum += parseInt(luhn.charAt(i));
  				}
  				valid = sum % 10 == 0;
  			}
  			if (!valid) return options.allrules.creditCard.alertText;
  		},
  		/**
  		* Ajax field validation
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		*            user options
  		* @return nothing! the ajax validator handles the prompts itself
  		*/
  		 _ajax: function(field, rules, i, options) {
  
  			 var errorSelector = rules[i + 1];
  			 var rule = options.allrules[errorSelector];
  			 var extraData = rule.extraData;
  			 var extraDataDynamic = rule.extraDataDynamic;
  			 var data = {
  				"fieldId" : field.attr("id"),
  				"fieldValue" : field.val()
  			 };
  
  			 if (typeof extraData === "object") {
  				$.extend(data, extraData);
  			 } else if (typeof extraData === "string") {
  				var tempData = extraData.split("&");
  				for(var i = 0; i < tempData.length; i++) {
  					var values = tempData[i].split("=");
  					if (values[0] && values[0]) {
  						data[values[0]] = values[1];
  					}
  				}
  			 }
  
  			 if (extraDataDynamic) {
  				 var tmpData = [];
  				 var domIds = String(extraDataDynamic).split(",");
  				 for (var i = 0; i < domIds.length; i++) {
  					 var id = domIds[i];
  					 if ($(id).length) {
  						 var inputValue = field.closest("form, .validationEngineContainer").find(id).val();
  						 var keyValue = id.replace('#', '') + '=' + escape(inputValue);
  						 data[id.replace('#', '')] = inputValue;
  					 }
  				 }
  			 }
  			 
  			 // If a field change event triggered this we want to clear the cache for this ID
  			 if (options.eventTrigger == "field") {
  				delete(options.ajaxValidCache[field.attr("id")]);
  			 }
  
  			 // If there is an error or if the the field is already validated, do not re-execute AJAX
  			 if (!options.isError && !methods._checkAjaxFieldStatus(field.attr("id"), options)) {
  				 $.ajax({
  					 type: options.ajaxFormValidationMethod,
  					 url: rule.url,
  					 cache: false,
  					 dataType: "json",
  					 data: data,
  					 field: field,
  					 rule: rule,
  					 methods: methods,
  					 options: options,
  					 beforeSend: function() {},
  					 error: function(data, transport) {
  						if (options.onFailure) {
  							options.onFailure(data, transport);
  						} else {
  							methods._ajaxError(data, transport);
  						}
  					 },
  					 success: function(json) {
  
  						 // asynchronously called on success, data is the json answer from the server
  						 var errorFieldId = json[0];
  						 //var errorField = $($("#" + errorFieldId)[0]);
  						 var errorField = $("#"+ errorFieldId).eq(0);
  
  						 // make sure we found the element
  						 if (errorField.length == 1) {
  							 var status = json[1];
  							 // read the optional msg from the server
  							 var msg = json[2];
  							 if (!status) {
  								 // Houston we got a problem - display an red prompt
  								 options.ajaxValidCache[errorFieldId] = false;
  								 options.isError = true;
  
  								 // resolve the msg prompt
  								 if(msg) {
  									 if (options.allrules[msg]) {
  										 var txt = options.allrules[msg].alertText;
  										 if (txt) {
  											msg = txt;
  							}
  									 }
  								 }
  								 else
  									msg = rule.alertText;
  
  								 if (options.showPrompts) methods._showPrompt(errorField, msg, "", true, options);
  							 } else {
  								 options.ajaxValidCache[errorFieldId] = true;
  
  								 // resolves the msg prompt
  								 if(msg) {
  									 if (options.allrules[msg]) {
  										 var txt = options.allrules[msg].alertTextOk;
  										 if (txt) {
  											msg = txt;
  							}
  									 }
  								 }
  								 else
  								 msg = rule.alertTextOk;
  
  								 if (options.showPrompts) {
  									 // see if we should display a green prompt
  									 if (msg)
  										methods._showPrompt(errorField, msg, "pass", true, options);
  									 else
  										methods._closePrompt(errorField);
  								}
  								
  								 // If a submit form triggered this, we want to re-submit the form
  								 if (options.eventTrigger == "submit")
  									field.closest("form").submit();
  							 }
  						 }
  						 errorField.trigger("jqv.field.result", [errorField, options.isError, msg]);
  					 }
  				 });
  				 
  				 return rule.alertTextLoad;
  			 }
  		 },
  		/**
  		* Common method to handle ajax errors
  		*
  		* @param {Object} data
  		* @param {Object} transport
  		*/
  		_ajaxError: function(data, transport) {
  			if(data.status == 0 && transport == null)
  				alert("The page is not served from a server! ajax call failed");
  			else if(typeof console != "undefined")
  				console.log("Ajax error: " + data.status + " " + transport);
  		},
  		/**
  		* date -> string
  		*
  		* @param {Object} date
  		*/
  		_dateToString: function(date) {
  			return date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate();
  		},
  		/**
  		* Parses an ISO date
  		* @param {String} d
  		*/
  		_parseDate: function(d) {
  
  			var dateParts = d.split("-");
  			if(dateParts==d)
  				dateParts = d.split("/");
  			if(dateParts==d) {
  				dateParts = d.split(".");
  				return new Date(dateParts[2], (dateParts[1] - 1), dateParts[0]);
  			}
  			return new Date(dateParts[0], (dateParts[1] - 1) ,dateParts[2]);
  		},
  		/**
  		* Builds or updates a prompt with the given information
  		*
  		* @param {jqObject} field
  		* @param {String} promptText html text to display type
  		* @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
  		* @param {boolean} ajaxed - use to mark fields than being validated with ajax
  		* @param {Map} options user options
  		*/
  		 _showPrompt: function(field, promptText, type, ajaxed, options, ajaxform) {
  		 	//Check if we need to adjust what element to show the prompt on
  			if(field.data('jqv-prompt-at') instanceof jQuery ){
  				field = field.data('jqv-prompt-at');
  			} else if(field.data('jqv-prompt-at')) {
  				field = $(field.data('jqv-prompt-at'));
  			}
  
  			 var prompt = methods._getPrompt(field);
  			 // The ajax submit errors are not see has an error in the form,
  			 // When the form errors are returned, the engine see 2 bubbles, but those are ebing closed by the engine at the same time
  			 // Because no error was found befor submitting
  			 if(ajaxform) prompt = false;
  			 // Check that there is indded text
  			 if($.trim(promptText)){ 
  				 if (prompt)
  					methods._updatePrompt(field, prompt, promptText, type, ajaxed, options);
  				 else
  					methods._buildPrompt(field, promptText, type, ajaxed, options);
  			}
  		 },
  		/**
  		* Builds and shades a prompt for the given field.
  		*
  		* @param {jqObject} field
  		* @param {String} promptText html text to display type
  		* @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
  		* @param {boolean} ajaxed - use to mark fields than being validated with ajax
  		* @param {Map} options user options
  		*/
  		_buildPrompt: function(field, promptText, type, ajaxed, options) {
  
  			// create the prompt
  			var prompt = $('<div>');
  			prompt.addClass(methods._getClassName(field.attr("id")) + "formError");
  			// add a class name to identify the parent form of the prompt
  			prompt.addClass("parentForm"+methods._getClassName(field.closest('form, .validationEngineContainer').attr("id")));
  			prompt.addClass("formError");
  
  			switch (type) {
  				case "pass":
  					prompt.addClass("greenPopup");
  					break;
  				case "load":
  					prompt.addClass("blackPopup");
  					break;
  				default:
  					/* it has error  */
  					//alert("unknown popup type:"+type);
  			}
  			if (ajaxed)
  				prompt.addClass("ajaxed");
  
  			// create the prompt content
  			var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt);
  
  			// determine position type
  			var positionType=field.data("promptPosition") || options.promptPosition;
  
  			// create the css arrow pointing at the field
  			// note that there is no triangle on max-checkbox and radio
  			if (options.showArrow) {
  				var arrow = $('<div>').addClass("formErrorArrow");
  
  				//prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10)
  				if (typeof(positionType)=='string') 
  				{
  					var pos=positionType.indexOf(":");
  					if(pos!=-1)
  						positionType=positionType.substring(0,pos);
  				}
  
  				switch (positionType) {
  					case "bottomLeft":
  					case "bottomRight":
  						prompt.find(".formErrorContent").before(arrow);
  						arrow.addClass("formErrorArrowBottom").html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>');
  						break;
  					case "topLeft":
  					case "topRight":
  						arrow.html('<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>');
  						prompt.append(arrow);
  						break;
  				}
  			}
  			// Add custom prompt class
  			if (options.addPromptClass)
  				prompt.addClass(options.addPromptClass);
  
              // Add custom prompt class defined in element
              var requiredOverride = field.attr('data-required-class');
              if(requiredOverride !== undefined) {
                  prompt.addClass(requiredOverride);
              } else {
                  if(options.prettySelect) {
                      if($('#' + field.attr('id')).next().is('select')) {
                          var prettyOverrideClass = $('#' + field.attr('id').substr(options.usePrefix.length).substring(options.useSuffix.length)).attr('data-required-class');
                          if(prettyOverrideClass !== undefined) {
                              prompt.addClass(prettyOverrideClass);
                          }
                      }
                  }
              }
  
  			prompt.css({
  				"opacity": 0
  			});
  			if(positionType === 'inline') {
  				prompt.addClass("inline");
  				if(typeof field.attr('data-prompt-target') !== 'undefined' && $('#'+field.attr('data-prompt-target')).length > 0) {
  					prompt.appendTo($('#'+field.attr('data-prompt-target')));
  				} else {
  					field.after(prompt);
  				}
  			} else {
  				field.before(prompt);				
  			}
  			
  			var pos = methods._calculatePosition(field, prompt, options);
  			prompt.css({
  				'position': positionType === 'inline' ? 'relative' : 'absolute',
  				"top": pos.callerTopPosition,
  				"left": pos.callerleftPosition,
  				"marginTop": pos.marginTopSize,
  				"opacity": 0
  			}).data("callerField", field);
  			
  
  			if (options.autoHidePrompt) {
  				setTimeout(function(){
  					prompt.animate({
  						"opacity": 0
  					},function(){
  						prompt.closest('.formErrorOuter').remove();
  						prompt.remove();
  					});
  				}, options.autoHideDelay);
  			} 
  			return prompt.animate({
  				"opacity": 0.87
  			});
  		},
  		/**
  		* Updates the prompt text field - the field for which the prompt
  		* @param {jqObject} field
  		* @param {String} promptText html text to display type
  		* @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
  		* @param {boolean} ajaxed - use to mark fields than being validated with ajax
  		* @param {Map} options user options
  		*/
  		_updatePrompt: function(field, prompt, promptText, type, ajaxed, options, noAnimation) {
  
  			if (prompt) {
  				if (typeof type !== "undefined") {
  					if (type == "pass")
  						prompt.addClass("greenPopup");
  					else
  						prompt.removeClass("greenPopup");
  
  					if (type == "load")
  						prompt.addClass("blackPopup");
  					else
  						prompt.removeClass("blackPopup");
  				}
  				if (ajaxed)
  					prompt.addClass("ajaxed");
  				else
  					prompt.removeClass("ajaxed");
  
  				prompt.find(".formErrorContent").html(promptText);
  
  				var pos = methods._calculatePosition(field, prompt, options);
  				var css = {"top": pos.callerTopPosition,
  				"left": pos.callerleftPosition, 
  				"marginTop": pos.marginTopSize};
  
  				if (noAnimation)
  					prompt.css(css);
  				else
  					prompt.animate(css);
  			}
  		},
  		/**
  		* Closes the prompt associated with the given field
  		*
  		* @param {jqObject}
  		*            field
  		*/
  		 _closePrompt: function(field) {
  			 var prompt = methods._getPrompt(field);
  			 if (prompt)
  				 prompt.fadeTo("fast", 0, function() {
  					 prompt.parent('.formErrorOuter').remove();
  					 prompt.remove();
  				 });
  		 },
  		 closePrompt: function(field) {
  			 return methods._closePrompt(field);
  		 },
  		/**
  		* Returns the error prompt matching the field if any
  		*
  		* @param {jqObject}
  		*            field
  		* @return undefined or the error prompt (jqObject)
  		*/
  		_getPrompt: function(field) {
  				var formId = $(field).closest('form, .validationEngineContainer').attr('id');
  			var className = methods._getClassName(field.attr("id")) + "formError";
  				var match = $("." + methods._escapeExpression(className) + '.parentForm' + methods._getClassName(formId))[0];
  			if (match)
  			return $(match);
  		},
  		/**
  		  * Returns the escapade classname
  		  *
  		  * @param {selector}
  		  *            className
  		  */
  		  _escapeExpression: function (selector) {
  			  return selector.replace(/([#;&,\.\+\*\~':"\!\^$\[\]\(\)=>\|])/g, "\\$1");
  		  },
  		/**
  		 * returns true if we are in a RTLed document
  		 *
  		 * @param {jqObject} field
  		 */
  		isRTL: function(field)
  		{
  			var $document = $(document);
  			var $body = $('body');
  			var rtl =
  				(field && field.hasClass('rtl')) ||
  				(field && (field.attr('dir') || '').toLowerCase()==='rtl') ||
  				$document.hasClass('rtl') ||
  				($document.attr('dir') || '').toLowerCase()==='rtl' ||
  				$body.hasClass('rtl') ||
  				($body.attr('dir') || '').toLowerCase()==='rtl';
  			return Boolean(rtl);
  		},
  		/**
  		* Calculates prompt position
  		*
  		* @param {jqObject}
  		*            field
  		* @param {jqObject}
  		*            the prompt
  		* @param {Map}
  		*            options
  		* @return positions
  		*/
  		_calculatePosition: function (field, promptElmt, options) {
  
  			var promptTopPosition, promptleftPosition, marginTopSize;
  			var fieldWidth 	= field.width();
  			var fieldLeft 	= field.position().left; 
  			var fieldTop 	=  field.position().top;
  			var fieldHeight 	=  field.height();	
  			var promptHeight = promptElmt.height();
  
  
  			// is the form contained in an overflown container?
  			promptTopPosition = promptleftPosition = 0;
  			// compensation for the arrow
  			marginTopSize = -promptHeight;
  		
  
  			//prompt positioning adjustment support
  			//now you can adjust prompt position
  			//usage: positionType:Xshift,Yshift
  			//for example:
  			//   bottomLeft:+20 means bottomLeft position shifted by 20 pixels right horizontally
  			//   topRight:20, -15 means topRight position shifted by 20 pixels to right and 15 pixels to top
  			//You can use +pixels, - pixels. If no sign is provided than + is default.
  			var positionType=field.data("promptPosition") || options.promptPosition;
  			var shift1="";
  			var shift2="";
  			var shiftX=0;
  			var shiftY=0;
  			if (typeof(positionType)=='string') {
  				//do we have any position adjustments ?
  				if (positionType.indexOf(":")!=-1) {
  					shift1=positionType.substring(positionType.indexOf(":")+1);
  					positionType=positionType.substring(0,positionType.indexOf(":"));
  
  					//if any advanced positioning will be needed (percents or something else) - parser should be added here
  					//for now we use simple parseInt()
  
  					//do we have second parameter?
  					if (shift1.indexOf(",") !=-1) {
  						shift2=shift1.substring(shift1.indexOf(",") +1);
  						shift1=shift1.substring(0,shift1.indexOf(","));
  						shiftY=parseInt(shift2);
  						if (isNaN(shiftY)) shiftY=0;
  					};
  
  					shiftX=parseInt(shift1);
  					if (isNaN(shift1)) shift1=0;
  
  				};
  			};
  
  			
  			switch (positionType) {
  				default:
  				case "topRight":
  					promptleftPosition +=  fieldLeft + fieldWidth - 30;
  					promptTopPosition +=  fieldTop;
  					break;
  
  				case "topLeft":
  					promptTopPosition +=  fieldTop;
  					promptleftPosition += fieldLeft; 
  					break;
  
  				case "centerRight":
  					promptTopPosition = fieldTop+4;
  					marginTopSize = 0;
  					promptleftPosition= fieldLeft + field.outerWidth(true)+5;
  					break;
  				case "centerLeft":
  					promptleftPosition = fieldLeft - (promptElmt.width() + 2);
  					promptTopPosition = fieldTop+4;
  					marginTopSize = 0;
  					
  					break;
  
  				case "bottomLeft":
  					promptTopPosition = fieldTop + field.height() + 5;
  					marginTopSize = 0;
  					promptleftPosition = fieldLeft;
  					break;
  				case "bottomRight":
  					promptleftPosition = fieldLeft + fieldWidth - 30;
  					promptTopPosition =  fieldTop +  field.height() + 5;
  					marginTopSize = 0;
  					break;
  				case "inline":
  					promptleftPosition = 0;
  					promptTopPosition = 0;
  					marginTopSize = 0;
  			};
  
  		
  
  			//apply adjusments if any
  			promptleftPosition += shiftX;
  			promptTopPosition  += shiftY;
  
  			return {
  				"callerTopPosition": promptTopPosition + "px",
  				"callerleftPosition": promptleftPosition + "px",
  				"marginTopSize": marginTopSize + "px"
  			};
  		},
  		/**
  		* Saves the user options and variables in the form.data
  		*
  		* @param {jqObject}
  		*            form - the form where the user option should be saved
  		* @param {Map}
  		*            options - the user options
  		* @return the user options (extended from the defaults)
  		*/
  		 _saveOptions: function(form, options) {
  
  			 // is there a language localisation ?
  			 if ($.validationEngineLanguage)
  			 var allRules = $.validationEngineLanguage.allRules;
  			 else
  			 $.error("jQuery.validationEngine rules are not loaded, plz add localization files to the page");
  			 // --- Internals DO NOT TOUCH or OVERLOAD ---
  			 // validation rules and i18
  			 $.validationEngine.defaults.allrules = allRules;
  
  			 var userOptions = $.extend(true,{},$.validationEngine.defaults,options);
  
  			 form.data('jqv', userOptions);
  			 return userOptions;
  		 },
  
  		 /**
  		 * Removes forbidden characters from class name
  		 * @param {String} className
  		 */
  		 _getClassName: function(className) {
  			 if(className)
  				 return className.replace(/:/g, "_").replace(/\./g, "_");
  					  },
  		/**
  		 * Escape special character for jQuery selector
  		 * http://totaldev.com/content/escaping-characters-get-valid-jquery-id
  		 * @param {String} selector
  		 */
  		 _jqSelector: function(str){
  			return str.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
  		},
  		/**
  		* Conditionally required field
  		*
  		* @param {jqObject} field
  		* @param {Array[String]} rules
  		* @param {int} i rules index
  		* @param {Map}
  		* user options
  		* @return an error string if validation failed
  		*/
  		_condRequired: function(field, rules, i, options) {
  			var idx, dependingField;
  
  			for(idx = (i + 1); idx < rules.length; idx++) {
  				dependingField = jQuery("#" + rules[idx]).first();
  
  				/* Use _required for determining wether dependingField has a value.
  				 * There is logic there for handling all field types, and default value; so we won't replicate that here
  				 * Indicate this special use by setting the last parameter to true so we only validate the dependingField on chackboxes and radio buttons (#462)
  				 */
  				if (dependingField.length && methods._required(dependingField, ["required"], 0, options, true) == undefined) {
  					/* We now know any of the depending fields has a value,
  					 * so we can validate this field as per normal required code
  					 */
  					return methods._required(field, ["required"], 0, options);
  				}
  			}
  		},
  
  	    _submitButtonClick: function(event) {
  	        var button = $(this);
  	        var form = button.closest('form, .validationEngineContainer');
  	        form.data("jqv_submitButton", button.attr("id"));
  	    }
  		  };
  
  	 /**
  	 * Plugin entry point.
  	 * You may pass an action as a parameter or a list of options.
  	 * if none, the init and attach methods are being called.
  	 * Remember: if you pass options, the attached method is NOT called automatically
  	 *
  	 * @param {String}
  	 *            method (optional) action
  	 */
  	 $.fn.validationEngine = function(method) {
  
  		 var form = $(this);
  		 if(!form[0]) return form;  // stop here if the form does not exist
  
  		 if (typeof(method) == 'string' && method.charAt(0) != '_' && methods[method]) {
  
  			 // make sure init is called once
  			 if(method != "showPrompt" && method != "hide" && method != "hideAll")
  			 methods.init.apply(form);
  
  			 return methods[method].apply(form, Array.prototype.slice.call(arguments, 1));
  		 } else if (typeof method == 'object' || !method) {
  
  			 // default constructor with or without arguments
  			 methods.init.apply(form, arguments);
  			 return methods.attach.apply(form);
  		 } else {
  			 $.error('Method ' + method + ' does not exist in jQuery.validationEngine');
  		 }
  	};
  
  
  
  	// LEAK GLOBAL OPTIONS
  	$.validationEngine= {fieldIdCounter: 0,defaults:{
  
  		// Name of the event triggering field validation
  		validationEventTrigger: "blur",
  		// Automatically scroll viewport to the first error
  		scroll: true,
  		// Focus on the first input
  		focusFirstField:true,
  		// Show prompts, set to false to disable prompts
  		showPrompts: true,
         // Should we attempt to validate non-visible input fields contained in the form? (Useful in cases of tabbed containers, e.g. jQuery-UI tabs)
         validateNonVisibleFields: false,
  		// Opening box position, possible locations are: topLeft,
  		// topRight, bottomLeft, centerRight, bottomRight, inline
  		// inline gets inserted after the validated field or into an element specified in data-prompt-target
  		promptPosition: "topRight",
  		bindMethod:"bind",
  		// internal, automatically set to true when it parse a _ajax rule
  		inlineAjax: false,
  		// if set to true, the form data is sent asynchronously via ajax to the form.action url (get)
  		ajaxFormValidation: false,
  		// The url to send the submit ajax validation (default to action)
  		ajaxFormValidationURL: false,
  		// HTTP method used for ajax validation
  		ajaxFormValidationMethod: 'get',
  		// Ajax form validation callback method: boolean onComplete(form, status, errors, options)
  		// retuns false if the form.submit event needs to be canceled.
  		onAjaxFormComplete: $.noop,
  		// called right before the ajax call, may return false to cancel
  		onBeforeAjaxFormValidation: $.noop,
  		// Stops form from submitting and execute function assiciated with it
  		onValidationComplete: false,
  
  		// Used when you have a form fields too close and the errors messages are on top of other disturbing viewing messages
  		doNotShowAllErrosOnSubmit: false,
  		// Object where you store custom messages to override the default error messages
  		custom_error_messages:{},
  		// true if you want to vind the input fields
  		binded: true,
  		// set to true, when the prompt arrow needs to be displayed
  		showArrow: true,
  		// did one of the validation fail ? kept global to stop further ajax validations
  		isError: false,
  		// Limit how many displayed errors a field can have
  		maxErrorsPerField: false,
  		
  		// Caches field validation status, typically only bad status are created.
  		// the array is used during ajax form validation to detect issues early and prevent an expensive submit
  		ajaxValidCache: {},
  		// Auto update prompt position after window resize
  		autoPositionUpdate: false,
  
  		InvalidFields: [],
  		onFieldSuccess: false,
  		onFieldFailure: false,
  		onSuccess: false,
  		onFailure: false,
  		validateAttribute: "class",
  		addSuccessCssClassToField: "",
  		addFailureCssClassToField: "",
  		
  		// Auto-hide prompt
  		autoHidePrompt: false,
  		// Delay before auto-hide
  		autoHideDelay: 10000,
  		// Fade out duration while hiding the validations
  		fadeDuration: 0.3,
  	 // Use Prettify select library
  	 prettySelect: false,
  	 // Add css class on prompt
  	 addPromptClass : "",
  	 // Custom ID uses prefix
  	 usePrefix: "",
  	 // Custom ID uses suffix
  	 useSuffix: "",
  	 // Only show one message per error prompt
  	 showOneMessage: false
  	}};
  	$(function(){$.validationEngine.defaults.promptPosition = methods.isRTL()?'topLeft':"topRight"});
  })(jQuery);
  
  
  