/client-side/forms/netteForms.js

http://github.com/nette/nette · JavaScript · 310 lines · 244 code · 60 blank · 6 comment · 88 complexity · b87ede3c4a638fa87641610ae91ed699 MD5 · raw file

  1. /**
  2. * NetteForms - simple form validation.
  3. *
  4. * This file is part of the Nette Framework.
  5. * Copyright (c) 2004, 2012 David Grudl (http://davidgrudl.com)
  6. */
  7. var Nette = Nette || {};
  8. Nette.addEvent = function (element, on, callback) {
  9. var original = element['on' + on];
  10. element['on' + on] = function () {
  11. if (typeof original === 'function' && original.apply(element, arguments) === false) {
  12. return false;
  13. }
  14. return callback.apply(element, arguments);
  15. };
  16. };
  17. Nette.getValue = function(elem) {
  18. var i, len;
  19. if (!elem) {
  20. return null;
  21. } else if (!elem.nodeName) { // radio
  22. for (i = 0, len = elem.length; i < len; i++) {
  23. if (elem[i].checked) {
  24. return elem[i].value;
  25. }
  26. }
  27. return null;
  28. } else if (elem.nodeName.toLowerCase() === 'select') {
  29. var index = elem.selectedIndex, options = elem.options;
  30. if (index < 0) {
  31. return null;
  32. } else if (elem.type === 'select-one') {
  33. return options[index].value;
  34. }
  35. for (i = 0, values = [], len = options.length; i < len; i++) {
  36. if (options[i].selected) {
  37. values.push(options[i].value);
  38. }
  39. }
  40. return values;
  41. } else if (elem.type === 'checkbox') {
  42. return elem.checked;
  43. } else if (elem.type === 'radio') {
  44. return Nette.getValue(elem.form.elements[elem.name]);
  45. } else {
  46. return elem.value.replace(/^\s+|\s+$/g, '');
  47. }
  48. };
  49. Nette.validateControl = function(elem, rules, onlyCheck) {
  50. rules = rules || eval('[' + (elem.getAttribute('data-nette-rules') || '') + ']');
  51. for (var id = 0, len = rules.length; id < len; id++) {
  52. var rule = rules[id], op = rule.op.match(/(~)?([^?]+)/);
  53. rule.neg = op[1];
  54. rule.op = op[2];
  55. rule.condition = !!rule.rules;
  56. var el = rule.control ? elem.form.elements[rule.control] : elem;
  57. var success = Nette.validateRule(el, rule.op, rule.arg);
  58. if (success === null) { continue; }
  59. if (rule.neg) { success = !success; }
  60. if (rule.condition && success) {
  61. if (!Nette.validateControl(elem, rule.rules, onlyCheck)) {
  62. return false;
  63. }
  64. } else if (!rule.condition && !success) {
  65. if (el.disabled) { continue; }
  66. if (!onlyCheck) {
  67. Nette.addError(el, rule.msg.replace('%value', Nette.getValue(el)));
  68. }
  69. return false;
  70. }
  71. }
  72. return true;
  73. };
  74. Nette.validateForm = function(sender) {
  75. var form = sender.form || sender;
  76. if (form['nette-submittedBy'] && form['nette-submittedBy'].getAttribute('formnovalidate') !== null) {
  77. return true;
  78. }
  79. for (var i = 0; i < form.elements.length; i++) {
  80. var elem = form.elements[i];
  81. if (!(elem.nodeName.toLowerCase() in {input:1, select:1, textarea:1}) || (elem.type in {hidden:1, submit:1, image:1, reset: 1}) || elem.disabled || elem.readonly) {
  82. continue;
  83. }
  84. if (!Nette.validateControl(elem)) {
  85. return false;
  86. }
  87. }
  88. return true;
  89. };
  90. Nette.addError = function(elem, message) {
  91. if (elem.focus) {
  92. elem.focus();
  93. }
  94. if (message) {
  95. alert(message);
  96. }
  97. };
  98. Nette.validateRule = function(elem, op, arg) {
  99. var val = Nette.getValue(elem);
  100. if (elem.getAttribute) {
  101. if (val === elem.getAttribute('data-nette-empty-value')) { val = ''; }
  102. }
  103. if (op.charAt(0) === ':') {
  104. op = op.substr(1);
  105. }
  106. op = op.replace('::', '_');
  107. op = op.replace('\\', '');
  108. return Nette.validators[op] ? Nette.validators[op](elem, arg, val) : null;
  109. };
  110. Nette.validators = {
  111. filled: function(elem, arg, val) {
  112. return val !== '' && val !== false && val !== null;
  113. },
  114. valid: function(elem, arg, val) {
  115. return Nette.validateControl(elem, null, true);
  116. },
  117. equal: function(elem, arg, val) {
  118. if (arg === undefined) {
  119. return null;
  120. }
  121. arg = Nette.isArray(arg) ? arg : [arg];
  122. for (var i = 0, len = arg.length; i < len; i++) {
  123. if (val == (arg[i].control ? Nette.getValue(elem.form.elements[arg[i].control]) : arg[i])) {
  124. return true;
  125. }
  126. }
  127. return false;
  128. },
  129. minLength: function(elem, arg, val) {
  130. return val.length >= arg;
  131. },
  132. maxLength: function(elem, arg, val) {
  133. return val.length <= arg;
  134. },
  135. length: function(elem, arg, val) {
  136. arg = Nette.isArray(arg) ? arg : [arg, arg];
  137. return (arg[0] === null || val.length >= arg[0]) && (arg[1] === null || val.length <= arg[1]);
  138. },
  139. email: function(elem, arg, val) {
  140. return (/^[^@\s]+@[^@\s]+\.[a-z]{2,10}$/i).test(val);
  141. },
  142. url: function(elem, arg, val) {
  143. return (/^.+\.[a-z]{2,6}(\/.*)?$/i).test(val);
  144. },
  145. regexp: function(elem, arg, val) {
  146. var parts = typeof arg === 'string' ? arg.match(/^\/(.*)\/([imu]*)$/) : false;
  147. if (parts) { try {
  148. return (new RegExp(parts[1], parts[2].replace('u', ''))).test(val);
  149. } catch (e) {} }
  150. },
  151. pattern: function(elem, arg, val) {
  152. try {
  153. return typeof arg === 'string' ? (new RegExp('^(' + arg + ')$')).test(val) : null;
  154. } catch (e) {}
  155. },
  156. integer: function(elem, arg, val) {
  157. return (/^-?[0-9]+$/).test(val);
  158. },
  159. float: function(elem, arg, val) {
  160. return (/^-?[0-9]*[.,]?[0-9]+$/).test(val);
  161. },
  162. range: function(elem, arg, val) {
  163. return Nette.isArray(arg) ? ((arg[0] === null || parseFloat(val) >= arg[0]) && (arg[1] === null || parseFloat(val) <= arg[1])) : null;
  164. },
  165. submitted: function(elem, arg, val) {
  166. return elem.form['nette-submittedBy'] === elem;
  167. }
  168. };
  169. Nette.toggleForm = function(form) {
  170. for (var i = 0; i < form.elements.length; i++) {
  171. if (form.elements[i].nodeName.toLowerCase() in {input:1, select:1, textarea:1, button:1}) {
  172. Nette.toggleControl(form.elements[i]);
  173. }
  174. }
  175. };
  176. Nette.toggleControl = function(elem, rules, firsttime) {
  177. rules = rules || eval('[' + (elem.getAttribute('data-nette-rules') || '') + ']');
  178. var has = false, __hasProp = Object.prototype.hasOwnProperty, handler = function() { Nette.toggleForm(elem.form); };
  179. for (var id = 0, len = rules.length; id < len; id++) {
  180. var rule = rules[id], op = rule.op.match(/(~)?([^?]+)/);
  181. rule.neg = op[1];
  182. rule.op = op[2];
  183. rule.condition = !!rule.rules;
  184. if (!rule.condition) { continue; }
  185. var el = rule.control ? elem.form.elements[rule.control] : elem;
  186. var success = Nette.validateRule(el, rule.op, rule.arg);
  187. if (success === null) { continue; }
  188. if (rule.neg) { success = !success; }
  189. if (Nette.toggleControl(elem, rule.rules, firsttime) || rule.toggle) {
  190. has = true;
  191. if (firsttime) {
  192. if (!el.nodeName) { // radio
  193. for (var i = 0; i < el.length; i++) {
  194. Nette.addEvent(el[i], 'click', handler);
  195. }
  196. } else if (el.nodeName.toLowerCase() === 'select') {
  197. Nette.addEvent(el, 'change', handler);
  198. } else {
  199. Nette.addEvent(el, 'click', handler);
  200. }
  201. }
  202. for (var id2 in rule.toggle || []) {
  203. if (__hasProp.call(rule.toggle, id2)) { Nette.toggle(id2, success ? rule.toggle[id2] : !rule.toggle[id2]); }
  204. }
  205. }
  206. }
  207. return has;
  208. };
  209. Nette.toggle = function(id, visible) {
  210. var elem = document.getElementById(id);
  211. if (elem) {
  212. elem.style.display = visible ? "" : "none";
  213. }
  214. };
  215. Nette.initForm = function(form) {
  216. form.noValidate = true;
  217. Nette.addEvent(form, 'submit', function() {
  218. return Nette.validateForm(form);
  219. });
  220. Nette.addEvent(form, 'click', function(e) {
  221. e = e || event;
  222. var target = e.target || e.srcElement;
  223. form['nette-submittedBy'] = (target.type in {submit:1, image:1}) ? target : null;
  224. });
  225. for (var i = 0; i < form.elements.length; i++) {
  226. Nette.toggleControl(form.elements[i], null, true);
  227. }
  228. if (/MSIE/.exec(navigator.userAgent)) {
  229. var labels = {},
  230. wheelHandler = function() { return false; },
  231. clickHandler = function() { document.getElementById(this.htmlFor).focus(); return false; };
  232. for (i = 0, elms = form.getElementsByTagName('label'); i < elms.length; i++) {
  233. labels[elms[i].htmlFor] = elms[i];
  234. }
  235. for (i = 0, elms = form.getElementsByTagName('select'); i < elms.length; i++) {
  236. Nette.addEvent(elms[i], 'mousewheel', wheelHandler); // prevents accidental change in IE
  237. if (labels[elms[i].htmlId]) {
  238. Nette.addEvent(labels[elms[i].htmlId], 'click', clickHandler); // prevents deselect in IE 5 - 6
  239. }
  240. }
  241. }
  242. };
  243. Nette.isArray = function(arg) {
  244. return Object.prototype.toString.call(arg) === '[object Array]';
  245. };
  246. Nette.addEvent(window, 'load', function () {
  247. for (var i = 0; i < document.forms.length; i++) {
  248. Nette.initForm(document.forms[i]);
  249. }
  250. });