-
-
Save anonymous/0349d8c92039fc8c6009 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Inline Form Validation Engine 2.6.1, 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 | |
$(".formError").live("click", 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) { | |
if(!$(this).is("form")) { | |
alert("Sorry, jqv.attach() only applies to a form"); | |
return this; | |
} | |
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) { | |
// bind fields | |
form.find("["+options.validateAttribute+"*=validate]").not("[type=checkbox]").not("[type=radio]").not(".datepicker").bind(options.validationEventTrigger, methods._onFieldEvent); | |
form.find("["+options.validateAttribute+"*=validate][type=checkbox],["+options.validateAttribute+"*=validate][type=radio]").bind("click", methods._onFieldEvent); | |
form.find("["+options.validateAttribute+"*=validate][class*=datepicker]").bind(options.validationEventTrigger,{"delay": 300}, methods._onFieldEvent); | |
} | |
if (options.autoPositionUpdate) { | |
$(window).bind("resize", { | |
"noAnimation": true, | |
"formElem": form | |
}, methods.updatePromptsPosition); | |
} | |
// bind form.submit | |
form.bind("submit", methods._onSubmitEvent); | |
return this; | |
}, | |
/** | |
* Unregisters any bindings that may point to jQuery.validaitonEngine | |
*/ | |
detach: function() { | |
if(!$(this).is("form")) { | |
alert("Sorry, jqv.detach() only applies to a form"); | |
return this; | |
} | |
var form = this; | |
var options = form.data('jqv'); | |
// unbind fields | |
form.find("["+options.validateAttribute+"*=validate]").not("[type=checkbox]").unbind(options.validationEventTrigger, methods._onFieldEvent); | |
form.find("["+options.validateAttribute+"*=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("["+options.validateAttribute+"*=validate]").not("[type=checkbox]").die(options.validationEventTrigger, methods._onFieldEvent); | |
form.find("["+options.validateAttribute+"*=validate][type=checkbox]").die("click", methods._onFieldEvent); | |
// unbind form.submit | |
form.die("submit", methods.onAjaxFormComplete); | |
form.removeData('jqv'); | |
if (options.autoPositionUpdate) | |
$(window).unbind("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('validating')) { | |
element.addClass('validating'); | |
var options = element.data('jqv'); | |
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.removeClass('validating'); | |
} else { | |
// field validation | |
var form = element.closest('form'); | |
var options = form.data('jqv'); | |
valid = methods._validateField(element, options); | |
if (valid && options.onFieldSuccess) | |
options.onFieldSuccess(); | |
else if (options.onFieldFailure && options.InvalidFields.length > 0) { | |
options.onFieldFailure(); | |
} | |
} | |
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')); | |
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 || options.prettyCheckboxes) && 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'); | |
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'); | |
var options = form.data('jqv'); | |
var fadeDuration = (options && options.fadeDuration) ? options.fadeDuration : 0.3; | |
var closingtag; | |
if($(this).is("form")) { | |
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:0.3; | |
$('.formError').fadeTo(duration, 0.3, 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'); | |
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'); | |
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 || options.prettyCheckboxes)) | |
first_err = field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix); | |
else | |
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; | |
} | |
} | |
// 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) { | |
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; | |
} | |
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, 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 (field.is(":hidden") && !(options.prettySelect || options.prettyCheckboxes) || 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")); | |
// Fix for adding spaces in the rules | |
for (var i = 0; i < rules.length; i++) { | |
rules[i] = rules[i].replace(" ", ""); | |
// Remove any parsing errors | |
if (rules[i] === '') { | |
delete rules[i]; | |
} | |
} | |
for (var i = 0, field_errors = 0; i < rules.length; i++) { | |
// If we are limiting errors, and have hit the max, break | |
if (limitErrors && field_errors >= options.maxErrorsPerField) { | |
// If we haven't hit a required yet, check to see if there is one in the validation rules for this | |
// field and that it's index is greater or equal to our current index | |
if (!required) { | |
var have_required = $.inArray('required', rules); | |
required = (have_required != -1 && have_required >= i); | |
} | |
break; | |
} | |
var errorMsg = undefined; | |
switch (rules[i]) { | |
case "required": | |
required = true; | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._required); | |
break; | |
case "custom": | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._custom); | |
break; | |
case "groupRequired": | |
// Check is its the first of group, if not, reload validation with first field | |
// AND continue normal validation on present field | |
var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]"; | |
var firstOfGroup = form.find(classGroup).eq(0); | |
if(firstOfGroup[0] != field[0]){ | |
methods._validateField(firstOfGroup, options, skipAjaxValidation); | |
options.showArrow = true; | |
} | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._groupRequired); | |
if(errorMsg) required = true; | |
options.showArrow = false; | |
break; | |
case "ajax": | |
// AJAX defaults to returning it's loading message | |
errorMsg = methods._ajax(field, rules, i, options); | |
if (errorMsg) { | |
promptType = "load"; | |
} | |
break; | |
case "minSize": | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minSize); | |
break; | |
case "maxSize": | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxSize); | |
break; | |
case "min": | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._min); | |
break; | |
case "max": | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._max); | |
break; | |
case "past": | |
errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._past); | |
break; | |
case "future": | |
errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._future); | |
break; | |
case "dateRange": | |
var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]"; | |
options.firstOfGroup = form.find(classGroup).eq(0); | |
options.secondOfGroup = form.find(classGroup).eq(1); | |
//if one entry out of the pair has value then proceed to run through validation | |
if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) { | |
errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateRange); | |
} | |
if (errorMsg) required = true; | |
options.showArrow = false; | |
break; | |
case "dateTimeRange": | |
var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]"; | |
options.firstOfGroup = form.find(classGroup).eq(0); | |
options.secondOfGroup = form.find(classGroup).eq(1); | |
//if one entry out of the pair has value then proceed to run through validation | |
if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) { | |
errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateTimeRange); | |
} | |
if (errorMsg) required = true; | |
options.showArrow = false; | |
break; | |
case "maxCheckbox": | |
field = $(form.find("input[name='" + fieldName + "']")); | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxCheckbox); | |
break; | |
case "minCheckbox": | |
field = $(form.find("input[name='" + fieldName + "']")); | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minCheckbox); | |
break; | |
case "equals": | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._equals); | |
break; | |
case "funcCall": | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._funcCall); | |
break; | |
case "creditCard": | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._creditCard); | |
break; | |
case "condRequired": | |
errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._condRequired); | |
if (errorMsg !== undefined) { | |
required = true; | |
} | |
break; | |
default: | |
} | |
var end_validation = false; | |
// If we were passed back an message object, check what the status was to determine what to do | |
if (typeof errorMsg == "object") { | |
switch (errorMsg.status) { | |
case "_break": | |
end_validation = true; | |
break; | |
// If we have an error message, set errorMsg to the error message | |
case "_error": | |
errorMsg = errorMsg.message; | |
break; | |
// If we want to throw an error, but not show a prompt, return early with true | |
case "_error_no_prompt": | |
return true; | |
break; | |
// Anything else we continue on | |
default: | |
break; | |
} | |
} | |
// If it has been specified that validation should end now, break | |
if (end_validation) { | |
break; | |
} | |
// If we have a string, that means that we have an error, so add it to the error message. | |
if (typeof errorMsg == 'string') { | |
promptText += errorMsg + "<br/>"; | |
options.isError = true; | |
field_errors++; | |
} | |
} | |
// If the rules required is not added, an empty field is not validated | |
if(!required && field.val().length < 1) 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"); | |
if ((fieldType == "radio" || fieldType == "checkbox") && form.find("input[name='" + fieldName + "']").size() > 1) { | |
field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:first")); | |
options.showArrow = false; | |
} | |
if(field.is(":hidden") && (options.prettySelect || options.prettyCheckboxes)) { | |
field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix); | |
} | |
if (options.isError){ | |
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); | |
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 beforeChangeRule = rule; | |
if (rule == "custom") { | |
var custom_validation_type_index = jQuery.inArray(rule, rules)+ 1; | |
var custom_validation_type = rules[custom_validation_type_index]; | |
rule = "custom[" + custom_validation_type + "]"; | |
} | |
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, beforeChangeRule, options); | |
if (custom_message) errorMsg = custom_message; | |
} | |
return errorMsg; | |
}, | |
_getCustomErrorMessage:function (field, classes, rule, options) { | |
var custom_message = false; | |
var validityProp = 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: | |
if (! $.trim(field.val()) || field.val() == field.attr("data-validation-placeholder") || field.val() == field.attr("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"); | |
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").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("input[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("input[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").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) { | |
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; | |
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; | |
// 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("/"); | |
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 | |
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"); | |
break; | |
default: | |
/* it has error */ | |
//alert("unknown popup type:"+type); | |
} | |
if (ajaxed) | |
prompt.addClass("ajaxed"); | |
// create the prompt content | |
var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt); | |
// create the css arrow pointing at the field | |
// note that there is no triangle on max-checkbox and radio | |
if (options.showArrow) { | |
var arrow = $('<div>').addClass("formErrorArrow"); | |
//prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10) | |
var positionType=field.data("promptPosition") || options.promptPosition; | |
if (typeof(positionType)=='string') | |
{ | |
var pos=positionType.indexOf(":"); | |
if(pos!=-1) | |
positionType=positionType.substring(0,pos); | |
} | |
switch (positionType) { | |
case "bottomLeft": | |
case "bottomRight": | |
prompt.find(".formErrorContent").before(arrow); | |
arrow.addClass("formErrorArrowBottom").html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>'); | |
break; | |
case "topLeft": | |
case "topRight": | |
arrow.html('<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>'); | |
prompt.append(arrow); | |
break; | |
} | |
} | |
// Add custom prompt class | |
if (options.addPromptClass) | |
prompt.addClass(options.addPromptClass); | |
prompt.css({ | |
"opacity": 0, | |
'position':'absolute' | |
}); | |
field.before(prompt); | |
var pos = methods._calculatePosition(field, prompt, options); | |
prompt.css({ | |
"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').attr('id'); | |
var className = methods._getClassName(field.attr("id")) + "formError"; | |
var match = $("." + methods._escapeExpression(className) + '.parentForm' + 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; | |
}; | |
//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); | |
} | |
} | |
} | |
}; | |
/** | |
* 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, | |
// 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, | |
// 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, | |
addSuccessCssClassToField: false, | |
addFailureCssClassToField: false, | |
// 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, | |
// Use prettyCheckboxes checkbox library | |
prettyCheckboxes: 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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
<fieldset id="discipline"> | |
<legend>Discipline</legend> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-1" class="validate[required] checkbox" value="1"><label for="discipline-1">Discipline 1</label></div> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-2" class="validate[required] checkbox" value="2"><label for="discipline-2">Discipline 2</label></div> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-3" class="validate[required] checkbox" value="3"><label for="discipline-3">Discipline 3</label></div> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-4" class="validate[required] checkbox" value="4"><label for="discipline-4">Discipline 4</label></div> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-5" class="validate[required] checkbox" value="5"><label for="discipline-5">Discipline 5</label></div> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-6" class="validate[required] checkbox" value="6"><label for="discipline-6">Discipline 6</label></div> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-7" class="validate[required] checkbox" value="7"><label for="discipline-7">Discipline 7</label></div> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-8" class="validate[required] checkbox" value="8"><label for="discipline-8">Discipline 8</label></div> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-9" class="validate[required] checkbox" value="9"><label for="discipline-9">Discipline 9</label></div> | |
<div><input type="checkbox" name="discipline[group]" id="discipline-10" class="validate[required] checkbox" value="10"><label for="discipline-10">Discipline 10</label></div> | |
</fieldset> | |
... | |
<!-- PrettyCheckboxes --> | |
<script type="text/javascript" src="<?php bloginfo( 'template_url' ); ?>/js/prettyCheckboxes.js?v=1.1"></script> | |
<!-- jQuery.validationEngine --> | |
<script type="text/javascript" src="<?php bloginfo( 'template_url' ); ?>/js/jquery.validationEngine/jquery.validationEngine-en.ugly.js?v=2.6.1"></script> | |
<script type="text/javascript" src="<?php bloginfo( 'template_url' ); ?>/js/jquery.validationEngine/jquery.validationEngine.js?v=2.6.1"></script> | |
<!-- Scripts --> | |
<script type="text/javascript" src="<?php bloginfo( 'template_url' ); ?>/js/scripts.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ------------------------------------------------------------------------ | |
prettyCheckboxes | |
Developped By: Stephane Caron (http://www.no-margin-for-errors.com) | |
Inspired By: All the non user friendly custom checkboxes solutions ;) | |
Version: 1.1 | |
Copyright: Feel free to redistribute the script/modify it, as | |
long as you leave my infos at the top. | |
------------------------------------------------------------------------- */ | |
jQuery.fn.prettyCheckboxes=function(a){a=jQuery.extend({checkboxWidth:17,checkboxHeight:17,className:"prettyCheckbox",display:"list"},a);$(this).each(function(){$label=$('label[for="'+$(this).attr("id")+'"]');$label.prepend("<span class='holderWrap'><span class='holder'></span></span>");if($(this).is(":checked")){$label.addClass("checked")}$label.addClass(a.className).addClass($(this).attr("type")).addClass(a.display);$label.find("span.holderWrap").width(a.checkboxWidth).height(a.checkboxHeight);$label.find("span.holder").width(a.checkboxWidth);$(this).addClass("hiddenCheckbox");$label.bind("click",function(){$("input#"+$(this).attr("for")).triggerHandler("click");if($("input#"+$(this).attr("for")).is(":checkbox")){$(this).toggleClass("checked");$("input#"+$(this).attr("for")).checked=true;$(this).find("span.holder").css("top",0)}else{$toCheck=$("input#"+$(this).attr("for"));$('input[name="'+$toCheck.attr("name")+'"]').each(function(){$('label[for="'+$(this).attr("id")+'"]').removeClass("checked")});$(this).addClass("checked");$toCheck.checked=true}});$("input#"+$label.attr("for")).bind("keypress",function(b){if(b.keyCode==32){if($.browser.msie){$('label[for="'+$(this).attr("id")+'"]').toggleClass("checked")}else{$(this).trigger("click")}return false}})})};checkAllPrettyCheckboxes=function(b,a){if($(b).is(":checked")){$(a).find("input[type=checkbox]:not(:checked)").each(function(){$('label[for="'+$(this).attr("id")+'"]').trigger("click");if($.browser.msie){$(this).attr("checked","checked")}else{$(this).trigger("click")}})}else{$(a).find("input[type=checkbox]:checked").each(function(){$('label[for="'+$(this).attr("id")+'"]').trigger("click");if($.browser.msie){$(this).attr("checked","")}else{$(this).trigger("click")}})}}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//On DOM ready... | |
$(function() { | |
var prefix = 'customFormField_'; | |
$('#create-ad').validationEngine({ | |
autoPositionUpdate : true, | |
prettyCheckboxes : true, //added in by me | |
prettySelect : true, | |
usePrefix : prefix | |
}); | |
// By default, selectBox does not create an id to the newly created element - We need to add this manually | |
$('select').each(function(){ | |
// The jquery validation engine needs an id on the "a" element created by selectBox plugin | |
$(this).next('a.selectBox') | |
// Since id needs to be unique, we use a prefix here (can use suffix - up to you) | |
.attr("id", prefix + this.id ) | |
// By default, all classes are passed on to the new element - Important: We need to remove it | |
.removeClass("validate[required]"); | |
}); | |
//As needed by selectBox, I assume a variation would be needed for the checkboxes also. | |
$('input[type="checkbox"]').each(function() { | |
$(this).next('label') | |
.attr("id", prefix + this.id ) | |
.removeClass("validate[required]"); | |
}); | |
$('.custom-tick').click(function() { | |
$(this).each(function() { | |
if ($(this).hasClass('checked')) { | |
$(this).siblings('input[type="checkbox"]') | |
.attr('checked', true); | |
} else { | |
$(this).siblings('input[type="checkbox"]') | |
.attr('checked', false); | |
} | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment