validation.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. /* jqBootstrapValidation
  2. * A plugin for automating validation on Twitter Bootstrap formatted forms.
  3. *
  4. * v1.3.6
  5. *
  6. * License: MIT <http://opensource.org/licenses/mit-license.php> - see LICENSE file
  7. *
  8. * http://ReactiveRaven.github.com/jqBootstrapValidation/
  9. */
  10. (function ($) {
  11. var createdElements = [];
  12. var defaults = {
  13. options: {
  14. prependExistingHelpBlock: false
  15. , sniffHtml: true, // sniff for 'required', 'maxlength', etc
  16. preventSubmit: true, // stop the form submit event from firing if validation fails
  17. submitError: false, // function called if there is an error when trying to submit
  18. submitSuccess: false, // function called just before a successful submit event is sent to the server
  19. semanticallyStrict: false, // set to true to tidy up generated HTML output
  20. bindEvents: [],
  21. autoAdd: {
  22. helpBlocks: true
  23. }
  24. , filter: function () {
  25. // return $(this).is(":visible"); // only validate elements you can see
  26. return true; // validate everything
  27. }
  28. }
  29. , methods: {
  30. init: function (options) {
  31. var settings = $.extend(true, {}, defaults);
  32. settings.options = $.extend(true, settings.options, options);
  33. var $siblingElements = this;
  34. var uniqueForms = $.unique($siblingElements.map(function () {
  35. return $(this).parents("form")[0];
  36. }).toArray());
  37. $(uniqueForms).bind("submit.validationSubmit", function (e) {
  38. var $form = $(this);
  39. var warningsFound = 0;
  40. var $allInputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter);
  41. var $allControlGroups = $form.find(".form-group");
  42. var $inputsWithValidators = $allInputs.filter(function () {
  43. return $(this).triggerHandler("getValidatorCount.validation") > 0;
  44. });
  45. $inputsWithValidators.trigger("submit.validation");
  46. $allInputs.trigger("validationLostFocus.validation");
  47. $allControlGroups.each(function (i, el) {
  48. var $controlGroup = $(el);
  49. if ($controlGroup.hasClass("issue") || $controlGroup.hasClass("error")) {
  50. $controlGroup.removeClass("issue").addClass("error");
  51. warningsFound++;
  52. }
  53. });
  54. if (warningsFound) {
  55. if (settings.options.preventSubmit) {
  56. e.preventDefault();
  57. e.stopImmediatePropagation();
  58. }
  59. $form.addClass("error");
  60. if ($.isFunction(settings.options.submitError)) {
  61. settings.options.submitError($form, e, $inputsWithValidators.jqBootstrapValidation("collectErrors", true));
  62. }
  63. }
  64. else {
  65. $form.removeClass("error");
  66. if ($.isFunction(settings.options.submitSuccess)) {
  67. settings.options.submitSuccess($form, e);
  68. }
  69. }
  70. });
  71. return this.each(function () {
  72. var $this = $(this)
  73. , $controlGroup = $this.parents(".form-group").first()
  74. , $helpBlock = $controlGroup.find(".help-block").first()
  75. , $form = $this.parents("form").first()
  76. , validatorNames = [];
  77. if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) {
  78. $helpBlock = $('<div class="help-block" />');
  79. $controlGroup.find('.controls').append($helpBlock);
  80. createdElements.push($helpBlock[0]);
  81. }
  82. if (settings.options.sniffHtml) {
  83. var message;
  84. if ($this.data("validationPatternPattern")) {
  85. $this.attr("pattern", $this.data("validationPatternPattern"));
  86. }
  87. if ($this.attr("pattern") !== undefined) {
  88. message = "Not in the expected format<!-- data-validation-pattern-message to override -->";
  89. if ($this.data("validationPatternMessage")) {
  90. message = $this.data("validationPatternMessage");
  91. }
  92. $this.data("validationPatternMessage", message);
  93. $this.data("validationPatternRegex", $this.attr("pattern"));
  94. }
  95. if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) {
  96. var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax"));
  97. message = "Too high: Maximum of '" + max + "'<!-- data-validation-max-message to override -->";
  98. if ($this.data("validationMaxMessage")) {
  99. message = $this.data("validationMaxMessage");
  100. }
  101. $this.data("validationMaxMessage", message);
  102. $this.data("validationMaxMax", max);
  103. }
  104. if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) {
  105. var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin"));
  106. message = "Too low: Minimum of '" + min + "'<!-- data-validation-min-message to override -->";
  107. if ($this.data("validationMinMessage")) {
  108. message = $this.data("validationMinMessage");
  109. }
  110. $this.data("validationMinMessage", message);
  111. $this.data("validationMinMin", min);
  112. }
  113. if ($this.attr("maxlength") !== undefined) {
  114. message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters<!-- data-validation-maxlength-message to override -->";
  115. if ($this.data("validationMaxlengthMessage")) {
  116. message = $this.data("validationMaxlengthMessage");
  117. }
  118. $this.data("validationMaxlengthMessage", message);
  119. $this.data("validationMaxlengthMaxlength", $this.attr("maxlength"));
  120. }
  121. if ($this.attr("minlength") !== undefined) {
  122. message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters<!-- data-validation-minlength-message to override -->";
  123. if ($this.data("validationMinlengthMessage")) {
  124. message = $this.data("validationMinlengthMessage");
  125. }
  126. $this.data("validationMinlengthMessage", message);
  127. $this.data("validationMinlengthMinlength", $this.attr("minlength"));
  128. }
  129. if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) {
  130. message = settings.builtInValidators.required.message;
  131. if ($this.data("validationRequiredMessage")) {
  132. message = $this.data("validationRequiredMessage");
  133. }
  134. $this.data("validationRequiredMessage", message);
  135. }
  136. if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") {
  137. message = settings.validatorTypes.number.message;
  138. if ($this.data("validationNumberMessage")) {
  139. message = $this.data("validationNumberMessage");
  140. }
  141. $this.data("validationNumberMessage", message);
  142. var step = settings.validatorTypes.number.step;
  143. if ($this.data("validationNumberStep")) {
  144. step = $this.data("validationNumberStep");
  145. }
  146. $this.data("validationNumberStep", step);
  147. var decimal = settings.validatorTypes.number.decimal;
  148. if ($this.data("validationNumberDecimal")) {
  149. decimal = $this.data("validationNumberDecimal");
  150. }
  151. $this.data("validationNumberDecimal", decimal);
  152. }
  153. if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") {
  154. message = "Not a valid email address<!-- data-validation-email-message to override -->";
  155. if ($this.data("validationEmailMessage")) {
  156. message = $this.data("validationEmailMessage");
  157. }
  158. $this.data("validationEmailMessage", message);
  159. }
  160. if ($this.attr("minchecked") !== undefined) {
  161. message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required<!-- data-validation-minchecked-message to override -->";
  162. if ($this.data("validationMincheckedMessage")) {
  163. message = $this.data("validationMincheckedMessage");
  164. }
  165. $this.data("validationMincheckedMessage", message);
  166. $this.data("validationMincheckedMinchecked", $this.attr("minchecked"));
  167. }
  168. if ($this.attr("maxchecked") !== undefined) {
  169. message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required<!-- data-validation-maxchecked-message to override -->";
  170. if ($this.data("validationMaxcheckedMessage")) {
  171. message = $this.data("validationMaxcheckedMessage");
  172. }
  173. $this.data("validationMaxcheckedMessage", message);
  174. $this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked"));
  175. }
  176. }
  177. if ($this.data("validation") !== undefined) {
  178. validatorNames = $this.data("validation").split(",");
  179. }
  180. $.each($this.data(), function (i, el) {
  181. var parts = i.replace(/([A-Z])/g, ",$1").split(",");
  182. if (parts[0] === "validation" && parts[1]) {
  183. validatorNames.push(parts[1]);
  184. }
  185. });
  186. var validatorNamesToInspect = validatorNames;
  187. var newValidatorNamesToInspect = [];
  188. var uppercaseEachValidatorName = function (i, el) {
  189. validatorNames[i] = formatValidatorName(el);
  190. };
  191. var inspectValidators = function (i, el) {
  192. if ($this.data("validation" + el + "Shortcut") !== undefined) {
  193. $.each($this.data("validation" + el + "Shortcut").split(","), function (i2, el2) {
  194. newValidatorNamesToInspect.push(el2);
  195. });
  196. }
  197. else if (settings.builtInValidators[el.toLowerCase()]) {
  198. var validator = settings.builtInValidators[el.toLowerCase()];
  199. if (validator.type.toLowerCase() === "shortcut") {
  200. $.each(validator.shortcut.split(","), function (i, el) {
  201. el = formatValidatorName(el);
  202. newValidatorNamesToInspect.push(el);
  203. validatorNames.push(el);
  204. });
  205. }
  206. }
  207. };
  208. do {
  209. $.each(validatorNames, uppercaseEachValidatorName);
  210. validatorNames = $.unique(validatorNames);
  211. newValidatorNamesToInspect = [];
  212. $.each(validatorNamesToInspect, inspectValidators);
  213. validatorNamesToInspect = newValidatorNamesToInspect;
  214. } while (validatorNamesToInspect.length > 0);
  215. var validators = {};
  216. $.each(validatorNames, function (i, el) {
  217. var message = $this.data("validation" + el + "Message");
  218. var hasOverrideMessage = !!message;
  219. var foundValidator = false;
  220. if (!message) {
  221. message = "'" + el + "' validation failed <!-- Add attribute 'data-validation-" + el.toLowerCase() + "-message' to input to change this message -->";
  222. }
  223. $.each(settings.validatorTypes, function (validatorType, validatorTemplate) {
  224. if (validators[validatorType] === undefined) {
  225. validators[validatorType] = [];
  226. }
  227. if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) {
  228. var initted = validatorTemplate.init($this, el);
  229. if (hasOverrideMessage) {
  230. initted.message = message;
  231. }
  232. validators[validatorType].push($.extend(true, {
  233. name: formatValidatorName(validatorTemplate.name)
  234. , message: message
  235. }, initted));
  236. foundValidator = true;
  237. }
  238. });
  239. if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) {
  240. var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]);
  241. if (hasOverrideMessage) {
  242. validator.message = message;
  243. }
  244. var validatorType = validator.type.toLowerCase();
  245. if (validatorType === "shortcut") {
  246. foundValidator = true;
  247. }
  248. else {
  249. $.each(settings.validatorTypes, function (validatorTemplateType, validatorTemplate) {
  250. if (validators[validatorTemplateType] === undefined) {
  251. validators[validatorTemplateType] = [];
  252. }
  253. if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) {
  254. $this.data("validation" + el + formatValidatorName(validatorTemplate.name), validator[validatorTemplate.name.toLowerCase()]);
  255. validators[validatorType].push($.extend(validator, validatorTemplate.init($this, el)));
  256. foundValidator = true;
  257. }
  258. });
  259. }
  260. }
  261. if (!foundValidator) {
  262. $.error("Cannot find validation info for '" + el + "'");
  263. }
  264. });
  265. $helpBlock.data("original-contents", ($helpBlock.data("original-contents") ? $helpBlock.data("original-contents") : $helpBlock.html()));
  266. $helpBlock.data("original-role", ($helpBlock.data("original-role") ? $helpBlock.data("original-role") : $helpBlock.attr("role")));
  267. $controlGroup.data("original-classes", ($controlGroup.data("original-clases") ? $controlGroup.data("original-classes") : $controlGroup.attr("class")));
  268. $this.data("original-aria-invalid", ($this.data("original-aria-invalid") ? $this.data("original-aria-invalid") : $this.attr("aria-invalid")));
  269. $this.bind("validation.validation", function (event, params) {
  270. var value = getValue($this);
  271. var errorsFound = [];
  272. $.each(validators, function (validatorType, validatorTypeArray) {
  273. if (value || value.length || ((params && params.includeEmpty) || !!settings.validatorTypes[validatorType].includeEmpty) || (!!settings.validatorTypes[validatorType].blockSubmit && params && !!params.submitting)) {
  274. $.each(validatorTypeArray, function (i, validator) {
  275. if (settings.validatorTypes[validatorType].validate($this, value, validator)) {
  276. errorsFound.push(validator.message);
  277. }
  278. });
  279. }
  280. });
  281. return errorsFound;
  282. });
  283. $this.bind("getValidators.validation", function () {
  284. return validators;
  285. });
  286. var numValidators = 0;
  287. $.each(validators, function (i, el) {
  288. numValidators += el.length;
  289. });
  290. $this.bind("getValidatorCount.validation", function () {
  291. return numValidators;
  292. });
  293. $this.bind("submit.validation", function () {
  294. return $this.triggerHandler("change.validation", {
  295. submitting: true
  296. });
  297. });
  298. $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) {
  299. var value = getValue($this);
  300. var errorsFound = [];
  301. if (params && !!params.submitting) {
  302. $controlGroup.data("jqbvIsSubmitting", true);
  303. }
  304. else if (e.type !== "revalidate") {
  305. $controlGroup.data("jqbvIsSubmitting", false);
  306. }
  307. var formIsSubmitting = !!$controlGroup.data("jqbvIsSubmitting");
  308. $controlGroup.find("input,textarea,select").not('[type=submit]').each(function (i, el) {
  309. var oldCount = errorsFound.length;
  310. $.each($(el).triggerHandler("validation.validation", params) || [], function (j, message) {
  311. errorsFound.push(message);
  312. });
  313. if (errorsFound.length > oldCount) {
  314. $(el).attr("aria-invalid", "true");
  315. }
  316. else {
  317. var original = $this.data("original-aria-invalid");
  318. $(el).attr("aria-invalid", (original !== undefined ? original : false));
  319. }
  320. });
  321. $form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation");
  322. errorsFound = $.unique(errorsFound.sort());
  323. if (errorsFound.length) {
  324. $controlGroup.removeClass("validate error issue").addClass(formIsSubmitting ? "error" : "issue");
  325. if (settings.options.semanticallyStrict && errorsFound.length === 1) {
  326. $helpBlock.html(errorsFound[0] + (settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : ""));
  327. }
  328. else {
  329. $helpBlock.html("<ul role=\"alert\"><li>" + errorsFound.join("</li><li>") + "</li></ul>" + (settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : ""));
  330. }
  331. }
  332. else {
  333. $controlGroup.removeClass("issue error validate");
  334. if (value.length > 0) {
  335. $controlGroup.addClass("validate");
  336. }
  337. $helpBlock.html($helpBlock.data("original-contents"));
  338. }
  339. if (e.type === "blur") {
  340. if (settings.options.removeSuccess) {}
  341. }
  342. });
  343. $this.bind("validationLostFocus.validation", function () {
  344. if (settings.options.removeSuccess) {}
  345. });
  346. });
  347. }
  348. , destroy: function () {
  349. return this.each(function () {
  350. var $this = $(this)
  351. , $controlGroup = $this.parents(".form-group").first()
  352. , $helpBlock = $controlGroup.find(".help-block").first()
  353. , $form = $this.parents("form").first();
  354. $this.unbind('.validation');
  355. $form.unbind(".validationSubmit");
  356. $helpBlock.html($helpBlock.data("original-contents"));
  357. $controlGroup.attr("class", $controlGroup.data("original-classes"));
  358. $this.attr("aria-invalid", $this.data("original-aria-invalid"));
  359. $helpBlock.attr("role", $this.data("original-role"));
  360. if ($.inArray($helpBlock[0], createdElements) > -1) {
  361. $helpBlock.remove();
  362. }
  363. });
  364. }
  365. , collectErrors: function (includeEmpty) {
  366. var errorMessages = {};
  367. this.each(function (i, el) {
  368. var $el = $(el);
  369. var name = $el.attr("name");
  370. var errors = $el.triggerHandler("validation.validation", {
  371. includeEmpty: true
  372. });
  373. errorMessages[name] = $.extend(true, errors, errorMessages[name]);
  374. });
  375. $.each(errorMessages, function (i, el) {
  376. if (el.length === 0) {
  377. delete errorMessages[i];
  378. }
  379. });
  380. return errorMessages;
  381. }
  382. , hasErrors: function () {
  383. var errorMessages = [];
  384. this.find('input,select,textarea').add(this).each(function (i, el) {
  385. errorMessages = errorMessages.concat($(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {
  386. submitting: true
  387. }) : []);
  388. });
  389. return (errorMessages.length > 0);
  390. }
  391. , override: function (newDefaults) {
  392. defaults = $.extend(true, defaults, newDefaults);
  393. }
  394. }
  395. , validatorTypes: {
  396. callback: {
  397. name: "callback"
  398. , init: function ($this, name) {
  399. var result = {
  400. validatorName: name
  401. , callback: $this.data("validation" + name + "Callback")
  402. , lastValue: $this.val()
  403. , lastValid: true
  404. , lastFinished: true
  405. };
  406. var message = "Not valid";
  407. if ($this.data("validation" + name + "Message")) {
  408. message = $this.data("validation" + name + "Message");
  409. }
  410. result.message = message;
  411. return result;
  412. }
  413. , validate: function ($this, value, validator) {
  414. if (validator.lastValue === value && validator.lastFinished) {
  415. return !validator.lastValid;
  416. }
  417. if (validator.lastFinished === true) {
  418. validator.lastValue = value;
  419. validator.lastValid = true;
  420. validator.lastFinished = false;
  421. var rrjqbvValidator = validator;
  422. var rrjqbvThis = $this;
  423. executeFunctionByName(validator.callback, window, $this, value, function (data) {
  424. if (rrjqbvValidator.lastValue === data.value) {
  425. rrjqbvValidator.lastValid = data.valid;
  426. if (data.message) {
  427. rrjqbvValidator.message = data.message;
  428. }
  429. rrjqbvValidator.lastFinished = true;
  430. rrjqbvThis.data("validation" + rrjqbvValidator.validatorName + "Message", rrjqbvValidator.message);
  431. setTimeout(function () {
  432. if (!$this.is(":focus") && $this.parents("form").first().data("jqbvIsSubmitting")) {
  433. rrjqbvThis.trigger("blur.validation");
  434. }
  435. else {
  436. rrjqbvThis.trigger("revalidate.validation");
  437. }
  438. }, 1);
  439. }
  440. });
  441. }
  442. return false;
  443. }
  444. }
  445. , ajax: {
  446. name: "ajax"
  447. , init: function ($this, name) {
  448. return {
  449. validatorName: name
  450. , url: $this.data("validation" + name + "Ajax")
  451. , lastValue: $this.val()
  452. , lastValid: true
  453. , lastFinished: true
  454. };
  455. }
  456. , validate: function ($this, value, validator) {
  457. if ("" + validator.lastValue === "" + value && validator.lastFinished === true) {
  458. return validator.lastValid === false;
  459. }
  460. if (validator.lastFinished === true) {
  461. validator.lastValue = value;
  462. validator.lastValid = true;
  463. validator.lastFinished = false;
  464. $.ajax({
  465. url: validator.url
  466. , data: "value=" + encodeURIComponent(value) + "&field=" + $this.attr("name")
  467. , dataType: "json"
  468. , success: function (data) {
  469. if ("" + validator.lastValue === "" + data.value) {
  470. validator.lastValid = !!(data.valid);
  471. if (data.message) {
  472. validator.message = data.message;
  473. }
  474. validator.lastFinished = true;
  475. $this.data("validation" + validator.validatorName + "Message", validator.message);
  476. setTimeout(function () {
  477. $this.trigger("revalidate.validation");
  478. }, 1);
  479. }
  480. }
  481. , failure: function () {
  482. validator.lastValid = true;
  483. validator.message = "ajax call failed";
  484. validator.lastFinished = true;
  485. $this.data("validation" + validator.validatorName + "Message", validator.message);
  486. setTimeout(function () {
  487. $this.trigger("revalidate.validation");
  488. }, 1);
  489. }
  490. });
  491. }
  492. return false;
  493. }
  494. }
  495. , regex: {
  496. name: "regex"
  497. , init: function ($this, name) {
  498. var result = {};
  499. var regexString = $this.data("validation" + name + "Regex");
  500. result.regex = regexFromString(regexString);
  501. if (regexString === undefined) {
  502. $.error("Can't find regex for '" + name + "' validator on '" + $this.attr("name") + "'");
  503. }
  504. var message = "Not in the expected format";
  505. if ($this.data("validation" + name + "Message")) {
  506. message = $this.data("validation" + name + "Message");
  507. }
  508. result.message = message;
  509. result.originalName = name;
  510. return result;
  511. }
  512. , validate: function ($this, value, validator) {
  513. return (!validator.regex.test(value) && !validator.negative) || (validator.regex.test(value) && validator.negative);
  514. }
  515. }
  516. , email: {
  517. name: "email"
  518. , init: function ($this, name) {
  519. var result = {};
  520. result.regex = regexFromString('[a-zA-Z0-9.!#$%&\u2019*+/=?^_`{|}~-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}');
  521. var message = "Not a valid email address";
  522. if ($this.data("validation" + name + "Message")) {
  523. message = $this.data("validation" + name + "Message");
  524. }
  525. result.message = message;
  526. result.originalName = name;
  527. return result;
  528. }
  529. , validate: function ($this, value, validator) {
  530. return (!validator.regex.test(value) && !validator.negative) || (validator.regex.test(value) && validator.negative);
  531. }
  532. }
  533. , required: {
  534. name: "required"
  535. , init: function ($this, name) {
  536. var message = "This is required";
  537. if ($this.data("validation" + name + "Message")) {
  538. message = $this.data("validation" + name + "Message");
  539. }
  540. return {
  541. message: message
  542. , includeEmpty: true
  543. };
  544. }
  545. , validate: function ($this, value, validator) {
  546. return !!((value.length === 0 && !validator.negative) || (value.length > 0 && validator.negative));
  547. }
  548. , blockSubmit: true
  549. }
  550. , match: {
  551. name: "match"
  552. , init: function ($this, name) {
  553. var elementName = $this.data("validation" + name + "Match");
  554. var $form = $this.parents("form").first();
  555. var $element = $form.find("[name=\"" + elementName + "\"]").first();
  556. $element.bind("validation.validation", function () {
  557. $this.trigger("revalidate.validation", {
  558. submitting: true
  559. });
  560. });
  561. var result = {};
  562. result.element = $element;
  563. if ($element.length === 0) {
  564. $.error("Can't find field '" + elementName + "' to match '" + $this.attr("name") + "' against in '" + name + "' validator");
  565. }
  566. var message = "Must match";
  567. var $label = null;
  568. if (($label = $form.find("label[for=\"" + elementName + "\"]")).length) {
  569. message += " '" + $label.text() + "'";
  570. }
  571. else if (($label = $element.parents(".form-group").first().find("label")).length) {
  572. message += " '" + $label.first().text() + "'";
  573. }
  574. if ($this.data("validation" + name + "Message")) {
  575. message = $this.data("validation" + name + "Message");
  576. }
  577. result.message = message;
  578. return result;
  579. }
  580. , validate: function ($this, value, validator) {
  581. return (value !== validator.element.val() && !validator.negative) || (value === validator.element.val() && validator.negative);
  582. }
  583. , blockSubmit: true
  584. , includeEmpty: true
  585. }
  586. , max: {
  587. name: "max"
  588. , init: function ($this, name) {
  589. var result = {};
  590. result.max = $this.data("validation" + name + "Max");
  591. result.message = "Too high: Maximum of '" + result.max + "'";
  592. if ($this.data("validation" + name + "Message")) {
  593. result.message = $this.data("validation" + name + "Message");
  594. }
  595. return result;
  596. }
  597. , validate: function ($this, value, validator) {
  598. return (parseFloat(value, 10) > parseFloat(validator.max, 10) && !validator.negative) || (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative);
  599. }
  600. }
  601. , min: {
  602. name: "min"
  603. , init: function ($this, name) {
  604. var result = {};
  605. result.min = $this.data("validation" + name + "Min");
  606. result.message = "Too low: Minimum of '" + result.min + "'";
  607. if ($this.data("validation" + name + "Message")) {
  608. result.message = $this.data("validation" + name + "Message");
  609. }
  610. return result;
  611. }
  612. , validate: function ($this, value, validator) {
  613. return (parseFloat(value) < parseFloat(validator.min) && !validator.negative) || (parseFloat(value) >= parseFloat(validator.min) && validator.negative);
  614. }
  615. }
  616. , maxlength: {
  617. name: "maxlength"
  618. , init: function ($this, name) {
  619. var result = {};
  620. result.maxlength = $this.data("validation" + name + "Maxlength");
  621. result.message = "Too long: Maximum of '" + result.maxlength + "' characters";
  622. if ($this.data("validation" + name + "Message")) {
  623. result.message = $this.data("validation" + name + "Message");
  624. }
  625. return result;
  626. }
  627. , validate: function ($this, value, validator) {
  628. return ((value.length > validator.maxlength) && !validator.negative) || ((value.length <= validator.maxlength) && validator.negative);
  629. }
  630. }
  631. , minlength: {
  632. name: "minlength"
  633. , init: function ($this, name) {
  634. var result = {};
  635. result.minlength = $this.data("validation" + name + "Minlength");
  636. result.message = "Too short: Minimum of '" + result.minlength + "' characters";
  637. if ($this.data("validation" + name + "Message")) {
  638. result.message = $this.data("validation" + name + "Message");
  639. }
  640. return result;
  641. }
  642. , validate: function ($this, value, validator) {
  643. return ((value.length < validator.minlength) && !validator.negative) || ((value.length >= validator.minlength) && validator.negative);
  644. }
  645. }
  646. , maxchecked: {
  647. name: "maxchecked"
  648. , init: function ($this, name) {
  649. var result = {};
  650. var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
  651. elements.bind("change.validation click.validation", function () {
  652. $this.trigger("revalidate.validation", {
  653. includeEmpty: true
  654. });
  655. });
  656. result.elements = elements;
  657. result.maxchecked = $this.data("validation" + name + "Maxchecked");
  658. var message = "Too many: Max '" + result.maxchecked + "' checked";
  659. if ($this.data("validation" + name + "Message")) {
  660. message = $this.data("validation" + name + "Message");
  661. }
  662. result.message = message;
  663. return result;
  664. }
  665. , validate: function ($this, value, validator) {
  666. return (validator.elements.filter(":checked").length > validator.maxchecked && !validator.negative) || (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative);
  667. }
  668. , blockSubmit: true
  669. }
  670. , minchecked: {
  671. name: "minchecked"
  672. , init: function ($this, name) {
  673. var result = {};
  674. var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
  675. elements.bind("change.validation click.validation", function () {
  676. $this.trigger("revalidate.validation", {
  677. includeEmpty: true
  678. });
  679. });
  680. result.elements = elements;
  681. result.minchecked = $this.data("validation" + name + "Minchecked");
  682. var message = "Too few: Min '" + result.minchecked + "' checked";
  683. if ($this.data("validation" + name + "Message")) {
  684. message = $this.data("validation" + name + "Message");
  685. }
  686. result.message = message;
  687. return result;
  688. }
  689. , validate: function ($this, value, validator) {
  690. return (validator.elements.filter(":checked").length < validator.minchecked && !validator.negative) || (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative);
  691. }
  692. , blockSubmit: true
  693. , includeEmpty: true
  694. }
  695. , number: {
  696. name: "number"
  697. , init: function ($this, name) {
  698. var result = {};
  699. result.step = 1;
  700. if ($this.attr("step")) {
  701. result.step = $this.attr("step");
  702. }
  703. if ($this.data("validation" + name + "Step")) {
  704. result.step = $this.data("validation" + name + "Step");
  705. }
  706. result.decimal = ".";
  707. if ($this.data("validation" + name + "Decimal")) {
  708. result.decimal = $this.data("validation" + name + "Decimal");
  709. }
  710. result.thousands = "";
  711. if ($this.data("validation" + name + "Thousands")) {
  712. result.thousands = $this.data("validation" + name + "Thousands");
  713. }
  714. result.regex = regexFromString("([+-]?\\d+(\\" + result.decimal + "\\d+)?)?");
  715. result.message = "Must be a number";
  716. var dataMessage = $this.data("validation" + name + "Message");
  717. if (dataMessage) {
  718. result.message = dataMessage;
  719. }
  720. return result;
  721. }
  722. , validate: function ($this, value, validator) {
  723. var globalValue = value.replace(validator.decimal, ".").replace(validator.thousands, "");
  724. var multipliedValue = parseFloat(globalValue);
  725. var multipliedStep = parseFloat(validator.step);
  726. while (multipliedStep % 1 !== 0) {
  727. multipliedStep = parseFloat(multipliedStep.toPrecision(12)) * 10;
  728. multipliedValue = parseFloat(multipliedValue.toPrecision(12)) * 10;
  729. }
  730. var regexResult = validator.regex.test(value);
  731. var stepResult = parseFloat(multipliedValue) % parseFloat(multipliedStep) === 0;
  732. var typeResult = !isNaN(parseFloat(globalValue)) && isFinite(globalValue);
  733. var result = !(regexResult && stepResult && typeResult);
  734. return result;
  735. }
  736. , message: "Must be a number",
  737. }
  738. }
  739. , builtInValidators: {
  740. email: {
  741. name: "Email"
  742. , type: "email"
  743. }
  744. , passwordagain: {
  745. name: "Passwordagain"
  746. , type: "match"
  747. , match: "password"
  748. , message: "Does not match the given password<!-- data-validator-paswordagain-message to override -->"
  749. }
  750. , positive: {
  751. name: "Positive"
  752. , type: "shortcut"
  753. , shortcut: "number,positivenumber"
  754. }
  755. , negative: {
  756. name: "Negative"
  757. , type: "shortcut"
  758. , shortcut: "number,negativenumber"
  759. }
  760. , integer: {
  761. name: "Integer"
  762. , type: "regex"
  763. , regex: "[+-]?\\d+"
  764. , message: "No decimal places allowed<!-- data-validator-integer-message to override -->"
  765. }
  766. , positivenumber: {
  767. name: "Positivenumber"
  768. , type: "min"
  769. , min: 0
  770. , message: "Must be a positive number<!-- data-validator-positivenumber-message to override -->"
  771. }
  772. , negativenumber: {
  773. name: "Negativenumber"
  774. , type: "max"
  775. , max: 0
  776. , message: "Must be a negative number<!-- data-validator-negativenumber-message to override -->"
  777. }
  778. , required: {
  779. name: "Required"
  780. , type: "required"
  781. , message: "This is required<!-- data-validator-required-message to override -->"
  782. }
  783. , checkone: {
  784. name: "Checkone"
  785. , type: "minchecked"
  786. , minchecked: 1
  787. , message: "Check at least one option<!-- data-validation-checkone-message to override -->"
  788. }
  789. , number: {
  790. name: "Number"
  791. , type: "number"
  792. , decimal: "."
  793. , step: "1"
  794. }
  795. , pattern: {
  796. name: "Pattern"
  797. , type: "regex"
  798. , message: "Not in expected format"
  799. }
  800. }
  801. };
  802. var formatValidatorName = function (name) {
  803. return name.toLowerCase().replace(/(^|\s)([a-z])/g, function (m, p1, p2) {
  804. return p1 + p2.toUpperCase();
  805. });
  806. };
  807. var getValue = function ($this) {
  808. var value = null;
  809. var type = $this.attr("type");
  810. if (type === "checkbox") {
  811. value = ($this.is(":checked") ? value : "");
  812. var checkboxParent = $this.parents("form").first() || $this.parents(".form-group").first();
  813. if (checkboxParent) {
  814. value = checkboxParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) {
  815. return $(el).val();
  816. }).toArray().join(",");
  817. }
  818. }
  819. else if (type === "radio") {
  820. value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? $this.val() : "");
  821. var radioParent = $this.parents("form").first() || $this.parents(".form-group").first();
  822. if (radioParent) {
  823. value = radioParent.find("input[name='" + $this.attr("name") + "']:checked").map(function (i, el) {
  824. return $(el).val();
  825. }).toArray().join(",");
  826. }
  827. }
  828. else if (type === "number") {
  829. if ($this[0].validity.valid) {
  830. value = $this.val();
  831. }
  832. else {
  833. if ($this[0].validity.badInput || $this[0].validity.stepMismatch) {
  834. value = "NaN";
  835. }
  836. else {
  837. value = "";
  838. }
  839. }
  840. }
  841. else {
  842. value = $this.val();
  843. }
  844. return value;
  845. };
  846. function regexFromString(inputstring) {
  847. return new RegExp("^" + inputstring + "$");
  848. }
  849. /**
  850. * Thanks to Jason Bunting via StackOverflow.com
  851. *
  852. * http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string#answer-359910
  853. * Short link: http://tinyurl.com/executeFunctionByName
  854. **/
  855. function executeFunctionByName(functionName, context) {
  856. var args = Array.prototype.slice.call(arguments, 2);
  857. var namespaces = functionName.split(".");
  858. var func = namespaces.pop();
  859. for (var i = 0; i < namespaces.length; i++) {
  860. context = context[namespaces[i]];
  861. }
  862. return context[func].apply(context, args);
  863. }
  864. $.fn.jqBootstrapValidation = function (method) {
  865. if (defaults.methods[method]) {
  866. return defaults.methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  867. }
  868. else if (typeof method === 'object' || !method) {
  869. return defaults.methods.init.apply(this, arguments);
  870. }
  871. else {
  872. $.error('Method ' + method + ' does not exist on jQuery.jqBootstrapValidation');
  873. return null;
  874. }
  875. };
  876. $.jqBootstrapValidation = function (options) {
  877. $(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this, arguments);
  878. };
  879. })(jQuery);