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.

1552 lines
50 KiB

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