text: /*
   * Inline Form Validation Engine 2.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($) {
  
      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 ) {
                  methods._saveOptions(form, options);
  
                  // bind all formError elements to close on click
                  $(".formError").live("click", function() {
                      $(this).fadeOut(150, function() {
  
                          // remove prompt once invisible
                          $(this).remove();
                      });
                  });
              }
          },
          /**
           * 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');
  
  			var validateAttribute = (form.find("[data-validation-engine*=validate]")) ? "data-validation-engine" : "class";
  			
              if (!options.binded) {
  					if (options.bindMethod == "bind"){
  						
  						// bind fields
                          form.find("[class*=validate]:not([type=checkbox])").not("[type=radio]").not(".datepicker").bind(options.validationEventTrigger, methods._onFieldEvent);
                          form.find("[class*=validate][type=checkbox],[class*=validate][type=radio]").bind("click", methods._onFieldEvent);
  						
  						form.find("[class*=validate][class*=datepicker]").bind(options.validationEventTrigger,{"delay": 300}, methods._onFieldEvent);
  
                          // bind form.submit
                          form.bind("submit", methods._onSubmitEvent);
  					} else if (options.bindMethod == "live") {
                          // bind fields with LIVE (for persistant state)
                          form.find("[class*=validate]:not([type=checkbox])").not(".datepicker").live(options.validationEventTrigger, methods._onFieldEvent);
                          form.find("[class*=validate][type=checkbox]").live("click", methods._onFieldEvent);
  
  						form.find("[class*=validate][class*=datepicker]").live(options.validationEventTrigger,{"delay": 300}, methods._onFieldEvent);
  
                          // bind form.submit
                          form.live("submit", methods._onSubmitEvent);
  					}
  
                  options.binded = true;
              }
  
          },
          /**
           * Unregisters any bindings that may point to jQuery.validaitonEngine
           */
          detach: function() {
              var form = this;
              var options = form.data('jqv');
              if (options.binded) {
  
                  // unbind fields
                  form.find("[class*=validate]").not("[type=checkbox]").unbind(options.validationEventTrigger, methods._onFieldEvent);
                  form.find("[class*=validate][type=checkbox],[class*=validate][type=radio]").unbind("click", methods._onFieldEvent);
  
                  // unbind form.submit
                  form.unbind("submit", methods.onAjaxFormComplete);
                  
                 
                  // unbind live fields (kill)
                  form.find("[class*=validate]").not("[type=checkbox]").die(options.validationEventTrigger, methods._onFieldEvent);
                  form.find("[class*=validate][type=checkbox]").die("click", methods._onFieldEvent);
                  // unbind form.submit
  
  				
  
  
                  form.die("submit", methods.onAjaxFormComplete);
                  
                  form.removeData('jqv');
              }
          },
          /**
           * Validates the form 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() {
              return methods._validateFields(this);
          },
          /**
           * Validates one field, shows prompt 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
           */
          validateField: function(el) {
              var options = $(this).data('jqv');
              return methods._validateField($(el), options);
          },
          /**
           * Validates the form fields, shows prompts accordingly.
           * Note: this methods performs fields and form ajax validations(if setup)
           *
           * @return true if the form validates, false if it fails, undefined if ajax is used for form validation
           */
          validateform: function() {
              return methods._onSubmitEvent.call(this);
          },
  		/**
           *  Redraw prompts position, useful when you change the DOM state when validating
           */
          updatePromptsPosition: function() {
  			var form = this.closest('form');
              var options = form.data('jqv');
              // No option, take default one
  			form.find('[class*=validate]').not(':hidden').not(":disabled").each(function(){
  				var field = $(this);
  
  				var prompt = methods._getPrompt(field);
  				var promptText = $(prompt).find(".formErrorContent").html();
  
  				if(prompt) methods._updatePrompt(field, $(prompt), promptText, undefined, false, options);
  			})
          },
          /**
           * 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');
              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);
          },
          /**
           * Closes all error prompts on the page
           */
          hidePrompt: function() {
          	var promptClass =  "."+ methods._getClassName($(this).attr("id")) + "formError";
              $(promptClass).fadeTo("fast", 0.3, function() {
                  $(this).remove();
              });
          },
          /**
           * Closes form error prompts, CAN be invidual
           */
          hide: function() {
  			var closingtag;
          	if($(this).is("form")){
          		closingtag = "parentForm"+$(this).attr('id');
          	}else{
          		closingtag = $(this).attr('id') +"formError";
          	}
              $('.'+closingtag).fadeTo("fast", 0.3, function() {
                  $(this).remove();
              });
          },
          /**
           * Closes all error prompts on the page
           */
          hideAll: function() {
              $('.formError').fadeTo("fast", 0.3, function() {
                  $(this).remove();
              });
          },
          /**
           * 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');
              var options = form.data('jqv');
              // validate the current field
  			window.setTimeout(function() {
  			    methods._validateField(field, options);
  			}, (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');
     
  			// validate each field (- skip field ajax validation, no necessary since we will perform an ajax form validation)
              var r=methods._validateFields(form, true);
  		
              if (r && options.ajaxFormValidation) {
                  methods._validateFormWithAjax(form, options);
                  return false;
              }
  
              if(options.onValidationComplete) {
                  options.onValidationComplete(form, r);
                  return false;
              }
              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;
          },
          /**
           * 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, skipAjaxValidation) {
              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
              form.find('[class*=validate]').not(':hidden').not(":disabled").each( function() {
                  var field = $(this);
                  errorFound |= methods._validateField(field, options, skipAjaxValidation);
              });
              // second, check to see if all ajax calls completed ok
              // errorFound |= !methods._checkAjaxStatus(options);
  			
              // thrird, check status and scroll the container accordingly
  			form.trigger("jqv.form.result", [errorFound]);
  			
              if (errorFound) {
  				
                  if (options.scroll) {
  
                      // 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;
  
                      // look for the visually top prompt
                      var destination = Number.MAX_VALUE;
                      var fixleft = 0;
                      var lst = $(".formError:not('.greenPopup')");
  
                      for (var i = 0; i < lst.length; i++) {
                          var d = $(lst[i]).offset().top;
                          if (d < destination){
                              destination = d;
                              fixleft = $(lst[i]).offset().left;
                          }
                      }
  
                      if (!options.isOverflown)
                          $("html:not(:animated),body:not(:animated)").animate({
                              scrollTop: destination,
                              scrollLeft: fixleft
                          }, 1100);
                      else {
                          var overflowDIV = $(options.overflownDIV);
                          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);
  
                          $("html:not(:animated),body:not(:animated)").animate({
                              scrollTop: overflowDIV.offset().top,
                              scrollLeft: fixleft
                          }, 1100);
                      }
                  }
                  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 url = (options.ajaxFormValidationURL) ? options.ajaxFormValidationURL : form.attr("action");
              $.ajax({
                  type: "GET",
                  url: url,
                  cache: false,
                  dataType: "json",
                  data: data,
                  form: form,
                  methods: methods,
                  options: options,
                  beforeSend: function() {
                      return options.onBeforeAjaxFormValidation(form, options);
                  },
                  error: function(data, transport) {
                      methods._ajaxError(data, transport);
                  },
                  success: function(json) {
  
                      if (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;
                                          }
                                          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;
                                      }
                                      methods._showPrompt(errorField, msg, "", false, options, true);
                                  }
                              }
                          }
                          options.onAjaxFormComplete(!errorInForm, form, json, options);
                      } else
                          options.onAjaxFormComplete(true, form, "", options);
                  }
              });
  
          },
          /**
           * Validates field, shows prompts accordingly
           *
           * @param {jqObject}
           *            field
           * @param {Array[String]}
           *            field's validation rules
           * @param {Map}
           *            user options
           * @return true if field is valid
           */
          _validateField: function(field, options, skipAjaxValidation) {
              if (!field.attr("id"))
                  $.error("jQueryValidate: an ID attribute is required for this field: " + field.attr("name") + " class:" +
                  field.attr("class"));
  
              var rulesParsing = field.attr('class');
              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 required = false;
              options.isError = false;
              options.showArrow = true;
  
              for (var i = 0; i < rules.length; i++) {
  
                  var errorMsg = undefined;
                  switch (rules[i]) {
  
                      case "required":
                          required = true;
                          errorMsg = methods._required(field, rules, i, options);
                          break;
                      case "custom":
                          errorMsg = methods._customRegex(field, rules, i, options);
                          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 = "[class*=" +rules[i + 1] +"]";	
  						var firstOfGroup = field.closest("form").find(classGroup).eq(0);
  						if(firstOfGroup[0] != field[0]){
  							methods._validateField(firstOfGroup, options, skipAjaxValidation)
  							options.showArrow = true;
  							continue;
  						};
                          errorMsg = methods._groupRequired(field, rules, i, options);
  						if(errorMsg) required = true;
  						options.showArrow = false;
                          break;
                      case "ajax":
                          // ajax has its own prompts handling technique
  						if(!skipAjaxValidation){
  							methods._ajax(field, rules, i, options);
  	                        isAjaxValidator = true;
  						}
                          break;
                      case "minSize":
                          errorMsg = methods._minSize(field, rules, i, options);
                          break;
                      case "maxSize":
                          errorMsg = methods._maxSize(field, rules, i, options);
                          break;
                      case "min":
                          errorMsg = methods._min(field, rules, i, options);
                          break;
                      case "max":
                          errorMsg = methods._max(field, rules, i, options);
                          break;
                      case "past":
                          errorMsg = methods._past(field, rules, i, options);
                          break;
                      case "future":
                          errorMsg = methods._future(field, rules, i, options);
                          break;
                      case "dateRange":
                          errorMsg = methods._dateRange(field, rules, i, options);
                          field = $($("input[name='" + fieldName + "']"));
                          break;
                      case "dateTimeRange":
                          errorMsg = methods._dateTimeRange(field, rules, i, options);
                          field = $($("input[name='" + fieldName + "']"));
                          break;
                      case "maxCheckbox":
                          errorMsg = methods._maxCheckbox(field, rules, i, options);
                          field = $($("input[name='" + fieldName + "']"));
                          break;
                      case "minCheckbox":
                          errorMsg = methods._minCheckbox(field, rules, i, options);
                          field = $($("input[name='" + fieldName + "']"));
                          break;
                      case "equals":
                          errorMsg = methods._equals(field, rules, i, options);
                          break;
                      case "funcCall":
                          errorMsg = methods._funcCall(field, rules, i, options);
                          break;
  
                      default:
                      //$.error("jQueryValidator rule not found"+rules[i]);
                  }
                  if (errorMsg !== undefined) {
                      promptText += errorMsg + "<br/>";
                      options.isError = true;
  					
                  }
  
              }
              // If the rules required is not added, an empty field is not validated
              if(!required){
              	if(field.val() == "") options.isError = false;
              }			
  
              // Hack for radio/checkbox group button, the validation go into the
              // first radio/checkbox of the group
              var fieldType = field.attr("type");
  
              if ((fieldType == "radio" || fieldType == "checkbox") && $("input[name='" + fieldName + "']").size() > 1) {
                  field = $($("input[name='" + fieldName + "'][type!=hidden]:first"));
                  options.showArrow = false;
              }
              if (fieldType == "text" && $("input[name='" + fieldName + "']").size() > 1) {
                  field = $($("input[name='" + fieldName + "'][type!=hidden]:first"));
                  options.showArrow = false;
              }
  
              if (options.isError){
  				
                  methods._showPrompt(field, promptText, "", false, options);
              }else{
  				if (!isAjaxValidator) methods._closePrompt(field);
  			}
  			field.trigger("jqv.field.result", [field, options.isError, promptText]);
              return options.isError;
          },
          /**
           * Required validation
           *
           * @param {jqObject} field
           * @param {Array[String]} rules
           * @param {int} i rules index
           * @param {Map}
           *            user options
           * @return an error string if validation failed
           */
          _required: function(field, rules, i, options) {
              switch (field.attr("type")) {
                  case "text":
                  case "password":
                  case "textarea":
                  case "file":
                  default:
                      if (!field.val())
                          return options.allrules[rules[i]].alertText;
                      break;
                  case "radio":
                  case "checkbox":
                      var name = field.attr("name");
                      if ($("input[name='" + name + "']:checked").size() == 0) {
                          if ($("input[name='" + name + "']").size() == 1)
                              return options.allrules[rules[i]].alertTextCheckboxe;
                          else
                              return options.allrules[rules[i]].alertTextCheckboxMultiple;
                      }
                      break;
                  case "dateTimeRange":
                  case "dateRange":
                      var name = field.attr("name");
                      var dateRangeFields = $("input[name='" + name + "']");
                      if (!dateRangeFields[0].val() || !dateRangeFields[1].val())
                          return options.allrules[rules[i]].alertTextDateRange;
                      break;
                  // required for <select>
                  case "select-one":
                      // added by paul@kinetek.net for select boxes, Thank you
                      if (!field.val())
                          return options.allrules[rules[i]].alertText;
                      break;
                  case "select-multiple":
                      // added by paul@kinetek.net for select boxes, Thank you
                      if (!field.find("option:selected").val())
                          return options.allrules[rules[i]].alertText;
                      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 = "[class*=" +rules[i + 1] +"]";
  			var isValid = false;
  			// Check all fields from the group
  			field.closest("form").find(classGroup).each(function(){
  				if(!methods._required($(this), rules, i, options)){
  					isValid = true;
  					return false;
  				}
  			})
  			
  			if(!isValid) return options.allrules[rules[i]].alertText;
          },
          /**
           * Validate Regex rules
           *
           * @param {jqObject} field
           * @param {Array[String]} rules
           * @param {int} i rules index
           * @param {Map}
           *            user options
           * @return an error string if validation failed
           */
          _customRegex: function(field, rules, i, options) {
              var customRule = rules[i + 1];
  			var rule = options.allrules[customRule];
  			if(!rule) {
  				alert("jqv:custom rule not found "+customRule);
  				return;
  			}
  			
  			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;
          },
          /**
           * 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 = window[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(field, rules, i, options) {
  
              var p=rules[i + 1];
              var pdate = (p.toLowerCase() == "now")? new Date():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(field, rules, i, options) {
  
              var p=rules[i + 1];
              var pdate = (p.toLowerCase() == "now")? new Date():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 date range
          *
          * @param {jqObject} field name
          * @param {Array[String]} rules
          * @param {int} i rules index
          * @param {Map}
          *            user options
          * @return an error string if validation failed
          */
          _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]))$/);
              if (dateRegEx.test(value)) {
                  return true;
              }
              return false;
          },
          _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})$/);
              if (dateTimeRegEx.test(value)) {
                  return true;
              }
              return false;
          },
          //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()));
          },
          _dateRange: function (field, rules, i, options) {
              var name = field.attr("name");
              //if there are 2 fields to compare
              if ($("input[name='" + name + "']").length == 2) {
                  var inDate1 = $("input[name='" + name + "']")[0].value;
                  var inDate2 = $("input[name='" + name + "']")[1].value;
                  if (methods._isDate(inDate1) && methods._isDate(inDate2)) {
                      if (!methods._dateCompare(inDate1, inDate2)) {
                          return "* Invalid Date Range";
                      }
                  } else {
                      return "* Invalid Date Range";
                  }
              } else {
                  return "* Invalid Date Range";
              }
          },
          _dateTimeRange: function (field, rules, i, options) {
              var name = field.attr("name");
              //if there are 2 fields to compare
              if ($("input[name='" + name + "']").length == 2) {
                  var inDate1 = $("input[name='" + name + "']")[0].value;
                  var inDate2 = $("input[name='" + name + "']")[1].value;
                  if (methods._isDateTime(inDate1) && methods._isDateTime(inDate2)) {
                      if (!methods._dateCompare(inDate1, inDate2)) {
                          return "* Invalid Date Time Range";
                      }
                  } else {
                      return "* Invalid Date Time Range";
                  }
              } else {
                  return "* Invalid Date Time Range";
              }
          },
          /**
           * 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(field, rules, i, options) {
  
              var nbCheck = rules[i + 1];
              var groupname = field.attr("name");
              var groupSize = $("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(field, rules, i, options) {
  
              var nbCheck = rules[i + 1];
              var groupname = field.attr("name");
              var groupSize = $("input[name='" + groupname + "']:checked").size();
              if (groupSize < nbCheck) {
                  options.showArrow = false;
                  return options.allrules.minCheckbox.alertText + " " + nbCheck + " " +
                  options.allrules.minCheckbox.alertText2;
              }
          },
          /**
           * 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;
  
              if (!extraData)
                  extraData = "";
  
              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").find(id).val();
                    var keyValue = id.replace('#', '') + '=' + escape(inputValue);
                    tmpData.push(keyValue);
                  }
                }
                extraDataDynamic = tmpData.join("&");
              } else {
                extraDataDynamic = "";              
              }
                                  
              if (!options.isError) {
                  $.ajax({
                      type: "GET",
                      url: rule.url,
                      cache: false,
                      dataType: "json",
                      data: "fieldId=" + field.attr("id") + "&fieldValue=" + field.val() + "&extraData=" + extraData + "&" + extraDataDynamic,
                      field: field,
                      rule: rule,
                      methods: methods,
                      options: options,
                      beforeSend: function() {
                          // build the loading prompt
                          var loadingText = rule.alertTextLoad;
                          if (loadingText)
                              methods._showPrompt(field, loadingText, "load", true, options);
                      },
                      error: function(data, transport) {
                          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]);
                          // 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;
                                  
  								methods._showPrompt(errorField, msg, "", true, options);
                              } else {
                                  if (options.ajaxValidCache[errorFieldId] !== undefined)
                                      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;                                
  
  								// see if we should display a green prompt
                                  if (msg)
                                      methods._showPrompt(errorField, msg, "pass", true, options);
                                  else
                                      methods._closePrompt(errorField);
                              }
                          }
                      }
                  });
              }
          },
          /**
           * 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("/");
              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) {
              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;
              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
              if(field.is(":input")) prompt.addClass("parentForm"+methods._getClassName(field.parents('form').attr("id")));
              prompt.addClass("formError");
  
              switch (type) {
                  case "pass":
                      prompt.addClass("greenPopup");
                      break;
                  case "load":
                      prompt.addClass("blackPopup");
              }
              if (ajaxed)
                  prompt.addClass("ajaxed");
  
              // create the prompt content
              var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt);
              // 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");
  
                  switch (options.promptPosition) {
                      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;
                  }
              }
  
              //Cedric: Needed if a container is in position:relative
              // insert prompt in the form or in the overflown container?
              if (options.isOverflown)
              	field.before(prompt);
              else
                 $("body").append(prompt);
  
              var pos = methods._calculatePosition(field, prompt, options);
              prompt.css({
                  "top": pos.callerTopPosition,
                  "left": pos.callerleftPosition,
                  "marginTop": pos.marginTopSize,
                  "opacity": 0
              });
  
              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) {
  			
              if (prompt) {
                  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);
                  prompt.animate({
                      "top": pos.callerTopPosition,
                      "marginTop": pos.marginTopSize
                  });
              }
          },
          /**
           * 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.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 className = field.attr("id").replace(":","_") + "formError";
  		    var match = $("." + methods._escapeExpression(className))[0];
  		    if (match)
  		      return $(match);
  		  },
           /**
           * Returns the escapade classname
           *
           * @param {selector}
           *            className
           */		
  		  _escapeExpression: function (selector) {
  		    return selector.replace(/([#;&,\.\+\*\~':"\!\^$\[\]\(\)=>\|])/g, "\\$1");
  		  },
          /**
           * 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 promptHeight = promptElmt.height();
  
              var overflow = options.isOverflown;
              if (overflow) {
                  // is the form contained in an overflown container?
                  promptTopPosition = promptleftPosition = 0;
                  // compensation for the arrow
                  marginTopSize = -promptHeight;
              } else {
                  var offset = field.offset();
                  promptTopPosition = offset.top;
                  promptleftPosition = offset.left;
                  marginTopSize = 0;
              }
  
              switch (options.promptPosition) {
  
                  default:
                  case "topRight":
                      if (overflow)
                          // Is the form contained in an overflown container?
                          promptleftPosition += fieldWidth - 30;
                      else {
                          promptleftPosition += fieldWidth - 30;
                          promptTopPosition += -promptHeight -2;
                      }
                      break;
                  case "topLeft":
                      promptTopPosition += -promptHeight - 10;
                      break;
                  case "centerRight":
                      promptleftPosition += fieldWidth + 13;
                      break;
                  case "bottomLeft":
                      promptTopPosition = promptTopPosition + field.height() + 15;
                      break;
                  case "bottomRight":
                      promptleftPosition += fieldWidth - 30;
                      promptTopPosition += field.height() + 5;
              }
  
              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({},$.validationEngine.defaults, options);
  
              form.data('jqv', userOptions);
              return userOptions;
          },
          
          /**
           * Removes forbidden characters from class name
           * @param {String} className
           */
          _getClassName: function(className) {
          	return className.replace(":","_").replace(".","_");
          }
      };
  
      /**
       * 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 false;  // 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 != "hidePrompt" && 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= {defaults:{
  
          // Name of the event triggering field validation
          validationEventTrigger: "blur",
          // Automatically scroll viewport to the first error
          scroll: true,
          // Opening box position, possible locations are: topLeft,
          // topRight, bottomLeft, centerRight, bottomRight
          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,
          // Ajax form validation callback method: boolean onComplete(form, status, errors, options)
          // retuns false if the form.submit event needs to be canceled.
  		ajaxFormValidationURL: false,
          // The url to send the submit ajax validation (default to action)
          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 the form is displayed within a scrolling DIV
          isOverflown: false,
          overflownDIV: "",
  
          // true when form and fields are binded
          binded: false,
          // 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,
          // 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: {}
  
      }}
  	
  })(jQuery);
  