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

10 years ago
9 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
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
8 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 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
9 years ago
8 years ago
9 years ago
10 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
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
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
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 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
10 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
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
10 years ago
10 years ago
10 years ago
10 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
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
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
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 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
10 years ago
10 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
10 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
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
10 years ago
10 years ago
10 years ago
10 years ago
10 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 );