You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1553 lines
50 KiB

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