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.

1256 lines
39 KiB

  1. /*!
  2. * # Semantic UI 2.0.0 - Form Validation
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2015 Contributors
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ( $, window, document, undefined ) {
  12. "use strict";
  13. $.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. defaultValue = '';
  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)
  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( $field.filter('#' + identifier).length > 0 ) {
  466. return true;
  467. }
  468. else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
  469. return true;
  470. }
  471. else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
  472. return true;
  473. }
  474. return false;
  475. }
  476. },
  477. add: {
  478. prompt: function(identifier, errors) {
  479. var
  480. $field = module.get.field(identifier),
  481. $fieldGroup = $field.closest($group),
  482. $prompt = $fieldGroup.children(selector.prompt),
  483. promptExists = ($prompt.length !== 0)
  484. ;
  485. errors = (typeof errors == 'string')
  486. ? [errors]
  487. : errors
  488. ;
  489. module.verbose('Adding field error state', identifier);
  490. $fieldGroup
  491. .addClass(className.error)
  492. ;
  493. if(settings.inline) {
  494. if(!promptExists) {
  495. $prompt = settings.templates.prompt(errors);
  496. $prompt
  497. .appendTo($fieldGroup)
  498. ;
  499. }
  500. $prompt
  501. .html(errors[0])
  502. ;
  503. if(!promptExists) {
  504. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  505. module.verbose('Displaying error with css transition', settings.transition);
  506. $prompt.transition(settings.transition + ' in', settings.duration);
  507. }
  508. else {
  509. module.verbose('Displaying error with fallback javascript animation');
  510. $prompt
  511. .fadeIn(settings.duration)
  512. ;
  513. }
  514. }
  515. else {
  516. module.verbose('Inline errors are disabled, no inline error added', identifier);
  517. }
  518. }
  519. },
  520. errors: function(errors) {
  521. module.debug('Adding form error messages', errors);
  522. $message
  523. .html( settings.templates.error(errors) )
  524. ;
  525. }
  526. },
  527. remove: {
  528. prompt: function(field) {
  529. var
  530. $field = module.get.field(field.identifier),
  531. $fieldGroup = $field.closest($group),
  532. $prompt = $fieldGroup.children(selector.prompt)
  533. ;
  534. $fieldGroup
  535. .removeClass(className.error)
  536. ;
  537. if(settings.inline && $prompt.is(':visible')) {
  538. module.verbose('Removing prompt for field', field);
  539. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  540. $prompt.transition(settings.transition + ' out', settings.duration, function() {
  541. $prompt.remove();
  542. });
  543. }
  544. else {
  545. $prompt
  546. .fadeOut(settings.duration, function(){
  547. $prompt.remove();
  548. })
  549. ;
  550. }
  551. }
  552. }
  553. },
  554. set: {
  555. success: function() {
  556. $module
  557. .removeClass(className.error)
  558. .addClass(className.success)
  559. ;
  560. },
  561. defaults: function () {
  562. $field
  563. .each(function () {
  564. var
  565. $field = $(this),
  566. isCheckbox = ($field.filter(selector.checkbox).length > 0),
  567. value = (isCheckbox)
  568. ? $field.is(':checked')
  569. : $field.val()
  570. ;
  571. $field.data(metadata.defaultValue, value);
  572. })
  573. ;
  574. },
  575. error: function() {
  576. $module
  577. .removeClass(className.success)
  578. .addClass(className.error)
  579. ;
  580. },
  581. value: function (field, value) {
  582. var
  583. fields = {}
  584. ;
  585. fields[field] = value;
  586. return module.set.values.call(element, fields);
  587. },
  588. values: function (fields) {
  589. if($.isEmptyObject(fields)) {
  590. return;
  591. }
  592. $.each(fields, function(key, value) {
  593. var
  594. $field = module.get.field(key),
  595. $element = $field.parent(),
  596. isMultiple = $.isArray(value),
  597. isCheckbox = $element.is(selector.uiCheckbox),
  598. isDropdown = $element.is(selector.uiDropdown),
  599. isRadio = ($field.is(selector.radio) && isCheckbox),
  600. fieldExists = ($field.length > 0),
  601. $multipleField
  602. ;
  603. if(fieldExists) {
  604. if(isMultiple && isCheckbox) {
  605. module.verbose('Selecting multiple', value, $field);
  606. $element.checkbox('uncheck');
  607. $.each(value, function(index, value) {
  608. $multipleField = $field.filter('[value="' + value + '"]');
  609. $element = $multipleField.parent();
  610. if($multipleField.length > 0) {
  611. $element.checkbox('check');
  612. }
  613. });
  614. }
  615. else if(isRadio) {
  616. module.verbose('Selecting radio value', value, $field);
  617. $field.filter('[value="' + value + '"]')
  618. .parent(selector.uiCheckbox)
  619. .checkbox('check')
  620. ;
  621. }
  622. else if(isCheckbox) {
  623. module.verbose('Setting checkbox value', value, $element);
  624. if(value === true) {
  625. $element.checkbox('check');
  626. }
  627. else {
  628. $element.checkbox('uncheck');
  629. }
  630. }
  631. else if(isDropdown) {
  632. module.verbose('Setting dropdown value', value, $element);
  633. $element.dropdown('set selected', value);
  634. }
  635. else {
  636. module.verbose('Setting field value', value, $field);
  637. $field.val(value);
  638. }
  639. }
  640. });
  641. }
  642. },
  643. validate: {
  644. form: function(event) {
  645. var
  646. apiRequest
  647. ;
  648. // input keydown event will fire submit repeatedly by browser default
  649. if(keyHeldDown) {
  650. return false;
  651. }
  652. // reset errors
  653. formErrors = [];
  654. if( module.is.valid() ) {
  655. module.debug('Form has no validation errors, submitting');
  656. module.set.success();
  657. return settings.onSuccess.call(element, event);
  658. }
  659. else {
  660. module.debug('Form has errors');
  661. module.set.error();
  662. if(!settings.inline) {
  663. module.add.errors(formErrors);
  664. }
  665. // prevent ajax submit
  666. if($module.data('moduleApi') !== undefined) {
  667. event.stopImmediatePropagation();
  668. }
  669. return settings.onFailure.call(element, formErrors);
  670. }
  671. },
  672. // takes a validation object and returns whether field passes validation
  673. field: function(field) {
  674. var
  675. $field = module.get.field(field.identifier),
  676. fieldValid = true,
  677. fieldErrors = []
  678. ;
  679. if($field.prop('disabled')) {
  680. module.debug('Field is disabled. Skipping', field.identifier);
  681. fieldValid = true;
  682. }
  683. else if(field.optional && $.trim($field.val()) === ''){
  684. module.debug('Field is optional and empty. Skipping', field.identifier);
  685. fieldValid = true;
  686. }
  687. else if(field.rules !== undefined) {
  688. $.each(field.rules, function(index, rule) {
  689. if( module.has.field(field.identifier) && !( module.validate.rule(field, rule) ) ) {
  690. module.debug('Field is invalid', field.identifier, rule.type);
  691. fieldErrors.push(rule.prompt);
  692. fieldValid = false;
  693. }
  694. });
  695. }
  696. if(fieldValid) {
  697. module.remove.prompt(field, fieldErrors);
  698. settings.onValid.call($field);
  699. }
  700. else {
  701. formErrors = formErrors.concat(fieldErrors);
  702. module.add.prompt(field.identifier, fieldErrors);
  703. settings.onInvalid.call($field, fieldErrors);
  704. return false;
  705. }
  706. return true;
  707. },
  708. // takes validation rule and returns whether field passes rule
  709. rule: function(field, validation) {
  710. var
  711. $field = module.get.field(field.identifier),
  712. type = validation.type,
  713. value = $field.val(),
  714. bracket = type.match(settings.regExp.bracket),
  715. isValid = true,
  716. rule,
  717. ancillary,
  718. functionType
  719. ;
  720. // cast to string
  721. value = $.trim($field.val() + '');
  722. // if bracket notation is used, pass in extra parameters
  723. if(bracket) {
  724. ancillary = '' + bracket[1];
  725. functionType = type.replace(bracket[0], '');
  726. rule = settings.rules[functionType];
  727. if( !$.isFunction(rule) ) {
  728. module.error(error.noRule, functionType);
  729. return;
  730. }
  731. isValid = rule.call($field, value, ancillary);
  732. }
  733. else {
  734. rule = settings.rules[type];
  735. if( !$.isFunction(rule) ) {
  736. module.error(error.noRule, type);
  737. return;
  738. }
  739. isValid = rule.call($field, value);
  740. }
  741. return isValid;
  742. }
  743. },
  744. setting: function(name, value) {
  745. if( $.isPlainObject(name) ) {
  746. $.extend(true, settings, name);
  747. }
  748. else if(value !== undefined) {
  749. settings[name] = value;
  750. }
  751. else {
  752. return settings[name];
  753. }
  754. },
  755. internal: function(name, value) {
  756. if( $.isPlainObject(name) ) {
  757. $.extend(true, module, name);
  758. }
  759. else if(value !== undefined) {
  760. module[name] = value;
  761. }
  762. else {
  763. return module[name];
  764. }
  765. },
  766. debug: function() {
  767. if(settings.debug) {
  768. if(settings.performance) {
  769. module.performance.log(arguments);
  770. }
  771. else {
  772. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  773. module.debug.apply(console, arguments);
  774. }
  775. }
  776. },
  777. verbose: function() {
  778. if(settings.verbose && settings.debug) {
  779. if(settings.performance) {
  780. module.performance.log(arguments);
  781. }
  782. else {
  783. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  784. module.verbose.apply(console, arguments);
  785. }
  786. }
  787. },
  788. error: function() {
  789. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  790. module.error.apply(console, arguments);
  791. },
  792. performance: {
  793. log: function(message) {
  794. var
  795. currentTime,
  796. executionTime,
  797. previousTime
  798. ;
  799. if(settings.performance) {
  800. currentTime = new Date().getTime();
  801. previousTime = time || currentTime;
  802. executionTime = currentTime - previousTime;
  803. time = currentTime;
  804. performance.push({
  805. 'Name' : message[0],
  806. 'Arguments' : [].slice.call(message, 1) || '',
  807. 'Element' : element,
  808. 'Execution Time' : executionTime
  809. });
  810. }
  811. clearTimeout(module.performance.timer);
  812. module.performance.timer = setTimeout(module.performance.display, 500);
  813. },
  814. display: function() {
  815. var
  816. title = settings.name + ':',
  817. totalTime = 0
  818. ;
  819. time = false;
  820. clearTimeout(module.performance.timer);
  821. $.each(performance, function(index, data) {
  822. totalTime += data['Execution Time'];
  823. });
  824. title += ' ' + totalTime + 'ms';
  825. if(moduleSelector) {
  826. title += ' \'' + moduleSelector + '\'';
  827. }
  828. if($allModules.length > 1) {
  829. title += ' ' + '(' + $allModules.length + ')';
  830. }
  831. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  832. console.groupCollapsed(title);
  833. if(console.table) {
  834. console.table(performance);
  835. }
  836. else {
  837. $.each(performance, function(index, data) {
  838. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  839. });
  840. }
  841. console.groupEnd();
  842. }
  843. performance = [];
  844. }
  845. },
  846. invoke: function(query, passedArguments, context) {
  847. var
  848. object = instance,
  849. maxDepth,
  850. found,
  851. response
  852. ;
  853. passedArguments = passedArguments || queryArguments;
  854. context = element || context;
  855. if(typeof query == 'string' && object !== undefined) {
  856. query = query.split(/[\. ]/);
  857. maxDepth = query.length - 1;
  858. $.each(query, function(depth, value) {
  859. var camelCaseValue = (depth != maxDepth)
  860. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  861. : query
  862. ;
  863. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  864. object = object[camelCaseValue];
  865. }
  866. else if( object[camelCaseValue] !== undefined ) {
  867. found = object[camelCaseValue];
  868. return false;
  869. }
  870. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  871. object = object[value];
  872. }
  873. else if( object[value] !== undefined ) {
  874. found = object[value];
  875. return false;
  876. }
  877. else {
  878. return false;
  879. }
  880. });
  881. }
  882. if( $.isFunction( found ) ) {
  883. response = found.apply(context, passedArguments);
  884. }
  885. else if(found !== undefined) {
  886. response = found;
  887. }
  888. if($.isArray(returnedValue)) {
  889. returnedValue.push(response);
  890. }
  891. else if(returnedValue !== undefined) {
  892. returnedValue = [returnedValue, response];
  893. }
  894. else if(response !== undefined) {
  895. returnedValue = response;
  896. }
  897. return found;
  898. }
  899. };
  900. module.initialize();
  901. })
  902. ;
  903. return (returnedValue !== undefined)
  904. ? returnedValue
  905. : this
  906. ;
  907. };
  908. $.fn.form.settings = {
  909. name : 'Form',
  910. namespace : 'form',
  911. debug : false,
  912. verbose : false,
  913. performance : true,
  914. fields : false,
  915. keyboardShortcuts : true,
  916. on : 'submit',
  917. inline : false,
  918. delay : 200,
  919. revalidate : true,
  920. transition : 'scale',
  921. duration : 200,
  922. onValid : function() {},
  923. onInvalid : function() {},
  924. onSuccess : function() { return true; },
  925. onFailure : function() { return false; },
  926. metadata : {
  927. defaultValue : 'default',
  928. validate : 'validate'
  929. },
  930. regExp: {
  931. bracket : /\[(.*)\]/i,
  932. escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
  933. 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])?",
  934. integer : /^\-?\d+$/,
  935. flags : /^\/(.*)\/(.*)?/,
  936. url : /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/i
  937. },
  938. selector : {
  939. checkbox : 'input[type="checkbox"], input[type="radio"]',
  940. clear : '.clear',
  941. field : 'input, textarea, select',
  942. group : '.field',
  943. input : 'input',
  944. message : '.error.message',
  945. prompt : '.prompt.label',
  946. radio : 'input[type="radio"]',
  947. reset : '.reset:not([type="reset"])',
  948. submit : '.submit:not([type="submit"])',
  949. uiCheckbox : '.ui.checkbox',
  950. uiDropdown : '.ui.dropdown'
  951. },
  952. className : {
  953. error : 'error',
  954. label : 'ui prompt label',
  955. pressed : 'down',
  956. success : 'success'
  957. },
  958. error: {
  959. oldSyntax : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
  960. noRule : 'There is no rule matching the one you specified',
  961. method : 'The method you called is not defined.'
  962. },
  963. templates: {
  964. // template that produces error message
  965. error: function(errors) {
  966. var
  967. html = '<ul class="list">'
  968. ;
  969. $.each(errors, function(index, value) {
  970. html += '<li>' + value + '</li>';
  971. });
  972. html += '</ul>';
  973. return $(html);
  974. },
  975. // template that produces label
  976. prompt: function(errors) {
  977. return $('<div/>')
  978. .addClass('ui red pointing prompt label')
  979. .html(errors[0])
  980. ;
  981. }
  982. },
  983. rules: {
  984. // checkbox checked
  985. checked: function() {
  986. return ($(this).filter(':checked').length > 0);
  987. },
  988. // value contains text (insensitive)
  989. contains: function(value, text) {
  990. // escape regex characters
  991. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  992. return (value.search( new RegExp(text, 'i') ) !== -1);
  993. },
  994. // value contains text (case sensitive)
  995. containsExactly: function(value, text) {
  996. // escape regex characters
  997. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  998. return (value.search( new RegExp(text) ) !== -1);
  999. },
  1000. // is most likely an email
  1001. email: function(value){
  1002. var
  1003. emailRegExp = new RegExp($.fn.form.settings.regExp.email, 'i')
  1004. ;
  1005. return emailRegExp.test(value);
  1006. },
  1007. // is not empty or blank string
  1008. empty: function(value) {
  1009. return !(value === undefined || '' === value || $.isArray(value) && value.length === 0);
  1010. },
  1011. // is valid integer
  1012. integer: function(value, range) {
  1013. var
  1014. intRegExp = $.fn.form.settings.regExp.integer,
  1015. min,
  1016. max,
  1017. parts
  1018. ;
  1019. if(range === undefined || range === '' || range === '..') {
  1020. // do nothing
  1021. }
  1022. else if(range.indexOf('..') == -1) {
  1023. if(intRegExp.test(range)) {
  1024. min = max = range - 0;
  1025. }
  1026. }
  1027. else {
  1028. parts = range.split('..', 2);
  1029. if(intRegExp.test(parts[0])) {
  1030. min = parts[0] - 0;
  1031. }
  1032. if(intRegExp.test(parts[1])) {
  1033. max = parts[1] - 0;
  1034. }
  1035. }
  1036. return (
  1037. intRegExp.test(value) &&
  1038. (min === undefined || value >= min) &&
  1039. (max === undefined || value <= max)
  1040. );
  1041. },
  1042. // is value (case insensitive)
  1043. is: function(value, text) {
  1044. text = (typeof text == 'string')
  1045. ? text.toLowerCase()
  1046. : text
  1047. ;
  1048. value = (typeof value == 'string')
  1049. ? value.toLowerCase()
  1050. : value
  1051. ;
  1052. return (value == text);
  1053. },
  1054. // is value
  1055. isExactly: function(value, text) {
  1056. return (value == text);
  1057. },
  1058. // is at least string length
  1059. length: function(value, requiredLength) {
  1060. return (value !== undefined)
  1061. ? (value.length >= requiredLength)
  1062. : false
  1063. ;
  1064. },
  1065. // matches another field
  1066. different: function(value, identifier) {
  1067. // use either id or name of field
  1068. var
  1069. $form = $(this),
  1070. matchingValue
  1071. ;
  1072. if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
  1073. matchingValue = $('[data-validate="'+ identifier +'"]').val();
  1074. }
  1075. else if($('#' + identifier).length > 0) {
  1076. matchingValue = $('#' + identifier).val();
  1077. }
  1078. else if($('[name="' + identifier +'"]').length > 0) {
  1079. matchingValue = $('[name="' + identifier + '"]').val();
  1080. }
  1081. else if( $('[name="' + identifier +'[]"]').length > 0 ) {
  1082. matchingValue = $('[name="' + identifier +'[]"]');
  1083. }
  1084. return (matchingValue !== undefined)
  1085. ? ( value.toString() !== matchingValue.toString() )
  1086. : false
  1087. ;
  1088. },
  1089. // matches another field
  1090. match: function(value, identifier) {
  1091. // use either id or name of field
  1092. var
  1093. $form = $(this),
  1094. matchingValue
  1095. ;
  1096. if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
  1097. matchingValue = $('[data-validate="'+ identifier +'"]').val();
  1098. }
  1099. else if($('#' + identifier).length > 0) {
  1100. matchingValue = $('#' + identifier).val();
  1101. }
  1102. else if($('[name="' + identifier +'"]').length > 0) {
  1103. matchingValue = $('[name="' + identifier + '"]').val();
  1104. }
  1105. else if( $('[name="' + identifier +'[]"]').length > 0 ) {
  1106. matchingValue = $('[name="' + identifier +'[]"]');
  1107. }
  1108. return (matchingValue !== undefined)
  1109. ? ( value.toString() == matchingValue.toString() )
  1110. : false
  1111. ;
  1112. },
  1113. maxCount: function(value, count) {
  1114. value = value.split(',');
  1115. return ($.isArray(value) && value.length <= count);
  1116. },
  1117. exactCount: function(value, count) {
  1118. value = value.split(',');
  1119. return ($.isArray(value) && value.length == count);
  1120. },
  1121. minCount: function(value, count) {
  1122. value = value.split(',');
  1123. return ($.isArray(value) && value.length >= count);
  1124. },
  1125. regExp: function(value, regExp) {
  1126. var
  1127. regExpParts = regExp.match($.fn.form.settings.regExp.flags),
  1128. flags
  1129. ;
  1130. // regular expression specified as /baz/gi (flags)
  1131. if(regExpParts) {
  1132. regExp = (regExpParts.length >= 2)
  1133. ? regExpParts[1]
  1134. : regExp
  1135. ;
  1136. flags = (regExpParts.length >= 3)
  1137. ? regExpParts[2]
  1138. : ''
  1139. ;
  1140. }
  1141. return value.match( new RegExp(regExp, flags) );
  1142. },
  1143. // string length is less than max length
  1144. maxLength: function(value, maxLength) {
  1145. return (value !== undefined)
  1146. ? (value.length <= maxLength)
  1147. : false
  1148. ;
  1149. },
  1150. // value is not value (case insensitive)
  1151. not: function(value, notValue) {
  1152. value = (typeof value == 'string')
  1153. ? value.toLowerCase()
  1154. : value
  1155. ;
  1156. notValue = (typeof notValue == 'string')
  1157. ? notValue.toLowerCase()
  1158. : notValue
  1159. ;
  1160. return (value != notValue);
  1161. },
  1162. // value is not value (case sensitive)
  1163. notExactly: function(value, notValue) {
  1164. return (value != notValue);
  1165. },
  1166. // value is most likely url
  1167. url: function(value) {
  1168. return $.fn.form.settings.regExp.url.match(value);
  1169. }
  1170. }
  1171. };
  1172. })( jQuery, window , document );