?
Path : /home/admin/domains/happytokorea.com/public_html/backend/js/forms/ |
Current File : /home/admin/domains/happytokorea.com/public_html/backend/js/forms/jquery.validationEngine.js |
/* * 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; } return this; }, /** * 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": var classGroup = "[class*=" + rules[i + 1] + "]"; var firstOfGroup = field.closest("form").find(classGroup).eq(0); var secondOfGroup = field.closest("form").find(classGroup).eq(1); /* if (firstOfGroup[0] != field[0]) { methods._validateField(firstOfGroup, options, skipAjaxValidation) options.showArrow = true; continue; }; */ //if one entry out of the pair has value then proceed to run through validation if (firstOfGroup[0].value || secondOfGroup[0].value) { errorMsg = methods._dateRange(firstOfGroup, secondOfGroup, rules, i, options); } if (errorMsg) required = true; options.showArrow = false; break; case "dateTimeRange": var classGroup = "[class*=" + rules[i + 1] + "]"; var firstOfGroup = field.closest("form").find(classGroup).eq(0); var secondOfGroup = field.closest("form").find(classGroup).eq(1); /* if (firstOfGroup[0] != field[0]) { methods._validateField(firstOfGroup, options, skipAjaxValidation) options.showArrow = true; continue; }; */ //if one entry out of the pair has value then proceed to run through validation if (firstOfGroup[0].value || secondOfGroup[0].value) { errorMsg = methods._dateTimeRange(firstOfGroup, secondOfGroup, rules, i, options); } if (errorMsg) required = true; options.showArrow = false; 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; // 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 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]))$/); if (dateRegEx.test(value)) { return true; } return false; }, /** * 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})$/); 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())); }, /** * Checks date range * * @param {jqObject} first field name * @param {jqObject} second field name * @return an error string if validation failed */ _dateRange: function (first, second, rules, i, options) { //are not both populated if ((!first[0].value && second[0].value) || (first[0].value && !second[0].value)) { return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; } //are not both dates if (!methods._isDate(first[0].value) || !methods._isDate(second[0].value)) { return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; } //are both dates but range is off if (!methods._dateCompare(first[0].value, second[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 (first, second, rules, i, options) { //are not both populated if ((!first[0].value && second[0].value) || (first[0].value && !second[0].value)) { return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; } //are not both dates if (!methods._isDateTime(first[0].value) || !methods._isDateTime(second[0].value)) { return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; } //are both dates but range is off if (!methods._dateCompare(first[0].value, second[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(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, "left": pos.callerleftPosition, "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);