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.

1300 lines
41 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
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
10 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
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
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
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
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
10 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
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
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
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
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
10 years ago
  1. /*!
  2. * # Semantic UI 2.0.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. valid: function() {
  205. var
  206. allValid = true
  207. ;
  208. module.verbose('Checking if form is valid');
  209. $.each(validation, function(fieldName, field) {
  210. if( !( module.validate.field(field) ) ) {
  211. allValid = false;
  212. }
  213. });
  214. return allValid;
  215. }
  216. },
  217. removeEvents: function() {
  218. $module
  219. .off(eventNamespace)
  220. ;
  221. $field
  222. .off(eventNamespace)
  223. ;
  224. $submit
  225. .off(eventNamespace)
  226. ;
  227. $field
  228. .off(eventNamespace)
  229. ;
  230. },
  231. event: {
  232. field: {
  233. keydown: function(event) {
  234. var
  235. $field = $(this),
  236. key = event.which,
  237. keyCode = {
  238. enter : 13,
  239. escape : 27
  240. }
  241. ;
  242. if( key == keyCode.escape) {
  243. module.verbose('Escape key pressed blurring field');
  244. $field
  245. .blur()
  246. ;
  247. }
  248. if(!event.ctrlKey && key == keyCode.enter && $field.is(selector.input) && $field.not(selector.checkbox).length > 0 ) {
  249. if(!keyHeldDown) {
  250. $field
  251. .one('keyup' + eventNamespace, module.event.field.keyup)
  252. ;
  253. module.submit();
  254. module.debug('Enter pressed on input submitting form');
  255. }
  256. keyHeldDown = true;
  257. }
  258. },
  259. keyup: function() {
  260. keyHeldDown = false;
  261. },
  262. blur: function() {
  263. var
  264. $field = $(this),
  265. $fieldGroup = $field.closest($group),
  266. validationRules = module.get.validation($field)
  267. ;
  268. if( $fieldGroup.hasClass(className.error) ) {
  269. module.debug('Revalidating field', $field, validationRules);
  270. module.validate.field( validationRules );
  271. }
  272. else if(settings.on == 'blur' || settings.on == 'change') {
  273. module.validate.field( validationRules );
  274. }
  275. },
  276. change: function() {
  277. var
  278. $field = $(this),
  279. $fieldGroup = $field.closest($group)
  280. ;
  281. if(settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) ) {
  282. clearTimeout(module.timer);
  283. module.timer = setTimeout(function() {
  284. module.debug('Revalidating field', $field, module.get.validation($field));
  285. module.validate.field( module.get.validation($field) );
  286. }, settings.delay);
  287. }
  288. }
  289. }
  290. },
  291. get: {
  292. changeEvent: function(type, $input) {
  293. if(type == 'checkbox' || type == 'radio' || type == 'hidden' || $input.is('select')) {
  294. return 'change';
  295. }
  296. else {
  297. return module.get.inputEvent();
  298. }
  299. },
  300. inputEvent: function() {
  301. return (document.createElement('input').oninput !== undefined)
  302. ? 'input'
  303. : (document.createElement('input').onpropertychange !== undefined)
  304. ? 'propertychange'
  305. : 'keyup'
  306. ;
  307. },
  308. settings: function() {
  309. var
  310. firstProperty
  311. ;
  312. if($.isPlainObject(parameters)) {
  313. var
  314. keys = Object.keys(parameters),
  315. isLegacySettings = (keys.length > 0)
  316. ? (parameters[keys[0]].identifier !== undefined && parameters[keys[0]].rules !== undefined)
  317. : false
  318. ;
  319. if(isLegacySettings) {
  320. // 1.x (ducktyped)
  321. settings = $.extend(true, {}, $.fn.form.settings, legacyParameters);
  322. validation = $.extend({}, $.fn.form.settings.defaults, parameters);
  323. module.error(settings.error.oldSyntax, element);
  324. module.verbose('Extending settings from legacy parameters', validation, settings);
  325. }
  326. else {
  327. // 2.x
  328. settings = $.extend(true, {}, $.fn.form.settings, parameters);
  329. validation = $.extend({}, $.fn.form.settings.defaults, settings.fields);
  330. module.verbose('Extending settings', validation, settings);
  331. }
  332. }
  333. else {
  334. settings = $.fn.form.settings;
  335. validation = $.fn.form.settings.defaults;
  336. module.verbose('Using default form validation', validation, settings);
  337. }
  338. // shorthand
  339. namespace = settings.namespace;
  340. metadata = settings.metadata;
  341. selector = settings.selector;
  342. className = settings.className;
  343. error = settings.error;
  344. moduleNamespace = 'module-' + namespace;
  345. eventNamespace = '.' + namespace;
  346. // grab instance
  347. instance = $module.data(moduleNamespace);
  348. // refresh selector cache
  349. module.refresh();
  350. },
  351. field: function(identifier) {
  352. module.verbose('Finding field with identifier', identifier);
  353. if( $field.filter('#' + identifier).length > 0 ) {
  354. return $field.filter('#' + identifier);
  355. }
  356. else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
  357. return $field.filter('[name="' + identifier +'"]');
  358. }
  359. else if( $field.filter('[name="' + identifier +'[]"]').length > 0 ) {
  360. return $field.filter('[name="' + identifier +'[]"]');
  361. }
  362. else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
  363. return $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]');
  364. }
  365. return $('<input/>');
  366. },
  367. fields: function(fields) {
  368. var
  369. $fields = $()
  370. ;
  371. $.each(fields, function(index, name) {
  372. $fields = $fields.add( module.get.field(name) );
  373. });
  374. return $fields;
  375. },
  376. validation: function($field) {
  377. var
  378. rules
  379. ;
  380. if(!validation) {
  381. return false;
  382. }
  383. $.each(validation, function(fieldName, field) {
  384. if( module.get.field(field.identifier)[0] == $field[0] ) {
  385. rules = field;
  386. }
  387. });
  388. return rules || false;
  389. },
  390. value: function (field) {
  391. var
  392. fields = [],
  393. results
  394. ;
  395. fields.push(field);
  396. results = module.get.values.call(element, fields);
  397. return results[field];
  398. },
  399. values: function (fields) {
  400. var
  401. $fields = $.isArray(fields)
  402. ? module.get.fields(fields)
  403. : $field,
  404. values = {}
  405. ;
  406. $fields.each(function(index, field) {
  407. var
  408. $field = $(field),
  409. type = $field.prop('type'),
  410. name = $field.prop('name'),
  411. value = $field.val(),
  412. isCheckbox = $field.is(selector.checkbox),
  413. isRadio = $field.is(selector.radio),
  414. isMultiple = (name.indexOf('[]') !== -1),
  415. isChecked = (isCheckbox)
  416. ? $field.is(':checked')
  417. : false
  418. ;
  419. if(name) {
  420. if(isMultiple) {
  421. name = name.replace('[]', '');
  422. if(!values[name]) {
  423. values[name] = [];
  424. }
  425. if(isCheckbox) {
  426. if(isChecked) {
  427. values[name].push(value);
  428. }
  429. else {
  430. module.debug('Omitted unchecked checkbox', $field);
  431. return true;
  432. }
  433. }
  434. else {
  435. values[name].push(value);
  436. }
  437. }
  438. else {
  439. if(isRadio) {
  440. if(isChecked) {
  441. values[name] = value;
  442. }
  443. }
  444. else if(isCheckbox) {
  445. if(isChecked) {
  446. values[name] = true;
  447. }
  448. else {
  449. module.debug('Omitted unchecked checkbox', $field);
  450. return true;
  451. }
  452. }
  453. else {
  454. values[name] = value;
  455. }
  456. }
  457. }
  458. });
  459. return values;
  460. }
  461. },
  462. has: {
  463. field: function(identifier) {
  464. module.verbose('Checking for existence of a field with identifier', identifier);
  465. if(typeof identifier !== 'string') {
  466. module.error(error.identifier, identifier);
  467. }
  468. if( $field.filter('#' + identifier).length > 0 ) {
  469. return true;
  470. }
  471. else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
  472. return true;
  473. }
  474. else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
  475. return true;
  476. }
  477. return false;
  478. }
  479. },
  480. add: {
  481. prompt: function(identifier, errors) {
  482. var
  483. $field = module.get.field(identifier),
  484. $fieldGroup = $field.closest($group),
  485. $prompt = $fieldGroup.children(selector.prompt),
  486. promptExists = ($prompt.length !== 0)
  487. ;
  488. errors = (typeof errors == 'string')
  489. ? [errors]
  490. : errors
  491. ;
  492. module.verbose('Adding field error state', identifier);
  493. $fieldGroup
  494. .addClass(className.error)
  495. ;
  496. if(settings.inline) {
  497. if(!promptExists) {
  498. $prompt = settings.templates.prompt(errors);
  499. $prompt
  500. .appendTo($fieldGroup)
  501. ;
  502. }
  503. $prompt
  504. .html(errors[0])
  505. ;
  506. if(!promptExists) {
  507. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  508. module.verbose('Displaying error with css transition', settings.transition);
  509. $prompt.transition(settings.transition + ' in', settings.duration);
  510. }
  511. else {
  512. module.verbose('Displaying error with fallback javascript animation');
  513. $prompt
  514. .fadeIn(settings.duration)
  515. ;
  516. }
  517. }
  518. else {
  519. module.verbose('Inline errors are disabled, no inline error added', identifier);
  520. }
  521. }
  522. },
  523. errors: function(errors) {
  524. module.debug('Adding form error messages', errors);
  525. $message
  526. .html( settings.templates.error(errors) )
  527. ;
  528. }
  529. },
  530. remove: {
  531. prompt: function(field) {
  532. var
  533. $field = module.get.field(field.identifier),
  534. $fieldGroup = $field.closest($group),
  535. $prompt = $fieldGroup.children(selector.prompt)
  536. ;
  537. $fieldGroup
  538. .removeClass(className.error)
  539. ;
  540. if(settings.inline && $prompt.is(':visible')) {
  541. module.verbose('Removing prompt for field', field);
  542. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  543. $prompt.transition(settings.transition + ' out', settings.duration, function() {
  544. $prompt.remove();
  545. });
  546. }
  547. else {
  548. $prompt
  549. .fadeOut(settings.duration, function(){
  550. $prompt.remove();
  551. })
  552. ;
  553. }
  554. }
  555. }
  556. },
  557. set: {
  558. success: function() {
  559. $module
  560. .removeClass(className.error)
  561. .addClass(className.success)
  562. ;
  563. },
  564. defaults: function () {
  565. $field
  566. .each(function () {
  567. var
  568. $field = $(this),
  569. isCheckbox = ($field.filter(selector.checkbox).length > 0),
  570. value = (isCheckbox)
  571. ? $field.is(':checked')
  572. : $field.val()
  573. ;
  574. $field.data(metadata.defaultValue, value);
  575. })
  576. ;
  577. },
  578. error: function() {
  579. $module
  580. .removeClass(className.success)
  581. .addClass(className.error)
  582. ;
  583. },
  584. value: function (field, value) {
  585. var
  586. fields = {}
  587. ;
  588. fields[field] = value;
  589. return module.set.values.call(element, fields);
  590. },
  591. values: function (fields) {
  592. if($.isEmptyObject(fields)) {
  593. return;
  594. }
  595. $.each(fields, function(key, value) {
  596. var
  597. $field = module.get.field(key),
  598. $element = $field.parent(),
  599. isMultiple = $.isArray(value),
  600. isCheckbox = $element.is(selector.uiCheckbox),
  601. isDropdown = $element.is(selector.uiDropdown),
  602. isRadio = ($field.is(selector.radio) && isCheckbox),
  603. fieldExists = ($field.length > 0),
  604. $multipleField
  605. ;
  606. if(fieldExists) {
  607. if(isMultiple && isCheckbox) {
  608. module.verbose('Selecting multiple', value, $field);
  609. $element.checkbox('uncheck');
  610. $.each(value, function(index, value) {
  611. $multipleField = $field.filter('[value="' + value + '"]');
  612. $element = $multipleField.parent();
  613. if($multipleField.length > 0) {
  614. $element.checkbox('check');
  615. }
  616. });
  617. }
  618. else if(isRadio) {
  619. module.verbose('Selecting radio value', value, $field);
  620. $field.filter('[value="' + value + '"]')
  621. .parent(selector.uiCheckbox)
  622. .checkbox('check')
  623. ;
  624. }
  625. else if(isCheckbox) {
  626. module.verbose('Setting checkbox value', value, $element);
  627. if(value === true) {
  628. $element.checkbox('check');
  629. }
  630. else {
  631. $element.checkbox('uncheck');
  632. }
  633. }
  634. else if(isDropdown) {
  635. module.verbose('Setting dropdown value', value, $element);
  636. $element.dropdown('set selected', value);
  637. }
  638. else {
  639. module.verbose('Setting field value', value, $field);
  640. $field.val(value);
  641. }
  642. }
  643. });
  644. }
  645. },
  646. validate: {
  647. form: function(event) {
  648. var
  649. apiRequest
  650. ;
  651. // input keydown event will fire submit repeatedly by browser default
  652. if(keyHeldDown) {
  653. return false;
  654. }
  655. // reset errors
  656. formErrors = [];
  657. if( module.is.valid() ) {
  658. module.debug('Form has no validation errors, submitting');
  659. module.set.success();
  660. return settings.onSuccess.call(element, event);
  661. }
  662. else {
  663. module.debug('Form has errors');
  664. module.set.error();
  665. if(!settings.inline) {
  666. module.add.errors(formErrors);
  667. }
  668. // prevent ajax submit
  669. if($module.data('moduleApi') !== undefined) {
  670. event.stopImmediatePropagation();
  671. }
  672. return settings.onFailure.call(element, formErrors);
  673. }
  674. },
  675. // takes a validation object and returns whether field passes validation
  676. field: function(field) {
  677. var
  678. $field = module.get.field(field.identifier),
  679. fieldValid = true,
  680. fieldErrors = []
  681. ;
  682. if($field.prop('disabled')) {
  683. module.debug('Field is disabled. Skipping', field.identifier);
  684. fieldValid = true;
  685. }
  686. else if(field.optional && $.trim($field.val()) === ''){
  687. module.debug('Field is optional and empty. Skipping', field.identifier);
  688. fieldValid = true;
  689. }
  690. else if(field.rules !== undefined) {
  691. $.each(field.rules, function(index, rule) {
  692. if( module.has.field(field.identifier) && !( module.validate.rule(field, rule) ) ) {
  693. module.debug('Field is invalid', field.identifier, rule.type);
  694. fieldErrors.push(rule.prompt);
  695. fieldValid = false;
  696. }
  697. });
  698. }
  699. if(fieldValid) {
  700. module.remove.prompt(field, fieldErrors);
  701. settings.onValid.call($field);
  702. }
  703. else {
  704. formErrors = formErrors.concat(fieldErrors);
  705. module.add.prompt(field.identifier, fieldErrors);
  706. settings.onInvalid.call($field, fieldErrors);
  707. return false;
  708. }
  709. return true;
  710. },
  711. // takes validation rule and returns whether field passes rule
  712. rule: function(field, validation) {
  713. var
  714. $field = module.get.field(field.identifier),
  715. type = validation.type,
  716. value = $field.val(),
  717. bracket = type.match(settings.regExp.bracket),
  718. isValid = true,
  719. rule,
  720. ancillary,
  721. functionType
  722. ;
  723. // cast to string avoiding encoding special values
  724. value = (value === undefined || value === '' || value === null)
  725. ? ''
  726. : $.trim(value + '')
  727. ;
  728. // if bracket notation is used, pass in extra parameters
  729. if(bracket) {
  730. ancillary = '' + bracket[1];
  731. functionType = type.replace(bracket[0], '');
  732. rule = settings.rules[functionType];
  733. if( !$.isFunction(rule) ) {
  734. module.error(error.noRule, functionType);
  735. return;
  736. }
  737. isValid = rule.call($field, value, ancillary);
  738. }
  739. else {
  740. rule = settings.rules[type];
  741. if( !$.isFunction(rule) ) {
  742. module.error(error.noRule, type);
  743. return;
  744. }
  745. isValid = rule.call($field, value);
  746. }
  747. return isValid;
  748. }
  749. },
  750. setting: function(name, value) {
  751. if( $.isPlainObject(name) ) {
  752. $.extend(true, settings, name);
  753. }
  754. else if(value !== undefined) {
  755. settings[name] = value;
  756. }
  757. else {
  758. return settings[name];
  759. }
  760. },
  761. internal: function(name, value) {
  762. if( $.isPlainObject(name) ) {
  763. $.extend(true, module, name);
  764. }
  765. else if(value !== undefined) {
  766. module[name] = value;
  767. }
  768. else {
  769. return module[name];
  770. }
  771. },
  772. debug: function() {
  773. if(settings.debug) {
  774. if(settings.performance) {
  775. module.performance.log(arguments);
  776. }
  777. else {
  778. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  779. module.debug.apply(console, arguments);
  780. }
  781. }
  782. },
  783. verbose: function() {
  784. if(settings.verbose && settings.debug) {
  785. if(settings.performance) {
  786. module.performance.log(arguments);
  787. }
  788. else {
  789. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  790. module.verbose.apply(console, arguments);
  791. }
  792. }
  793. },
  794. error: function() {
  795. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  796. module.error.apply(console, arguments);
  797. },
  798. performance: {
  799. log: function(message) {
  800. var
  801. currentTime,
  802. executionTime,
  803. previousTime
  804. ;
  805. if(settings.performance) {
  806. currentTime = new Date().getTime();
  807. previousTime = time || currentTime;
  808. executionTime = currentTime - previousTime;
  809. time = currentTime;
  810. performance.push({
  811. 'Name' : message[0],
  812. 'Arguments' : [].slice.call(message, 1) || '',
  813. 'Element' : element,
  814. 'Execution Time' : executionTime
  815. });
  816. }
  817. clearTimeout(module.performance.timer);
  818. module.performance.timer = setTimeout(module.performance.display, 500);
  819. },
  820. display: function() {
  821. var
  822. title = settings.name + ':',
  823. totalTime = 0
  824. ;
  825. time = false;
  826. clearTimeout(module.performance.timer);
  827. $.each(performance, function(index, data) {
  828. totalTime += data['Execution Time'];
  829. });
  830. title += ' ' + totalTime + 'ms';
  831. if(moduleSelector) {
  832. title += ' \'' + moduleSelector + '\'';
  833. }
  834. if($allModules.length > 1) {
  835. title += ' ' + '(' + $allModules.length + ')';
  836. }
  837. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  838. console.groupCollapsed(title);
  839. if(console.table) {
  840. console.table(performance);
  841. }
  842. else {
  843. $.each(performance, function(index, data) {
  844. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  845. });
  846. }
  847. console.groupEnd();
  848. }
  849. performance = [];
  850. }
  851. },
  852. invoke: function(query, passedArguments, context) {
  853. var
  854. object = instance,
  855. maxDepth,
  856. found,
  857. response
  858. ;
  859. passedArguments = passedArguments || queryArguments;
  860. context = element || context;
  861. if(typeof query == 'string' && object !== undefined) {
  862. query = query.split(/[\. ]/);
  863. maxDepth = query.length - 1;
  864. $.each(query, function(depth, value) {
  865. var camelCaseValue = (depth != maxDepth)
  866. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  867. : query
  868. ;
  869. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  870. object = object[camelCaseValue];
  871. }
  872. else if( object[camelCaseValue] !== undefined ) {
  873. found = object[camelCaseValue];
  874. return false;
  875. }
  876. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  877. object = object[value];
  878. }
  879. else if( object[value] !== undefined ) {
  880. found = object[value];
  881. return false;
  882. }
  883. else {
  884. return false;
  885. }
  886. });
  887. }
  888. if( $.isFunction( found ) ) {
  889. response = found.apply(context, passedArguments);
  890. }
  891. else if(found !== undefined) {
  892. response = found;
  893. }
  894. if($.isArray(returnedValue)) {
  895. returnedValue.push(response);
  896. }
  897. else if(returnedValue !== undefined) {
  898. returnedValue = [returnedValue, response];
  899. }
  900. else if(response !== undefined) {
  901. returnedValue = response;
  902. }
  903. return found;
  904. }
  905. };
  906. module.initialize();
  907. })
  908. ;
  909. return (returnedValue !== undefined)
  910. ? returnedValue
  911. : this
  912. ;
  913. };
  914. $.fn.form.settings = {
  915. name : 'Form',
  916. namespace : 'form',
  917. debug : false,
  918. verbose : false,
  919. performance : true,
  920. fields : false,
  921. keyboardShortcuts : true,
  922. on : 'submit',
  923. inline : false,
  924. delay : 200,
  925. revalidate : true,
  926. transition : 'scale',
  927. duration : 200,
  928. onValid : function() {},
  929. onInvalid : function() {},
  930. onSuccess : function() { return true; },
  931. onFailure : function() { return false; },
  932. metadata : {
  933. defaultValue : 'default',
  934. validate : 'validate'
  935. },
  936. regExp: {
  937. bracket : /\[(.*)\]/i,
  938. escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
  939. 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])?",
  940. integer : /^\-?\d+$/,
  941. flags : /^\/(.*)\/(.*)?/,
  942. url : /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i
  943. },
  944. selector : {
  945. checkbox : 'input[type="checkbox"], input[type="radio"]',
  946. clear : '.clear',
  947. field : 'input, textarea, select',
  948. group : '.field',
  949. input : 'input',
  950. message : '.error.message',
  951. prompt : '.prompt.label',
  952. radio : 'input[type="radio"]',
  953. reset : '.reset:not([type="reset"])',
  954. submit : '.submit:not([type="submit"])',
  955. uiCheckbox : '.ui.checkbox',
  956. uiDropdown : '.ui.dropdown'
  957. },
  958. className : {
  959. error : 'error',
  960. label : 'ui prompt label',
  961. pressed : 'down',
  962. success : 'success'
  963. },
  964. error: {
  965. oldSyntax : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
  966. identifier : 'You must specify a string identifier for each field',
  967. noRule : 'There is no rule matching the one you specified',
  968. method : 'The method you called is not defined.'
  969. },
  970. templates: {
  971. // template that produces error message
  972. error: function(errors) {
  973. var
  974. html = '<ul class="list">'
  975. ;
  976. $.each(errors, function(index, value) {
  977. html += '<li>' + value + '</li>';
  978. });
  979. html += '</ul>';
  980. return $(html);
  981. },
  982. // template that produces label
  983. prompt: function(errors) {
  984. return $('<div/>')
  985. .addClass('ui red pointing prompt label')
  986. .html(errors[0])
  987. ;
  988. }
  989. },
  990. rules: {
  991. // is not empty or blank string
  992. empty: function(value) {
  993. return !(value === undefined || '' === value || $.isArray(value) && value.length === 0);
  994. },
  995. // checkbox checked
  996. checked: function() {
  997. return ($(this).filter(':checked').length > 0);
  998. },
  999. // is most likely an email
  1000. email: function(value){
  1001. var
  1002. emailRegExp = new RegExp($.fn.form.settings.regExp.email, 'i')
  1003. ;
  1004. return emailRegExp.test(value);
  1005. },
  1006. // value is most likely url
  1007. url: function(value) {
  1008. return $.fn.form.settings.regExp.url.test(value);
  1009. },
  1010. // matches specified regExp
  1011. regExp: function(value, regExp) {
  1012. var
  1013. regExpParts = regExp.match($.fn.form.settings.regExp.flags),
  1014. flags
  1015. ;
  1016. // regular expression specified as /baz/gi (flags)
  1017. if(regExpParts) {
  1018. regExp = (regExpParts.length >= 2)
  1019. ? regExpParts[1]
  1020. : regExp
  1021. ;
  1022. flags = (regExpParts.length >= 3)
  1023. ? regExpParts[2]
  1024. : ''
  1025. ;
  1026. }
  1027. return value.match( new RegExp(regExp, flags) );
  1028. },
  1029. // is valid integer or matches range
  1030. integer: function(value, range) {
  1031. var
  1032. intRegExp = $.fn.form.settings.regExp.integer,
  1033. min,
  1034. max,
  1035. parts
  1036. ;
  1037. if(range === undefined || range === '' || range === '..') {
  1038. // do nothing
  1039. }
  1040. else if(range.indexOf('..') == -1) {
  1041. if(intRegExp.test(range)) {
  1042. min = max = range - 0;
  1043. }
  1044. }
  1045. else {
  1046. parts = range.split('..', 2);
  1047. if(intRegExp.test(parts[0])) {
  1048. min = parts[0] - 0;
  1049. }
  1050. if(intRegExp.test(parts[1])) {
  1051. max = parts[1] - 0;
  1052. }
  1053. }
  1054. return (
  1055. intRegExp.test(value) &&
  1056. (min === undefined || value >= min) &&
  1057. (max === undefined || value <= max)
  1058. );
  1059. },
  1060. // is value (case insensitive)
  1061. is: function(value, text) {
  1062. text = (typeof text == 'string')
  1063. ? text.toLowerCase()
  1064. : text
  1065. ;
  1066. value = (typeof value == 'string')
  1067. ? value.toLowerCase()
  1068. : value
  1069. ;
  1070. return (value == text);
  1071. },
  1072. // is value
  1073. isExactly: function(value, text) {
  1074. return (value == text);
  1075. },
  1076. // value is not another value (case insensitive)
  1077. not: function(value, notValue) {
  1078. value = (typeof value == 'string')
  1079. ? value.toLowerCase()
  1080. : value
  1081. ;
  1082. notValue = (typeof notValue == 'string')
  1083. ? notValue.toLowerCase()
  1084. : notValue
  1085. ;
  1086. return (value != notValue);
  1087. },
  1088. // value is not another value (case sensitive)
  1089. notExactly: function(value, notValue) {
  1090. return (value != notValue);
  1091. },
  1092. // value contains text (insensitive)
  1093. contains: function(value, text) {
  1094. // escape regex characters
  1095. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  1096. return (value.search( new RegExp(text, 'i') ) !== -1);
  1097. },
  1098. // value contains text (case sensitive)
  1099. containsExactly: function(value, text) {
  1100. // escape regex characters
  1101. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  1102. return (value.search( new RegExp(text) ) !== -1);
  1103. },
  1104. // value contains text (insensitive)
  1105. doesntContain: function(value, text) {
  1106. // escape regex characters
  1107. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  1108. return (value.search( new RegExp(text, 'i') ) === -1);
  1109. },
  1110. // value contains text (case sensitive)
  1111. doesntContainExactly: function(value, text) {
  1112. // escape regex characters
  1113. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  1114. return (value.search( new RegExp(text) ) === -1);
  1115. },
  1116. // is exactly length
  1117. length: function(value, requiredLength) {
  1118. return (value !== undefined)
  1119. ? (value.length == requiredLength)
  1120. : false
  1121. ;
  1122. },
  1123. // is at least string length
  1124. minLength: function(value, requiredLength) {
  1125. return (value !== undefined)
  1126. ? (value.length >= requiredLength)
  1127. : false
  1128. ;
  1129. },
  1130. // is less than length
  1131. maxLength: function(value, maxLength) {
  1132. return (value !== undefined)
  1133. ? (value.length <= maxLength)
  1134. : false
  1135. ;
  1136. },
  1137. // matches another field
  1138. match: function(value, identifier) {
  1139. var
  1140. $form = $(this),
  1141. matchingValue
  1142. ;
  1143. if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
  1144. matchingValue = $('[data-validate="'+ identifier +'"]').val();
  1145. }
  1146. else if($('#' + identifier).length > 0) {
  1147. matchingValue = $('#' + identifier).val();
  1148. }
  1149. else if($('[name="' + identifier +'"]').length > 0) {
  1150. matchingValue = $('[name="' + identifier + '"]').val();
  1151. }
  1152. else if( $('[name="' + identifier +'[]"]').length > 0 ) {
  1153. matchingValue = $('[name="' + identifier +'[]"]');
  1154. }
  1155. return (matchingValue !== undefined)
  1156. ? ( value.toString() == matchingValue.toString() )
  1157. : false
  1158. ;
  1159. },
  1160. // different than another field
  1161. different: function(value, identifier) {
  1162. // use either id or name of field
  1163. var
  1164. $form = $(this),
  1165. matchingValue
  1166. ;
  1167. if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
  1168. matchingValue = $('[data-validate="'+ identifier +'"]').val();
  1169. }
  1170. else if($('#' + identifier).length > 0) {
  1171. matchingValue = $('#' + identifier).val();
  1172. }
  1173. else if($('[name="' + identifier +'"]').length > 0) {
  1174. matchingValue = $('[name="' + identifier + '"]').val();
  1175. }
  1176. else if( $('[name="' + identifier +'[]"]').length > 0 ) {
  1177. matchingValue = $('[name="' + identifier +'[]"]');
  1178. }
  1179. return (matchingValue !== undefined)
  1180. ? ( value.toString() !== matchingValue.toString() )
  1181. : false
  1182. ;
  1183. },
  1184. exactCount: function(value, exactCount) {
  1185. if(exactCount == 0) {
  1186. return (value === '');
  1187. }
  1188. if(exactCount == 1) {
  1189. return (value !== '' && value.search(',') === -1);
  1190. }
  1191. return (value.split(',').length == exactCount);
  1192. },
  1193. minCount: function(value, minCount) {
  1194. if(minCount == 0) {
  1195. return true;
  1196. }
  1197. if(minCount == 1) {
  1198. return (value !== '');
  1199. }
  1200. return (value.split(',').length >= minCount);
  1201. },
  1202. maxCount: function(value, maxCount) {
  1203. if(maxCount == 0) {
  1204. return false;
  1205. }
  1206. if(maxCount == 1) {
  1207. return (value.search(',') === -1);
  1208. }
  1209. return (value.split(',').length <= maxCount);
  1210. }
  1211. }
  1212. };
  1213. })( jQuery, window , document );