PageSourceSearch

https://aschersleben-zoo.de/output/js/ikiss/formulare/formular.js

js aschersleben-zoo.de collected 2026-10-02 21:36:56 UTC 47,266 bytes, 1,478 lines download raw bytes

1/**
2* iKISS Formular-Modul (Mod 8.)
3* 
4* @param params
5*/
6var mailform = function( params ) {
7	this.constructor(params);
8};
9
10mailform.prototype = {
11
12	fid : null,
13	la: 1,
14	prefix : null,
15	id : null,
16	rootNode : null,
17	cls : {
18		infofield  : "info-field",
19		infolabel  : "info-label",
20		errorfield : "error-field",
21		errorlabel : "error-label",
22		transition : "transition"
23	},
24	msg : [],
25	tx : {
26		required : "ist ein Pflichtfeld",
27		validate : "hat ein ungültiges Format"
28	},
29	isMobile : false,
30	
31	ikissValidateAttribute: 'data-ikiss-validate',
32	
33	ikissCustomValidateAttribute: 'data-ikiss-custom-validate',
34		
35	ikissValidateAttributeChars: 'data-ikiss-validate-chars',
36	
37	fieldErrorCls: 'ikiss-mailform-field-error',
38	
39	wrapErrorCls: 'ikiss-mailform-field-wrap-error',
40	
41	labelErrorCls: 'ikiss-mailform-field-label-error',
42
43	buttonDisabledCls: 'ikiss-mailform-button-disabled',
44	
45	checkboxMulti: {},
46	
47	radioMulti: {},
48	
49	suspendCheckboxSync: false,
50	
51	customRules: {},
52	
53	hasNativeFileReader: !!(window.File && window.FileReader && window.FileList),
54	
55	hideValidationSummary: false,
56	
57	hideInlineValidationMessages: false,
58
59	minSubmitTime: 0,
60	minSubmitTimeLeft: 0,
61	minSubmitTimeInitialized: false,
62	minSubmitTimeTokenFetched: null,
63	minSubmitTimeTokenFetchError: '',
64
65	idCounter: 0,
66	submitButtonSelector: 'input[type="submit"], button[type="submit"]',
67	eventProxyInputSelector: 'input, textarea, select',
68	securityCheckStep: false,
69	captchaExists: false,
70	captchaInitialized: false,
71	blockingMechanisms: [],
72	blockingMechanismMessages: {},
73	
74	errMessages: {
75		'summaryhead'	: 'Bitte korrigieren Sie die folgenden Fehler, damit das Formular abgesendet werden kann:',
76		'text'			: 'Bitte füllen Sie das Feld {name} aus.',
77		'email'			: 'Bitte geben Sie eine E-Mail-Adresse in das Feld {name} ein.',
78		'email_invalid'	: 'Bitte geben Sie eine gültige E-Mail-Adresse in das Feld {name} ein.',
79		'date'			: 'Bitte geben Sie ein Datum in das Feld {name} ein.',
80		'datetime-local': 'Bitte geben Sie ein Datum in das Feld {name} ein.',
81		'date_invalid'	: 'Ihre Eingabe im Feld {name} befindet sich außerhalb des zulässigen Bereichs.',
82		'url'			: 'Bitte geben Sie eine Internetadresse in das Feld {name} ein.<br>Diese sollte mit &bdquo;https://&ldquo; oder &bdquo;http://&ldquo; beginnen.',
83		'ip'			: 'Bitte geben Sie eine IP-Adresse in das Feld {name} ein.',
84		'phone'			: 'Bitte geben Sie eine Telefonnummer in das Feld {name} ein.',
85		'fkzahl'        : 'Bitte geben Sie eine Zahl in das Feld {name} ein. Ein Komma zur Ganzzahl-Trennung ist erlaubt.',
86		'file'			: 'Bitte wählen Sie eine Datei im Feld {name} aus.',
87		'minlength'  	: 'Bitte geben Sie mindestens {reqChars} Zeichen in das Feld {name} ein. Derzeit verwenden Sie {curChars} Zeichen.',
88		'maxlength'  	: 'Bitte geben Sie höchstens {reqChars} Zeichen in das Feld {name} ein. Derzeit verwenden Sie {curChars} Zeichen.',
89		'strictlength'  : 'Bitte geben Sie genau {reqChars} Zeichen in das Feld {name} ein. Derzeit verwenden Sie {curChars} Zeichen.',
90		'checkbox'		: 'Bitte wählen Sie das Kontrollkästchen im Bereich {name} aus.',
91		'checkboxmulti'	: 'Bitte wählen Sie mindestens ein Kontrollkästchen im Bereich {name} aus.',
92		'radio'			: 'Bitte wählen Sie die Option im Bereich {name} aus.',
93		'radiomulti'	: 'Bitte wählen Sie eine Option im Bereich {name} aus.',
94		'chars'			: 'Bitte verwenden Sie im Feld {name} nur {chars}.',
95		'accept'		: 'Das Dateiformat der Datei {filename} ist nicht erlaubt.',
96		'maxfilesize'	: 'Die Datei {filename}
96 überschreitet die zugelassene Größe.',
97		'pattern'		: 'Bitte verwenden Sie im Feld {name} das vorgegebene Format.',
98		'select'		: 'Bitte wählen Sie einen Eintrag im Feld {name} aus.',
99		'selectmulti'	: 'Bitte wählen Sie mindestens einen Eintrag im Feld {name} aus.',
100		'number'		: 'Bitte verwenden Sie nur Zahlen im Feld {name}.',
101		'number_under'	: 'Bitte geben Sie im Feld {name} eine Zahl, die größer ist als {min}.',
102		'number_over' 	: 'Bitte geben Sie im Feld {name} eine Zahl, die kleiner ist als {max}.',
103		'number_between': 'Bitte geben Sie im Feld {name} eine Zahl zwischen {min} und {max} ein.',
104		'number_whole'	: 'Bitte verwenden Sie im Feld {name} nur ganze Zahlen ohne Nachkommastellen.',
105		'number_onedig' : 'Bitte verwenden Sie im Feld {name} maximal eine Nachkommastelle.',
106		'number_twodig' : 'Bitte verwenden Sie im Feld {name} maximal zwei Nachkommastellen.',
107		'number_threedig' : 'Bitte verwenden Sie im Feld {name} maximal drei Nachkommastellen.',
108		'mintime_single': 'Sie k&ouml;nnen in {time} Sekunde fortfahren.',
109		'mintime_multi'	: 'Sie k&ouml;nnen in {time} Sekunden fortfahren.',
110		'click_form'    : 'Klicken Sie einfach in das Formular, um zu beginnen.'
111	},
112	
113	errMessages_en: {
114		'summaryhead'	: 'Please correct the following errors so that the form can be submitted:',
115		'text'			: 'Please fill out the field {name}.',
116		'email'			: 'Please enter an email adress in field {name}.',
117		'email_invalid'	: 'Please enter a valid email adress in field {name}.',
118		'date'			: 'Please enter a date in field {name}.',
119		'datetime-local': 'Please enter a date in field {name}.',
120		'date_invalid'	: 'Your entry in field {name} is outside the permitted range.',
121		'url'			: 'Please enter a URL in field {name}.',
122		'ip'			: 'Please enter an ip address in field {name}.',
123		'phone'			: 'Please enter a phone number in field {name}.',
124		'fkzahl'		: 'Please enter a number in field {name}. If needed, use a comma as a decimal separator.',
125		'file'			: 'Please choose a file in field {name}.',
126		'minlength' 	: 'Please enter at least {reqChars} characters in field {name}. You are currently using {curChars} characters.',
127		'maxlength' 	: 'Please enter no more than {reqChars} characters in field {name}. You are currently using {curChars} characters.',
128		'strictlength' 	: 'Please enter exactly {reqChars} characters in field {name}. You are currently using {curChars} characters.',
129		'checkbox'		: 'Please check the checkbox in {name}.',
130		'checkboxmulti'	: 'Please select at least one checkbox in {name}.',
131		'radio'			: 'Please select an option in {name}.',
132		'radiomulti'	: 'Please select an option in {name}.',
133		'chars'			: 'Please use only {chars} in field {name}.',
134		'accept'		: 'File format of {filename} is not allowed.',
135		'maxfilesize'	: 'File {filename} exceeds maximum allowed size.',
136		'pattern'		: 'Please match the requested format in field {name}.',
137		'select'		: 'Please select an item in the list in field {name}.',
138		'selectmulti'	: 'Please select at least one item in the list in field {name}.',
139		'number'		: 'Please enter a number in field {name}.',
140		'number_under'	: 'Please enter a number greater than {min} in field {name}.',
141		'number_over' 	: 'Please enter a number less than {min} in field {name}.',
142		'number_between': 'Please enter a number between {min} and {max} in field {name}.',
143		'number_whole'	: 'Please only use whole number in field {name}.',
144		'number_onedig' : 'Please only use a maximum of one decimal place in field {name}.',
145		'number_twodig' : 'Please only use a maximum of two decimal placed in field {name}.',
146		'number_threedig' : 'Please only use a maximum of three decimal placed in field {name}.',
147		'mintime_single': 'You can proceed in {time} second.',
148		'mintime_multi'	: 'You can proceed in {time} seconds.',
149		'click_form'    : 'Please click within the form to start.'
150	},
151	
152
153	/**
154	* Konstruktor
155	* 
156	* @param params
157	* @returns object
158	*/
159	constructor: function( params ) {
160		var me  = this;
161
162		//$(...).on() Polyfill für jQuery <1.7
163		$.fn.on = $.fn.on || function(events, fn) { return this.bind(events, fn); }
164		
165		//Interner Zähler für Fehlermeldungen für ID Zuweisung
166		window.ikissFormErrCounter = window.ikissFormErrCounter || 1;
167		
168		//Interner Zähler für ID Vergabe für das aria-describedby Attribut
169		window.ikissFormDescribedByCounter = window.ikissFormDescribedByCounter || 1; 
170		
171		$.extend(me, params);
172		
173		me.fid = me.fid || '';
174		
175		for (var a in me.cls)
176			me.cls[a] = me.prefix + "-" + me.cls[a];
177
178		me.checkPlatform();
179		
180		if (!me.rootNode) {
181			me.id = me.prefix + "-" + me.fid.replace(/\./g, "-");
182			me.rootNode = $("#" + me.id);	
183		}
184
185		me.la = (me.fid.split('.') || []).pop();
186		if (me.la == 2) {
187			me.errMessages = me.errMessages_en; 	
188		}
189		
190		me.rootNode
191			.on("submit", $.proxy(me.onSubmit, me))
192			.on("reset", $.proxy(me.onReset, me))
193			.on("pointerdown", $.proxy(me.onFocusSecurity, me))
194			.data("ikissMailform", me);
195		
196		
197		if ($('input[type=submit]', me.rootNode).length) {
198			$('input[type=submit]', me.rootNode).on('click', $.proxy(me.onBeforeSubmit, me));	
199		} else {
200			$('button', me.rootNode).on('click', $.proxy(me.onBeforeSubmit, me));
201		}
202		
203		$(me.eventProxyInputSelector, me.rootNode)
204			.on('invalid', $.proxy(me.onInvalid, me))
205			.on('input', $.proxy(me.onInput, me))
206			.on('blur', $.proxy(me.onBlur, me))
207			.on('focus', $.proxy(me.onFocus, me));
208
209		window.addEventListener('pageshow', me.onPageShow.bind(me));
210		
211		// 0 Werte in Selectfeldern auf Leerstring setzen
212		$('select[required][size=1]', me.rootNode).each(function() {
213			$('option[value=0]', this).attr('value', '');	
214		});
215		
216		//Attribut name bei Schaltflächen entfernen #13046
217		$('input[type=submit]', me.rootNode).removeAttr('name');
218
219		//Mehrfachcheckboxen zusammentragen
220		me.collectCheckboxes();
221		me.collectRadiobuttons();
222		me.moveReqMarkerText();
223		me.setAriaDescribedBy();
224		me.urlAutoFill();
225		me.securityInit();
226		me.initResetableFileUploads();
227
228		// Alle direkten P Elemente in dem Div .ikiss-form-fieldgroup ohne Klasse sollten die Klasse 'ikiss-field-wrapper' erhalten
229		$('.ikiss-form-fieldgroup > p:not([class]), .ikiss-form-fieldgroup > fieldset > p:not([class])', me.rootNode).addClass('ikiss-field-wrapper');
230
231		return me;
232	},
233	
234	onInvalid: function(ev) {
235		var me		= this,
236			fld		= ev.target,
237			err 	= me.getValidationErrors(fld),
238			errText = me.getErrorText(fld);
239		
240		me.addInlineMessage(fld, errText);	
241	},
242	
243	onInput: function(ev) {
244		var fld		= ev.target,
245			valObj	= fld.validity,
246			type 	= $(fld).attr('type');
247		
248		if (valObj.customError == true || fld.validateOnInput) { //Feld hat eine Fehlermeldung
249			var err = this.getValidationErrors(fld);
250			if (!err.length) {
251				this.removeInlineMessage(fld);
252			} else {
253				this.onInvalid({target: fld});	
254			}
255		}
256		
257		if (this.hasNativeFileReader && type == 'file') {
258			this.validateField(fld);
259		}
260	},
261	
262	onFocus: function(ev) {
263		var fld		= ev.target,
264			valObj	= fld.validity,
265			me		= this;
266		
267		if (valObj.customError == true) {
268			fld.validateOnInput = true;	
269		}
270
271		me.onFocusSecurity();
272	},
273
274	onFocusSecurity: function () {
275		var me = this;
276
277		// Sollen in diesem Schritt Sicherheitschecks stattfinden?
278		if (me.securityCheckStep) {
279			// #14095
280			if (me.minSubmitTime && (typeof(newsletterAnAbmeldung) == 'undefined')) {
281				// Reset nach History-Back.
282				if (me.minSubmitTimeInitialized && !document.cookie.match(/(^|;\s*)ft=/)) {
283					me.minSubmitTimeInitialized = false;
284				}
285
286				if (!me.minSubmitTimeInitialized) {
287					me.minSubmitTimeInitialized = true;
288					me.setMinSubmitTimeCookie('loading');
289					me.minSubmitTimeInit();
290				}
291			}
292
293			// #14251
294			if (me.captchaExists && !me.captchaInitialized) {
295				me.toggleSubmitButtons(false, 'captcha', 'Bitte warten Sie auf die L&ouml;sung des Captchas.');
296				me.captchaInitialized = true;
297                if (me.getCaptchaSolution()) me.toggleSubmitButtons(true, 'captcha'); // TT13927
298			}
299		}
300	},
301	
302	onBlur: function(ev) {
303		var fld	= ev.target,
304			err = this.getValidationErrors(fld);
305		
306		fld.validateOnInput = false;
307		
308		if (!err.length) {
309			this.removeInlineMessage(fld);
310		} else {
311			if (err[0] !== 'valueMissing') // #14776 - Keine Valdierung bei onBlur für required
312				this.onInvalid({target: fld});
313		}
314	},
315	
316	onBeforeSubmit: function(ev) {
317		var me		= this,
318			invalid = me.extendedValidation(),
319			trg		= me.rootNode[0];
320
321		if ($(ev.target).hasClass(me.buttonDisabledCls)) {
322			ev.preventDefault();
323			return false;
324		}
325
326		if (trg.checkValidity() === false || invalid) {     
327			this.showValidationSummary();
328		  
329			if (this.hideInlineValidationMessages != true) {
330				ev.preventDefault();
331				return false;
332			}
333		}
334	},
335	
336	onSubmit: function(ev) {
337		var me		= this,
338			invalid	= me.extendedValidation();
339		
340		if (ev.target.checkValidity() === false || invalid) {
341		  ev.preventDefault();
342		  return false;
343		}
344	},
345	
346	onReset: function() {
347		var me	= this,
348			rn	= me.rootNode,
349			flds = rn[0].elements;
350		
351		
352		//Bisherige Fehlerzusammenfassung entfernen
353		if (me.hideValidationSummary != true) {
354			$('.ikiss-mailform-validation-summary', rn).off('click').remove();	
355		}
356			
357		for (var i=0; i < flds.length; i++) {
358			this.removeInlineMessage(flds[i]);
359			flds[i].setCustomValidity('');	
360		}
361		
362		$('input:invalid, textarea:invalid, select:invalid', rn).each(function(idx, fld) {
363			me.onInvalid({target: fld});
364		});
365	},
366	
367	onPageShow: function (ev) {
368		var me = this;
369
370		if (me.securityCheckStep && me.captchaExists && me.captchaInitialized && me.getCaptchaSolution()) {
371			// Es gibt schon eine Captcha-Lösung, obwohl die Seite jetzt erst angezeigt wird.
372			// -> Der Nutzer ist in der Browser-Historie zurückgegangen, z.B. über "Eingaben korrigieren".
373			// -> Deswegen Reset des Captcha-Widgets, damit eine neue Lösung generiert wird.
374			// (Nur wenn es genau ein Captcha insgesamt gibt,
375			// und window.friendlyChallenge.autoWidget somit garantiert das richtige ist.)
376			if ($('div.frc-captcha').length === 1 && window.friendlyChallenge && window.friendlyChallenge.autoWidget) {
377				window.friendlyChallenge.autoWidget.reset();
378				
379				me.captchaInitialized = false;
380				// Hier wird der Handler, der auf die Captcha-Lösung wartet, erstellt.
381				me.securityInit();
382				$(me.eventProxyInputSelector, me.rootNode).trigger('focus');
383			}
384		}
385
386		if (document.activeElement && me.rootNode.length
387			&& $(document.activeElement).closest('form').is(me.rootNode)) {
388
389			// Wenn beim Anzeigen der Seite schon ein Element innerhalb von rootNode fokussiert ist
390			// (z.B. durch automatisches Ausfüllen), onFocusSecurity() sofort aufrufen.
391			me.onFocusSecurity();
392		}
393	},
394	
395	showValidationSummary: function() {
396		var me	= this,
397			err = [];
398		
399		if (me.hideValidationSummary == true)
400			return;
401		
402		//Bisherige Fehlerzusammenfassung entfernen
403		$('.ikiss-mailform-validation-summary', me.rootNode).off('click').remove();
404		
405		//Fehler zusammentragen
406		var collected = {};
407		$('input:invalid, textarea:invalid, select:invalid', me.rootNode).each(function(idx, fld) {
408			var na = $(fld).attr('name');
409			if (!collected[na]) {
410				err.push({id: $(fld).attr('id'), msg: me.getErrorText(fld) });
411				collected[na] = 1;	
412			}
413			
414		});
415		
416		//Fehlerzusammenfassung ausgeben 
417		if (err.length) {
418			var cnt 	= window.ikissFormErrCounter++,
419				descId	= 'ikiss-error-description-'+cnt,
420				msgId	= 'ikiss-error-message-'+cnt;
421				
422			var html = [
423				'<div class="ikiss-mailform-validation-summary" aria-labelledby="'+msgId+'" aria-describedby="'+descId+'" aria-live="polite" tabindex="0">', //role="alert"  
424					'<p class="ikiss-mailform-validation-headline" id="'+msgId+'">'+me.errMessages.summaryhead+'</p>',
425						'<ul class="ikiss-mailform-validation-summary-list" id="'+descId+'">'
426			];
427			
428			for (var i=0; i < err.length; i++) 
429				html.push('<li><a href="#'+err[i].id+'">'+err[i].msg+'</a></li>');	
430			
431			html.push('</ul></div>');
432			var firstEl = me.getFirstFormRow();
433			
434			$(firstEl).before(html.join(''));
435			
436			var sum 	= $('.ikiss-mailform-validation-summary', me.rootNode),
437				scTop	= parseInt($('.ikiss-mailform-validation-headline', sum).css('scroll-margin-top'), 10) || 0;
438			$('html, body').animate({scrollTop: parseInt(sum.offset().top-10, 10) - scTop}, 500);
439			sum.focus();
440			
441			$('a', sum).on('click', me.onSummaryClick);
442		}	
443	}, 
444	
445	onSummaryClick: function(ev) {
446		var $trg = $(ev.target);
447		if ($trg.is('a')) {
448			var trgFld = $($trg.attr('href')),
449				scTop	= parseInt(trgFld.css('scroll-margin-top'), 10) || 0;
450			$('html, body').animate({scrollTop: parseInt(trgFld.offset().top-10, 10) - scTop}, 500);
451			trgFld.focus();
452			ev.preventDefault();					
453		}	
454	},
455	
456	getFirstFormRow: function() {
457		var rn			= this.rootNode,
458			firstFld	= $('.ikiss-form-element:first', rn),
459			fldSet		= firstFld.closest('fieldset', rn[0]),
460			errCt		= $('.ikiss-field-error-container', rn),
461			acc			= firstFld.closest('.accordion-container, .toggler-container', rn[0]); 
462		
463		if (errCt.length) {
464			var block = $(errCt).closest('p,div', rn[0]);
465			if (block.length) {
466				block.hide();
467				return block;
468			} else {
469				errCt.hide();
470				return errCt;	
471			}
472		}
473		
474		return (acc.length && acc.prev(':header')) || (fldSet.length && fldSet) || firstFld.parentsUntil(rn, 'p, div').last();	
475	},
476	
477	extendedValidation: function() {
478		var me	= this,
479			rn	= me.rootNode,
480			err = false;
481		
482		var flds = rn[0].elements;
483		
484		for (var i=0; i < flds.length; i++) {
485			me.validateField(flds[i]);
486			
487			var vArr = me.getValidationErrors(flds[i]);
488			if (vArr.length) {
489				//err = true;	
490			}
491				
492			//err = err || ().length !== 0;
493		}
494		
495		if (me.validateCustom) {
496			var isValid = me.validateCustom();
497			if (!isValid) {
498				err = true;	
499			}	
500		}
501		
502		return err;	
503	},
504	
505	addCustomRule: function(ruleObj) {
506		var me	= this,
507			rn	= me.rootNode,
508			fld = ruleObj.field;
509		
510		me.customRules[ruleObj.name] = ruleObj;	
511		me.errMessages[ruleObj.name] = ruleObj.message; 
512		
513		if (fld) {
514			var cAttr = $(fld, rn).attr(me.ikissCustomValidateAttribute);
515			if (cAttr) {
516				$(fld, rn).attr(me.ikissCustomValidateAttribute, cAttr + '|' + ruleObj.name);
517			} else {
518				$(fld, rn).attr(me.ikissCustomValidateAttribute, ruleObj.name);	
519			}
520			
521		}	
522	},
523	
524	validateField: function(fld) {
525		if (!fld || !fld.willValidate)
526			return; 
527		
528		var val		= $(fld).val(),
529			type	= $(fld).attr('type');
530		
531		//Prüfung auf genaue Zeichenlänge
532		if ($(fld).attr('strictlength')) {
533			if (val.length == 0) {
534				fld.validity.strictlengthError = $(fld).attr('required') ? true : false;	
535			} else {
536				fld.validity.strictlengthError = (val.length != $(fld).attr('strictlength'));	
537			}
538
539			if (fld.validity.strictlengthError)
540				this.onInvalid({target: fld});	
541		}	
542		
543		//Prüfung auf Mindestlänge
544		if ($(fld).attr('minlength') && val.length != 0) {
545			fld.validity.tooShort = (val.length < $(fld).attr('minlength'));
546		}
547
548		
549		//Prüfung bei Dateiupload uf Dateiformat und Uploadgröße
550		if (this.hasNativeFileReader && type == 'file' && fld.files.length) {
551			var accept 	= $(fld).attr('accept'),
552				maxSize	= $(fld).attr('maxfilesize') || $(fld).attr('data-maxfilesize'),
553				file	= fld.files[0];
554			
555			if (accept) {
556				var fType	= file.name.split(".").pop(),
557					patt	= new RegExp("\."+fType, "gi");
558				fld.validity.acceptError = !patt.test(accept);
559			}
560			
561			if (maxSize) {
562				fld.validity.maxfilesizeError = (file.size > maxSize); 
563			}
564			
565			if (fld.validity.maxfilesizeError || fld.validity.acceptError)
566				this.onInvalid({target: fld});	
567			
568		}
569		
570	},
571	
572	getValidationErrors: function(fld) {
573		this.setCustomValidationProps(fld);
574		
575		var v 		= fld.validity,
576			err 	= [],
577			skip	= { 'customError': 1, 'valid' : 1};
578		
579		for (var a in v) {
580			if (!skip[a] && v[a] == true)
581				err.push(a);	
582		}
583		
584		return err;	
585	},
586	
587	setCustomValidationProps: function(fld) {
588		if (!fld || !fld.willValidate)
589			return; 
590		
591		var me		= this,
592			val		= $(fld).val(),
593			sLen	= $(fld).attr('strictlength'),
594			type	= $(fld).attr('type'),
595			patt	= $(fld).attr('pattern'),
596			vType	= ($(fld).attr(this.ikissCustomValidateAttribute) || '').split('|');
597		
598		if (sLen && val.length != 0) {
599			fld.validity.strictlengthError = (val.length != sLen);
600		}
601		
602		//Polyfill für tooShort (minlength), z.B. für IE11
603		if (!('tooShort' in fld.validity) && val.length != 0 && $(fld).attr('minlength')) {
604			fld.validity.tooShort = (val.length < $(fld).attr('minlength'));
605		}
606		
607		//Textareas pattern support
608		if ($(fld).is('textarea') && patt && val.length) {
609			var pattRegex	= new RegExp( "^" + patt.replace(/^\^|\$$/g, '') + "$", "g" ),
610				cleanedVal	= val.replace("\n", "");		
611			
612			fld.validity.ikissTextareaPatternError = !cleanedVal.match(pattRegex) ? true : false;
613		}
614		
615		//Prüfung bei Dateiupload uf Dateiformat und Uploadgröße
616		if (this.hasNativeFileReader && type == 'file' && fld.files.length) {
617			var accept 	= $(fld).attr('accept'),
618				maxSize	= $(fld).attr('maxfilesize') || $(fld).attr('data-maxfilesize'),
619				file	= fld.files[0];
620			
621			if (accept) {
622				var fType	= file.name.split(".").pop(),
623					patt	= new RegExp("\."+fType, "gi");
624				fld.validity.acceptError = !patt.test(accept);
625			}
626			
627			if (maxSize) {
628				fld.validity.maxfilesizeError = (file.size > maxSize); 
629			}
630		}	
631		
632		$.each(vType, function(idx, val) {
633			var cr = me.customRules[val];
634			if (val && cr) {
635				fld.validity[cr.name+'Error'] = !(cr.validate()); 	
636			}
637		});
638	},
639	
640	/**
641	* Gibt (falls vorhanden) das Label zurück 
642	* 
643	* @returns $el/false
644	*/
645	getLabel: function(field) {
646		var $f 		= $(field),
647			na		= $f.attr('name'),
648			isMulti = (this.checkboxMulti[na] || ( ($f.attr('type') == 'file' || $f.is('select')) && $f[0].hasAttribute('multiple')) ), 
649			lbl 	= $('label[for="'+( isMulti ? na.replace(/(\[\])$/, '') : na)+'"]', this.rootNode);
650		
651		// Neues Markup bzgl. #16209 prüfen	
652		var fieldWrapper = $f.closest('fieldset.ikiss-field-wrapper', this.rootNode);
653		if (fieldWrapper.length) {
654			lbl = $('legend', fieldWrapper);			
655		} 
656		
657		if (!lbl.length) {
658			lbl = $('label[id="'+( isMulti ? na.replace(/(\[\])$/, '') : na)+'"]', this.rootNode);
659		}
660		
661		if (!lbl.length) {
662			lbl = $('label[for="'+($f.attr('id'))+'"]', this.rootNode);
663		}
664
665		if (!lbl.length) {
666			lbl = $('label[data-ikiss-for="'+($f.attr('id'))+'"]', this.rootNode);
667		}
668		
669		return lbl.length ? lbl.first() : false;  
670	},
671	
672	/**
673	* Gibt (falls vorhanden) den Labeltext oder sonst den Namen des Feldes zurück 
674	* 
675	* @returns string
676	*/
677	getLabelText: function(field) {
678		var lbl = this.getLabel(field),
679			na	= $(field).attr('name'),
680			tx	= lbl.length ? lbl.text() : na;
681			 
682		return tx.replace(/[^\d\w\säöüÄÖÜß/\-\_\.\?\!\,]/g, "").trim();
683	},
684	
685	getFormField: function(name) {
686		var me = this,
687			rn = me.rootNode,
688			fld;
689		
690		fld = $('*[name="'+name+'"]', rn);
691		if (!fld.length)
692			fld = $('*[name="'+name+'[]"]', rn);
693		
694		return fld.length ? fld : false;	
695	},
696	
697	moveReqMarkerText: function() {
698		$('.ikiss-mailform-req-marker-text', this.rootNode).insertBefore(this.getFirstFormRow());
699	},
700	
701	getValidationTypes: function(fld) {
702		var attr			= function(att) { return $(fld).attr(att); }, 
703			type 			= attr('type'),
704			validate		= attr(this.ikissValidateAttribute),
705			valObj			= fld.validity,
706			name			= attr('name'),
707			hasValue		= ($(fld).val() || '').length,
708			valTypes		= [];
709		
710		if (fld.nodeName.toLowerCase() === 'select') {
711			valTypes.push(fld.hasAttribute('multiple') ? 'selectmulti' : 'select'); 
712		}
713		
714		if (/^(email|url|file)$/.test(type)) {
715			valTypes.push(type);	
716		}
717		
718		if (type === 'checkbox') {
719			valTypes.push(this.checkboxMulti[name] ? 'checkboxmulti' : 'checkbox');
720		}
721		
722		if (type === 'radio') {
723			valTypes.push(this.radioMulti[name] ? 'radiomulti' : 'radio');
724		}
725		
726		if (type === 'file') {
727			if (attr('accept')) valTypes.push('accept');
728			if (attr('maxfilesize') || attr('data-maxfilesize')) valTypes.push('maxfilesize');
729		}
730
731		if (type == 'date' || type == 'datetime-local') {
732			if (valObj.rangeUnderflow || valObj.rangeOverflow) {
733				valTypes.push('date_invalid');
734			}
735		}
736
737		if (type === 'number') {
738			valObj.badInput && valTypes.push('number');
739			if ((valObj.rangeUnderflow || valObj.rangeOverflow) && attr('min') && attr('max')) {
740				valTypes.push('number_between');
741			} else {
742				valObj.rangeUnderflow && valTypes.push('number_under');
743				valObj.rangeOverflow && valTypes.push('number_over');
744			}
745
746			if (valObj.stepMismatch) {
747				switch (attr('step')) {
748					case '1'	: valTypes.push('number_whole'); break;
749					case '0.1'	: valTypes.push('number_onedig'); break;
750					case '0.01'	: valTypes.push('number_twodig'); break;
751					case '0.001': valTypes.push('number_threedig'); break;
752				}
753			}
754
755		}
756		
757		if (attr('minlength') && hasValue)		valObj.tooShort && valTypes.push('minlength');
758		if (attr('maxlength') && hasValue) 		valTypes.push('maxlength');
759		if (attr('strictlength'))				valTypes.push('strictlength');
760		
761		if (validate) {
762			switch (validate) {
763				case 'ip'		:	valTypes.push('ip'); break;
764				case 'telefon'	: 	valTypes.push('phone'); break; 
765				case 'datum'	: 	valTypes.push('date'); break;
766				case 'fkzahl'	: 	valTypes.push('fkzahl'); break;
767				default			: 	if (!/^(email|url|checkbox)$/.test(type)) {
768										if (hasValue && valObj.patternMismatch) {
769											valTypes.push('chars'); break;	
770										}
771									}
772			}	
773		}        
774				
775		return valTypes.length ? valTypes : ['text'];
776	},
777	
778	getValidationChars: function(fld) {
779		var vl = $(fld).attr(this.ikissValidateAttribute).split(","),
780			msg = "",
781			tx = [];
782
783		for (var a in vl) {
784			switch (vl[a]) {
785				case 'a-z' 		:	tx.push(this.la == 2 ? "lowercase letters" : "Kleinbuchstaben");
786									break;
787				case 'A-Z' 		:	tx.push(this.la == 2 ? "capital letters" : "Großbuchstaben");
788									break;
789				case '0-9' 		:	tx.push(this.la == 2 ? "numbers" : "Zahlen"); 
790									break;
791				case '\\s' 		:	tx.push(this.la == 2 ? "spaces" : "Leerzeichen"); 
792									break;
793				case 'chars'	:   var chars = $(fld).attr(this.ikissValidateAttributeChars);
794									chars = chars.replace(/\u201C|\u201D/g, '"');
795									chars = chars.replace(/\s/, '');
796									var charTx = (this.la == 2) ? 'the characters: ' : 'die Zeichen: '; 
797									tx.push(charTx + $('<span>').text(chars.split("").join(" ")).html());
798									break;	
799			}
800		}
801		if (tx.length > 1) 
802			msg = (this.la == 2 ? ' and ' : ' und ') + tx.pop();
803		
804		return tx.join(", ") + msg;
805	},
806	
807	/**
808	* Gibt den Felermeldungstext eines Feldes 
809	* 
810	* @returns string
811	*/
812	getErrorText: function(fld) {
813		var $f		= $(fld),
814			types	= this.getValidationTypes(fld),
815			errText = '',
816			fName	= (this.la == 2) ? '"'+this.getLabelText(fld)+'"' : '&bdquo;'+this.getLabelText(fld)+'&ldquo;', 
817			replTx	= { name: fName },
818			cv 		= ($f.attr(this.ikissCustomValidateAttribute) || '').split('|'),
819			i		= 0,
820			type; 	
821		
822		$.each(cv, function(idx, val) {
823			val && types.push(val);
824		});
825
826		for (; i < types.length; i++) {
827			type = types[i]; 
828			
829			if (/^(minlength|maxlength|strictlength)$/.test(type)) {
830				switch (type) {
831					case 'minlength'	:	replTx.reqChars = $f.attr('minlength'); break;
832					case 'maxlength'    :   replTx.reqChars = $f.attr('maxlength'); break;
833					case 'strictlength'	:   replTx.reqChars = $f.attr('strictlength'); break;
834				}
835				replTx.curChars = ($f.val()).length;
836			}
837			
838			if (type == 'chars') {
839				replTx.chars = this.getValidationChars(fld); 	
840			}
841			
842			if (type == 'text' && !fld.validity.valueMissing) {
843				continue;	
844			}
845			
846			if ((type == 'select' || type == 'selectmulti') && !fld.validity.valueMissing) {
847				continue;	
848			}
849			
850			if (type == 'file' && !fld.validity.valueMissing) {
851				continue;
852			} 
853
854			if (type == 'email') {
855				if (!fld.validity.valueMissing && !fld.validity.typeMismatch) continue;
856				else if (fld.validity.typeMismatch) type = 'email_invalid';
857			}
858			
859			if (this.customRules[type] && !fld.validity[type+'Error']) {
860				continue;
861			}
862			
863			if (type == 'maxfilesize') {
864				if (fld.validity.maxfilesizeError) {
865					replTx.filename = '"'+fld.files[0].name+'"';
866				} else {
867					continue;
868				}	
869			} 
870			
871			if (type == 'accept') {
872				if (fld.validity.acceptError) {
873					replTx.filename = '"'+fld.files[0].name+'"';
874				} else {
875					continue;
876				}
877			}
878
879			if (/^number/.test(type)) {
880				replTx.min = $f.attr('min');
881				replTx.max = $f.attr('max');
882			}
883			
884			var tx = this.errMessages[type];
885			for (var a in replTx) {
886				tx = tx.replace('{'+a+'}', replTx[a]);	
887			}
888			errText += tx+'<br>';
889		}
890		
891		return errText;
892	},
893
894	getInlineMessageContainer: function($f) {
895		var ct = $f.closest('span.ikiss-form-element');
896		return ct.length ? ct : $f.closest('div, p');
897	},
898	
899	toggleInlineMessage: function(mode, fld, msgText) {
900		var me	= this,
901			$f	= $(fld),
902			ct	= me.getInlineMessageContainer($f), 
903			na	= $f.attr('name'),
904			$trg = me.checkboxMulti[na] || me.radioMulti[na] || $f,
905			$lbl = me.getLabel(fld);
906		
907		if ($f.attr('type') == 'submit')
908			return;
909		
910		
911		switch(mode) {
912			case 'add' 		:   //Fehlerklasse zum äusseren Span hinzufügen
913								$(ct).addClass(me.wrapErrorCls);
914								
915								//Fehlerklasse zum Label hinzufügen
916								$lbl && $lbl.addClass(me.labelErrorCls);
917								
918								//Fehlerklasse zum feld hinzufügen
919								$trg.addClass(me.fieldErrorCls)
920									.each(function() {
921										var mTx = msgText
922													.replace(/\n|\t/gi, "")
923													.replace(/<br>/gi, "\n")
924													.replace(/&bdquo;/g, '„')
925													.replace(/&ldquo;/g, '“'); 
926										
927										this.setCustomValidity(mTx); 
928									});
929								
930								//Fehlermeldung unterhalb der Felder setzen/updaten
931								if (me.hideInlineValidationMessages != true) {
932									var ariaDescribedby = $f.attr('aria-describedby') || '';
933									if (!ariaDescribedby || ariaDescribedby.indexOf('formerror_') === -1 || $f.attr('aria-describedby') == $f.data('ikiss-aria-describedby')) {
934										var ariaId 		= 'formerror_'+window.ikissFormErrCounter++,
935											ariaHidden 	= me.hideValidationSummary ? '' : ' aria-hidden="true"';
936										
937										ariaDescribedby = ariaDescribedby.replace(/\s*formerror_\d*/gi, '');
938										$trg.attr('aria-describedby', ariaDescribedby+' '+ariaId);    
939										ct.append('<div class="ikiss-mailform-inline-error-clear"'+ariaHidden+'></div><span class="ikiss-mailform-inline-error" id="'+ariaId+'">'+msgText+'</span>');		
940									} else { //Nur updaten
941										$('.ikiss-mailform-inline-error', ct).html(msgText);
942									}	
943								}
944								break;
945			case 'remove'	:   //Fehlerklasse zum aeusseren Span hinzufuegen
946								$(ct).removeClass(me.wrapErrorCls);  
947								
948								//Fehlerklasse zum Label hinzufügen
949								$lbl && $lbl.removeClass(me.labelErrorCls);
950								
951								//HTML5 Fehlermeldung entfernen
952								$trg.removeClass(me.fieldErrorCls)
953									.each(function() { this.setCustomValidity(''); })
954								
955								$trg.attr('aria-describedby', ($trg.attr('aria-describedby') || '').replace(/\s*formerror_\d/gi, ''))
956								if ($trg.data('ikiss-aria-describedby')) {
957									$trg.attr('aria-describedby', $trg.data('ikiss-aria-describedby'));	
958								} else {
959									$trg.removeAttr('aria-describedby');
960								}
961								
962								//Fehlermeldung unterhalb der Felder entfernen	
963								if (me.hideInlineValidationMessages != true) {
964									$('span.ikiss-mailform-inline-error', ct).remove();	
965									$('.ikiss-mailform-inline-error-clear', ct).remove();
966								}
967
968								//Fehlermeldung für das Feld aus der Fehlerübersicht entfernen
969								if (me.hideValidationSummary !== true) {
970									var summary = $('.ikiss-mailform-validation-summary', me.rootNode);
971									if (summary.length) {
972										// Nutzer wurde schon über Fehler informiert, keine weiteren meldungen bei Modifikation
973										summary.attr('aria-live', 'off');
974										// Fehlermeldung aus der Fehlerübersicht entfernen
975										$('a[href="#'+$trg.attr('id')+'"]', summary).parent('li').remove();
976										// Falls keine weiteren Fehlermeldungen vorhanden, Fehlerübersicht komplett entfernen
977										if ($('.ikiss-mailform-validation-summary-list li', summary).length === 0) {
978											summary.remove();
979										}
980									}
981								}
982
983								break;
984		}
985	},
986	
987	/**
988	* Gibt eine Fehlermeldung unterhalb des Feldes aus 
989	* 
990	* @returns string
991	*/
992	addInlineMessage: function(fld, msgText) {
993		this.toggleInlineMessage('add', fld, msgText);
994	},
995	
996	/**
997	* Entfernt eine Fehlermeldung unterhalb des Feldes 
998	* 
999	* @returns string
1000	*/
1001	removeInlineMessage: function(fld) {
1002		this.toggleInlineMessage('remove', fld);
1003	},
1004	
1005	/**
1006	* Gleicht das required-Attribut für Multicheckboxen in this.checkboxCollection ab
1007	* 
1008	* @returns void
1009	*/
1010	syncCheckboxes: function(ev) {
1011		var trg	= ev && ev.target, 
1012			cc	= this.checkboxMulti;
1013
1014		for (var a in cc) {
1015			var hasChecked = cc[a].filter(':checked').length;
1016			if (hasChecked) {
1017				cc[a].removeAttr('required').removeClass(this.fieldErrorCls);
1018				this.removeInlineMessage(trg);	
1019			} else {
1020				cc[a].attr('required', true);	
1021			}  
1022
1023			if (!hasChecked && trg && $(trg).attr('name') == a) {
1024				cc[a].prop('checked', true).prop('checked', false);
1025				$(trg).blur().focus();	
1026			}	
1027		}	
1028	}, 
1029
1030	/**
1031	* Setzt das required-Attribut für Checkboxen mit dem Attribute data-required=yes
1032	* Füllt this.checkboxCollection mit den Feldnamen für Multicheckboxen mit dem Attribute data-required=yes
1033	* 
1034	* @returns void
1035	*/
1036	collectCheckboxes: function() {
1037		var me = this,
1038			cc = me.checkboxMulti,
1039			rn = me.rootNode;
1040
1041		$('input:checkbox', rn).each(function(idx, el) {
1042			var $el		= $(el),
1043				name	= $el.attr('name'),
1044				cbItems = $('input[name="'+name+'"]', rn);
1045				
1046			if ($el.attr('data-required') == 'yes') {
1047				if (!cc[name] && cbItems.length > 1) {
1048					cbItems.on('change click', $.proxy(me.syncCheckboxes, me))
1049					cc[name] = cbItems;
1050				}
1051				$el.attr('required', true);
1052			}
1053		});
1054
1055		me.syncCheckboxes();	
1056	},
1057	
1058	collectRadiobuttons: function() {
1059		var me = this,
1060			cc = me.radioMulti,
1061			rn = me.rootNode;
1062
1063		$('input:radio', rn).each(function(idx, el) {
1064			var $el		= $(el),
1065				name	= $el.attr('name'),
1066				cbItems;
1067				
1068			if ($el.attr('required')) {
1069				if (!cc[name]) {
1070					cbItems = $('input[name="'+name+'"]', rn);	
1071					if (cbItems.length > 1) {
1072						cc[name] = cbItems;
1073					}
1074				}
1075			}
1076		});	
1077	},
1078	
1079	setAriaDescribedBy: function() {
1080		var me = this,
1081			rn = me.rootNode;
1082		
1083		$('div[data-ikiss-describe]', rn).each(function(idx, el) {
1084			var descName	= $(el).data('ikiss-describe'),
1085				$trgFld 	= me.getFormField(descName);
1086			
1087			if (!$trgFld || !$trgFld.length)
1088				return;
1089			
1090			var ariaId		= 'formdescription_' + window.ikissFormDescribedByCounter++,
1091				$attrTrg	= ($trgFld.length == 1) ? $trgFld : $trgFld.first().parent('.ikiss-form-element'); 
1092			
1093			$(el).attr('id', ariaId);
1094			
1095			$attrTrg.attr('aria-describedby', ariaId)
1096					.data('ikiss-aria-describedby', ariaId);
1097		});
1098		
1099	},
1100	
1101	/**
1102	* Prueft auf Mobile-Device
1103	* 
1104	* @returns void
1105	*/
1106	checkPlatform : function() {
1107		var devices = ['Android','iPhone','iPad','BlackBerry','Nintendo','PSP'],
1108			regexp  = new RegExp('('+ devices.join('|') +')', 'gi');
1109
1110		if ('platform' in navigator)
1111			this.isMobile = regexp.test(navigator.platform);
1112	},
1113
1114	minSubmitTimeInit: function () {
1115		var me = this,
1116		mstMessage;
1117
1118		me.minSubmitTimeTokenFetched = null;
1119		$.ajax({url: "/output/form_token.php"})
1120			.done(function (data) {
1121				if (data.token) {
1122					me.setMinSubmitTimeCookie(data.token);
1123					me.minSubmitTimeTokenFetched = true;
1124				}
1125			})
1126			.fail(function (jqXHR) {
1127				me.minSubmitTimeTokenFetched = false;
1128				me.minSubmitTimeTokenFetchError = ' (HTTP Status ' + jqXHR.status + ': ' + jqXHR.statusText + ')';
1129			});
1130
1131		me.minSubmitTimeLeft = me.minSubmitTime;
1132		// Submit-Buttons blockieren mit Info über restliche Wartezeit.
1133		mstMessage = me.errMessages['mintime_' + (me.minSubmitTimeLeft > 1 ? 'multi' : 'single')].replace('{time}', me.minSubmitTimeLeft);
1134		me.toggleSubmitButtons(false, 'mst', mstMessage);
1135
1136
1137		var interval = setInterval(function () {
1138			me.minSubmitTimeLeft--;
1139
1140			if (me.minSubmitTimeLeft > 0) {
1141				// Submit-Buttons blockieren mit Info über restliche Wartezeit.
1142				mstMessage = me.errMessages['mintime_' + (me.minSubmitTimeLeft > 1 ? 'multi' : 'single')].replace('{time}', me.minSubmitTimeLeft);
1143				me.toggleSubmitButtons(false, 'mst', mstMessage);
1144			} else {
1145				switch (me.minSubmitTimeTokenFetched) {
1146					case null:
1147						// Submit-Buttons blockieren, während auf die Token-Anfrage gewartet wird.
1148						me.toggleSubmitButtons(false, 'mst', 'Bitte warten Sie noch kurz...');
1149						break;
1150					case false:
1151						clearInterval(interval);
1152						// Fehler bei der Token-Anfrage -> Submit-Buttons bleiben blockiert.
1153						me.toggleSubmitButtons(
1154							false,
1155							'mst',
1156							'Es ist ein Fehler aufgetreten' + me.minSubmitTimeTokenFetchError
1157							+ '. Bitte versuchen Sie es sp&auml;ter noch einmal.'
1158						);
1159						break;
1160					case true:
1161						clearInterval(interval);
1162						// Submit-Buttons freischalten.
1163						me.toggleSubmitButtons(true, 'mst');
1164						break;
1165				}
1166			}
1167		}, 1000);
1168	},
1169
1170	setMinSubmitTimeCookie: function (/* string */ value) {
1171		document.cookie = 'ft=' + encodeURIComponent(value) + '; path=/; samesite=strict';
1172	},
1173
1174	/**
1175	 * @return {Object.<string, string|string[]>}
1176	 */
1177	getUrlAutoFillParams: function () {
1178		var params = Object.create(null); // Object ohne Prototyp.
1179
1180		var rawParams =
1181			document.location.hash
1182				.substring(1) // Entfernt das '#'.
1183				.split('&'); // Verschiedene Parameter können mit '&' getrennt werden.
1184
1185		rawParams = rawParams.concat(
1186			document.location.search
1187				.substring(1) // Entfernt das '?'.
1188				.split('&')
1189		);
1190
1191		rawParams.forEach(function (e) {
1192			try {
1193				var parts = decodeURIComponent(e.replace(/\+/g, ' ')).split('='),
1194					name = parts[0],
1195					value = parts[1] || '';
1196			} catch (e) {
1197				return;
1198			}
1199
1200			if (name.slice(-2) === '[]') {
1201				if (!params[name]) params[name] = [];
1202				params[name].push(value);
1203			} else if (name) {
1204				params[name] = value;
1205			}
1206		});
1207
1208		return params;
1209	},
1210
1211	urlAutoFill: function () {
1212		// Kein AutoFill auf Login-Seiten.
1213		if (document.location.pathname.match(/\Wlogin(?:\W|$)/i)) return;
1214
1215		var me = this,
1216			params = me.getUrlAutoFillParams();
1217
1218		$('input[name][type], textarea[name], select[name]', me.rootNode)
1219			.not('[autocomplete="off"]')
1220			.filter(function () {
1221				return $(this).attr('name') in params; // Ist params[Input[name]] vorhanden?
1222			})
1223			.each(function () {
1224				var input = $(this),
1225					type = input.prop('type'),
1226					name = input.attr('name'),
1227					value = params[name];
1228
1229				switch (type) {
1230					case 'text':
1231					case 'email':
1232					case 'url':
1233					case 'textarea':
1234						if (typeof value === 'string')
1235							input.val(value);
1236						break;
1237					case 'radio':
1238					case 'select-one':
1239						if (typeof value === 'string')
1240							input.val([value]);
1241						break;
1242					case 'select-multiple':
1243						if (Array.isArray(value))
1244							input.val(value);
1245						break;
1246					case 'checkbox':
1247						if (typeof value === 'string')
1248							input.prop('checked', value !== '0');
1249						else if (Array.isArray(value))
1250							input.val(value);
1251						break;
1252				}
1253			});
1254	},
1255
1256	/**
1257	 * Disabled oder ent-disabled die Submit-Buttons des aktuellen Formulars.
1258	 * Wenn ein Sicherheitsmechanismus erfolgreich "gelöst" wurde,
1259	 * wird (sofern vorhanden) die Nachricht vom ersten noch ungelösten Mechanismus eingeblendet.
1260	 * #14251
1261	 * @param {boolean} enable True -> Buttons freischalten; False -> Buttons sperren.
1262	 * @param {string} mechanism Bezeichnung vom aktuellen Sicherheitsmechanismus
1263	 * @param {string} [htmlMessage] Nachricht, die dem Nutzer unter dem Submit-Button angezeigt werden soll
1264	 */
1265	toggleSubmitButtons: function (enable, mechanism, htmlMessage) {
1266		var me = this,
1267			submitButtons = $(me.submitButtonSelector, me.rootNode),
1268			classInfoMessage = 'ikiss-mailform-min-submit-time-info',
1269			mechanismIndex = me.blockingMechanisms.indexOf(mechanism);
1270
1271		if (!htmlMessage) htmlMessage = '';
1272		// Neueste Nachricht pro Mechanismus abspeichern.
1273		me.blockingMechanismMessages[mechanism] = htmlMessage;
1274
1275		if (enable) {
1276			// mechanism aus me.blockingMechanisms entfernen, wenn vorhanden.
1277			if (mechanismIndex !== -1) {
1278				me.blockingMechanisms.splice(mechanismIndex, 1);
1279			}
1280
1281			// Buttons freischalten, wenn es keine blockierenden Mechanismen mehr gibt.
1282			if (!me.blockingMechanisms.length) {
1283				submitButtons
1284					.prop('disabled', false) // Falls der Button durch ein anderes Skript disabled wurde. Siehe auch #15061.
1285					.removeAttr('aria-disabled')
1286					.removeClass(me.buttonDisabledCls);
1287			}
1288		} else {
1289			// mechanism zu me.blockingMechanisms hinzufügen, wenn noch nicht vorhanden.
1290			if (mechanism !== 'info' && mechanismIndex === -1) {
1291				me.blockingMechanisms.push(mechanism);
1292			}
1293
1294			// Buttons sperren.
1295			submitButtons
1296				.attr('aria-disabled', 'true')
1297				.not('.' + me.buttonDisabledCls)
1298				.addClass(me.buttonDisabledCls);
1299		}
1300
1301		// Die aktuelle Message vom ersten aktivierten (und noch aktiven) Mechanismus auswählen.
1302		// Beispiel: Die letzte angezeigte Nachricht betraf die min. Absendezeit; diese ist jetzt abgelaufen.
1303		// Das Captcha ist aber noch nicht gelöst. Dann wird hier die Nachricht des Captchas wieder "hervorgeholt".
1304		if (mechanism !== 'info' && me.blockingMechanisms.length) {
1305			htmlMessage = me.blockingMechanismMessages[me.blockingMechanisms[0]];
1306		}
1307
1308		// Ggfls. vorhandene alte Messages entfernen.
1309		submitButtons.removeAttr('aria-describedby');
1310		$('.' + classInfoMessage, me.rootNode).remove();
1311
1312		// Und ggfls. neu erstellen.
1313		if (htmlMessage) {
1314			submitButtons.each(function () {
1315				var newID = 'ikiss-mailform-id-' + me.idCounter++,
1316					buttonWidth = $(this).css('width');
1317
1318				$('<span class="ikiss-mailform-infolabel"></span>')
1319					.html(htmlMessage)
1320					.addClass(classInfoMessage)
1321					.attr('id', newID)
1322					.css({'width': buttonWidth, 'hyphens': 'manual', 'display': 'block'})
1323					.insertAfter(this);
1324
1325				$(this).attr('aria-describedby', newID);
1326			});
1327		}
1328	},
1329
1330	/**
1331	 * Disabled die Submit-Buttons des aktuellen Formulars,
1332	 * wenn Sicherheitsmechanismen wie min. Absendezeit oder Captcha aktiviert sind.
1333	 * Außerdem wird ein setInterval-Callback registriert, das regelmäßig auf die Lösung des Captchas prüft.
1334	 * #14251
1335	 */
1336	securityInit: function () {
1337		var me = this,
1338			mailformStep = me.rootNode.find('input[name="ikiss-mailform\[step\]"]');
1339
1340		// Template-Formular oder Formularmodul-Formular mit korrektem Step
1341		me.securityCheckStep = !mailformStep.length || ['send', 'missing'].indexOf(mailformStep.val()) !== -1;
1342		me.captchaExists = $('div.frc-captcha,div.eu-captcha', me.rootNode).length > 0;
1343		if (!me.securityCheckStep) return;
1344
1345		if (me.minSubmitTime || me.captchaExists) {
1346			// Submit-Buttons sperren, damit man sie nicht anklicken kann, ohne vorher das Formular fokussiert zu haben,
1347			// d.h. bevor die Sicherheitsmechanismen initialisiert werden konnten.
1348			me.toggleSubmitButtons(false, 'info', me.errMessages.click_form);
1349		}
1350
1351		if (me.captchaExists) {
1352			var interval = setInterval(function () {
1353				if (me.getCaptchaSolution()) {
1354					// Captcha gelöst -> Buttons freischalten
1355					me.toggleSubmitButtons(true, 'captcha');
1356					clearInterval(interval);
1357				}
1358			}, 100);
1359		}
1360	},
1361
1362	/**
1363	 * @returns {string|null}
1364	 */
1365	getCaptchaSolution: function () {
1366		// Input wird zur Laufzeit erstellt, und ist während des Mailform-Constructors noch nicht vorhanden.
1367		var solution = $('input[type="hidden"][name="frc-captcha-solution"]', this.rootNode).val();
1368		// 50 sollte länger als die Statusinformationen sein,
1369		// die vor der erfolgreichen Lösung des Captchas in dem Input gespeichert werden.
1370		if(solution && solution.length > 50) return solution;
1371        
1372        // Und das gleiche nochmal für Friendly Captcha v2
1373        // Hier heisst das Input-Feld "frc-captcha-response"
1374        // Warum die Umbenennung nötig war muss man glaube ich nicht verstehe
1374n
1375        solution = $('input[type="hidden"][name="frc-captcha-response"]', this.rootNode).val();
1376        if(solution && solution.length > 50) return solution;
1377        
1378        // Nochmal für Myra Captcha
1379        solution = $('input[type="hidden"][name="eu-captcha-response"]', this.rootNode).val();
1380        if(solution && solution.length > 50) return solution;
1381        
1382        return null;
1383	},
1384
1385	/**
1386	 * #15598: Dateiupload sollte wieder entfernt werden können
1387	 * Fügt einen Button nach dem Uploadfeld ein
1388	 * Dieser wird angezeigt, wenn das Feld einen Wert hat
1389	 */
1390	initResetableFileUploads: function() {
1391
1392		/**
1393		 * Erstellt oder liefert die Live-Region für die Datei-Upload-Bestätigung.
1394		 * @param {JQuery<HTMLElement>} $wrap
1395		 * @returns {JQuery<HTMLElement>}
1396		 */
1397		var getFileUploadStatusRegion = function($wrap) {
1398			var $status = $wrap.find('.ikiss-mailform-file-upload-status');
1399			if (!$status.length) {
1400				$status = $('<span class="sr-only ikiss-mailform-file-upload-status" role="status" aria-live="polite" aria-atomic="true"></span>').appendTo($wrap);
1401			}
1402			return $status;
1403		};
1404
1405		/**
1406		 * Meldung in die Live-Region einfügen.
1407		 * @param {JQuery<HTMLElement>} $status
1408		 * @param {string} text
1409		 */
1410		var announceFileUploadStatus = function($status, text) {
1411			$status.text('');
1412			window.setTimeout(function() {
1413				$status.text(text);
1414			}, 50);
1415		};
1416
1417		/**
1418		 * Ein-/Ausblenden des jweiligen Buttons und Text setzen
1419		 * @param {HTMLInputElement} field
1420		 * @param {JQuery<HTMLButtonElement>} resetButton
1421		 * @param {JQuery<HTMLElement>} $wrap
1422		 * @param {boolean} announce
1423		 */
1424		var checkForFiles = function(field, resetButton, $wrap, announce) {
1425			var filesLength = field.files.length;
1426			var $status = getFileUploadStatusRegion($wrap);
1427			if (filesLength > 0) {
1428				resetButton.html('<span class="fa-light fa-trash-alt ikiss-mailform-file-reset-button-icon" aria-hidden="true"></span>Datei' + (filesLength > 1 ? 'en' : '') + ' entfernen');
1429				resetButton.show();
1430				if (announce) {
1431					var message = filesLength > 1
1432						? filesLength + ' Dateien erfolgreich hochgeladen.'
1433						: 'Datei ' + (field.files[0] ? '\'' + field.files[0].name + '\'' : '') + ' erfolgreich hochgeladen.';
1434					announceFileUploadStatus($status, message);
1435				}
1436			} else {
1437				resetButton.html('<span class="fa-light fa-trash-alt ikiss-mailform-file-reset-button-icon" aria-hidden="true"></span>Datei entfernen');
1438				resetButton.hide();
1439				if (announce) {
1440					announceFileUploadStatus($status, 'Datei entfernt.');
1441				}
1442			}
1443		};
1444
1445		$('input[type="file"]', this.rootNode).each(function(idx, el) {
1446			var $el = $(el),
1447				$wrap = $el.closest('.ikiss-form-element');
1448
1449			if (!$wrap.length)
1450				return;
1451
1452			// ggf. Infolabel unterhalb des Feldes holen
1453			var $infoLabel = $('.ikiss-mailform-infolabel', $wrap).last();
1454			var insertTarget = $infoLabel.length ? $infoLabel : $wrap;
1455
1456			// Button einfügen
1457			var resetButton = $('<button />', {
1458				type: 'button',
1459				html: '<span class="fa-light fa-trash-alt ikiss-mailform-file-reset-button-icon" aria-hidden="true"></span>Datei entfernen',
1460				class: 'ikiss-mailform-file-reset-button',
1461				'aria-controls': $el.attr('id'),
1462				click: function() {
1463					$el.val('').trigger('change').focus();
1464				}
1465			}).appendTo(insertTarget);
1466
1467			// Leerzeichen vor dem Button einfügen
1468			resetButton.before(' ');
1469
1470			// Prüfung, ob Button angezeigt werden soll, bei onchange und beim Laden der Seite
1471			$(el).on('change', function() {
1472				checkForFiles(el, resetButton, insertTarget, true);
1473			});
1474			checkForFiles(el, resetButton, insertTarget, false);
1475		});
1476	}
1477
1478};

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.