PageSourceSearch

https://euphoria.eu/assets/netteForms.js

js euphoria.eu collected 2026-10-04 03:33:41 UTC 19,001 bytes, 719 lines download raw bytes

1/**
2 * NetteForms - simple form validation.
3 *
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7
8(function(global, factory) {
9	if (!global.JSON) {
10		return;
11	}
12
13	if (typeof define === 'function' && define.amd) {
14		define(function() {
15			return factory(global);
16		});
17	} else if (typeof module === 'object' && typeof module.exports === 'object') {
18		module.exports = factory(global);
19	} else {
20		var init = !global.Nette || !global.Nette.noInit;
21		global.Nette = factory(global);
22		if (init) {
23			global.Nette.initOnLoad();
24		}
25	}
26
27}(typeof window !== 'undefined' ? window : this, function(window) {
28
29	'use strict';
30
31	var Nette = {};
32
33	Nette.formErrors = [];
34	Nette.version = '3.0';
35
36
37	/**
38	 * Function to execute when the DOM is fully loaded.
39	 * @private
40	 */
41	Nette.onDocumentReady = function(callback) {
42		if (document.readyState !== 'loading') {
43			callback.call(this);
44		} else {
45			document.addEventListener('DOMContentLoaded', callback);
46		}
47	};
48
49
50	/**
51	 * Returns the value of form element.
52	 */
53	Nette.getValue = function(elem) {
54		var i;
55		if (!elem) {
56			return null;
57
58		} else if (!elem.tagName) { // RadioNodeList, HTMLCollection, array
59			return elem[0] ? Nette.getValue(elem[0]) : null;
60
61		} else if (elem.type === 'radio') {
62			var elements = elem.form.elements; // prevents problem with name 'item' or 'namedItem'
63			for (i = 0; i < elements.length; i++) {
64				if (elements[i].name === elem.name && elements[i].checked) {
65					return elements[i].value;
66				}
67			}
68			return null;
69
70		} else if (elem.type === 'file') {
71			return elem.files || elem.value;
72
73		} else if (elem.tagName.toLowerCase() === 'select') {
74			var index = elem.selectedIndex,
75				options = elem.options,
76				values = [];
77
78			if (elem.type === 'select-one') {
79				return index < 0 ? null : options[index].value;
80			}
81
82			for (i = 0; i < options.length; i++) {
83				if (options[i].selected) {
84					values.push(options[i].value);
85				}
86			}
87			return values;
88
89		} else if (elem.name && elem.name.match(/\[\]$/)) { // multiple elements []
90			elements = elem.form.elements[elem.name].tagName ? [elem] : elem.form.elements[elem.name];
91			values = [];
92
93			for (i = 0; i < elements.length; i++) {
94				if (elements[i].type !== 'checkbox' || elements[i].checked) {
95					values.push(elements[i].value);
96				}
97			}
98			return values;
99
100		} else if (elem.type === 'checkbox') {
101			return elem.checked;
102
103		} else if (elem.tagName.toLowerCase() === 'textarea') {
104			return elem.value.replace('\r', '');
105
106		} else {
107			return elem.value.replace('\r', '').replace(/^\s+|\s+$/g, '');
108		}
109	};
110
111
112	/**
113	 * Returns the effective value of form element.
114	 */
115	Nette.getEffectiveValue = function(elem, filter) {
116		var val = Nette.getValue(elem);
117		if (elem.getAttribute) {
118			if (val === elem.getAttribute('data-nette-empty-value')) {
119				val = '';
120			}
121		}
122		if (filter) {
123			var ref = {value: val};
124			Nette.validateControl(elem, null, true, ref);
125			val = ref.value;
126		}
127		return val;
128	};
129
130
131	/**
132	 * Validates form element against given rules.
133	 */
134	Nette.validateControl = function(elem, rules, onlyCheck, value, emptyOptional) {
135		elem = elem.tagName ? elem : elem[0]; // RadioNodeList
136		rules = rules || JSON.parse(elem.getAttribute('data-nette-rules') || '[]');
137		value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value;
138		emptyOptional = emptyOptional || !Nette.validateRule(elem, ':filled', null, value);
139
140		for (var id = 0, len = rules.length; id < len; id++) {
141			var rule = rules[id],
142				op = rule.op.match(/(~)?([^?]+)/),
143				curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem;
144
145			rule.neg = op[1];
146			rule.op = op[2];
147			rule.condition = !!rule.rules;
148
149			if (!curElem) {
150				continue;
151			} else if (emptyOptional && !rule.condition && rule.op !== ':filled') {
152				continue;
153			}
154
155			curElem = curElem.tagName ? curElem : curElem[0]; // RadioNodeList
156			var success = Nette.validateRule(curElem, rule.op, rule.arg, elem === curElem ? value : undefined);
157
158			if (success === null) {
159				continue;
160			} else if (rule.neg) {
161				success = !success;
162			}
163
164			if (rule.condition && success) {
165				if (!Nette.validateControl(elem, rule.rules, onlyCheck, value, rule.op === ':blank' ? false : emptyOptional)) {
166					return false;
167				}
168			} else if (!rule.condition && !success) {
169				if (Nette.isDisabled(curElem)) {
170					continue;
171				}
172				if (!onlyCheck) {
173					var arr = Array.isArray(rule.arg) ? rule.arg : [rule.arg],
174						message = rule.msg.replace(/%(value|\d+)/g, function(foo, m) {
175							return Nette.getValue(m === 'value' ? curElem : elem.form.elements.namedItem(arr[m].control));
176						});
177					Nette.addError(curElem, message);
178				}
179				return false;
180			}
181		}
182
183		if (elem.type === 'number' && !elem.validity.valid) {
184			if (!onlyCheck) {
185				Nette.addError(elem, 'Please enter a valid value.');
186			}
187			return false;
188		}
189
190		return true;
191	};
192
193
194	/**
195	 * Validates whole form.
196	 */
197	Nette.validateForm = function(sender, onlyCheck) {
198		var form = sender.form || sender,
199			scope = false;
200
201		Nette.formErrors = [];
202
203		if (form['nette-submittedBy'] && form['nette-submittedBy'].getAttribute('formnovalidate') !== null) {
204			var scopeArr = JSON.parse(form['nette-submittedBy'].getAttribute('data-nette-validation-scope') || '[]');
205			if (scopeArr.length) {
206				scope = new RegExp('^(' + scopeArr.join('-|') + '-)');
207			} else {
208				Nette.showFormErrors(form, []);
209				return true;
210			}
211		}
212
213		var radios = {}, i, elem;
214
215		for (i = 0; i < form.elements.length; i++) {
216			elem = form.elements[i];
217
218			if (elem.tagName && !(elem.tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1})) {
219				continue;
220
221			} else if (elem.type === 'radio') {
222				if (radios[elem.name]) {
223					continue;
224				}
225				radios[elem.name] = true;
226			}
227
228			if ((scope && !elem.name.replace(/]\[|\[|]|$/g, '-').match(scope)) || Nette.isDisabled(elem)) {
229				continue;
230			}
231
232			if (!Nette.validateControl(elem, null, onlyCheck) && !Nette.formErrors.length) {
233				return false;
234			}
235		}
236		var success = !Nette.formErrors.length;
237		Nette.showFormErrors(form, Nette.formErrors);
238		return success;
239	};
240
241
242	/**
243	 * Check if input is disabled.
244	 */
245	Nette.isDisabled = function(elem) {
246		if (elem.type === 'radio') {
247			for (var i = 0, elements = elem.form.elements; i < elements.length; i++) {
248				if (elements[i].name === elem.name && !elements[i].disabled) {
249					return false;
250				}
251			}
252			return true;
253		}
254		return elem.disabled;
255	};
256
257
258	/**
259	 * Adds error message to the queue.
260	 */
261	Nette.addError = function(elem, message) {
262		Nette.formErrors.push({
263			element: elem,
264			message: message
265		});
266	};
267
268
269	/**
270	 * Display error messages.
271	 */
272	Nette.showFormErrors = function(form, errors) {
273		var messages = [],
274			focusElem;
275
276		for (var i = 0; i < errors.length; i++) {
277			var elem = errors[i].element,
278				message = errors[i].message;
279
280			if (messages.indexOf(message) < 0) {
281				messages.push(message);
282
283				if (!focusElem && elem.focus) {
284					focusElem = elem;
285				}
286			}
287		}
288
289		if (messages.length) {
290			alert(messages.join('\n'));
291
292			if (focusElem) {
293				focusElem.focus();
294			}
295		}
296	};
297
298
299	var preventFiltering = false;
300
301	/**
302	 * Validates single rule.
303	 */
304	Nette.validateRule = function(elem, op, arg, value) {
305		value = value === undefined ? {value: Nette.getEffectiveValue(elem, true)} : value;
306
307		if (op.charAt(0) === ':') {
308			op = op.substr(1);
309		}
310		op = op.replace('::', '_');
311		op = op.replace(/\\/g, '');
312
313		var arr = Array.isArray(arg) ? arg.slice(0) : [arg];
314		if (!preventFiltering) {
315			preventFiltering = true;
316			for (var i = 0, len = arr.length; i < len; i++) {
317				if (arr[i] && arr[i].control) {
318					var control = elem.form.elements.namedItem(arr[i].control);
319					arr[i] = control === elem ? value.value : Nette.getEffectiveValue(control, true);
320				}
321			}
322			preventFiltering = false;
323		}
324		return Nette.validators[op]
325			? Nette.validators[op](elem, Array.isArray(arg) ? arr : arr[0], value.value, value)
326			: null;
327	};
328
329
330	Nette.validators = {
331		filled: function(elem, arg, val) {
332			if (elem.type === 'number' && elem.validity.badInput) {
333				return true;
334			}
335			return val !== '' && val !== false && val !== null
336				&& (!Array.isArray(val) || !!val.length)
337				&& (!window.FileList || !(val instanceof window.FileList) || val.length);
338		},
339
340		blank: function(elem, arg, val) {
341			return !Nette.validators.filled(elem, arg, val);
342		},
343
344		valid: function(elem) {
345			return Nette.validateControl(elem, null, true);
346		},
347
348		equal: function(elem, arg, val) {
349			if (arg === undefined) {
350				return null;
351			}
352
353			function toString(val) {
354				if (typeof val === 'number' || typeof val === 'string') {
355					return '' + val;
356				} else {
357					return val === true ? '1' : '';
358				}
359			}
360
361			val = Array.isArray(val) ? val : [val];
362			arg = Array.isArray(arg) ? arg : [arg];
363			loop:
364			for (var i1 = 0, len1 = val.length; i1 < len1; i1++) {
365				for (var i2 = 0, len2 = arg.length; i2 < len2; i2++) {
366					if (toString(val[i1]) === toString(arg[i2])) {
367						continue loop;
368					}
369				}
370				return false;
371			}
372			return true;
373		},
374
375		notEqual: function(elem, arg, val) {
376			return arg === undefined ? null : !Nette.validators.equal(elem, arg, val);
377		},
378
379		minLength: function(elem, arg, val) {
380			if (elem.type === 'number') {
381				if (elem.validity.tooShort) {
382					return false;
383				} else if (elem.validity.badInput) {
384					return null;
385				}
386			}
387			return val.length >= arg;
388		},
389
390		maxLength: function(elem, arg, val) {
391			if (elem.type === 'number') {
392				if (elem.validity.tooLong) {
393					return false;
394				} else if (elem.validity.badInput) {
395					return null;
396				}
397			}
398			return val.length <= arg;
399		},
400
401		length: function(elem, arg, val) {
402			if (elem.type === 'number') {
403				if (elem.validity.tooShort || elem.validity.tooLong) {
404					return false;
405				} else if (elem.validity.badInput) {
406					return null;
407				}
408			}
409			arg = Array.isArray(arg) ? arg : [arg, arg];
410			return (arg[0] === null || val.length >= arg[0]) && (arg[1] === null || val.length <= arg[1]);
411		},
412
413		email: function(elem, arg, val) {
414			return (/^("([ !#-[\]-~]|\\[ -~])+"|[-a-z0-9!#$%&'*+/=?^_`{|}~]+(\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*)@([0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)+[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?$/i).test(val);
415		},
416
417		url: function(elem, arg, val, value) {
418			if (!(/^[a-z\d+.-]+:/).test(val)) {
419				val = 'http://' + val;
420			}
421			if ((/^https?:\/\/((([-_0-9a-z\u00C0-\u02FF\u0370-\u1EFF]+\.)*[0-9a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,61}[0-9a-z\u00C0-\u02FF\u0370-\u1EFF])?\.)?[a-z\u00C0-\u02FF\u0370-\u1EFF]([-0-9a-z\u00C0-\u02FF\u0370-\u1EFF]{0,17}[a-z\u00C0-\u02FF\u0370-\u1EFF])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[0-9a-f:]{3,39}\])(:\d{1,5})?(\/\S*)?$/i).test(val)) {
422				value.value = val;
423				return true;
424			}
425			return false;
426		},
427
428		regexp: function(elem, arg, val) {
429			var parts = typeof arg === 'string' ? arg.match(/^\/(.*)\/([imu]*)$/) : false;
430			try {
431				return parts && (new RegExp(parts[1], parts[2].replace('u', ''))).test(val);
432			} catch (e) {} // eslint-disable-line no-empty
433		},
434
435		pattern: function(elem, arg, val, value, caseInsensitive) {
436			if (typeof arg !== 'string') {
437				return null;
438			}
439
440			try {
441				try {
442					var regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'ui' : 'u');
443				} catch (e) {
444					regExp = new RegExp('^(?:' + arg + ')$', caseInsensitive ? 'i' : '');
445				}
446
447				if (window.FileList && val instanceof FileList) {
448					for (var i = 0; i < val.length; i++) {
449						if (!regExp.test(val[i].name)) {
450							return false;
451						}
452					}
453
454					return true;
455				}
456
457				return regExp.test(val);
458			} catch (e) {} // eslint-disable-line no-empty
459		},
460
461		patternCaseInsensitive: function(elem, arg, val) {
462			return Nette.validators.pattern(elem, arg, val, null, true);
463		},
464
465		numeric: function(elem, arg, val) {
466			if (elem.type === 'number' && elem.validity.badInput) {
467				return false;
468			}
469			return (/^[0-9]+$/).test(val);
470		},
471
472		integer: function(elem, arg, val) {
473			if (elem.type === 'number' && elem.validity.badInput) {
474				return false;
475			}
476			return (/^-?[0-9]+$/).test(val);
477		},
478
479		'float': function(elem, arg, val, value) {
480			if (elem.type === 'number' && elem.validity.badInput) {
481				return false;
482			}
483			val = val.replace(/ +/g, '').replace(/,/g, '.');
484			if ((/^-?[0-9]*\.?[0-9]+$/).test(val)) {
485				value.value = val;
486				return true;
487			}
488			return false;
489		},
490
491		min: function(elem, arg, val) {
492			if (elem.type === 'number') {
493				if (elem.validity.rangeUnderflow) {
494					return false;
495				} else if (elem.validity.badInput) {
496					return null;
497				}
498			}
499			return arg === null || parseFloat(val) >= arg;
500		},
501
502		max: function(elem, arg, val) {
503			if (elem.type === 'number') {
504				if (elem.validity.rangeOverflow) {
505					return false;
506				} else if (elem.validity.badInput) {
507					return null;
508				}
509			}
510			return arg === null || parseFloat(val) <= arg;
511		},
512
513		range: function(elem, arg, val) {
514			if (elem.type === 'number') {
515				if (elem.validity.rangeUnderflow || elem.validity.rangeOverflow) {
516					return false;
517				} else if (elem.validity.badInput) {
518					return null;
519				}
520			}
521			return Array.isArray(arg) ?
522				((arg[0] === null || parseFloat(val) >= arg[0]) && (arg[1] === null || parseFloat(val) <= arg[1])) : null;
523		},
524
525		submitted: function(elem) {
526			return elem.form['nette-submittedBy'] === elem;
527		},
528
529		fileSize: function(elem, arg, val) {
530			if (window.FileList) {
531				for (var i = 0; i < val.length; i++) {
532					if (val[i].size > arg) {
533						return false;
534					}
535				}
536			}
537			return true;
538		},
539
540		image: function (elem, arg, val) {
541			if (window.FileList && val instanceof window.FileList) {
542				for (var i = 0; i < val.length; i++) {
543					var type = val[i].type;
544					if (type && type !== 'image/gif' && type !== 'image/png' && type !== 'image/jpeg') {
545						return false;
546					}
547				}
548			}
549			return true;
550		},
551
552		'static': function (elem, arg) {
553			return arg;
554		}
555	};
556
557
558	/**
559	 * Process all toggles in form.
560	 */
561	Nette.toggleForm = function(form, elem) {
562		var i;
563		Nette.toggles = {};
564		for (i = 0; i < form.elements.length; i++) {
565			if (form.elements[i].tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1}) {
566				Nette.toggleControl(form.elements[i], null, null, !elem);
567			}
568		}
569
570		for (i in Nette.toggles) {
571			Nette.toggle(i, Nette.toggles[i], elem);
572		}
573	};
574
575
576	/**
577	 * Process toggles on form element.
578	 */
579	Nette.toggleControl = function(elem, rules, success, firsttime, value) {
580		rules = rules || JSON.parse(elem.getAttribute('data-nette-rules') || '[]');
581		value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value;
582
583		var has = false,
584			handled = [],
585			handler = function () {
586				Nette.toggleForm(elem.form, elem);
587			},
588			curSuccess;
589
590		for (var id = 0, len = rules.length; id < len; id++) {
591			var rule = rules[id],
592				op = rule.op.match(/(~)?([^?]+)/),
593				curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem;
594
595			if (!curElem) {
596				continue;
597			}
598
599			curSuccess = success;
600			if (success !== false) {
601				rule.neg = op[1];
602				rule.op = op[2];
603				curSuccess = Nette.validateRule(curElem, rule.op, rule.arg, elem === curElem ? value : undefined);
604				if (curSuccess === null) {
605					continue;
606
607				} else if (rule.neg) {
608					curSuccess = !curSuccess;
609				}
610				if (!rule.rules) {
611					success = curSuccess;
612				}
613			}
614
615			if ((rule.rules && Nette.toggleControl(elem, rule.rules, curSuccess, firsttime, value)) || rule.toggle) {
616				has = true;
617				if (firsttime) {
618					var name = curElem.tagName ? curElem.name : curElem[0].name,
619						els = curElem.tagName ? curElem.form.elements : curElem;
620
621					for (var i = 0; i < els.length; i++) {
622						if (els[i].name === name && handled.indexOf(els[i]) < 0) {
623							els[i].addEventListener('change', handler);
624							handled.push(els[i]);
625						}
626					}
627				}
628				for (var id2 in rule.toggle || []) {
629					if (Object.prototype.hasOwnProperty.call(rule.toggle, id2)) {
630						Nette.toggles[id2] = Nette.toggles[id2] || (rule.toggle[id2] ? curSuccess : !curSuccess);
631					}
632				}
633			}
634		}
635		return has;
636	};
637
638
639	/**
640	 * Displays or hides HTML element.
641	 */
642	Nette.toggle = function(selector, visible, srcElement) { // eslint-disable-line no-unused-vars
643		if (/^\w[\w.:-]*$/.test(selector)) { // id
644			selector = '#' + selector;
645		}
646		var elems = document.querySelectorAll(selector);
647		for (var i = 0; i < elems.length; i++) {
648			elems[i].hidden = !visible;
649		}
650	};
651
652
653	/**
654	 * Setup handlers.
655	 */
656	Nette.initForm = function(form) {
657		Nette.toggleForm(form);
658
659		if (form.noValidate) {
660			return;
661		}
662
663		form.noValidate = true;
664
665		form.addEventListener('submit', function(e) {
666			if (!Nette.validateForm(form)) {
667				e.stopPropagation();
668				e.preventDefault();
669			}
670		});
671	};
672
673
674	/**
675	 * @private
676	 */
677	Nette.initOnLoad = function() {
678		Nette.onDocumentReady(function() {
679			for (var i = 0; i < document.forms.length; i++) {
680				var form = document.forms[i];
681				for (var j = 0; j < form.elements.length; j++) {
682					if (form.elements[j].getAttribute('data-nette-rules')) {
683						Nette.initForm(form);
684						break;
685					}
686				}
687			}
688
689			document.body.addEventListener('click', function(e) {
690				var target = e.target;
691				while (target) {
692					if (target.form && target.type in {submit: 1, image: 1}) {
693						target.form['nette-submittedBy'] = target;
694						break;
695					}
696					target = target.parentNode;
697				}
698			});
699		});
700	};
701
702
703	/**
704	 * Converts string to web safe characters [a-z0-9-] text.
705	 */
706	Nette.webalize = function(s) {
707		s = s.toLowerCase();
708		var res = '', i, ch;
709		for (i = 0; i < s.length; i++) {
710			ch = Nette.webalizeTable[s.charAt(i)];
711			res += ch ? ch : s.charAt(i);
712		}
713		return res.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
714	};
715
716	Nette.webalizeTable = {\u00e1: 'a', \u00e4: 'a', \u010d: 'c', \u010f: 'd', \u00e9: 'e', \u011b: 'e', \u00ed: 'i', \u013e: 'l', \u0148: 'n', \u00f3: 'o', \u00f4: 'o', \u0159: 'r', \u0161: 's', \u0165: 't', \u00fa: 'u', \u016f: 'u', \u00fd: 'y', \u017e: 'z'};
717
718	return Nette;
719}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.