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.

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