diff --git a/.travis.yml b/.travis.yml index f90e8b9..6ef04eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js node_js: - - "0.9" + - "0.10" before_script: - npm install -g grunt-cli diff --git a/dist/jqBootstrapValidation-1.3.7.js b/dist/jqBootstrapValidation-1.3.7.js index 3944a41..1ebad0a 100644 --- a/dist/jqBootstrapValidation-1.3.7.js +++ b/dist/jqBootstrapValidation-1.3.7.js @@ -1,667 +1,668 @@ -/*! jqBootstrapValidation - v1.3.7 - 2013-05-07 +/*! jqBootstrapValidation - v1.3.7 - 2014-08-15 * http://reactiveraven.github.com/jqBootstrapValidation -* Copyright (c) 2013 David Godfrey; Licensed MIT */ - -(function( $ ){ - - var createdElements = []; - - var defaults = { - options: { - prependExistingHelpBlock: false, - sniffHtml: true, // sniff for 'required', 'maxlength', etc - preventSubmit: true, // stop the form submit event from firing if validation fails - submitError: false, // function called if there is an error when trying to submit - submitSuccess: false, // function called just before a successful submit event is sent to the server +* Copyright (c) 2014 David Godfrey; Licensed MIT */ +(function ($) { + + var createdElements = []; + + var defaults = { + options: { + prependExistingHelpBlock: false, + sniffHtml: true, // sniff for 'required', 'maxlength', etc + preventSubmit: true, // stop the form submit event from firing if validation fails + submitError: false, // function called if there is an error when trying to submit + submitSuccess: false, // function called just before a successful submit event is sent to the server semanticallyStrict: false, // set to true to tidy up generated HTML output + removeSuccess : true, bindEvents: [], - autoAdd: { - helpBlocks: true - }, - filter: function () { - // return $(this).is(":visible"); // only validate elements you can see - return true; // validate everything - } - }, - methods: { - init : function( options ) { - - // Get a clean copy of the defaults for extending - var settings = $.extend(true, {}, defaults); - // Set up the options based on the input - settings.options = $.extend(true, settings.options, options); - - var $siblingElements = this; - - var uniqueForms = $.unique( - $siblingElements.map( function () { - return $(this).parents("form")[0]; - }).toArray() - ); - - $(uniqueForms).bind("submit.validationSubmit", function (e) { - var $form = $(this); - var warningsFound = 0; - // Get all inputs - var $allInputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter); - var $allControlGroups = $form.find(".control-group"); - - // Only trigger validation on the ones that actually _have_ validation - var $inputsWithValidators = $allInputs.filter(function () { - return $(this).triggerHandler("getValidatorCount.validation") > 0; - }); - $inputsWithValidators.trigger("submit.validation"); - - // But all of them are out-of-focus now, because we're submitting. - $allInputs.trigger("validationLostFocus.validation"); - - // Okay, now check each controlgroup for errors (or warnings) - $allControlGroups.each(function (i, el) { - var $controlGroup = $(el); - if ($controlGroup.hasClass("warning") || $controlGroup.hasClass("error")) { - $controlGroup.removeClass("warning").addClass("error"); - warningsFound++; - } - }); - - if (warningsFound) { - // If we found any warnings, maybe we should prevent the submit - // event, and trigger 'submitError' (if they're set up) - if (settings.options.preventSubmit) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - $form.addClass("error"); - if ($.isFunction(settings.options.submitError)) { - settings.options.submitError($form, e, $inputsWithValidators.jqBootstrapValidation("collectErrors", true)); - } - } else { - // Woo! No errors! We can pass the submit event to submitSuccess - // (if it has been set up) - $form.removeClass("error"); - if ($.isFunction(settings.options.submitSuccess)) { - settings.options.submitSuccess($form, e); - } - } - }); - - return this.each(function(){ - - // Get references to everything we're interested in - var $this = $(this), - $controlGroup = $this.parents(".control-group").first(), - $helpBlock = $controlGroup.find(".help-block").first(), - $form = $this.parents("form").first(), - validatorNames = []; - - // create message container if not exists - if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) { - $helpBlock = $('
'); - $controlGroup.find('.controls').append($helpBlock); - createdElements.push($helpBlock[0]); - } - - // ============================================================= - // SNIFF HTML FOR VALIDATORS - // ============================================================= - - // *snort sniff snuffle* - - if (settings.options.sniffHtml) { - var message; - // --------------------------------------------------------- - // PATTERN - // --------------------------------------------------------- - if ($this.data("validationPatternPattern")) { - $this.attr("pattern", $this.data("validationPatternPattern")); - } - if ($this.attr("pattern") !== undefined) { - message = "Not in the expected format"; - if ($this.data("validationPatternMessage")) { - message = $this.data("validationPatternMessage"); - } - $this.data("validationPatternMessage", message); - $this.data("validationPatternRegex", $this.attr("pattern")); - } - // --------------------------------------------------------- - // MAX - // --------------------------------------------------------- - if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) { - var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax")); - message = "Too high: Maximum of '" + max + "'"; - if ($this.data("validationMaxMessage")) { - message = $this.data("validationMaxMessage"); - } - $this.data("validationMaxMessage", message); - $this.data("validationMaxMax", max); - } - // --------------------------------------------------------- - // MIN - // --------------------------------------------------------- - if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) { - var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin")); - message = "Too low: Minimum of '" + min + "'"; - if ($this.data("validationMinMessage")) { - message = $this.data("validationMinMessage"); - } - $this.data("validationMinMessage", message); - $this.data("validationMinMin", min); - } - // --------------------------------------------------------- - // MAXLENGTH - // --------------------------------------------------------- - if ($this.attr("maxlength") !== undefined) { - message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters"; - if ($this.data("validationMaxlengthMessage")) { - message = $this.data("validationMaxlengthMessage"); - } - $this.data("validationMaxlengthMessage", message); - $this.data("validationMaxlengthMaxlength", $this.attr("maxlength")); - } - // --------------------------------------------------------- - // MINLENGTH - // --------------------------------------------------------- - if ($this.attr("minlength") !== undefined) { - message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters"; - if ($this.data("validationMinlengthMessage")) { - message = $this.data("validationMinlengthMessage"); - } - $this.data("validationMinlengthMessage", message); - $this.data("validationMinlengthMinlength", $this.attr("minlength")); - } - // --------------------------------------------------------- - // REQUIRED - // --------------------------------------------------------- - if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) { - message = settings.builtInValidators.required.message; - if ($this.data("validationRequiredMessage")) { - message = $this.data("validationRequiredMessage"); - } - $this.data("validationRequiredMessage", message); - } - // --------------------------------------------------------- - // NUMBER - // --------------------------------------------------------- - if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") { - message = settings.validatorTypes.number.message; // TODO: fix this - if ($this.data("validationNumberMessage")) { - message = $this.data("validationNumberMessage"); - } - $this.data("validationNumberMessage", message); - - var step = settings.validatorTypes.number.step; // TODO: and this - if ($this.data("validationNumberStep")) { - step = $this.data("validationNumberStep"); - } - $this.data("validationNumberStep", step); - - var decimal = settings.validatorTypes.number.decimal; - if ($this.data("validationNumberDecimal")) { - decimal = $this.data("validationNumberDecimal"); - } - $this.data("validationNumberDecimal", decimal); - } - // --------------------------------------------------------- - // EMAIL - // --------------------------------------------------------- - if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") { - message = "Not a valid email address"; - if ($this.data("validationEmailMessage")) { - message = $this.data("validationEmailMessage"); - } - $this.data("validationEmailMessage", message); - } - // --------------------------------------------------------- - // MINCHECKED - // --------------------------------------------------------- - if ($this.attr("minchecked") !== undefined) { - message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required"; - if ($this.data("validationMincheckedMessage")) { - message = $this.data("validationMincheckedMessage"); - } - $this.data("validationMincheckedMessage", message); - $this.data("validationMincheckedMinchecked", $this.attr("minchecked")); + autoAdd: { + helpBlocks: true + }, + filter: function () { + // return $(this).is(":visible"); // only validate elements you can see + return true; // validate everything } - // --------------------------------------------------------- - // MAXCHECKED - // --------------------------------------------------------- - if ($this.attr("maxchecked") !== undefined) { - message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required"; - if ($this.data("validationMaxcheckedMessage")) { - message = $this.data("validationMaxcheckedMessage"); - } - $this.data("validationMaxcheckedMessage", message); - $this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked")); - } - } - - // ============================================================= - // COLLECT VALIDATOR NAMES - // ============================================================= - - // Get named validators - if ($this.data("validation") !== undefined) { - validatorNames = $this.data("validation").split(","); - } - - // Get extra ones defined on the element's data attributes - $.each($this.data(), function (i, el) { - var parts = i.replace(/([A-Z])/g, ",$1").split(","); - if (parts[0] === "validation" && parts[1]) { - validatorNames.push(parts[1]); - } - }); - - // ============================================================= - // NORMALISE VALIDATOR NAMES - // ============================================================= - - var validatorNamesToInspect = validatorNames; - var newValidatorNamesToInspect = []; - - var uppercaseEachValidatorName = function (i, el) { - validatorNames[i] = formatValidatorName(el); - }; - - var inspectValidators = function(i, el) { - if ($this.data("validation" + el + "Shortcut") !== undefined) { - // Are these custom validators? - // Pull them out! - $.each($this.data("validation" + el + "Shortcut").split(","), function(i2, el2) { - newValidatorNamesToInspect.push(el2); - }); - } else if (settings.builtInValidators[el.toLowerCase()]) { - // Is this a recognised built-in? - // Pull it out! - var validator = settings.builtInValidators[el.toLowerCase()]; - if (validator.type.toLowerCase() === "shortcut") { - $.each(validator.shortcut.split(","), function (i, el) { - el = formatValidatorName(el); - newValidatorNamesToInspect.push(el); - validatorNames.push(el); + }, + methods: { + init: function (options) { + + // Get a clean copy of the defaults for extending + var settings = $.extend(true, {}, defaults); + // Set up the options based on the input + settings.options = $.extend(true, settings.options, options); + + var $siblingElements = this; + + var uniqueForms = $.unique( + $siblingElements.map(function () { + return $(this).parents("form")[0]; + }).toArray() + ); + + $(uniqueForms).bind("submit.validationSubmit", function (e) { + var $form = $(this); + var warningsFound = 0; + // Get all inputs + var $allInputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter); + var $allControlGroups = $form.find(".form-group"); + + // Only trigger validation on the ones that actually _have_ validation + var $inputsWithValidators = $allInputs.filter(function () { + return $(this).triggerHandler("getValidatorCount.validation") > 0; + }); + $inputsWithValidators.trigger("submit.validation"); + + // But all of them are out-of-focus now, because we're submitting. + $allInputs.trigger("validationLostFocus.validation"); + + // Okay, now check each controlgroup for errors (or warnings) + $allControlGroups.each(function (i, el) { + var $controlGroup = $(el); + if ($controlGroup.hasClass("warning") || $controlGroup.hasClass("error")) { + $controlGroup.removeClass("warning").addClass("error"); + warningsFound++; + } + }); + + if (warningsFound) { + // If we found any warnings, maybe we should prevent the submit + // event, and trigger 'submitError' (if they're set up) + if (settings.options.preventSubmit) { + e.preventDefault(); + e.stopImmediatePropagation(); + } + $form.addClass("error"); + if ($.isFunction(settings.options.submitError)) { + settings.options.submitError($form, e, $inputsWithValidators.jqBootstrapValidation("collectErrors", true)); + } + } else { + // Woo! No errors! We can pass the submit event to submitSuccess + // (if it has been set up) + $form.removeClass("error"); + if ($.isFunction(settings.options.submitSuccess)) { + settings.options.submitSuccess($form, e); + } + } }); - } - } - }; - - do // repeatedly expand 'shortcut' validators into their real validators - { - // Uppercase only the first letter of each name - $.each(validatorNames, uppercaseEachValidatorName); - - // Remove duplicate validator names - validatorNames = $.unique(validatorNames); - - // Pull out the new validator names from each shortcut - newValidatorNamesToInspect = []; - $.each(validatorNamesToInspect, inspectValidators); - - validatorNamesToInspect = newValidatorNamesToInspect; - - } while (validatorNamesToInspect.length > 0); - - // ============================================================= - // SET UP VALIDATOR ARRAYS - // ============================================================= - - /* We're gonna generate something like - * - * { - * "regex": [ - * { -- a validator object here --}, - * { -- a validator object here --} - * ], - * "required": [ - * { -- a validator object here --}, - * { -- a validator object here --} - * ] - * } - * - * with a few more entries. - * - * Because we only add a few validators to each field, most of the - * keys will be empty arrays with no validator objects in them, and - * thats fine. - */ - - var validators = {}; - - $.each(validatorNames, function (i, el) { - // Set up the 'override' message - var message = $this.data("validation" + el + "Message"); - var hasOverrideMessage = !!message; - var foundValidator = false; - if (!message) { - message = "'" + el + "' validation failed "; - } - $.each( - settings.validatorTypes, - function (validatorType, validatorTemplate) { - if (validators[validatorType] === undefined) { - validators[validatorType] = []; - } - if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) { - var initted = validatorTemplate.init($this, el); - if (hasOverrideMessage) { - initted.message = message; - } - - validators[validatorType].push( - $.extend( - true, - { - name: formatValidatorName(validatorTemplate.name), - message: message - }, - initted - ) - ); - foundValidator = true; - } - } - ); - - if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) { - - var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]); - if (hasOverrideMessage) { - validator.message = message; - } - var validatorType = validator.type.toLowerCase(); - - if (validatorType === "shortcut") { - foundValidator = true; - } else { - $.each( - settings.validatorTypes, - function (validatorTemplateType, validatorTemplate) { - if (validators[validatorTemplateType] === undefined) { - validators[validatorTemplateType] = []; + return this.each(function () { + + // Get references to everything we're interested in + var $this = $(this), + $controlGroup = $this.parents(".form-group").first(), + $helpBlock = $controlGroup.find(".help-block").first(), + $form = $this.parents("form").first(), + validatorNames = []; + + // create message container if not exists + if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) { + $helpBlock = $('
'); + $controlGroup.find('.controls').append($helpBlock); + createdElements.push($helpBlock[0]); } - if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) { - $this.data( - "validation" + el + formatValidatorName(validatorTemplate.name), - validator[validatorTemplate.name.toLowerCase()] - ); - validators[validatorType].push( - $.extend( - validator, - validatorTemplate.init($this, el) - ) - ); - foundValidator = true; + + // ============================================================= + // SNIFF HTML FOR VALIDATORS + // ============================================================= + + // *snort sniff snuffle* + + if (settings.options.sniffHtml) { + var message; + // --------------------------------------------------------- + // PATTERN + // --------------------------------------------------------- + if ($this.data("validationPatternPattern")) { + $this.attr("pattern", $this.data("validationPatternPattern")); + } + if ($this.attr("pattern") !== undefined) { + message = "Not in the expected format"; + if ($this.data("validationPatternMessage")) { + message = $this.data("validationPatternMessage"); + } + $this.data("validationPatternMessage", message); + $this.data("validationPatternRegex", $this.attr("pattern")); + } + // --------------------------------------------------------- + // MAX + // --------------------------------------------------------- + if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) { + var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax")); + message = "Too high: Maximum of '" + max + "'"; + if ($this.data("validationMaxMessage")) { + message = $this.data("validationMaxMessage"); + } + $this.data("validationMaxMessage", message); + $this.data("validationMaxMax", max); + } + // --------------------------------------------------------- + // MIN + // --------------------------------------------------------- + if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) { + var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin")); + message = "Too low: Minimum of '" + min + "'"; + if ($this.data("validationMinMessage")) { + message = $this.data("validationMinMessage"); + } + $this.data("validationMinMessage", message); + $this.data("validationMinMin", min); + } + // --------------------------------------------------------- + // MAXLENGTH + // --------------------------------------------------------- + if ($this.attr("maxlength") !== undefined) { + message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters"; + if ($this.data("validationMaxlengthMessage")) { + message = $this.data("validationMaxlengthMessage"); + } + $this.data("validationMaxlengthMessage", message); + $this.data("validationMaxlengthMaxlength", $this.attr("maxlength")); + } + // --------------------------------------------------------- + // MINLENGTH + // --------------------------------------------------------- + if ($this.attr("minlength") !== undefined) { + message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters"; + if ($this.data("validationMinlengthMessage")) { + message = $this.data("validationMinlengthMessage"); + } + $this.data("validationMinlengthMessage", message); + $this.data("validationMinlengthMinlength", $this.attr("minlength")); + } + // --------------------------------------------------------- + // REQUIRED + // --------------------------------------------------------- + if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) { + message = settings.builtInValidators.required.message; + if ($this.data("validationRequiredMessage")) { + message = $this.data("validationRequiredMessage"); + } + $this.data("validationRequiredMessage", message); + } + // --------------------------------------------------------- + // NUMBER + // --------------------------------------------------------- + if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") { + message = settings.validatorTypes.number.message; // TODO: fix this + if ($this.data("validationNumberMessage")) { + message = $this.data("validationNumberMessage"); + } + $this.data("validationNumberMessage", message); + + var step = settings.validatorTypes.number.step; // TODO: and this + if ($this.data("validationNumberStep")) { + step = $this.data("validationNumberStep"); + } + $this.data("validationNumberStep", step); + + var decimal = settings.validatorTypes.number.decimal; + if ($this.data("validationNumberDecimal")) { + decimal = $this.data("validationNumberDecimal"); + } + $this.data("validationNumberDecimal", decimal); + } + // --------------------------------------------------------- + // EMAIL + // --------------------------------------------------------- + if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") { + message = "Not a valid email address"; + if ($this.data("validationEmailMessage")) { + message = $this.data("validationEmailMessage"); + } + $this.data("validationEmailMessage", message); + } + // --------------------------------------------------------- + // MINCHECKED + // --------------------------------------------------------- + if ($this.attr("minchecked") !== undefined) { + message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required"; + if ($this.data("validationMincheckedMessage")) { + message = $this.data("validationMincheckedMessage"); + } + $this.data("validationMincheckedMessage", message); + $this.data("validationMincheckedMinchecked", $this.attr("minchecked")); + } + // --------------------------------------------------------- + // MAXCHECKED + // --------------------------------------------------------- + if ($this.attr("maxchecked") !== undefined) { + message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required"; + if ($this.data("validationMaxcheckedMessage")) { + message = $this.data("validationMaxcheckedMessage"); + } + $this.data("validationMaxcheckedMessage", message); + $this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked")); + } } - } - ); - } - } - if (! foundValidator) { - $.error("Cannot find validation info for '" + el + "'"); - } - }); - - // ============================================================= - // STORE FALLBACK VALUES - // ============================================================= - - $helpBlock.data( - "original-contents", - ( - $helpBlock.data("original-contents") ? - $helpBlock.data("original-contents") : - $helpBlock.html() - ) - ); - - $helpBlock.data( - "original-role", - ( - $helpBlock.data("original-role") ? - $helpBlock.data("original-role") : - $helpBlock.attr("role") - ) - ); - - $controlGroup.data( - "original-classes", - ( - $controlGroup.data("original-clases") ? - $controlGroup.data("original-classes") : - $controlGroup.attr("class") - ) - ); - - $this.data( - "original-aria-invalid", - ( - $this.data("original-aria-invalid") ? - $this.data("original-aria-invalid") : - $this.attr("aria-invalid") - ) - ); - - // ============================================================= - // VALIDATION - // ============================================================= - - $this.bind( - "validation.validation", - function (event, params) { - - var value = getValue($this); - - // Get a list of the errors to apply - var errorsFound = []; - - $.each(validators, function (validatorType, validatorTypeArray) { - if ( - value || // has a truthy value - value.length || // not an empty string - ( // am including empty values - ( - params && - params.includeEmpty - ) || - !!settings.validatorTypes[validatorType].includeEmpty - ) || - ( // validator is blocking submit - !!settings.validatorTypes[validatorType].blockSubmit && - params && - !!params.submitting - ) - ) - { - $.each( - validatorTypeArray, - function (i, validator) { - if (settings.validatorTypes[validatorType].validate($this, value, validator)) { - errorsFound.push(validator.message); - } + // ============================================================= + // COLLECT VALIDATOR NAMES + // ============================================================= + + // Get named validators + if ($this.data("validation") !== undefined) { + validatorNames = $this.data("validation").split(","); } - ); - } - }); - return errorsFound; - } - ); + // Get extra ones defined on the element's data attributes + $.each($this.data(), function (i, el) { + var parts = i.replace(/([A-Z])/g, ",$1").split(","); + if (parts[0] === "validation" && parts[1]) { + validatorNames.push(parts[1]); + } + }); - $this.bind( - "getValidators.validation", - function () { - return validators; - } - ); - - var numValidators = 0; - - $.each(validators, function (i, el) { - numValidators += el.length; - }); - - $this.bind("getValidatorCount.validation", function () { - return numValidators; - }); - - // ============================================================= - // WATCH FOR CHANGES - // ============================================================= - $this.bind( - "submit.validation", - function () { - return $this.triggerHandler("change.validation", {submitting: true}); - } - ); - $this.bind( - ( - settings.options.bindEvents.length > 0 ? - settings.options.bindEvents : - [ - "keyup", - "focus", - "blur", - "click", - "keydown", - "keypress", - "change" - ] - ).concat(["revalidate"]).join(".validation ") + ".validation", - function (e, params) { - - var value = getValue($this); - - var errorsFound = []; - - if (params && !!params.submitting) { - $controlGroup.data("jqbvIsSubmitting", true); - } else if (e.type !== "revalidate") { - $controlGroup.data("jqbvIsSubmitting", false); - } - - var formIsSubmitting = !!$controlGroup.data("jqbvIsSubmitting"); - - $controlGroup.find("input,textarea,select").each(function (i, el) { - var oldCount = errorsFound.length; - $.each($(el).triggerHandler("validation.validation", params), function (j, message) { - errorsFound.push(message); + // ============================================================= + // NORMALISE VALIDATOR NAMES + // ============================================================= + + var validatorNamesToInspect = validatorNames; + var newValidatorNamesToInspect = []; + + var uppercaseEachValidatorName = function (i, el) { + validatorNames[i] = formatValidatorName(el); + }; + + var inspectValidators = function (i, el) { + if ($this.data("validation" + el + "Shortcut") !== undefined) { + // Are these custom validators? + // Pull them out! + $.each($this.data("validation" + el + "Shortcut").split(","), function (i2, el2) { + newValidatorNamesToInspect.push(el2); + }); + } else if (settings.builtInValidators[el.toLowerCase()]) { + // Is this a recognised built-in? + // Pull it out! + var validator = settings.builtInValidators[el.toLowerCase()]; + if (validator.type.toLowerCase() === "shortcut") { + $.each(validator.shortcut.split(","), function (i, el) { + el = formatValidatorName(el); + newValidatorNamesToInspect.push(el); + validatorNames.push(el); + }); + } + } + }; + + do // repeatedly expand 'shortcut' validators into their real validators + { + // Uppercase only the first letter of each name + $.each(validatorNames, uppercaseEachValidatorName); + + // Remove duplicate validator names + validatorNames = $.unique(validatorNames); + + // Pull out the new validator names from each shortcut + newValidatorNamesToInspect = []; + $.each(validatorNamesToInspect, inspectValidators); + + validatorNamesToInspect = newValidatorNamesToInspect; + + } while (validatorNamesToInspect.length > 0); + + // ============================================================= + // SET UP VALIDATOR ARRAYS + // ============================================================= + + /* We're gonna generate something like + * + * { + * "regex": [ + * { -- a validator object here --}, + * { -- a validator object here --} + * ], + * "required": [ + * { -- a validator object here --}, + * { -- a validator object here --} + * ] + * } + * + * with a few more entries. + * + * Because we only add a few validators to each field, most of the + * keys will be empty arrays with no validator objects in them, and + * thats fine. + */ + + var validators = {}; + + $.each(validatorNames, function (i, el) { + // Set up the 'override' message + var message = $this.data("validation" + el + "Message"); + var hasOverrideMessage = !!message; + var foundValidator = false; + if (!message) { + message = "'" + el + "' validation failed "; + } + + $.each( + settings.validatorTypes, + function (validatorType, validatorTemplate) { + if (validators[validatorType] === undefined) { + validators[validatorType] = []; + } + if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) { + var initted = validatorTemplate.init($this, el); + if (hasOverrideMessage) { + initted.message = message; + } + + validators[validatorType].push( + $.extend( + true, + { + name: formatValidatorName(validatorTemplate.name), + message: message + }, + initted + ) + ); + foundValidator = true; + } + } + ); + + if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) { + + var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]); + if (hasOverrideMessage) { + validator.message = message; + } + var validatorType = validator.type.toLowerCase(); + + if (validatorType === "shortcut") { + foundValidator = true; + } else { + $.each( + settings.validatorTypes, + function (validatorTemplateType, validatorTemplate) { + if (validators[validatorTemplateType] === undefined) { + validators[validatorTemplateType] = []; + } + if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) { + $this.data( + "validation" + el + formatValidatorName(validatorTemplate.name), + validator[validatorTemplate.name.toLowerCase()] + ); + validators[validatorType].push( + $.extend( + validator, + validatorTemplate.init($this, el) + ) + ); + foundValidator = true; + } + } + ); + } + } + + if (!foundValidator) { + $.error("Cannot find validation info for '" + el + "'"); + } + }); + + // ============================================================= + // STORE FALLBACK VALUES + // ============================================================= + + $helpBlock.data( + "original-contents", + ( + $helpBlock.data("original-contents") ? + $helpBlock.data("original-contents") : + $helpBlock.html() + ) + ); + + $helpBlock.data( + "original-role", + ( + $helpBlock.data("original-role") ? + $helpBlock.data("original-role") : + $helpBlock.attr("role") + ) + ); + + $controlGroup.data( + "original-classes", + ( + $controlGroup.data("original-clases") ? + $controlGroup.data("original-classes") : + $controlGroup.attr("class") + ) + ); + + $this.data( + "original-aria-invalid", + ( + $this.data("original-aria-invalid") ? + $this.data("original-aria-invalid") : + $this.attr("aria-invalid") + ) + ); + + // ============================================================= + // VALIDATION + // ============================================================= + + $this.bind( + "validation.validation", + function (event, params) { + + var value = getValue($this); + + // Get a list of the errors to apply + var errorsFound = []; + + $.each(validators, function (validatorType, validatorTypeArray) { + if ( + value || // has a truthy value + value.length || // not an empty string + ( // am including empty values + ( + params && + params.includeEmpty + ) || !!settings.validatorTypes[validatorType].includeEmpty + ) || + ( // validator is blocking submit + !!settings.validatorTypes[validatorType].blockSubmit && + params && !!params.submitting + ) + ) { + $.each( + validatorTypeArray, + function (i, validator) { + if (settings.validatorTypes[validatorType].validate($this, value, validator)) { + errorsFound.push(validator.message); + } + } + ); + } + }); + + return errorsFound; + } + ); + + $this.bind( + "getValidators.validation", + function () { + return validators; + } + ); + + var numValidators = 0; + + $.each(validators, function (i, el) { + numValidators += el.length; + }); + + $this.bind("getValidatorCount.validation", function () { + return numValidators; + }); + + // ============================================================= + // WATCH FOR CHANGES + // ============================================================= + $this.bind( + "submit.validation", + function () { + return $this.triggerHandler("change.validation", {submitting: true}); + } + ); + $this.bind( + ( + settings.options.bindEvents.length > 0 ? + settings.options.bindEvents : + [ + "keyup", + "focus", + "blur", + "click", + "keydown", + "keypress", + "change" + ] + ).concat(["revalidate"]).join(".validation ") + ".validation", + function (e, params) { + + var value = getValue($this); + + var errorsFound = []; + + if (params && !!params.submitting) { + $controlGroup.data("jqbvIsSubmitting", true); + } else if (e.type !== "revalidate") { + $controlGroup.data("jqbvIsSubmitting", false); + } + + var formIsSubmitting = !!$controlGroup.data("jqbvIsSubmitting"); + + $controlGroup.find("input,textarea,select").not('[type=submit]').each(function (i, el) { + var oldCount = errorsFound.length; + $.each($(el).triggerHandler("validation.validation", params) || [], function (j, message) { + errorsFound.push(message); + }); + if (errorsFound.length > oldCount) { + $(el).attr("aria-invalid", "true"); + } else { + var original = $this.data("original-aria-invalid"); + $(el).attr("aria-invalid", (original !== undefined ? original : false)); + } + }); + + $form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation"); + + errorsFound = $.unique(errorsFound.sort()); + + // Were there any errors? + if (errorsFound.length) { + // Better flag it up as a warning. + $controlGroup.removeClass("success error warning").addClass(formIsSubmitting ? "error" : "warning"); + + // How many errors did we find? + if (settings.options.semanticallyStrict && errorsFound.length === 1) { + // Only one? Being strict? Just output it. + $helpBlock.html(errorsFound[0] + + ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); + } else { + // Multiple? Being sloppy? Glue them together into an UL. + $helpBlock.html("" + + ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); + } + } else { + $controlGroup.removeClass("warning error success"); + if (value.length > 0) { + $controlGroup.addClass("success"); + } + $helpBlock.html($helpBlock.data("original-contents")); + } + + if (e.type === "blur") { + if( settings.options.removeSuccess ){ + $controlGroup.removeClass("success"); + } + } + } + ); + $this.bind("validationLostFocus.validation", function () { + if( settings.options.removeSuccess ){ + $controlGroup.removeClass("success"); + } + }); }); - if (errorsFound.length > oldCount) { - $(el).attr("aria-invalid", "true"); - } else { - var original = $this.data("original-aria-invalid"); - $(el).attr("aria-invalid", (original !== undefined ? original : false)); - } - }); + }, + destroy: function () { + + return this.each( + function () { + + var + $this = $(this), + $controlGroup = $this.parents(".form-group").first(), + $helpBlock = $controlGroup.find(".help-block").first(), + $form = $this.parents("form").first(); + + // remove our events + $this.unbind('.validation'); // events are namespaced. + $form.unbind(".validationSubmit"); + // reset help text + $helpBlock.html($helpBlock.data("original-contents")); + // reset classes + $controlGroup.attr("class", $controlGroup.data("original-classes")); + // reset aria + $this.attr("aria-invalid", $this.data("original-aria-invalid")); + // reset role + $helpBlock.attr("role", $this.data("original-role")); + // remove all elements we created + if ($.inArray($helpBlock[0], createdElements) > -1) { + $helpBlock.remove(); + } - $form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation"); + } + ); - errorsFound = $.unique(errorsFound.sort()); + }, + collectErrors: function (includeEmpty) { - // Were there any errors? - if (errorsFound.length) { - // Better flag it up as a warning. - $controlGroup.removeClass("success error warning").addClass(formIsSubmitting ? "error" : "warning"); + var errorMessages = {}; + this.each(function (i, el) { + var $el = $(el); + var name = $el.attr("name"); + var errors = $el.triggerHandler("validation.validation", {includeEmpty: true}); + errorMessages[name] = $.extend(true, errors, errorMessages[name]); + }); - // How many errors did we find? - if (settings.options.semanticallyStrict && errorsFound.length === 1) { - // Only one? Being strict? Just output it. - $helpBlock.html(errorsFound[0] + - ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); - } else { - // Multiple? Being sloppy? Glue them together into an UL. - $helpBlock.html("" + - ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); - } - } else { - $controlGroup.removeClass("warning error success"); - if (value.length > 0) { - $controlGroup.addClass("success"); - } - $helpBlock.html($helpBlock.data("original-contents")); - } + $.each(errorMessages, function (i, el) { + if (el.length === 0) { + delete errorMessages[i]; + } + }); - if (e.type === "blur") { - $controlGroup.removeClass("success"); - } - } - ); - $this.bind("validationLostFocus.validation", function () { - $controlGroup.removeClass("success"); - }); - }); - }, - destroy : function( ) { - - return this.each( - function() { - - var - $this = $(this), - $controlGroup = $this.parents(".control-group").first(), - $helpBlock = $controlGroup.find(".help-block").first(), - $form = $this.parents("form").first(); - - // remove our events - $this.unbind('.validation'); // events are namespaced. - $form.unbind(".validationSubmit"); - // reset help text - $helpBlock.html($helpBlock.data("original-contents")); - // reset classes - $controlGroup.attr("class", $controlGroup.data("original-classes")); - // reset aria - $this.attr("aria-invalid", $this.data("original-aria-invalid")); - // reset role - $helpBlock.attr("role", $this.data("original-role")); - // remove all elements we created - if ($.inArray($helpBlock[0], createdElements) > -1) { - $helpBlock.remove(); - } + return errorMessages; + + }, + hasErrors: function () { + + var errorMessages = []; + + this.find('input,select,textarea').add(this).each(function (i, el) { + errorMessages = errorMessages.concat( + $(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {submitting: true}) : [] + ); + }); - } - ); - - }, - collectErrors : function(includeEmpty) { - - var errorMessages = {}; - this.each(function (i, el) { - var $el = $(el); - var name = $el.attr("name"); - var errors = $el.triggerHandler("validation.validation", {includeEmpty: true}); - errorMessages[name] = $.extend(true, errors, errorMessages[name]); - }); - - $.each(errorMessages, function (i, el) { - if (el.length === 0) { - delete errorMessages[i]; - } - }); - - return errorMessages; - - }, - hasErrors: function() { - - var errorMessages = []; - - this.find('input,select,textarea').add(this).each(function (i, el) { - errorMessages = errorMessages.concat( - $(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {submitting: true}) : [] - ); - }); - - return (errorMessages.length > 0); - }, - override : function (newDefaults) { - defaults = $.extend(true, defaults, newDefaults); - } - }, - validatorTypes: { - callback: { + return (errorMessages.length > 0); + }, + override: function (newDefaults) { + defaults = $.extend(true, defaults, newDefaults); + } + }, + validatorTypes: { + callback: { name: "callback", - init: function($this, name) { + init: function ($this, name) { var result = { validatorName: name, callback: $this.data("validation" + name + "Callback"), @@ -678,13 +679,12 @@ return result; }, - validate: function($this, value, validator) { + validate: function ($this, value, validator) { if (validator.lastValue === value && validator.lastFinished) { return !validator.lastValid; } - if (validator.lastFinished === true) - { + if (validator.lastFinished === true) { validator.lastValue = value; validator.lastValid = true; validator.lastFinished = false; @@ -696,7 +696,7 @@ window, $this, value, - function(data) { + function (data) { if (rrjqbvValidator.lastValue === data.value) { rrjqbvValidator.lastValid = data.valid; if (data.message) { @@ -704,12 +704,12 @@ } rrjqbvValidator.lastFinished = true; rrjqbvThis.data( - "validation" + rrjqbvValidator.validatorName + "Message", + "validation" + rrjqbvValidator.validatorName + "Message", rrjqbvValidator.message ); - + // Timeout is set to avoid problems with the events being considered 'already fired' - setTimeout(function() { + setTimeout(function () { if (!$this.is(":focus") && $this.parents("form").first().data("jqbvIsSubmitting")) { rrjqbvThis.trigger("blur.validation"); } else { @@ -724,481 +724,493 @@ return false; } - }, - ajax: { - name: "ajax", - init: function ($this, name) { - return { - validatorName: name, - url: $this.data("validation" + name + "Ajax"), - lastValue: $this.val(), - lastValid: true, - lastFinished: true - }; - }, - validate: function ($this, value, validator) { - if (""+validator.lastValue === ""+value && validator.lastFinished === true) { - return validator.lastValid === false; - } - - if (validator.lastFinished === true) - { - validator.lastValue = value; - validator.lastValid = true; - validator.lastFinished = false; - $.ajax({ - url: validator.url, - data: "value=" + encodeURIComponent(value) + "&field=" + $this.attr("name"), - dataType: "json", - success: function (data) { - if (""+validator.lastValue === ""+data.value) { - validator.lastValid = !!(data.valid); - if (data.message) { - validator.message = data.message; - } - validator.lastFinished = true; - $this.data("validation" + validator.validatorName + "Message", validator.message); - // Timeout is set to avoid problems with the events being considered 'already fired' - setTimeout(function () { - $this.trigger("revalidate.validation"); - }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst + }, + ajax: { + name: "ajax", + init: function ($this, name) { + return { + validatorName: name, + url: $this.data("validation" + name + "Ajax"), + lastValue: $this.val(), + lastValid: true, + lastFinished: true + }; + }, + validate: function ($this, value, validator) { + if ("" + validator.lastValue === "" + value && validator.lastFinished === true) { + return validator.lastValid === false; + } + + if (validator.lastFinished === true) { + validator.lastValue = value; + validator.lastValid = true; + validator.lastFinished = false; + $.ajax({ + url: validator.url, + data: "value=" + encodeURIComponent(value) + "&field=" + $this.attr("name"), + dataType: "json", + success: function (data) { + if ("" + validator.lastValue === "" + data.value) { + validator.lastValid = !!(data.valid); + if (data.message) { + validator.message = data.message; + } + validator.lastFinished = true; + $this.data("validation" + validator.validatorName + "Message", validator.message); + // Timeout is set to avoid problems with the events being considered 'already fired' + setTimeout(function () { + $this.trigger("revalidate.validation"); + }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst + } + }, + failure: function () { + validator.lastValid = true; + validator.message = "ajax call failed"; + validator.lastFinished = true; + $this.data("validation" + validator.validatorName + "Message", validator.message); + // Timeout is set to avoid problems with the events being considered 'already fired' + setTimeout(function () { + $this.trigger("revalidate.validation"); + }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst + } + }); + } + + return false; + } - }, - failure: function () { - validator.lastValid = true; - validator.message = "ajax call failed"; - validator.lastFinished = true; - $this.data("validation" + validator.validatorName + "Message", validator.message); - // Timeout is set to avoid problems with the events being considered 'already fired' - setTimeout(function () { - $this.trigger("revalidate.validation"); - }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst - } - }); - } - - return false; + }, + regex: { + name: "regex", + init: function ($this, name) { + var result = {}; + var regexString = $this.data("validation" + name + "Regex"); + result.regex = regexFromString(regexString); + if (regexString === undefined) { + $.error("Can't find regex for '" + name + "' validator on '" + $this.attr("name") + "'"); + } - } - }, - regex: { - name: "regex", - init: function ($this, name) { - var result = {}; - var regexString = $this.data("validation" + name + "Regex"); - result.regex = regexFromString(regexString); - if (regexString === undefined) { - $.error("Can't find regex for '" + name + "' validator on '" + $this.attr("name") + "'"); - } - - var message = "Not in the expected format"; - if ($this.data("validation" + name + "Message")) { - message = $this.data("validation" + name + "Message"); - } - - result.message = message; - - result.originalName = name; - return result; - }, - validate: function ($this, value, validator) { - return (!validator.regex.test(value) && ! validator.negative) || - (validator.regex.test(value) && validator.negative); - } - }, - email: { - name: "email", - init: function ($this, name) { - var result = {}; - result.regex = regexFromString('[a-zA-Z0-9.!#$%&\u2019*+/=?^_`{|}~-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}'); - - var message = "Not a valid email address"; - if ($this.data("validation" + name + "Message")) { - message = $this.data("validation" + name + "Message"); - } - - result.message = message; - - result.originalName = name; - return result; - }, - validate: function ($this, value, validator) { - return (!validator.regex.test(value) && ! validator.negative) || - (validator.regex.test(value) && validator.negative); - } - }, - required: { - name: "required", - init: function ($this, name) { - var message = "This is required"; - if ($this.data("validation" + name + "Message")) { - message = $this.data("validation" + name + "Message"); - } - - return {message: message, includeEmpty: true}; - }, - validate: function ($this, value, validator) { - return !!( - (value.length === 0 && !validator.negative) || - (value.length > 0 && validator.negative) - ); - }, - blockSubmit: true - }, - match: { - name: "match", - init: function ($this, name) { - var elementName = $this.data("validation" + name + "Match"); - var $form = $this.parents("form").first(); - var $element = $form.find("[name=\"" + elementName + "\"]").first(); - $element.bind("validation.validation", function () { - $this.trigger("revalidate.validation", {submitting: true}); - }); - var result = {}; - result.element = $element; - - if ($element.length === 0) { - $.error("Can't find field '" + elementName + "' to match '" + $this.attr("name") + "' against in '" + name + "' validator"); - } - - var message = "Must match"; - var $label = null; - if (($label = $form.find("label[for=\"" + elementName + "\"]")).length) { - message += " '" + $label.text() + "'"; - } else if (($label = $element.parents(".control-group").first().find("label")).length) { - message += " '" + $label.first().text() + "'"; - } - - if ($this.data("validation" + name + "Message")) { - message = $this.data("validation" + name + "Message"); - } - - result.message = message; - - return result; - }, - validate: function ($this, value, validator) { - return (value !== validator.element.val() && ! validator.negative) || - (value === validator.element.val() && validator.negative); - }, - blockSubmit: true, - includeEmpty: true - }, - max: { - name: "max", - init: function ($this, name) { - var result = {}; - - result.max = $this.data("validation" + name + "Max"); - - result.message = "Too high: Maximum of '" + result.max + "'"; - if ($this.data("validation" + name + "Message")) { - result.message = $this.data("validation" + name + "Message"); - } - - return result; - }, - validate: function ($this, value, validator) { - return (parseFloat(value, 10) > parseFloat(validator.max, 10) && ! validator.negative) || - (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative); - } - }, - min: { - name: "min", - init: function ($this, name) { - var result = {}; - - result.min = $this.data("validation" + name + "Min"); - - result.message = "Too low: Minimum of '" + result.min + "'"; - if ($this.data("validation" + name + "Message")) { - result.message = $this.data("validation" + name + "Message"); - } - - return result; - }, - validate: function ($this, value, validator) { - return (parseFloat(value) < parseFloat(validator.min) && ! validator.negative) || - (parseFloat(value) >= parseFloat(validator.min) && validator.negative); - } - }, - maxlength: { - name: "maxlength", - init: function ($this, name) { - var result = {}; - - result.maxlength = $this.data("validation" + name + "Maxlength"); - - result.message = "Too long: Maximum of '" + result.maxlength + "' characters"; - if ($this.data("validation" + name + "Message")) { - result.message = $this.data("validation" + name + "Message"); - } - - return result; - }, - validate: function ($this, value, validator) { - return ((value.length > validator.maxlength) && ! validator.negative) || - ((value.length <= validator.maxlength) && validator.negative); - } - }, - minlength: { - name: "minlength", - init: function ($this, name) { - var result = {}; - - result.minlength = $this.data("validation" + name + "Minlength"); - - result.message = "Too short: Minimum of '" + result.minlength + "' characters"; - if ($this.data("validation" + name + "Message")) { - result.message = $this.data("validation" + name + "Message"); - } - - return result; - }, - validate: function ($this, value, validator) { - return ((value.length < validator.minlength) && ! validator.negative) || - ((value.length >= validator.minlength) && validator.negative); - } - }, - maxchecked: { - name: "maxchecked", - init: function ($this, name) { - var result = {}; - - var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); - elements.bind("change.validation click.validation", function () { - $this.trigger("revalidate.validation", {includeEmpty: true}); - }); - - result.elements = elements; - result.maxchecked = $this.data("validation" + name + "Maxchecked"); - - var message = "Too many: Max '" + result.maxchecked + "' checked"; - if ($this.data("validation" + name + "Message")) { - message = $this.data("validation" + name + "Message"); - } - result.message = message; - - return result; - }, - validate: function ($this, value, validator) { - return (validator.elements.filter(":checked").length > validator.maxchecked && ! validator.negative) || - (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative); - }, - blockSubmit: true - }, - minchecked: { - name: "minchecked", - init: function ($this, name) { - var result = {}; - - var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); - elements.bind("change.validation click.validation", function () { - $this.trigger("revalidate.validation", {includeEmpty: true}); - }); - - result.elements = elements; - result.minchecked = $this.data("validation" + name + "Minchecked"); - - var message = "Too few: Min '" + result.minchecked + "' checked"; - if ($this.data("validation" + name + "Message")) { - message = $this.data("validation" + name + "Message"); - } - result.message = message; - - return result; - }, - validate: function ($this, value, validator) { - return (validator.elements.filter(":checked").length < validator.minchecked && ! validator.negative) || - (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative); - }, - blockSubmit: true, - includeEmpty: true - }, - number: { - name: "number", - init: function ($this, name) { - var result = {}; - result.step = 1; - if ($this.attr("step")) { - result.step = $this.attr("step"); - } - if ($this.data("validation" + name + "Step")) { - result.step = $this.data("validation" + name + "Step"); - } - - result.decimal = "."; - if ($this.data("validation" + name + "Decimal")) { - result.decimal = $this.data("validation" + name + "Decimal"); - } - - result.thousands = ""; - if ($this.data("validation" + name + "Thousands")) { - result.thousands = $this.data("validation" + name + "Thousands"); - } - - result.regex = regexFromString("([+-]?\\d+(\\" + result.decimal + "\\d+)?)?"); - - result.message = "Must be a number"; - var dataMessage = $this.data("validation" + name + "Message"); - if (dataMessage) { - result.message = dataMessage; - } - - return result; - }, - validate: function ($this, value, validator) { - var globalValue = value.replace(validator.decimal, ".").replace(validator.thousands, ""); - var multipliedValue = parseFloat(globalValue); - var multipliedStep = parseFloat(validator.step); - while (multipliedStep % 1 !== 0) { - /* thanks to @jkey #57 */ - multipliedStep = parseFloat(multipliedStep.toPrecision(12)) * 10; - multipliedValue = parseFloat(multipliedValue.toPrecision(12)) * 10; - } - var regexResult = validator.regex.test(value); - var stepResult = parseFloat(multipliedValue) % parseFloat(multipliedStep) === 0; - var typeResult = !isNaN(parseFloat(globalValue)) && isFinite(globalValue); - var result = !(regexResult && stepResult && typeResult); - return result; + var message = "Not in the expected format"; + if ($this.data("validation" + name + "Message")) { + message = $this.data("validation" + name + "Message"); + } + + result.message = message; + + result.originalName = name; + return result; + }, + validate: function ($this, value, validator) { + return (!validator.regex.test(value) && !validator.negative) || + (validator.regex.test(value) && validator.negative); + } + }, + email: { + name: "email", + init: function ($this, name) { + var result = {}; + result.regex = regexFromString('[a-zA-Z0-9.!#$%&\u2019*+/=?^_`{|}~-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}'); + + var message = "Not a valid email address"; + if ($this.data("validation" + name + "Message")) { + message = $this.data("validation" + name + "Message"); + } + + result.message = message; + + result.originalName = name; + return result; + }, + validate: function ($this, value, validator) { + return (!validator.regex.test(value) && !validator.negative) || + (validator.regex.test(value) && validator.negative); + } + }, + required: { + name: "required", + init: function ($this, name) { + var message = "This is required"; + if ($this.data("validation" + name + "Message")) { + message = $this.data("validation" + name + "Message"); + } + + return {message: message, includeEmpty: true}; + }, + validate: function ($this, value, validator) { + return !!( + (value.length === 0 && !validator.negative) || + (value.length > 0 && validator.negative) + ); + }, + blockSubmit: true + }, + match: { + name: "match", + init: function ($this, name) { + var elementName = $this.data("validation" + name + "Match"); + var $form = $this.parents("form").first(); + var $element = $form.find("[name=\"" + elementName + "\"]").first(); + $element.bind("validation.validation", function () { + $this.trigger("revalidate.validation", {submitting: true}); + }); + var result = {}; + result.element = $element; + + if ($element.length === 0) { + $.error("Can't find field '" + elementName + "' to match '" + $this.attr("name") + "' against in '" + name + "' validator"); + } + + var message = "Must match"; + var $label = null; + if (($label = $form.find("label[for=\"" + elementName + "\"]")).length) { + message += " '" + $label.text() + "'"; + } else if (($label = $element.parents(".form-group").first().find("label")).length) { + message += " '" + $label.first().text() + "'"; + } + + if ($this.data("validation" + name + "Message")) { + message = $this.data("validation" + name + "Message"); + } + + result.message = message; + + return result; + }, + validate: function ($this, value, validator) { + return (value !== validator.element.val() && !validator.negative) || + (value === validator.element.val() && validator.negative); + }, + blockSubmit: true, + includeEmpty: true + }, + max: { + name: "max", + init: function ($this, name) { + var result = {}; + + result.max = $this.data("validation" + name + "Max"); + + result.message = "Too high: Maximum of '" + result.max + "'"; + if ($this.data("validation" + name + "Message")) { + result.message = $this.data("validation" + name + "Message"); + } + + return result; + }, + validate: function ($this, value, validator) { + return (parseFloat(value, 10) > parseFloat(validator.max, 10) && !validator.negative) || + (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative); + } + }, + min: { + name: "min", + init: function ($this, name) { + var result = {}; + + result.min = $this.data("validation" + name + "Min"); + + result.message = "Too low: Minimum of '" + result.min + "'"; + if ($this.data("validation" + name + "Message")) { + result.message = $this.data("validation" + name + "Message"); + } + + return result; + }, + validate: function ($this, value, validator) { + return (parseFloat(value) < parseFloat(validator.min) && !validator.negative) || + (parseFloat(value) >= parseFloat(validator.min) && validator.negative); + } + }, + maxlength: { + name: "maxlength", + init: function ($this, name) { + var result = {}; + + result.maxlength = $this.data("validation" + name + "Maxlength"); + + result.message = "Too long: Maximum of '" + result.maxlength + "' characters"; + if ($this.data("validation" + name + "Message")) { + result.message = $this.data("validation" + name + "Message"); + } + + return result; + }, + validate: function ($this, value, validator) { + return ((value.length > validator.maxlength) && !validator.negative) || + ((value.length <= validator.maxlength) && validator.negative); + } + }, + minlength: { + name: "minlength", + init: function ($this, name) { + var result = {}; + + result.minlength = $this.data("validation" + name + "Minlength"); + + result.message = "Too short: Minimum of '" + result.minlength + "' characters"; + if ($this.data("validation" + name + "Message")) { + result.message = $this.data("validation" + name + "Message"); + } + + return result; + }, + validate: function ($this, value, validator) { + return ((value.length < validator.minlength) && !validator.negative) || + ((value.length >= validator.minlength) && validator.negative); + } + }, + maxchecked: { + name: "maxchecked", + init: function ($this, name) { + var result = {}; + + var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); + elements.bind("change.validation click.validation", function () { + $this.trigger("revalidate.validation", {includeEmpty: true}); + }); + + result.elements = elements; + result.maxchecked = $this.data("validation" + name + "Maxchecked"); + + var message = "Too many: Max '" + result.maxchecked + "' checked"; + if ($this.data("validation" + name + "Message")) { + message = $this.data("validation" + name + "Message"); + } + result.message = message; + + return result; + }, + validate: function ($this, value, validator) { + return (validator.elements.filter(":checked").length > validator.maxchecked && !validator.negative) || + (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative); + }, + blockSubmit: true + }, + minchecked: { + name: "minchecked", + init: function ($this, name) { + var result = {}; + + var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); + elements.bind("change.validation click.validation", function () { + $this.trigger("revalidate.validation", {includeEmpty: true}); + }); + + result.elements = elements; + result.minchecked = $this.data("validation" + name + "Minchecked"); + + var message = "Too few: Min '" + result.minchecked + "' checked"; + if ($this.data("validation" + name + "Message")) { + message = $this.data("validation" + name + "Message"); + } + result.message = message; + + return result; + }, + validate: function ($this, value, validator) { + return (validator.elements.filter(":checked").length < validator.minchecked && !validator.negative) || + (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative); + }, + blockSubmit: true, + includeEmpty: true + }, + number: { + name: "number", + init: function ($this, name) { + var result = {}; + result.step = 1; + if ($this.attr("step")) { + result.step = $this.attr("step"); + } + if ($this.data("validation" + name + "Step")) { + result.step = $this.data("validation" + name + "Step"); + } + + result.decimal = "."; + if ($this.data("validation" + name + "Decimal")) { + result.decimal = $this.data("validation" + name + "Decimal"); + } + + result.thousands = ""; + if ($this.data("validation" + name + "Thousands")) { + result.thousands = $this.data("validation" + name + "Thousands"); + } + + result.regex = regexFromString("([+-]?\\d+(\\" + result.decimal + "\\d+)?)?"); + + result.message = "Must be a number"; + var dataMessage = $this.data("validation" + name + "Message"); + if (dataMessage) { + result.message = dataMessage; + } + + return result; + }, + validate: function ($this, value, validator) { + var globalValue = value.replace(validator.decimal, ".").replace(validator.thousands, ""); + var multipliedValue = parseFloat(globalValue); + var multipliedStep = parseFloat(validator.step); + while (multipliedStep % 1 !== 0) { + /* thanks to @jkey #57 */ + multipliedStep = parseFloat(multipliedStep.toPrecision(12)) * 10; + multipliedValue = parseFloat(multipliedValue.toPrecision(12)) * 10; + } + var regexResult = validator.regex.test(value); + var stepResult = parseFloat(multipliedValue) % parseFloat(multipliedStep) === 0; + var typeResult = !isNaN(parseFloat(globalValue)) && isFinite(globalValue); + var result = !(regexResult && stepResult && typeResult); + return result; + }, + message: "Must be a number" + } }, - message: "Must be a number" - } - }, - builtInValidators: { - email: { - name: "Email", - type: "email" - }, - passwordagain: { - name: "Passwordagain", - type: "match", - match: "password", - message: "Does not match the given password" - }, - positive: { - name: "Positive", - type: "shortcut", - shortcut: "number,positivenumber" - }, - negative: { - name: "Negative", - type: "shortcut", - shortcut: "number,negativenumber" - }, - integer: { - name: "Integer", - type: "regex", - regex: "[+-]?\\d+", - message: "No decimal places allowed" - }, - positivenumber: { - name: "Positivenumber", - type: "min", - min: 0, - message: "Must be a positive number" - }, - negativenumber: { - name: "Negativenumber", - type: "max", - max: 0, - message: "Must be a negative number" - }, - required: { - name: "Required", - type: "required", - message: "This is required" - }, - checkone: { - name: "Checkone", - type: "minchecked", - minchecked: 1, - message: "Check at least one option" - }, - number: { - name: "Number", - type: "number", - decimal: ".", - step: "1" - }, - pattern: { - name: "Pattern", - type: "regex", - message: "Not in expected format" - } - } - }; - - var formatValidatorName = function (name) { - return name - .toLowerCase() - .replace( - /(^|\s)([a-z])/g , - function(m,p1,p2) { - return p1+p2.toUpperCase(); - } - ) - ; - }; - - var getValue = function ($this) { - // Extract the value we're talking about - var value = null; - var type = $this.attr("type"); - if (type === "checkbox") { - value = ($this.is(":checked") ? value : ""); - var checkboxParent = $this.parents("form").first() || $this.parents(".control-group").first(); - if (checkboxParent) { - value = checkboxParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) { return $(el).val(); }).toArray().join(","); - } - } - else if (type === "radio") { - value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? $this.val() : ""); - var radioParent = $this.parents("form").first() || $this.parents(".control-group").first(); - if (radioParent) { - value = radioParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) { return $(el).val(); }).toArray().join(","); - } - } - else { - value = $this.val(); + builtInValidators: { + email: { + name: "Email", + type: "email" + }, + passwordagain: { + name: "Passwordagain", + type: "match", + match: "password", + message: "Does not match the given password" + }, + positive: { + name: "Positive", + type: "shortcut", + shortcut: "number,positivenumber" + }, + negative: { + name: "Negative", + type: "shortcut", + shortcut: "number,negativenumber" + }, + integer: { + name: "Integer", + type: "regex", + regex: "[+-]?\\d+", + message: "No decimal places allowed" + }, + positivenumber: { + name: "Positivenumber", + type: "min", + min: 0, + message: "Must be a positive number" + }, + negativenumber: { + name: "Negativenumber", + type: "max", + max: 0, + message: "Must be a negative number" + }, + required: { + name: "Required", + type: "required", + message: "This is required" + }, + checkone: { + name: "Checkone", + type: "minchecked", + minchecked: 1, + message: "Check at least one option" + }, + number: { + name: "Number", + type: "number", + decimal: ".", + step: "1" + }, + pattern: { + name: "Pattern", + type: "regex", + message: "Not in expected format" + } + } + }; + + var formatValidatorName = function (name) { + return name + .toLowerCase() + .replace( + /(^|\s)([a-z])/g, + function (m, p1, p2) { + return p1 + p2.toUpperCase(); + } + ) + ; + }; + + var getValue = function ($this) { + // Extract the value we're talking about + var value = null; + var type = $this.attr("type"); + if (type === "checkbox") { + value = ($this.is(":checked") ? value : ""); + var checkboxParent = $this.parents("form").first() || $this.parents(".form-group").first(); + if (checkboxParent) { + value = checkboxParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) { + return $(el).val(); + }).toArray().join(","); + } + } + else if (type === "radio") { + value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? $this.val() : ""); + var radioParent = $this.parents("form").first() || $this.parents(".form-group").first(); + if (radioParent) { + value = radioParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) { + return $(el).val(); + }).toArray().join(","); + } + } else if (type === "number") { + if ($this[0].validity.valid) { + value = $this.val(); + } else { + if ($this[0].validity.badInput || $this[0].validity.stepMismatch) { + value = "NaN"; + } else { + value = ""; + } + } + } else { + value = $this.val(); + } + return value; + }; + + function regexFromString(inputstring) { + return new RegExp("^" + inputstring + "$"); } - return value; - }; - - function regexFromString(inputstring) { - return new RegExp("^" + inputstring + "$"); - } - - /** - * Thanks to Jason Bunting / Alex Nazarov via StackOverflow.com - * - * http://stackoverflow.com/a/4351575 - **/ -function executeFunctionByName(functionName, context /*, args */) { - var args = Array.prototype.slice.call(arguments, 2); - var namespaces = functionName.split("."); - var func = namespaces.pop(); - for (var i = 0; i < namespaces.length; i++) { - context = context[namespaces[i]]; + + /** + * Thanks to Jason Bunting / Alex Nazarov via StackOverflow.com + * + * http://stackoverflow.com/a/4351575 + **/ + function executeFunctionByName(functionName, context /*, args */) { + var args = Array.prototype.slice.call(arguments, 2); + var namespaces = functionName.split("."); + var func = namespaces.pop(); + for (var i = 0; i < namespaces.length; i++) { + context = context[namespaces[i]]; + } + return context[func].apply(context, args); } - return context[func].apply(context, args); -} -$.fn.jqBootstrapValidation = function( method ) { + $.fn.jqBootstrapValidation = function (method) { - if ( defaults.methods[method] ) { - return defaults.methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); - } else if ( typeof method === 'object' || ! method ) { - return defaults.methods.init.apply( this, arguments ); - } else { - $.error( 'Method ' + method + ' does not exist on jQuery.jqBootstrapValidation' ); - return null; - } + if (defaults.methods[method]) { + return defaults.methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (typeof method === 'object' || !method) { + return defaults.methods.init.apply(this, arguments); + } else { + $.error('Method ' + method + ' does not exist on jQuery.jqBootstrapValidation'); + return null; + } -}; + }; - $.jqBootstrapValidation = function (options) { - $(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments); - }; + $.jqBootstrapValidation = function (options) { + $(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this, arguments); + }; -})( jQuery ); \ No newline at end of file +})(jQuery); diff --git a/dist/jqBootstrapValidation-1.3.7.min.js b/dist/jqBootstrapValidation-1.3.7.min.js index c5d5236..28c195b 100644 --- a/dist/jqBootstrapValidation-1.3.7.min.js +++ b/dist/jqBootstrapValidation-1.3.7.min.js @@ -1,4 +1,4 @@ -/*! jqBootstrapValidation - v1.3.7 - 2013-05-07 +/*! jqBootstrapValidation - v1.3.7 - 2014-08-15 * http://reactiveraven.github.com/jqBootstrapValidation -* Copyright (c) 2013 David Godfrey; Licensed MIT */ -(function(e){function s(e){return new RegExp("^"+e+"$")}function o(e,t){var n=Array.prototype.slice.call(arguments,2),r=e.split("."),i=r.pop();for(var s=0;s0});u.trigger("submit.validation"),i.trigger("validationLostFocus.validation"),s.each(function(t,n){var i=e(n);if(i.hasClass("warning")||i.hasClass("error"))i.removeClass("warning").addClass("error"),r++}),r?(o.options.preventSubmit&&(t.preventDefault(),t.stopImmediatePropagation()),n.addClass("error"),e.isFunction(o.options.submitError)&&o.options.submitError(n,t,u.jqBootstrapValidation("collectErrors",!0))):(n.removeClass("error"),e.isFunction(o.options.submitSuccess)&&o.options.submitSuccess(n,t))}),this.each(function(){var n=e(this),s=n.parents(".control-group").first(),u=s.find(".help-block").first(),a=n.parents("form").first(),f=[];!u.length&&o.options.autoAdd&&o.options.autoAdd.helpBlocks&&(u=e('
'),s.find(".controls").append(u),t.push(u[0]));if(o.options.sniffHtml){var l;n.data("validationPatternPattern")&&n.attr("pattern",n.data("validationPatternPattern")),n.attr("pattern")!==undefined&&(l="Not in the expected format",n.data("validationPatternMessage")&&(l=n.data("validationPatternMessage")),n.data("validationPatternMessage",l),n.data("validationPatternRegex",n.attr("pattern")));if(n.attr("max")!==undefined||n.attr("aria-valuemax")!==undefined){var c=n.attr("max")!==undefined?n.attr("max"):n.attr("aria-valuemax");l="Too high: Maximum of '"+c+"'",n.data("validationMaxMessage")&&(l=n.data("validationMaxMessage")),n.data("validationMaxMessage",l),n.data("validationMaxMax",c)}if(n.attr("min")!==undefined||n.attr("aria-valuemin")!==undefined){var h=n.attr("min")!==undefined?n.attr("min"):n.attr("aria-valuemin");l="Too low: Minimum of '"+h+"'",n.data("validationMinMessage")&&(l=n.data("validationMinMessage")),n.data("validationMinMessage",l),n.data("validationMinMin",h)}n.attr("maxlength")!==undefined&&(l="Too long: Maximum of '"+n.attr("maxlength")+"' characters",n.data("validationMaxlengthMessage")&&(l=n.data("validationMaxlengthMessage")),n.data("validationMaxlengthMessage",l),n.data("validationMaxlengthMaxlength",n.attr("maxlength"))),n.attr("minlength")!==undefined&&(l="Too short: Minimum of '"+n.attr("minlength")+"' characters",n.data("validationMinlengthMessage")&&(l=n.data("validationMinlengthMessage")),n.data("validationMinlengthMessage",l),n.data("validationMinlengthMinlength",n.attr("minlength")));if(n.attr("required")!==undefined||n.attr("aria-required")!==undefined)l=o.builtInValidators.required.message,n.data("validationRequiredMessage")&&(l=n.data("validationRequiredMessage")),n.data("validationRequiredMessage",l);if(n.attr("type")!==undefined&&n.attr("type").toLowerCase()==="number"){l=o.validatorTypes.number.message,n.data("validationNumberMessage")&&(l=n.data("validationNumberMessage")),n.data("validationNumberMessage",l);var p=o.validatorTypes.number.step;n.data("validationNumberStep")&&(p=n.data("validationNumberStep")),n.data("validationNumberStep",p);var d=o.validatorTypes.number.decimal;n.data("validationNumberDecimal")&&(d=n.data("validationNumberDecimal")),n.data("validationNumberDecimal",d)}n.attr("type")!==undefined&&n.attr("type").toLowerCase()==="email"&&(l="Not a valid email address",n.data("validationEmailMessage")&&(l=n.data("validationEmailMessage")),n.data("validationEmailMessage",l)),n.attr("minchecked")!==undefined&&(l="Not enough options checked; Minimum of '"+n.attr("minchecked")+"' required",n.data("validationMincheckedMessage")&&(l=n.data("validationMincheckedMessage")),n.data("validationMincheckedMessage",l),n.data("validationMincheckedMinchecked",n.attr("minchecked"))),n.attr("maxchecked")!==undefined&&(l="Too many options checked; Maximum of '"+n.attr("maxchecked")+"' required",n.data("validationMaxcheckedMessage")&&(l=n.data("validationMaxcheckedMessage")),n.data("validationMaxcheckedMessage",l),n.data("validationMaxcheckedMaxchecked",n.attr("maxchecked")))}n.data("validation")!==undefined&&(f=n.data("validation").split(",")),e.each(n.data(),function(e,t){var n=e.replace(/([A-Z])/g,",$1").split(",");n[0]==="validation"&&n[1]&&f.push(n[1])});var v=f,m=[],g=function(e,t){f[e]=r(t)},y=function(t,i){if(n.data("validation"+i+"Shortcut")!==undefined)e.each(n.data("validation"+i+"Shortcut").split(","),function(e,t){m.push(t)});else if(o.builtInValidators[i.toLowerCase()]){var s=o.builtInValidators[i.toLowerCase()];s.type.toLowerCase()==="shortcut"&&e.each(s.shortcut.split(","),function(e,t){t=r(t),m.push(t),f.push(t)})}};do e.each(f,g),f=e.unique(f),m=[],e.each(v,y),v=m;while(v.length>0);var b={};e.each(f,function(t,i){var s=n.data("validation"+i+"Message"),u=!!s,a=!1;s||(s="'"+i+"' validation failed "),e.each(o.validatorTypes,function(t,o){b[t]===undefined&&(b[t]=[]);if(!a&&n.data("validation"+i+r(o.name))!==undefined){var f=o.init(n,i);u&&(f.message=s),b[t].push(e.extend(!0,{name:r(o.name),message:s},f)),a=!0}});if(!a&&o.builtInValidators[i.toLowerCase()]){var f=e.extend(!0,{},o.builtInValidators[i.toLowerCase()]);u&&(f.message=s);var l=f.type.toLowerCase();l==="shortcut"?a=!0:e.each(o.validatorTypes,function(t,s){b[t]===undefined&&(b[t]=[]),!a&&l===t.toLowerCase()&&(n.data("validation"+i+r(s.name),f[s.name.toLowerCase()]),b[l].push(e.extend(f,s.init(n,i))),a=!0)})}a||e.error("Cannot find validation info for '"+i+"'")}),u.data("original-contents",u.data("original-contents")?u.data("original-contents"):u.html()),u.data("original-role",u.data("original-role")?u.data("original-role"):u.attr("role")),s.data("original-classes",s.data("original-clases")?s.data("original-classes"):s.attr("class")),n.data("original-aria-invalid",n.data("original-aria-invalid")?n.data("original-aria-invalid"):n.attr("aria-invalid")),n.bind("validation.validation",function(t,r){var s=i(n),u=[];return e.each(b,function(t,i){(s||s.length||r&&r.includeEmpty||!!o.validatorTypes[t].includeEmpty||!!o.validatorTypes[t].blockSubmit&&r&&!!r.submitting)&&e.each(i,function(e,r){o.validatorTypes[t].validate(n,s,r)&&u.push(r.message)})}),u}),n.bind("getValidators.validation",function(){return b});var w=0;e.each(b,function(e,t){w+=t.length}),n.bind("getValidatorCount.validation",function(){return w}),n.bind("submit.validation",function(){return n.triggerHandler("change.validation",{submitting:!0})}),n.bind((o.options.bindEvents.length>0?o.options.bindEvents:["keyup","focus","blur","click","keydown","keypress","change"]).concat(["revalidate"]).join(".validation ")+".validation",function(t,r){var f=i(n),l=[];r&&!!r.submitting?s.data("jqbvIsSubmitting",!0):t.type!=="revalidate"&&s.data("jqbvIsSubmitting",!1);var c=!!s.data("jqbvIsSubmitting");s.find("input,textarea,select").each(function(t,i){var s=l.length;e.each(e(i).triggerHandler("validation.validation",r),function(e,t){l.push(t)});if(l.length>s)e(i).attr("aria-invalid","true");else{var o=n.data("original-aria-invalid");e(i).attr("aria-invalid",o!==undefined?o:!1)}}),a.find("input,select,textarea").not(n).not('[name="'+n.attr("name")+'"]').trigger("validationLostFocus.validation"),l=e.unique(l.sort()),l.length?(s.removeClass("success error warning").addClass(c?"error":"warning"),o.options.semanticallyStrict&&l.length===1?u.html(l[0]+(o.options.prependExistingHelpBlock?u.data("original-contents"):"")):u.html('
  • '+l.join("
  • ")+"
"+(o.options.prependExistingHelpBlock?u.data("original-contents"):""))):(s.removeClass("warning error success"),f.length>0&&s.addClass("success"),u.html(u.data("original-contents"))),t.type==="blur"&&s.removeClass("success")}),n.bind("validationLostFocus.validation",function(){s.removeClass("success")})})},destroy:function(){return this.each(function(){var n=e(this),r=n.parents(".control-group").first(),i=r.find(".help-block").first(),s=n.parents("form").first();n.unbind(".validation"),s.unbind(".validationSubmit"),i.html(i.data("original-contents")),r.attr("class",r.data("original-classes")),n.attr("aria-invalid",n.data("original-aria-invalid")),i.attr("role",n.data("original-role")),e.inArray(i[0],t)>-1&&i.remove()})},collectErrors:function(t){var n={};return this.each(function(t,r){var i=e(r),s=i.attr("name"),o=i.triggerHandler("validation.validation",{includeEmpty:!0});n[s]=e.extend(!0,o,n[s])}),e.each(n,function(e,t){t.length===0&&delete n[e]}),n},hasErrors:function(){var t=[];return this.find("input,select,textarea").add(this).each(function(n,r){t=t.concat(e(r).triggerHandler("getValidators.validation")?e(r).triggerHandler("validation.validation",{submitting:!0}):[])}),t.length>0},override:function(t){n=e.extend(!0,n,t)}},validatorTypes:{callback:{name:"callback",init:function(e,t){var n={validatorName:t,callback:e.data("validation"+t+"Callback"),lastValue:e.val(),lastValid:!0,lastFinished:!0},r="Not valid";return e.data("validation"+t+"Message")&&(r=e.data("validation"+t+"Message")),n.message=r,n},validate:function(e,t,n){if(n.lastValue===t&&n.lastFinished)return!n.lastValid;if(n.lastFinished===!0){n.lastValue=t,n.lastValid=!0,n.lastFinished=!1;var r=n,i=e;o(n.callback,window,e,t,function(t){r.lastValue===t.value&&(r.lastValid=t.valid,t.message&&(r.message=t.message),r.lastFinished=!0,i.data("validation"+r.validatorName+"Message",r.message),setTimeout(function(){!e.is(":focus")&&e.parents("form").first().data("jqbvIsSubmitting")?i.trigger("blur.validation"):i.trigger("revalidate.validation")},1))})}return!1}},ajax:{name:"ajax",init:function(e,t){return{validatorName:t,url:e.data("validation"+t+"Ajax"),lastValue:e.val(),lastValid:!0,lastFinished:!0}},validate:function(t,n,r){return""+r.lastValue==""+n&&r.lastFinished===!0?r.lastValid===!1:(r.lastFinished===!0&&(r.lastValue=n,r.lastValid=!0,r.lastFinished=!1,e.ajax({url:r.url,data:"value="+encodeURIComponent(n)+"&field="+t.attr("name"),dataType:"json",success:function(e){""+r.lastValue==""+e.value&&(r.lastValid=!!e.valid,e.message&&(r.message=e.message),r.lastFinished=!0,t.data("validation"+r.validatorName+"Message",r.message),setTimeout(function(){t.trigger("revalidate.validation")},1))},failure:function(){r.lastValid=!0,r.message="ajax call failed",r.lastFinished=!0,t.data("validation"+r.validatorName+"Message",r.message),setTimeout(function(){t.trigger("revalidate.validation")},1)}})),!1)}},regex:{name:"regex",init:function(t,n){var r={},i=t.data("validation"+n+"Regex");r.regex=s(i),i===undefined&&e.error("Can't find regex for '"+n+"' validator on '"+t.attr("name")+"'");var o="Not in the expected format";return t.data("validation"+n+"Message")&&(o=t.data("validation"+n+"Message")),r.message=o,r.originalName=n,r},validate:function(e,t,n){return!n.regex.test(t)&&!n.negative||n.regex.test(t)&&n.negative}},email:{name:"email",init:function(e,t){var n={};n.regex=s("[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}");var r="Not a valid email address";return e.data("validation"+t+"Message")&&(r=e.data("validation"+t+"Message")),n.message=r,n.originalName=t,n},validate:function(e,t,n){return!n.regex.test(t)&&!n.negative||n.regex.test(t)&&n.negative}},required:{name:"required",init:function(e,t){var n="This is required";return e.data("validation"+t+"Message")&&(n=e.data("validation"+t+"Message")),{message:n,includeEmpty:!0}},validate:function(e,t,n){return!!(t.length===0&&!n.negative||t.length>0&&n.negative)},blockSubmit:!0},match:{name:"match",init:function(t,n){var r=t.data("validation"+n+"Match"),i=t.parents("form").first(),s=i.find('[name="'+r+'"]').first();s.bind("validation.validation",function(){t.trigger("revalidate.validation",{submitting:!0})});var o={};o.element=s,s.length===0&&e.error("Can't find field '"+r+"' to match '"+t.attr("name")+"' against in '"+n+"' validator");var u="Must match",a=null;return(a=i.find('label[for="'+r+'"]')).length?u+=" '"+a.text()+"'":(a=s.parents(".control-group").first().find("label")).length&&(u+=" '"+a.first().text()+"'"),t.data("validation"+n+"Message")&&(u=t.data("validation"+n+"Message")),o.message=u,o},validate:function(e,t,n){return t!==n.element.val()&&!n.negative||t===n.element.val()&&n.negative},blockSubmit:!0,includeEmpty:!0},max:{name:"max",init:function(e,t){var n={};return n.max=e.data("validation"+t+"Max"),n.message="Too high: Maximum of '"+n.max+"'",e.data("validation"+t+"Message")&&(n.message=e.data("validation"+t+"Message")),n},validate:function(e,t,n){return parseFloat(t,10)>parseFloat(n.max,10)&&!n.negative||parseFloat(t,10)<=parseFloat(n.max,10)&&n.negative}},min:{name:"min",init:function(e,t){var n={};return n.min=e.data("validation"+t+"Min"),n.message="Too low: Minimum of '"+n.min+"'",e.data("validation"+t+"Message")&&(n.message=e.data("validation"+t+"Message")),n},validate:function(e,t,n){return parseFloat(t)=parseFloat(n.min)&&n.negative}},maxlength:{name:"maxlength",init:function(e,t){var n={};return n.maxlength=e.data("validation"+t+"Maxlength"),n.message="Too long: Maximum of '"+n.maxlength+"' characters",e.data("validation"+t+"Message")&&(n.message=e.data("validation"+t+"Message")),n},validate:function(e,t,n){return t.length>n.maxlength&&!n.negative||t.length<=n.maxlength&&n.negative}},minlength:{name:"minlength",init:function(e,t){var n={};return n.minlength=e.data("validation"+t+"Minlength"),n.message="Too short: Minimum of '"+n.minlength+"' characters",e.data("validation"+t+"Message")&&(n.message=e.data("validation"+t+"Message")),n},validate:function(e,t,n){return t.length=n.minlength&&n.negative}},maxchecked:{name:"maxchecked",init:function(e,t){var n={},r=e.parents("form").first().find('[name="'+e.attr("name")+'"]');r.bind("change.validation click.validation",function(){e.trigger("revalidate.validation",{includeEmpty:!0})}),n.elements=r,n.maxchecked=e.data("validation"+t+"Maxchecked");var i="Too many: Max '"+n.maxchecked+"' checked";return e.data("validation"+t+"Message")&&(i=e.data("validation"+t+"Message")),n.message=i,n},validate:function(e,t,n){return n.elements.filter(":checked").length>n.maxchecked&&!n.negative||n.elements.filter(":checked").length<=n.maxchecked&&n.negative},blockSubmit:!0},minchecked:{name:"minchecked",init:function(e,t){var n={},r=e.parents("form").first().find('[name="'+e.attr("name")+'"]');r.bind("change.validation click.validation",function(){e.trigger("revalidate.validation",{includeEmpty:!0})}),n.elements=r,n.minchecked=e.data("validation"+t+"Minchecked");var i="Too few: Min '"+n.minchecked+"' checked";return e.data("validation"+t+"Message")&&(i=e.data("validation"+t+"Message")),n.message=i,n},validate:function(e,t,n){return n.elements.filter(":checked").length=n.minchecked&&n.negative},blockSubmit:!0,includeEmpty:!0},number:{name:"number",init:function(e,t){var n={};n.step=1,e.attr("step")&&(n.step=e.attr("step")),e.data("validation"+t+"Step")&&(n.step=e.data("validation"+t+"Step")),n.decimal=".",e.data("validation"+t+"Decimal")&&(n.decimal=e.data("validation"+t+"Decimal")),n.thousands="",e.data("validation"+t+"Thousands")&&(n.thousands=e.data("validation"+t+"Thousands")),n.regex=s("([+-]?\\d+(\\"+n.decimal+"\\d+)?)?"),n.message="Must be a number";var r=e.data("validation"+t+"Message");return r&&(n.message=r),n},validate:function(e,t,n){var r=t.replace(n.decimal,".").replace(n.thousands,""),i=parseFloat(r),s=parseFloat(n.step);while(s%1!==0)s=parseFloat(s.toPrecision(12))*10,i=parseFloat(i.toPrecision(12))*10;var o=n.regex.test(t),u=parseFloat(i)%parseFloat(s)===0,a=!isNaN(parseFloat(r))&&isFinite(r),f=!(o&&u&&a);return f},message:"Must be a number"}},builtInValidators:{email:{name:"Email",type:"email"},passwordagain:{name:"Passwordagain",type:"match",match:"password",message:"Does not match the given password"},positive:{name:"Positive",type:"shortcut",shortcut:"number,positivenumber"},negative:{name:"Negative",type:"shortcut",shortcut:"number,negativenumber"},integer:{name:"Integer",type:"regex",regex:"[+-]?\\d+",message:"No decimal places allowed"},positivenumber:{name:"Positivenumber",type:"min",min:0,message:"Must be a positive number"},negativenumber:{name:"Negativenumber",type:"max",max:0,message:"Must be a negative number"},required:{name:"Required",type:"required",message:"This is required"},checkone:{name:"Checkone",type:"minchecked",minchecked:1,message:"Check at least one option"},number:{name:"Number",type:"number",decimal:".",step:"1"},pattern:{name:"Pattern",type:"regex",message:"Not in expected format"}}},r=function(e){return e.toLowerCase().replace(/(^|\s)([a-z])/g,function(e,t,n){return t+n.toUpperCase()})},i=function(t){var n=null,r=t.attr("type");if(r==="checkbox"){n=t.is(":checked")?n:"";var i=t.parents("form").first()||t.parents(".control-group").first();i&&(n=i.find("input[name='"+t.attr("name")+"']:checked").map(function(t,n){return e(n).val()}).toArray().join(","))}else if(r==="radio"){n=e('input[name="'+t.attr("name")+'"]:checked').length>0?t.val():"";var s=t.parents("form").first()||t.parents(".control-group").first();s&&(n=s.find("input[name='"+t.attr("name")+"']:checked").map(function(t,n){return e(n).val()}).toArray().join(","))}else n=t.val();return n};e.fn.jqBootstrapValidation=function(t){return n.methods[t]?n.methods[t].apply(this,Array.prototype.slice.call(arguments,1)):typeof t=="object"||!t?n.methods.init.apply(this,arguments):(e.error("Method "+t+" does not exist on jQuery.jqBootstrapValidation"),null)},e.jqBootstrapValidation=function(t){e(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments)}})(jQuery); \ No newline at end of file +* Copyright (c) 2014 David Godfrey; Licensed MIT */ +!function(a){function b(a){return new RegExp("^"+a+"$")}function c(a,b){for(var c=Array.prototype.slice.call(arguments,2),d=a.split("."),e=d.pop(),f=0;f0});h.trigger("submit.validation"),f.trigger("validationLostFocus.validation"),g.each(function(b,c){var d=a(c);(d.hasClass("warning")||d.hasClass("error"))&&(d.removeClass("warning").addClass("error"),e++)}),e?(c.options.preventSubmit&&(b.preventDefault(),b.stopImmediatePropagation()),d.addClass("error"),a.isFunction(c.options.submitError)&&c.options.submitError(d,b,h.jqBootstrapValidation("collectErrors",!0))):(d.removeClass("error"),a.isFunction(c.options.submitSuccess)&&c.options.submitSuccess(d,b))}),this.each(function(){var b=a(this),e=b.parents(".form-group").first(),h=e.find(".help-block").first(),i=b.parents("form").first(),j=[];if(!h.length&&c.options.autoAdd&&c.options.autoAdd.helpBlocks&&(h=a('
'),e.find(".controls").append(h),d.push(h[0])),c.options.sniffHtml){var k;if(b.data("validationPatternPattern")&&b.attr("pattern",b.data("validationPatternPattern")),void 0!==b.attr("pattern")&&(k="Not in the expected format",b.data("validationPatternMessage")&&(k=b.data("validationPatternMessage")),b.data("validationPatternMessage",k),b.data("validationPatternRegex",b.attr("pattern"))),void 0!==b.attr("max")||void 0!==b.attr("aria-valuemax")){var l=b.attr(void 0!==b.attr("max")?"max":"aria-valuemax");k="Too high: Maximum of '"+l+"'",b.data("validationMaxMessage")&&(k=b.data("validationMaxMessage")),b.data("validationMaxMessage",k),b.data("validationMaxMax",l)}if(void 0!==b.attr("min")||void 0!==b.attr("aria-valuemin")){var m=b.attr(void 0!==b.attr("min")?"min":"aria-valuemin");k="Too low: Minimum of '"+m+"'",b.data("validationMinMessage")&&(k=b.data("validationMinMessage")),b.data("validationMinMessage",k),b.data("validationMinMin",m)}if(void 0!==b.attr("maxlength")&&(k="Too long: Maximum of '"+b.attr("maxlength")+"' characters",b.data("validationMaxlengthMessage")&&(k=b.data("validationMaxlengthMessage")),b.data("validationMaxlengthMessage",k),b.data("validationMaxlengthMaxlength",b.attr("maxlength"))),void 0!==b.attr("minlength")&&(k="Too short: Minimum of '"+b.attr("minlength")+"' characters",b.data("validationMinlengthMessage")&&(k=b.data("validationMinlengthMessage")),b.data("validationMinlengthMessage",k),b.data("validationMinlengthMinlength",b.attr("minlength"))),(void 0!==b.attr("required")||void 0!==b.attr("aria-required"))&&(k=c.builtInValidators.required.message,b.data("validationRequiredMessage")&&(k=b.data("validationRequiredMessage")),b.data("validationRequiredMessage",k)),void 0!==b.attr("type")&&"number"===b.attr("type").toLowerCase()){k=c.validatorTypes.number.message,b.data("validationNumberMessage")&&(k=b.data("validationNumberMessage")),b.data("validationNumberMessage",k);var n=c.validatorTypes.number.step;b.data("validationNumberStep")&&(n=b.data("validationNumberStep")),b.data("validationNumberStep",n);var o=c.validatorTypes.number.decimal;b.data("validationNumberDecimal")&&(o=b.data("validationNumberDecimal")),b.data("validationNumberDecimal",o)}void 0!==b.attr("type")&&"email"===b.attr("type").toLowerCase()&&(k="Not a valid email address",b.data("validationEmailMessage")&&(k=b.data("validationEmailMessage")),b.data("validationEmailMessage",k)),void 0!==b.attr("minchecked")&&(k="Not enough options checked; Minimum of '"+b.attr("minchecked")+"' required",b.data("validationMincheckedMessage")&&(k=b.data("validationMincheckedMessage")),b.data("validationMincheckedMessage",k),b.data("validationMincheckedMinchecked",b.attr("minchecked"))),void 0!==b.attr("maxchecked")&&(k="Too many options checked; Maximum of '"+b.attr("maxchecked")+"' required",b.data("validationMaxcheckedMessage")&&(k=b.data("validationMaxcheckedMessage")),b.data("validationMaxcheckedMessage",k),b.data("validationMaxcheckedMaxchecked",b.attr("maxchecked")))}void 0!==b.data("validation")&&(j=b.data("validation").split(",")),a.each(b.data(),function(a){var b=a.replace(/([A-Z])/g,",$1").split(",");"validation"===b[0]&&b[1]&&j.push(b[1])});var p=j,q=[],r=function(a,b){j[a]=f(b)},s=function(d,e){if(void 0!==b.data("validation"+e+"Shortcut"))a.each(b.data("validation"+e+"Shortcut").split(","),function(a,b){q.push(b)});else if(c.builtInValidators[e.toLowerCase()]){var g=c.builtInValidators[e.toLowerCase()];"shortcut"===g.type.toLowerCase()&&a.each(g.shortcut.split(","),function(a,b){b=f(b),q.push(b),j.push(b)})}};do a.each(j,r),j=a.unique(j),q=[],a.each(p,s),p=q;while(p.length>0);var t={};a.each(j,function(d,e){var g=b.data("validation"+e+"Message"),h=!!g,i=!1;if(g||(g="'"+e+"' validation failed "),a.each(c.validatorTypes,function(c,d){if(void 0===t[c]&&(t[c]=[]),!i&&void 0!==b.data("validation"+e+f(d.name))){var j=d.init(b,e);h&&(j.message=g),t[c].push(a.extend(!0,{name:f(d.name),message:g},j)),i=!0}}),!i&&c.builtInValidators[e.toLowerCase()]){var j=a.extend(!0,{},c.builtInValidators[e.toLowerCase()]);h&&(j.message=g);var k=j.type.toLowerCase();"shortcut"===k?i=!0:a.each(c.validatorTypes,function(c,d){void 0===t[c]&&(t[c]=[]),i||k!==c.toLowerCase()||(b.data("validation"+e+f(d.name),j[d.name.toLowerCase()]),t[k].push(a.extend(j,d.init(b,e))),i=!0)})}i||a.error("Cannot find validation info for '"+e+"'")}),h.data("original-contents",h.data("original-contents")?h.data("original-contents"):h.html()),h.data("original-role",h.data("original-role")?h.data("original-role"):h.attr("role")),e.data("original-classes",e.data("original-clases")?e.data("original-classes"):e.attr("class")),b.data("original-aria-invalid",b.data("original-aria-invalid")?b.data("original-aria-invalid"):b.attr("aria-invalid")),b.bind("validation.validation",function(d,e){var f=g(b),h=[];return a.each(t,function(d,g){(f||f.length||e&&e.includeEmpty||c.validatorTypes[d].includeEmpty||c.validatorTypes[d].blockSubmit&&e&&e.submitting)&&a.each(g,function(a,e){c.validatorTypes[d].validate(b,f,e)&&h.push(e.message)})}),h}),b.bind("getValidators.validation",function(){return t});var u=0;a.each(t,function(a,b){u+=b.length}),b.bind("getValidatorCount.validation",function(){return u}),b.bind("submit.validation",function(){return b.triggerHandler("change.validation",{submitting:!0})}),b.bind((c.options.bindEvents.length>0?c.options.bindEvents:["keyup","focus","blur","click","keydown","keypress","change"]).concat(["revalidate"]).join(".validation ")+".validation",function(d,f){var j=g(b),k=[];f&&f.submitting?e.data("jqbvIsSubmitting",!0):"revalidate"!==d.type&&e.data("jqbvIsSubmitting",!1);var l=!!e.data("jqbvIsSubmitting");e.find("input,textarea,select").not("[type=submit]").each(function(c,d){var e=k.length;if(a.each(a(d).triggerHandler("validation.validation",f)||[],function(a,b){k.push(b)}),k.length>e)a(d).attr("aria-invalid","true");else{var g=b.data("original-aria-invalid");a(d).attr("aria-invalid",void 0!==g?g:!1)}}),i.find("input,select,textarea").not(b).not('[name="'+b.attr("name")+'"]').trigger("validationLostFocus.validation"),k=a.unique(k.sort()),k.length?(e.removeClass("success error warning").addClass(l?"error":"warning"),h.html(c.options.semanticallyStrict&&1===k.length?k[0]+(c.options.prependExistingHelpBlock?h.data("original-contents"):""):'
  • '+k.join("
  • ")+"
"+(c.options.prependExistingHelpBlock?h.data("original-contents"):""))):(e.removeClass("warning error success"),j.length>0&&e.addClass("success"),h.html(h.data("original-contents"))),"blur"===d.type&&c.options.removeSuccess&&e.removeClass("success")}),b.bind("validationLostFocus.validation",function(){c.options.removeSuccess&&e.removeClass("success")})})},destroy:function(){return this.each(function(){var b=a(this),c=b.parents(".form-group").first(),e=c.find(".help-block").first(),f=b.parents("form").first();b.unbind(".validation"),f.unbind(".validationSubmit"),e.html(e.data("original-contents")),c.attr("class",c.data("original-classes")),b.attr("aria-invalid",b.data("original-aria-invalid")),e.attr("role",b.data("original-role")),a.inArray(e[0],d)>-1&&e.remove()})},collectErrors:function(){var b={};return this.each(function(c,d){var e=a(d),f=e.attr("name"),g=e.triggerHandler("validation.validation",{includeEmpty:!0});b[f]=a.extend(!0,g,b[f])}),a.each(b,function(a,c){0===c.length&&delete b[a]}),b},hasErrors:function(){var b=[];return this.find("input,select,textarea").add(this).each(function(c,d){b=b.concat(a(d).triggerHandler("getValidators.validation")?a(d).triggerHandler("validation.validation",{submitting:!0}):[])}),b.length>0},override:function(b){e=a.extend(!0,e,b)}},validatorTypes:{callback:{name:"callback",init:function(a,b){var c={validatorName:b,callback:a.data("validation"+b+"Callback"),lastValue:a.val(),lastValid:!0,lastFinished:!0},d="Not valid";return a.data("validation"+b+"Message")&&(d=a.data("validation"+b+"Message")),c.message=d,c},validate:function(a,b,d){if(d.lastValue===b&&d.lastFinished)return!d.lastValid;if(d.lastFinished===!0){d.lastValue=b,d.lastValid=!0,d.lastFinished=!1;var e=d,f=a;c(d.callback,window,a,b,function(b){e.lastValue===b.value&&(e.lastValid=b.valid,b.message&&(e.message=b.message),e.lastFinished=!0,f.data("validation"+e.validatorName+"Message",e.message),setTimeout(function(){f.trigger(!a.is(":focus")&&a.parents("form").first().data("jqbvIsSubmitting")?"blur.validation":"revalidate.validation")},1))})}return!1}},ajax:{name:"ajax",init:function(a,b){return{validatorName:b,url:a.data("validation"+b+"Ajax"),lastValue:a.val(),lastValid:!0,lastFinished:!0}},validate:function(b,c,d){return""+d.lastValue==""+c&&d.lastFinished===!0?d.lastValid===!1:(d.lastFinished===!0&&(d.lastValue=c,d.lastValid=!0,d.lastFinished=!1,a.ajax({url:d.url,data:"value="+encodeURIComponent(c)+"&field="+b.attr("name"),dataType:"json",success:function(a){""+d.lastValue==""+a.value&&(d.lastValid=!!a.valid,a.message&&(d.message=a.message),d.lastFinished=!0,b.data("validation"+d.validatorName+"Message",d.message),setTimeout(function(){b.trigger("revalidate.validation")},1))},failure:function(){d.lastValid=!0,d.message="ajax call failed",d.lastFinished=!0,b.data("validation"+d.validatorName+"Message",d.message),setTimeout(function(){b.trigger("revalidate.validation")},1)}})),!1)}},regex:{name:"regex",init:function(c,d){var e={},f=c.data("validation"+d+"Regex");e.regex=b(f),void 0===f&&a.error("Can't find regex for '"+d+"' validator on '"+c.attr("name")+"'");var g="Not in the expected format";return c.data("validation"+d+"Message")&&(g=c.data("validation"+d+"Message")),e.message=g,e.originalName=d,e},validate:function(a,b,c){return!c.regex.test(b)&&!c.negative||c.regex.test(b)&&c.negative}},email:{name:"email",init:function(a,c){var d={};d.regex=b("[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}");var e="Not a valid email address";return a.data("validation"+c+"Message")&&(e=a.data("validation"+c+"Message")),d.message=e,d.originalName=c,d},validate:function(a,b,c){return!c.regex.test(b)&&!c.negative||c.regex.test(b)&&c.negative}},required:{name:"required",init:function(a,b){var c="This is required";return a.data("validation"+b+"Message")&&(c=a.data("validation"+b+"Message")),{message:c,includeEmpty:!0}},validate:function(a,b,c){return!!(0===b.length&&!c.negative||b.length>0&&c.negative)},blockSubmit:!0},match:{name:"match",init:function(b,c){var d=b.data("validation"+c+"Match"),e=b.parents("form").first(),f=e.find('[name="'+d+'"]').first();f.bind("validation.validation",function(){b.trigger("revalidate.validation",{submitting:!0})});var g={};g.element=f,0===f.length&&a.error("Can't find field '"+d+"' to match '"+b.attr("name")+"' against in '"+c+"' validator");var h="Must match",i=null;return(i=e.find('label[for="'+d+'"]')).length?h+=" '"+i.text()+"'":(i=f.parents(".form-group").first().find("label")).length&&(h+=" '"+i.first().text()+"'"),b.data("validation"+c+"Message")&&(h=b.data("validation"+c+"Message")),g.message=h,g},validate:function(a,b,c){return b!==c.element.val()&&!c.negative||b===c.element.val()&&c.negative},blockSubmit:!0,includeEmpty:!0},max:{name:"max",init:function(a,b){var c={};return c.max=a.data("validation"+b+"Max"),c.message="Too high: Maximum of '"+c.max+"'",a.data("validation"+b+"Message")&&(c.message=a.data("validation"+b+"Message")),c},validate:function(a,b,c){return parseFloat(b,10)>parseFloat(c.max,10)&&!c.negative||parseFloat(b,10)<=parseFloat(c.max,10)&&c.negative}},min:{name:"min",init:function(a,b){var c={};return c.min=a.data("validation"+b+"Min"),c.message="Too low: Minimum of '"+c.min+"'",a.data("validation"+b+"Message")&&(c.message=a.data("validation"+b+"Message")),c},validate:function(a,b,c){return parseFloat(b)=parseFloat(c.min)&&c.negative}},maxlength:{name:"maxlength",init:function(a,b){var c={};return c.maxlength=a.data("validation"+b+"Maxlength"),c.message="Too long: Maximum of '"+c.maxlength+"' characters",a.data("validation"+b+"Message")&&(c.message=a.data("validation"+b+"Message")),c},validate:function(a,b,c){return b.length>c.maxlength&&!c.negative||b.length<=c.maxlength&&c.negative}},minlength:{name:"minlength",init:function(a,b){var c={};return c.minlength=a.data("validation"+b+"Minlength"),c.message="Too short: Minimum of '"+c.minlength+"' characters",a.data("validation"+b+"Message")&&(c.message=a.data("validation"+b+"Message")),c},validate:function(a,b,c){return b.length=c.minlength&&c.negative}},maxchecked:{name:"maxchecked",init:function(a,b){var c={},d=a.parents("form").first().find('[name="'+a.attr("name")+'"]');d.bind("change.validation click.validation",function(){a.trigger("revalidate.validation",{includeEmpty:!0})}),c.elements=d,c.maxchecked=a.data("validation"+b+"Maxchecked");var e="Too many: Max '"+c.maxchecked+"' checked";return a.data("validation"+b+"Message")&&(e=a.data("validation"+b+"Message")),c.message=e,c},validate:function(a,b,c){return c.elements.filter(":checked").length>c.maxchecked&&!c.negative||c.elements.filter(":checked").length<=c.maxchecked&&c.negative},blockSubmit:!0},minchecked:{name:"minchecked",init:function(a,b){var c={},d=a.parents("form").first().find('[name="'+a.attr("name")+'"]');d.bind("change.validation click.validation",function(){a.trigger("revalidate.validation",{includeEmpty:!0})}),c.elements=d,c.minchecked=a.data("validation"+b+"Minchecked");var e="Too few: Min '"+c.minchecked+"' checked";return a.data("validation"+b+"Message")&&(e=a.data("validation"+b+"Message")),c.message=e,c},validate:function(a,b,c){return c.elements.filter(":checked").length=c.minchecked&&c.negative},blockSubmit:!0,includeEmpty:!0},number:{name:"number",init:function(a,c){var d={};d.step=1,a.attr("step")&&(d.step=a.attr("step")),a.data("validation"+c+"Step")&&(d.step=a.data("validation"+c+"Step")),d.decimal=".",a.data("validation"+c+"Decimal")&&(d.decimal=a.data("validation"+c+"Decimal")),d.thousands="",a.data("validation"+c+"Thousands")&&(d.thousands=a.data("validation"+c+"Thousands")),d.regex=b("([+-]?\\d+(\\"+d.decimal+"\\d+)?)?"),d.message="Must be a number";var e=a.data("validation"+c+"Message");return e&&(d.message=e),d},validate:function(a,b,c){for(var d=b.replace(c.decimal,".").replace(c.thousands,""),e=parseFloat(d),f=parseFloat(c.step);f%1!==0;)f=10*parseFloat(f.toPrecision(12)),e=10*parseFloat(e.toPrecision(12));var g=c.regex.test(b),h=parseFloat(e)%parseFloat(f)===0,i=!isNaN(parseFloat(d))&&isFinite(d),j=!(g&&h&&i);return j},message:"Must be a number"}},builtInValidators:{email:{name:"Email",type:"email"},passwordagain:{name:"Passwordagain",type:"match",match:"password",message:"Does not match the given password"},positive:{name:"Positive",type:"shortcut",shortcut:"number,positivenumber"},negative:{name:"Negative",type:"shortcut",shortcut:"number,negativenumber"},integer:{name:"Integer",type:"regex",regex:"[+-]?\\d+",message:"No decimal places allowed"},positivenumber:{name:"Positivenumber",type:"min",min:0,message:"Must be a positive number"},negativenumber:{name:"Negativenumber",type:"max",max:0,message:"Must be a negative number"},required:{name:"Required",type:"required",message:"This is required"},checkone:{name:"Checkone",type:"minchecked",minchecked:1,message:"Check at least one option"},number:{name:"Number",type:"number",decimal:".",step:"1"},pattern:{name:"Pattern",type:"regex",message:"Not in expected format"}}},f=function(a){return a.toLowerCase().replace(/(^|\s)([a-z])/g,function(a,b,c){return b+c.toUpperCase()})},g=function(b){var c=null,d=b.attr("type");if("checkbox"===d){c=b.is(":checked")?c:"";var e=b.parents("form").first()||b.parents(".form-group").first();e&&(c=e.find("input[name='"+b.attr("name")+"']:checked").map(function(b,c){return a(c).val()}).toArray().join(","))}else if("radio"===d){c=a('input[name="'+b.attr("name")+'"]:checked').length>0?b.val():"";var f=b.parents("form").first()||b.parents(".form-group").first();f&&(c=f.find("input[name='"+b.attr("name")+"']:checked").map(function(b,c){return a(c).val()}).toArray().join(","))}else c="number"===d?b[0].validity.valid?b.val():b[0].validity.badInput||b[0].validity.stepMismatch?"NaN":"":b.val();return c};a.fn.jqBootstrapValidation=function(b){return e.methods[b]?e.methods[b].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof b&&b?(a.error("Method "+b+" does not exist on jQuery.jqBootstrapValidation"),null):e.methods.init.apply(this,arguments)},a.jqBootstrapValidation=function(){a(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments)}}(jQuery); \ No newline at end of file diff --git a/dist/jqBootstrapValidation.zip b/dist/jqBootstrapValidation.zip index 188aabe..3f2e84a 100644 Binary files a/dist/jqBootstrapValidation.zip and b/dist/jqBootstrapValidation.zip differ diff --git a/src/jqBootstrapValidation.js b/src/jqBootstrapValidation.js index 79f3048..0e3a503 100644 --- a/src/jqBootstrapValidation.js +++ b/src/jqBootstrapValidation.js @@ -41,7 +41,7 @@ var warningsFound = 0; // Get all inputs var $allInputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter); - var $allControlGroups = $form.find(".control-group"); + var $allControlGroups = $form.find(".form-group"); // Only trigger validation on the ones that actually _have_ validation var $inputsWithValidators = $allInputs.filter(function () { @@ -86,7 +86,7 @@ // Get references to everything we're interested in var $this = $(this), - $controlGroup = $this.parents(".control-group").first(), + $controlGroup = $this.parents(".form-group").first(), $helpBlock = $controlGroup.find(".help-block").first(), $form = $this.parents("form").first(), validatorNames = []; @@ -597,7 +597,7 @@ var $this = $(this), - $controlGroup = $this.parents(".control-group").first(), + $controlGroup = $this.parents(".form-group").first(), $helpBlock = $controlGroup.find(".help-block").first(), $form = $this.parents("form").first(); @@ -861,7 +861,7 @@ var $label = null; if (($label = $form.find("label[for=\"" + elementName + "\"]")).length) { message += " '" + $label.text() + "'"; - } else if (($label = $element.parents(".control-group").first().find("label")).length) { + } else if (($label = $element.parents(".form-group").first().find("label")).length) { message += " '" + $label.first().text() + "'"; } @@ -1143,7 +1143,7 @@ var type = $this.attr("type"); if (type === "checkbox") { value = ($this.is(":checked") ? value : ""); - var checkboxParent = $this.parents("form").first() || $this.parents(".control-group").first(); + var checkboxParent = $this.parents("form").first() || $this.parents(".form-group").first(); if (checkboxParent) { value = checkboxParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) { return $(el).val(); @@ -1152,7 +1152,7 @@ } else if (type === "radio") { value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? $this.val() : ""); - var radioParent = $this.parents("form").first() || $this.parents(".control-group").first(); + var radioParent = $this.parents("form").first() || $this.parents(".form-group").first(); if (radioParent) { value = radioParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) { return $(el).val(); diff --git a/test/issues/39/test.js b/test/issues/39/test.js index 8b17107..fda1f88 100644 --- a/test/issues/39/test.js +++ b/test/issues/39/test.js @@ -29,7 +29,7 @@ setup: function() { $("#qunit-fixture").append("\
\ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ 1); @@ -143,7 +143,7 @@ } $input.trigger("change.validation"); - var changeClassExpected = ["control-group"].concat(classChange); + var changeClassExpected = ["form-group"].concat(classChange); var changeClassActual = $controlGroup.attr("class").split(" "); deepEqual(changeClassActual, changeClassExpected, "classes as expected on change - " + valueJson); @@ -156,7 +156,7 @@ var nowErrors = window.number_of_submit_errors; var nowSuccess = window.number_of_submit_successes; - var submitClassExpected = ["control-group"].concat(classSubmit); + var submitClassExpected = ["form-group"].concat(classSubmit); var submitClassActual = $controlGroup.attr("class").split(" "); deepEqual(submitClassActual, submitClassExpected, "classes as expected on submit - " + valueJson); @@ -183,7 +183,7 @@ window.runJQBVTestAsync = function(value, classChange, classSubmit, messageChange, messageSubmit, callback) { var $input = $("#qunit-fixture").find("[name=input]"); - var $controlGroup = $($input.parents(".control-group")[0]); + var $controlGroup = $($input.parents(".form-group")[0]); var $form = $input.parents("form").first(); var isMulti = ($input.length > 1); @@ -235,7 +235,7 @@ $input.trigger("change.validation"); setTimeout( function() { - var changeClassExpected = ["control-group"].concat(classChange); + var changeClassExpected = ["form-group"].concat(classChange); var changeClassActual = $controlGroup.attr("class").split(" "); deepEqual(changeClassActual, changeClassExpected, "classes as expected on change - " + valueJson); @@ -251,7 +251,7 @@ var nowErrors = window.number_of_submit_errors; var nowSuccess = window.number_of_submit_successes; - var submitClassExpected = ["control-group"].concat(classSubmit); + var submitClassExpected = ["form-group"].concat(classSubmit); var submitClassActual = $controlGroup.attr("class").split(" "); deepEqual(submitClassActual, submitClassExpected, "classes as expected on submit - " + valueJson); @@ -273,7 +273,7 @@ $form.trigger("submit"); setTimeout( function() { - var submitClassExpected = ["control-group"].concat(classSubmit); + var submitClassExpected = ["form-group"].concat(classSubmit); var submitClassActual = $controlGroup.attr("class").split(" "); deepEqual(submitClassActual, submitClassExpected, "classes as expected on submit - " + valueJson); diff --git a/test/jqBootstrapValidation_test.js b/test/jqBootstrapValidation_test.js index c025a05..7d70166 100644 --- a/test/jqBootstrapValidation_test.js +++ b/test/jqBootstrapValidation_test.js @@ -29,7 +29,7 @@ setup: function() { $("#qunit-fixture").append("\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \ -
\ +
\ \
\ \
\
\ -
\ +
\ \
\ \ -
\ +
\ \
\ \
\
\ -
\ +
\ \
\ \ -
\ +
\ \
\ \
\
\ -
\ +
\ \
\ \ -
\ +
\
\ \
\
\ -
\ +
\ \
\ \ -
\ +
\ \
\