validation.js 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. (function (factory) {
  6. 'use strict';
  7. if (typeof define === 'function' && define.amd) {
  8. define([
  9. 'jquery',
  10. 'moment',
  11. 'jquery/ui',
  12. 'jquery/validate',
  13. 'mage/translate'
  14. ], factory);
  15. } else {
  16. factory(jQuery);
  17. }
  18. }(function ($, moment) {
  19. 'use strict';
  20. var creditCartTypes, rules, showLabel, originValidateDelegate;
  21. $.extend(true, $, {
  22. // @TODO: Move methods 'isEmpty', 'isEmptyNoTrim', 'parseNumber', 'stripHtml' in file with utility functions
  23. mage: {
  24. /**
  25. * Check if string is empty with trim
  26. * @param {String} value
  27. */
  28. isEmpty: function (value) {
  29. return value === '' || value === undefined ||
  30. value == null || value.length === 0 || /^\s+$/.test(value);
  31. },
  32. /**
  33. * Check if string is empty no trim
  34. * @param {String} value
  35. */
  36. isEmptyNoTrim: function (value) {
  37. return value === '' || value == null || value.length === 0;
  38. },
  39. /**
  40. * Checks if {value} is between numbers {from} and {to}
  41. * @param {String} value
  42. * @param {String} from
  43. * @param {String} to
  44. * @returns {Boolean}
  45. */
  46. isBetween: function (value, from, to) {
  47. return ($.mage.isEmpty(from) || value >= $.mage.parseNumber(from)) &&
  48. ($.mage.isEmpty(to) || value <= $.mage.parseNumber(to));
  49. },
  50. /**
  51. * Parse price string
  52. * @param {String} value
  53. */
  54. parseNumber: function (value) {
  55. var isDot, isComa;
  56. if (typeof value !== 'string') {
  57. return parseFloat(value);
  58. }
  59. isDot = value.indexOf('.');
  60. isComa = value.indexOf(',');
  61. if (isDot !== -1 && isComa !== -1) {
  62. if (isComa > isDot) {
  63. value = value.replace('.', '').replace(',', '.');
  64. } else {
  65. value = value.replace(',', '');
  66. }
  67. } else if (isComa !== -1) {
  68. value = value.replace(',', '.');
  69. }
  70. return parseFloat(value);
  71. },
  72. /**
  73. * Removes HTML tags and space characters, numbers and punctuation.
  74. *
  75. * @param {String} value - Value being stripped.
  76. * @return {String}
  77. */
  78. stripHtml: function (value) {
  79. return value.replace(/<.[^<>]*?>/g, ' ').replace(/&nbsp;|&#160;/gi, ' ')
  80. .replace(/[0-9.(),;:!?%#$'"_+=\/-]*/g, '');
  81. }
  82. }
  83. });
  84. /**
  85. * @param {String} name
  86. * @param {*} method
  87. * @param {*} message
  88. * @param {*} dontSkip
  89. */
  90. $.validator.addMethod = function (name, method, message, dontSkip) {
  91. $.validator.methods[name] = method;
  92. $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];
  93. if (method.length < 3 || dontSkip) {
  94. $.validator.addClassRules(name, $.validator.normalizeRule(name));
  95. }
  96. };
  97. /**
  98. * Javascript object with credit card types
  99. * 0 - regexp for card number
  100. * 1 - regexp for cvn
  101. * 2 - check or not credit card number trough Luhn algorithm by
  102. */
  103. creditCartTypes = {
  104. 'SO': [
  105. new RegExp('^(6334[5-9]([0-9]{11}|[0-9]{13,14}))|(6767([0-9]{12}|[0-9]{14,15}))$'),
  106. new RegExp('^([0-9]{3}|[0-9]{4})?$'),
  107. true
  108. ],
  109. 'SM': [
  110. new RegExp('(^(5[0678])[0-9]{11,18}$)|(^(6[^05])[0-9]{11,18}$)|' +
  111. '(^(601)[^1][0-9]{9,16}$)|(^(6011)[0-9]{9,11}$)|(^(6011)[0-9]{13,16}$)|' +
  112. '(^(65)[0-9]{11,13}$)|(^(65)[0-9]{15,18}$)|(^(49030)[2-9]([0-9]{10}$|[0-9]{12,13}$))|' +
  113. '(^(49033)[5-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49110)[1-2]([0-9]{10}$|[0-9]{12,13}$))|' +
  114. '(^(49117)[4-9]([0-9]{10}$|[0-9]{12,13}$))|(^(49118)[0-2]([0-9]{10}$|[0-9]{12,13}$))|' +
  115. '(^(4936)([0-9]{12}$|[0-9]{14,15}$))'), new RegExp('^([0-9]{3}|[0-9]{4})?$'),
  116. true
  117. ],
  118. 'VI': [new RegExp('^4[0-9]{12}([0-9]{3})?$'), new RegExp('^[0-9]{3}$'), true],
  119. 'MC': [
  120. new RegExp('^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$'),
  121. new RegExp('^[0-9]{3}$'),
  122. true
  123. ],
  124. 'AE': [new RegExp('^3[47][0-9]{13}$'), new RegExp('^[0-9]{4}$'), true],
  125. 'DI': [new RegExp('^(6011(0|[2-4]|74|7[7-9]|8[6-9]|9)|6(4[4-9]|5))\\d*$'), new RegExp('^[0-9]{3}$'), true],
  126. 'JCB': [new RegExp('^35(2[8-9]|[3-8])\\d*$'), new RegExp('^[0-9]{3}$'), true],
  127. 'DN': [new RegExp('^(3(0[0-5]|095|6|[8-9]))\\d*$'), new RegExp('^[0-9]{3}$'), true],
  128. 'UN': [
  129. new RegExp('^(622(1(2[6-9]|[3-9])|[3-8]|9([[0-1]|2[0-5]))|62[4-6]|628([2-8]))\\d*?$'),
  130. new RegExp('^[0-9]{3}$'),
  131. true
  132. ],
  133. 'MI': [new RegExp('^(5(0|[6-9])|63|67(?!59|6770|6774))\\d*$'), new RegExp('^[0-9]{3}$'), true],
  134. 'MD': [new RegExp('^6759(?!24|38|40|6[3-9]|70|76)|676770|676774\\d*$'), new RegExp('^[0-9]{3}$'), true]
  135. };
  136. /**
  137. * validate credit card number using mod10
  138. * @param {String} s
  139. * @return {Boolean}
  140. */
  141. function validateCreditCard(s) {
  142. // remove non-numerics
  143. var v = '0123456789',
  144. w = '',
  145. i, j, k, m, c, a, x;
  146. for (i = 0; i < s.length; i++) {
  147. x = s.charAt(i);
  148. if (v.indexOf(x, 0) !== -1) {
  149. w += x;
  150. }
  151. }
  152. // validate number
  153. j = w.length / 2;
  154. k = Math.floor(j);
  155. m = Math.ceil(j) - k;
  156. c = 0;
  157. for (i = 0; i < k; i++) {
  158. a = w.charAt(i * 2 + m) * 2;
  159. c += a > 9 ? Math.floor(a / 10 + a % 10) : a;
  160. }
  161. for (i = 0; i < k + m; i++) {
  162. c += w.charAt(i * 2 + 1 - m) * 1;
  163. }
  164. return c % 10 === 0;
  165. }
  166. /**
  167. * validate all table required inputs at once, using single hidden input
  168. * @param {String} value
  169. * @param {HTMLElement} element
  170. *
  171. * @return {Boolean}
  172. */
  173. function tableSingleValidation(value, element) {
  174. var empty = $(element).closest('table')
  175. .find('input.required-option:visible')
  176. .filter(function (i, el) {
  177. if ($(el).is('disabled')) {
  178. return $.mage.isEmpty(el.value);
  179. }
  180. })
  181. .length;
  182. return empty === 0;
  183. }
  184. /**
  185. * Collection of validation rules including rules from additional-methods.js
  186. * @type {Object}
  187. */
  188. rules = {
  189. 'max-words': [
  190. function (value, element, params) {
  191. return this.optional(element) || $.mage.stripHtml(value).match(/\b\w+\b/g).length < params;
  192. },
  193. $.mage.__('Please enter {0} words or less.')
  194. ],
  195. 'min-words': [
  196. function (value, element, params) {
  197. return this.optional(element) || $.mage.stripHtml(value).match(/\b\w+\b/g).length >= params;
  198. },
  199. $.mage.__('Please enter at least {0} words.')
  200. ],
  201. 'range-words': [
  202. function (value, element, params) {
  203. return this.optional(element) ||
  204. $.mage.stripHtml(value).match(/\b\w+\b/g).length >= params[0] &&
  205. value.match(/bw+b/g).length < params[1];
  206. },
  207. $.mage.__('Please enter between {0} and {1} words.')
  208. ],
  209. 'letters-with-basic-punc': [
  210. function (value, element) {
  211. return this.optional(element) || /^[a-z\-.,()'\"\s]+$/i.test(value);
  212. },
  213. $.mage.__('Letters or punctuation only please')
  214. ],
  215. 'alphanumeric': [
  216. function (value, element) {
  217. return this.optional(element) || /^\w+$/i.test(value);
  218. },
  219. $.mage.__('Letters, numbers, spaces or underscores only please')
  220. ],
  221. 'letters-only': [
  222. function (value, element) {
  223. return this.optional(element) || /^[a-z]+$/i.test(value);
  224. },
  225. $.mage.__('Letters only please')
  226. ],
  227. 'no-whitespace': [
  228. function (value, element) {
  229. return this.optional(element) || /^\S+$/i.test(value);
  230. },
  231. $.mage.__('No white space please')
  232. ],
  233. 'no-marginal-whitespace': [
  234. function (value, element) {
  235. return this.optional(element) || !/^\s+|\s+$/i.test(value);
  236. },
  237. $.mage.__('No marginal white space please')
  238. ],
  239. 'zip-range': [
  240. function (value, element) {
  241. return this.optional(element) || /^90[2-5]-\d{2}-\d{4}$/.test(value);
  242. },
  243. $.mage.__('Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx')
  244. ],
  245. 'integer': [
  246. function (value, element) {
  247. return this.optional(element) || /^-?\d+$/.test(value);
  248. },
  249. $.mage.__('A positive or negative non-decimal number please')
  250. ],
  251. 'vinUS': [
  252. function (v) {
  253. var i, n, d, f, cd, cdv, LL, VL, FL, rs;
  254. /* eslint-disable max-depth */
  255. if (v.length !== 17) {
  256. return false;
  257. }
  258. LL = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L',
  259. 'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
  260. VL = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9];
  261. FL = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];
  262. rs = 0;
  263. for (i = 0; i < 17; i++) {
  264. f = FL[i];
  265. d = v.slice(i, i + 1);
  266. if (i === 8) {
  267. cdv = d;
  268. }
  269. if (!isNaN(d)) {
  270. d *= f;
  271. } else {
  272. for (n = 0; n < LL.length; n++) {
  273. if (d.toUpperCase() === LL[n]) {
  274. d = VL[n];
  275. d *= f;
  276. if (isNaN(cdv) && n === 8) {
  277. cdv = LL[n];
  278. }
  279. break;
  280. }
  281. }
  282. }
  283. rs += d;
  284. }
  285. /* eslint-enable max-depth */
  286. cd = rs % 11;
  287. if (cd === 10) {
  288. cd = 'X';
  289. }
  290. if (cd === cdv) {
  291. return true;
  292. }
  293. return false;
  294. },
  295. $.mage.__('The specified vehicle identification number (VIN) is invalid.')
  296. ],
  297. 'dateITA': [
  298. function (value, element) {
  299. var check = false,
  300. re = /^\d{1,2}\/\d{1,2}\/\d{4}$/,
  301. adata, gg, mm, aaaa, xdata;
  302. if (re.test(value)) {
  303. adata = value.split('/');
  304. gg = parseInt(adata[0], 10);
  305. mm = parseInt(adata[1], 10);
  306. aaaa = parseInt(adata[2], 10);
  307. xdata = new Date(aaaa, mm - 1, gg);
  308. if (xdata.getFullYear() === aaaa &&
  309. xdata.getMonth() === mm - 1 &&
  310. xdata.getDate() === gg
  311. ) {
  312. check = true;
  313. } else {
  314. check = false;
  315. }
  316. } else {
  317. check = false;
  318. }
  319. return this.optional(element) || check;
  320. },
  321. $.mage.__('Please enter a correct date')
  322. ],
  323. 'dateNL': [
  324. function (value, element) {
  325. return this.optional(element) || /^\d\d?[\.\/-]\d\d?[\.\/-]\d\d\d?\d?$/.test(value);
  326. },
  327. 'Vul hier een geldige datum in.'
  328. ],
  329. 'time': [
  330. function (value, element) {
  331. return this.optional(element) || /^([01]\d|2[0-3])(:[0-5]\d){0,2}$/.test(value);
  332. },
  333. $.mage.__('Please enter a valid time, between 00:00 and 23:59')
  334. ],
  335. 'time12h': [
  336. function (value, element) {
  337. return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\s[AP]M))$/i.test(value);
  338. },
  339. $.mage.__('Please enter a valid time, between 00:00 am and 12:00 pm')
  340. ],
  341. 'phoneUS': [
  342. function (phoneNumber, element) {
  343. phoneNumber = phoneNumber.replace(/\s+/g, '');
  344. return this.optional(element) || phoneNumber.length > 9 &&
  345. phoneNumber.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/);
  346. },
  347. $.mage.__('Please specify a valid phone number')
  348. ],
  349. 'phoneUK': [
  350. function (phoneNumber, element) {
  351. return this.optional(element) || phoneNumber.length > 9 &&
  352. phoneNumber.match(/^(\(?(0|\+44)[1-9]{1}\d{1,4}?\)?\s?\d{3,4}\s?\d{3,4})$/);
  353. },
  354. $.mage.__('Please specify a valid phone number')
  355. ],
  356. 'mobileUK': [
  357. function (phoneNumber, element) {
  358. return this.optional(element) || phoneNumber.length > 9 &&
  359. phoneNumber.match(/^((0|\+44)7\d{3}\s?\d{6})$/);
  360. },
  361. $.mage.__('Please specify a valid mobile number')
  362. ],
  363. 'stripped-min-length': [
  364. function (value, element, param) {
  365. return value.length >= param;
  366. },
  367. $.mage.__('Please enter at least {0} characters')
  368. ],
  369. /* detect chars that would require more than 3 bytes */
  370. 'validate-no-utf8mb4-characters': [
  371. function (value) {
  372. var validator = this,
  373. message = $.mage.__('Please remove invalid characters: {0}.'),
  374. matches = value.match(/(?:[\uD800-\uDBFF][\uDC00-\uDFFF])/g),
  375. result = matches === null;
  376. if (!result) {
  377. validator.charErrorMessage = message.replace('{0}', matches.join());
  378. }
  379. return result;
  380. }, function () {
  381. return this.charErrorMessage;
  382. }
  383. ],
  384. /* eslint-disable max-len */
  385. 'email2': [
  386. function (value, element) {
  387. return this.optional(element) ||
  388. /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
  389. },
  390. $.validator.messages.email
  391. ],
  392. 'url2': [
  393. function (value, element) {
  394. return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
  395. },
  396. $.validator.messages.url
  397. ],
  398. /* eslint-enable max-len */
  399. 'credit-card-types': [
  400. function (value, element, param) {
  401. var validTypes;
  402. if (/[^0-9-]+/.test(value)) {
  403. return false;
  404. }
  405. value = value.replace(/\D/g, '');
  406. validTypes = 0x0000;
  407. if (param.mastercard) {
  408. validTypes |= 0x0001;
  409. }
  410. if (param.visa) {
  411. validTypes |= 0x0002;
  412. }
  413. if (param.amex) {
  414. validTypes |= 0x0004;
  415. }
  416. if (param.dinersclub) {
  417. validTypes |= 0x0008;
  418. }
  419. if (param.enroute) {
  420. validTypes |= 0x0010;
  421. }
  422. if (param.discover) {
  423. validTypes |= 0x0020;
  424. }
  425. if (param.jcb) {
  426. validTypes |= 0x0040;
  427. }
  428. if (param.unknown) {
  429. validTypes |= 0x0080;
  430. }
  431. if (param.all) {
  432. validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080;
  433. }
  434. if (validTypes & 0x0001 && /^(51|52|53|54|55)/.test(value)) { //mastercard
  435. return value.length === 16;
  436. }
  437. if (validTypes & 0x0002 && /^(4)/.test(value)) { //visa
  438. return value.length === 16;
  439. }
  440. if (validTypes & 0x0004 && /^(34|37)/.test(value)) { //amex
  441. return value.length === 15;
  442. }
  443. if (validTypes & 0x0008 && /^(300|301|302|303|304|305|36|38)/.test(value)) { //dinersclub
  444. return value.length === 14;
  445. }
  446. if (validTypes & 0x0010 && /^(2014|2149)/.test(value)) { //enroute
  447. return value.length === 15;
  448. }
  449. if (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover
  450. return value.length === 16;
  451. }
  452. if (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb
  453. return value.length === 16;
  454. }
  455. if (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb
  456. return value.length === 15;
  457. }
  458. if (validTypes & 0x0080) { //unknown
  459. return true;
  460. }
  461. return false;
  462. },
  463. $.mage.__('Please enter a valid credit card number.')
  464. ],
  465. /* eslint-disable max-len */
  466. 'ipv4': [
  467. function (value, element) {
  468. return this.optional(element) ||
  469. /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i.test(value);
  470. },
  471. $.mage.__('Please enter a valid IP v4 address.')
  472. ],
  473. 'ipv6': [
  474. function (value, element) {
  475. return this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);
  476. },
  477. $.mage.__('Please enter a valid IP v6 address.')
  478. ],
  479. /* eslint-enable max-len */
  480. 'pattern': [
  481. function (value, element, param) {
  482. return this.optional(element) || param.test(value);
  483. },
  484. $.mage.__('Invalid format.')
  485. ],
  486. 'allow-container-className': [
  487. function (element) {
  488. if (element.type === 'radio' || element.type === 'checkbox') {
  489. return $(element).hasClass('change-container-classname');
  490. }
  491. },
  492. ''
  493. ],
  494. 'validate-no-html-tags': [
  495. function (value) {
  496. return !/<(\/)?\w+/.test(value);
  497. },
  498. $.mage.__('HTML tags are not allowed.')
  499. ],
  500. 'validate-select': [
  501. function (value) {
  502. return value !== 'none' && value != null && value.length !== 0;
  503. },
  504. $.mage.__('Please select an option.')
  505. ],
  506. 'validate-no-empty': [
  507. function (value) {
  508. return !$.mage.isEmpty(value);
  509. },
  510. $.mage.__('Empty Value.')
  511. ],
  512. 'validate-alphanum-with-spaces': [
  513. function (v) {
  514. return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9 ]+$/.test(v);
  515. },
  516. $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or spaces only in this field.')
  517. ],
  518. 'validate-data': [
  519. function (v) {
  520. return $.mage.isEmptyNoTrim(v) || /^[A-Za-z]+[A-Za-z0-9_]+$/.test(v);
  521. },
  522. $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.') //eslint-disable-line max-len
  523. ],
  524. 'validate-street': [
  525. function (v) {
  526. return $.mage.isEmptyNoTrim(v) || /^[ \w]{3,}([A-Za-z]\.)?([ \w]*\#\d+)?(\r\n| )[ \w]{3,}/.test(v);
  527. },
  528. $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9), spaces and "#" in this field.')
  529. ],
  530. 'validate-phoneStrict': [
  531. function (v) {
  532. return $.mage.isEmptyNoTrim(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
  533. },
  534. $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')
  535. ],
  536. 'validate-phoneLax': [
  537. function (v) {
  538. return $.mage.isEmptyNoTrim(v) ||
  539. /^((\d[\-. ]?)?((\(\d{3}\))|\d{3}))?[\-. ]?\d{3}[\-. ]?\d{4}$/.test(v);
  540. },
  541. $.mage.__('Please enter a valid phone number. For example (123) 456-7890 or 123-456-7890.')
  542. ],
  543. 'validate-fax': [
  544. function (v) {
  545. return $.mage.isEmptyNoTrim(v) || /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/.test(v);
  546. },
  547. $.mage.__('Please enter a valid fax number (Ex: 123-456-7890).')
  548. ],
  549. 'validate-email': [
  550. function (v) {
  551. return $.mage.isEmptyNoTrim(v) || /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i.test(v); //eslint-disable-line max-len
  552. },
  553. $.mage.__('Please enter a valid email address (Ex: johndoe@domain.com).')
  554. ],
  555. 'validate-emailSender': [
  556. function (v) {
  557. return $.mage.isEmptyNoTrim(v) || /^[\S ]+$/.test(v);
  558. },
  559. $.mage.__('Please enter a valid email address (Ex: johndoe@domain.com).')
  560. ],
  561. 'validate-password': [
  562. function (v) {
  563. var pass;
  564. if (v == null) {
  565. return false;
  566. }
  567. //strip leading and trailing spaces
  568. pass = $.trim(v);
  569. if (!pass.length) {
  570. return true;
  571. }
  572. return !(pass.length > 0 && pass.length < 6);
  573. },
  574. $.mage.__('Please enter 6 or more characters. Leading and trailing spaces will be ignored.')
  575. ],
  576. 'validate-admin-password': [
  577. function (v) {
  578. var pass;
  579. if (v == null) {
  580. return false;
  581. }
  582. pass = $.trim(v);
  583. // strip leading and trailing spaces
  584. if (pass.length === 0) {
  585. return true;
  586. }
  587. if (!/[a-z]/i.test(v) || !/[0-9]/.test(v)) {
  588. return false;
  589. }
  590. if (pass.length < 7) {
  591. return false;
  592. }
  593. return true;
  594. },
  595. $.mage.__('Please enter 7 or more characters, using both numeric and alphabetic.')
  596. ],
  597. 'validate-customer-password': [
  598. function (v, elm) {
  599. var validator = this,
  600. counter = 0,
  601. passwordMinLength = $(elm).data('password-min-length'),
  602. passwordMinCharacterSets = $(elm).data('password-min-character-sets'),
  603. pass = $.trim(v),
  604. result = pass.length >= passwordMinLength;
  605. if (result === false) {
  606. validator.passwordErrorMessage = $.mage.__('Minimum length of this field must be equal or greater than %1 symbols. Leading and trailing spaces will be ignored.').replace('%1', passwordMinLength); //eslint-disable-line max-len
  607. return result;
  608. }
  609. if (pass.match(/\d+/)) {
  610. counter++;
  611. }
  612. if (pass.match(/[a-z]+/)) {
  613. counter++;
  614. }
  615. if (pass.match(/[A-Z]+/)) {
  616. counter++;
  617. }
  618. if (pass.match(/[^a-zA-Z0-9]+/)) {
  619. counter++;
  620. }
  621. if (counter < passwordMinCharacterSets) {
  622. result = false;
  623. validator.passwordErrorMessage = $.mage.__('Minimum of different classes of characters in password is %1. Classes of characters: Lower Case, Upper Case, Digits, Special Characters.').replace('%1', passwordMinCharacterSets); //eslint-disable-line max-len
  624. }
  625. return result;
  626. }, function () {
  627. return this.passwordErrorMessage;
  628. }
  629. ],
  630. 'validate-url': [
  631. function (v) {
  632. if ($.mage.isEmptyNoTrim(v)) {
  633. return true;
  634. }
  635. v = (v || '').replace(/^\s+/, '').replace(/\s+$/, '');
  636. return (/^(http|https|ftp):\/\/(([A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))(\.[A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))*)(:(\d+))?(\/[A-Z0-9~](([A-Z0-9_~-]|\.)*[A-Z0-9~]|))*\/?(.*)?$/i).test(v); //eslint-disable-line max-len
  637. },
  638. $.mage.__('Please enter a valid URL. Protocol is required (http://, https:// or ftp://).')
  639. ],
  640. 'validate-clean-url': [
  641. function (v) {
  642. return $.mage.isEmptyNoTrim(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v) || /^(www)((\.[A-Z0-9][A-Z0-9_-]*)+.(com|org|net|dk|at|us|tv|info|uk|co.uk|biz|se)$)(:(\d+))?\/?/i.test(v); //eslint-disable-line max-len
  643. },
  644. $.mage.__('Please enter a valid URL. For example http://www.example.com or www.example.com.')
  645. ],
  646. 'validate-xml-identifier': [
  647. function (v) {
  648. return $.mage.isEmptyNoTrim(v) || /^[A-Z][A-Z0-9_\/-]*$/i.test(v);
  649. },
  650. $.mage.__('Please enter a valid XML-identifier (Ex: something_1, block5, id-4).')
  651. ],
  652. 'validate-ssn': [
  653. function (v) {
  654. return $.mage.isEmptyNoTrim(v) || /^\d{3}-?\d{2}-?\d{4}$/.test(v);
  655. },
  656. $.mage.__('Please enter a valid social security number (Ex: 123-45-6789).')
  657. ],
  658. 'validate-zip-us': [
  659. function (v) {
  660. return $.mage.isEmptyNoTrim(v) || /(^\d{5}$)|(^\d{5}-\d{4}$)/.test(v);
  661. },
  662. $.mage.__('Please enter a valid zip code (Ex: 90602 or 90602-1234).')
  663. ],
  664. 'validate-date-au': [
  665. function (v) {
  666. var regex, d;
  667. if ($.mage.isEmptyNoTrim(v)) {
  668. return true;
  669. }
  670. regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
  671. if ($.mage.isEmpty(v) || !regex.test(v)) {
  672. return false;
  673. }
  674. d = new Date(v.replace(regex, '$2/$1/$3'));
  675. return parseInt(RegExp.$2, 10) === 1 + d.getMonth() &&
  676. parseInt(RegExp.$1, 10) === d.getDate() &&
  677. parseInt(RegExp.$3, 10) === d.getFullYear();
  678. },
  679. $.mage.__('Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.')
  680. ],
  681. 'validate-currency-dollar': [
  682. function (v) {
  683. return $.mage.isEmptyNoTrim(v) || /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(v); //eslint-disable-line max-len
  684. },
  685. $.mage.__('Please enter a valid $ amount. For example $100.00.')
  686. ],
  687. 'validate-not-negative-number': [
  688. function (v) {
  689. if ($.mage.isEmptyNoTrim(v)) {
  690. return true;
  691. }
  692. v = $.mage.parseNumber(v);
  693. return !isNaN(v) && v >= 0;
  694. },
  695. $.mage.__('Please enter a number 0 or greater in this field.')
  696. ],
  697. // validate-not-negative-number should be replaced in all places with this one and then removed
  698. 'validate-zero-or-greater': [
  699. function (v) {
  700. if ($.mage.isEmptyNoTrim(v)) {
  701. return true;
  702. }
  703. v = $.mage.parseNumber(v);
  704. return !isNaN(v) && v >= 0;
  705. },
  706. $.mage.__('Please enter a number 0 or greater in this field.')
  707. ],
  708. 'validate-greater-than-zero': [
  709. function (v) {
  710. if ($.mage.isEmptyNoTrim(v)) {
  711. return true;
  712. }
  713. v = $.mage.parseNumber(v);
  714. return !isNaN(v) && v > 0;
  715. },
  716. $.mage.__('Please enter a number greater than 0 in this field.')
  717. ],
  718. 'validate-css-length': [
  719. function (v) {
  720. if (v !== '') {
  721. return (/^[0-9]*\.*[0-9]+(px|pc|pt|ex|em|mm|cm|in|%)?$/).test(v);
  722. }
  723. return true;
  724. },
  725. $.mage.__('Please input a valid CSS-length (Ex: 100px, 77pt, 20em, .5ex or 50%).')
  726. ],
  727. // Additional methods
  728. 'validate-number': [
  729. function (v) {
  730. return $.mage.isEmptyNoTrim(v) || !isNaN($.mage.parseNumber(v)) && /^\s*-?\d*(\.\d*)?\s*$/.test(v);
  731. },
  732. $.mage.__('Please enter a valid number in this field.')
  733. ],
  734. 'required-number': [
  735. function (v) {
  736. return !!v.length;
  737. },
  738. $.mage.__('Please enter a valid number in this field.')
  739. ],
  740. 'validate-number-range': [
  741. function (v, elm, param) {
  742. var numValue, dataAttrRange, classNameRange, result, range, m, classes, ii;
  743. if ($.mage.isEmptyNoTrim(v)) {
  744. return true;
  745. }
  746. numValue = $.mage.parseNumber(v);
  747. if (isNaN(numValue)) {
  748. return false;
  749. }
  750. dataAttrRange = /^(-?[\d.,]+)?-(-?[\d.,]+)?$/;
  751. classNameRange = /^number-range-(-?[\d.,]+)?-(-?[\d.,]+)?$/;
  752. result = true;
  753. range = param;
  754. if (typeof range === 'string') {
  755. m = dataAttrRange.exec(range);
  756. if (m) {
  757. result = result && $.mage.isBetween(numValue, m[1], m[2]);
  758. } else {
  759. result = false;
  760. }
  761. } else if (elm && elm.className) {
  762. classes = elm.className.split(' ');
  763. ii = classes.length;
  764. while (ii--) {
  765. range = classes[ii];
  766. m = classNameRange.exec(range);
  767. if (m) { //eslint-disable-line max-depth
  768. result = result && $.mage.isBetween(numValue, m[1], m[2]);
  769. break;
  770. }
  771. }
  772. }
  773. return result;
  774. },
  775. $.mage.__('The value is not within the specified range.'),
  776. true
  777. ],
  778. 'validate-digits': [
  779. function (v) {
  780. return $.mage.isEmptyNoTrim(v) || !/[^\d]/.test(v);
  781. },
  782. $.mage.__('Please enter a valid number in this field.')
  783. ],
  784. 'validate-forbidden-extensions': [
  785. function (v, elem) {
  786. var forbiddenExtensions = $(elem).attr('data-validation-params'),
  787. forbiddenExtensionsArray = forbiddenExtensions.split(','),
  788. extensionsArray = v.split(','),
  789. result = true;
  790. this.validateExtensionsMessage = $.mage.__('Forbidden extensions has been used. Avoid usage of ') +
  791. forbiddenExtensions;
  792. $.each(extensionsArray, function (key, extension) {
  793. if (forbiddenExtensionsArray.indexOf(extension) !== -1) {
  794. result = false;
  795. }
  796. });
  797. return result;
  798. }, function () {
  799. return this.validateExtensionsMessage;
  800. }
  801. ],
  802. 'validate-digits-range': [
  803. function (v, elm, param) {
  804. var numValue, dataAttrRange, classNameRange, result, range, m, classes, ii;
  805. if ($.mage.isEmptyNoTrim(v)) {
  806. return true;
  807. }
  808. numValue = $.mage.parseNumber(v);
  809. if (isNaN(numValue)) {
  810. return false;
  811. }
  812. dataAttrRange = /^(-?\d+)?-(-?\d+)?$/;
  813. classNameRange = /^digits-range-(-?\d+)?-(-?\d+)?$/;
  814. result = true;
  815. range = param;
  816. if (typeof range === 'string') {
  817. m = dataAttrRange.exec(range);
  818. if (m) {
  819. result = result && $.mage.isBetween(numValue, m[1], m[2]);
  820. } else {
  821. result = false;
  822. }
  823. } else if (elm && elm.className) {
  824. classes = elm.className.split(' ');
  825. ii = classes.length;
  826. while (ii--) {
  827. range = classes[ii];
  828. m = classNameRange.exec(range);
  829. if (m) { //eslint-disable-line max-depth
  830. result = result && $.mage.isBetween(numValue, m[1], m[2]);
  831. break;
  832. }
  833. }
  834. }
  835. return result;
  836. },
  837. $.mage.__('The value is not within the specified range.'),
  838. true
  839. ],
  840. 'validate-range': [
  841. function (v, elm) {
  842. var minValue, maxValue, ranges, reRange, result, values,
  843. i, name, validRange, minValidRange, maxValidRange;
  844. if ($.mage.isEmptyNoTrim(v)) {
  845. return true;
  846. } else if ($.validator.methods['validate-digits'] && $.validator.methods['validate-digits'](v)) {
  847. minValue = maxValue = $.mage.parseNumber(v);
  848. } else {
  849. ranges = /^(-?\d+)?-(-?\d+)?$/.exec(v);
  850. if (ranges) {
  851. minValue = $.mage.parseNumber(ranges[1]);
  852. maxValue = $.mage.parseNumber(ranges[2]);
  853. if (minValue > maxValue) { //eslint-disable-line max-depth
  854. return false;
  855. }
  856. } else {
  857. return false;
  858. }
  859. }
  860. reRange = /^range-(-?\d+)?-(-?\d+)?$/;
  861. result = true;
  862. values = $(elm).prop('class').split(' ');
  863. for (i = values.length - 1; i >= 0; i--) {
  864. name = values[i];
  865. validRange = reRange.exec(name);
  866. if (validRange) {
  867. minValidRange = $.mage.parseNumber(validRange[1]);
  868. maxValidRange = $.mage.parseNumber(validRange[2]);
  869. result = result &&
  870. (isNaN(minValidRange) || minValue >= minValidRange) &&
  871. (isNaN(maxValidRange) || maxValue <= maxValidRange);
  872. }
  873. }
  874. return result;
  875. },
  876. $.mage.__('The value is not within the specified range.')
  877. ],
  878. 'validate-alpha': [
  879. function (v) {
  880. return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+$/.test(v);
  881. },
  882. $.mage.__('Please use letters only (a-z or A-Z) in this field.')
  883. ],
  884. 'validate-code': [
  885. function (v) {
  886. return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+[a-zA-Z0-9_]+$/.test(v);
  887. },
  888. $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.') //eslint-disable-line max-len
  889. ],
  890. 'validate-alphanum': [
  891. function (v) {
  892. return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z0-9]+$/.test(v);
  893. },
  894. $.mage.__('Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.') //eslint-disable-line max-len
  895. ],
  896. 'validate-not-number-first': [
  897. function (value) {
  898. return $.mage.isEmptyNoTrim(value) || /^[^0-9-\.].*$/.test(value.trim());
  899. },
  900. $.mage.__('First character must be letter.')
  901. ],
  902. 'validate-date': [
  903. function (value, params, additionalParams) {
  904. var test = moment(value, additionalParams.dateFormat);
  905. return $.mage.isEmptyNoTrim(value) || test.isValid();
  906. },
  907. $.mage.__('Please enter a valid date.')
  908. ],
  909. 'validate-date-range': [
  910. function (v, elm) {
  911. var m = /\bdate-range-(\w+)-(\w+)\b/.exec(elm.className),
  912. currentYear, normalizedTime, dependentElements;
  913. if (!m || m[2] === 'to' || $.mage.isEmptyNoTrim(v)) {
  914. return true;
  915. }
  916. currentYear = new Date().getFullYear() + '';
  917. /**
  918. * @param {String} vd
  919. * @return {Number}
  920. */
  921. normalizedTime = function (vd) {
  922. vd = vd.split(/[.\/]/);
  923. if (vd[2] && vd[2].length < 4) {
  924. vd[2] = currentYear.substr(0, vd[2].length) + vd[2];
  925. }
  926. return new Date(vd.join('/')).getTime();
  927. };
  928. dependentElements = $(elm.form).find('.validate-date-range.date-range-' + m[1] + '-to');
  929. return !dependentElements.length || $.mage.isEmptyNoTrim(dependentElements[0].value) ||
  930. normalizedTime(v) <= normalizedTime(dependentElements[0].value);
  931. },
  932. $.mage.__('Make sure the To Date is later than or the same as the From Date.')
  933. ],
  934. 'validate-cpassword': [
  935. function () {
  936. var conf = $('#confirmation').length > 0 ? $('#confirmation') : $($('.validate-cpassword')[0]),
  937. pass = false,
  938. passwordElements, i, passwordElement;
  939. if ($('#password')) {
  940. pass = $('#password');
  941. }
  942. passwordElements = $('.validate-password');
  943. for (i = 0; i < passwordElements.length; i++) {
  944. passwordElement = $(passwordElements[i]);
  945. if (passwordElement.closest('form').attr('id') === conf.closest('form').attr('id')) {
  946. pass = passwordElement;
  947. }
  948. }
  949. if ($('.validate-admin-password').length) {
  950. pass = $($('.validate-admin-password')[0]);
  951. }
  952. return pass.val() === conf.val();
  953. },
  954. $.mage.__('Please make sure your passwords match.')
  955. ],
  956. 'validate-identifier': [
  957. function (v) {
  958. return $.mage.isEmptyNoTrim(v) || /^[a-z0-9][a-z0-9_\/-]+(\.[a-z0-9_-]+)?$/.test(v);
  959. },
  960. $.mage.__('Please enter a valid URL Key (Ex: "example-page", "example-page.html" or "anotherlevel/example-page").') //eslint-disable-line max-len
  961. ],
  962. 'validate-zip-international': [
  963. /*function(v) {
  964. // @TODO: Cleanup
  965. return Validation.get('IsEmpty').test(v) ||
  966. /(^[A-z0-9]{2,10}([\s]{0,1}|[\-]{0,1})[A-z0-9]{2,10}$)/.test(v);
  967. }*/
  968. function () {
  969. return true;
  970. },
  971. $.mage.__('Please enter a valid zip code.')
  972. ],
  973. 'validate-one-required': [
  974. function (v, elm) {
  975. var p = $(elm).parent(),
  976. options = p.find('input');
  977. return options.map(function (el) {
  978. return $(el).val();
  979. }).length > 0;
  980. },
  981. $.mage.__('Please select one of the options above.')
  982. ],
  983. 'validate-state': [
  984. function (v) {
  985. return v !== 0 || v === '';
  986. },
  987. $.mage.__('Please select State/Province.')
  988. ],
  989. 'required-file': [
  990. function (v, elm) {
  991. var result = !$.mage.isEmptyNoTrim(v),
  992. ovId;
  993. if (!result) {
  994. ovId = $('#' + $(elm).attr('id') + '_value');
  995. if (ovId.length > 0) {
  996. result = !$.mage.isEmptyNoTrim(ovId.val());
  997. }
  998. }
  999. return result;
  1000. },
  1001. $.mage.__('Please select a file.')
  1002. ],
  1003. 'validate-ajax-error': [
  1004. function (v, element) {
  1005. element = $(element);
  1006. element.on('change.ajaxError', function () {
  1007. element.removeClass('validate-ajax-error');
  1008. element.off('change.ajaxError');
  1009. });
  1010. return !element.hasClass('validate-ajax-error');
  1011. },
  1012. ''
  1013. ],
  1014. 'validate-optional-datetime': [
  1015. function (v, elm, param) {
  1016. var dateTimeParts = $('.datetime-picker[id^="options_' + param + '"]'),
  1017. hasWithValue = false,
  1018. hasWithNoValue = false,
  1019. pattern = /day_part$/i,
  1020. i;
  1021. for (i = 0; i < dateTimeParts.length; i++) {
  1022. if (!pattern.test($(dateTimeParts[i]).attr('id'))) {
  1023. if ($(dateTimeParts[i]).val() === 's') { //eslint-disable-line max-depth
  1024. hasWithValue = true;
  1025. } else {
  1026. hasWithNoValue = true;
  1027. }
  1028. }
  1029. }
  1030. return hasWithValue ^ hasWithNoValue;
  1031. },
  1032. $.mage.__('The field isn\'t complete.')
  1033. ],
  1034. 'validate-required-datetime': [
  1035. function (v, elm, param) {
  1036. var dateTimeParts = $('.datetime-picker[id^="options_' + param + '"]'),
  1037. i;
  1038. for (i = 0; i < dateTimeParts.length; i++) {
  1039. if (dateTimeParts[i].value === '') {
  1040. return false;
  1041. }
  1042. }
  1043. return true;
  1044. },
  1045. $.mage.__('This is a required field.')
  1046. ],
  1047. 'validate-one-required-by-name': [
  1048. function (v, elm, selector) {
  1049. var name = elm.name.replace(/([\\"])/g, '\\$1'),
  1050. container = this.currentForm;
  1051. selector = selector === true ? 'input[name="' + name + '"]:checked' : selector;
  1052. return !!container.querySelectorAll(selector).length;
  1053. },
  1054. $.mage.__('Please select one of the options.')
  1055. ],
  1056. 'less-than-equals-to': [
  1057. function (value, element, params) {
  1058. if ($.isNumeric($(params).val()) && $.isNumeric(value)) {
  1059. this.lteToVal = $(params).val();
  1060. return parseFloat(value) <= parseFloat($(params).val());
  1061. }
  1062. return true;
  1063. },
  1064. function () {
  1065. var message = $.mage.__('Please enter a value less than or equal to %s.');
  1066. return message.replace('%s', this.lteToVal);
  1067. }
  1068. ],
  1069. 'greater-than-equals-to': [
  1070. function (value, element, params) {
  1071. if ($.isNumeric($(params).val()) && $.isNumeric(value)) {
  1072. this.gteToVal = $(params).val();
  1073. return parseFloat(value) >= parseFloat($(params).val());
  1074. }
  1075. return true;
  1076. },
  1077. function () {
  1078. var message = $.mage.__('Please enter a value greater than or equal to %s.');
  1079. return message.replace('%s', this.gteToVal);
  1080. }
  1081. ],
  1082. 'validate-emails': [
  1083. function (value) {
  1084. var validRegexp, emails, i;
  1085. if ($.mage.isEmpty(value)) {
  1086. return true;
  1087. }
  1088. validRegexp = /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i; //eslint-disable-line max-len
  1089. emails = value.split(/[\s\n\,]+/g);
  1090. for (i = 0; i < emails.length; i++) {
  1091. if (!validRegexp.test(emails[i].trim())) {
  1092. return false;
  1093. }
  1094. }
  1095. return true;
  1096. },
  1097. $.mage.__('Please enter valid email addresses, separated by commas. For example, johndoe@domain.com, johnsmith@domain.com.') //eslint-disable-line max-len
  1098. ],
  1099. 'validate-cc-type-select': [
  1100. /**
  1101. * Validate credit card type matches credit card number
  1102. * @param {*} value - select credit card type
  1103. * @param {*} element - element contains the select box for credit card types
  1104. * @param {*} params - selector for credit card number
  1105. * @return {Boolean}
  1106. */
  1107. function (value, element, params) {
  1108. if (value && params && creditCartTypes[value]) {
  1109. return creditCartTypes[value][0].test($(params).val().replace(/\s+/g, ''));
  1110. }
  1111. return false;
  1112. },
  1113. $.mage.__('Card type does not match credit card number.')
  1114. ],
  1115. 'validate-cc-number': [
  1116. /**
  1117. * Validate credit card number based on mod 10.
  1118. *
  1119. * @param {*} value - credit card number
  1120. * @return {Boolean}
  1121. */
  1122. function (value) {
  1123. if (value) {
  1124. return validateCreditCard(value);
  1125. }
  1126. return false;
  1127. },
  1128. $.mage.__('Please enter a valid credit card number.')
  1129. ],
  1130. 'validate-cc-type': [
  1131. /**
  1132. * Validate credit card number is for the correct credit card type.
  1133. *
  1134. * @param {String} value - credit card number
  1135. * @param {*} element - element contains credit card number
  1136. * @param {*} params - selector for credit card type
  1137. * @return {Boolean}
  1138. */
  1139. function (value, element, params) {
  1140. var ccType;
  1141. if (value && params) {
  1142. ccType = $(params).val();
  1143. value = value.replace(/\s/g, '').replace(/\-/g, '');
  1144. if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {
  1145. return creditCartTypes[ccType][0].test(value);
  1146. } else if (creditCartTypes[ccType] && !creditCartTypes[ccType][0]) {
  1147. return true;
  1148. }
  1149. }
  1150. return false;
  1151. },
  1152. $.mage.__('Credit card number does not match credit card type.')
  1153. ],
  1154. 'validate-cc-exp': [
  1155. /**
  1156. * Validate credit card expiration date, make sure it's within the year and not before current month.
  1157. *
  1158. * @param {*} value - month
  1159. * @param {*} element - element contains month
  1160. * @param {*} params - year selector
  1161. * @return {Boolean}
  1162. */
  1163. function (value, element, params) {
  1164. var isValid = false,
  1165. month, year, currentTime, currentMonth, currentYear;
  1166. if (value && params) {
  1167. month = value;
  1168. year = $(params).val();
  1169. currentTime = new Date();
  1170. currentMonth = currentTime.getMonth() + 1;
  1171. currentYear = currentTime.getFullYear();
  1172. isValid = !year || year > currentYear || year == currentYear && month >= currentMonth; //eslint-disable-line
  1173. }
  1174. return isValid;
  1175. },
  1176. $.mage.__('Incorrect credit card expiration date.')
  1177. ],
  1178. 'validate-cc-cvn': [
  1179. /**
  1180. * Validate credit card cvn based on credit card type.
  1181. *
  1182. * @param {*} value - credit card cvn
  1183. * @param {*} element - element contains credit card cvn
  1184. * @param {*} params - credit card type selector
  1185. * @return {*}
  1186. */
  1187. function (value, element, params) {
  1188. var ccType;
  1189. if (value && params) {
  1190. ccType = $(params).val();
  1191. if (creditCartTypes[ccType] && creditCartTypes[ccType][0]) {
  1192. return creditCartTypes[ccType][1].test(value);
  1193. }
  1194. }
  1195. return false;
  1196. },
  1197. $.mage.__('Please enter a valid credit card verification number.')
  1198. ],
  1199. 'validate-cc-ukss': [
  1200. /**
  1201. * Validate Switch/Solo/Maestro issue number and start date is filled.
  1202. *
  1203. * @param {*} value - input field value
  1204. * @return {*}
  1205. */
  1206. function (value) {
  1207. return value;
  1208. },
  1209. $.mage.__('Please enter issue number or start date for switch/solo card type.')
  1210. ],
  1211. 'validate-length': [
  1212. function (v, elm) {
  1213. var reMax = new RegExp(/^maximum-length-[0-9]+$/),
  1214. reMin = new RegExp(/^minimum-length-[0-9]+$/),
  1215. validator = this,
  1216. result = true,
  1217. length = 0;
  1218. $.each(elm.className.split(' '), function (index, name) {
  1219. if (name.match(reMax) && result) {
  1220. length = name.split('-')[2];
  1221. result = v.length <= length;
  1222. validator.validateMessage =
  1223. $.mage.__('Please enter less or equal than %1 symbols.').replace('%1', length);
  1224. }
  1225. if (name.match(reMin) && result && !$.mage.isEmpty(v)) {
  1226. length = name.split('-')[2];
  1227. result = v.length >= length;
  1228. validator.validateMessage =
  1229. $.mage.__('Please enter more or equal than %1 symbols.').replace('%1', length);
  1230. }
  1231. });
  1232. return result;
  1233. }, function () {
  1234. return this.validateMessage;
  1235. }
  1236. ],
  1237. 'required-entry': [
  1238. function (value) {
  1239. return !$.mage.isEmpty(value);
  1240. }, $.mage.__('This is a required field.')
  1241. ],
  1242. 'not-negative-amount': [
  1243. function (v) {
  1244. if (v.length) {
  1245. return (/^\s*\d+([,.]\d+)*\s*%?\s*$/).test(v);
  1246. }
  1247. return true;
  1248. },
  1249. $.mage.__('Please enter positive number in this field.')
  1250. ],
  1251. 'validate-per-page-value-list': [
  1252. function (v) {
  1253. var isValid = !$.mage.isEmpty(v),
  1254. values = v.split(','),
  1255. i;
  1256. for (i = 0; i < values.length; i++) {
  1257. if (!/^[0-9]+$/.test(values[i])) {
  1258. isValid = false;
  1259. }
  1260. }
  1261. return isValid;
  1262. },
  1263. $.mage.__('Please enter a valid value, ex: 10,20,30')
  1264. ],
  1265. 'validate-per-page-value': [
  1266. function (v, elm) {
  1267. var values;
  1268. if ($.mage.isEmpty(v)) {
  1269. return false;
  1270. }
  1271. values = $('#' + elm.id + '_values').val().split(',');
  1272. return values.indexOf(v) !== -1;
  1273. },
  1274. $.mage.__('Please enter a valid value from list')
  1275. ],
  1276. 'validate-new-password': [
  1277. function (v) {
  1278. if ($.validator.methods['validate-password'] && !$.validator.methods['validate-password'](v)) {
  1279. return false;
  1280. }
  1281. if ($.mage.isEmpty(v) && v !== '') {
  1282. return false;
  1283. }
  1284. return true;
  1285. },
  1286. $.mage.__('Please enter 6 or more characters. Leading and trailing spaces will be ignored.')
  1287. ],
  1288. 'required-if-not-specified': [
  1289. function (value, element, params) {
  1290. var valid = false,
  1291. alternate = $(params),
  1292. alternateValue;
  1293. if (alternate.length > 0) {
  1294. valid = this.check(alternate);
  1295. // if valid, it may be blank, so check for that
  1296. if (valid) {
  1297. alternateValue = alternate.val();
  1298. if (typeof alternateValue == 'undefined' || alternateValue.length === 0) { //eslint-disable-line
  1299. valid = false;
  1300. }
  1301. }
  1302. }
  1303. if (!valid) {
  1304. valid = !this.optional(element);
  1305. }
  1306. return valid;
  1307. },
  1308. $.mage.__('This is a required field.')
  1309. ],
  1310. 'required-if-all-sku-empty-and-file-not-loaded': [
  1311. function (value, element, params) {
  1312. var valid = false,
  1313. alternate = $(params.specifiedId),
  1314. alternateValue;
  1315. if (alternate.length > 0) {
  1316. valid = this.check(alternate);
  1317. // if valid, it may be blank, so check for that
  1318. if (valid) {
  1319. alternateValue = alternate.val();
  1320. if (typeof alternateValue == 'undefined' || alternateValue.length === 0) { //eslint-disable-line
  1321. valid = false;
  1322. }
  1323. }
  1324. }
  1325. if (!valid) {
  1326. valid = !this.optional(element);
  1327. }
  1328. $('input[' + params.dataSku + '=true]').each(function () {
  1329. if ($(this).val() !== '') {
  1330. valid = true;
  1331. }
  1332. });
  1333. return valid;
  1334. },
  1335. $.mage.__('Please enter valid SKU key.')
  1336. ],
  1337. 'required-if-specified': [
  1338. function (value, element, params) {
  1339. var valid = true,
  1340. dependent = $(params),
  1341. dependentValue;
  1342. if (dependent.length > 0) {
  1343. valid = this.check(dependent);
  1344. // if valid, it may be blank, so check for that
  1345. if (valid) {
  1346. dependentValue = dependent.val();
  1347. valid = typeof dependentValue != 'undefined' && dependentValue.length > 0;
  1348. }
  1349. }
  1350. if (valid) {
  1351. valid = !this.optional(element);
  1352. } else {
  1353. valid = true; // dependent was not valid, so don't even check
  1354. }
  1355. return valid;
  1356. },
  1357. $.mage.__('This is a required field.')
  1358. ],
  1359. 'required-number-if-specified': [
  1360. function (value, element, params) {
  1361. var valid = true,
  1362. dependent = $(params),
  1363. depeValue;
  1364. if (dependent.length) {
  1365. valid = this.check(dependent);
  1366. if (valid) {
  1367. depeValue = dependent[0].value;
  1368. valid = !!(depeValue && depeValue.length);
  1369. }
  1370. }
  1371. return valid ? !!value.length : true;
  1372. },
  1373. $.mage.__('Please enter a valid number.')
  1374. ],
  1375. 'datetime-validation': [
  1376. function (value, element) {
  1377. var isValid = true;
  1378. if ($(element).val().length === 0) {
  1379. isValid = false;
  1380. $(element).addClass('mage-error');
  1381. }
  1382. return isValid;
  1383. },
  1384. $.mage.__('This is required field')
  1385. ],
  1386. 'required-text-swatch-entry': [
  1387. tableSingleValidation,
  1388. $.mage.__('Admin is a required field in each row.')
  1389. ],
  1390. 'required-visual-swatch-entry': [
  1391. tableSingleValidation,
  1392. $.mage.__('Admin is a required field in each row.')
  1393. ],
  1394. 'required-dropdown-attribute-entry': [
  1395. tableSingleValidation,
  1396. $.mage.__('Admin is a required field in each row.')
  1397. ],
  1398. 'validate-item-quantity': [
  1399. function (value, element, params) {
  1400. var validator = this,
  1401. result = false,
  1402. // obtain values for validation
  1403. qty = $.mage.parseNumber(value),
  1404. isMinAllowedValid = typeof params.minAllowed === 'undefined' ||
  1405. qty >= $.mage.parseNumber(params.minAllowed),
  1406. isMaxAllowedValid = typeof params.maxAllowed === 'undefined' ||
  1407. qty <= $.mage.parseNumber(params.maxAllowed),
  1408. isQtyIncrementsValid = typeof params.qtyIncrements === 'undefined' ||
  1409. qty % $.mage.parseNumber(params.qtyIncrements) === 0;
  1410. result = qty > 0;
  1411. if (result === false) {
  1412. validator.itemQtyErrorMessage = $.mage.__('Please enter a quantity greater than 0.');//eslint-disable-line max-len
  1413. return result;
  1414. }
  1415. result = isMinAllowedValid;
  1416. if (result === false) {
  1417. validator.itemQtyErrorMessage = $.mage.__('The fewest you may purchase is %1.').replace('%1', params.minAllowed);//eslint-disable-line max-len
  1418. return result;
  1419. }
  1420. result = isMaxAllowedValid;
  1421. if (result === false) {
  1422. validator.itemQtyErrorMessage = $.mage.__('The maximum you may purchase is %1.').replace('%1', params.maxAllowed);//eslint-disable-line max-len
  1423. return result;
  1424. }
  1425. result = isQtyIncrementsValid;
  1426. if (result === false) {
  1427. validator.itemQtyErrorMessage = $.mage.__('You can buy this product only in quantities of %1 at a time.').replace('%1', params.qtyIncrements);//eslint-disable-line max-len
  1428. return result;
  1429. }
  1430. return result;
  1431. }, function () {
  1432. return this.itemQtyErrorMessage;
  1433. }
  1434. ],
  1435. 'password-not-equal-to-user-name': [
  1436. function (value, element, params) {
  1437. if (typeof params === 'string') {
  1438. return value.toLowerCase() !== params.toLowerCase();
  1439. }
  1440. return true;
  1441. },
  1442. $.mage.__('The password can\'t be the same as the email address. Create a new password and try again.')
  1443. ]
  1444. };
  1445. $.each(rules, function (i, rule) {
  1446. rule.unshift(i);
  1447. $.validator.addMethod.apply($.validator, rule);
  1448. });
  1449. $.validator.addClassRules({
  1450. 'required-option': {
  1451. required: true
  1452. },
  1453. 'required-options-count': {
  1454. required: true
  1455. },
  1456. 'validate-both-passwords': {
  1457. 'validate-cpassword': true
  1458. }
  1459. });
  1460. $.validator.messages = $.extend($.validator.messages, {
  1461. required: $.mage.__('This is a required field.'),
  1462. remote: $.mage.__('Please fix this field.'),
  1463. email: $.mage.__('Please enter a valid email address.'),
  1464. url: $.mage.__('Please enter a valid URL.'),
  1465. date: $.mage.__('Please enter a valid date.'),
  1466. dateISO: $.mage.__('Please enter a valid date (ISO).'),
  1467. number: $.mage.__('Please enter a valid number.'),
  1468. digits: $.mage.__('Please enter only digits.'),
  1469. creditcard: $.mage.__('Please enter a valid credit card number.'),
  1470. equalTo: $.mage.__('Please enter the same value again.'),
  1471. maxlength: $.validator.format($.mage.__('Please enter no more than {0} characters.')),
  1472. minlength: $.validator.format($.mage.__('Please enter at least {0} characters.')),
  1473. rangelength: $.validator.format($.mage.__('Please enter a value between {0} and {1} characters long.')),
  1474. range: $.validator.format($.mage.__('Please enter a value between {0} and {1}.')),
  1475. max: $.validator.format($.mage.__('Please enter a value less than or equal to {0}.')),
  1476. min: $.validator.format($.mage.__('Please enter a value greater than or equal to {0}.'))
  1477. });
  1478. if ($.metadata) {
  1479. // Setting the type as html5 to enable data-validate attribute
  1480. $.metadata.setType('html5');
  1481. }
  1482. showLabel = $.validator.prototype.showLabel;
  1483. $.extend(true, $.validator.prototype, {
  1484. /**
  1485. * @param {*} element
  1486. * @param {*} message
  1487. */
  1488. showLabel: function (element, message) {
  1489. var label, elem;
  1490. showLabel.call(this, element, message);
  1491. // ARIA (adding aria-invalid & aria-describedby)
  1492. label = this.errorsFor(element);
  1493. elem = $(element);
  1494. if (!label.attr('id')) {
  1495. label.attr('id', this.idOrName(element) + '-error');
  1496. }
  1497. elem.attr('aria-invalid', 'true')
  1498. .attr('aria-describedby', label.attr('id'));
  1499. }
  1500. });
  1501. /**
  1502. * Validate form field without instantiating validate plug-in.
  1503. *
  1504. * @param {Element|String} element - DOM element or selector
  1505. * @return {Boolean} validation result
  1506. */
  1507. $.validator.validateElement = function (element) {
  1508. var form, validator, valid, classes;
  1509. element = $(element);
  1510. form = element.get(0).form;
  1511. validator = form ? $(form).data('validator') : null;
  1512. if (validator) {
  1513. return validator.element(element.get(0));
  1514. }
  1515. valid = true;
  1516. classes = element.prop('class').split(' ');
  1517. $.each(classes, $.proxy(function (i, className) {
  1518. if (this.methods[className] && !this.methods[className](element.val(), element.get(0))) {
  1519. valid = false;
  1520. return valid;
  1521. }
  1522. }, this));
  1523. return valid;
  1524. };
  1525. originValidateDelegate = $.fn.validateDelegate;
  1526. /**
  1527. * @return {*}
  1528. */
  1529. $.fn.validateDelegate = function () {
  1530. if (!this[0].form) {
  1531. return this;
  1532. }
  1533. return originValidateDelegate.apply(this, arguments);
  1534. };
  1535. /**
  1536. * Validate single element.
  1537. *
  1538. * @param {Element} element
  1539. * @param {Object} config
  1540. * @returns {*}
  1541. */
  1542. $.validator.validateSingleElement = function (element, config) {
  1543. var errors = {},
  1544. valid = true,
  1545. validateConfig = {
  1546. errorElement: 'label',
  1547. ignore: '.ignore-validate',
  1548. hideError: false
  1549. },
  1550. form, validator, classes, elementValue;
  1551. $.extend(validateConfig, config);
  1552. element = $(element).not(validateConfig.ignore);
  1553. if (!element.length) {
  1554. return true;
  1555. }
  1556. form = element.get(0).form;
  1557. validator = form ? $(form).data('validator') : null;
  1558. if (validator) {
  1559. return validator.element(element.get(0));
  1560. }
  1561. classes = element.prop('class').split(' ');
  1562. validator = element.parent().data('validator') ||
  1563. $.mage.validation(validateConfig, element.parent()).validate;
  1564. element.removeClass(validator.settings.errorClass);
  1565. validator.toHide = validator.toShow;
  1566. validator.hideErrors();
  1567. validator.toShow = validator.toHide = $([]);
  1568. $.each(classes, $.proxy(function (i, className) {
  1569. elementValue = element.val();
  1570. if (element.is(':checkbox') || element.is(':radio')) {
  1571. elementValue = element.is(':checked') || null;
  1572. }
  1573. if (this.methods[className] && !this.methods[className](elementValue, element.get(0))) {
  1574. valid = false;
  1575. errors[element.get(0).name] = this.messages[className];
  1576. validator.invalid[element.get(0).name] = true;
  1577. if (!validateConfig.hideError) {
  1578. validator.showErrors(errors);
  1579. }
  1580. return valid;
  1581. }
  1582. }, this));
  1583. return valid;
  1584. };
  1585. $.widget('mage.validation', {
  1586. options: {
  1587. meta: 'validate',
  1588. onfocusout: false,
  1589. onkeyup: false,
  1590. onclick: false,
  1591. ignoreTitle: true,
  1592. errorClass: 'mage-error',
  1593. errorElement: 'div',
  1594. /**
  1595. * @param {*} error
  1596. * @param {*} element
  1597. */
  1598. errorPlacement: function (error, element) {
  1599. var errorPlacement = element,
  1600. fieldWrapper;
  1601. // logic for date-picker error placement
  1602. if (element.hasClass('_has-datepicker')) {
  1603. errorPlacement = element.siblings('button');
  1604. }
  1605. // logic for field wrapper
  1606. fieldWrapper = element.closest('.addon');
  1607. if (fieldWrapper.length) {
  1608. errorPlacement = fieldWrapper.after(error);
  1609. }
  1610. //logic for checkboxes/radio
  1611. if (element.is(':checkbox') || element.is(':radio')) {
  1612. errorPlacement = element.parents('.control').children().last();
  1613. //fallback if group does not have .control parent
  1614. if (!errorPlacement.length) {
  1615. errorPlacement = element.siblings('label').last();
  1616. }
  1617. }
  1618. //logic for control with tooltip
  1619. if (element.siblings('.tooltip').length) {
  1620. errorPlacement = element.siblings('.tooltip');
  1621. }
  1622. errorPlacement.after(error);
  1623. }
  1624. },
  1625. /**
  1626. * Check if form pass validation rules without submit.
  1627. *
  1628. * @return boolean
  1629. */
  1630. isValid: function () {
  1631. return this.element.valid();
  1632. },
  1633. /**
  1634. * Remove validation error messages
  1635. */
  1636. clearError: function () {
  1637. if (arguments.length) {
  1638. $.each(arguments, $.proxy(function (index, item) {
  1639. this.validate.prepareElement(item);
  1640. this.validate.hideErrors();
  1641. }, this));
  1642. } else {
  1643. this.validate.resetForm();
  1644. }
  1645. },
  1646. /**
  1647. * Validation creation.
  1648. *
  1649. * @protected
  1650. */
  1651. _create: function () {
  1652. this.validate = this.element.validate(this.options);
  1653. // ARIA (adding aria-required attribute)
  1654. this.element
  1655. .find('.field.required')
  1656. .find('.control')
  1657. .find('input, select, textarea')
  1658. .attr('aria-required', 'true');
  1659. this._listenFormValidate();
  1660. },
  1661. /**
  1662. * Validation listening.
  1663. * @protected
  1664. */
  1665. _listenFormValidate: function () {
  1666. $('form').on('invalid-form.validate', this.listenFormValidateHandler);
  1667. },
  1668. /**
  1669. * Handle form validation. Focus on first invalid form field.
  1670. *
  1671. * @param {jQuery.Event} event
  1672. * @param {Object} validation
  1673. */
  1674. listenFormValidateHandler: function (event, validation) {
  1675. var firstActive = $(validation.errorList[0].element || []),
  1676. lastActive = $(validation.findLastActive() ||
  1677. validation.errorList.length && validation.errorList[0].element || []),
  1678. parent, windowHeight, successList;
  1679. if (lastActive.is(':hidden')) {
  1680. parent = lastActive.parent();
  1681. windowHeight = $(window).height();
  1682. $('html, body').animate({
  1683. scrollTop: parent.offset().top - windowHeight / 2
  1684. });
  1685. }
  1686. // ARIA (removing aria attributes if success)
  1687. successList = validation.successList;
  1688. if (successList.length) {
  1689. $.each(successList, function () {
  1690. $(this)
  1691. .removeAttr('aria-describedby')
  1692. .removeAttr('aria-invalid');
  1693. });
  1694. }
  1695. if (firstActive.length) {
  1696. $('html, body').animate({
  1697. scrollTop: firstActive.offset().top
  1698. });
  1699. firstActive.focus();
  1700. }
  1701. }
  1702. });
  1703. return $.mage.validation;
  1704. }));