PageSourceSearch

https://www.eastport.cz/js/live-form-validation/live-form-validation.js

js eastport.cz collected 2026-10-06 10:23:52 UTC 32,430 bytes, 1,209 lines download raw bytes

1/**
2 * Live Form Validation for Nette Forms 2.4
3 *
4 * @author Robert Pösel, zakrava, Radek Ježdík, MartyIX, David Grudl
5 * @version 1.9.0-dev
6 * @url https://github.com/Robyer/nette-live-form-validation/
7 */
8
9var LiveForm = {
10	options: {
11		// CSS class of control's parent where error/valid class should be added; or "false" to use control directly
12		showMessageClassOnParent: 'form-group',
13
14		// CSS class of control's parent where error/valid message should be added (fallback to direct parent if not found); or "false" to use control's direct parent
15		messageParentClass: false,
16
17		// CSS class for an invalid control
18		controlErrorClass: 'has-error',
19
20		// CSS class for a valid control
21		controlValidClass: 'has-success',
22
23		// CSS class for an error message
24		messageErrorClass: 'help-block text-danger',
25
26		// control with this CSS class will show error/valid message even when control itself is hidden (useful for controls which are hidden and wrapped into special component)
27		enableHiddenMessageClass: 'show-hidden-error',
28
29		// control with this CSS class will have disabled live validation
30		disableLiveValidationClass: 'no-live-validation',
31
32		// control with this CSS class will not show valid message
33		disableShowValidClass: 'no-show-valid',
34
35		// tag that will hold the error/valid message
36		messageTag: 'span',
37
38		// message element id = control id + this postfix
39		messageIdPostfix: '_message',
40
41		// show this html before error message itself
42		messageErrorPrefix: '&nbsp;<i class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></i>&nbsp;',
43
44		// show all errors when submitting form; or use "false" to show only first error
45		showAllErrors: true,
46
47		// show message when valid
48		showValid: false,
49
50		// delay in ms before validating on keyup/keydown; or use "false" to disable it
51		wait: false,
52
53		// vertical screen offset in px to scroll after focusing element with error (useful when using fixed navbar menu which may otherwise obscure the element in focus); or use "false" for default behavior
54		focusScreenOffsetY: false
55	},
56
57	forms: { }
58};
59
60LiveForm.setOptions = function(userOptions) {
61	for (var prop in userOptions) {
62		if (Object.prototype.hasOwnProperty.call(this.options, prop)) {
63			this.options[prop] = userOptions[prop];
64		}
65	}
66}
67
68// Allow setting options before loading the script just by creating global LiveFormOptions object with options.
69if (typeof window.LiveFormOptions !== 'undefined') {
70	LiveForm.setOptions(window.LiveFormOptions);
71}
72
73LiveForm.isSpecialKey = function(k) {
74	// http://stackoverflow.com/questions/7770561/jquery-javascript-reject-control-keys-on-keydown-event
75	return (k == 20 /* Caps lock */
76		|| k == 16 /* Shift */
77		|| k == 9 /* Tab */
78		|| k == 27 /* Escape Key */
79		|| k == 17 /* Control Key */
80		|| k == 91 /* Windows Command Key */
81		|| k == 19 /* Pause Break */
82		|| k == 18 /* Alt Key */
83		|| k == 93 /* Right Click Point Key */
84		|| (k >= 35 && k <= 40) /* Home, End, Arrow Keys */
85		|| k == 45 /* Insert Key */
86		|| (k >= 33 && k <= 34) /*Page Down, Page Up */
87		|| (k >= 112 && k <= 123) /* F1 - F12 */
88		|| (k >= 144 && k <= 145)); /* Num Lock, Scroll Lock */
89}
90
91/**
92 * Handlers for all the events that trigger validation
93 * YOU CAN CHANGE these handlers (ie. to use jQuery events instead)
94 */
95LiveForm.setupHandlers = function(el) {
96	if (this.hasClass(el, this.options.disableLiveValidationClass))
97		return;
98
99	// Check if element was already initialized
100	if (el.getAttribute("data-lfv-initialized"))
101		return;
102
103	// Remember we initialized this element so we won't do it again
104	el.setAttribute('data-lfv-initialized', 'true');
105
106	var handler = function(event) {
107		event = event || window.event;
108		Nette.validateControl(event.target ? event.target : event.srcElement);
109	};
110
111	var self = this;
112
113	Nette.addEvent(el, "change", handler);
114	Nette.addEvent(el, "blur", handler);
115	Nette.addEvent(el, "keydown", function (event) {
116		if (!self.isSpecialKey(event.which) && (self.options.wait === false || self.options.wait >= 200)) {
117			// Hide validation span tag.
118			self.removeClass(self.getGroupElement(this), self.options.controlErrorClass);
119			self.removeClass(self.getGroupElement(this), self.options.controlValidClass);
120
121			var messageEl = self.getMessageElement(this);
122			messageEl.innerHTML = '';
123			messageEl.className = '';
124
125			// Cancel timeout to run validation handler
126			if (self.timeout) {
127				clearTimeout(self.timeout);
128			}
129		}
130	});
131	Nette.addEvent(el, "keyup", function(event) {
132		if (self.options.wait !== false) {
133			event = event || window.event;
134			if (event.keyCode !== 9) {
135				if (self.timeout) clearTimeout(self.timeout);
136					self.timeout = setTimeout(function() {
137					handler(event);
138				}, self.options.wait);
139			}
140		}
141	});
142};
143
144LiveForm.processServerErrors = function(el) {
145	var messageEl = this.getMessageElement(el);
146	var parentEl = this.getMessageParent(el); // This is parent element which contain the error elements
147
148	var errors = [];
149
150	// Find existing error elements by class (from server-validation)
151	var errorEls = parentEl.getElementsByClassName(this.options.messageErrorClass);
152	for (var i = errorEls.length - 1; i > -1; i--) {
153		// Don't touch our main message element
154		if (errorEls[i] == messageEl)
155			continue;
156
157		// Remove only direct children
158		var errorParent = errorEls[i].parentNode;
159		if (errorParent == parentEl) {
160			errors.push(errorEls[i].outerHTML);
161			errorParent.removeChild(errorEls[i]);
162		}
163	}
164
165	// Wrap all server errors into one element
166	if (errors.length > 0) {
167		messageEl.innerHTML = errors.join("");
168	}
169};
170
171LiveForm.addError = function(el, message) {
172	// Ignore elements with disabled live validation
173	if (this.hasClass(el, this.options.disableLiveValidationClass))
174		return;
175
176	var groupEl = this.getGroupElement(el);
177	this.setFormProperty(el.form, "hasError", true);
178	this.addClass(groupEl, this.options.controlErrorClass);
179
180	if (this.options.showValid) {
181		this.removeClass(groupEl, this.options.controlValidClass);
182	}
183
184	if (!message) {
185		message = '&nbsp;';
186	} else {
187		message = this.options.messageErrorPrefix + message;
188	}
189
190	var messageEl = this.getMessageElement(el);
191	messageEl.innerHTML = message;
192	messageEl.className = this.options.messageErrorClass;
193};
194
195LiveForm.removeError = function(el) {
196	// We don't want to remove any errors during onLoadValidation
197	if (this.getFormProperty(el.form, "onLoadValidation"))
198		return;
199
200	var groupEl = this.getGroupElement(el);
201	this.removeClass(groupEl, this.options.controlErrorClass);
202
203	var id = el.getAttribute('data-lfv-message-id');
204	if (id) {
205		var messageEl = this.getMessageElement(el);
206		messageEl.innerHTML = '';
207		messageEl.className = '';
208	}
209
210	if (this.options.showValid) {
211		if (this.showValid(el))
212			this.addClass(groupEl, this.options.controlValidClass);
213		else
214			this.removeClass(groupEl, this.options.controlValidClass);
215	}
216};
217
218LiveForm.showValid = function(el) {
219	if (el.type) {
220		var type = el.type.toLowerCase();
221		if (type == 'checkbox' || type == 'radio') {
222			return false;
223		}
224	}
225
226	var rules = Nette.parseJSON(el.getAttribute('data-nette-rules'));
227	if (rules.length == 0) {
228		return false;
229	}
230  
231	if (Nette.getEffectiveValue(el) == '') {
232		return false;
233	}
234
235	if (this.hasClass(el, this.options.disableShowValidClass)) {
236		return false;
237	}
238
239	return true;
240};
241
242LiveForm.getGroupElement = function(el) {
243	if (this.options.showMessageClassOnParent === false)
244		return el;
245
246	var groupEl = el;
247
248	while (!this.hasClass(groupEl, this.options.showMessageClassOnParent)) {
249		groupEl = groupEl.parentNode;
250
251		if (groupEl === null) {
252			return el;
253		}
254	}
255
256	return groupEl;
257}
258
259LiveForm.getMessageId = function(el) {
260	var tmp = el.id + this.options.messageIdPostfix;
261	
262	// For elements without ID, or multi elements (with same name), we must generate whole ID ourselves
263	if (el.name && (!el.id || !el.form.elements[el.name].tagName)) {
264		// Strip possible [] from name
265		var name = el.name.match(/\[\]$/) ? el.name.match(/(.*)\[\]$/)[1] : el.name;
266		// Generate new ID based on form ID, element name and messageIdPostfix from options
267		tmp = (el.form.id ? el.form.id : 'frm') + '-' + name + this.options.messageIdPostfix;
268	}
269	
270	// We want unique ID which doesn't exist yet
271	var id = tmp,
272	    i = 0;
273	while (document.getElementById(id)) {
274		id = id + '_' + ++i;
275	}
276
277	return id;
278}
279
280LiveForm.getMessageElement = function(el) {
281	// For multi elements (with same name) work only with first element attributes
282	if (el.name && el.name.match(/\[\]$/)) {
283		el = el.form.elements[el.name].tagName ? el : el.form.elements[el.name][0];
284	}
285
286	var id = el.getAttribute('data-lfv-message-id');
287	if (!id) {
288		// ID is not specified yet, let's create a new one
289		id = this.getMessageId(el);
290
291		// Remember this id for next use
292		el.setAttribute('data-lfv-message-id', id);
293	}
294
295	var messageEl = document.getElementById(id);
296	if (!messageEl) {
297		// Message element doesn't exist, lets create a new one
298		messageEl = document.createElement(this.options.messageTag);
299		messageEl.id = id;
300		if (el.style.display == 'none' && !this.hasClass(el, this.options.enableHiddenMessageClass)) {
301			messageEl.style.display = 'none';
302		}
303
304		var parentEl = this.getMessageParent(el);
305		if (parentEl) {
306			parentEl.appendChild(messageEl);
307		}
308	}
309
310	return messageEl;
311};
312
313LiveForm.getMessageParent = function(el) {
314	var parentEl = el.parentNode;
315	var parentFound = false;
316	
317	if (this.options.messageParentClass !== false) {
318		parentFound = true;
319		while (!this.hasClass(parentEl, this.options.messageParentClass)) {
320			parentEl = parentEl.parentNode;
321
322			if (parentEl === null) {
323				// We didn't found wanted parent, so use element's direct parent
324				parentEl = el.parentNode;
325				parentFound = false;
326				break;
327			}
328		}
329	}
330
331	// Don't append error message to radio/checkbox input's label, but along label
332	if (el.type) {
333		var type = el.type.toLowerCase();
334		if ((type == 'checkbox' || type == 'radio') && parentEl.tagName == 'LABEL') {
335			parentEl = parentEl.parentNode;
336		}
337	}
338
339	// For multi elements (with same name) use parent's parent as parent (if wanted one is not found)
340	if (!parentFound && el.name && !el.form.elements[el.name].tagName) {
341		parentEl = parentEl.parentNode; 
342	}
343
344	return parentEl;
345}
346
347LiveForm.addClass = function(el, className) {
348	if (!el.className) {
349		el.className = className;
350	} else if (!this.hasClass(el, className)) {
351		el.className += ' ' + className;
352	}
353};
354
355LiveForm.hasClass = function(el, className) {
356	if (el.className)
357		return el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
358	return false;
359};
360
361LiveForm.removeClass = function(el, className) {
362	if (this.hasClass(el, className)) {
363		var reg = new RegExp('(\\s|^)'+ className + '(\\s|$)');
364		var m = el.className.match(reg);
365		el.className = el.className.replace(reg, (m[1] == ' ' && m[2] == ' ') ? ' ' : '');
366	}
367};
368
369LiveForm.getFormProperty = function(form, propertyName) {
370	if (form == null || this.forms[form.id] == null)
371		return false;
372
373	return this.forms[form.id][propertyName];
374};
375
376LiveForm.setFormProperty = function(form, propertyName, value) {
377	if (form == null)
378		return;
379
380	if (this.forms[form.id] == null)
381		this.forms[form.id] = {};
382
383	this.forms[form.id][propertyName] = value;
384};
385
386////////////////////////////   modified netteForms.js   ///////////////////////////////////
387
388/**
389 * NetteForms - simple form validation.
390 *
391 * This file is part of the Nette Framework (https://nette.org)
392 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
393 */
394
395(function(global, factory) {
396	if (!global.JSON) {
397		return;
398	}
399
400	if (typeof define === 'function' && define.amd) {
401		define(function() {
402			return factory(global);
403		});
404	} else if (typeof module === 'object' && typeof module.exports === 'object') {
405		module.exports = factory(global);
406	} else {
407		var init = !global.Nette || !global.Nette.noInit;
408		global.Nette = factory(global);
409		if (init) {
410			global.Nette.initOnLoad();
411		}
412	}
413
414}(typeof window !== 'undefined' ? window : this, function(window) {
415
416'use strict';
417
418var Nette = {};
419
420// LiveForm: original netteForms.js code
421// Nette.formErrors = [];
422Nette.version = '2.4';
423
424
425/**
426 * Attaches a handler to an event for the element.
427 */
428Nette.addEvent = function(element, on, callback) {
429	if (element.addEventListener) {
430		element.addEventListener(on, callback);
431	} else if (on === 'DOMContentLoaded') {
432		element.attachEvent('onreadystatechange', function() {
433			if (element.readyState === 'complete') {
434				callback.call(this);
435			}
436		});
437	} else {
438		element.attachEvent('on' + on, getHandler(callback));
439	}
440};
441
442
443function getHandler(callback) {
444	return function(e) {
445		return callback.call(this, e);
446	};
447}
448
449
450/**
451 * Returns the value of form element.
452 */
453Nette.getValue = function(elem) {
454	var i;
455	if (!elem) {
456		return null;
457
458	} else if (!elem.tagName) { // RadioNodeList, HTMLCollection, array
459		return elem[0] ? Nette.getValue(elem[0]) : null;
460
461	} else if (elem.type === 'radio') {
462		var elements = elem.form.elements; // prevents problem with name 'item' or 'namedItem'
463		for (i = 0; i < elements.length; i++) {
464			if (elements[i].name === elem.name && elements[i].checked) {
465				return elements[i].value;
466			}
467		}
468		return null;
469
470	} else if (elem.type === 'file') {
471		return elem.files || elem.value;
472
473	} else if (elem.tagName.toLowerCase() === 'select') {
474		var index = elem.selectedIndex,
475			options = elem.options,
476			values = [];
477
478		if (elem.type === 'select-one') {
479			return index < 0 ? null : options[index].value;
480		}
481
482		for (i = 0; i < options.length; i++) {
483			if (options[i].selected) {
484				values.push(options[i].value);
485			}
486		}
487		return values;
488
489	} else if (elem.name && elem.name.match(/\[\]$/)) { // multiple elements []
490		var elements = elem.form.elements[elem.name].tagName ? [elem] : elem.form.elements[elem.name],
491			values = [];
492
493		for (i = 0; i < elements.length; i++) {
494			// LiveForm: original netteForms.js code
495			/*if (elements[i].type !== 'checkbox' || elements[i].checked) {
496				values.push(elements[i].value);
497			}*/
498			// LiveForm: addition
499			var value = elements[i].value;
500			if (elements[i].type === 'checkbox' && elements[i].checked) {
501				values.push(value);
502			} else if (elements[i].type !== 'checkbox' && value !== '') {
503				values.push(value);
504			}
505		}
506		return values;
507
508	} else if (elem.type === 'checkbox') {
509		return elem.checked;
510
511	} else if (elem.tagName.toLowerCase() === 'textarea') {
512		return elem.value.replace("\r", '');
513
514	} else {
515		return elem.value.replace("\r", '').replace(/^\s+|\s+$/g, '');
516	}
517};
518
519
520/**
521 * Returns the effective value of form element.
522 */
523Nette.getEffectiveValue = function(elem) {
524	var val = Nette.getValue(elem);
525	if (elem.getAttribute) {
526		if (val === elem.getAttribute('data-nette-empty-value')) {
527			val = '';
528		}
529	}
530	return val;
531};
532
533
534/**
535 * Validates form element against given rules.
536 */
537Nette.validateControl = function(elem, rules, onlyCheck, value, emptyOptional) {
538	// LiveForm: addition
539	// Fix for CheckboxList - validation rules are present always only on first input
540	if (elem.name && elem.name.match(/\[\]$/) && elem.type.toLowerCase() == 'checkbox') {
541		elem = elem.form.elements[elem.name].tagName ? elem : elem.form.elements[elem.name][0];
542	}
543	
544	elem = elem.tagName ? elem : elem[0]; // RadioNodeList
545	rules = rules || Nette.parseJSON(elem.getAttribute('data-nette-rules'));
546	value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value;
547
548	for (var id = 0, len = rules.length; id < len; id++) {
549		var rule = rules[id],
550			op = rule.op.match(/(~)?([^?]+)/),
551			curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem;
552
553		rule.neg = op[1];
554		rule.op = op[2];
555		rule.condition = !!rule.rules;
556
557		if (!curElem) {
558			continue;
559		} else if (rule.op === 'optional') {
560			emptyOptional = !Nette.validateRule(elem, ':filled', null, value);
561			continue;
562		} else if (emptyOptional && !rule.condition && rule.op !== ':filled') {
563			continue;
564		}
565
566		curElem = curElem.tagName ? curElem : curElem[0]; // RadioNodeList
567		var curValue = elem === curElem ? value : {value: Nette.getEffectiveValue(curElem)},
568			success = Nette.validateRule(curElem, rule.op, rule.arg, curValue);
569
570		if (success === null) {
571			continue;
572		} else if (rule.neg) {
573			success = !success;
574		}
575
576		if (rule.condition && success) {
577			if (!Nette.validateControl(elem, rule.rules, onlyCheck, value, rule.op === ':blank' ? false : emptyOptional)) {
578				return false;
579			}
580		} else if (!rule.condition && !success) {
581			if (Nette.isDisabled(curElem)) {
582				continue;
583			}
584			if (!onlyCheck) {
585				var arr = Nette.isArray(rule.arg) ? rule.arg : [rule.arg],
586					message = rule.msg.replace(/%(value|\d+)/g, function(foo, m) {
587						return Nette.getValue(m === 'value' ? curElem : elem.form.elements.namedItem(arr[m].control));
588					});
589				Nette.addError(curElem, message);
590			}
591			return false;
592		}
593	}
594
595	if (elem.type === 'number' && !elem.validity.valid) {
596		if (!onlyCheck) {
597			Nette.addError(elem, 'Please enter a valid value.');
598		}
599		return false;
600	}
601
602	// LiveForm: addition
603	if (!onlyCheck) {
604		LiveForm.removeError(elem);
605	}
606
607	return true;
608};
609
610
611/**
612 * Validates whole form.
613 */
614Nette.validateForm = function(sender, onlyCheck) {
615	var form = sender.form || sender,
616		scope = false;
617
618	// LiveForm: addition
619	LiveForm.setFormProperty(form, "hasError", false);
620
621	// LiveForm: original netteForms.js code
622	// Nette.formErrors = [];
623
624	if (form['nette-submittedBy'] && form['nette-submittedBy'].getAttribute('formnovalidate') !== null) {
625		var scopeArr = Nette.parseJSON(form['nette-submittedBy'].getAttribute('data-nette-validation-scope'));
626		if (scopeArr.length) {
627			scope = new RegExp('^(' + scopeArr.join('-|') + '-)');
628		} else {
629			// LiveForm: original netteForms.js code
630			// Nette.showFormErrors(form, []);
631			return true;
632		}
633	}
634
635	var radios = {}, i, elem;
636	// LiveForm: addition
637	var success = true;
638
639	for (i = 0; i < form.elements.length; i++) {
640		elem = form.elements[i];
641
642		if (elem.tagName && !(elem.tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1})) {
643			continue;
644
645		} else if (elem.type === 'radio') {
646			if (radios[elem.name]) {
647				continue;
648			}
649			radios[elem.name] = true;
650		}
651
652		if ((scope && !elem.name.replace(/]\[|\[|]|$/g, '-').match(scope)) || Nette.isDisabled(elem)) {
653			continue;
654		}
655
656		// LiveForm: addition
657		success = Nette.validateControl(elem) && success;
658		if (!success && !LiveForm.options.showAllErrors) {
659			break;
660		}
661		// LiveForm: original netteForms.js code
662		/*if (!Nette.validateControl(elem, null, onlyCheck) && !Nette.formErrors.length) {
663			return false;
664		}*/
665	}
666	// LiveForm: change
667	return success;
668
669	// LiveForm: original netteForms.js code
670	/*var success = !Nette.formErrors.length;
671	Nette.showFormErrors(form, Nette.formErrors);
672	return success;*/
673};
674
675
676/**
677 * Check if input is disabled.
678 */
679Nette.isDisabled = function(elem) {
680	if (elem.type === 'radio') {
681		for (var i = 0, elements = elem.form.elements; i < elements.length; i++) {
682			if (elements[i].name === elem.name && !elements[i].disabled) {
683				return false;
684			}
685		}
686		return true;
687	}
688	return elem.disabled;
689};
690
691// LiveForm: change
692/**
693 * Display error message.
694 */
695Nette.addError = function(elem, message) {
696	// LiveForm: addition
697	var noLiveValidation = LiveForm.hasClass(elem, LiveForm.options.disableLiveValidationClass);
698	// User explicitly disabled live-validation so we want to show simple alerts
699	if (noLiveValidation) {
700		// notify errors for elements with disabled live validation (but only errors and not during onLoadValidation)
701		if (message && !LiveForm.getFormProperty(elem.form, "hasError") && !LiveForm.getFormProperty(elem.form, "onLoadValidation")) {
702			alert(message);
703		}
704	}
705	if (elem.focus && !LiveForm.getFormProperty(elem.form, "hasError")) {
706		if (!LiveForm.focusing) {
707			LiveForm.focusing = true;
708			elem.focus();
709			setTimeout(function() {
710				LiveForm.focusing = false;
711
712				// Scroll by defined offset (if enabled)
713				// NOTE: We use it with setTimetout because IE9 doesn't always catch instant scrollTo request
714				var focusOffsetY = LiveForm.options.focusScreenOffsetY;
715				if (focusOffsetY !== false && elem.getBoundingClientRect().top < focusOffsetY) {
716					window.scrollBy(0, elem.getBoundingClientRect().top - focusOffsetY);
717				}
718			}, 10);
719		}
720	}
721	if (!noLiveValidation) {
722		LiveForm.addError(elem, message);
723	}
724};
725
726
727// LiveForm: original netteForms.js code
728/**
729 * Adds error message to the queue.
730 */
731/*Nette.addError = function(elem, message) {
732	Nette.formErrors.push({
733		element: elem,
734		message: message
735	});
736};*/
737
738
739// LiveForm: original netteForms.js code
740/**
741 * Display error messages.
742 */
743/*Nette.showFormErrors = function(form, errors) {
744	var messages = [],
745		focusElem;
746
747	for (var i = 0; i < errors.length; i++) {
748		var elem = errors[i].element,
749			message = errors[i].message;
750
751		if (!Nette.inArray(messages, message)) {
752			messages.push(message);
753
754			if (!focusElem && elem.focus) {
755				focusElem = elem;
756			}
757		}
758	}
759
760	if (messages.length) {
761		alert(messages.join('\n'));
762
763		if (focusElem) {
764			focusElem.focus();
765		}
766	}
767};*/
768
769
770/**
771 * Expand rule argument.
772 */
773Nette.expandRuleArgument = function(form, arg) {
774	if (arg && arg.control) {
775		var control = form.elements.namedItem(arg.control),
776			value = {value: Nette.getEffectiveValue(control)};
777		Nette.validateControl(control, null, true, value);
778		arg = value.value;
779	}
780	return arg;
781};
782
783
784/**
785 * Validates single rule.
786 */
787Nette.validateRule = function(elem, op, arg, value) {
788	value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value;
789
790	if (op.charAt(0) === ':') {
791		op = op.substr(1);
792	}
793	op = op.replace('::', '_');
794	op = op.replace(/\\/g, '');
795
796	var arr = Nette.isArray(arg) ? arg.slice(0) : [arg];
797	for (var i = 0, len = arr.length; i < len; i++) {
798		arr[i] = Nette.expandRuleArgument(elem.form, arr[i]);
799	}
800	return Nette.validators[op]
801		? Nette.validators[op](elem, Nette.isArray(arg) ? arr : arr[0], value.value, value)
802		: null;
803};
804
805
806Nette.validators = {
807	filled: function(elem, arg, val) {
808		if (elem.type === 'number' && elem.validity.badInput) {
809			return true;
810		}
811		return val !== '' && val !== false && val !== null
812			&& (!Nette.isArray(val) || !!val.length)
813			&& (!window.FileList || !(val instanceof window.FileList) || val.length);
814	},
815
816	blank: function(elem, arg, val) {
817		return !Nette.validators.filled(elem, arg, val);
818	},
819
820	valid: function(elem, arg, val) {
821		return Nette.validateControl(elem, null, true);
822	},
823
824	equal: function(elem, arg, val) {
825		if (arg === undefined) {
826			return null;
827		}
828
829		function toString(val) {
830			if (typeof val === 'number' || typeof val === 'string') {
831				return '' + val;
832			} else {
833				return val === true ? '1' : '';
834			}
835		}
836
837		val = Nette.isArray(val) ? val : [val];
838		arg = Nette.isArray(arg) ? arg : [arg];
839		loop:
840		for (var i1 = 0, len1 = val.length; i1 < len1; i1++) {
841			for (var i2 = 0, len2 = arg.length; i2 < len2; i2++) {
842				if (toString(val[i1]) === toString(arg[i2])) {
843					continue loop;
844				}
845			}
846			return false;
847		}
848		return true;
849	},
850
851	notEqual: function(elem, arg, val) {
852		return arg === undefined ? null : !Nette.validators.equal(elem, arg, val);
853	},
854
855	minLength: function(elem, arg, val) {
856		if (elem.type === 'number') {
857			if (elem.validity.tooShort) {
858				return false
859			} else if (elem.validity.badInput) {
860				return null;
861			}
862		}
863		return val.length >= arg;
864	},
865
866	maxLength: function(elem, arg, val) {
867		if (elem.type === 'number') {
868			if (elem.validity.tooLong) {
869				return false
870			} else if (elem.validity.badInput) {
871				return null;
872			}
873		}
874		return val.length <= arg;
875	},
876
877	length: function(elem, arg, val) {
878		if (elem.type === 'number') {
879			if (elem.validity.tooShort || elem.validity.tooLong) {
880				return false
881			} else if (elem.validity.badInput) {
882				return null;
883			}
884		}
885		arg = Nette.isArray(arg) ? arg : [arg, arg];
886		return (arg[0] === null || val.length >= arg[0]) && (arg[1] === null || val.length <= arg[1]);
887	},
888
889	email: function(elem, arg, val) {
890		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);
891	},
892
893	url: function(elem, arg, val, value) {
894		if (!(/^[a-z\d+.-]+:/).test(val)) {
895			val = 'http://' + val;
896		}
897		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)) {
898			value.value = val;
899			return true;
900		}
901		return false;
902	},
903
904	regexp: function(elem, arg, val) {
905		var parts = typeof arg === 'string' ? arg.match(/^\/(.*)\/([imu]*)$/) : false;
906		try {
907			return parts && (new RegExp(parts[1], parts[2].replace('u', ''))).test(val);
908		} catch (e) {}
909	},
910
911	pattern: function(elem, arg, val) {
912		try {
913			return typeof arg === 'string' ? (new RegExp('^(?:' + arg + ')$')).test(val) : null;
914		} catch (e) {}
915	},
916
917	integer: function(elem, arg, val) {
918		if (elem.type === 'number' && elem.validity.badInput) {
919			return false;
920		}
921		return (/^-?[0-9]+$/).test(val);
922	},
923
924	'float': function(elem, arg, val, value) {
925		if (elem.type === 'number' && elem.validity.badInput) {
926			return false;
927		}
928		val = val.replace(' ', '').replace(',', '.');
929		if ((/^-?[0-9]*[.,]?[0-9]+$/).test(val)) {
930			value.value = val;
931			return true;
932		}
933		return false;
934	},
935
936	min: function(elem, arg, val) {
937		if (elem.type === 'number') {
938			if (elem.validity.rangeUnderflow) {
939				return false
940			} else if (elem.validity.badInput) {
941				return null;
942			}
943		}
944		return Nette.validators.range(elem, [arg, null], val);
945	},
946
947	max: function(elem, arg, val) {
948		if (elem.type === 'number') {
949			if (elem.validity.rangeOverflow) {
950				return false
951			} else if (elem.validity.badInput) {
952				return null;
953			}
954		}
955		return Nette.validators.range(elem, [null, arg], val);
956	},
957
958	range: function(elem, arg, val) {
959		if (elem.type === 'number') {
960			if ((elem.validity.rangeUnderflow && arg[0] !== null) || (elem.validity.rangeOverflow && arg[1] !== null)) {
961				return false
962			} else if (elem.validity.badInput) {
963				return null;
964			}
965		}
966		return Nette.isArray(arg) ?
967			((arg[0] === null || parseFloat(val) >= arg[0]) && (arg[1] === null || parseFloat(val) <= arg[1])) : null;
968	},
969
970	submitted: function(elem, arg, val) {
971		return elem.form['nette-submittedBy'] === elem;
972	},
973
974	fileSize: function(elem, arg, val) {
975		if (window.FileList) {
976			for (var i = 0; i < val.length; i++) {
977				if (val[i].size > arg) {
978					return false;
979				}
980			}
981		}
982		return true;
983	},
984
985	image: function (elem, arg, val) {
986		if (window.FileList && val instanceof window.FileList) {
987			for (var i = 0; i < val.length; i++) {
988				var type = val[i].type;
989				if (type && type !== 'image/gif' && type !== 'image/png' && type !== '
989image/jpeg') {
990					return false;
991				}
992			}
993		}
994		return true;
995	}
996};
997
998
999/**
1000 * Process all toggles in form.
1001 */
1002Nette.toggleForm = function(form, elem) {
1003	var i;
1004	Nette.toggles = {};
1005	for (i = 0; i < form.elements.length; i++) {
1006		if (form.elements[i].tagName.toLowerCase() in {input: 1, select: 1, textarea: 1, button: 1}) {
1007			Nette.toggleControl(form.elements[i], null, null, !elem);
1008		}
1009	}
1010
1011	for (i in Nette.toggles) {
1012		Nette.toggle(i, Nette.toggles[i], elem);
1013	}
1014};
1015
1016
1017/**
1018 * Process toggles on form element.
1019 */
1020Nette.toggleControl = function(elem, rules, success, firsttime, value) {
1021	rules = rules || Nette.parseJSON(elem.getAttribute('data-nette-rules'));
1022	value = value === undefined ? {value: Nette.getEffectiveValue(elem)} : value;
1023
1024	var has = false,
1025		handled = [],
1026		handler = function () {
1027			Nette.toggleForm(elem.form, elem);
1028		},
1029		curSuccess;
1030
1031	for (var id = 0, len = rules.length; id < len; id++) {
1032		var rule = rules[id],
1033			op = rule.op.match(/(~)?([^?]+)/),
1034			curElem = rule.control ? elem.form.elements.namedItem(rule.control) : elem;
1035
1036		if (!curElem) {
1037			continue;
1038		}
1039
1040		curSuccess = success;
1041		if (success !== false) {
1042			rule.neg = op[1];
1043			rule.op = op[2];
1044			var curValue = elem === curElem ? value : {value: Nette.getEffectiveValue(curElem)};
1045			curSuccess = Nette.validateRule(curElem, rule.op, rule.arg, curValue);
1046			if (curSuccess === null) {
1047				continue;
1048
1049			} else if (rule.neg) {
1050				curSuccess = !curSuccess;
1051			}
1052			if (!rule.rules) {
1053				success = curSuccess;
1054			}
1055		}
1056
1057		if ((rule.rules && Nette.toggleControl(elem, rule.rules, curSuccess, firsttime, value)) || rule.toggle) {
1058			has = true;
1059			if (firsttime) {
1060				var oldIE = !document.addEventListener, // IE < 9
1061					name = curElem.tagName ? curElem.name : curElem[0].name,
1062					els = curElem.tagName ? curElem.form.elements : curElem;
1063
1064				for (var i = 0; i < els.length; i++) {
1065					if (els[i].name === name && !Nette.inArray(handled, els[i])) {
1066						Nette.addEvent(els[i], oldIE && els[i].type in {checkbox: 1, radio: 1} ? 'click' : 'change', handler);
1067						handled.push(els[i]);
1068					}
1069				}
1070			}
1071			for (var id2 in rule.toggle || []) {
1072				if (Object.prototype.hasOwnProperty.call(rule.toggle, id2)) {
1073					Nette.toggles[id2] = Nette.toggles[id2] || (rule.toggle[id2] ? curSuccess : !curSuccess);
1074				}
1075			}
1076		}
1077	}
1078	return has;
1079};
1080
1081
1082Nette.parseJSON = function(s) {
1083	return (s || '').substr(0, 3) === '{op'
1084		? eval('[' + s + ']') // backward compatibility with Nette 2.0.x
1085		: JSON.parse(s || '[]');
1086};
1087
1088
1089/**
1090 * Displays or hides HTML element.
1091 */
1092Nette.toggle = function(id, visible, srcElement) {
1093	var elem = document.getElementById(id);
1094	if (elem) {
1095		elem.style.display = visible ? '' : 'none';
1096	}
1097};
1098
1099
1100/**
1101 * Setup handlers.
1102 */
1103Nette.initForm = function(form) {
1104	form.noValidate = 'novalidate';
1105
1106	// LiveForm: addition
1107	LiveForm.forms[form.id] = {
1108		hasError: false,
1109		onLoadValidation: false
1110	};
1111
1112	Nette.addEvent(form, 'submit', function(e) {
1113		if (!Nette.validateForm(form)) {
1114			if (e && e.stopPropagation) {
1115				e.stopPropagation();
1116				e.preventDefault();
1117			} else if (window.event) {
1118				event.cancelBubble = true;
1119				event.returnValue = false;
1120			}
1121		}
1122	});
1123
1124	Nette.toggleForm(form);
1125
1126	// LiveForm: addition
1127	for (var i = 0; i < form.elements.length; i++) {
1128		LiveForm.setupHandlers(form.elements[i]);
1129		LiveForm.processServerErrors(form.elements[i]);
1130	}
1131};
1132
1133
1134/**
1135 * @private
1136 */
1137Nette.initOnLoad = function() {
1138	Nette.addEvent(document, 'DOMContentLoaded', function() {
1139		for (var i = 0; i < document.forms.length; i++) {
1140			var form = document.forms[i];
1141			for (var j = 0; j < form.elements.length; j++) {
1142				if (form.elements[j].getAttribute('data-nette-rules')) {
1143					Nette.initForm(form);
1144
1145					// LiveForm: addition
1146					if (LiveForm.hasClass(form, 'validate-on-load')) {
1147						// This is not so nice way, but I don't want to spoil validateForm, validateControl and other methods with another parameter
1148						LiveForm.setFormProperty(form, "onLoadValidation", true);
1149						Nette.validateForm(form);
1150						LiveForm.setFormProperty(form, "onLoadValidation", false);
1151					}
1152
1153					break;
1154				}
1155			}
1156		}
1157
1158		Nette.addEvent(document.body, 'click', function(e) {
1159			var target = e.target || e.srcElement;
1160			if (target.form && target.type in {submit: 1, image: 1}) {
1161				target.form['nette-submittedBy'] = target;
1162			}
1163		});
1164	});
1165};
1166
1167
1168/**
1169 * Determines whether the argument is an array.
1170 */
1171Nette.isArray = function(arg) {
1172	return Object.prototype.toString.call(arg) === '[object Array]';
1173};
1174
1175
1176/**
1177 * Search for a specified value within an array.
1178 */
1179Nette.inArray = function(arr, val) {
1180	if ([].indexOf) {
1181		return arr.indexOf(val) > -1;
1182	} else {
1183		for (var i = 0; i < arr.length; i++) {
1184			if (arr[i] === val) {
1185				return true;
1186			}
1187		}
1188		return false;
1189	}
1190};
1191
1192
1193/**
1194 * Converts string to web safe characters [a-z0-9-] text.
1195 */
1196Nette.webalize = function(s) {
1197	s = s.toLowerCase();
1198	var res = '', i, ch;
1199	for (i = 0; i < s.length; i++) {
1200		ch = Nette.webalizeTable[s.charAt(i)];
1201		res += ch ? ch : s.charAt(i);
1202	}
1203	return res.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
1204};
1205
1206Nette.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'};
1207
1208return Nette;
1209}));

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.