Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

jquery.validate.js 64KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605
  1. /*!
  2. * jQuery Validation Plugin v1.17.0
  3. *
  4. * https://jqueryvalidation.org/
  5. *
  6. * Copyright (c) 2017 Jörn Zaefferer
  7. * Released under the MIT license
  8. */
  9. (function (factory) {
  10. if (typeof define === "function" && define.amd) {
  11. define(["jquery"], factory);
  12. } else if (typeof module === "object" && module.exports) {
  13. module.exports = factory(require("jquery"));
  14. } else {
  15. factory(jQuery);
  16. }
  17. }(function ($) {
  18. $.extend($.fn, {
  19. // https://jqueryvalidation.org/validate/
  20. validate: function (options) {
  21. // If nothing is selected, return nothing; can't chain anyway
  22. if (!this.length) {
  23. if (options && options.debug && window.console) {
  24. console.warn("Nothing selected, can't validate, returning nothing.");
  25. }
  26. return;
  27. }
  28. // Check if a validator for this form was already created
  29. var validator = $.data(this[0], "validator");
  30. if (validator) {
  31. return validator;
  32. }
  33. // Add novalidate tag if HTML5.
  34. this.attr("novalidate", "novalidate");
  35. validator = new $.validator(options, this[0]);
  36. $.data(this[0], "validator", validator);
  37. if (validator.settings.onsubmit) {
  38. this.on("click.validate", ":submit", function (event) {
  39. // Track the used submit button to properly handle scripted
  40. // submits later.
  41. validator.submitButton = event.currentTarget;
  42. // Allow suppressing validation by adding a cancel class to the submit button
  43. if ($(this).hasClass("cancel")) {
  44. validator.cancelSubmit = true;
  45. }
  46. // Allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
  47. if ($(this).attr("formnovalidate") !== undefined) {
  48. validator.cancelSubmit = true;
  49. }
  50. });
  51. // Validate the form on submit
  52. this.on("submit.validate", function (event) {
  53. if (validator.settings.debug) {
  54. // Prevent form submit to be able to see console output
  55. event.preventDefault();
  56. }
  57. function handle() {
  58. var hidden, result;
  59. // Insert a hidden input as a replacement for the missing submit button
  60. // The hidden input is inserted in two cases:
  61. // - A user defined a `submitHandler`
  62. // - There was a pending request due to `remote` method and `stopRequest()`
  63. // was called to submit the form in case it's valid
  64. if (validator.submitButton && (validator.settings.submitHandler || validator.formSubmitted)) {
  65. hidden = $("<input type='hidden'/>")
  66. .attr("name", validator.submitButton.name)
  67. .val($(validator.submitButton).val())
  68. .appendTo(validator.currentForm);
  69. }
  70. if (validator.settings.submitHandler) {
  71. result = validator.settings.submitHandler.call(validator, validator.currentForm, event);
  72. if (hidden) {
  73. // And clean up afterwards; thanks to no-block-scope, hidden can be referenced
  74. hidden.remove();
  75. }
  76. if (result !== undefined) {
  77. return result;
  78. }
  79. return false;
  80. }
  81. return true;
  82. }
  83. // Prevent submit for invalid forms or custom submit handlers
  84. if (validator.cancelSubmit) {
  85. validator.cancelSubmit = false;
  86. return handle();
  87. }
  88. if (validator.form()) {
  89. if (validator.pendingRequest) {
  90. validator.formSubmitted = true;
  91. return false;
  92. }
  93. return handle();
  94. } else {
  95. validator.focusInvalid();
  96. return false;
  97. }
  98. });
  99. }
  100. return validator;
  101. },
  102. // https://jqueryvalidation.org/valid/
  103. valid: function () {
  104. var valid, validator, errorList;
  105. if ($(this[0]).is("form")) {
  106. valid = this.validate().form();
  107. } else {
  108. errorList = [];
  109. valid = true;
  110. validator = $(this[0].form).validate();
  111. this.each(function () {
  112. valid = validator.element(this) && valid;
  113. if (!valid) {
  114. errorList = errorList.concat(validator.errorList);
  115. }
  116. });
  117. validator.errorList = errorList;
  118. }
  119. return valid;
  120. },
  121. // https://jqueryvalidation.org/rules/
  122. rules: function (command, argument) {
  123. var element = this[0],
  124. settings, staticRules, existingRules, data, param, filtered;
  125. // If nothing is selected, return empty object; can't chain anyway
  126. if (element == null) {
  127. return;
  128. }
  129. if (!element.form && element.hasAttribute("contenteditable")) {
  130. element.form = this.closest("form")[0];
  131. element.name = this.attr("name");
  132. }
  133. if (element.form == null) {
  134. return;
  135. }
  136. if (command) {
  137. settings = $.data(element.form, "validator").settings;
  138. staticRules = settings.rules;
  139. existingRules = $.validator.staticRules(element);
  140. switch (command) {
  141. case "add":
  142. $.extend(existingRules, $.validator.normalizeRule(argument));
  143. // Remove messages from rules, but allow them to be set separately
  144. delete existingRules.messages;
  145. staticRules[element.name] = existingRules;
  146. if (argument.messages) {
  147. settings.messages[element.name] = $.extend(settings.messages[element.name], argument.messages);
  148. }
  149. break;
  150. case "remove":
  151. if (!argument) {
  152. delete staticRules[element.name];
  153. return existingRules;
  154. }
  155. filtered = {};
  156. $.each(argument.split(/\s/), function (index, method) {
  157. filtered[method] = existingRules[method];
  158. delete existingRules[method];
  159. });
  160. return filtered;
  161. }
  162. }
  163. data = $.validator.normalizeRules(
  164. $.extend(
  165. {},
  166. $.validator.classRules(element),
  167. $.validator.attributeRules(element),
  168. $.validator.dataRules(element),
  169. $.validator.staticRules(element)
  170. ), element);
  171. // Make sure required is at front
  172. if (data.required) {
  173. param = data.required;
  174. delete data.required;
  175. data = $.extend({required: param}, data);
  176. }
  177. // Make sure remote is at back
  178. if (data.remote) {
  179. param = data.remote;
  180. delete data.remote;
  181. data = $.extend(data, {remote: param});
  182. }
  183. return data;
  184. }
  185. });
  186. // Custom selectors
  187. $.extend($.expr.pseudos || $.expr[":"], { // '|| $.expr[ ":" ]' here enables backwards compatibility to jQuery 1.7. Can be removed when dropping jQ 1.7.x support
  188. // https://jqueryvalidation.org/blank-selector/
  189. blank: function (a) {
  190. return !$.trim("" + $(a).val());
  191. },
  192. // https://jqueryvalidation.org/filled-selector/
  193. filled: function (a) {
  194. var val = $(a).val();
  195. return val !== null && !!$.trim("" + val);
  196. },
  197. // https://jqueryvalidation.org/unchecked-selector/
  198. unchecked: function (a) {
  199. return !$(a).prop("checked");
  200. }
  201. });
  202. // Constructor for validator
  203. $.validator = function (options, form) {
  204. this.settings = $.extend(true, {}, $.validator.defaults, options);
  205. this.currentForm = form;
  206. this.init();
  207. };
  208. // https://jqueryvalidation.org/jQuery.validator.format/
  209. $.validator.format = function (source, params) {
  210. if (arguments.length === 1) {
  211. return function () {
  212. var args = $.makeArray(arguments);
  213. args.unshift(source);
  214. return $.validator.format.apply(this, args);
  215. };
  216. }
  217. if (params === undefined) {
  218. return source;
  219. }
  220. if (arguments.length > 2 && params.constructor !== Array) {
  221. params = $.makeArray(arguments).slice(1);
  222. }
  223. if (params.constructor !== Array) {
  224. params = [params];
  225. }
  226. $.each(params, function (i, n) {
  227. source = source.replace(new RegExp("\\{" + i + "\\}", "g"), function () {
  228. return n;
  229. });
  230. });
  231. return source;
  232. };
  233. $.extend($.validator, {
  234. defaults: {
  235. messages: {},
  236. groups: {},
  237. rules: {},
  238. errorClass: "error",
  239. pendingClass: "pending",
  240. validClass: "valid",
  241. errorElement: "label",
  242. focusCleanup: false,
  243. focusInvalid: true,
  244. errorContainer: $([]),
  245. errorLabelContainer: $([]),
  246. onsubmit: true,
  247. ignore: ":hidden",
  248. ignoreTitle: false,
  249. onfocusin: function (element) {
  250. this.lastActive = element;
  251. // Hide error label and remove error class on focus if enabled
  252. if (this.settings.focusCleanup) {
  253. if (this.settings.unhighlight) {
  254. this.settings.unhighlight.call(this, element, this.settings.errorClass, this.settings.validClass);
  255. }
  256. this.hideThese(this.errorsFor(element));
  257. }
  258. },
  259. onfocusout: function (element) {
  260. if (!this.checkable(element) && (element.name in this.submitted || !this.optional(element))) {
  261. this.element(element);
  262. }
  263. },
  264. onkeyup: function (element, event) {
  265. // Avoid revalidate the field when pressing one of the following keys
  266. // Shift => 16
  267. // Ctrl => 17
  268. // Alt => 18
  269. // Caps lock => 20
  270. // End => 35
  271. // Home => 36
  272. // Left arrow => 37
  273. // Up arrow => 38
  274. // Right arrow => 39
  275. // Down arrow => 40
  276. // Insert => 45
  277. // Num lock => 144
  278. // AltGr key => 225
  279. var excludedKeys = [
  280. 16, 17, 18, 20, 35, 36, 37,
  281. 38, 39, 40, 45, 144, 225
  282. ];
  283. if (event.which === 9 && this.elementValue(element) === "" || $.inArray(event.keyCode, excludedKeys) !== -1) {
  284. } else if (element.name in this.submitted || element.name in this.invalid) {
  285. this.element(element);
  286. }
  287. },
  288. onclick: function (element) {
  289. // Click on selects, radiobuttons and checkboxes
  290. if (element.name in this.submitted) {
  291. this.element(element);
  292. // Or option elements, check parent select in that case
  293. } else if (element.parentNode.name in this.submitted) {
  294. this.element(element.parentNode);
  295. }
  296. },
  297. highlight: function (element, errorClass, validClass) {
  298. if (element.type === "radio") {
  299. this.findByName(element.name).addClass(errorClass).removeClass(validClass);
  300. } else {
  301. $(element).addClass(errorClass).removeClass(validClass);
  302. }
  303. },
  304. unhighlight: function (element, errorClass, validClass) {
  305. if (element.type === "radio") {
  306. this.findByName(element.name).removeClass(errorClass).addClass(validClass);
  307. } else {
  308. $(element).removeClass(errorClass).addClass(validClass);
  309. }
  310. }
  311. },
  312. // https://jqueryvalidation.org/jQuery.validator.setDefaults/
  313. setDefaults: function (settings) {
  314. $.extend($.validator.defaults, settings);
  315. },
  316. messages: {
  317. required: "This field is required.",
  318. remote: "Please fix this field.",
  319. email: "Please enter a valid email address.",
  320. url: "Please enter a valid URL.",
  321. date: "Please enter a valid date.",
  322. dateISO: "Please enter a valid date (ISO).",
  323. number: "Please enter a valid number.",
  324. digits: "Please enter only digits.",
  325. equalTo: "Please enter the same value again.",
  326. maxlength: $.validator.format("Please enter no more than {0} characters."),
  327. minlength: $.validator.format("Please enter at least {0} characters."),
  328. rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
  329. range: $.validator.format("Please enter a value between {0} and {1}."),
  330. max: $.validator.format("Please enter a value less than or equal to {0}."),
  331. min: $.validator.format("Please enter a value greater than or equal to {0}."),
  332. step: $.validator.format("Please enter a multiple of {0}.")
  333. },
  334. autoCreateRanges: false,
  335. prototype: {
  336. init: function () {
  337. this.labelContainer = $(this.settings.errorLabelContainer);
  338. this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
  339. this.containers = $(this.settings.errorContainer).add(this.settings.errorLabelContainer);
  340. this.submitted = {};
  341. this.valueCache = {};
  342. this.pendingRequest = 0;
  343. this.pending = {};
  344. this.invalid = {};
  345. this.reset();
  346. var groups = (this.groups = {}),
  347. rules;
  348. $.each(this.settings.groups, function (key, value) {
  349. if (typeof value === "string") {
  350. value = value.split(/\s/);
  351. }
  352. $.each(value, function (index, name) {
  353. groups[name] = key;
  354. });
  355. });
  356. rules = this.settings.rules;
  357. $.each(rules, function (key, value) {
  358. rules[key] = $.validator.normalizeRule(value);
  359. });
  360. function delegate(event) {
  361. // Set form expando on contenteditable
  362. if (!this.form && this.hasAttribute("contenteditable")) {
  363. this.form = $(this).closest("form")[0];
  364. this.name = $(this).attr("name");
  365. }
  366. var validator = $.data(this.form, "validator"),
  367. eventType = "on" + event.type.replace(/^validate/, ""),
  368. settings = validator.settings;
  369. if (settings[eventType] && !$(this).is(settings.ignore)) {
  370. settings[eventType].call(validator, this, event);
  371. }
  372. }
  373. $(this.currentForm)
  374. .on("focusin.validate focusout.validate keyup.validate",
  375. ":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " +
  376. "[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " +
  377. "[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " +
  378. "[type='radio'], [type='checkbox'], [contenteditable], [type='button']", delegate)
  379. // Support: Chrome, oldIE
  380. // "select" is provided as event.target when clicking a option
  381. .on("click.validate", "select, option, [type='radio'], [type='checkbox']", delegate);
  382. if (this.settings.invalidHandler) {
  383. $(this.currentForm).on("invalid-form.validate", this.settings.invalidHandler);
  384. }
  385. },
  386. // https://jqueryvalidation.org/Validator.form/
  387. form: function () {
  388. this.checkForm();
  389. $.extend(this.submitted, this.errorMap);
  390. this.invalid = $.extend({}, this.errorMap);
  391. if (!this.valid()) {
  392. $(this.currentForm).triggerHandler("invalid-form", [this]);
  393. }
  394. this.showErrors();
  395. return this.valid();
  396. },
  397. checkForm: function () {
  398. this.prepareForm();
  399. for (var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++) {
  400. this.check(elements[i]);
  401. }
  402. return this.valid();
  403. },
  404. // https://jqueryvalidation.org/Validator.element/
  405. element: function (element) {
  406. var cleanElement = this.clean(element),
  407. checkElement = this.validationTargetFor(cleanElement),
  408. v = this,
  409. result = true,
  410. rs, group;
  411. if (checkElement === undefined) {
  412. delete this.invalid[cleanElement.name];
  413. } else {
  414. this.prepareElement(checkElement);
  415. this.currentElements = $(checkElement);
  416. // If this element is grouped, then validate all group elements already
  417. // containing a value
  418. group = this.groups[checkElement.name];
  419. if (group) {
  420. $.each(this.groups, function (name, testgroup) {
  421. if (testgroup === group && name !== checkElement.name) {
  422. cleanElement = v.validationTargetFor(v.clean(v.findByName(name)));
  423. if (cleanElement && cleanElement.name in v.invalid) {
  424. v.currentElements.push(cleanElement);
  425. result = v.check(cleanElement) && result;
  426. }
  427. }
  428. });
  429. }
  430. rs = this.check(checkElement) !== false;
  431. result = result && rs;
  432. if (rs) {
  433. this.invalid[checkElement.name] = false;
  434. } else {
  435. this.invalid[checkElement.name] = true;
  436. }
  437. if (!this.numberOfInvalids()) {
  438. // Hide error containers on last error
  439. this.toHide = this.toHide.add(this.containers);
  440. }
  441. this.showErrors();
  442. // Add aria-invalid status for screen readers
  443. $(element).attr("aria-invalid", !rs);
  444. }
  445. return result;
  446. },
  447. // https://jqueryvalidation.org/Validator.showErrors/
  448. showErrors: function (errors) {
  449. if (errors) {
  450. var validator = this;
  451. // Add items to error list and map
  452. $.extend(this.errorMap, errors);
  453. this.errorList = $.map(this.errorMap, function (message, name) {
  454. return {
  455. message: message,
  456. element: validator.findByName(name)[0]
  457. };
  458. });
  459. // Remove items from success list
  460. this.successList = $.grep(this.successList, function (element) {
  461. return !(element.name in errors);
  462. });
  463. }
  464. if (this.settings.showErrors) {
  465. this.settings.showErrors.call(this, this.errorMap, this.errorList);
  466. } else {
  467. this.defaultShowErrors();
  468. }
  469. },
  470. // https://jqueryvalidation.org/Validator.resetForm/
  471. resetForm: function () {
  472. if ($.fn.resetForm) {
  473. $(this.currentForm).resetForm();
  474. }
  475. this.invalid = {};
  476. this.submitted = {};
  477. this.prepareForm();
  478. this.hideErrors();
  479. var elements = this.elements()
  480. .removeData("previousValue")
  481. .removeAttr("aria-invalid");
  482. this.resetElements(elements);
  483. },
  484. resetElements: function (elements) {
  485. var i;
  486. if (this.settings.unhighlight) {
  487. for (i = 0; elements[i]; i++) {
  488. this.settings.unhighlight.call(this, elements[i],
  489. this.settings.errorClass, "");
  490. this.findByName(elements[i].name).removeClass(this.settings.validClass);
  491. }
  492. } else {
  493. elements
  494. .removeClass(this.settings.errorClass)
  495. .removeClass(this.settings.validClass);
  496. }
  497. },
  498. numberOfInvalids: function () {
  499. return this.objectLength(this.invalid);
  500. },
  501. objectLength: function (obj) {
  502. /* jshint unused: false */
  503. var count = 0,
  504. i;
  505. for (i in obj) {
  506. // This check allows counting elements with empty error
  507. // message as invalid elements
  508. if (obj[i] !== undefined && obj[i] !== null && obj[i] !== false) {
  509. count++;
  510. }
  511. }
  512. return count;
  513. },
  514. hideErrors: function () {
  515. this.hideThese(this.toHide);
  516. },
  517. hideThese: function (errors) {
  518. errors.not(this.containers).text("");
  519. this.addWrapper(errors).hide();
  520. },
  521. valid: function () {
  522. return this.size() === 0;
  523. },
  524. size: function () {
  525. return this.errorList.length;
  526. },
  527. focusInvalid: function () {
  528. if (this.settings.focusInvalid) {
  529. try {
  530. $(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
  531. .filter(":visible")
  532. .focus()
  533. // Manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
  534. .trigger("focusin");
  535. } catch (e) {
  536. // Ignore IE throwing errors when focusing hidden elements
  537. }
  538. }
  539. },
  540. findLastActive: function () {
  541. var lastActive = this.lastActive;
  542. return lastActive && $.grep(this.errorList, function (n) {
  543. return n.element.name === lastActive.name;
  544. }).length === 1 && lastActive;
  545. },
  546. elements: function () {
  547. var validator = this,
  548. rulesCache = {};
  549. // Select all valid inputs inside the form (no submit or reset buttons)
  550. return $(this.currentForm)
  551. .find("input, select, textarea, [contenteditable]")
  552. .not(":submit, :reset, :image, :disabled")
  553. .not(this.settings.ignore)
  554. .filter(function () {
  555. var name = this.name || $(this).attr("name"); // For contenteditable
  556. if (!name && validator.settings.debug && window.console) {
  557. console.error("%o has no name assigned", this);
  558. }
  559. // Set form expando on contenteditable
  560. if (this.hasAttribute("contenteditable")) {
  561. this.form = $(this).closest("form")[0];
  562. this.name = name;
  563. }
  564. // Select only the first element for each name, and only those with rules specified
  565. if (name in rulesCache || !validator.objectLength($(this).rules())) {
  566. return false;
  567. }
  568. rulesCache[name] = true;
  569. return true;
  570. });
  571. },
  572. clean: function (selector) {
  573. return $(selector)[0];
  574. },
  575. errors: function () {
  576. var errorClass = this.settings.errorClass.split(" ").join(".");
  577. return $(this.settings.errorElement + "." + errorClass, this.errorContext);
  578. },
  579. resetInternals: function () {
  580. this.successList = [];
  581. this.errorList = [];
  582. this.errorMap = {};
  583. this.toShow = $([]);
  584. this.toHide = $([]);
  585. },
  586. reset: function () {
  587. this.resetInternals();
  588. this.currentElements = $([]);
  589. },
  590. prepareForm: function () {
  591. this.reset();
  592. this.toHide = this.errors().add(this.containers);
  593. },
  594. prepareElement: function (element) {
  595. this.reset();
  596. this.toHide = this.errorsFor(element);
  597. },
  598. elementValue: function (element) {
  599. var $element = $(element),
  600. type = element.type,
  601. val, idx;
  602. if (type === "radio" || type === "checkbox") {
  603. return this.findByName(element.name).filter(":checked").val();
  604. } else if (type === "number" && typeof element.validity !== "undefined") {
  605. return element.validity.badInput ? "NaN" : $element.val();
  606. }
  607. if (element.hasAttribute("contenteditable")) {
  608. val = $element.text();
  609. } else {
  610. val = $element.val();
  611. }
  612. if (type === "file") {
  613. // Modern browser (chrome & safari)
  614. if (val.substr(0, 12) === "C:\\fakepath\\") {
  615. return val.substr(12);
  616. }
  617. // Legacy browsers
  618. // Unix-based path
  619. idx = val.lastIndexOf("/");
  620. if (idx >= 0) {
  621. return val.substr(idx + 1);
  622. }
  623. // Windows-based path
  624. idx = val.lastIndexOf("\\");
  625. if (idx >= 0) {
  626. return val.substr(idx + 1);
  627. }
  628. // Just the file name
  629. return val;
  630. }
  631. if (typeof val === "string") {
  632. return val.replace(/\r/g, "");
  633. }
  634. return val;
  635. },
  636. check: function (element) {
  637. element = this.validationTargetFor(this.clean(element));
  638. var rules = $(element).rules(),
  639. rulesCount = $.map(rules, function (n, i) {
  640. return i;
  641. }).length,
  642. dependencyMismatch = false,
  643. val = this.elementValue(element),
  644. result, method, rule, normalizer;
  645. // Prioritize the local normalizer defined for this element over the global one
  646. // if the former exists, otherwise user the global one in case it exists.
  647. if (typeof rules.normalizer === "function") {
  648. normalizer = rules.normalizer;
  649. } else if (typeof this.settings.normalizer === "function") {
  650. normalizer = this.settings.normalizer;
  651. }
  652. // If normalizer is defined, then call it to retreive the changed value instead
  653. // of using the real one.
  654. // Note that `this` in the normalizer is `element`.
  655. if (normalizer) {
  656. val = normalizer.call(element, val);
  657. if (typeof val !== "string") {
  658. throw new TypeError("The normalizer should return a string value.");
  659. }
  660. // Delete the normalizer from rules to avoid treating it as a pre-defined method.
  661. delete rules.normalizer;
  662. }
  663. for (method in rules) {
  664. rule = {method: method, parameters: rules[method]};
  665. try {
  666. result = $.validator.methods[method].call(this, val, element, rule.parameters);
  667. // If a method indicates that the field is optional and therefore valid,
  668. // don't mark it as valid when there are no other rules
  669. if (result === "dependency-mismatch" && rulesCount === 1) {
  670. dependencyMismatch = true;
  671. continue;
  672. }
  673. dependencyMismatch = false;
  674. if (result === "pending") {
  675. this.toHide = this.toHide.not(this.errorsFor(element));
  676. return;
  677. }
  678. if (!result) {
  679. this.formatAndAdd(element, rule);
  680. return false;
  681. }
  682. } catch (e) {
  683. if (this.settings.debug && window.console) {
  684. console.log("Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e);
  685. }
  686. if (e instanceof TypeError) {
  687. e.message += ". Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.";
  688. }
  689. throw e;
  690. }
  691. }
  692. if (dependencyMismatch) {
  693. return;
  694. }
  695. if (this.objectLength(rules)) {
  696. this.successList.push(element);
  697. }
  698. return true;
  699. },
  700. // Return the custom message for the given element and validation method
  701. // specified in the element's HTML5 data attribute
  702. // return the generic message if present and no method specific message is present
  703. customDataMessage: function (element, method) {
  704. return $(element).data("msg" + method.charAt(0).toUpperCase() +
  705. method.substring(1).toLowerCase()) || $(element).data("msg");
  706. },
  707. // Return the custom message for the given element name and validation method
  708. customMessage: function (name, method) {
  709. var m = this.settings.messages[name];
  710. return m && (m.constructor === String ? m : m[method]);
  711. },
  712. // Return the first defined argument, allowing empty strings
  713. findDefined: function () {
  714. for (var i = 0; i < arguments.length; i++) {
  715. if (arguments[i] !== undefined) {
  716. return arguments[i];
  717. }
  718. }
  719. return undefined;
  720. },
  721. // The second parameter 'rule' used to be a string, and extended to an object literal
  722. // of the following form:
  723. // rule = {
  724. // method: "method name",
  725. // parameters: "the given method parameters"
  726. // }
  727. //
  728. // The old behavior still supported, kept to maintain backward compatibility with
  729. // old code, and will be removed in the next major release.
  730. defaultMessage: function (element, rule) {
  731. if (typeof rule === "string") {
  732. rule = {method: rule};
  733. }
  734. var message = this.findDefined(
  735. this.customMessage(element.name, rule.method),
  736. this.customDataMessage(element, rule.method),
  737. // 'title' is never undefined, so handle empty string as undefined
  738. !this.settings.ignoreTitle && element.title || undefined,
  739. $.validator.messages[rule.method],
  740. "<strong>Warning: No message defined for " + element.name + "</strong>"
  741. ),
  742. theregex = /\$?\{(\d+)\}/g;
  743. if (typeof message === "function") {
  744. message = message.call(this, rule.parameters, element);
  745. } else if (theregex.test(message)) {
  746. message = $.validator.format(message.replace(theregex, "{$1}"), rule.parameters);
  747. }
  748. return message;
  749. },
  750. formatAndAdd: function (element, rule) {
  751. var message = this.defaultMessage(element, rule);
  752. this.errorList.push({
  753. message: message,
  754. element: element,
  755. method: rule.method
  756. });
  757. this.errorMap[element.name] = message;
  758. this.submitted[element.name] = message;
  759. },
  760. addWrapper: function (toToggle) {
  761. if (this.settings.wrapper) {
  762. toToggle = toToggle.add(toToggle.parent(this.settings.wrapper));
  763. }
  764. return toToggle;
  765. },
  766. defaultShowErrors: function () {
  767. var i, elements, error;
  768. for (i = 0; this.errorList[i]; i++) {
  769. error = this.errorList[i];
  770. if (this.settings.highlight) {
  771. this.settings.highlight.call(this, error.element, this.settings.errorClass, this.settings.validClass);
  772. }
  773. this.showLabel(error.element, error.message);
  774. }
  775. if (this.errorList.length) {
  776. this.toShow = this.toShow.add(this.containers);
  777. }
  778. if (this.settings.success) {
  779. for (i = 0; this.successList[i]; i++) {
  780. this.showLabel(this.successList[i]);
  781. }
  782. }
  783. if (this.settings.unhighlight) {
  784. for (i = 0, elements = this.validElements(); elements[i]; i++) {
  785. this.settings.unhighlight.call(this, elements[i], this.settings.errorClass, this.settings.validClass);
  786. }
  787. }
  788. this.toHide = this.toHide.not(this.toShow);
  789. this.hideErrors();
  790. this.addWrapper(this.toShow).show();
  791. },
  792. validElements: function () {
  793. return this.currentElements.not(this.invalidElements());
  794. },
  795. invalidElements: function () {
  796. return $(this.errorList).map(function () {
  797. return this.element;
  798. });
  799. },
  800. showLabel: function (element, message) {
  801. var place, group, errorID, v,
  802. error = this.errorsFor(element),
  803. elementID = this.idOrName(element),
  804. describedBy = $(element).attr("aria-describedby");
  805. if (error.length) {
  806. // Refresh error/success class
  807. error.removeClass(this.settings.validClass).addClass(this.settings.errorClass);
  808. // Replace message on existing label
  809. error.html(message);
  810. } else {
  811. // Create error element
  812. error = $("<" + this.settings.errorElement + ">")
  813. .attr("id", elementID + "-error")
  814. .addClass(this.settings.errorClass)
  815. .html(message || "");
  816. // Maintain reference to the element to be placed into the DOM
  817. place = error;
  818. if (this.settings.wrapper) {
  819. // Make sure the element is visible, even in IE
  820. // actually showing the wrapped element is handled elsewhere
  821. place = error.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
  822. }
  823. if (this.labelContainer.length) {
  824. this.labelContainer.append(place);
  825. } else if (this.settings.errorPlacement) {
  826. this.settings.errorPlacement.call(this, place, $(element));
  827. } else {
  828. place.insertAfter(element);
  829. }
  830. // Link error back to the element
  831. if (error.is("label")) {
  832. // If the error is a label, then associate using 'for'
  833. error.attr("for", elementID);
  834. // If the element is not a child of an associated label, then it's necessary
  835. // to explicitly apply aria-describedby
  836. } else if (error.parents("label[for='" + this.escapeCssMeta(elementID) + "']").length === 0) {
  837. errorID = error.attr("id");
  838. // Respect existing non-error aria-describedby
  839. if (!describedBy) {
  840. describedBy = errorID;
  841. } else if (!describedBy.match(new RegExp("\\b" + this.escapeCssMeta(errorID) + "\\b"))) {
  842. // Add to end of list if not already present
  843. describedBy += " " + errorID;
  844. }
  845. $(element).attr("aria-describedby", describedBy);
  846. // If this element is grouped, then assign to all elements in the same group
  847. group = this.groups[element.name];
  848. if (group) {
  849. v = this;
  850. $.each(v.groups, function (name, testgroup) {
  851. if (testgroup === group) {
  852. $("[name='" + v.escapeCssMeta(name) + "']", v.currentForm)
  853. .attr("aria-describedby", error.attr("id"));
  854. }
  855. });
  856. }
  857. }
  858. }
  859. if (!message && this.settings.success) {
  860. error.text("");
  861. if (typeof this.settings.success === "string") {
  862. error.addClass(this.settings.success);
  863. } else {
  864. this.settings.success(error, element);
  865. }
  866. }
  867. this.toShow = this.toShow.add(error);
  868. },
  869. errorsFor: function (element) {
  870. var name = this.escapeCssMeta(this.idOrName(element)),
  871. describer = $(element).attr("aria-describedby"),
  872. selector = "label[for='" + name + "'], label[for='" + name + "'] *";
  873. // 'aria-describedby' should directly reference the error element
  874. if (describer) {
  875. selector = selector + ", #" + this.escapeCssMeta(describer)
  876. .replace(/\s+/g, ", #");
  877. }
  878. return this
  879. .errors()
  880. .filter(selector);
  881. },
  882. // See https://api.jquery.com/category/selectors/, for CSS
  883. // meta-characters that should be escaped in order to be used with JQuery
  884. // as a literal part of a name/id or any selector.
  885. escapeCssMeta: function (string) {
  886. return string.replace(/([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g, "\\$1");
  887. },
  888. idOrName: function (element) {
  889. return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
  890. },
  891. validationTargetFor: function (element) {
  892. // If radio/checkbox, validate first element in group instead
  893. if (this.checkable(element)) {
  894. element = this.findByName(element.name);
  895. }
  896. // Always apply ignore filter
  897. return $(element).not(this.settings.ignore)[0];
  898. },
  899. checkable: function (element) {
  900. return (/radio|checkbox/i).test(element.type);
  901. },
  902. findByName: function (name) {
  903. return $(this.currentForm).find("[name='" + this.escapeCssMeta(name) + "']");
  904. },
  905. getLength: function (value, element) {
  906. switch (element.nodeName.toLowerCase()) {
  907. case "select":
  908. return $("option:selected", element).length;
  909. case "input":
  910. if (this.checkable(element)) {
  911. return this.findByName(element.name).filter(":checked").length;
  912. }
  913. }
  914. return value.length;
  915. },
  916. depend: function (param, element) {
  917. return this.dependTypes[typeof param] ? this.dependTypes[typeof param](param, element) : true;
  918. },
  919. dependTypes: {
  920. "boolean": function (param) {
  921. return param;
  922. },
  923. "string": function (param, element) {
  924. return !!$(param, element.form).length;
  925. },
  926. "function": function (param, element) {
  927. return param(element);
  928. }
  929. },
  930. optional: function (element) {
  931. var val = this.elementValue(element);
  932. return !$.validator.methods.required.call(this, val, element) && "dependency-mismatch";
  933. },
  934. startRequest: function (element) {
  935. if (!this.pending[element.name]) {
  936. this.pendingRequest++;
  937. $(element).addClass(this.settings.pendingClass);
  938. this.pending[element.name] = true;
  939. }
  940. },
  941. stopRequest: function (element, valid) {
  942. this.pendingRequest--;
  943. // Sometimes synchronization fails, make sure pendingRequest is never < 0
  944. if (this.pendingRequest < 0) {
  945. this.pendingRequest = 0;
  946. }
  947. delete this.pending[element.name];
  948. $(element).removeClass(this.settings.pendingClass);
  949. if (valid && this.pendingRequest === 0 && this.formSubmitted && this.form()) {
  950. $(this.currentForm).submit();
  951. // Remove the hidden input that was used as a replacement for the
  952. // missing submit button. The hidden input is added by `handle()`
  953. // to ensure that the value of the used submit button is passed on
  954. // for scripted submits triggered by this method
  955. if (this.submitButton) {
  956. $("input:hidden[name='" + this.submitButton.name + "']", this.currentForm).remove();
  957. }
  958. this.formSubmitted = false;
  959. } else if (!valid && this.pendingRequest === 0 && this.formSubmitted) {
  960. $(this.currentForm).triggerHandler("invalid-form", [this]);
  961. this.formSubmitted = false;
  962. }
  963. },
  964. previousValue: function (element, method) {
  965. method = typeof method === "string" && method || "remote";
  966. return $.data(element, "previousValue") || $.data(element, "previousValue", {
  967. old: null,
  968. valid: true,
  969. message: this.defaultMessage(element, {method: method})
  970. });
  971. },
  972. // Cleans up all forms and elements, removes validator-specific events
  973. destroy: function () {
  974. this.resetForm();
  975. $(this.currentForm)
  976. .off(".validate")
  977. .removeData("validator")
  978. .find(".validate-equalTo-blur")
  979. .off(".validate-equalTo")
  980. .removeClass("validate-equalTo-blur");
  981. }
  982. },
  983. classRuleSettings: {
  984. required: {required: true},
  985. email: {email: true},
  986. url: {url: true},
  987. date: {date: true},
  988. dateISO: {dateISO: true},
  989. number: {number: true},
  990. digits: {digits: true},
  991. creditcard: {creditcard: true}
  992. },
  993. addClassRules: function (className, rules) {
  994. if (className.constructor === String) {
  995. this.classRuleSettings[className] = rules;
  996. } else {
  997. $.extend(this.classRuleSettings, className);
  998. }
  999. },
  1000. classRules: function (element) {
  1001. var rules = {},
  1002. classes = $(element).attr("class");
  1003. if (classes) {
  1004. $.each(classes.split(" "), function () {
  1005. if (this in $.validator.classRuleSettings) {
  1006. $.extend(rules, $.validator.classRuleSettings[this]);
  1007. }
  1008. });
  1009. }
  1010. return rules;
  1011. },
  1012. normalizeAttributeRule: function (rules, type, method, value) {
  1013. // Convert the value to a number for number inputs, and for text for backwards compability
  1014. // allows type="date" and others to be compared as strings
  1015. if (/min|max|step/.test(method) && (type === null || /number|range|text/.test(type))) {
  1016. value = Number(value);
  1017. // Support Opera Mini, which returns NaN for undefined minlength
  1018. if (isNaN(value)) {
  1019. value = undefined;
  1020. }
  1021. }
  1022. if (value || value === 0) {
  1023. rules[method] = value;
  1024. } else if (type === method && type !== "range") {
  1025. // Exception: the jquery validate 'range' method
  1026. // does not test for the html5 'range' type
  1027. rules[method] = true;
  1028. }
  1029. },
  1030. attributeRules: function (element) {
  1031. var rules = {},
  1032. $element = $(element),
  1033. type = element.getAttribute("type"),
  1034. method, value;
  1035. for (method in $.validator.methods) {
  1036. // Support for <input required> in both html5 and older browsers
  1037. if (method === "required") {
  1038. value = element.getAttribute(method);
  1039. // Some browsers return an empty string for the required attribute
  1040. // and non-HTML5 browsers might have required="" markup
  1041. if (value === "") {
  1042. value = true;
  1043. }
  1044. // Force non-HTML5 browsers to return bool
  1045. value = !!value;
  1046. } else {
  1047. value = $element.attr(method);
  1048. }
  1049. this.normalizeAttributeRule(rules, type, method, value);
  1050. }
  1051. // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs
  1052. if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
  1053. delete rules.maxlength;
  1054. }
  1055. return rules;
  1056. },
  1057. dataRules: function (element) {
  1058. var rules = {},
  1059. $element = $(element),
  1060. type = element.getAttribute("type"),
  1061. method, value;
  1062. for (method in $.validator.methods) {
  1063. value = $element.data("rule" + method.charAt(0).toUpperCase() + method.substring(1).toLowerCase());
  1064. this.normalizeAttributeRule(rules, type, method, value);
  1065. }
  1066. return rules;
  1067. },
  1068. staticRules: function (element) {
  1069. var rules = {},
  1070. validator = $.data(element.form, "validator");
  1071. if (validator.settings.rules) {
  1072. rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
  1073. }
  1074. return rules;
  1075. },
  1076. normalizeRules: function (rules, element) {
  1077. // Handle dependency check
  1078. $.each(rules, function (prop, val) {
  1079. // Ignore rule when param is explicitly false, eg. required:false
  1080. if (val === false) {
  1081. delete rules[prop];
  1082. return;
  1083. }
  1084. if (val.param || val.depends) {
  1085. var keepRule = true;
  1086. switch (typeof val.depends) {
  1087. case "string":
  1088. keepRule = !!$(val.depends, element.form).length;
  1089. break;
  1090. case "function":
  1091. keepRule = val.depends.call(element, element);
  1092. break;
  1093. }
  1094. if (keepRule) {
  1095. rules[prop] = val.param !== undefined ? val.param : true;
  1096. } else {
  1097. $.data(element.form, "validator").resetElements($(element));
  1098. delete rules[prop];
  1099. }
  1100. }
  1101. });
  1102. // Evaluate parameters
  1103. $.each(rules, function (rule, parameter) {
  1104. rules[rule] = $.isFunction(parameter) && rule !== "normalizer" ? parameter(element) : parameter;
  1105. });
  1106. // Clean number parameters
  1107. $.each(["minlength", "maxlength"], function () {
  1108. if (rules[this]) {
  1109. rules[this] = Number(rules[this]);
  1110. }
  1111. });
  1112. $.each(["rangelength", "range"], function () {
  1113. var parts;
  1114. if (rules[this]) {
  1115. if ($.isArray(rules[this])) {
  1116. rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
  1117. } else if (typeof rules[this] === "string") {
  1118. parts = rules[this].replace(/[\[\]]/g, "").split(/[\s,]+/);
  1119. rules[this] = [Number(parts[0]), Number(parts[1])];
  1120. }
  1121. }
  1122. });
  1123. if ($.validator.autoCreateRanges) {
  1124. // Auto-create ranges
  1125. if (rules.min != null && rules.max != null) {
  1126. rules.range = [rules.min, rules.max];
  1127. delete rules.min;
  1128. delete rules.max;
  1129. }
  1130. if (rules.minlength != null && rules.maxlength != null) {
  1131. rules.rangelength = [rules.minlength, rules.maxlength];
  1132. delete rules.minlength;
  1133. delete rules.maxlength;
  1134. }
  1135. }
  1136. return rules;
  1137. },
  1138. // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
  1139. normalizeRule: function (data) {
  1140. if (typeof data === "string") {
  1141. var transformed = {};
  1142. $.each(data.split(/\s/), function () {
  1143. transformed[this] = true;
  1144. });
  1145. data = transformed;
  1146. }
  1147. return data;
  1148. },
  1149. // https://jqueryvalidation.org/jQuery.validator.addMethod/
  1150. addMethod: function (name, method, message) {
  1151. $.validator.methods[name] = method;
  1152. $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];
  1153. if (method.length < 3) {
  1154. $.validator.addClassRules(name, $.validator.normalizeRule(name));
  1155. }
  1156. },
  1157. // https://jqueryvalidation.org/jQuery.validator.methods/
  1158. methods: {
  1159. // https://jqueryvalidation.org/required-method/
  1160. required: function (value, element, param) {
  1161. // Check if dependency is met
  1162. if (!this.depend(param, element)) {
  1163. return "dependency-mismatch";
  1164. }
  1165. if (element.nodeName.toLowerCase() === "select") {
  1166. // Could be an array for select-multiple or a string, both are fine this way
  1167. var val = $(element).val();
  1168. return val && val.length > 0;
  1169. }
  1170. if (this.checkable(element)) {
  1171. return this.getLength(value, element) > 0;
  1172. }
  1173. return value.length > 0;
  1174. },
  1175. // https://jqueryvalidation.org/email-method/
  1176. email: function (value, element) {
  1177. // From https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address
  1178. // Retrieved 2014-01-14
  1179. // If you have a problem with this implementation, report a bug against the above spec
  1180. // Or use custom methods to implement your own email validation
  1181. return this.optional(element) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(value);
  1182. },
  1183. // https://jqueryvalidation.org/url-method/
  1184. url: function (value, element) {
  1185. // Copyright (c) 2010-2013 Diego Perini, MIT licensed
  1186. // https://gist.github.com/dperini/729294
  1187. // see also https://mathiasbynens.be/demo/url-regex
  1188. // modified to allow protocol-relative URLs
  1189. return this.optional(element) || /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(value);
  1190. },
  1191. // https://jqueryvalidation.org/date-method/
  1192. date: function (value, element) {
  1193. return this.optional(element) || !/Invalid|NaN/.test(new Date(value).toString());
  1194. },
  1195. // https://jqueryvalidation.org/dateISO-method/
  1196. dateISO: function (value, element) {
  1197. return this.optional(element) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value);
  1198. },
  1199. // https://jqueryvalidation.org/number-method/
  1200. number: function (value, element) {
  1201. return this.optional(element) || /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
  1202. },
  1203. // https://jqueryvalidation.org/digits-method/
  1204. digits: function (value, element) {
  1205. return this.optional(element) || /^\d+$/.test(value);
  1206. },
  1207. // https://jqueryvalidation.org/minlength-method/
  1208. minlength: function (value, element, param) {
  1209. var length = $.isArray(value) ? value.length : this.getLength(value, element);
  1210. return this.optional(element) || length >= param;
  1211. },
  1212. // https://jqueryvalidation.org/maxlength-method/
  1213. maxlength: function (value, element, param) {
  1214. var length = $.isArray(value) ? value.length : this.getLength(value, element);
  1215. return this.optional(element) || length <= param;
  1216. },
  1217. // https://jqueryvalidation.org/rangelength-method/
  1218. rangelength: function (value, element, param) {
  1219. var length = $.isArray(value) ? value.length : this.getLength(value, element);
  1220. return this.optional(element) || (length >= param[0] && length <= param[1]);
  1221. },
  1222. // https://jqueryvalidation.org/min-method/
  1223. min: function (value, element, param) {
  1224. return this.optional(element) || value >= param;
  1225. },
  1226. // https://jqueryvalidation.org/max-method/
  1227. max: function (value, element, param) {
  1228. return this.optional(element) || value <= param;
  1229. },
  1230. // https://jqueryvalidation.org/range-method/
  1231. range: function (value, element, param) {
  1232. return this.optional(element) || (value >= param[0] && value <= param[1]);
  1233. },
  1234. // https://jqueryvalidation.org/step-method/
  1235. step: function (value, element, param) {
  1236. var type = $(element).attr("type"),
  1237. errorMessage = "Step attribute on input type " + type + " is not supported.",
  1238. supportedTypes = ["text", "number", "range"],
  1239. re = new RegExp("\\b" + type + "\\b"),
  1240. notSupported = type && !re.test(supportedTypes.join()),
  1241. decimalPlaces = function (num) {
  1242. var match = ("" + num).match(/(?:\.(\d+))?$/);
  1243. if (!match) {
  1244. return 0;
  1245. }
  1246. // Number of digits right of decimal point.
  1247. return match[1] ? match[1].length : 0;
  1248. },
  1249. toInt = function (num) {
  1250. return Math.round(num * Math.pow(10, decimals));
  1251. },
  1252. valid = true,
  1253. decimals;
  1254. // Works only for text, number and range input types
  1255. // TODO find a way to support input types date, datetime, datetime-local, month, time and week
  1256. if (notSupported) {
  1257. throw new Error(errorMessage);
  1258. }
  1259. decimals = decimalPlaces(param);
  1260. // Value can't have too many decimals
  1261. if (decimalPlaces(value) > decimals || toInt(value) % toInt(param) !== 0) {
  1262. valid = false;
  1263. }
  1264. return this.optional(element) || valid;
  1265. },
  1266. // https://jqueryvalidation.org/equalTo-method/
  1267. equalTo: function (value, element, param) {
  1268. // Bind to the blur event of the target in order to revalidate whenever the target field is updated
  1269. var target = $(param);
  1270. if (this.settings.onfocusout && target.not(".validate-equalTo-blur").length) {
  1271. target.addClass("validate-equalTo-blur").on("blur.validate-equalTo", function () {
  1272. $(element).valid();
  1273. });
  1274. }
  1275. return value === target.val();
  1276. },
  1277. // https://jqueryvalidation.org/remote-method/
  1278. remote: function (value, element, param, method) {
  1279. if (this.optional(element)) {
  1280. return "dependency-mismatch";
  1281. }
  1282. method = typeof method === "string" && method || "remote";
  1283. var previous = this.previousValue(element, method),
  1284. validator, data, optionDataString;
  1285. if (!this.settings.messages[element.name]) {
  1286. this.settings.messages[element.name] = {};
  1287. }
  1288. previous.originalMessage = previous.originalMessage || this.settings.messages[element.name][method];
  1289. this.settings.messages[element.name][method] = previous.message;
  1290. param = typeof param === "string" && {url: param} || param;
  1291. optionDataString = $.param($.extend({data: value}, param.data));
  1292. if (previous.old === optionDataString) {
  1293. return previous.valid;
  1294. }
  1295. previous.old = optionDataString;
  1296. validator = this;
  1297. this.startRequest(element);
  1298. data = {};
  1299. data[element.name] = value;
  1300. $.ajax($.extend(true, {
  1301. mode: "abort",
  1302. port: "validate" + element.name,
  1303. dataType: "json",
  1304. data: data,
  1305. context: validator.currentForm,
  1306. success: function (response) {
  1307. var valid = response === true || response === "true",
  1308. errors, message, submitted;
  1309. validator.settings.messages[element.name][method] = previous.originalMessage;
  1310. if (valid) {
  1311. submitted = validator.formSubmitted;
  1312. validator.resetInternals();
  1313. validator.toHide = validator.errorsFor(element);
  1314. validator.formSubmitted = submitted;
  1315. validator.successList.push(element);
  1316. validator.invalid[element.name] = false;
  1317. validator.showErrors();
  1318. } else {
  1319. errors = {};
  1320. message = response || validator.defaultMessage(element, {
  1321. method: method,
  1322. parameters: value
  1323. });
  1324. errors[element.name] = previous.message = message;
  1325. validator.invalid[element.name] = true;
  1326. validator.showErrors(errors);
  1327. }
  1328. previous.valid = valid;
  1329. validator.stopRequest(element, valid);
  1330. }
  1331. }, param));
  1332. return "pending";
  1333. }
  1334. }
  1335. });
  1336. // Ajax mode: abort
  1337. // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
  1338. // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
  1339. var pendingRequests = {},
  1340. ajax;
  1341. // Use a prefilter if available (1.5+)
  1342. if ($.ajaxPrefilter) {
  1343. $.ajaxPrefilter(function (settings, _, xhr) {
  1344. var port = settings.port;
  1345. if (settings.mode === "abort") {
  1346. if (pendingRequests[port]) {
  1347. pendingRequests[port].abort();
  1348. }
  1349. pendingRequests[port] = xhr;
  1350. }
  1351. });
  1352. } else {
  1353. // Proxy ajax
  1354. ajax = $.ajax;
  1355. $.ajax = function (settings) {
  1356. var mode = ("mode" in settings ? settings : $.ajaxSettings).mode,
  1357. port = ("port" in settings ? settings : $.ajaxSettings).port;
  1358. if (mode === "abort") {
  1359. if (pendingRequests[port]) {
  1360. pendingRequests[port].abort();
  1361. }
  1362. pendingRequests[port] = ajax.apply(this, arguments);
  1363. return pendingRequests[port];
  1364. }
  1365. return ajax.apply(this, arguments);
  1366. };
  1367. }
  1368. return $;
  1369. }));