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.

1515 lines
48 KiB

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