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 „https://“ oder „http://“ 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önnen in {time} Sekunde fortfahren.', 109 'mintime_multi' : 'Sie kö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ö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)+'"' : '„'+this.getLabelText(fld)+'“',
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(/„/g, 'â') 925 .replace(/“/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ä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.