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.

1246 lines
39 KiB

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
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
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 x.x - 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).get(0) == $field.get(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. if(isCheckbox) {
  555. console.log($field.is(':checked'), $field, $field.data(metadata.defaultValue));
  556. }
  557. })
  558. ;
  559. },
  560. error: function() {
  561. $module
  562. .removeClass(className.success)
  563. .addClass(className.error)
  564. ;
  565. },
  566. value: function (field, value) {
  567. var
  568. fields = {}
  569. ;
  570. fields[field] = value;
  571. return module.set.values.call(element, fields);
  572. },
  573. values: function (fields) {
  574. if($.isEmptyObject(fields)) {
  575. return;
  576. }
  577. $.each(fields, function(key, value) {
  578. var
  579. $field = module.get.field(key),
  580. $element = $field.parent(),
  581. isMultiple = $.isArray(value),
  582. isCheckbox = $element.is(selector.uiCheckbox),
  583. isDropdown = $element.is(selector.uiDropdown),
  584. isRadio = ($field.is(selector.radio) && isCheckbox),
  585. fieldExists = ($field.length > 0),
  586. $multipleField
  587. ;
  588. if(fieldExists) {
  589. if(isMultiple && isCheckbox) {
  590. module.verbose('Selecting multiple', value, $field);
  591. $element.checkbox('uncheck');
  592. $.each(value, function(index, value) {
  593. $multipleField = $field.filter('[value="' + value + '"]');
  594. $element = $multipleField.parent();
  595. if($multipleField.length > 0) {
  596. $element.checkbox('check');
  597. }
  598. });
  599. }
  600. else if(isRadio) {
  601. module.verbose('Selecting radio value', value, $field);
  602. $field.filter('[value="' + value + '"]')
  603. .parent(selector.uiCheckbox)
  604. .checkbox('check')
  605. ;
  606. }
  607. else if(isCheckbox) {
  608. module.verbose('Setting checkbox value', value, $element);
  609. if(value === true) {
  610. $element.checkbox('check');
  611. }
  612. else {
  613. $element.checkbox('uncheck');
  614. }
  615. }
  616. else if(isDropdown) {
  617. module.verbose('Setting dropdown value', value, $element);
  618. $element.dropdown('set selected', value);
  619. }
  620. else {
  621. module.verbose('Setting field value', value, $field);
  622. $field.val(value);
  623. }
  624. }
  625. });
  626. }
  627. },
  628. validate: {
  629. form: function(event) {
  630. var
  631. allValid = true,
  632. apiRequest
  633. ;
  634. // input keydown event will fire submit repeatedly by browser default
  635. if(keyHeldDown) {
  636. return false;
  637. }
  638. // reset errors
  639. formErrors = [];
  640. $.each(validation, function(fieldName, field) {
  641. if( !( module.validate.field(field) ) ) {
  642. allValid = false;
  643. }
  644. });
  645. if(allValid) {
  646. module.debug('Form has no validation errors, submitting');
  647. module.set.success();
  648. return settings.onSuccess.call(element, event);
  649. }
  650. else {
  651. module.debug('Form has errors');
  652. module.set.error();
  653. if(!settings.inline) {
  654. module.add.errors(formErrors);
  655. }
  656. // prevent ajax submit
  657. if($module.data('moduleApi') !== undefined) {
  658. event.stopImmediatePropagation();
  659. }
  660. return settings.onFailure.call(element, formErrors);
  661. }
  662. },
  663. // takes a validation object and returns whether field passes validation
  664. field: function(field) {
  665. var
  666. $field = module.get.field(field.identifier),
  667. fieldValid = true,
  668. fieldErrors = []
  669. ;
  670. if($field.prop('disabled')) {
  671. module.debug('Field is disabled. Skipping', field.identifier);
  672. fieldValid = true;
  673. }
  674. else if(field.optional && $.trim($field.val()) === ''){
  675. module.debug('Field is optional and empty. Skipping', field.identifier);
  676. fieldValid = true;
  677. }
  678. else if(field.rules !== undefined) {
  679. $.each(field.rules, function(index, rule) {
  680. if( module.has.field(field.identifier) && !( module.validate.rule(field, rule) ) ) {
  681. module.debug('Field is invalid', field.identifier, rule.type);
  682. fieldErrors.push(rule.prompt);
  683. fieldValid = false;
  684. }
  685. });
  686. }
  687. if(fieldValid) {
  688. module.remove.prompt(field, fieldErrors);
  689. settings.onValid.call($field);
  690. }
  691. else {
  692. formErrors = formErrors.concat(fieldErrors);
  693. module.add.prompt(field.identifier, fieldErrors);
  694. settings.onInvalid.call($field, fieldErrors);
  695. return false;
  696. }
  697. return true;
  698. },
  699. // takes validation rule and returns whether field passes rule
  700. rule: function(field, validation) {
  701. var
  702. $field = module.get.field(field.identifier),
  703. type = validation.type,
  704. value = $field.val(),
  705. bracket = type.match(settings.regExp.bracket),
  706. isValid = true,
  707. rule,
  708. ancillary,
  709. functionType
  710. ;
  711. // cast to string
  712. value = $.trim($field.val() + '');
  713. // if bracket notation is used, pass in extra parameters
  714. if(bracket) {
  715. ancillary = '' + bracket[1];
  716. functionType = type.replace(bracket[0], '');
  717. rule = settings.rules[functionType];
  718. if( !$.isFunction(rule) ) {
  719. module.error(error.noRule, functionType);
  720. return;
  721. }
  722. isValid = rule.call($field, value, ancillary);
  723. }
  724. else {
  725. rule = settings.rules[type];
  726. if( !$.isFunction(rule) ) {
  727. module.error(error.noRule, type);
  728. return;
  729. }
  730. isValid = rule.call($field, value);
  731. }
  732. return isValid;
  733. }
  734. },
  735. setting: function(name, value) {
  736. if( $.isPlainObject(name) ) {
  737. $.extend(true, settings, name);
  738. }
  739. else if(value !== undefined) {
  740. settings[name] = value;
  741. }
  742. else {
  743. return settings[name];
  744. }
  745. },
  746. internal: function(name, value) {
  747. if( $.isPlainObject(name) ) {
  748. $.extend(true, module, name);
  749. }
  750. else if(value !== undefined) {
  751. module[name] = value;
  752. }
  753. else {
  754. return module[name];
  755. }
  756. },
  757. debug: function() {
  758. if(settings.debug) {
  759. if(settings.performance) {
  760. module.performance.log(arguments);
  761. }
  762. else {
  763. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  764. module.debug.apply(console, arguments);
  765. }
  766. }
  767. },
  768. verbose: function() {
  769. if(settings.verbose && settings.debug) {
  770. if(settings.performance) {
  771. module.performance.log(arguments);
  772. }
  773. else {
  774. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  775. module.verbose.apply(console, arguments);
  776. }
  777. }
  778. },
  779. error: function() {
  780. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  781. module.error.apply(console, arguments);
  782. },
  783. performance: {
  784. log: function(message) {
  785. var
  786. currentTime,
  787. executionTime,
  788. previousTime
  789. ;
  790. if(settings.performance) {
  791. currentTime = new Date().getTime();
  792. previousTime = time || currentTime;
  793. executionTime = currentTime - previousTime;
  794. time = currentTime;
  795. performance.push({
  796. 'Name' : message[0],
  797. 'Arguments' : [].slice.call(message, 1) || '',
  798. 'Element' : element,
  799. 'Execution Time' : executionTime
  800. });
  801. }
  802. clearTimeout(module.performance.timer);
  803. module.performance.timer = setTimeout(module.performance.display, 500);
  804. },
  805. display: function() {
  806. var
  807. title = settings.name + ':',
  808. totalTime = 0
  809. ;
  810. time = false;
  811. clearTimeout(module.performance.timer);
  812. $.each(performance, function(index, data) {
  813. totalTime += data['Execution Time'];
  814. });
  815. title += ' ' + totalTime + 'ms';
  816. if(moduleSelector) {
  817. title += ' \'' + moduleSelector + '\'';
  818. }
  819. if($allModules.length > 1) {
  820. title += ' ' + '(' + $allModules.length + ')';
  821. }
  822. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  823. console.groupCollapsed(title);
  824. if(console.table) {
  825. console.table(performance);
  826. }
  827. else {
  828. $.each(performance, function(index, data) {
  829. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  830. });
  831. }
  832. console.groupEnd();
  833. }
  834. performance = [];
  835. }
  836. },
  837. invoke: function(query, passedArguments, context) {
  838. var
  839. object = instance,
  840. maxDepth,
  841. found,
  842. response
  843. ;
  844. passedArguments = passedArguments || queryArguments;
  845. context = element || context;
  846. if(typeof query == 'string' && object !== undefined) {
  847. query = query.split(/[\. ]/);
  848. maxDepth = query.length - 1;
  849. $.each(query, function(depth, value) {
  850. var camelCaseValue = (depth != maxDepth)
  851. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  852. : query
  853. ;
  854. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  855. object = object[camelCaseValue];
  856. }
  857. else if( object[camelCaseValue] !== undefined ) {
  858. found = object[camelCaseValue];
  859. return false;
  860. }
  861. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  862. object = object[value];
  863. }
  864. else if( object[value] !== undefined ) {
  865. found = object[value];
  866. return false;
  867. }
  868. else {
  869. return false;
  870. }
  871. });
  872. }
  873. if( $.isFunction( found ) ) {
  874. response = found.apply(context, passedArguments);
  875. }
  876. else if(found !== undefined) {
  877. response = found;
  878. }
  879. if($.isArray(returnedValue)) {
  880. returnedValue.push(response);
  881. }
  882. else if(returnedValue !== undefined) {
  883. returnedValue = [returnedValue, response];
  884. }
  885. else if(response !== undefined) {
  886. returnedValue = response;
  887. }
  888. return found;
  889. }
  890. };
  891. module.initialize();
  892. })
  893. ;
  894. return (returnedValue !== undefined)
  895. ? returnedValue
  896. : this
  897. ;
  898. };
  899. $.fn.form.settings = {
  900. name : 'Form',
  901. namespace : 'form',
  902. debug : false,
  903. verbose : false,
  904. performance : true,
  905. fields : false,
  906. keyboardShortcuts : true,
  907. on : 'submit',
  908. inline : false,
  909. delay : 200,
  910. revalidate : true,
  911. transition : 'scale',
  912. duration : 200,
  913. onValid : function() {},
  914. onInvalid : function() {},
  915. onSuccess : function() { return true; },
  916. onFailure : function() { return false; },
  917. metadata : {
  918. defaultValue : 'default',
  919. validate : 'validate'
  920. },
  921. regExp: {
  922. bracket : /\[(.*)\]/i,
  923. escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
  924. 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])?",
  925. integer : /^\-?\d+$/,
  926. flags : /^\/(.*)\/(.*)?/,
  927. url : /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/i
  928. },
  929. selector : {
  930. checkbox : 'input[type="checkbox"], input[type="radio"]',
  931. clear : '.clear',
  932. field : 'input, textarea, select',
  933. group : '.field',
  934. input : 'input',
  935. message : '.error.message',
  936. prompt : '.prompt.label',
  937. radio : 'input[type="radio"]',
  938. reset : '.reset:not([type="reset"])',
  939. submit : '.submit:not([type="submit"])',
  940. uiCheckbox : '.ui.checkbox',
  941. uiDropdown : '.ui.dropdown'
  942. },
  943. className : {
  944. error : 'error',
  945. label : 'ui prompt label',
  946. pressed : 'down',
  947. success : 'success'
  948. },
  949. error: {
  950. oldSyntax : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
  951. noRule : 'There is no rule matching the one you specified',
  952. method : 'The method you called is not defined.'
  953. },
  954. templates: {
  955. // template that produces error message
  956. error: function(errors) {
  957. var
  958. html = '<ul class="list">'
  959. ;
  960. $.each(errors, function(index, value) {
  961. html += '<li>' + value + '</li>';
  962. });
  963. html += '</ul>';
  964. return $(html);
  965. },
  966. // template that produces label
  967. prompt: function(errors) {
  968. return $('<div/>')
  969. .addClass('ui red pointing prompt label')
  970. .html(errors[0])
  971. ;
  972. }
  973. },
  974. rules: {
  975. // checkbox checked
  976. checked: function() {
  977. return ($(this).filter(':checked').length > 0);
  978. },
  979. // value contains text (insensitive)
  980. contains: function(value, text) {
  981. // escape regex characters
  982. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  983. return (value.search( new RegExp(text, 'i') ) !== -1);
  984. },
  985. // value contains text (case sensitive)
  986. containsExactly: function(value, text) {
  987. // escape regex characters
  988. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  989. return (value.search( new RegExp(text) ) !== -1);
  990. },
  991. // is most likely an email
  992. email: function(value){
  993. var
  994. emailRegExp = new RegExp($.fn.form.settings.regExp.email, 'i')
  995. ;
  996. return emailRegExp.test(value);
  997. },
  998. // is not empty or blank string
  999. empty: function(value) {
  1000. return !(value === undefined || '' === value || $.isArray(value) && value.length === 0);
  1001. },
  1002. // is valid integer
  1003. integer: function(value, range) {
  1004. var
  1005. intRegExp = $.fn.form.settings.regExp.integer,
  1006. min,
  1007. max,
  1008. parts
  1009. ;
  1010. if(range === undefined || range === '' || range === '..') {
  1011. // do nothing
  1012. }
  1013. else if(range.indexOf('..') == -1) {
  1014. if(intRegExp.test(range)) {
  1015. min = max = range - 0;
  1016. }
  1017. }
  1018. else {
  1019. parts = range.split('..', 2);
  1020. if(intRegExp.test(parts[0])) {
  1021. min = parts[0] - 0;
  1022. }
  1023. if(intRegExp.test(parts[1])) {
  1024. max = parts[1] - 0;
  1025. }
  1026. }
  1027. return (
  1028. intRegExp.test(value) &&
  1029. (min === undefined || value >= min) &&
  1030. (max === undefined || value <= max)
  1031. );
  1032. },
  1033. // is value (case insensitive)
  1034. is: function(value, text) {
  1035. text = (typeof text == 'string')
  1036. ? text.toLowerCase()
  1037. : text
  1038. ;
  1039. value = (typeof value == 'string')
  1040. ? value.toLowerCase()
  1041. : value
  1042. ;
  1043. return (value == text);
  1044. },
  1045. // is value
  1046. isExactly: function(value, text) {
  1047. return (value == text);
  1048. },
  1049. // is at least string length
  1050. length: function(value, requiredLength) {
  1051. return (value !== undefined)
  1052. ? (value.length >= requiredLength)
  1053. : false
  1054. ;
  1055. },
  1056. // matches another field
  1057. different: function(value, identifier) {
  1058. // use either id or name of field
  1059. var
  1060. $form = $(this),
  1061. matchingValue
  1062. ;
  1063. if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
  1064. matchingValue = $('[data-validate="'+ identifier +'"]').val();
  1065. }
  1066. else if($('#' + identifier).length > 0) {
  1067. matchingValue = $('#' + identifier).val();
  1068. }
  1069. else if($('[name="' + identifier +'"]').length > 0) {
  1070. matchingValue = $('[name="' + identifier + '"]').val();
  1071. }
  1072. else if( $('[name="' + identifier +'[]"]').length > 0 ) {
  1073. matchingValue = $('[name="' + identifier +'[]"]');
  1074. }
  1075. return (matchingValue !== undefined)
  1076. ? ( value.toString() !== matchingValue.toString() )
  1077. : false
  1078. ;
  1079. },
  1080. // matches another field
  1081. match: function(value, identifier) {
  1082. // use either id or name of field
  1083. var
  1084. $form = $(this),
  1085. matchingValue
  1086. ;
  1087. if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
  1088. matchingValue = $('[data-validate="'+ identifier +'"]').val();
  1089. }
  1090. else if($('#' + identifier).length > 0) {
  1091. matchingValue = $('#' + identifier).val();
  1092. }
  1093. else if($('[name="' + identifier +'"]').length > 0) {
  1094. matchingValue = $('[name="' + identifier + '"]').val();
  1095. }
  1096. else if( $('[name="' + identifier +'[]"]').length > 0 ) {
  1097. matchingValue = $('[name="' + identifier +'[]"]');
  1098. }
  1099. return (matchingValue !== undefined)
  1100. ? ( value.toString() == matchingValue.toString() )
  1101. : false
  1102. ;
  1103. },
  1104. maxCount: function(value, count) {
  1105. value = value.split(',');
  1106. return ($.isArray(value) && value.length <= count);
  1107. },
  1108. exactCount: function(value, count) {
  1109. value = value.split(',');
  1110. return ($.isArray(value) && value.length == count);
  1111. },
  1112. minCount: function(value, count) {
  1113. value = value.split(',');
  1114. return ($.isArray(value) && value.length >= count);
  1115. },
  1116. regExp: function(value, regExp) {
  1117. var
  1118. regExpParts = regExp.match($.fn.form.settings.regExp.flags),
  1119. flags
  1120. ;
  1121. // regular expression specified as /baz/gi (flags)
  1122. if(regExpParts) {
  1123. regExp = (regExpParts.length >= 2)
  1124. ? regExpParts[1]
  1125. : regExp
  1126. ;
  1127. flags = (regExpParts.length >= 3)
  1128. ? regExpParts[2]
  1129. : ''
  1130. ;
  1131. }
  1132. return value.match( new RegExp(regExp, flags) );
  1133. },
  1134. // string length is less than max length
  1135. maxLength: function(value, maxLength) {
  1136. return (value !== undefined)
  1137. ? (value.length <= maxLength)
  1138. : false
  1139. ;
  1140. },
  1141. // value is not value (case insensitive)
  1142. not: function(value, notValue) {
  1143. value = (typeof value == 'string')
  1144. ? value.toLowerCase()
  1145. : value
  1146. ;
  1147. notValue = (typeof notValue == 'string')
  1148. ? notValue.toLowerCase()
  1149. : notValue
  1150. ;
  1151. return (value != notValue);
  1152. },
  1153. // value is not value (case sensitive)
  1154. notExactly: function(value, notValue) {
  1155. return (value != notValue);
  1156. },
  1157. // value is most likely url
  1158. url: function(value) {
  1159. return $.fn.form.settings.regExp.url.match(value);
  1160. }
  1161. }
  1162. };
  1163. })( jQuery, window , document );