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.

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