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.

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