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.

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