form.js 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558
  1. /*!
  2. * # Semantic UI 2.2.6 - Form Validation
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Released under the MIT license
  7. * http://opensource.org/licenses/MIT
  8. *
  9. */
  10. ;(function ($, window, document, undefined) {
  11. "use strict";
  12. window = (typeof window != 'undefined' && window.Math == Math)
  13. ? window
  14. : (typeof self != 'undefined' && self.Math == Math)
  15. ? self
  16. : Function('return this')()
  17. ;
  18. $.fn.form = function(parameters) {
  19. var
  20. $allModules = $(this),
  21. moduleSelector = $allModules.selector || '',
  22. time = new Date().getTime(),
  23. performance = [],
  24. query = arguments[0],
  25. legacyParameters = arguments[1],
  26. methodInvoked = (typeof query == 'string'),
  27. queryArguments = [].slice.call(arguments, 1),
  28. returnedValue
  29. ;
  30. $allModules
  31. .each(function() {
  32. var
  33. $module = $(this),
  34. element = this,
  35. formErrors = [],
  36. keyHeldDown = false,
  37. // set at run-time
  38. $field,
  39. $group,
  40. $message,
  41. $prompt,
  42. $submit,
  43. $clear,
  44. $reset,
  45. settings,
  46. validation,
  47. metadata,
  48. selector,
  49. className,
  50. error,
  51. namespace,
  52. moduleNamespace,
  53. eventNamespace,
  54. instance,
  55. module
  56. ;
  57. module = {
  58. initialize: function() {
  59. // settings grabbed at run time
  60. module.get.settings();
  61. if(methodInvoked) {
  62. if(instance === undefined) {
  63. module.instantiate();
  64. }
  65. module.invoke(query);
  66. }
  67. else {
  68. if(instance !== undefined) {
  69. instance.invoke('destroy');
  70. }
  71. module.verbose('Initializing form validation', $module, settings);
  72. module.bindEvents();
  73. module.set.defaults();
  74. module.instantiate();
  75. }
  76. },
  77. instantiate: function() {
  78. module.verbose('Storing instance of module', module);
  79. instance = module;
  80. $module
  81. .data(moduleNamespace, module)
  82. ;
  83. },
  84. destroy: function() {
  85. module.verbose('Destroying previous module', instance);
  86. module.removeEvents();
  87. $module
  88. .removeData(moduleNamespace)
  89. ;
  90. },
  91. refresh: function() {
  92. module.verbose('Refreshing selector cache');
  93. $field = $module.find(selector.field);
  94. $group = $module.find(selector.group);
  95. $message = $module.find(selector.message);
  96. $prompt = $module.find(selector.prompt);
  97. $submit = $module.find(selector.submit);
  98. $clear = $module.find(selector.clear);
  99. $reset = $module.find(selector.reset);
  100. },
  101. submit: function() {
  102. module.verbose('Submitting form', $module);
  103. $module
  104. .submit()
  105. ;
  106. },
  107. attachEvents: function(selector, action) {
  108. action = action || 'submit';
  109. $(selector)
  110. .on('click' + eventNamespace, function(event) {
  111. module[action]();
  112. event.preventDefault();
  113. })
  114. ;
  115. },
  116. bindEvents: function() {
  117. module.verbose('Attaching form events');
  118. $module
  119. .on('submit' + eventNamespace, module.validate.form)
  120. .on('blur' + eventNamespace, selector.field, module.event.field.blur)
  121. .on('click' + eventNamespace, selector.submit, module.submit)
  122. .on('click' + eventNamespace, selector.reset, module.reset)
  123. .on('click' + eventNamespace, selector.clear, module.clear)
  124. ;
  125. if(settings.keyboardShortcuts) {
  126. $module
  127. .on('keydown' + eventNamespace, selector.field, module.event.field.keydown)
  128. ;
  129. }
  130. $field
  131. .each(function() {
  132. var
  133. $input = $(this),
  134. type = $input.prop('type'),
  135. inputEvent = module.get.changeEvent(type, $input)
  136. ;
  137. $(this)
  138. .on(inputEvent + eventNamespace, module.event.field.change)
  139. ;
  140. })
  141. ;
  142. },
  143. clear: function() {
  144. $field
  145. .each(function () {
  146. var
  147. $field = $(this),
  148. $element = $field.parent(),
  149. $fieldGroup = $field.closest($group),
  150. $prompt = $fieldGroup.find(selector.prompt),
  151. defaultValue = $field.data(metadata.defaultValue) || '',
  152. isCheckbox = $element.is(selector.uiCheckbox),
  153. isDropdown = $element.is(selector.uiDropdown),
  154. isErrored = $fieldGroup.hasClass(className.error)
  155. ;
  156. if(isErrored) {
  157. module.verbose('Resetting error on field', $fieldGroup);
  158. $fieldGroup.removeClass(className.error);
  159. $prompt.remove();
  160. }
  161. if(isDropdown) {
  162. module.verbose('Resetting dropdown value', $element, defaultValue);
  163. $element.dropdown('clear');
  164. }
  165. else if(isCheckbox) {
  166. $field.prop('checked', false);
  167. }
  168. else {
  169. module.verbose('Resetting field value', $field, defaultValue);
  170. $field.val('');
  171. }
  172. })
  173. ;
  174. },
  175. reset: function() {
  176. $field
  177. .each(function () {
  178. var
  179. $field = $(this),
  180. $element = $field.parent(),
  181. $fieldGroup = $field.closest($group),
  182. $prompt = $fieldGroup.find(selector.prompt),
  183. defaultValue = $field.data(metadata.defaultValue),
  184. isCheckbox = $element.is(selector.uiCheckbox),
  185. isDropdown = $element.is(selector.uiDropdown),
  186. isErrored = $fieldGroup.hasClass(className.error)
  187. ;
  188. if(defaultValue === undefined) {
  189. return;
  190. }
  191. if(isErrored) {
  192. module.verbose('Resetting error on field', $fieldGroup);
  193. $fieldGroup.removeClass(className.error);
  194. $prompt.remove();
  195. }
  196. if(isDropdown) {
  197. module.verbose('Resetting dropdown value', $element, defaultValue);
  198. $element.dropdown('restore defaults');
  199. }
  200. else if(isCheckbox) {
  201. module.verbose('Resetting checkbox value', $element, defaultValue);
  202. $field.prop('checked', defaultValue);
  203. }
  204. else {
  205. module.verbose('Resetting field value', $field, defaultValue);
  206. $field.val(defaultValue);
  207. }
  208. })
  209. ;
  210. },
  211. is: {
  212. bracketedRule: function(rule) {
  213. return (rule.type && rule.type.match(settings.regExp.bracket));
  214. },
  215. empty: function($field) {
  216. if(!$field || $field.length === 0) {
  217. return true;
  218. }
  219. else if($field.is('input[type="checkbox"]')) {
  220. return !$field.is(':checked');
  221. }
  222. else {
  223. return module.is.blank($field);
  224. }
  225. },
  226. blank: function($field) {
  227. return $.trim($field.val()) === '';
  228. },
  229. valid: function() {
  230. var
  231. allValid = true
  232. ;
  233. module.verbose('Checking if form is valid');
  234. $.each(validation, function(fieldName, field) {
  235. if( !( module.validate.field(field, fieldName) ) ) {
  236. allValid = false;
  237. }
  238. });
  239. return allValid;
  240. }
  241. },
  242. removeEvents: function() {
  243. $module
  244. .off(eventNamespace)
  245. ;
  246. $field
  247. .off(eventNamespace)
  248. ;
  249. $submit
  250. .off(eventNamespace)
  251. ;
  252. $field
  253. .off(eventNamespace)
  254. ;
  255. },
  256. event: {
  257. field: {
  258. keydown: function(event) {
  259. var
  260. $field = $(this),
  261. key = event.which,
  262. isInput = $field.is(selector.input),
  263. isCheckbox = $field.is(selector.checkbox),
  264. isInDropdown = ($field.closest(selector.uiDropdown).length > 0),
  265. keyCode = {
  266. enter : 13,
  267. escape : 27
  268. }
  269. ;
  270. if( key == keyCode.escape) {
  271. module.verbose('Escape key pressed blurring field');
  272. $field
  273. .blur()
  274. ;
  275. }
  276. if(!event.ctrlKey && key == keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
  277. if(!keyHeldDown) {
  278. $field
  279. .one('keyup' + eventNamespace, module.event.field.keyup)
  280. ;
  281. module.submit();
  282. module.debug('Enter pressed on input submitting form');
  283. }
  284. keyHeldDown = true;
  285. }
  286. },
  287. keyup: function() {
  288. keyHeldDown = false;
  289. },
  290. blur: function(event) {
  291. var
  292. $field = $(this),
  293. $fieldGroup = $field.closest($group),
  294. validationRules = module.get.validation($field)
  295. ;
  296. if( $fieldGroup.hasClass(className.error) ) {
  297. module.debug('Revalidating field', $field, validationRules);
  298. if(validationRules) {
  299. module.validate.field( validationRules );
  300. }
  301. }
  302. else if(settings.on == 'blur' || settings.on == 'change') {
  303. if(validationRules) {
  304. module.validate.field( validationRules );
  305. }
  306. }
  307. },
  308. change: function(event) {
  309. var
  310. $field = $(this),
  311. $fieldGroup = $field.closest($group),
  312. validationRules = module.get.validation($field)
  313. ;
  314. if(settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) ) {
  315. clearTimeout(module.timer);
  316. module.timer = setTimeout(function() {
  317. module.debug('Revalidating field', $field, module.get.validation($field));
  318. module.validate.field( validationRules );
  319. }, settings.delay);
  320. }
  321. }
  322. }
  323. },
  324. get: {
  325. ancillaryValue: function(rule) {
  326. if(!rule.type || (!rule.value && !module.is.bracketedRule(rule))) {
  327. return false;
  328. }
  329. return (rule.value !== undefined)
  330. ? rule.value
  331. : rule.type.match(settings.regExp.bracket)[1] + ''
  332. ;
  333. },
  334. ruleName: function(rule) {
  335. if( module.is.bracketedRule(rule) ) {
  336. return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], '');
  337. }
  338. return rule.type;
  339. },
  340. changeEvent: function(type, $input) {
  341. if(type == 'checkbox' || type == 'radio' || type == 'hidden' || $input.is('select')) {
  342. return 'change';
  343. }
  344. else {
  345. return module.get.inputEvent();
  346. }
  347. },
  348. inputEvent: function() {
  349. return (document.createElement('input').oninput !== undefined)
  350. ? 'input'
  351. : (document.createElement('input').onpropertychange !== undefined)
  352. ? 'propertychange'
  353. : 'keyup'
  354. ;
  355. },
  356. prompt: function(rule, field) {
  357. var
  358. ruleName = module.get.ruleName(rule),
  359. ancillary = module.get.ancillaryValue(rule),
  360. prompt = rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule,
  361. requiresValue = (prompt.search('{value}') !== -1),
  362. requiresName = (prompt.search('{name}') !== -1),
  363. $label,
  364. $field,
  365. name
  366. ;
  367. if(requiresName || requiresValue) {
  368. $field = module.get.field(field.identifier);
  369. }
  370. if(requiresValue) {
  371. prompt = prompt.replace('{value}', $field.val());
  372. }
  373. if(requiresName) {
  374. $label = $field.closest(selector.group).find('label').eq(0);
  375. name = ($label.length == 1)
  376. ? $label.text()
  377. : $field.prop('placeholder') || settings.text.unspecifiedField
  378. ;
  379. prompt = prompt.replace('{name}', name);
  380. }
  381. prompt = prompt.replace('{identifier}', field.identifier);
  382. prompt = prompt.replace('{ruleValue}', ancillary);
  383. if(!rule.prompt) {
  384. module.verbose('Using default validation prompt for type', prompt, ruleName);
  385. }
  386. return prompt;
  387. },
  388. settings: function() {
  389. if($.isPlainObject(parameters)) {
  390. var
  391. keys = Object.keys(parameters),
  392. isLegacySettings = (keys.length > 0)
  393. ? (parameters[keys[0]].identifier !== undefined && parameters[keys[0]].rules !== undefined)
  394. : false,
  395. ruleKeys
  396. ;
  397. if(isLegacySettings) {
  398. // 1.x (ducktyped)
  399. settings = $.extend(true, {}, $.fn.form.settings, legacyParameters);
  400. validation = $.extend({}, $.fn.form.settings.defaults, parameters);
  401. module.error(settings.error.oldSyntax, element);
  402. module.verbose('Extending settings from legacy parameters', validation, settings);
  403. }
  404. else {
  405. // 2.x
  406. if(parameters.fields) {
  407. ruleKeys = Object.keys(parameters.fields);
  408. if( typeof parameters.fields[ruleKeys[0]] == 'string' || $.isArray(parameters.fields[ruleKeys[0]]) ) {
  409. $.each(parameters.fields, function(name, rules) {
  410. if(typeof rules == 'string') {
  411. rules = [rules];
  412. }
  413. parameters.fields[name] = {
  414. rules: []
  415. };
  416. $.each(rules, function(index, rule) {
  417. parameters.fields[name].rules.push({ type: rule });
  418. });
  419. });
  420. }
  421. }
  422. settings = $.extend(true, {}, $.fn.form.settings, parameters);
  423. validation = $.extend({}, $.fn.form.settings.defaults, settings.fields);
  424. module.verbose('Extending settings', validation, settings);
  425. }
  426. }
  427. else {
  428. settings = $.fn.form.settings;
  429. validation = $.fn.form.settings.defaults;
  430. module.verbose('Using default form validation', validation, settings);
  431. }
  432. // shorthand
  433. namespace = settings.namespace;
  434. metadata = settings.metadata;
  435. selector = settings.selector;
  436. className = settings.className;
  437. error = settings.error;
  438. moduleNamespace = 'module-' + namespace;
  439. eventNamespace = '.' + namespace;
  440. // grab instance
  441. instance = $module.data(moduleNamespace);
  442. // refresh selector cache
  443. module.refresh();
  444. },
  445. field: function(identifier) {
  446. module.verbose('Finding field with identifier', identifier);
  447. if( $field.filter('#' + identifier).length > 0 ) {
  448. return $field.filter('#' + identifier);
  449. }
  450. else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
  451. return $field.filter('[name="' + identifier +'"]');
  452. }
  453. else if( $field.filter('[name="' + identifier +'[]"]').length > 0 ) {
  454. return $field.filter('[name="' + identifier +'[]"]');
  455. }
  456. else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
  457. return $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]');
  458. }
  459. return $('<input/>');
  460. },
  461. fields: function(fields) {
  462. var
  463. $fields = $()
  464. ;
  465. $.each(fields, function(index, name) {
  466. $fields = $fields.add( module.get.field(name) );
  467. });
  468. return $fields;
  469. },
  470. validation: function($field) {
  471. var
  472. fieldValidation,
  473. identifier
  474. ;
  475. if(!validation) {
  476. return false;
  477. }
  478. $.each(validation, function(fieldName, field) {
  479. identifier = field.identifier || fieldName;
  480. if( module.get.field(identifier)[0] == $field[0] ) {
  481. field.identifier = identifier;
  482. fieldValidation = field;
  483. }
  484. });
  485. return fieldValidation || false;
  486. },
  487. value: function (field) {
  488. var
  489. fields = [],
  490. results
  491. ;
  492. fields.push(field);
  493. results = module.get.values.call(element, fields);
  494. return results[field];
  495. },
  496. values: function (fields) {
  497. var
  498. $fields = $.isArray(fields)
  499. ? module.get.fields(fields)
  500. : $field,
  501. values = {}
  502. ;
  503. $fields.each(function(index, field) {
  504. var
  505. $field = $(field),
  506. type = $field.prop('type'),
  507. name = $field.prop('name'),
  508. value = $field.val(),
  509. isCheckbox = $field.is(selector.checkbox),
  510. isRadio = $field.is(selector.radio),
  511. isMultiple = (name.indexOf('[]') !== -1),
  512. isChecked = (isCheckbox)
  513. ? $field.is(':checked')
  514. : false
  515. ;
  516. if(name) {
  517. if(isMultiple) {
  518. name = name.replace('[]', '');
  519. if(!values[name]) {
  520. values[name] = [];
  521. }
  522. if(isCheckbox) {
  523. if(isChecked) {
  524. values[name].push(value || true);
  525. }
  526. else {
  527. values[name].push(false);
  528. }
  529. }
  530. else {
  531. values[name].push(value);
  532. }
  533. }
  534. else {
  535. if(isRadio) {
  536. if(isChecked) {
  537. values[name] = value;
  538. }
  539. }
  540. else if(isCheckbox) {
  541. if(isChecked) {
  542. values[name] = value || true;
  543. }
  544. else {
  545. values[name] = false;
  546. }
  547. }
  548. else {
  549. values[name] = value;
  550. }
  551. }
  552. }
  553. });
  554. return values;
  555. }
  556. },
  557. has: {
  558. field: function(identifier) {
  559. module.verbose('Checking for existence of a field with identifier', identifier);
  560. if(typeof identifier !== 'string') {
  561. module.error(error.identifier, identifier);
  562. }
  563. if( $field.filter('#' + identifier).length > 0 ) {
  564. return true;
  565. }
  566. else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
  567. return true;
  568. }
  569. else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
  570. return true;
  571. }
  572. return false;
  573. }
  574. },
  575. add: {
  576. prompt: function(identifier, errors) {
  577. var
  578. $field = module.get.field(identifier),
  579. $fieldGroup = $field.closest($group),
  580. $prompt = $fieldGroup.children(selector.prompt),
  581. promptExists = ($prompt.length !== 0)
  582. ;
  583. errors = (typeof errors == 'string')
  584. ? [errors]
  585. : errors
  586. ;
  587. module.verbose('Adding field error state', identifier);
  588. $fieldGroup
  589. .addClass(className.error)
  590. ;
  591. if(settings.inline) {
  592. if(!promptExists) {
  593. $prompt = settings.templates.prompt(errors);
  594. $prompt
  595. .appendTo($fieldGroup)
  596. ;
  597. }
  598. $prompt
  599. .html(errors[0])
  600. ;
  601. if(!promptExists) {
  602. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  603. module.verbose('Displaying error with css transition', settings.transition);
  604. $prompt.transition(settings.transition + ' in', settings.duration);
  605. }
  606. else {
  607. module.verbose('Displaying error with fallback javascript animation');
  608. $prompt
  609. .fadeIn(settings.duration)
  610. ;
  611. }
  612. }
  613. else {
  614. module.verbose('Inline errors are disabled, no inline error added', identifier);
  615. }
  616. }
  617. },
  618. errors: function(errors) {
  619. module.debug('Adding form error messages', errors);
  620. module.set.error();
  621. $message
  622. .html( settings.templates.error(errors) )
  623. ;
  624. }
  625. },
  626. remove: {
  627. prompt: function(identifier) {
  628. var
  629. $field = module.get.field(identifier),
  630. $fieldGroup = $field.closest($group),
  631. $prompt = $fieldGroup.children(selector.prompt)
  632. ;
  633. $fieldGroup
  634. .removeClass(className.error)
  635. ;
  636. if(settings.inline && $prompt.is(':visible')) {
  637. module.verbose('Removing prompt for field', identifier);
  638. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  639. $prompt.transition(settings.transition + ' out', settings.duration, function() {
  640. $prompt.remove();
  641. });
  642. }
  643. else {
  644. $prompt
  645. .fadeOut(settings.duration, function(){
  646. $prompt.remove();
  647. })
  648. ;
  649. }
  650. }
  651. }
  652. },
  653. set: {
  654. success: function() {
  655. $module
  656. .removeClass(className.error)
  657. .addClass(className.success)
  658. ;
  659. },
  660. defaults: function () {
  661. $field
  662. .each(function () {
  663. var
  664. $field = $(this),
  665. isCheckbox = ($field.filter(selector.checkbox).length > 0),
  666. value = (isCheckbox)
  667. ? $field.is(':checked')
  668. : $field.val()
  669. ;
  670. $field.data(metadata.defaultValue, value);
  671. })
  672. ;
  673. },
  674. error: function() {
  675. $module
  676. .removeClass(className.success)
  677. .addClass(className.error)
  678. ;
  679. },
  680. value: function (field, value) {
  681. var
  682. fields = {}
  683. ;
  684. fields[field] = value;
  685. return module.set.values.call(element, fields);
  686. },
  687. values: function (fields) {
  688. if($.isEmptyObject(fields)) {
  689. return;
  690. }
  691. $.each(fields, function(key, value) {
  692. var
  693. $field = module.get.field(key),
  694. $element = $field.parent(),
  695. isMultiple = $.isArray(value),
  696. isCheckbox = $element.is(selector.uiCheckbox),
  697. isDropdown = $element.is(selector.uiDropdown),
  698. isRadio = ($field.is(selector.radio) && isCheckbox),
  699. fieldExists = ($field.length > 0),
  700. $multipleField
  701. ;
  702. if(fieldExists) {
  703. if(isMultiple && isCheckbox) {
  704. module.verbose('Selecting multiple', value, $field);
  705. $element.checkbox('uncheck');
  706. $.each(value, function(index, value) {
  707. $multipleField = $field.filter('[value="' + value + '"]');
  708. $element = $multipleField.parent();
  709. if($multipleField.length > 0) {
  710. $element.checkbox('check');
  711. }
  712. });
  713. }
  714. else if(isRadio) {
  715. module.verbose('Selecting radio value', value, $field);
  716. $field.filter('[value="' + value + '"]')
  717. .parent(selector.uiCheckbox)
  718. .checkbox('check')
  719. ;
  720. }
  721. else if(isCheckbox) {
  722. module.verbose('Setting checkbox value', value, $element);
  723. if(value === true) {
  724. $element.checkbox('check');
  725. }
  726. else {
  727. $element.checkbox('uncheck');
  728. }
  729. }
  730. else if(isDropdown) {
  731. module.verbose('Setting dropdown value', value, $element);
  732. $element.dropdown('set selected', value);
  733. }
  734. else {
  735. module.verbose('Setting field value', value, $field);
  736. $field.val(value);
  737. }
  738. }
  739. });
  740. }
  741. },
  742. validate: {
  743. form: function(event, ignoreCallbacks) {
  744. var
  745. values = module.get.values(),
  746. apiRequest
  747. ;
  748. // input keydown event will fire submit repeatedly by browser default
  749. if(keyHeldDown) {
  750. return false;
  751. }
  752. // reset errors
  753. formErrors = [];
  754. if( module.is.valid() ) {
  755. module.debug('Form has no validation errors, submitting');
  756. module.set.success();
  757. if(ignoreCallbacks !== true) {
  758. return settings.onSuccess.call(element, event, values);
  759. }
  760. }
  761. else {
  762. module.debug('Form has errors');
  763. module.set.error();
  764. if(!settings.inline) {
  765. module.add.errors(formErrors);
  766. }
  767. // prevent ajax submit
  768. if($module.data('moduleApi') !== undefined) {
  769. event.stopImmediatePropagation();
  770. }
  771. if(ignoreCallbacks !== true) {
  772. return settings.onFailure.call(element, formErrors, values);
  773. }
  774. }
  775. },
  776. // takes a validation object and returns whether field passes validation
  777. field: function(field, fieldName) {
  778. var
  779. identifier = field.identifier || fieldName,
  780. $field = module.get.field(identifier),
  781. $dependsField = (field.depends)
  782. ? module.get.field(field.depends)
  783. : false,
  784. fieldValid = true,
  785. fieldErrors = []
  786. ;
  787. if(!field.identifier) {
  788. module.debug('Using field name as identifier', identifier);
  789. field.identifier = identifier;
  790. }
  791. if($field.prop('disabled')) {
  792. module.debug('Field is disabled. Skipping', identifier);
  793. fieldValid = true;
  794. }
  795. else if(field.optional && module.is.blank($field)){
  796. module.debug('Field is optional and blank. Skipping', identifier);
  797. fieldValid = true;
  798. }
  799. else if(field.depends && module.is.empty($dependsField)) {
  800. module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField);
  801. fieldValid = true;
  802. }
  803. else if(field.rules !== undefined) {
  804. $.each(field.rules, function(index, rule) {
  805. if( module.has.field(identifier) && !( module.validate.rule(field, rule) ) ) {
  806. module.debug('Field is invalid', identifier, rule.type);
  807. fieldErrors.push(module.get.prompt(rule, field));
  808. fieldValid = false;
  809. }
  810. });
  811. }
  812. if(fieldValid) {
  813. module.remove.prompt(identifier, fieldErrors);
  814. settings.onValid.call($field);
  815. }
  816. else {
  817. formErrors = formErrors.concat(fieldErrors);
  818. module.add.prompt(identifier, fieldErrors);
  819. settings.onInvalid.call($field, fieldErrors);
  820. return false;
  821. }
  822. return true;
  823. },
  824. // takes validation rule and returns whether field passes rule
  825. rule: function(field, rule) {
  826. var
  827. $field = module.get.field(field.identifier),
  828. type = rule.type,
  829. value = $field.val(),
  830. isValid = true,
  831. ancillary = module.get.ancillaryValue(rule),
  832. ruleName = module.get.ruleName(rule),
  833. ruleFunction = settings.rules[ruleName]
  834. ;
  835. if( !$.isFunction(ruleFunction) ) {
  836. module.error(error.noRule, ruleName);
  837. return;
  838. }
  839. // cast to string avoiding encoding special values
  840. value = (value === undefined || value === '' || value === null)
  841. ? ''
  842. : $.trim(value + '')
  843. ;
  844. return ruleFunction.call($field, value, ancillary);
  845. }
  846. },
  847. setting: function(name, value) {
  848. if( $.isPlainObject(name) ) {
  849. $.extend(true, settings, name);
  850. }
  851. else if(value !== undefined) {
  852. settings[name] = value;
  853. }
  854. else {
  855. return settings[name];
  856. }
  857. },
  858. internal: function(name, value) {
  859. if( $.isPlainObject(name) ) {
  860. $.extend(true, module, name);
  861. }
  862. else if(value !== undefined) {
  863. module[name] = value;
  864. }
  865. else {
  866. return module[name];
  867. }
  868. },
  869. debug: function() {
  870. if(!settings.silent && settings.debug) {
  871. if(settings.performance) {
  872. module.performance.log(arguments);
  873. }
  874. else {
  875. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  876. module.debug.apply(console, arguments);
  877. }
  878. }
  879. },
  880. verbose: function() {
  881. if(!settings.silent && settings.verbose && settings.debug) {
  882. if(settings.performance) {
  883. module.performance.log(arguments);
  884. }
  885. else {
  886. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  887. module.verbose.apply(console, arguments);
  888. }
  889. }
  890. },
  891. error: function() {
  892. if(!settings.silent) {
  893. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  894. module.error.apply(console, arguments);
  895. }
  896. },
  897. performance: {
  898. log: function(message) {
  899. var
  900. currentTime,
  901. executionTime,
  902. previousTime
  903. ;
  904. if(settings.performance) {
  905. currentTime = new Date().getTime();
  906. previousTime = time || currentTime;
  907. executionTime = currentTime - previousTime;
  908. time = currentTime;
  909. performance.push({
  910. 'Name' : message[0],
  911. 'Arguments' : [].slice.call(message, 1) || '',
  912. 'Element' : element,
  913. 'Execution Time' : executionTime
  914. });
  915. }
  916. clearTimeout(module.performance.timer);
  917. module.performance.timer = setTimeout(module.performance.display, 500);
  918. },
  919. display: function() {
  920. var
  921. title = settings.name + ':',
  922. totalTime = 0
  923. ;
  924. time = false;
  925. clearTimeout(module.performance.timer);
  926. $.each(performance, function(index, data) {
  927. totalTime += data['Execution Time'];
  928. });
  929. title += ' ' + totalTime + 'ms';
  930. if(moduleSelector) {
  931. title += ' \'' + moduleSelector + '\'';
  932. }
  933. if($allModules.length > 1) {
  934. title += ' ' + '(' + $allModules.length + ')';
  935. }
  936. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  937. console.groupCollapsed(title);
  938. if(console.table) {
  939. console.table(performance);
  940. }
  941. else {
  942. $.each(performance, function(index, data) {
  943. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  944. });
  945. }
  946. console.groupEnd();
  947. }
  948. performance = [];
  949. }
  950. },
  951. invoke: function(query, passedArguments, context) {
  952. var
  953. object = instance,
  954. maxDepth,
  955. found,
  956. response
  957. ;
  958. passedArguments = passedArguments || queryArguments;
  959. context = element || context;
  960. if(typeof query == 'string' && object !== undefined) {
  961. query = query.split(/[\. ]/);
  962. maxDepth = query.length - 1;
  963. $.each(query, function(depth, value) {
  964. var camelCaseValue = (depth != maxDepth)
  965. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  966. : query
  967. ;
  968. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  969. object = object[camelCaseValue];
  970. }
  971. else if( object[camelCaseValue] !== undefined ) {
  972. found = object[camelCaseValue];
  973. return false;
  974. }
  975. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  976. object = object[value];
  977. }
  978. else if( object[value] !== undefined ) {
  979. found = object[value];
  980. return false;
  981. }
  982. else {
  983. return false;
  984. }
  985. });
  986. }
  987. if( $.isFunction( found ) ) {
  988. response = found.apply(context, passedArguments);
  989. }
  990. else if(found !== undefined) {
  991. response = found;
  992. }
  993. if($.isArray(returnedValue)) {
  994. returnedValue.push(response);
  995. }
  996. else if(returnedValue !== undefined) {
  997. returnedValue = [returnedValue, response];
  998. }
  999. else if(response !== undefined) {
  1000. returnedValue = response;
  1001. }
  1002. return found;
  1003. }
  1004. };
  1005. module.initialize();
  1006. })
  1007. ;
  1008. return (returnedValue !== undefined)
  1009. ? returnedValue
  1010. : this
  1011. ;
  1012. };
  1013. $.fn.form.settings = {
  1014. name : 'Form',
  1015. namespace : 'form',
  1016. debug : false,
  1017. verbose : false,
  1018. performance : true,
  1019. fields : false,
  1020. keyboardShortcuts : true,
  1021. on : 'submit',
  1022. inline : false,
  1023. delay : 200,
  1024. revalidate : true,
  1025. transition : 'scale',
  1026. duration : 200,
  1027. onValid : function() {},
  1028. onInvalid : function() {},
  1029. onSuccess : function() { return true; },
  1030. onFailure : function() { return false; },
  1031. metadata : {
  1032. defaultValue : 'default',
  1033. validate : 'validate'
  1034. },
  1035. regExp: {
  1036. bracket : /\[(.*)\]/i,
  1037. decimal : /^\d*(\.)\d+/,
  1038. email : /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
  1039. escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
  1040. flags : /^\/(.*)\/(.*)?/,
  1041. integer : /^\-?\d+$/,
  1042. number : /^\-?\d*(\.\d+)?$/,
  1043. url : /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i
  1044. },
  1045. text: {
  1046. unspecifiedRule : 'Please enter a valid value',
  1047. unspecifiedField : 'This field'
  1048. },
  1049. prompt: {
  1050. empty : '{name} must have a value',
  1051. checked : '{name} must be checked',
  1052. email : '{name} must be a valid e-mail',
  1053. url : '{name} must be a valid url',
  1054. regExp : '{name} is not formatted correctly',
  1055. integer : '{name} must be an integer',
  1056. decimal : '{name} must be a decimal number',
  1057. number : '{name} must be set to a number',
  1058. is : '{name} must be "{ruleValue}"',
  1059. isExactly : '{name} must be exactly "{ruleValue}"',
  1060. not : '{name} cannot be set to "{ruleValue}"',
  1061. notExactly : '{name} cannot be set to exactly "{ruleValue}"',
  1062. contain : '{name} cannot contain "{ruleValue}"',
  1063. containExactly : '{name} cannot contain exactly "{ruleValue}"',
  1064. doesntContain : '{name} must contain "{ruleValue}"',
  1065. doesntContainExactly : '{name} must contain exactly "{ruleValue}"',
  1066. minLength : '{name} must be at least {ruleValue} characters',
  1067. length : '{name} must be at least {ruleValue} characters',
  1068. exactLength : '{name} must be exactly {ruleValue} characters',
  1069. maxLength : '{name} cannot be longer than {ruleValue} characters',
  1070. match : '{name} must match {ruleValue} field',
  1071. different : '{name} must have a different value than {ruleValue} field',
  1072. creditCard : '{name} must be a valid credit card number',
  1073. minCount : '{name} must have at least {ruleValue} choices',
  1074. exactCount : '{name} must have exactly {ruleValue} choices',
  1075. maxCount : '{name} must have {ruleValue} or less choices'
  1076. },
  1077. selector : {
  1078. checkbox : 'input[type="checkbox"], input[type="radio"]',
  1079. clear : '.clear',
  1080. field : 'input, textarea, select',
  1081. group : '.field',
  1082. input : 'input',
  1083. message : '.error.message',
  1084. prompt : '.prompt.label',
  1085. radio : 'input[type="radio"]',
  1086. reset : '.reset:not([type="reset"])',
  1087. submit : '.submit:not([type="submit"])',
  1088. uiCheckbox : '.ui.checkbox',
  1089. uiDropdown : '.ui.dropdown'
  1090. },
  1091. className : {
  1092. error : 'error',
  1093. label : 'ui prompt label',
  1094. pressed : 'down',
  1095. success : 'success'
  1096. },
  1097. error: {
  1098. identifier : 'You must specify a string identifier for each field',
  1099. method : 'The method you called is not defined.',
  1100. noRule : 'There is no rule matching the one you specified',
  1101. oldSyntax : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.'
  1102. },
  1103. templates: {
  1104. // template that produces error message
  1105. error: function(errors) {
  1106. var
  1107. html = '<ul class="list">'
  1108. ;
  1109. $.each(errors, function(index, value) {
  1110. html += '<li>' + value + '</li>';
  1111. });
  1112. html += '</ul>';
  1113. return $(html);
  1114. },
  1115. // template that produces label
  1116. prompt: function(errors) {
  1117. return $('<div/>')
  1118. .addClass('ui basic red pointing prompt label')
  1119. .html(errors[0])
  1120. ;
  1121. }
  1122. },
  1123. rules: {
  1124. // is not empty or blank string
  1125. empty: function(value) {
  1126. return !(value === undefined || '' === value || $.isArray(value) && value.length === 0);
  1127. },
  1128. // checkbox checked
  1129. checked: function() {
  1130. return ($(this).filter(':checked').length > 0);
  1131. },
  1132. // is most likely an email
  1133. email: function(value){
  1134. return $.fn.form.settings.regExp.email.test(value);
  1135. },
  1136. // value is most likely url
  1137. url: function(value) {
  1138. return $.fn.form.settings.regExp.url.test(value);
  1139. },
  1140. // matches specified regExp
  1141. regExp: function(value, regExp) {
  1142. if(regExp instanceof RegExp) {
  1143. return value.match(regExp);
  1144. }
  1145. var
  1146. regExpParts = regExp.match($.fn.form.settings.regExp.flags),
  1147. flags
  1148. ;
  1149. // regular expression specified as /baz/gi (flags)
  1150. if(regExpParts) {
  1151. regExp = (regExpParts.length >= 2)
  1152. ? regExpParts[1]
  1153. : regExp
  1154. ;
  1155. flags = (regExpParts.length >= 3)
  1156. ? regExpParts[2]
  1157. : ''
  1158. ;
  1159. }
  1160. return value.match( new RegExp(regExp, flags) );
  1161. },
  1162. // is valid integer or matches range
  1163. integer: function(value, range) {
  1164. var
  1165. intRegExp = $.fn.form.settings.regExp.integer,
  1166. min,
  1167. max,
  1168. parts
  1169. ;
  1170. if( !range || ['', '..'].indexOf(range) !== -1) {
  1171. // do nothing
  1172. }
  1173. else if(range.indexOf('..') == -1) {
  1174. if(intRegExp.test(range)) {
  1175. min = max = range - 0;
  1176. }
  1177. }
  1178. else {
  1179. parts = range.split('..', 2);
  1180. if(intRegExp.test(parts[0])) {
  1181. min = parts[0] - 0;
  1182. }
  1183. if(intRegExp.test(parts[1])) {
  1184. max = parts[1] - 0;
  1185. }
  1186. }
  1187. return (
  1188. intRegExp.test(value) &&
  1189. (min === undefined || value >= min) &&
  1190. (max === undefined || value <= max)
  1191. );
  1192. },
  1193. // is valid number (with decimal)
  1194. decimal: function(value) {
  1195. return $.fn.form.settings.regExp.decimal.test(value);
  1196. },
  1197. // is valid number
  1198. number: function(value) {
  1199. return $.fn.form.settings.regExp.number.test(value);
  1200. },
  1201. // is value (case insensitive)
  1202. is: function(value, text) {
  1203. text = (typeof text == 'string')
  1204. ? text.toLowerCase()
  1205. : text
  1206. ;
  1207. value = (typeof value == 'string')
  1208. ? value.toLowerCase()
  1209. : value
  1210. ;
  1211. return (value == text);
  1212. },
  1213. // is value
  1214. isExactly: function(value, text) {
  1215. return (value == text);
  1216. },
  1217. // value is not another value (case insensitive)
  1218. not: function(value, notValue) {
  1219. value = (typeof value == 'string')
  1220. ? value.toLowerCase()
  1221. : value
  1222. ;
  1223. notValue = (typeof notValue == 'string')
  1224. ? notValue.toLowerCase()
  1225. : notValue
  1226. ;
  1227. return (value != notValue);
  1228. },
  1229. // value is not another value (case sensitive)
  1230. notExactly: function(value, notValue) {
  1231. return (value != notValue);
  1232. },
  1233. // value contains text (insensitive)
  1234. contains: function(value, text) {
  1235. // escape regex characters
  1236. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  1237. return (value.search( new RegExp(text, 'i') ) !== -1);
  1238. },
  1239. // value contains text (case sensitive)
  1240. containsExactly: function(value, text) {
  1241. // escape regex characters
  1242. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  1243. return (value.search( new RegExp(text) ) !== -1);
  1244. },
  1245. // value contains text (insensitive)
  1246. doesntContain: function(value, text) {
  1247. // escape regex characters
  1248. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  1249. return (value.search( new RegExp(text, 'i') ) === -1);
  1250. },
  1251. // value contains text (case sensitive)
  1252. doesntContainExactly: function(value, text) {
  1253. // escape regex characters
  1254. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  1255. return (value.search( new RegExp(text) ) === -1);
  1256. },
  1257. // is at least string length
  1258. minLength: function(value, requiredLength) {
  1259. return (value !== undefined)
  1260. ? (value.length >= requiredLength)
  1261. : false
  1262. ;
  1263. },
  1264. // see rls notes for 2.0.6 (this is a duplicate of minLength)
  1265. length: function(value, requiredLength) {
  1266. return (value !== undefined)
  1267. ? (value.length >= requiredLength)
  1268. : false
  1269. ;
  1270. },
  1271. // is exactly length
  1272. exactLength: function(value, requiredLength) {
  1273. return (value !== undefined)
  1274. ? (value.length == requiredLength)
  1275. : false
  1276. ;
  1277. },
  1278. // is less than length
  1279. maxLength: function(value, maxLength) {
  1280. return (value !== undefined)
  1281. ? (value.length <= maxLength)
  1282. : false
  1283. ;
  1284. },
  1285. // matches another field
  1286. match: function(value, identifier) {
  1287. var
  1288. $form = $(this),
  1289. matchingValue
  1290. ;
  1291. if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
  1292. matchingValue = $('[data-validate="'+ identifier +'"]').val();
  1293. }
  1294. else if($('#' + identifier).length > 0) {
  1295. matchingValue = $('#' + identifier).val();
  1296. }
  1297. else if($('[name="' + identifier +'"]').length > 0) {
  1298. matchingValue = $('[name="' + identifier + '"]').val();
  1299. }
  1300. else if( $('[name="' + identifier +'[]"]').length > 0 ) {
  1301. matchingValue = $('[name="' + identifier +'[]"]');
  1302. }
  1303. return (matchingValue !== undefined)
  1304. ? ( value.toString() == matchingValue.toString() )
  1305. : false
  1306. ;
  1307. },
  1308. // different than another field
  1309. different: function(value, identifier) {
  1310. // use either id or name of field
  1311. var
  1312. $form = $(this),
  1313. matchingValue
  1314. ;
  1315. if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
  1316. matchingValue = $('[data-validate="'+ identifier +'"]').val();
  1317. }
  1318. else if($('#' + identifier).length > 0) {
  1319. matchingValue = $('#' + identifier).val();
  1320. }
  1321. else if($('[name="' + identifier +'"]').length > 0) {
  1322. matchingValue = $('[name="' + identifier + '"]').val();
  1323. }
  1324. else if( $('[name="' + identifier +'[]"]').length > 0 ) {
  1325. matchingValue = $('[name="' + identifier +'[]"]');
  1326. }
  1327. return (matchingValue !== undefined)
  1328. ? ( value.toString() !== matchingValue.toString() )
  1329. : false
  1330. ;
  1331. },
  1332. creditCard: function(cardNumber, cardTypes) {
  1333. var
  1334. cards = {
  1335. visa: {
  1336. pattern : /^4/,
  1337. length : [16]
  1338. },
  1339. amex: {
  1340. pattern : /^3[47]/,
  1341. length : [15]
  1342. },
  1343. mastercard: {
  1344. pattern : /^5[1-5]/,
  1345. length : [16]
  1346. },
  1347. discover: {
  1348. pattern : /^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/,
  1349. length : [16]
  1350. },
  1351. unionPay: {
  1352. pattern : /^(62|88)/,
  1353. length : [16, 17, 18, 19]
  1354. },
  1355. jcb: {
  1356. pattern : /^35(2[89]|[3-8][0-9])/,
  1357. length : [16]
  1358. },
  1359. maestro: {
  1360. pattern : /^(5018|5020|5038|6304|6759|676[1-3])/,
  1361. length : [12, 13, 14, 15, 16, 17, 18, 19]
  1362. },
  1363. dinersClub: {
  1364. pattern : /^(30[0-5]|^36)/,
  1365. length : [14]
  1366. },
  1367. laser: {
  1368. pattern : /^(6304|670[69]|6771)/,
  1369. length : [16, 17, 18, 19]
  1370. },
  1371. visaElectron: {
  1372. pattern : /^(4026|417500|4508|4844|491(3|7))/,
  1373. length : [16]
  1374. }
  1375. },
  1376. valid = {},
  1377. validCard = false,
  1378. requiredTypes = (typeof cardTypes == 'string')
  1379. ? cardTypes.split(',')
  1380. : false,
  1381. unionPay,
  1382. validation
  1383. ;
  1384. if(typeof cardNumber !== 'string' || cardNumber.length === 0) {
  1385. return;
  1386. }
  1387. // verify card types
  1388. if(requiredTypes) {
  1389. $.each(requiredTypes, function(index, type){
  1390. // verify each card type
  1391. validation = cards[type];
  1392. if(validation) {
  1393. valid = {
  1394. length : ($.inArray(cardNumber.length, validation.length) !== -1),
  1395. pattern : (cardNumber.search(validation.pattern) !== -1)
  1396. };
  1397. if(valid.length && valid.pattern) {
  1398. validCard = true;
  1399. }
  1400. }
  1401. });
  1402. if(!validCard) {
  1403. return false;
  1404. }
  1405. }
  1406. // skip luhn for UnionPay
  1407. unionPay = {
  1408. number : ($.inArray(cardNumber.length, cards.unionPay.length) !== -1),
  1409. pattern : (cardNumber.search(cards.unionPay.pattern) !== -1)
  1410. };
  1411. if(unionPay.number && unionPay.pattern) {
  1412. return true;
  1413. }
  1414. // verify luhn, adapted from <https://gist.github.com/2134376>
  1415. var
  1416. length = cardNumber.length,
  1417. multiple = 0,
  1418. producedValue = [
  1419. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  1420. [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
  1421. ],
  1422. sum = 0
  1423. ;
  1424. while (length--) {
  1425. sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)];
  1426. multiple ^= 1;
  1427. }
  1428. return (sum % 10 === 0 && sum > 0);
  1429. },
  1430. minCount: function(value, minCount) {
  1431. if(minCount == 0) {
  1432. return true;
  1433. }
  1434. if(minCount == 1) {
  1435. return (value !== '');
  1436. }
  1437. return (value.split(',').length >= minCount);
  1438. },
  1439. exactCount: function(value, exactCount) {
  1440. if(exactCount == 0) {
  1441. return (value === '');
  1442. }
  1443. if(exactCount == 1) {
  1444. return (value !== '' && value.search(',') === -1);
  1445. }
  1446. return (value.split(',').length == exactCount);
  1447. },
  1448. maxCount: function(value, maxCount) {
  1449. if(maxCount == 0) {
  1450. return false;
  1451. }
  1452. if(maxCount == 1) {
  1453. return (value.search(',') === -1);
  1454. }
  1455. return (value.split(',').length <= maxCount);
  1456. }
  1457. }
  1458. };
  1459. })( jQuery, window, document );