foundation.abide.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. 'use strict';
  2. import $ from 'jquery';
  3. import { Plugin } from './foundation.core.plugin';
  4. import { GetYoDigits } from './foundation.core.utils';
  5. /**
  6. * Abide module.
  7. * @module foundation.abide
  8. */
  9. class Abide extends Plugin {
  10. /**
  11. * Creates a new instance of Abide.
  12. * @class
  13. * @name Abide
  14. * @fires Abide#init
  15. * @param {Object} element - jQuery object to add the trigger to.
  16. * @param {Object} options - Overrides to the default plugin settings.
  17. */
  18. _setup(element, options = {}) {
  19. this.$element = element;
  20. this.options = $.extend(true, {}, Abide.defaults, this.$element.data(), options);
  21. this.className = 'Abide'; // ie9 back compat
  22. this._init();
  23. }
  24. /**
  25. * Initializes the Abide plugin and calls functions to get Abide functioning on load.
  26. * @private
  27. */
  28. _init() {
  29. this.$inputs = $.merge( // Consider as input to validate:
  30. this.$element.find('input').not('[type=submit]'), // * all input fields expect submit
  31. this.$element.find('textarea, select') // * all textareas and select fields
  32. );
  33. const $globalErrors = this.$element.find('[data-abide-error]');
  34. // Add a11y attributes to all fields
  35. if (this.options.a11yAttributes) {
  36. this.$inputs.each((i, input) => this.addA11yAttributes($(input)));
  37. $globalErrors.each((i, error) => this.addGlobalErrorA11yAttributes($(error)));
  38. }
  39. this._events();
  40. }
  41. /**
  42. * Initializes events for Abide.
  43. * @private
  44. */
  45. _events() {
  46. this.$element.off('.abide')
  47. .on('reset.zf.abide', () => {
  48. this.resetForm();
  49. })
  50. .on('submit.zf.abide', () => {
  51. return this.validateForm();
  52. });
  53. if (this.options.validateOn === 'fieldChange') {
  54. this.$inputs
  55. .off('change.zf.abide')
  56. .on('change.zf.abide', (e) => {
  57. this.validateInput($(e.target));
  58. });
  59. }
  60. if (this.options.liveValidate) {
  61. this.$inputs
  62. .off('input.zf.abide')
  63. .on('input.zf.abide', (e) => {
  64. this.validateInput($(e.target));
  65. });
  66. }
  67. if (this.options.validateOnBlur) {
  68. this.$inputs
  69. .off('blur.zf.abide')
  70. .on('blur.zf.abide', (e) => {
  71. this.validateInput($(e.target));
  72. });
  73. }
  74. }
  75. /**
  76. * Calls necessary functions to update Abide upon DOM change
  77. * @private
  78. */
  79. _reflow() {
  80. this._init();
  81. }
  82. /**
  83. * Checks whether or not a form element has the required attribute and if it's checked or not
  84. * @param {Object} element - jQuery object to check for required attribute
  85. * @returns {Boolean} Boolean value depends on whether or not attribute is checked or empty
  86. */
  87. requiredCheck($el) {
  88. if (!$el.attr('required')) return true;
  89. var isGood = true;
  90. switch ($el[0].type) {
  91. case 'checkbox':
  92. isGood = $el[0].checked;
  93. break;
  94. case 'select':
  95. case 'select-one':
  96. case 'select-multiple':
  97. var opt = $el.find('option:selected');
  98. if (!opt.length || !opt.val()) isGood = false;
  99. break;
  100. default:
  101. if(!$el.val() || !$el.val().length) isGood = false;
  102. }
  103. return isGood;
  104. }
  105. /**
  106. * Get:
  107. * - Based on $el, the first element(s) corresponding to `formErrorSelector` in this order:
  108. * 1. The element's direct sibling('s).
  109. * 2. The element's parent's children.
  110. * - Element(s) with the attribute `[data-form-error-for]` set with the element's id.
  111. *
  112. * This allows for multiple form errors per input, though if none are found, no form errors will be shown.
  113. *
  114. * @param {Object} $el - jQuery object to use as reference to find the form error selector.
  115. * @returns {Object} jQuery object with the selector.
  116. */
  117. findFormError($el) {
  118. var id = $el[0].id;
  119. var $error = $el.siblings(this.options.formErrorSelector);
  120. if (!$error.length) {
  121. $error = $el.parent().find(this.options.formErrorSelector);
  122. }
  123. if (id) {
  124. $error = $error.add(this.$element.find(`[data-form-error-for="${id}"]`));
  125. }
  126. return $error;
  127. }
  128. /**
  129. * Get the first element in this order:
  130. * 2. The <label> with the attribute `[for="someInputId"]`
  131. * 3. The `.closest()` <label>
  132. *
  133. * @param {Object} $el - jQuery object to check for required attribute
  134. * @returns {Boolean} Boolean value depends on whether or not attribute is checked or empty
  135. */
  136. findLabel($el) {
  137. var id = $el[0].id;
  138. var $label = this.$element.find(`label[for="${id}"]`);
  139. if (!$label.length) {
  140. return $el.closest('label');
  141. }
  142. return $label;
  143. }
  144. /**
  145. * Get the set of labels associated with a set of radio els in this order
  146. * 2. The <label> with the attribute `[for="someInputId"]`
  147. * 3. The `.closest()` <label>
  148. *
  149. * @param {Object} $el - jQuery object to check for required attribute
  150. * @returns {Boolean} Boolean value depends on whether or not attribute is checked or empty
  151. */
  152. findRadioLabels($els) {
  153. var labels = $els.map((i, el) => {
  154. var id = el.id;
  155. var $label = this.$element.find(`label[for="${id}"]`);
  156. if (!$label.length) {
  157. $label = $(el).closest('label');
  158. }
  159. return $label[0];
  160. });
  161. return $(labels);
  162. }
  163. /**
  164. * Adds the CSS error class as specified by the Abide settings to the label, input, and the form
  165. * @param {Object} $el - jQuery object to add the class to
  166. */
  167. addErrorClasses($el) {
  168. var $label = this.findLabel($el);
  169. var $formError = this.findFormError($el);
  170. if ($label.length) {
  171. $label.addClass(this.options.labelErrorClass);
  172. }
  173. if ($formError.length) {
  174. $formError.addClass(this.options.formErrorClass);
  175. }
  176. $el.addClass(this.options.inputErrorClass).attr({
  177. 'data-invalid': '',
  178. 'aria-invalid': true
  179. });
  180. }
  181. /**
  182. * Adds [for] and [role=alert] attributes to all form error targetting $el,
  183. * and [aria-describedby] attribute to $el toward the first form error.
  184. * @param {Object} $el - jQuery object
  185. */
  186. addA11yAttributes($el) {
  187. let $errors = this.findFormError($el);
  188. let $labels = $errors.filter('label');
  189. let $error = $errors.first();
  190. if (!$errors.length) return;
  191. // Set [aria-describedby] on the input toward the first form error if it is not set
  192. if (typeof $el.attr('aria-describedby') === 'undefined') {
  193. // Get the first error ID or create one
  194. let errorId = $error.attr('id');
  195. if (typeof errorId === 'undefined') {
  196. errorId = GetYoDigits(6, 'abide-error');
  197. $error.attr('id', errorId);
  198. };
  199. $el.attr('aria-describedby', errorId);
  200. }
  201. if ($labels.filter('[for]').length < $labels.length) {
  202. // Get the input ID or create one
  203. let elemId = $el.attr('id');
  204. if (typeof elemId === 'undefined') {
  205. elemId = GetYoDigits(6, 'abide-input');
  206. $el.attr('id', elemId);
  207. };
  208. // For each label targeting $el, set [for] if it is not set.
  209. $labels.each((i, label) => {
  210. const $label = $(label);
  211. if (typeof $label.attr('for') === 'undefined')
  212. $label.attr('for', elemId);
  213. });
  214. }
  215. // For each error targeting $el, set [role=alert] if it is not set.
  216. $errors.each((i, label) => {
  217. const $label = $(label);
  218. if (typeof $label.attr('role') === 'undefined')
  219. $label.attr('role', 'alert');
  220. }).end();
  221. }
  222. /**
  223. * Adds [aria-live] attribute to the given global form error $el.
  224. * @param {Object} $el - jQuery object to add the attribute to
  225. */
  226. addGlobalErrorA11yAttributes($el) {
  227. if (typeof $el.attr('aria-live') === 'undefined')
  228. $el.attr('aria-live', this.options.a11yErrorLevel);
  229. }
  230. /**
  231. * Remove CSS error classes etc from an entire radio button group
  232. * @param {String} groupName - A string that specifies the name of a radio button group
  233. *
  234. */
  235. removeRadioErrorClasses(groupName) {
  236. var $els = this.$element.find(`:radio[name="${groupName}"]`);
  237. var $labels = this.findRadioLabels($els);
  238. var $formErrors = this.findFormError($els);
  239. if ($labels.length) {
  240. $labels.removeClass(this.options.labelErrorClass);
  241. }
  242. if ($formErrors.length) {
  243. $formErrors.removeClass(this.options.formErrorClass);
  244. }
  245. $els.removeClass(this.options.inputErrorClass).attr({
  246. 'data-invalid': null,
  247. 'aria-invalid': null
  248. });
  249. }
  250. /**
  251. * Removes CSS error class as specified by the Abide settings from the label, input, and the form
  252. * @param {Object} $el - jQuery object to remove the class from
  253. */
  254. removeErrorClasses($el) {
  255. // radios need to clear all of the els
  256. if($el[0].type == 'radio') {
  257. return this.removeRadioErrorClasses($el.attr('name'));
  258. }
  259. var $label = this.findLabel($el);
  260. var $formError = this.findFormError($el);
  261. if ($label.length) {
  262. $label.removeClass(this.options.labelErrorClass);
  263. }
  264. if ($formError.length) {
  265. $formError.removeClass(this.options.formErrorClass);
  266. }
  267. $el.removeClass(this.options.inputErrorClass).attr({
  268. 'data-invalid': null,
  269. 'aria-invalid': null
  270. });
  271. }
  272. /**
  273. * Goes through a form to find inputs and proceeds to validate them in ways specific to their type.
  274. * Ignores inputs with data-abide-ignore, type="hidden" or disabled attributes set
  275. * @fires Abide#invalid
  276. * @fires Abide#valid
  277. * @param {Object} element - jQuery object to validate, should be an HTML input
  278. * @returns {Boolean} goodToGo - If the input is valid or not.
  279. */
  280. validateInput($el) {
  281. var clearRequire = this.requiredCheck($el),
  282. validated = false,
  283. customValidator = true,
  284. validator = $el.attr('data-validator'),
  285. equalTo = true;
  286. // don't validate ignored inputs or hidden inputs or disabled inputs
  287. if ($el.is('[data-abide-ignore]') || $el.is('[type="hidden"]') || $el.is('[disabled]')) {
  288. return true;
  289. }
  290. switch ($el[0].type) {
  291. case 'radio':
  292. validated = this.validateRadio($el.attr('name'));
  293. break;
  294. case 'checkbox':
  295. validated = clearRequire;
  296. break;
  297. case 'select':
  298. case 'select-one':
  299. case 'select-multiple':
  300. validated = clearRequire;
  301. break;
  302. default:
  303. validated = this.validateText($el);
  304. }
  305. if (validator) {
  306. customValidator = this.matchValidation($el, validator, $el.attr('required'));
  307. }
  308. if ($el.attr('data-equalto')) {
  309. equalTo = this.options.validators.equalTo($el);
  310. }
  311. var goodToGo = [clearRequire, validated, customValidator, equalTo].indexOf(false) === -1;
  312. var message = (goodToGo ? 'valid' : 'invalid') + '.zf.abide';
  313. if (goodToGo) {
  314. // Re-validate inputs that depend on this one with equalto
  315. const dependentElements = this.$element.find(`[data-equalto="${$el.attr('id')}"]`);
  316. if (dependentElements.length) {
  317. let _this = this;
  318. dependentElements.each(function() {
  319. if ($(this).val()) {
  320. _this.validateInput($(this));
  321. }
  322. });
  323. }
  324. }
  325. this[goodToGo ? 'removeErrorClasses' : 'addErrorClasses']($el);
  326. /**
  327. * Fires when the input is done checking for validation. Event trigger is either `valid.zf.abide` or `invalid.zf.abide`
  328. * Trigger includes the DOM element of the input.
  329. * @event Abide#valid
  330. * @event Abide#invalid
  331. */
  332. $el.trigger(message, [$el]);
  333. return goodToGo;
  334. }
  335. /**
  336. * Goes through a form and if there are any invalid inputs, it will display the form error element
  337. * @returns {Boolean} noError - true if no errors were detected...
  338. * @fires Abide#formvalid
  339. * @fires Abide#forminvalid
  340. */
  341. validateForm() {
  342. var acc = [];
  343. var _this = this;
  344. this.$inputs.each(function() {
  345. acc.push(_this.validateInput($(this)));
  346. });
  347. var noError = acc.indexOf(false) === -1;
  348. this.$element.find('[data-abide-error]').each((i, elem) => {
  349. const $elem = $(elem);
  350. // Ensure a11y attributes are set
  351. if (this.options.a11yAttributes) this.addGlobalErrorA11yAttributes($elem);
  352. // Show or hide the error
  353. $elem.css('display', (noError ? 'none' : 'block'));
  354. });
  355. /**
  356. * Fires when the form is finished validating. Event trigger is either `formvalid.zf.abide` or `forminvalid.zf.abide`.
  357. * Trigger includes the element of the form.
  358. * @event Abide#formvalid
  359. * @event Abide#forminvalid
  360. */
  361. this.$element.trigger((noError ? 'formvalid' : 'forminvalid') + '.zf.abide', [this.$element]);
  362. return noError;
  363. }
  364. /**
  365. * Determines whether or a not a text input is valid based on the pattern specified in the attribute. If no matching pattern is found, returns true.
  366. * @param {Object} $el - jQuery object to validate, should be a text input HTML element
  367. * @param {String} pattern - string value of one of the RegEx patterns in Abide.options.patterns
  368. * @returns {Boolean} Boolean value depends on whether or not the input value matches the pattern specified
  369. */
  370. validateText($el, pattern) {
  371. // A pattern can be passed to this function, or it will be infered from the input's "pattern" attribute, or it's "type" attribute
  372. pattern = (pattern || $el.attr('pattern') || $el.attr('type'));
  373. var inputText = $el.val();
  374. var valid = false;
  375. if (inputText.length) {
  376. // If the pattern attribute on the element is in Abide's list of patterns, then test that regexp
  377. if (this.options.patterns.hasOwnProperty(pattern)) {
  378. valid = this.options.patterns[pattern].test(inputText);
  379. }
  380. // If the pattern name isn't also the type attribute of the field, then test it as a regexp
  381. else if (pattern !== $el.attr('type')) {
  382. valid = new RegExp(pattern).test(inputText);
  383. }
  384. else {
  385. valid = true;
  386. }
  387. }
  388. // An empty field is valid if it's not required
  389. else if (!$el.prop('required')) {
  390. valid = true;
  391. }
  392. return valid;
  393. }
  394. /**
  395. * Determines whether or a not a radio input is valid based on whether or not it is required and selected. Although the function targets a single `<input>`, it validates by checking the `required` and `checked` properties of all radio buttons in its group.
  396. * @param {String} groupName - A string that specifies the name of a radio button group
  397. * @returns {Boolean} Boolean value depends on whether or not at least one radio input has been selected (if it's required)
  398. */
  399. validateRadio(groupName) {
  400. // If at least one radio in the group has the `required` attribute, the group is considered required
  401. // Per W3C spec, all radio buttons in a group should have `required`, but we're being nice
  402. var $group = this.$element.find(`:radio[name="${groupName}"]`);
  403. var valid = false, required = false;
  404. // For the group to be required, at least one radio needs to be required
  405. $group.each((i, e) => {
  406. if ($(e).attr('required')) {
  407. required = true;
  408. }
  409. });
  410. if(!required) valid=true;
  411. if (!valid) {
  412. // For the group to be valid, at least one radio needs to be checked
  413. $group.each((i, e) => {
  414. if ($(e).prop('checked')) {
  415. valid = true;
  416. }
  417. });
  418. };
  419. return valid;
  420. }
  421. /**
  422. * Determines if a selected input passes a custom validation function. Multiple validations can be used, if passed to the element with `data-validator="foo bar baz"` in a space separated listed.
  423. * @param {Object} $el - jQuery input element.
  424. * @param {String} validators - a string of function names matching functions in the Abide.options.validators object.
  425. * @param {Boolean} required - self explanatory?
  426. * @returns {Boolean} - true if validations passed.
  427. */
  428. matchValidation($el, validators, required) {
  429. required = required ? true : false;
  430. var clear = validators.split(' ').map((v) => {
  431. return this.options.validators[v]($el, required, $el.parent());
  432. });
  433. return clear.indexOf(false) === -1;
  434. }
  435. /**
  436. * Resets form inputs and styles
  437. * @fires Abide#formreset
  438. */
  439. resetForm() {
  440. var $form = this.$element,
  441. opts = this.options;
  442. $(`.${opts.labelErrorClass}`, $form).not('small').removeClass(opts.labelErrorClass);
  443. $(`.${opts.inputErrorClass}`, $form).not('small').removeClass(opts.inputErrorClass);
  444. $(`${opts.formErrorSelector}.${opts.formErrorClass}`).removeClass(opts.formErrorClass);
  445. $form.find('[data-abide-error]').css('display', 'none');
  446. $(':input', $form).not(':button, :submit, :reset, :hidden, :radio, :checkbox, [data-abide-ignore]').val('').attr({
  447. 'data-invalid': null,
  448. 'aria-invalid': null
  449. });
  450. $(':input:radio', $form).not('[data-abide-ignore]').prop('checked',false).attr({
  451. 'data-invalid': null,
  452. 'aria-invalid': null
  453. });
  454. $(':input:checkbox', $form).not('[data-abide-ignore]').prop('checked',false).attr({
  455. 'data-invalid': null,
  456. 'aria-invalid': null
  457. });
  458. /**
  459. * Fires when the form has been reset.
  460. * @event Abide#formreset
  461. */
  462. $form.trigger('formreset.zf.abide', [$form]);
  463. }
  464. /**
  465. * Destroys an instance of Abide.
  466. * Removes error styles and classes from elements, without resetting their values.
  467. */
  468. _destroy() {
  469. var _this = this;
  470. this.$element
  471. .off('.abide')
  472. .find('[data-abide-error]')
  473. .css('display', 'none');
  474. this.$inputs
  475. .off('.abide')
  476. .each(function() {
  477. _this.removeErrorClasses($(this));
  478. });
  479. }
  480. }
  481. /**
  482. * Default settings for plugin
  483. */
  484. Abide.defaults = {
  485. /**
  486. * The default event to validate inputs. Checkboxes and radios validate immediately.
  487. * Remove or change this value for manual validation.
  488. * @option
  489. * @type {?string}
  490. * @default 'fieldChange'
  491. */
  492. validateOn: 'fieldChange',
  493. /**
  494. * Class to be applied to input labels on failed validation.
  495. * @option
  496. * @type {string}
  497. * @default 'is-invalid-label'
  498. */
  499. labelErrorClass: 'is-invalid-label',
  500. /**
  501. * Class to be applied to inputs on failed validation.
  502. * @option
  503. * @type {string}
  504. * @default 'is-invalid-input'
  505. */
  506. inputErrorClass: 'is-invalid-input',
  507. /**
  508. * Class selector to use to target Form Errors for show/hide.
  509. * @option
  510. * @type {string}
  511. * @default '.form-error'
  512. */
  513. formErrorSelector: '.form-error',
  514. /**
  515. * Class added to Form Errors on failed validation.
  516. * @option
  517. * @type {string}
  518. * @default 'is-visible'
  519. */
  520. formErrorClass: 'is-visible',
  521. /**
  522. * If true, automatically insert when possible:
  523. * - `[aria-describedby]` on fields
  524. * - `[role=alert]` on form errors and `[for]` on form error labels
  525. * - `[aria-live]` on global errors `[data-abide-error]` (see option `a11yErrorLevel`).
  526. * @option
  527. * @type {boolean}
  528. * @default true
  529. */
  530. a11yAttributes: true,
  531. /**
  532. * [aria-live] attribute value to be applied on global errors `[data-abide-error]`.
  533. * Options are: 'assertive', 'polite' and 'off'/null
  534. * @option
  535. * @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions
  536. * @type {string}
  537. * @default 'assertive'
  538. */
  539. a11yErrorLevel: 'assertive',
  540. /**
  541. * Set to true to validate text inputs on any value change.
  542. * @option
  543. * @type {boolean}
  544. * @default false
  545. */
  546. liveValidate: false,
  547. /**
  548. * Set to true to validate inputs on blur.
  549. * @option
  550. * @type {boolean}
  551. * @default false
  552. */
  553. validateOnBlur: false,
  554. patterns: {
  555. alpha : /^[a-zA-Z]+$/,
  556. alpha_numeric : /^[a-zA-Z0-9]+$/,
  557. integer : /^[-+]?\d+$/,
  558. number : /^[-+]?\d*(?:[\.\,]\d+)?$/,
  559. // amex, visa, diners
  560. card : /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(?:222[1-9]|2[3-6][0-9]{2}|27[0-1][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/,
  561. cvv : /^([0-9]){3,4}$/,
  562. // http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
  563. email : /^[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])?)+$/,
  564. // From CommonRegexJS (@talyssonoc)
  565. // https://github.com/talyssonoc/CommonRegexJS/blob/e2901b9f57222bc14069dc8f0598d5f412555411/lib/commonregex.js#L76
  566. // For more restrictive URL Regexs, see https://mathiasbynens.be/demo/url-regex.
  567. url: /^((?:(https?|ftps?|file|ssh|sftp):\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\))+(?:\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?\xab\xbb\u201c\u201d\u2018\u2019]))$/,
  568. // abc.de
  569. domain : /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,8}$/,
  570. datetime : /^([0-2][0-9]{3})\-([0-1][0-9])\-([0-3][0-9])T([0-5][0-9])\:([0-5][0-9])\:([0-5][0-9])(Z|([\-\+]([0-1][0-9])\:00))$/,
  571. // YYYY-MM-DD
  572. date : /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))$/,
  573. // HH:MM:SS
  574. time : /^(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]){2}$/,
  575. dateISO : /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/,
  576. // MM/DD/YYYY
  577. month_day_year : /^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.]\d{4}$/,
  578. // DD/MM/YYYY
  579. day_month_year : /^(0[1-9]|[12][0-9]|3[01])[- \/.](0[1-9]|1[012])[- \/.]\d{4}$/,
  580. // #FFF or #FFFFFF
  581. color : /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,
  582. // Domain || URL
  583. website: {
  584. test: (text) => {
  585. return Abide.defaults.patterns['domain'].test(text) || Abide.defaults.patterns['url'].test(text);
  586. }
  587. }
  588. },
  589. /**
  590. * Optional validation functions to be used. `equalTo` being the only default included function.
  591. * Functions should return only a boolean if the input is valid or not. Functions are given the following arguments:
  592. * el : The jQuery element to validate.
  593. * required : Boolean value of the required attribute be present or not.
  594. * parent : The direct parent of the input.
  595. * @option
  596. */
  597. validators: {
  598. equalTo: function (el, required, parent) {
  599. return $(`#${el.attr('data-equalto')}`).val() === el.val();
  600. }
  601. }
  602. }
  603. export {Abide};