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.

1253 lines
39 KiB

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