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.

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