PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/vendors/jqBootstrapValidation/dist/jqBootstrapValidation-1.3.7.js

https://github.com/aduyng/travel-time-watcher-chrome-extension
JavaScript | 1153 lines | 951 code | 119 blank | 83 comment | 137 complexity | 090e97c24b3b040e676c97d09e3ab256 MD5 | raw file
Possible License(s): Apache-2.0, MIT, BSD-3-Clause, GPL-2.0
  1. /*! jqBootstrapValidation - v1.3.7 - 2013-05-03
  2. * http://reactiveraven.github.com/jqBootstrapValidation
  3. * Copyright (c) 2013 David Godfrey; Licensed MIT */
  4. (function( $ ){
  5. var createdElements = [];
  6. var defaults = {
  7. options: {
  8. prependExistingHelpBlock: false,
  9. sniffHtml: true, // sniff for 'required', 'maxlength', etc
  10. preventSubmit: true, // stop the form submit event from firing if validation fails
  11. submitError: false, // function called if there is an error when trying to submit
  12. submitSuccess: false, // function called just before a successful submit event is sent to the server
  13. semanticallyStrict: false, // set to true to tidy up generated HTML output
  14. bindEvents: [],
  15. autoAdd: {
  16. helpBlocks: true
  17. },
  18. filter: function () {
  19. // return $(this).is(":visible"); // only validate elements you can see
  20. return true; // validate everything
  21. }
  22. },
  23. methods: {
  24. init : function( options ) {
  25. var settings = $.extend(true, {}, defaults);
  26. settings.options = $.extend(true, settings.options, options);
  27. var $siblingElements = this;
  28. var uniqueForms = $.unique(
  29. $siblingElements.map( function () {
  30. return $(this).parents("form")[0];
  31. }).toArray()
  32. );
  33. $(uniqueForms).bind("submit", function (e) {
  34. var $form = $(this);
  35. var warningsFound = 0;
  36. var $inputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter);
  37. $inputs.trigger("submit.validation").trigger("validationLostFocus.validation");
  38. $inputs.each(function (i, el) {
  39. var $this = $(el),
  40. $controlGroup = $this.parents(".control-group").first();
  41. if (
  42. $controlGroup.hasClass("warning") || $controlGroup.hasClass("error")
  43. ) {
  44. $controlGroup.removeClass("warning").addClass("error");
  45. warningsFound++;
  46. }
  47. });
  48. if (warningsFound) {
  49. if (settings.options.preventSubmit) {
  50. e.preventDefault();
  51. e.stopImmediatePropagation();
  52. }
  53. $form.addClass("error");
  54. if ($.isFunction(settings.options.submitError)) {
  55. settings.options.submitError($form, e, $inputs.jqBootstrapValidation("collectErrors", true));
  56. }
  57. } else {
  58. $form.removeClass("error");
  59. if ($.isFunction(settings.options.submitSuccess)) {
  60. settings.options.submitSuccess($form, e);
  61. }
  62. }
  63. });
  64. return this.each(function(){
  65. // Get references to everything we're interested in
  66. var $this = $(this),
  67. $controlGroup = $this.parents(".control-group").first(),
  68. $helpBlock = $controlGroup.find(".help-block").first(),
  69. $form = $this.parents("form").first(),
  70. validatorNames = [];
  71. // create message container if not exists
  72. if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) {
  73. $helpBlock = $('<div class="help-block" />');
  74. $controlGroup.find('.controls').append($helpBlock);
  75. createdElements.push($helpBlock[0]);
  76. }
  77. // =============================================================
  78. // SNIFF HTML FOR VALIDATORS
  79. // =============================================================
  80. // *snort sniff snuffle*
  81. if (settings.options.sniffHtml) {
  82. var message;
  83. // ---------------------------------------------------------
  84. // PATTERN
  85. // ---------------------------------------------------------
  86. if ($this.data("validationPatternPattern")) {
  87. $this.attr("pattern", $this.data("validationPatternPattern"));
  88. }
  89. if ($this.attr("pattern") !== undefined) {
  90. message = "Not in the expected format<!-- data-validation-pattern-message to override -->";
  91. if ($this.data("validationPatternMessage")) {
  92. message = $this.data("validationPatternMessage");
  93. }
  94. $this.data("validationPatternMessage", message);
  95. $this.data("validationPatternRegex", $this.attr("pattern"));
  96. }
  97. // ---------------------------------------------------------
  98. // MAX
  99. // ---------------------------------------------------------
  100. if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) {
  101. var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax"));
  102. message = "Too high: Maximum of '" + max + "'<!-- data-validation-max-message to override -->";
  103. if ($this.data("validationMaxMessage")) {
  104. message = $this.data("validationMaxMessage");
  105. }
  106. $this.data("validationMaxMessage", message);
  107. $this.data("validationMaxMax", max);
  108. }
  109. // ---------------------------------------------------------
  110. // MIN
  111. // ---------------------------------------------------------
  112. if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) {
  113. var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin"));
  114. message = "Too low: Minimum of '" + min + "'<!-- data-validation-min-message to override -->";
  115. if ($this.data("validationMinMessage")) {
  116. message = $this.data("validationMinMessage");
  117. }
  118. $this.data("validationMinMessage", message);
  119. $this.data("validationMinMin", min);
  120. }
  121. // ---------------------------------------------------------
  122. // MAXLENGTH
  123. // ---------------------------------------------------------
  124. if ($this.attr("maxlength") !== undefined) {
  125. message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters<!-- data-validation-maxlength-message to override -->";
  126. if ($this.data("validationMaxlengthMessage")) {
  127. message = $this.data("validationMaxlengthMessage");
  128. }
  129. $this.data("validationMaxlengthMessage", message);
  130. $this.data("validationMaxlengthMaxlength", $this.attr("maxlength"));
  131. }
  132. // ---------------------------------------------------------
  133. // MINLENGTH
  134. // ---------------------------------------------------------
  135. if ($this.attr("minlength") !== undefined) {
  136. message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters<!-- data-validation-minlength-message to override -->";
  137. if ($this.data("validationMinlengthMessage")) {
  138. message = $this.data("validationMinlengthMessage");
  139. }
  140. $this.data("validationMinlengthMessage", message);
  141. $this.data("validationMinlengthMinlength", $this.attr("minlength"));
  142. }
  143. // ---------------------------------------------------------
  144. // REQUIRED
  145. // ---------------------------------------------------------
  146. if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) {
  147. message = settings.builtInValidators.required.message;
  148. if ($this.data("validationRequiredMessage")) {
  149. message = $this.data("validationRequiredMessage");
  150. }
  151. $this.data("validationRequiredMessage", message);
  152. }
  153. // ---------------------------------------------------------
  154. // NUMBER
  155. // ---------------------------------------------------------
  156. if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") {
  157. message = settings.validatorTypes.number.message; // TODO: fix this
  158. if ($this.data("validationNumberMessage")) {
  159. message = $this.data("validationNumberMessage");
  160. }
  161. $this.data("validationNumberMessage", message);
  162. var step = settings.validatorTypes.number.step; // TODO: and this
  163. if ($this.data("validationNumberStep")) {
  164. step = $this.data("validationNumberStep");
  165. }
  166. $this.data("validationNumberStep", step);
  167. var decimal = settings.validatorTypes.number.decimal;
  168. if ($this.data("validationNumberDecimal")) {
  169. decimal = $this.data("validationNumberDecimal");
  170. }
  171. $this.data("validationNumberDecimal", decimal);
  172. }
  173. // ---------------------------------------------------------
  174. // EMAIL
  175. // ---------------------------------------------------------
  176. if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") {
  177. message = "Not a valid email address<!-- data-validation-email-message to override -->";
  178. if ($this.data("validationEmailMessage")) {
  179. message = $this.data("validationEmailMessage");
  180. }
  181. $this.data("validationEmailMessage", message);
  182. }
  183. // ---------------------------------------------------------
  184. // MINCHECKED
  185. // ---------------------------------------------------------
  186. if ($this.attr("minchecked") !== undefined) {
  187. message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required<!-- data-validation-minchecked-message to override -->";
  188. if ($this.data("validationMincheckedMessage")) {
  189. message = $this.data("validationMincheckedMessage");
  190. }
  191. $this.data("validationMincheckedMessage", message);
  192. $this.data("validationMincheckedMinchecked", $this.attr("minchecked"));
  193. }
  194. // ---------------------------------------------------------
  195. // MAXCHECKED
  196. // ---------------------------------------------------------
  197. if ($this.attr("maxchecked") !== undefined) {
  198. message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required<!-- data-validation-maxchecked-message to override -->";
  199. if ($this.data("validationMaxcheckedMessage")) {
  200. message = $this.data("validationMaxcheckedMessage");
  201. }
  202. $this.data("validationMaxcheckedMessage", message);
  203. $this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked"));
  204. }
  205. }
  206. // =============================================================
  207. // COLLECT VALIDATOR NAMES
  208. // =============================================================
  209. // Get named validators
  210. if ($this.data("validation") !== undefined) {
  211. validatorNames = $this.data("validation").split(",");
  212. }
  213. // Get extra ones defined on the element's data attributes
  214. $.each($this.data(), function (i, el) {
  215. var parts = i.replace(/([A-Z])/g, ",$1").split(",");
  216. if (parts[0] === "validation" && parts[1]) {
  217. validatorNames.push(parts[1]);
  218. }
  219. });
  220. // =============================================================
  221. // NORMALISE VALIDATOR NAMES
  222. // =============================================================
  223. var validatorNamesToInspect = validatorNames;
  224. var newValidatorNamesToInspect = [];
  225. var uppercaseEachValidatorName = function (i, el) {
  226. validatorNames[i] = formatValidatorName(el);
  227. };
  228. var inspectValidators = function(i, el) {
  229. if ($this.data("validation" + el + "Shortcut") !== undefined) {
  230. // Are these custom validators?
  231. // Pull them out!
  232. $.each($this.data("validation" + el + "Shortcut").split(","), function(i2, el2) {
  233. newValidatorNamesToInspect.push(el2);
  234. });
  235. } else if (settings.builtInValidators[el.toLowerCase()]) {
  236. // Is this a recognised built-in?
  237. // Pull it out!
  238. var validator = settings.builtInValidators[el.toLowerCase()];
  239. if (validator.type.toLowerCase() === "shortcut") {
  240. $.each(validator.shortcut.split(","), function (i, el) {
  241. el = formatValidatorName(el);
  242. newValidatorNamesToInspect.push(el);
  243. validatorNames.push(el);
  244. });
  245. }
  246. }
  247. };
  248. do // repeatedly expand 'shortcut' validators into their real validators
  249. {
  250. // Uppercase only the first letter of each name
  251. $.each(validatorNames, uppercaseEachValidatorName);
  252. // Remove duplicate validator names
  253. validatorNames = $.unique(validatorNames);
  254. // Pull out the new validator names from each shortcut
  255. newValidatorNamesToInspect = [];
  256. $.each(validatorNamesToInspect, inspectValidators);
  257. validatorNamesToInspect = newValidatorNamesToInspect;
  258. } while (validatorNamesToInspect.length > 0);
  259. // =============================================================
  260. // SET UP VALIDATOR ARRAYS
  261. // =============================================================
  262. var validators = {};
  263. $.each(validatorNames, function (i, el) {
  264. // Set up the 'override' message
  265. var message = $this.data("validation" + el + "Message");
  266. var hasOverrideMessage = !!message;
  267. var foundValidator = false;
  268. if (!message) {
  269. message = "'" + el + "' validation failed <!-- Add attribute 'data-validation-" + el.toLowerCase() + "-message' to input to change this message -->";
  270. }
  271. $.each(
  272. settings.validatorTypes,
  273. function (validatorType, validatorTemplate) {
  274. if (validators[validatorType] === undefined) {
  275. validators[validatorType] = [];
  276. }
  277. if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) {
  278. var initted = validatorTemplate.init($this, el);
  279. if (hasOverrideMessage) {
  280. initted.message = message;
  281. }
  282. validators[validatorType].push(
  283. $.extend(
  284. true,
  285. {
  286. name: formatValidatorName(validatorTemplate.name),
  287. message: message
  288. },
  289. initted
  290. )
  291. );
  292. foundValidator = true;
  293. }
  294. }
  295. );
  296. if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) {
  297. var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]);
  298. if (hasOverrideMessage) {
  299. validator.message = message;
  300. }
  301. var validatorType = validator.type.toLowerCase();
  302. if (validatorType === "shortcut") {
  303. foundValidator = true;
  304. } else {
  305. $.each(
  306. settings.validatorTypes,
  307. function (validatorTemplateType, validatorTemplate) {
  308. if (validators[validatorTemplateType] === undefined) {
  309. validators[validatorTemplateType] = [];
  310. }
  311. if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) {
  312. $this.data(
  313. "validation" + el + formatValidatorName(validatorTemplate.name),
  314. validator[validatorTemplate.name.toLowerCase()]
  315. );
  316. validators[validatorType].push(
  317. $.extend(
  318. validator,
  319. validatorTemplate.init($this, el)
  320. )
  321. );
  322. foundValidator = true;
  323. }
  324. }
  325. );
  326. }
  327. }
  328. if (! foundValidator) {
  329. $.error("Cannot find validation info for '" + el + "'");
  330. }
  331. });
  332. // =============================================================
  333. // STORE FALLBACK VALUES
  334. // =============================================================
  335. $helpBlock.data(
  336. "original-contents",
  337. (
  338. $helpBlock.data("original-contents") ?
  339. $helpBlock.data("original-contents") :
  340. $helpBlock.html()
  341. )
  342. );
  343. $helpBlock.data(
  344. "original-role",
  345. (
  346. $helpBlock.data("original-role") ?
  347. $helpBlock.data("original-role") :
  348. $helpBlock.attr("role")
  349. )
  350. );
  351. $controlGroup.data(
  352. "original-classes",
  353. (
  354. $controlGroup.data("original-clases") ?
  355. $controlGroup.data("original-classes") :
  356. $controlGroup.attr("class")
  357. )
  358. );
  359. $this.data(
  360. "original-aria-invalid",
  361. (
  362. $this.data("original-aria-invalid") ?
  363. $this.data("original-aria-invalid") :
  364. $this.attr("aria-invalid")
  365. )
  366. );
  367. // =============================================================
  368. // VALIDATION
  369. // =============================================================
  370. $this.bind(
  371. "validation.validation",
  372. function (event, params) {
  373. var value = getValue($this);
  374. // Get a list of the errors to apply
  375. var errorsFound = [];
  376. $.each(validators, function (validatorType, validatorTypeArray) {
  377. if (
  378. value || // has a truthy value
  379. value.length || // not an empty string
  380. ( // am including empty values
  381. (
  382. params &&
  383. params.includeEmpty
  384. ) ||
  385. !!settings.validatorTypes[validatorType].includeEmpty
  386. ) ||
  387. ( // validator is blocking submit
  388. !!settings.validatorTypes[validatorType].blockSubmit &&
  389. params &&
  390. !!params.submitting
  391. )
  392. )
  393. {
  394. $.each(
  395. validatorTypeArray,
  396. function (i, validator) {
  397. if (settings.validatorTypes[validatorType].validate($this, value, validator)) {
  398. errorsFound.push(validator.message);
  399. }
  400. }
  401. );
  402. }
  403. });
  404. return errorsFound;
  405. }
  406. );
  407. $this.bind(
  408. "getValidators.validation",
  409. function () {
  410. return validators;
  411. }
  412. );
  413. // =============================================================
  414. // WATCH FOR CHANGES
  415. // =============================================================
  416. $this.bind(
  417. "submit.validation",
  418. function () {
  419. return $this.triggerHandler("change.validation", {submitting: true});
  420. }
  421. );
  422. $this.bind(
  423. (
  424. settings.options.bindEvents.length > 0 ?
  425. settings.options.bindEvents :
  426. [
  427. "keyup",
  428. "focus",
  429. "blur",
  430. "click",
  431. "keydown",
  432. "keypress",
  433. "change"
  434. ]
  435. ).concat(["revalidate"]).join(".validation ") + ".validation",
  436. function (e, params) {
  437. var value = getValue($this);
  438. var errorsFound = [];
  439. if (params && !!params.submitting) {
  440. $controlGroup.data("jqbvIsSubmitting", true);
  441. } else if (e.type !== "revalidate") {
  442. $controlGroup.data("jqbvIsSubmitting", false);
  443. }
  444. var formIsSubmitting = !!$controlGroup.data("jqbvIsSubmitting");
  445. $controlGroup.find("input,textarea,select").each(function (i, el) {
  446. var oldCount = errorsFound.length;
  447. $.each($(el).triggerHandler("validation.validation", params), function (j, message) {
  448. errorsFound.push(message);
  449. });
  450. if (errorsFound.length > oldCount) {
  451. $(el).attr("aria-invalid", "true");
  452. } else {
  453. var original = $this.data("original-aria-invalid");
  454. $(el).attr("aria-invalid", (original !== undefined ? original : false));
  455. }
  456. });
  457. $form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation");
  458. errorsFound = $.unique(errorsFound.sort());
  459. // Were there any errors?
  460. if (errorsFound.length) {
  461. // Better flag it up as a warning.
  462. $controlGroup.removeClass("success error warning").addClass(formIsSubmitting ? "error" : "warning");
  463. // How many errors did we find?
  464. if (settings.options.semanticallyStrict && errorsFound.length === 1) {
  465. // Only one? Being strict? Just output it.
  466. $helpBlock.html(errorsFound[0] +
  467. ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" ));
  468. } else {
  469. // Multiple? Being sloppy? Glue them together into an UL.
  470. $helpBlock.html("<ul role=\"alert\"><li>" + errorsFound.join("</li><li>") + "</li></ul>" +
  471. ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" ));
  472. }
  473. } else {
  474. $controlGroup.removeClass("warning error success");
  475. if (value.length > 0) {
  476. $controlGroup.addClass("success");
  477. }
  478. $helpBlock.html($helpBlock.data("original-contents"));
  479. }
  480. if (e.type === "blur") {
  481. $controlGroup.removeClass("success");
  482. }
  483. }
  484. );
  485. $this.bind("validationLostFocus.validation", function () {
  486. $controlGroup.removeClass("success");
  487. });
  488. });
  489. },
  490. destroy : function( ) {
  491. return this.each(
  492. function() {
  493. var
  494. $this = $(this),
  495. $controlGroup = $this.parents(".control-group").first(),
  496. $helpBlock = $controlGroup.find(".help-block").first();
  497. // remove our events
  498. $this.unbind('.validation'); // events are namespaced.
  499. // reset help text
  500. $helpBlock.html($helpBlock.data("original-contents"));
  501. // reset classes
  502. $controlGroup.attr("class", $controlGroup.data("original-classes"));
  503. // reset aria
  504. $this.attr("aria-invalid", $this.data("original-aria-invalid"));
  505. // reset role
  506. $helpBlock.attr("role", $this.data("original-role"));
  507. // remove all elements we created
  508. if ($.inArray($helpBlock[0], createdElements) > -1) {
  509. $helpBlock.remove();
  510. }
  511. }
  512. );
  513. },
  514. collectErrors : function(includeEmpty) {
  515. var errorMessages = {};
  516. this.each(function (i, el) {
  517. var $el = $(el);
  518. var name = $el.attr("name");
  519. var errors = $el.triggerHandler("validation.validation", {includeEmpty: true});
  520. errorMessages[name] = $.extend(true, errors, errorMessages[name]);
  521. });
  522. $.each(errorMessages, function (i, el) {
  523. if (el.length === 0) {
  524. delete errorMessages[i];
  525. }
  526. });
  527. return errorMessages;
  528. },
  529. hasErrors: function() {
  530. var errorMessages = [];
  531. this.find('input,select,textarea').add(this).each(function (i, el) {
  532. errorMessages = errorMessages.concat(
  533. $(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {submitting: true}) : []
  534. );
  535. });
  536. return (errorMessages.length > 0);
  537. },
  538. override : function (newDefaults) {
  539. defaults = $.extend(true, defaults, newDefaults);
  540. }
  541. },
  542. validatorTypes: {
  543. callback: {
  544. name: "callback",
  545. init: function($this, name) {
  546. var result = {
  547. validatorName: name,
  548. callback: $this.data("validation" + name + "Callback"),
  549. lastValue: $this.val(),
  550. lastValid: true,
  551. lastFinished: true
  552. };
  553. var message = "Not valid";
  554. if ($this.data("validation" + name + "Message")) {
  555. message = $this.data("validation" + name + "Message");
  556. }
  557. result.message = message;
  558. return result;
  559. },
  560. validate: function($this, value, validator) {
  561. if (validator.lastValue === value && validator.lastFinished) {
  562. return !validator.lastValid;
  563. }
  564. if (validator.lastFinished === true)
  565. {
  566. validator.lastValue = value;
  567. validator.lastValid = true;
  568. validator.lastFinished = false;
  569. var rrjqbvValidator = validator;
  570. var rrjqbvThis = $this;
  571. executeFunctionByName(
  572. validator.callback,
  573. window,
  574. $this,
  575. value,
  576. function(data) {
  577. if (rrjqbvValidator.lastValue === data.value) {
  578. rrjqbvValidator.lastValid = data.valid;
  579. if (data.message) {
  580. rrjqbvValidator.message = data.message;
  581. }
  582. rrjqbvValidator.lastFinished = true;
  583. rrjqbvThis.data(
  584. "validation" + rrjqbvValidator.validatorName + "Message",
  585. rrjqbvValidator.message
  586. );
  587. // Timeout is set to avoid problems with the events being considered 'already fired'
  588. setTimeout(function() {
  589. if (!$this.is(":focus") && $this.parents("form").first().data("jqbvIsSubmitting")) {
  590. rrjqbvThis.trigger("blur.validation");
  591. } else {
  592. rrjqbvThis.trigger("revalidate.validation");
  593. }
  594. }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
  595. }
  596. }
  597. );
  598. }
  599. return false;
  600. }
  601. },
  602. ajax: {
  603. name: "ajax",
  604. init: function ($this, name) {
  605. return {
  606. validatorName: name,
  607. url: $this.data("validation" + name + "Ajax"),
  608. lastValue: $this.val(),
  609. lastValid: true,
  610. lastFinished: true
  611. };
  612. },
  613. validate: function ($this, value, validator) {
  614. if (""+validator.lastValue === ""+value && validator.lastFinished === true) {
  615. return validator.lastValid === false;
  616. }
  617. if (validator.lastFinished === true)
  618. {
  619. validator.lastValue = value;
  620. validator.lastValid = true;
  621. validator.lastFinished = false;
  622. $.ajax({
  623. url: validator.url,
  624. data: "value=" + encodeURIComponent(value) + "&field=" + $this.attr("name"),
  625. dataType: "json",
  626. success: function (data) {
  627. if (""+validator.lastValue === ""+data.value) {
  628. validator.lastValid = !!(data.valid);
  629. if (data.message) {
  630. validator.message = data.message;
  631. }
  632. validator.lastFinished = true;
  633. $this.data("validation" + validator.validatorName + "Message", validator.message);
  634. // Timeout is set to avoid problems with the events being considered 'already fired'
  635. setTimeout(function () {
  636. $this.trigger("revalidate.validation");
  637. }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
  638. }
  639. },
  640. failure: function () {
  641. validator.lastValid = true;
  642. validator.message = "ajax call failed";
  643. validator.lastFinished = true;
  644. $this.data("validation" + validator.validatorName + "Message", validator.message);
  645. // Timeout is set to avoid problems with the events being considered 'already fired'
  646. setTimeout(function () {
  647. $this.trigger("revalidate.validation");
  648. }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
  649. }
  650. });
  651. }
  652. return false;
  653. }
  654. },
  655. regex: {
  656. name: "regex",
  657. init: function ($this, name) {
  658. var result = {};
  659. var regexString = $this.data("validation" + name + "Regex");
  660. result.regex = regexFromString(regexString);
  661. if (regexString === undefined) {
  662. $.error("Can't find regex for '" + name + "' validator on '" + $this.attr("name") + "'");
  663. }
  664. var message = "Not in the expected format";
  665. if ($this.data("validation" + name + "Message")) {
  666. message = $this.data("validation" + name + "Message");
  667. }
  668. result.message = message;
  669. result.originalName = name;
  670. return result;
  671. },
  672. validate: function ($this, value, validator) {
  673. return (!validator.regex.test(value) && ! validator.negative) ||
  674. (validator.regex.test(value) && validator.negative);
  675. }
  676. },
  677. email: {
  678. name: "email",
  679. init: function ($this, name) {
  680. var result = {};
  681. result.regex = regexFromString('[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}');
  682. var message = "Not a valid email address";
  683. if ($this.data("validation" + name + "Message")) {
  684. message = $this.data("validation" + name + "Message");
  685. }
  686. result.message = message;
  687. result.originalName = name;
  688. return result;
  689. },
  690. validate: function ($this, value, validator) {
  691. return (!validator.regex.test(value) && ! validator.negative) ||
  692. (validator.regex.test(value) && validator.negative);
  693. }
  694. },
  695. required: {
  696. name: "required",
  697. init: function ($this, name) {
  698. var message = "This is required";
  699. if ($this.data("validation" + name + "Message")) {
  700. message = $this.data("validation" + name + "Message");
  701. }
  702. return {message: message};
  703. },
  704. validate: function ($this, value, validator) {
  705. return !!(value.length === 0 && ! validator.negative) ||
  706. !!(value.length > 0 && validator.negative);
  707. },
  708. blockSubmit: true
  709. },
  710. match: {
  711. name: "match",
  712. init: function ($this, name) {
  713. var elementName = $this.data("validation" + name + "Match");
  714. var $form = $this.parents("form").first();
  715. var $element = $form.find("[name=\"" + elementName + "\"]").first();
  716. $element.bind("validation.validation", function () {
  717. $this.trigger("revalidate.validation", {submitting: true});
  718. });
  719. var result = {};
  720. result.element = $element;
  721. if ($element.length === 0) {
  722. $.error("Can't find field '" + elementName + "' to match '" + $this.attr("name") + "' against in '" + name + "' validator");
  723. }
  724. var message = "Must match";
  725. var $label = null;
  726. if (($label = $form.find("label[for=\"" + elementName + "\"]")).length) {
  727. message += " '" + $label.text() + "'";
  728. } else if (($label = $element.parents(".control-group").first().find("label")).length) {
  729. message += " '" + $label.first().text() + "'";
  730. }
  731. if ($this.data("validation" + name + "Message")) {
  732. message = $this.data("validation" + name + "Message");
  733. }
  734. result.message = message;
  735. return result;
  736. },
  737. validate: function ($this, value, validator) {
  738. return (value !== validator.element.val() && ! validator.negative) ||
  739. (value === validator.element.val() && validator.negative);
  740. },
  741. blockSubmit: true,
  742. includeEmpty: true
  743. },
  744. max: {
  745. name: "max",
  746. init: function ($this, name) {
  747. var result = {};
  748. result.max = $this.data("validation" + name + "Max");
  749. result.message = "Too high: Maximum of '" + result.max + "'";
  750. if ($this.data("validation" + name + "Message")) {
  751. result.message = $this.data("validation" + name + "Message");
  752. }
  753. return result;
  754. },
  755. validate: function ($this, value, validator) {
  756. return (parseFloat(value, 10) > parseFloat(validator.max, 10) && ! validator.negative) ||
  757. (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative);
  758. }
  759. },
  760. min: {
  761. name: "min",
  762. init: function ($this, name) {
  763. var result = {};
  764. result.min = $this.data("validation" + name + "Min");
  765. result.message = "Too low: Minimum of '" + result.min + "'";
  766. if ($this.data("validation" + name + "Message")) {
  767. result.message = $this.data("validation" + name + "Message");
  768. }
  769. return result;
  770. },
  771. validate: function ($this, value, validator) {
  772. return (parseFloat(value) < parseFloat(validator.min) && ! validator.negative) ||
  773. (parseFloat(value) >= parseFloat(validator.min) && validator.negative);
  774. }
  775. },
  776. maxlength: {
  777. name: "maxlength",
  778. init: function ($this, name) {
  779. var result = {};
  780. result.maxlength = $this.data("validation" + name + "Maxlength");
  781. result.message = "Too long: Maximum of '" + result.maxlength + "' characters";
  782. if ($this.data("validation" + name + "Message")) {
  783. result.message = $this.data("validation" + name + "Message");
  784. }
  785. return result;
  786. },
  787. validate: function ($this, value, validator) {
  788. return ((value.length > validator.maxlength) && ! validator.negative) ||
  789. ((value.length <= validator.maxlength) && validator.negative);
  790. }
  791. },
  792. minlength: {
  793. name: "minlength",
  794. init: function ($this, name) {
  795. var result = {};
  796. result.minlength = $this.data("validation" + name + "Minlength");
  797. result.message = "Too short: Minimum of '" + result.minlength + "' characters";
  798. if ($this.data("validation" + name + "Message")) {
  799. result.message = $this.data("validation" + name + "Message");
  800. }
  801. return result;
  802. },
  803. validate: function ($this, value, validator) {
  804. return ((value.length < validator.minlength) && ! validator.negative) ||
  805. ((value.length >= validator.minlength) && validator.negative);
  806. }
  807. },
  808. maxchecked: {
  809. name: "maxchecked",
  810. init: function ($this, name) {
  811. var result = {};
  812. var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
  813. elements.bind("change.validation click.validation", function () {
  814. $this.trigger("revalidate.validation", {includeEmpty: true});
  815. });
  816. result.elements = elements;
  817. result.maxchecked = $this.data("validation" + name + "Maxchecked");
  818. var message = "Too many: Max '" + result.maxchecked + "' checked";
  819. if ($this.data("validation" + name + "Message")) {
  820. message = $this.data("validation" + name + "Message");
  821. }
  822. result.message = message;
  823. return result;
  824. },
  825. validate: function ($this, value, validator) {
  826. return (validator.elements.filter(":checked").length > validator.maxchecked && ! validator.negative) ||
  827. (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative);
  828. },
  829. blockSubmit: true
  830. },
  831. minchecked: {
  832. name: "minchecked",
  833. init: function ($this, name) {
  834. var result = {};
  835. var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
  836. elements.bind("change.validation click.validation", function () {
  837. $this.trigger("revalidate.validation", {includeEmpty: true});
  838. });
  839. result.elements = elements;
  840. result.minchecked = $this.data("validation" + name + "Minchecked");
  841. var message = "Too few: Min '" + result.minchecked + "' checked";
  842. if ($this.data("validation" + name + "Message")) {
  843. message = $this.data("validation" + name + "Message");
  844. }
  845. result.message = message;
  846. return result;
  847. },
  848. validate: function ($this, value, validator) {
  849. return (validator.elements.filter(":checked").length < validator.minchecked && ! validator.negative) ||
  850. (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative);
  851. },
  852. blockSubmit: true,
  853. includeEmpty: true
  854. },
  855. number: {
  856. name: "number",
  857. init: function ($this, name) {
  858. var result = {};
  859. result.step = 1;
  860. if ($this.attr("step")) {
  861. result.step = $this.attr("step");
  862. }
  863. if ($this.data("validation" + name + "Step")) {
  864. result.step = $this.data("validation" + name + "Step");
  865. }
  866. result.decimal = ".";
  867. if ($this.data("validation" + name + "Decimal")) {
  868. result.decimal = $this.data("validation" + name + "Decimal");
  869. }
  870. result.thousands = "";
  871. if ($this.data("validation" + name + "Thousands")) {
  872. result.thousands = $this.data("validation" + name + "Thousands");
  873. }
  874. result.regex = regexFromString("([+-]?\\d+(\\" + result.decimal + "\\d+)?)?");
  875. result.message = "Must be a number";
  876. var dataMessage = $this.data("validation" + name + "Message");
  877. if (dataMessage) {
  878. result.message = dataMessage;
  879. }
  880. return result;
  881. },
  882. validate: function ($this, value, validator) {
  883. var globalValue = value.replace(validator.decimal, ".").replace(validator.thousands, "");
  884. var multipliedValue = parseFloat(globalValue);
  885. var multipliedStep = parseFloat(validator.step);
  886. while (multipliedStep % 1 !== 0) {
  887. multipliedStep *= 10;
  888. multipliedValue *= 10;
  889. }
  890. var regexResult = validator.regex.test(value);
  891. var stepResult = parseFloat(multipliedValue) % parseFloat(multipliedStep) === 0;
  892. var typeResult = !isNaN(parseFloat(globalValue)) && isFinite(globalValue);
  893. var result = !(regexResult && stepResult && typeResult);
  894. return result;
  895. },
  896. message: "Must be a number"
  897. }
  898. },
  899. builtInValidators: {
  900. email: {
  901. name: "Email",
  902. type: "email"
  903. },
  904. passwordagain: {
  905. name: "Passwordagain",
  906. type: "match",
  907. match: "password",
  908. message: "Does not match the given password<!-- data-validator-paswordagain-message to override -->"
  909. },
  910. positive: {
  911. name: "Positive",
  912. type: "shortcut",
  913. shortcut: "number,positivenumber"
  914. },
  915. negative: {
  916. name: "Negative",
  917. type: "shortcut",
  918. shortcut: "number,negativenumber"
  919. },
  920. integer: {
  921. name: "Integer",
  922. type: "regex",
  923. regex: "[+-]?\\d+",
  924. message: "No decimal places allowed<!-- data-validator-integer-message to override -->"
  925. },
  926. positivenumber: {
  927. name: "Positivenumber",
  928. type: "min",
  929. min: 0,
  930. message: "Must be a positive number<!-- data-validator-positivenumber-message to override -->"
  931. },
  932. negativenumber: {
  933. name: "Negativenumber",
  934. type: "max",
  935. max: 0,
  936. message: "Must be a negative number<!-- data-validator-negativenumber-message to override -->"
  937. },
  938. required: {
  939. name: "Required",
  940. type: "required",
  941. message: "This is required<!-- data-validator-required-message to override -->"
  942. },
  943. checkone: {
  944. name: "Checkone",
  945. type: "minchecked",
  946. minchecked: 1,
  947. message: "Check at least one option<!-- data-validation-checkone-message to override -->"
  948. },
  949. number: {
  950. name: "Number",
  951. type: "number",
  952. decimal: ".",
  953. step: "1"
  954. },
  955. pattern: {
  956. name: "Pattern",
  957. type: "regex",
  958. message: "Not in expected format"
  959. }
  960. }
  961. };
  962. var formatValidatorName = function (name) {
  963. return name
  964. .toLowerCase()
  965. .replace(
  966. /(^|\s)([a-z])/g ,
  967. function(m,p1,p2) {
  968. return p1+p2.toUpperCase();
  969. }
  970. )
  971. ;
  972. };
  973. var getValue = function ($this) {
  974. // Extract the value we're talking about
  975. var value = $this.val();
  976. var type = $this.attr("type");
  977. var parent = null;
  978. var hasParent = !!(parent = $this.parents("form").first()) || !!(parent = $this.parents(".control-group").first());
  979. if (type === "checkbox") {
  980. value = ($this.is(":checked") ? value : "");
  981. if (hasParent) {
  982. value = parent.find("input[type='checkbox'][name='" + $this.attr("name") + "']:checked").map(function (i, el) { return $(el).val(); }).toArray().join(",");
  983. }
  984. }
  985. if (type === "radio") {
  986. value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? value : "");
  987. if (hasParent) {
  988. value = parent.find("input[type='radio'][name='" + $this.attr("name") + "']:checked").map(function (i, el) { return $(el).val(); }).toArray().join(",");
  989. }
  990. }
  991. return value;
  992. };
  993. function regexFromString(inputstring) {
  994. return new RegExp("^" + inputstring + "$");
  995. }
  996. /**
  997. * Thanks to Jason Bunting / Alex Nazarov via StackOverflow.com
  998. *
  999. * http://stackoverflow.com/a/4351575
  1000. **/
  1001. function executeFunctionByName(functionName, context /*, args */) {
  1002. var args = Array.prototype.slice.call(arguments, 2);
  1003. var namespaces = functionName.split(".");
  1004. var func = namespaces.pop();
  1005. for (var i = 0; i < namespaces.length; i++) {
  1006. context = context[namespaces[i]];
  1007. }
  1008. return context[func].apply(context, args);
  1009. }
  1010. $.fn.jqBootstrapValidation = function( method ) {
  1011. if ( defaults.methods[method] ) {
  1012. return defaults.methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
  1013. } else if ( typeof method === 'object' || ! method ) {
  1014. return defaults.methods.init.apply( this, arguments );
  1015. } else {
  1016. $.error( 'Method ' + method + ' does not exist on jQuery.jqBootstrapValidation' );
  1017. return null;
  1018. }
  1019. };
  1020. $.jqBootstrapValidation = function (options) {
  1021. $(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments);
  1022. };
  1023. })( jQuery );