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.

1188 lines
37 KiB

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