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.

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