From ad405c7045c6e242080ec9d3ce4c9e586b517c4f Mon Sep 17 00:00:00 2001 From: Daniel Schmitzer Date: Thu, 14 Jan 2016 14:55:36 +0000 Subject: - Testweise 4 git-svn-id: https://[internal]/wordpress/plugins/wpsg4@5261 0d5edaf7-2b12-4e95-93f4-047927463d4b --- views/js/jquery.validationEngine.js | 2077 +++++++++++++++++++++++++++++++++++ 1 file changed, 2077 insertions(+) create mode 100644 views/js/jquery.validationEngine.js (limited to 'views/js/jquery.validationEngine.js') diff --git a/views/js/jquery.validationEngine.js b/views/js/jquery.validationEngine.js new file mode 100644 index 0000000..054553d --- /dev/null +++ b/views/js/jquery.validationEngine.js @@ -0,0 +1,2077 @@ +/* + * 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 + "
"; + 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 = $('
'); + 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 = $('
').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 = $('
').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('
'); + break; + case "topLeft": + case "topRight": + arrow.html('
'); + 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); + + -- cgit v1.2.3