PageSourceSearch

https://www.kompetenz-agentur.de/typo3conf/ext/dw_ai_access/Resources/Public/JavaScript/WebMcpAnnotator.js

js kompetenz-agentur.de collected 2026-10-06 19:27:53 UTC 4,658 bytes, 142 lines download raw bytes

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.