1(() => { 2 'use strict'; 3 4 const script = document.currentScript; 5 if (!script) { 6 return; 7 } 8 9 let config; 10 try { 11 config = JSON.parse(script.dataset.config || '{}'); 12 } catch (error) { 13 console.error('[D&W AI Access] WebMCP-Konfiguration ist ungültig.', error); 14 return; 15 } 16 17 const getAssociatedLabelText = (field) => { 18 const ariaLabel = (field.getAttribute('aria-label') || '').trim(); 19 if (ariaLabel !== '') { 20 return ariaLabel; 21 } 22 23 const labelledBy = (field.getAttribute('aria-labelledby') || '').trim(); 24 if (labelledBy !== '') { 25 const text = labelledBy 26 .split(/\s+/) 27 .map((id) => document.getElementById(id)?.textContent?.trim() || '') 28 .filter(Boolean) 29 .join(' ') 30 .trim(); 31 if (text !== '') { 32 return text; 33 } 34 } 35 36 if (field.id) { 37 const labels = Array.from(document.getElementsByTagName('label')); 38 const label = labels.find((candidate) => candidate.htmlFor === field.id); 39 const text = label?.textContent?.replace(/\s+/g, ' ').trim() || ''; 40 if (text !== '') { 41 return text; 42 } 43 } 44 45 const wrappingLabel = field.closest('label'); 46 return wrappingLabel?.textContent?.replace(/\s+/g, ' ').trim() || ''; 47 }; 48 49 const isCaptchaField = (field) => { 50 const signature = [ 51 field.id, 52 field.getAttribute('name'), 53 field.className, 54 field.getAttribute('autocomplete'), 55 ].join(' ').toLowerCase(); 56 57 if (signature.includes('captcha')) { 58 return true; 59 } 60 61 const group = field.closest('.form-group, .powermail_fieldwrap, fieldset, .form-element'); 62 return Boolean(group?.querySelector('.captcha, img[src*="captcha" i], img[src*="type=3413"]')); 63 }; 64 65 const improveCaptchaAccessibility = (form) => { 66 let fixed = 0; 67 const fields = form.querySelectorAll('input:not([type="hidden"]), textarea'); 68 69 fields.forEach((field) => { 70 if (!isCaptchaField(field)) { 71 return; 72 } 73 74 // An explicit aria-label avoids browser-specific gaps in the 75 // accessibility tree of dynamically rendered CAPTCHA partials. 76 if (!(field.getAttribute('aria-label') || '').trim()) { 77 const label = getAssociatedLabelText(field) || 'Sicherheitscode aus dem Bild eingeben'; 78 field.setAttribute('aria-label', label.replace(/\s*\*\s*$/, '').trim()); 79 field.dataset.dwAccessibility = 'captcha-label-added'; 80 fixed++; 81 } 82 }); 83 84 return fixed; 85 }; 86 87 const annotate = () => { 88 let form; 89 try { 90 form = document.querySelector(config.formSelector); 91 } catch (error) { 92 console.error('[D&W AI Access] Ungültiger Formularselektor.', error); 93 return; 94 } 95 96 if (!form) { 97 if (config.debug) { 98 console.warn('[D&W AI Access] WebMCP-Formular nicht gefunden:', config.formSelector); 99 } 100 return; 101 } 102 103 form.setAttribute('toolname', config.toolName); 104 form.setAttribute('tooldescription', config.toolDescription); 105 106 if (config.autoSubmit) { 107 form.setAttribute('toolautosubmit', ''); 108 } else { 109 form.removeAttribute('toolautosubmit'); 110 } 111 112 for (const mapping of config.parameterMappings || []) { 113 let fields = []; 114 try { 115 fields = form.querySelectorAll(mapping.selector); 116 } catch (error) { 117 console.error('[D&W AI Access] Ungültiger Feldselektor:', mapping.selector, error); 118 continue; 119 } 120 121 fields.forEach((field) => { 122 field.setAttribute('toolparamdescription', mapping.description); 123 }); 124 } 125 126 const captchaFixes = improveCaptchaAccessibility(form); 127 form.dataset.dwWebmcp = 'annotated'; 128 129 if (config.debug) { 130 console.info('[D&W AI Access] WebMCP-Tool registriert:', config.toolName, form); 131 if (captchaFixes > 0) { 132 console.info('[D&W AI Access] CAPTCHA-Accessibility-Label ergänzt:', captchaFixes); 133 } 134 } 135 }; 136 137 if (document.readyState === 'loading') { 138 document.addEventListener('DOMContentLoaded', annotate, { once: true }); 139 } else { 140 annotate(); 141 } 142})();
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.