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.

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