// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
// for details on configuring this project to bundle and minify static web assets.

// Write your JavaScript code.

(function () {
    function showBusy(text) {
        var busy = document.getElementById('pageBusy');
        if (busy) {
            var label = busy.querySelector('[data-busy-label]');
            if (label && text) {
                label.textContent = text;
            }
            busy.classList.add('is-active');
            busy.setAttribute('aria-hidden', 'false');
        }
    }

    function hideBusy() {
        var busy = document.getElementById('pageBusy');
        if (busy) {
            busy.classList.remove('is-active');
            busy.setAttribute('aria-hidden', 'true');
        }
    }

    function showPageMessage(message, kind) {
        var main = document.querySelector('main') || document.body;
        if (!main) {
            window.alert(message);
            return;
        }
        var existing = main.querySelector('[data-async-page-message]');
        if (existing) {
            existing.remove();
        }
        var alert = document.createElement('div');
        alert.setAttribute('data-async-page-message', 'true');
        alert.className = kind === 'success' ? 'alert alert-success' : 'alert alert-warning';
        alert.textContent = message;
        main.insertBefore(alert, main.firstChild);
    }

    function submitArchiveForm(form) {
        if (!form || form.dataset.archiveSubmitting === 'true') {
            return;
        }
        form.dataset.archiveSubmitting = 'true';

        var archiveKind = form.getAttribute('data-archive-kind') || 'Ablage';
        var archiveLabel = archiveKind + '-Ablage';

        var timeoutMs = parseInt(form.getAttribute('data-busy-timeout-ms') || '120000', 10);
        if (!Number.isFinite(timeoutMs) || timeoutMs < 10000) {
            timeoutMs = 120000;
        }
        var controller = window.AbortController ? new AbortController() : null;
        var timeout = window.setTimeout(function () {
            if (controller) {
                controller.abort();
            }
        }, timeoutMs);

        showBusy(form.getAttribute('data-busy-text') || (archiveLabel + ' wird erstellt...'));

        fetch(form.action || window.location.href, {
            method: (form.method || 'post').toUpperCase(),
            body: new FormData(form),
            credentials: 'same-origin',
            headers: {
                'X-A4W-Async': '1',
                'X-Requested-With': 'XMLHttpRequest'
            },
            signal: controller ? controller.signal : undefined
        })
            .then(function (response) {
                return response.json().catch(function () {
                    return {
                        success: false,
                        message: 'Die Serverantwort zur ' + archiveLabel + ' konnte nicht gelesen werden.'
                    };
                }).then(function (result) {
                    if (!response.ok && result.success !== true) {
                        result.success = false;
                    }
                    return result;
                });
            })
            .then(function (result) {
                if (!result.success) {
                    throw new Error(result.message || archiveLabel + ' wurde nicht abgeschlossen.');
                }
                showPageMessage(result.message || archiveLabel + ' wurde aktualisiert.', 'success');
                if (form.closest && form.closest('#a4wPreviewDialog')) {
                    hideBusy();
                    form.dataset.archiveSubmitting = 'false';
                    var submitButton = form.querySelector('button[type="submit"], button:not([type])');
                    if (submitButton) {
                        submitButton.textContent = archiveKind + ' aktualisieren ✓';
                    }
                    return;
                }
                window.location.assign(result.redirect || window.location.href);
            })
            .catch(function (error) {
                var aborted = error && error.name === 'AbortError';
                var seconds = Math.round(timeoutMs / 1000);
                var message = aborted
                    ? archiveLabel + ' wurde nach ' + seconds + ' Sekunden ohne Serverantwort abgebrochen.'
                    : archiveLabel + ' wurde nicht abgeschlossen: ' + (error && error.message ? error.message : 'Unbekannter Fehler');
                hideBusy();
                showPageMessage(message, 'warning');
                form.dataset.archiveSubmitting = 'false';
            })
            .finally(function () {
                window.clearTimeout(timeout);
            });
    }

    window.auftragoGoBack = function () {
        hideBusy();
        if (window.history.length > 1) {
            window.history.back();
            window.setTimeout(function () {
                hideBusy();
            }, 400);
            return;
        }
        window.location.assign('/');
    };

    window.addEventListener('pageshow', hideBusy);

    window.addEventListener('DOMContentLoaded', function () {
        var trail = document.querySelector('[data-return-trail]');
        var links = document.querySelector('[data-return-trail-links]');
        if (!trail || !links || !window.sessionStorage) {
            return;
        }

        var storageKey = 'a4w.returnTrail.v1';
        var maxItems = 10;
        var contextParameters = [
            'selected',
            'edit',
            'id',
            'documentId',
            'customerId',
            'articleId',
            'projectId',
            'tripId',
            'entry',
            'entryId',
            'workEntryId',
            'new',
            'newTrip',
            'live',
            'typeView',
            'view'
        ];
        var ignoredPaths = {
            '/Login': true,
            '/ForgotPassword': true,
            '/ResetPassword': true,
            '/Print': true,
            '/TemplatePreview': true
        };

        function normalizePath(path) {
            if (!path || path === '/Index') {
                return '/';
            }
            return path.replace(/\/$/, '') || '/';
        }

        function isTrackable() {
            var path = normalizePath(window.location.pathname);
            if (ignoredPaths[path]) {
                return false;
            }
            var search = new URLSearchParams(window.location.search);
            if (search.has('handler')) {
                return false;
            }
            return true;
        }

        function currentUrl() {
            return window.location.pathname + window.location.search + window.location.hash;
        }

        function trailKeyFromUrl(url) {
            var parsed;
            try {
                parsed = new URL(url, window.location.origin);
            } catch (error) {
                return url || '';
            }
            var path = normalizePath(parsed.pathname);
            var keyParts = [path];
            contextParameters.forEach(function (name) {
                var value = parsed.searchParams.get(name);
                if (value) {
                    keyParts.push(name + '=' + value);
                }
            });
            if (parsed.hash && path === normalizePath(window.location.pathname)) {
                keyParts.push(parsed.hash);
            }
            return keyParts.join('|');
        }

        function safeParseTrail() {
            try {
                var parsed = JSON.parse(window.sessionStorage.getItem(storageKey) || '[]');
                return Array.isArray(parsed) ? parsed : [];
            } catch (error) {
                return [];
            }
        }

        function pageLabel() {
            var path = normalizePath(window.location.pathname);
            var selectedHeading = document.querySelector('.document-detail-panel--selected h2, .customer-detail h2, [data-return-title]');
            var heading = selectedHeading || document.querySelector('main h1');
            var text = heading
                ? ((heading.getAttribute('data-return-title') || heading.textContent || '').trim().replace(/\s+/g, ' '))
                : '';
            if (!text) {
                text = (document.title || '').split(' - ')[0].trim();
            }
            if (!text) {
                var labels = {
                    '/': 'Dashboard',
                    '/Customers': 'Kunden',
                    '/CustomerArchive': 'Kundenablage',
                    '/Documents': 'Belege',
                    '/DocumentEdit': 'Beleg bearbeiten',
                    '/Projects': 'Projekte',
                    '/ProjectEdit': 'Projekt',
                    '/Payments': 'Zahlungen',
                    '/Articles': 'Artikel',
                    '/Settings': 'Einstellungen'
                };
                text = labels[path] || path.replace('/', '') || 'Seite';
            }
            return text.length > 42 ? text.slice(0, 39).trimEnd() + '...' : text;
        }

        function samePage(a, b) {
            return a && b && a.key === b.key;
        }

        function compactTrail(items) {
            var compacted = [];
            items.forEach(function (item) {
                if (!item || !item.url) {
                    return;
                }
                item.key = item.key || trailKeyFromUrl(item.url);
                var existingIndex = compacted.findIndex(function (candidate) { return candidate.key === item.key; });
                if (existingIndex >= 0) {
                    compacted.splice(existingIndex, 1);
                }
                compacted.push(item);
            });
            return compacted;
        }

        function renderTrail(items, current) {
            var candidates = items
                .filter(function (item) { return item && item.url && item.label && item.key !== current.key; })
                .slice(-maxItems);
            links.innerHTML = '';
            if (candidates.length === 0) {
                trail.hidden = true;
                return;
            }
            candidates.forEach(function (item, index) {
                var link = document.createElement('a');
                link.className = 'a4w-return-trail-link';
                link.href = item.url;
                link.textContent = item.label;
                link.title = item.url;
                links.appendChild(link);
                if (index < candidates.length - 1) {
                    var separator = document.createElement('span');
                    separator.className = 'a4w-return-trail-separator';
                    separator.textContent = '>';
                    links.appendChild(separator);
                }
            });
            trail.hidden = false;
        }

        if (!isTrackable()) {
            trail.hidden = true;
            return;
        }

        var current = { url: currentUrl(), label: pageLabel(), key: trailKeyFromUrl(currentUrl()), at: Date.now() };
        var storedItems = compactTrail(safeParseTrail().filter(function (item) { return item && item.url; }));
        var currentIndex = storedItems.findIndex(function (item) { return item.key === current.key; });
        var items = currentIndex >= 0
            ? storedItems.slice(0, currentIndex)
            : storedItems;
        renderTrail(items, current);
        items.push(current);
        if (items.length > maxItems + 1) {
            items = items.slice(-(maxItems + 1));
        }
        if (!samePage(items[items.length - 2], current)) {
            window.sessionStorage.setItem(storageKey, JSON.stringify(items));
        }
    });

    window.addEventListener('DOMContentLoaded', function () {
        var selectedRow = document.querySelector('[data-selected-row="true"]');
        if (!selectedRow) {
            return;
        }

        selectedRow.scrollIntoView({ block: 'nearest', inline: 'nearest' });
        if (selectedRow.tabIndex >= 0) {
            selectedRow.focus({ preventScroll: true });
        }
    });

    window.addEventListener('DOMContentLoaded', function () {
        var list = document.querySelector('.document-position-list');
        var form = document.getElementById('documentEditForm');
        if (!list || !form) {
            return;
        }

        var draggedRow = null;
        var dragStartedFromHandle = false;
        var pointerDragging = false;

        function rows() {
            return Array.prototype.slice.call(list.querySelectorAll('[data-document-position-row]'));
        }

        function itemIds() {
            return rows().map(function (row) { return row.getAttribute('data-item-id') || ''; }).filter(Boolean);
        }

        function syncOrderField() {
            var orderField = form.querySelector('input[name="orderedItemIds"]');
            if (orderField) {
                orderField.value = itemIds().join(',');
            }
        }

        function saveOrder() {
            var ids = itemIds();
            if (ids.length < 2) {
                return;
            }

            var payload = new FormData();
            var documentId = form.querySelector('input[name="documentId"]');
            var token = form.querySelector('input[name="__RequestVerificationToken"]');
            if (documentId) {
                payload.append('documentId', documentId.value);
            }
            if (token) {
                payload.append('__RequestVerificationToken', token.value);
            }
            ids.forEach(function (id) {
                payload.append('orderedItemIds', id);
            });

            fetch('?handler=ReorderItems', {
                method: 'POST',
                body: payload,
                credentials: 'same-origin',
                headers: { 'X-Requested-With': 'XMLHttpRequest' }
            })
                .then(function (response) {
                    if (!response.ok) {
                        throw new Error('Reihenfolge konnte nicht gespeichert werden.');
                    }
                    return response.json().catch(function () { return { success: true }; });
                })
                .then(function (result) {
                    if (!result.success) {
                        throw new Error(result.message || 'Reihenfolge konnte nicht gespeichert werden.');
                    }
                    showPageMessage('Positionen wurden neu sortiert.', 'success');
                })
                .catch(function (error) {
                    showPageMessage(error.message || 'Reihenfolge konnte nicht gespeichert werden.', 'warning');
                });
        }

        function rowAfterPointer(container, y) {
            return rows()
                .filter(function (row) { return row !== draggedRow && !row.classList.contains('is-dragging'); })
                .reduce(function (closest, row) {
                    var box = row.getBoundingClientRect();
                    var offset = y - box.top - box.height / 2;
                    if (offset < 0 && offset > closest.offset) {
                        return { offset: offset, row: row };
                    }
                    return closest;
                }, { offset: Number.NEGATIVE_INFINITY, row: null }).row;
        }

        list.addEventListener('mousedown', function (event) {
            dragStartedFromHandle = !!(event.target.closest && event.target.closest('[data-position-drag-handle]'));
        });

        list.addEventListener('pointerdown', function (event) {
            var handle = event.target.closest && event.target.closest('[data-position-drag-handle]');
            if (!handle || event.button !== 0) {
                return;
            }
            var row = handle.closest('[data-document-position-row]');
            if (!row || row.getAttribute('data-position-locked') === 'true') {
                return;
            }
            event.preventDefault();
            draggedRow = row;
            pointerDragging = true;
            dragStartedFromHandle = true;
            row.classList.add('is-dragging');
            if (handle.setPointerCapture) {
                handle.setPointerCapture(event.pointerId);
            }
        });

        document.addEventListener('pointermove', function (event) {
            if (!pointerDragging || !draggedRow) {
                return;
            }
            event.preventDefault();
            var after = rowAfterPointer(list, event.clientY);
            rows().forEach(function (row) { row.classList.remove('is-drop-target'); });
            if (after) {
                after.classList.add('is-drop-target');
                list.insertBefore(draggedRow, after);
            } else {
                list.appendChild(draggedRow);
            }
            syncOrderField();
        });

        document.addEventListener('pointerup', function () {
            if (!pointerDragging || !draggedRow) {
                return;
            }
            rows().forEach(function (row) {
                row.classList.remove('is-dragging');
                row.classList.remove('is-drop-target');
            });
            draggedRow = null;
            pointerDragging = false;
            dragStartedFromHandle = false;
            syncOrderField();
            saveOrder();
        });

        list.addEventListener('dragstart', function (event) {
            var row = event.target.closest && event.target.closest('[data-document-position-row]');
            if (!row || row.getAttribute('data-position-locked') === 'true' || !dragStartedFromHandle) {
                event.preventDefault();
                return;
            }
            draggedRow = row;
            row.classList.add('is-dragging');
            if (event.dataTransfer) {
                event.dataTransfer.effectAllowed = 'move';
                event.dataTransfer.setData('text/plain', row.getAttribute('data-item-id') || '');
            }
        });

        list.addEventListener('dragover', function (event) {
            if (!draggedRow) {
                return;
            }
            event.preventDefault();
            var after = rowAfterPointer(list, event.clientY);
            rows().forEach(function (row) { row.classList.remove('is-drop-target'); });
            if (after) {
                after.classList.add('is-drop-target');
                list.insertBefore(draggedRow, after);
            } else {
                list.appendChild(draggedRow);
            }
            syncOrderField();
        });

        list.addEventListener('dragend', function () {
            if (!draggedRow) {
                return;
            }
            rows().forEach(function (row) {
                row.classList.remove('is-dragging');
                row.classList.remove('is-drop-target');
            });
            draggedRow = null;
            dragStartedFromHandle = false;
            syncOrderField();
            saveOrder();
        });

        syncOrderField();
    });

    window.addEventListener('DOMContentLoaded', function () {
        document.querySelectorAll('[data-document-row], [data-work-entry-row]').forEach(function (row) {
            row.addEventListener('contextmenu', function (event) {
                if (event.target.closest('.work-entry-select, .work-entry-actions, .document-row-menu')) {
                    return;
                }
                var toggle = row.querySelector('.document-row-menu-toggle');
                if (!toggle) {
                    return;
                }
                event.preventDefault();
                event.stopPropagation();
                toggle.click();
                toggle.focus({ preventScroll: true });
            });
        });
    });

    window.addEventListener('DOMContentLoaded', function () {
        document.querySelectorAll('[data-batch-form]').forEach(function (form) {
            var formId = form.id;
            var relatedSelector = formId ? '[form="' + formId.replace(/"/g, '\\"') + '"]' : '';
            var scopedControls = function (selector) {
                var controls = Array.prototype.slice.call(form.querySelectorAll(selector));
                if (relatedSelector) {
                    document.querySelectorAll(selector + relatedSelector).forEach(function (control) {
                        if (controls.indexOf(control) === -1) {
                            controls.push(control);
                        }
                    });
                }
                return controls;
            };
            var boxes = scopedControls('[data-batch-checkbox]');
            var selectAll = scopedControls('[data-batch-select-all]')[0] || null;
            var count = form.querySelector('[data-batch-count]');
            var actions = Array.prototype.slice.call(form.querySelectorAll('[data-batch-action]'));
            var help = form.querySelector('[data-batch-help]');
            var selectedLabel = form.querySelector('[data-batch-selected-label]');
            var targetSelect = form.querySelector('[data-batch-target]');
            var reminderSelect = form.querySelector('[data-batch-reminder]');
            var batchBar = form.querySelector('.document-batch-bar');
            var typeLabel = {
                Angebot: 'Angebote',
                Auftrag: 'Aufträge',
                Lieferschein: 'Lieferscheine',
                Rechnung: 'Rechnungen'
            };
            var selectedBoxes = function () {
                return boxes.filter(function (box) { return box.checked; });
            };
            var selectedTypes = function () {
                return selectedBoxes().map(function (box) { return box.getAttribute('data-batch-type') || ''; })
                    .filter(Boolean)
                    .filter(function (value, index, all) { return all.indexOf(value) === index; });
            };
            var actionAllowed = function (button, selected, types) {
                if (selected < 2) {
                    return false;
                }
                var allowedTypes = (button.getAttribute('data-allowed-types') || '').split(',').map(function (x) { return x.trim(); }).filter(Boolean);
                if (allowedTypes.length && (types.length !== 1 || allowedTypes.indexOf(types[0]) === -1)) {
                    return false;
                }
                var requires = button.getAttribute('data-requires') || '';
                if (requires === 'reminder' && selectedBoxes().some(function (box) { return box.getAttribute('data-can-reminder') !== 'true'; })) {
                    return false;
                }
                return true;
            };
            var sync = function () {
                var selected = selectedBoxes().length;
                var types = selectedTypes();
                if (batchBar) {
                    batchBar.classList.toggle('has-selection', selected > 1);
                }
                if (count) {
                    count.textContent = selected.toString();
                }
                if (selectedLabel) {
                    selectedLabel.textContent = selected < 2
                        ? selected === 0 ? 'Keine Belege ausgewählt' : 'Ein Beleg ausgewählt'
                        : types.length === 1
                            ? selected + ' ' + (typeLabel[types[0]] || types[0]) + ' ausgewählt'
                            : selected + ' Belege aus ' + types.length + ' Belegarten ausgewählt';
                }
                if (help) {
                    help.textContent = selected < 2
                        ? selected === 0 ? 'Wählen Sie zuerst fachlich zusammengehörige Belege aus.' : 'Für Sammelaktionen mindestens zwei fachlich zusammengehörige Belege auswählen.'
                        : types.length > 1
                            ? 'Gemischte Auswahl: nur Ablageaktionen sind verfügbar, Folgebelege bleiben deaktiviert.'
                            : 'Verfügbare Aktionen sind auf ' + (typeLabel[types[0]] || types[0]) + ' beschränkt.';
                }
                actions.forEach(function (button) {
                    button.disabled = !actionAllowed(button, selected, types);
                });
                if (targetSelect) {
                    targetSelect.disabled = selected < 2 || types.length !== 1 || !['Angebot', 'Arbeitseinsatz'].includes(types[0]);
                }
                if (reminderSelect) {
                    reminderSelect.disabled = selected < 2 || types.length !== 1 || types[0] !== 'Rechnung';
                }
                if (selectAll) {
                    selectAll.checked = selected > 0 && selected === boxes.length;
                    selectAll.indeterminate = selected > 0 && selected < boxes.length;
                }
            };
            boxes.forEach(function (box) { box.addEventListener('change', sync); });
            if (selectAll) {
                selectAll.addEventListener('change', function () {
                    boxes.forEach(function (box) { box.checked = selectAll.checked; });
                    sync();
                });
            }
            form.addEventListener('submit', function (event) {
                var selected = boxes.filter(function (box) { return box.checked; }).length;
                if (selected < 2) {
                    event.preventDefault();
                    return;
                }
                var current = 0;
                showBusy('Sammelaktion wird vorbereitet...');
                window.setInterval(function () {
                    current = Math.min(selected, current + 1);
                    showBusy('Verarbeite ' + current + ' von ' + selected + ' Beleg(en)...');
                }, 650);
            });
            sync();
        });
    });

    window.addEventListener('DOMContentLoaded', function () {
        var exportStatus = document.querySelector('[data-export-status]');
        if (!exportStatus) {
            return;
        }

        document.querySelectorAll('[data-download-form]').forEach(function (form) {
            form.addEventListener('submit', function (event) {
                var label = event.submitter ? event.submitter.textContent.trim() : 'Export';
                exportStatus.classList.add('status-note-active');
                exportStatus.innerHTML = '<strong>' + label + ' wird erzeugt...</strong><br><span>Die Datei wird lokal abgelegt. Danach erscheint sie in der Liste der zuletzt erzeugten Exportdateien.</span>';
                showBusy(label + ' wird erstellt...');
                window.setTimeout(hideBusy, 1400);
            });
        });
    });

    window.addEventListener('DOMContentLoaded', function () {
        window.auftragoEnableCodeScanner && window.auftragoEnableCodeScanner();
    });

    document.addEventListener('submit', function (event) {
        var form = event.target;
        if (!form || !form.matches || !form.matches('[data-internal-email-form]')) {
            return;
        }

        event.preventDefault();
        event.stopPropagation();
        var previewDialog = form.closest('#a4wPreviewDialog');
        var isPreviewAction = previewDialog && form.closest('[data-preview-document-actions]');
        if (isPreviewAction) {
            form.dataset.previewAction = 'true';
        }
        openInternalEmailDialog(form);
    }, true);

    document.addEventListener('submit', function (event) {
        var form = event.target;
        if (!form || !form.matches || form.dataset.confirmed === 'true') {
            return;
        }

        var submitter = event.submitter || null;
        var confirmSource = form.matches('[data-confirm-dialog]')
            ? form
            : (submitter && submitter.matches && submitter.matches('[data-confirm-dialog]') ? submitter : null);

        if (!confirmSource) {
            return;
        }

        event.preventDefault();
        event.stopPropagation();
        openConfirmDialog(form, submitter, confirmSource);
    }, true);

    document.addEventListener('click', function (event) {
        var link = event.target && event.target.closest ? event.target.closest('a[data-confirm-dialog]') : null;
        if (!link || link.dataset.confirmed === 'true') {
            return;
        }

        event.preventDefault();
        event.stopPropagation();
        openConfirmLinkDialog(link);
    }, true);

    document.addEventListener('submit', function (event) {
        var form = event.target;
        if (!form || !form.matches || !form.matches('[data-archive-form]')) {
            return;
        }
        if (form.matches('[data-confirm-dialog]') && form.dataset.confirmed !== 'true') {
            return;
        }

        event.preventDefault();
        event.stopPropagation();
        if (event.stopImmediatePropagation) {
            event.stopImmediatePropagation();
        }
        submitArchiveForm(form);
    }, true);

    document.addEventListener('submit', function (event) {
        var form = event.target;
        if (!form || !form.matches || form.matches('[data-internal-email-form]')) {
            return;
        }
        if (form.matches('[data-confirm-dialog]') && form.dataset.confirmed !== 'true') {
            return;
        }
        var previewDialog = form.closest('#a4wPreviewDialog');
        if (previewDialog && form.closest('[data-preview-document-actions]')) {
            closePreviewDialog(previewDialog);
        }
    }, true);

    function positionPortalDropdown(dropdown) {
        var menu = dropdown && dropdown._a4wPortalMenu;
        var toggle = dropdown && dropdown.querySelector('[data-bs-toggle="dropdown"]');
        if (!menu || !toggle) {
            return;
        }

        var rect = toggle.getBoundingClientRect();
        var gap = 4;
        var margin = 8;
        menu.style.visibility = 'hidden';
        menu.style.display = 'block';
        menu.style.maxHeight = '';
        menu.style.overflowY = '';
        var menuWidth = Math.max(menu.offsetWidth, 216);
        var left = Math.min(window.innerWidth - menuWidth - margin, Math.max(margin, rect.right - menuWidth));
        var menuHeight = menu.offsetHeight;
        var availableBelow = window.innerHeight - rect.bottom - gap - margin;
        var availableAbove = rect.top - gap - margin;
        var openAbove = menuHeight > availableBelow && availableAbove > availableBelow;
        var maxHeight = Math.max(96, openAbove ? availableAbove : availableBelow);
        var top = openAbove
            ? Math.max(margin, rect.top - Math.min(menuHeight, maxHeight) - gap)
            : Math.min(window.innerHeight - margin - Math.min(menuHeight, maxHeight), rect.bottom + gap);
        if (menuHeight > maxHeight) {
            menu.style.maxHeight = maxHeight + 'px';
            menu.style.overflowY = 'auto';
        }
        menu.style.left = left + 'px';
        menu.style.top = Math.max(margin, top) + 'px';
        menu.style.visibility = '';
    }

    function hideOtherPortalDropdowns(activeDropdown) {
        document.querySelectorAll('.document-row-menu').forEach(function (dropdown) {
            if (dropdown === activeDropdown) {
                return;
            }

            var toggle = dropdown.querySelector('[data-bs-toggle="dropdown"]');
            if (toggle && window.bootstrap && window.bootstrap.Dropdown) {
                var instance = window.bootstrap.Dropdown.getInstance(toggle);
                if (instance) {
                    instance.hide();
                }
            }

            restorePortalDropdown(dropdown);
            if (toggle) {
                toggle.classList.remove('show');
                toggle.setAttribute('aria-expanded', 'false');
            }
        });
    }

    function restorePortalDropdown(dropdown) {
        var menu = dropdown && dropdown._a4wPortalMenu;
        var placeholder = dropdown && dropdown._a4wPortalPlaceholder;
        if (!menu || !placeholder) {
            return;
        }
        menu.classList.remove('a4w-dropdown-portal-menu');
        menu.style.left = '';
        menu.style.top = '';
        menu.style.display = '';
        menu.style.visibility = '';
        menu.style.maxHeight = '';
        menu.style.overflowY = '';
        placeholder.replaceWith(menu);
        delete dropdown._a4wPortalMenu;
        delete dropdown._a4wPortalPlaceholder;
    }

    document.addEventListener('show.bs.dropdown', function (event) {
        var dropdown = event.target && event.target.closest && event.target.closest('.document-row-menu');
        if (!dropdown) {
            return;
        }

        hideOtherPortalDropdowns(dropdown);
    });

    document.addEventListener('shown.bs.dropdown', function (event) {
        var dropdown = event.target && event.target.closest && event.target.closest('.document-row-menu');
        if (!dropdown || dropdown._a4wPortalMenu) {
            return;
        }
        var menu = dropdown.querySelector('.dropdown-menu');
        if (!menu) {
            return;
        }
        var placeholder = document.createComment('a4w document dropdown');
        menu.parentNode.insertBefore(placeholder, menu);
        document.body.appendChild(menu);
        menu.classList.add('a4w-dropdown-portal-menu');
        dropdown._a4wPortalMenu = menu;
        dropdown._a4wPortalPlaceholder = placeholder;
        positionPortalDropdown(dropdown);
        window.setTimeout(function () { positionPortalDropdown(dropdown); }, 0);
    });

    document.addEventListener('hide.bs.dropdown', function (event) {
        var dropdown = event.target && event.target.closest && event.target.closest('.document-row-menu');
        restorePortalDropdown(dropdown);
    });

    window.addEventListener('resize', function () {
        document.querySelectorAll('.document-row-menu').forEach(positionPortalDropdown);
    });

    document.addEventListener('scroll', function () {
        document.querySelectorAll('.document-row-menu').forEach(positionPortalDropdown);
    }, true);

    document.addEventListener('click', function (event) {
        var link = event.target.closest && event.target.closest('[data-preview-dialog]');
        if (!link) {
            return;
        }

        event.preventDefault();
        event.stopPropagation();
        openPreviewDialog(link);
    }, true);

    function closeSearchDialog(dialog) {
        if (!dialog) {
            return;
        }
        dialog.classList.remove('is-active');
        dialog.setAttribute('aria-hidden', 'true');
    }

    function openSearchDialog(dialog, opener) {
        if (!dialog) {
            return;
        }
        if (opener) {
            dialog._a4wSearchOwner = opener.closest('form') || null;
        }
        if (dialog.parentElement !== document.body) {
            document.body.appendChild(dialog);
        }
        dialog.classList.add('is-active');
        dialog.setAttribute('aria-hidden', 'false');
        var focusTarget = dialog.querySelector('input, select, textarea, button, a[href]');
        if (focusTarget) {
            window.setTimeout(function () { focusTarget.focus(); }, 40);
        }
    }

    document.addEventListener('click', function (event) {
        var opener = event.target.closest && event.target.closest('[data-search-dialog-open]');
        if (opener) {
            var target = document.querySelector(opener.getAttribute('data-search-dialog-open'));
            if (target) {
                event.preventDefault();
                openSearchDialog(target, opener);
            }
            return;
        }

        var closer = event.target.closest && event.target.closest('[data-search-dialog-close]');
        if (closer) {
            var dialog = closer.closest('[data-search-dialog]');
            if (dialog) {
                event.preventDefault();
                closeSearchDialog(dialog);
            }
            return;
        }

        var disabledApply = event.target.closest && event.target.closest('[data-search-apply].disabled');
        if (disabledApply) {
            event.preventDefault();
            return;
        }

        var row = event.target.closest && event.target.closest('[data-search-row]');
        if (row && !event.target.closest('a, button, input, select, textarea')) {
            var dialogForRow = row.closest('[data-search-dialog]');
            if (!dialogForRow) {
                return;
            }
            event.preventDefault();
            dialogForRow.querySelectorAll('[data-search-row].is-selected').forEach(function (other) {
                other.classList.remove('is-selected');
                other.removeAttribute('aria-selected');
            });
            row.classList.add('is-selected');
            row.setAttribute('aria-selected', 'true');
            var radio = row.querySelector('input[type="radio"]');
            if (radio) {
                radio.checked = true;
            }
            var tripCustomerApply = dialogForRow.querySelector('[data-trip-customer-apply]');
            if (tripCustomerApply && row.matches('[data-trip-customer-row]')) {
                tripCustomerApply.disabled = false;
            }
            var apply = dialogForRow.querySelector('[data-search-apply]');
            var href = row.getAttribute('data-search-href');
            if (apply && href) {
                apply.href = href;
                apply.classList.remove('disabled');
                apply.removeAttribute('aria-disabled');
            }
        }
    }, true);

    document.addEventListener('dblclick', function (event) {
        var row = event.target.closest && event.target.closest('[data-search-row]');
        if (!row || event.target.closest('a, button, input, select, textarea')) {
            return;
        }
        var dialogForRow = row.closest('[data-search-dialog]');
        if (dialogForRow) {
            event.preventDefault();
            dialogForRow.querySelectorAll('[data-search-row].is-selected').forEach(function (other) {
                other.classList.remove('is-selected');
                other.removeAttribute('aria-selected');
            });
            row.classList.add('is-selected');
            row.setAttribute('aria-selected', 'true');
            var radio = row.querySelector('input[type="radio"]');
            if (radio) {
                radio.checked = true;
            }
            if (row.matches('[data-trip-customer-row]')) {
                return;
            }
            event.stopPropagation();
        }
        var href = row.getAttribute('data-search-href');
        if (href) {
            window.location.assign(href);
            return;
        }
        var form = row.closest('form');
        if (form && typeof form.requestSubmit === 'function') {
            form.requestSubmit();
        }
    }, true);

    window.addEventListener('DOMContentLoaded', function () {
        document.querySelectorAll('[data-search-dialog][data-search-open-on-load="true"]').forEach(function (dialog) {
            openSearchDialog(dialog);
        });
    });

    function ensureConfirmDialog() {
        var existing = document.getElementById('a4wConfirmDialog');
        if (existing) {
            return existing;
        }

        var overlay = document.createElement('div');
        overlay.id = 'a4wConfirmDialog';
        overlay.className = 'a4w-confirm-overlay';
        overlay.setAttribute('aria-hidden', 'true');
        overlay.innerHTML = [
            '<div class="a4w-confirm-dialog" role="dialog" aria-modal="true" aria-labelledby="a4wConfirmTitle">',
            '  <h2 id="a4wConfirmTitle">Aktion bestaetigen</h2>',
            '  <p data-confirm-message>Diese Aktion ausfuehren?</p>',
            '  <div class="a4w-confirm-actions">',
            '    <button class="btn btn-outline-secondary" type="button" data-confirm-cancel>Abbrechen</button>',
            '    <button class="btn btn-primary" type="button" data-confirm-ok>Bestaetigen</button>',
            '  </div>',
            '</div>'
        ].join('');
        document.body.appendChild(overlay);
        return overlay;
    }

    function openConfirmDialog(form, submitter, source) {
        var dialog = ensureConfirmDialog();
        var title = dialog.querySelector('#a4wConfirmTitle');
        var message = dialog.querySelector('[data-confirm-message]');
        var cancel = dialog.querySelector('[data-confirm-cancel]');
        var ok = dialog.querySelector('[data-confirm-ok]');
        var confirmSource = source || form;

        title.textContent = confirmSource.getAttribute('data-confirm-title') || 'Aktion bestaetigen';
        message.textContent = confirmSource.getAttribute('data-confirm-message') || 'Diese Aktion wirklich ausfuehren?';
        ok.textContent = confirmSource.getAttribute('data-confirm-ok') || 'Bestaetigen';
        cancel.textContent = confirmSource.getAttribute('data-confirm-cancel') || 'Abbrechen';

        function closeDialog() {
            dialog.classList.remove('is-active');
            dialog.setAttribute('aria-hidden', 'true');
        }

        cancel.onclick = closeDialog;
        ok.onclick = function () {
            closeDialog();
            form.dataset.confirmed = 'true';
            if (submitter && typeof form.requestSubmit === 'function') {
                form.requestSubmit(submitter);
            } else if (typeof form.requestSubmit === 'function') {
                form.requestSubmit();
            } else {
                form.submit();
            }
            window.setTimeout(function () {
                delete form.dataset.confirmed;
            }, 1200);
        };

        dialog.classList.add('is-active');
        dialog.setAttribute('aria-hidden', 'false');
        ok.focus();
    }

    function openConfirmLinkDialog(link) {
        var dialog = ensureConfirmDialog();
        var title = dialog.querySelector('#a4wConfirmTitle');
        var message = dialog.querySelector('[data-confirm-message]');
        var cancel = dialog.querySelector('[data-confirm-cancel]');
        var ok = dialog.querySelector('[data-confirm-ok]');

        title.textContent = link.getAttribute('data-confirm-title') || 'Aktion bestaetigen';
        message.textContent = link.getAttribute('data-confirm-message') || 'Diese Aktion wirklich ausfuehren?';
        ok.textContent = link.getAttribute('data-confirm-ok') || 'Bestaetigen';
        cancel.textContent = link.getAttribute('data-confirm-cancel') || 'Abbrechen';

        function closeDialog() {
            dialog.classList.remove('is-active');
            dialog.setAttribute('aria-hidden', 'true');
        }

        cancel.onclick = closeDialog;
        ok.onclick = function () {
            closeDialog();
            link.dataset.confirmed = 'true';
            window.location.assign(link.href);
            window.setTimeout(function () {
                delete link.dataset.confirmed;
            }, 1200);
        };

        dialog.classList.add('is-active');
        dialog.setAttribute('aria-hidden', 'false');
        ok.focus();
    }

    function ensurePreviewDialog() {
        var existing = document.getElementById('a4wPreviewDialog');
        if (existing) {
            return existing;
        }

        var overlay = document.createElement('div');
        overlay.id = 'a4wPreviewDialog';
        overlay.className = 'a4w-preview-overlay';
        overlay.setAttribute('aria-hidden', 'true');
        overlay.innerHTML = [
            '<div class="a4w-preview-dialog" role="dialog" aria-modal="true" aria-labelledby="a4wPreviewTitle">',
            '  <div class="a4w-preview-head">',
            '    <div><h2 id="a4wPreviewTitle">Belegvorschau</h2><p>Vorschau prüfen, archivieren oder ausgeben.</p></div>',
            '    <div class="a4w-window-actions">',
            '      <a class="a4w-icon-button" href="#" target="_blank" title="In eigenem Fenster öffnen" aria-label="In eigenem Fenster öffnen" data-preview-open><span aria-hidden="true">□</span></a>',
            '      <button class="a4w-icon-button" type="button" title="Schliessen" aria-label="Schliessen" data-preview-close><span aria-hidden="true">×</span></button>',
            '    </div>',
            '  </div>',
            '  <iframe data-preview-frame title="Belegvorschau"></iframe>',
            '  <div class="a4w-preview-actions">',
            '    <div class="a4w-preview-document-actions" data-preview-document-actions></div>',
            '    <button class="btn btn-outline-secondary" type="button" data-preview-close>Abbrechen</button>',
            '    <a class="btn btn-primary" href="#" target="_blank" data-preview-print>Drucken</a>',
            '  </div>',
            '</div>'
        ].join('');
        document.body.appendChild(overlay);
        return overlay;
    }

    function closePreviewDialog(dialog) {
        if (!dialog) {
            return;
        }
        var frame = dialog.querySelector('[data-preview-frame]');
        var actions = dialog.querySelector('[data-preview-document-actions]');
        dialog.classList.remove('is-active');
        dialog.setAttribute('aria-hidden', 'true');
        if (frame) {
            frame.removeAttribute('src');
        }
        if (actions) {
            actions.innerHTML = '';
        }
    }

    function openPreviewDialog(link) {
        var dialog = ensurePreviewDialog();
        var title = dialog.querySelector('#a4wPreviewTitle');
        var frame = dialog.querySelector('[data-preview-frame]');
        var close = dialog.querySelector('[data-preview-close]');
        var open = dialog.querySelector('[data-preview-open]');
        var print = dialog.querySelector('[data-preview-print]');
        var actions = dialog.querySelector('[data-preview-document-actions]');
        var href = link.getAttribute('href') || '';
        if (!href) {
            return;
        }

        var previewHref = isDocumentPrintPreview(href)
            ? addQueryParameter(href, 'skipCompanions', '1')
            : href;
        title.textContent = link.getAttribute('data-preview-title') || 'Belegvorschau';
        frame.src = previewHref;
        open.href = previewHref;
        var printHref = link.getAttribute('data-preview-print-href');
        var printLabel = link.getAttribute('data-preview-print-label');
        var hidePrint = link.getAttribute('data-preview-hide-print') === 'true';
        print.hidden = hidePrint;
        if (!hidePrint) {
            print.href = printHref || (href.indexOf('?') === -1 ? href + '?print=1' : href + '&print=1');
            print.textContent = printLabel || 'Drucken';
            print.onclick = function (event) {
                var message = 'Drucken starten? Bei Rechnungen wird der Beleg danach, sofern die Rechnungssperre aktiv ist, finalisiert und nicht mehr direkt bearbeitbar.';
                if (!window.confirm(message)) {
                    event.preventDefault();
                }
            };
        } else {
            print.onclick = null;
        }
        actions.innerHTML = '';

        var actionSourceSelector = link.getAttribute('data-preview-action-source');
        var actionSource = actionSourceSelector ? document.querySelector(actionSourceSelector) : null;
        if (actionSource) {
            Array.prototype.forEach.call(actionSource.children, function (child) {
                var clone = child.cloneNode(true);
                if (clone.matches && clone.matches('form') && !clone.querySelector('input[name="returnUrl"]')) {
                    var returnInput = document.createElement('input');
                    returnInput.type = 'hidden';
                    returnInput.name = 'returnUrl';
                    returnInput.value = window.location.pathname + window.location.search;
                    clone.appendChild(returnInput);
                }
                actions.appendChild(clone);
            });
            actions.hidden = actions.children.length === 0;
        } else {
            actions.hidden = true;
        }

        dialog.querySelectorAll('[data-preview-close]').forEach(function (button) {
            button.onclick = function () { closePreviewDialog(dialog); };
        });
        dialog.classList.add('is-active');
        dialog.setAttribute('aria-hidden', 'false');
        if (close) {
            close.focus();
        }
    }

    function addQueryParameter(href, name, value) {
        var hashIndex = href.indexOf('#');
        var hash = hashIndex >= 0 ? href.slice(hashIndex) : '';
        var base = hashIndex >= 0 ? href.slice(0, hashIndex) : href;
        var separator = base.indexOf('?') === -1 ? '?' : '&';
        return base + separator + encodeURIComponent(name) + '=' + encodeURIComponent(value) + hash;
    }

    function isDocumentPrintPreview(href) {
        return /^\/Print\/[0-9a-f-]{36}(?:[?#]|$)/i.test(href);
    }

    function ensureInternalEmailDialog() {
        var existing = document.getElementById('internalEmailDialog');
        if (existing) {
            return existing;
        }

        var overlay = document.createElement('div');
        overlay.id = 'internalEmailDialog';
        overlay.className = 'internal-email-overlay';
        overlay.setAttribute('aria-hidden', 'true');
        overlay.innerHTML = [
            '<div class="internal-email-dialog" role="dialog" aria-modal="true" aria-labelledby="internalEmailTitle">',
            '  <div class="internal-email-head">',
            '    <div><h2 id="internalEmailTitle">E-Mail versenden</h2><p data-email-summary>Beleg wird als PDF-Anlage versendet.</p></div>',
            '    <button class="btn btn-outline-secondary btn-sm" type="button" data-email-close>Schliessen</button>',
            '  </div>',
            '  <div class="internal-email-grid">',
            '    <div class="wide"><div class="internal-email-field-head"><label class="field-label" for="internalEmailRecipient">Empfaengeradressen</label><button class="btn btn-sm btn-outline-secondary" type="button" data-email-add-recipient>+ Adresse</button></div><textarea class="form-control" id="internalEmailRecipient" rows="2" autocomplete="email"></textarea><div class="muted small mt-1">Eine Adresse pro Zeile, alternativ mit Komma oder Semikolon trennen.</div></div>',
            '    <label class="form-check wide"><input class="form-check-input" id="internalEmailBccEnabled" type="checkbox"> Blindkopie mitsenden</label>',
            '    <div class="wide"><label class="field-label" for="internalEmailBcc">Blindkopie-Adressen</label><textarea class="form-control" id="internalEmailBcc" rows="3" autocomplete="email"></textarea><div class="muted small mt-1">Eine Adresse pro Zeile, alternativ mit Komma oder Semikolon trennen. BCC ist fuer Kunden nicht sichtbar.</div></div>',
            '    <label class="form-check wide"><input class="form-check-input" id="internalEmailReadReceipt" type="checkbox"> Lesebestaetigung anfordern</label>',
            '    <label class="form-check wide"><input class="form-check-input" id="internalEmailDeliveryReceipt" type="checkbox"> Uebertragungsbestaetigung anfordern <span class="muted small">Rueckmeldungen haengen vom Empfaenger-Mailserver ab.</span></label>',
            '  </div>',
            '  <div class="internal-email-status" data-email-status>Bitte Empfaenger pruefen und Versand bewusst starten.</div>',
            '  <div class="internal-email-actions">',
            '    <button class="btn btn-outline-secondary" type="button" data-email-cancel>Abbrechen</button>',
            '    <button class="btn btn-primary" type="button" data-email-send>Versand starten</button>',
            '  </div>',
            '</div>'
        ].join('');
        document.body.appendChild(overlay);
        return overlay;
    }

    function openInternalEmailDialog(form) {
        var dialog = ensureInternalEmailDialog();
        var recipient = dialog.querySelector('#internalEmailRecipient');
        var bccEnabled = dialog.querySelector('#internalEmailBccEnabled');
        var bcc = dialog.querySelector('#internalEmailBcc');
        var readReceipt = dialog.querySelector('#internalEmailReadReceipt');
        var deliveryReceipt = dialog.querySelector('#internalEmailDeliveryReceipt');
        var title = dialog.querySelector('#internalEmailTitle');
        var summary = dialog.querySelector('[data-email-summary]');
        var status = dialog.querySelector('[data-email-status]');
        var send = dialog.querySelector('[data-email-send]');
        var addRecipient = dialog.querySelector('[data-email-add-recipient]');
        var close = dialog.querySelector('[data-email-close]');
        var cancel = dialog.querySelector('[data-email-cancel]');
        var defaultBccEnabled = (form.getAttribute('data-email-bcc-enabled') || '').toLowerCase() === 'true';
        var defaultBccAddress = form.getAttribute('data-email-bcc-address') || '';
        var defaultReadReceipt = (form.getAttribute('data-email-read-receipt') || '').toLowerCase() === 'true';
        var defaultDeliveryReceipt = (form.getAttribute('data-email-delivery-receipt') || '').toLowerCase() === 'true';
        var lastResultWasSuccess = false;
        var redirectAfterClose = '';
        var keepPreviewOpen = form.getAttribute('data-preview-action') === 'true' || !!form.closest('#a4wPreviewDialog');

        title.textContent = form.getAttribute('data-email-title') || 'E-Mail versenden';
        summary.textContent = 'Der Beleg wird als PDF-Anlage direkt ueber den eingerichteten SMTP-Zugang gesendet.';
        recipient.value = form.getAttribute('data-email-recipient') || '';
        bccEnabled.checked = defaultBccEnabled && !!defaultBccAddress;
        bcc.value = defaultBccAddress;
        bcc.disabled = !bccEnabled.checked;
        readReceipt.checked = defaultReadReceipt;
        deliveryReceipt.checked = defaultDeliveryReceipt;
        status.className = 'internal-email-status';
        status.textContent = 'Bitte Empfaenger pruefen und Versand bewusst starten.';
        send.disabled = false;
        send.textContent = 'Versand starten';
        cancel.disabled = false;
        lastResultWasSuccess = false;
        redirectAfterClose = '';

        function closeDialog() {
            dialog.classList.remove('is-active');
            dialog.setAttribute('aria-hidden', 'true');
            if (lastResultWasSuccess && !keepPreviewOpen) {
                window.location.assign(redirectAfterClose || window.location.href);
            }
        }

        function setStatus(text, kind) {
            status.className = 'internal-email-status' + (kind ? ' is-' + kind : '');
            status.textContent = text;
        }

        if (addRecipient) {
            addRecipient.onclick = function () {
                var current = recipient.value;
                recipient.value = current.trim()
                    ? current.replace(/\s+$/, '') + '\n'
                    : '';
                recipient.focus();
            };
        }

        function startSend() {
            if (!recipient.value.trim()) {
                setStatus('Bitte mindestens eine Empfaengeradresse eintragen.', 'error');
                recipient.focus();
                return;
            }

            var payload = new FormData(form);
            payload.set('recipientEmail', recipient.value.trim());
            if (bccEnabled.checked && bcc.value.trim()) {
                payload.set('bccEnabled', 'true');
                payload.set('bccAddress', bcc.value.trim());
            } else {
                payload.delete('bccEnabled');
                payload.set('bccAddress', '');
            }
            if (readReceipt.checked) {
                payload.set('requestReadReceipt', 'true');
            } else {
                payload.delete('requestReadReceipt');
            }
            if (deliveryReceipt.checked) {
                payload.set('requestDeliveryReceipt', 'true');
            } else {
                payload.delete('requestDeliveryReceipt');
            }

            send.disabled = true;
            cancel.disabled = true;
            send.textContent = 'Versand laeuft...';
            setStatus('E-Mail wird gesendet. Dieses Fenster bleibt bis zur Rueckmeldung offen.', 'pending');

            fetch(form.action || window.location.href, {
                method: (form.method || 'post').toUpperCase(),
                body: payload,
                credentials: 'same-origin',
                headers: { 'X-Requested-With': 'XMLHttpRequest' }
            })
                .then(function (response) {
                    return response.json().catch(function () {
                        return { success: false, message: 'Die Serverantwort konnte nicht gelesen werden.' };
                    });
                })
                .then(function (result) {
                    lastResultWasSuccess = !!result.success;
                    redirectAfterClose = result.redirect || '';
                    setStatus(result.message || (result.success ? 'E-Mail wurde gesendet.' : 'E-Mail konnte nicht gesendet werden.'), result.success ? 'success' : 'error');
                    send.textContent = result.success ? 'Gesendet' : 'Erneut versuchen';
                    send.disabled = !!result.success;
                    cancel.disabled = false;
                    cancel.textContent = result.success ? 'Schliessen' : 'Abbrechen';
                    close.textContent = result.success ? 'Schliessen' : 'Schliessen';
                })
                .catch(function (error) {
                    setStatus('E-Mail-Versand wurde abgebrochen: ' + error.message, 'error');
                    send.disabled = false;
                    cancel.disabled = false;
                    send.textContent = 'Erneut versuchen';
                });
        }

        bccEnabled.onchange = function () {
            bcc.disabled = !bccEnabled.checked;
            if (bccEnabled.checked && !bcc.value) {
                bcc.focus();
            }
        };
        send.onclick = startSend;
        close.onclick = closeDialog;
        cancel.onclick = closeDialog;
        dialog.classList.add('is-active');
        dialog.setAttribute('aria-hidden', 'false');
        recipient.focus();
    }

    document.addEventListener('submit', function (event) {
        if (!event.target || event.target.matches('[data-no-busy]') || (event.submitter && event.submitter.matches('[data-no-busy]'))) {
            return;
        }
        if (event.target.dataset.submitted === 'true') {
            event.preventDefault();
            return;
        }
        event.target.dataset.submitted = 'true';
        var submittedButton = event.submitter || null;
        window.setTimeout(function () {
            Array.prototype.slice.call(event.target.querySelectorAll('button, input[type="submit"]')).forEach(function (button) {
                if (button === submittedButton) {
                    return;
                }
                button.disabled = true;
            });
        }, 0);
        if (event.target.matches('[data-download-form]') || (event.submitter && event.submitter.matches('[data-download]'))) {
            window.setTimeout(hideBusy, 500);
            return;
        }
        showBusy(event.target.getAttribute('data-busy-text') || (event.submitter && event.submitter.getAttribute('data-busy-text')));
    });

    document.addEventListener('click', function (event) {
        var link = event.target.closest && event.target.closest('a[href]');
        if (!link || link.target === '_blank' || link.href.startsWith('mailto:') || link.hasAttribute('download') || link.matches('[data-no-busy]') || link.matches('[data-download]')) {
            return;
        }
        var href = link.getAttribute('href') || '';
        if (href.indexOf('handler=Pdf') !== -1 || href.indexOf('handler=Excel') !== -1 || href.indexOf('handler=Datev') !== -1 || href.indexOf('handler=Download') !== -1 || href.indexOf('/download') !== -1) {
            window.setTimeout(hideBusy, 500);
            return;
        }
        if (href === '#' || href.startsWith('#') || link.hasAttribute('data-bs-toggle')) {
            return;
        }
        if (link.origin === window.location.origin && link.pathname === window.location.pathname && link.hash) {
            return;
        }
        if (link.origin === window.location.origin) {
            showBusy();
        }
    });

    window.auftragoEnableSettingsSearch = function () {
        var input = document.getElementById('settingsSearch');
        if (!input) {
            return;
        }

        var sections = Array.prototype.slice.call(document.querySelectorAll('[data-settings-section]'));
        var tabs = Array.prototype.slice.call(document.querySelectorAll('[data-settings-tab]'));
        var activeTab = '';

        function tabFromHash() {
            var hash = (window.location.hash || '').replace('#', '');
            var target = hash ? document.getElementById(hash) : null;
            return target ? target.getAttribute('data-settings-panel') || 'general' : 'general';
        }

        function applySettingsView() {
            var query = (input.value || '').trim().toLowerCase();
            if (!query) {
                activeTab = activeTab || tabFromHash();
            }

            sections.forEach(function (section) {
                var haystack = ((section.getAttribute('data-settings-section') || '') + ' ' + section.textContent).toLowerCase();
                var matchesSearch = query.length === 0 || haystack.indexOf(query) !== -1;
                var matchesTab = query.length > 0 || (section.getAttribute('data-settings-panel') || 'general') === activeTab;
                section.classList.toggle('is-filtered-out', !matchesSearch || !matchesTab);
                section.classList.toggle('is-tab-active', matchesSearch && matchesTab);
            });

            tabs.forEach(function (tab) {
                var selected = (tab.getAttribute('data-settings-tab') || 'general') === activeTab && query.length === 0;
                tab.classList.toggle('is-active', selected);
                tab.setAttribute('aria-current', selected ? 'page' : 'false');
            });
        }

        tabs.forEach(function (tab) {
            tab.addEventListener('click', function () {
                activeTab = tab.getAttribute('data-settings-tab') || 'general';
                input.value = '';
                window.setTimeout(applySettingsView, 0);
            });
        });

        input.addEventListener('input', applySettingsView);
        window.addEventListener('hashchange', function () {
            activeTab = tabFromHash();
            applySettingsView();
        });
        activeTab = tabFromHash();
        applySettingsView();
    };

    window.auftragoEnableTemplatePlaceholders = function () {
        var textarea = document.getElementById('templateHtml');
        if (!textarea && !document.querySelector('[data-insert-target]')) {
            return;
        }
        document.querySelectorAll('[data-insert]').forEach(function (button) {
            button.addEventListener('click', function () {
                var token = button.getAttribute('data-insert') || '';
                var targetSelector = button.getAttribute('data-insert-target') || '';
                var target = targetSelector ? document.querySelector(targetSelector) : textarea;
                if (!target) {
                    return;
                }
                var start = target.selectionStart || 0;
                var end = target.selectionEnd || 0;
                target.value = target.value.substring(0, start) + token + target.value.substring(end);
                target.focus();
                target.selectionStart = target.selectionEnd = start + token.length;
                target.dispatchEvent(new Event('input', { bubbles: true }));
            });
        });
    };

    // Kamera-Scanner fuer Artikelnummern, EAN/GTIN, QR-Codes und Belegnummern.
    // Die Web-App nutzt die native BarcodeDetector-Schnittstelle des Browsers.
    // Das funktioniert vor allem auf modernen Smartphone-Browsern und benoetigt HTTPS
    // oder localhost. Wenn der Browser die Funktion nicht anbietet, bekommt der Nutzer
    // einen klaren Hinweis und kann den Wert weiterhin normal eintippen.
    window.auftragoEnableCodeScanner = function () {
        var buttons = Array.prototype.slice.call(document.querySelectorAll('[data-scan-target]'));
        if (buttons.length === 0) {
            return;
        }

        var overlay;
        var video;
        var message;
        var stopButton;
        var stream;
        var scanTimer;
        var detector;
        var activeTarget;

        function ensureOverlay() {
            if (overlay) {
                return;
            }

            overlay = document.createElement('div');
            overlay.className = 'code-scan-overlay';
            overlay.setAttribute('aria-hidden', 'true');
            overlay.innerHTML = [
                '<div class="code-scan-dialog" role="dialog" aria-modal="true" aria-label="Code scannen">',
                '  <div class="code-scan-head">',
                '    <div><strong>Code scannen</strong><div class="muted small">EAN, QR-Code oder Barcode ruhig in den Rahmen halten.</div></div>',
                '    <button class="btn btn-outline-secondary btn-sm" type="button" data-scan-stop>Schließen</button>',
                '  </div>',
                '  <video class="code-scan-video" playsinline muted></video>',
                '  <div class="code-scan-frame" aria-hidden="true"></div>',
                '  <div class="status-note small code-scan-message">Kamera wird gestartet...</div>',
                '</div>'
            ].join('');
            document.body.appendChild(overlay);
            video = overlay.querySelector('video');
            message = overlay.querySelector('.code-scan-message');
            stopButton = overlay.querySelector('[data-scan-stop]');
            stopButton.addEventListener('click', stopScan);
        }

        function setMessage(text) {
            if (message) {
                message.textContent = text;
            }
        }

        function stopScan() {
            if (scanTimer) {
                window.clearInterval(scanTimer);
                scanTimer = null;
            }
            if (stream) {
                stream.getTracks().forEach(function (track) { track.stop(); });
                stream = null;
            }
            if (video) {
                video.srcObject = null;
            }
            if (overlay) {
                overlay.classList.remove('is-active');
                overlay.setAttribute('aria-hidden', 'true');
            }
            activeTarget = null;
        }

        function resolveTarget(button) {
            var selector = button.getAttribute('data-scan-target') || '';
            var scope = button.closest('form') || document;
            return scope.querySelector(selector) || document.querySelector(selector);
        }

        function fillTarget(value) {
            if (!activeTarget || !value) {
                return;
            }
            activeTarget.value = value.trim();
            activeTarget.dispatchEvent(new Event('input', { bubbles: true }));
            activeTarget.dispatchEvent(new Event('change', { bubbles: true }));
            activeTarget.focus();
        }

        async function startScan(button) {
            ensureOverlay();
            activeTarget = resolveTarget(button);
            if (!activeTarget) {
                window.alert('Das Zielfeld fuer den Scan wurde nicht gefunden. Bitte den Wert manuell eintragen.');
                return;
            }
            if (!('BarcodeDetector' in window)) {
                window.alert('Dieser Browser unterstützt den Kamera-Scanner nicht. Bitte Chrome/Edge auf dem Smartphone verwenden oder den Code manuell eintragen.');
                return;
            }
            if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
                window.alert('Die Kamera kann von diesem Browser nicht geöffnet werden. Bitte HTTPS verwenden oder den Code manuell eintragen.');
                return;
            }

            try {
                detector = detector || new BarcodeDetector({
                    formats: ['qr_code', 'ean_13', 'ean_8', 'code_128', 'code_39', 'upc_a', 'upc_e', 'itf']
                });
                overlay.classList.add('is-active');
                overlay.setAttribute('aria-hidden', 'false');
                setMessage('Kamera wird gestartet. Bitte Zugriff erlauben.');
                stream = await navigator.mediaDevices.getUserMedia({
                    video: { facingMode: { ideal: 'environment' } },
                    audio: false
                });
                video.srcObject = stream;
                await video.play();
                setMessage('Code in den Rahmen halten. Der erkannte Wert wird automatisch übernommen.');

                scanTimer = window.setInterval(async function () {
                    if (!video || video.readyState < 2 || !detector) {
                        return;
                    }
                    try {
                        var codes = await detector.detect(video);
                        if (codes && codes.length > 0) {
                            var rawValue = codes[0].rawValue || '';
                            fillTarget(rawValue);
                            stopScan();
                        }
                    } catch (scanError) {
                        setMessage('Der Code konnte noch nicht gelesen werden. Bitte Kamera ruhig halten oder Abstand ändern.');
                    }
                }, 350);
            } catch (error) {
                stopScan();
                window.alert('Kamera-Scan konnte nicht gestartet werden. Bitte Kamerazugriff erlauben oder den Code manuell eintragen.');
            }
        }

        buttons.forEach(function (button) {
            button.addEventListener('click', function () {
                startScan(button);
            });
        });
    };

    window.auftragoEnableTripLog = function () {
        if (document.documentElement.dataset.tripLogInitialized === 'true') {
            return;
        }
        document.documentElement.dataset.tripLogInitialized = 'true';
        var prefillElement = document.getElementById('tripLogPrefillData');
        var prefill = {};
        if (prefillElement) {
            try {
                prefill = JSON.parse(prefillElement.textContent || '{}');
            } catch (_) {
                prefill = {};
            }
        }
        var customers = prefill.customers || [];
        var vehicles = prefill.vehicles || [];
        var projects = prefill.projects || [];
        var entries = prefill.entries || [];
        var customerPageSize = 80;

        var updateRouteLinks = function (form) {
            if (!form) {
                return;
            }
            var destination = form.querySelector('[data-trip-destination], input[name="destinationAddress"]');
            var routeLink = form.querySelector('[data-trip-route-link]');
            if (!destination || !routeLink) {
                return;
            }
            var target = (destination.value || '').trim();
            if (!target) {
                routeLink.classList.add('d-none');
                routeLink.href = '#';
                return;
            }
            var origin = (prefill.companyRouteAddress || '').trim();
            var url = 'https://www.google.com/maps/dir/?api=1&destination=' + encodeURIComponent(target);
            if (origin) {
                url += '&origin=' + encodeURIComponent(origin);
            }
            routeLink.href = url;
            routeLink.classList.remove('d-none');
        };

        var findCustomer = function (value) {
            var normalized = (value || '').trim().toLowerCase();
            if (!normalized) {
                return null;
            }
            return customers.find(function (customer) {
                return (customer.id || '').toLowerCase() === normalized ||
                    (customer.label || '').toLowerCase() === normalized ||
                    (customer.name || '').toLowerCase() === normalized ||
                    (customer.search || '').toLowerCase().indexOf(normalized) !== -1;
            }) || null;
        };

        var applyCustomer = function (form, customerSelect, customerSearch, customer) {
            if (!customerSelect) {
                return;
            }
            var summary = form.querySelector('[data-trip-customer-summary]');
            if (!customer) {
                customerSelect.value = '';
                if (summary) {
                    summary.textContent = 'Ohne Kundenbezug';
                }
                return;
            }
            customerSelect.value = customer.id;
            if (customerSearch) {
                customerSearch.value = customer.label || customer.name || '';
            }
            if (summary) {
                summary.textContent = customer.name || customer.label || 'Kunde ausgewählt';
            }
            var dialog = customerSearch ? customerSearch.closest('[data-search-dialog]') : null;
            var filter = dialog ? dialog.querySelector('[data-trip-customer-filter]') : null;
            if (filter) {
                filter.value = customer.label || customer.name || '';
            }
            var destination = form.querySelector('[data-trip-destination], input[name="destinationAddress"]');
            if (destination && !destination.value && customer.address) {
                destination.value = customer.address;
            }
            updateRouteLinks(form);
            if (dialog) {
                dialog.classList.remove('is-active');
                dialog.setAttribute('aria-hidden', 'true');
            }
        };

        var customerMatches = function (customer, query) {
            var normalized = (query || '').trim().toLowerCase();
            if (!normalized) {
                return true;
            }
            return (customer.search || '').toLowerCase().indexOf(normalized) !== -1 ||
                (customer.label || '').toLowerCase().indexOf(normalized) !== -1 ||
                (customer.name || '').toLowerCase().indexOf(normalized) !== -1;
        };

        var selectedCustomerFromDialog = function (dialog) {
            var selectedRow = dialog ? dialog.querySelector('[data-trip-customer-row].is-selected') : null;
            var id = selectedRow ? selectedRow.getAttribute('data-trip-customer-id') : '';
            return id ? customers.find(function (customer) { return customer.id === id; }) || null : null;
        };

        var ownerFormFromDialog = function (dialog) {
            return dialog ? (dialog._a4wSearchOwner || dialog.closest('form')) : null;
        };

        var syncTripCustomerApply = function (dialog) {
            if (!dialog) {
                return;
            }
            var apply = dialog.querySelector('[data-trip-customer-apply]');
            if (apply) {
                apply.disabled = !selectedCustomerFromDialog(dialog);
            }
        };

        var selectTripCustomerRow = function (row, applyNow) {
            var dialog = row ? row.closest('[data-search-dialog]') : null;
            if (!dialog) {
                return;
            }
            dialog.querySelectorAll('[data-trip-customer-row].is-selected').forEach(function (other) {
                other.classList.remove('is-selected');
                other.removeAttribute('aria-selected');
            });
            row.classList.add('is-selected');
            row.setAttribute('aria-selected', 'true');
            syncTripCustomerApply(dialog);
            if (!applyNow) {
                return;
            }
            var form = ownerFormFromDialog(dialog);
            var customer = customers.find(function (item) { return item.id === row.getAttribute('data-trip-customer-id'); });
            if (customer && form) {
                applyCustomer(form, form.querySelector('select[name="customerId"]'), dialog.querySelector('[data-trip-customer-search]'), customer);
            }
        };

        var filterTripCustomerDialog = function (input) {
            var dialog = input.closest('[data-search-dialog]');
            if (!dialog) {
                return;
            }
            var query = input.value || '';
            var page = parseInt(dialog.getAttribute('data-trip-customer-page') || '0', 10) || 0;
            var matchingRows = [];
            dialog.querySelectorAll('[data-trip-customer-row]').forEach(function (row) {
                var customer = customers.find(function (item) { return item.id === row.getAttribute('data-trip-customer-id'); });
                var matches = customer ? customerMatches(customer, query) : false;
                if (matches) {
                    matchingRows.push(row);
                }
                row.hidden = true;
                if (!matches) {
                    row.classList.remove('is-selected');
                    row.removeAttribute('aria-selected');
                }
            });
            var pageCount = Math.max(1, Math.ceil(matchingRows.length / customerPageSize));
            if (page >= pageCount) {
                page = pageCount - 1;
                dialog.setAttribute('data-trip-customer-page', page.toString());
            }
            matchingRows.slice(page * customerPageSize, (page + 1) * customerPageSize).forEach(function (row) {
                row.hidden = false;
            });
            var pager = dialog.querySelector('[data-trip-customer-pager]');
            if (!pager) {
                pager = document.createElement('div');
                pager.className = 'a4w-search-actions';
                pager.setAttribute('data-trip-customer-pager', 'true');
                pager.innerHTML = '<span class="muted small" data-trip-customer-pager-label></span><button class="btn btn-outline-secondary btn-sm" type="button" data-trip-customer-prev>Zurueck</button><button class="btn btn-outline-secondary btn-sm" type="button" data-trip-customer-next>Weiter</button>';
                var results = dialog.querySelector('[data-trip-customer-results]');
                if (results) {
                    results.insertAdjacentElement('afterend', pager);
                }
            }
            var label = pager.querySelector('[data-trip-customer-pager-label]');
            var prev = pager.querySelector('[data-trip-customer-prev]');
            var next = pager.querySelector('[data-trip-customer-next]');
            if (label) {
                var start = matchingRows.length === 0 ? 0 : (page * customerPageSize) + 1;
                var end = Math.min(matchingRows.length, (page + 1) * customerPageSize);
                label.textContent = start + '-' + end + ' von ' + matchingRows.length + ' Kunden';
            }
            if (prev) {
                prev.disabled = page <= 0;
            }
            if (next) {
                next.disabled = page >= pageCount - 1;
            }
            syncTripCustomerApply(dialog);
        };

        var bindCustomerSearch = function (form, customerSelect, customerSearch) {
            if (!form || !customerSelect || !customerSearch || customerSearch.dataset.tripCustomerBound === 'true') {
                return;
            }
            var dialog = customerSearch.closest('[data-search-dialog]');
            if (dialog && !dialog._a4wSearchOwner) {
                dialog._a4wSearchOwner = form;
            }
            customerSearch.dataset.tripCustomerBound = 'true';
            var syncFromInput = function () {
                var customer = findCustomer(customerSearch.value);
                if (customer || !(customerSearch.value || '').trim()) {
                    applyCustomer(form, customerSelect, customerSearch, customer);
                }
            };
            customerSearch.addEventListener('change', syncFromInput);
            customerSearch.addEventListener('blur', syncFromInput);
            form.addEventListener('submit', syncFromInput);
            var selected = customers.find(function (customer) {
                return customer.id === customerSelect.value || customer.id === prefill.prefillCustomerId;
            });
            if (selected) {
                applyCustomer(form, customerSelect, customerSearch, selected);
            }
        };

        document.querySelectorAll('[data-trip-customer-search]').forEach(function (customerSearch) {
            var form = customerSearch.closest('form');
            bindCustomerSearch(form, form ? form.querySelector('select[name="customerId"]') : null, customerSearch);
        });

        document.querySelectorAll('[data-trip-customer-filter]').forEach(function (input) {
            input.addEventListener('input', function () {
                var dialog = input.closest('[data-search-dialog]');
                if (dialog) {
                    dialog.setAttribute('data-trip-customer-page', '0');
                }
                filterTripCustomerDialog(input);
            });
            filterTripCustomerDialog(input);
        });

        document.addEventListener('click', function (event) {
            var row = event.target.closest && event.target.closest('[data-trip-customer-row]');
            if (row && !event.target.closest('a, button, input, select, textarea')) {
                selectTripCustomerRow(row, false);
                return;
            }

            var apply = event.target.closest && event.target.closest('[data-trip-customer-apply]');
            var pageButton = event.target.closest && event.target.closest('[data-trip-customer-prev], [data-trip-customer-next]');
            if (pageButton) {
                var pageDialog = pageButton.closest('[data-search-dialog]');
                var filter = pageDialog ? pageDialog.querySelector('[data-trip-customer-filter]') : null;
                var currentPage = parseInt(pageDialog.getAttribute('data-trip-customer-page') || '0', 10) || 0;
                pageDialog.setAttribute('data-trip-customer-page', (currentPage + (pageButton.matches('[data-trip-customer-next]') ? 1 : -1)).toString());
                if (filter) {
                    filterTripCustomerDialog(filter);
                }
                return;
            }

            if (!apply) {
                return;
            }
            var dialog = apply.closest('[data-search-dialog]');
            var customer = selectedCustomerFromDialog(dialog);
            if (!customer) {
                return;
            }
            var form = ownerFormFromDialog(dialog);
            var customerSelect = form ? form.querySelector('select[name="customerId"]') : null;
            var customerSearch = dialog ? dialog.querySelector('[data-trip-customer-search]') : null;
            applyCustomer(form, customerSelect, customerSearch, customer);
        }, true);

        document.addEventListener('dblclick', function (event) {
            var row = event.target.closest && event.target.closest('[data-trip-customer-row]');
            if (!row || event.target.closest('a, button, input, select, textarea')) {
                return;
            }
            event.preventDefault();
            event.stopPropagation();
            selectTripCustomerRow(row, true);
        }, true);

        document.querySelectorAll('form[data-trip-geo-form]').forEach(function (form) {
            var customerSelect = form.querySelector('select[name="customerId"]');
            if (customerSelect && customers.length) {
                customerSelect.classList.add('d-none');
            }

            var vehicleSelect = form.querySelector('[data-trip-vehicle], select[name="vehicleId"]');
            var startOdometer = form.querySelector('[data-trip-start-odometer], input[name="startOdometer"]');
            if (vehicleSelect && startOdometer) {
                var syncOdometer = function () {
                    var currentValue = (startOdometer.value || '').trim();
                    var lastSuggestion = startOdometer.dataset.tripSuggestedOdometer || '';
                    if (currentValue && currentValue !== lastSuggestion) {
                        return;
                    }
                    var vehicle = vehicles.find(function (item) { return item.id === vehicleSelect.value; });
                    if (vehicle && vehicle.odometer > 0) {
                        var suggestedOdometer = vehicle.odometer.toString();
                        startOdometer.value = suggestedOdometer;
                        startOdometer.dataset.tripSuggestedOdometer = suggestedOdometer;
                    } else if (!currentValue) {
                        startOdometer.dataset.tripSuggestedOdometer = '';
                    }
                };
                vehicleSelect.addEventListener('change', syncOdometer);
                syncOdometer();
            }
        });

        var parseTripNumber = function (value) {
            var normalized = (value || '').toString().replace(',', '.').trim();
            if (!normalized) {
                return null;
            }
            var parsed = Number(normalized);
            return Number.isFinite(parsed) ? parsed : null;
        };

        document.querySelectorAll('form').forEach(function (form) {
            var startOdometer = form.querySelector('[data-trip-start-odometer], input[name="startOdometer"]');
            var endOdometer = form.querySelector('[data-trip-end-odometer], input[name="endOdometer"]');
            var distance = form.querySelector('[data-trip-distance], input[name="distanceKm"]');
            if (!endOdometer || !distance) {
                return;
            }
            var syncDistance = function () {
                var currentDistance = (distance.value || '').trim();
                var lastSuggestion = distance.dataset.tripSuggestedDistance || '';
                if (currentDistance && currentDistance !== lastSuggestion) {
                    return;
                }
                var startValue = startOdometer ? parseTripNumber(startOdometer.value) : parseTripNumber(form.dataset.tripStartValue);
                var endValue = parseTripNumber(endOdometer.value);
                if (startValue === null || endValue === null || endValue < startValue) {
                    distance.dataset.tripSuggestedDistance = '';
                    return;
                }
                var suggested = (Math.round((endValue - startValue) * 10) / 10).toString();
                distance.value = suggested;
                distance.dataset.tripSuggestedDistance = suggested;
            };
            if (startOdometer) {
                startOdometer.addEventListener('input', syncDistance);
                startOdometer.addEventListener('change', syncDistance);
            }
            endOdometer.addEventListener('input', syncDistance);
            endOdometer.addEventListener('change', syncDistance);
            syncDistance();
        });

        document.querySelectorAll('[data-trip-route-link]').forEach(function (routeLink) {
            var form = routeLink.closest('form');
            if (!form) {
                return;
            }
            updateRouteLinks(form);
            var destination = form.querySelector('[data-trip-destination], input[name="destinationAddress"]');
            if (destination && destination.dataset.tripRouteBound !== 'true') {
                destination.dataset.tripRouteBound = 'true';
                destination.addEventListener('input', function () { updateRouteLinks(form); });
                destination.addEventListener('change', function () { updateRouteLinks(form); });
            }
        });

        document.querySelectorAll('[data-trip-project]').forEach(function (projectSelect) {
            var form = projectSelect.closest('form');
            var entrySelect = form ? form.querySelector('select[name="workEntryId"]') : null;
            if (!entrySelect) {
                return;
            }
            var allOptions = Array.prototype.slice.call(entrySelect.querySelectorAll('option'));
            var syncEntries = function () {
                var projectId = projectSelect.value || '';
                allOptions.forEach(function (option) {
                    var optionProject = option.getAttribute('data-project-id') || '';
                    option.hidden = optionProject && projectId && optionProject !== projectId;
                    if (option.hidden && option.selected) {
                        entrySelect.value = '';
                    }
                });
                var project = projects.find(function (item) { return item.id === projectId; });
                var customerSelect = form ? form.querySelector('select[name="customerId"]') : null;
                if (project && customerSelect && !customerSelect.value) {
                    customerSelect.value = project.customerId;
                    var customer = customers.find(function (item) { return item.id === project.customerId; });
                    var customerSearch = form.querySelector('[data-trip-customer-search]');
                    if (customerSearch && customer) {
                        customerSearch.value = customer.label || customer.name || '';
                    }
                    var destination = form.querySelector('[data-trip-destination], input[name="destinationAddress"]');
                    if (destination && !destination.value && customer && customer.address) {
                        destination.value = customer.address;
                    }
                    updateRouteLinks(form);
                }
            };
            projectSelect.addEventListener('change', syncEntries);
            if (prefill.prefillProjectId && !projectSelect.value) {
                projectSelect.value = prefill.prefillProjectId;
            }
            syncEntries();
            if (entrySelect) {
                if (prefill.prefillWorkEntryId && !entrySelect.value) {
                    entrySelect.value = prefill.prefillWorkEntryId;
                }
                entrySelect.addEventListener('change', function () {
                    var entry = entries.find(function (item) { return item.id === entrySelect.value; });
                    var destination = form ? form.querySelector('[data-trip-destination], input[name="destinationAddress"]') : null;
                    if (entry && destination && entry.location && !destination.value) {
                        destination.value = entry.location;
                    }
                    var purpose = form ? form.querySelector('input[name="purpose"]') : null;
                    if (entry && purpose && entry.text && !purpose.value) {
                        purpose.value = entry.text;
                    }
                    updateRouteLinks(form);
                });
            }
        });

        var normalizeTripAddress = function (value) {
            return (value || '').toString().trim().replace(/\s+/g, ' ').toLowerCase();
        };

        var updateTripGeoDistance = function (form, position) {
            if (!form || !position || !position.coords) {
                return;
            }
            var output = form.querySelector('[data-geo-distance-status]');
            if (!output) {
                return;
            }
            var refLat = parseTripNumber(form.getAttribute('data-trip-reference-lat'));
            var refLng = parseTripNumber(form.getAttribute('data-trip-reference-lng'));
            if (refLat === null || refLng === null) {
                output.textContent = '';
                output.classList.remove('trip-geo-distance-note');
                return;
            }
            var toRadians = function (degrees) { return degrees * Math.PI / 180; };
            var lat = position.coords.latitude;
            var lng = position.coords.longitude;
            var dLat = toRadians(lat - refLat);
            var dLng = toRadians(lng - refLng);
            var startLat = toRadians(refLat);
            var endLat = toRadians(lat);
            var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(startLat) * Math.cos(endLat) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
            var km = 6371 * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
            var label = form.getAttribute('data-trip-reference-label') || 'Referenz';
            output.textContent = 'GPS-Luftlinie ab ' + label + ': ' + (Math.round(km * 10) / 10).toString().replace('.', ',') + ' km.';
            output.classList.add('trip-geo-distance-note');
        };

        var markTripAddressDifference = function (input, original, current, status) {
            if (!input) {
                return;
            }
            var oldNote = input.parentElement ? input.parentElement.querySelector('[data-trip-geo-address-note]') : null;
            if (!oldNote && input.parentElement && input.parentElement.nextElementSibling && input.parentElement.nextElementSibling.matches('[data-trip-geo-address-note]')) {
                oldNote = input.parentElement.nextElementSibling;
            }
            if (oldNote) {
                oldNote.remove();
            }
            input.classList.remove('trip-geo-address-changed');
            if (!original || !current || normalizeTripAddress(original) === normalizeTripAddress(current)) {
                return;
            }
            input.classList.add('trip-geo-address-changed');
            var note = document.createElement('span');
            note.className = 'trip-geo-address-note';
            note.setAttribute('data-trip-geo-address-note', 'true');
            note.textContent = 'GPS-Adresse weicht vom bisherigen Vorschlag ab; bitte pruefen oder anpassen.';
            if (input.parentElement) {
                input.parentElement.insertAdjacentElement('afterend', note);
            }
            if (status) {
                status.textContent = 'GPS-Position und abweichende Adresse wurden erfasst. Adresse bitte pruefen.';
            }
        };

        var applyReverseGeocodedAddress = function (form, position, status) {
            var addressInput = form ? form.querySelector('[data-trip-geo-address-target]') : null;
            if (!addressInput || (prefill.reverseGeocodingProvider || '') !== 'OpenStreetMap' || !prefill.reverseGeocodeUrl) {
                return;
            }

            var originalAddress = addressInput.getAttribute('data-trip-original-address') || addressInput.defaultValue || addressInput.value || '';
            var url = prefill.reverseGeocodeUrl + '&latitude=' + encodeURIComponent(position.coords.latitude.toString()) + '&longitude=' + encodeURIComponent(position.coords.longitude.toString());
            if (status) {
                status.textContent += ' Adresse wird ermittelt...';
            }

            fetch(url, { headers: { 'Accept': 'application/json' } })
                .then(function (response) { return response.ok ? response.json() : { ok: false }; })
                .then(function (result) {
                    if (result && result.ok && result.address) {
                        addressInput.value = result.address;
                        markTripAddressDifference(addressInput, originalAddress, result.address, status);
                        if (status) {
                            status.textContent = addressInput.classList.contains('trip-geo-address-changed')
                                ? 'GPS-Position und abweichende Adresse wurden erfasst. Adresse bitte pruefen.'
                                : 'GPS-Position und Adresse wurden erfasst. Adresse bitte kurz pruefen.';
                        }
                        updateRouteLinks(form);
                        return;
                    }
                    if (status && result && result.message) {
                        status.textContent = 'GPS-Position erfasst. ' + result.message;
                    }
                })
                .catch(function () {
                    if (status) {
                        status.textContent = 'GPS-Position erfasst. Adresse konnte nicht automatisch ermittelt werden.';
                    }
                });
        };

        var captureTripGeo = function (form, trigger) {
            if (!form) {
                return;
            }
            if (!navigator.geolocation) {
                var status = form.querySelector('[data-geo-status]') || document.querySelector('[data-geo-status]');
                if (status) {
                    status.textContent = 'GPS ist in diesem Browser nicht verfuegbar.';
                }
                return;
            }

            var status = form.querySelector('[data-geo-status]') || document.querySelector('[data-geo-status]');
            if (status) {
                status.textContent = 'GPS-Position wird abgefragt...';
            }
            if (trigger) {
                trigger.disabled = true;
            }

            navigator.geolocation.getCurrentPosition(function (position) {
                var latitude = form.querySelector('[data-geo-lat]');
                var longitude = form.querySelector('[data-geo-lng]');
                var accuracy = form.querySelector('[data-geo-accuracy]');
                if (latitude) {
                    latitude.value = position.coords.latitude.toString();
                }
                if (longitude) {
                    longitude.value = position.coords.longitude.toString();
                }
                if (accuracy) {
                    accuracy.value = position.coords.accuracy ? position.coords.accuracy.toString() : '';
                }
                if (status) {
                    var accuracyText = position.coords.accuracy ? ' Genauigkeit ca. ' + Math.round(position.coords.accuracy) + ' m.' : '';
                    status.textContent = 'GPS-Position erfasst.' + accuracyText + ' Adressen bleiben manuell korrigierbar.';
                }
                updateTripGeoDistance(form, position);
                applyReverseGeocodedAddress(form, position, status);
                if (trigger) {
                    trigger.disabled = false;
                }
            }, function () {
                if (status) {
                    status.textContent = 'GPS nicht verfuegbar oder nicht erlaubt. Die Fahrt kann ohne Positionsdaten gespeichert werden.';
                }
                if (trigger) {
                    trigger.disabled = false;
                }
            }, {
                enableHighAccuracy: true,
                timeout: 8000,
                maximumAge: 30000
            });
        };

        document.querySelectorAll('[data-trip-geo-capture]').forEach(function (button) {
            button.addEventListener('click', function () {
                captureTripGeo(button.closest('[data-trip-geo-form]'), button);
            });
        });
    };

    window.auftragoEnableMailProviderTemplates = function () {
        var select = document.querySelector('[data-mail-provider-template]');
        var apply = document.querySelector('[data-apply-mail-provider]');
        if (!select || !apply) {
            return;
        }
        var templates = {
            ionos: { host: 'smtp.ionos.de', port: 587, mode: 'StartTls' },
            gmail: { host: 'smtp.gmail.com', port: 587, mode: 'StartTls' },
            outlook: { host: 'smtp.office365.com', port: 587, mode: 'StartTls' },
            strato: { host: 'smtp.strato.de', port: 587, mode: 'StartTls' },
            webde: { host: 'smtp.web.de', port: 587, mode: 'StartTls' },
            gmx: { host: 'mail.gmx.net', port: 587, mode: 'StartTls' },
            freenet: { host: 'mx.freenet.de', port: 587, mode: 'StartTls' },
            't-online': { host: 'securesmtp.t-online.de', port: 587, mode: 'StartTls' }
        };
        apply.addEventListener('click', function () {
            var template = templates[select.value || ''];
            if (!template) {
                return;
            }
            var host = document.querySelector('input[name="smtpHost"]');
            var port = document.querySelector('input[name="smtpPort"]');
            var tls = document.querySelector('input[name="useStartTls"]');
            var securityMode = document.querySelector('[data-mail-security-mode]');
            var user = document.querySelector('input[name="userName"]');
            var from = document.querySelector('input[name="fromAddress"]');
            if (host) {
                host.value = template.host;
            }
            if (port) {
                port.value = template.port.toString();
            }
            if (securityMode) {
                securityMode.value = template.mode || 'StartTls';
            }
            if (tls) {
                tls.value = template.mode === 'None' ? 'false' : 'true';
            }
            if (user && from && !user.value && from.value) {
                user.value = from.value;
            }
            if (from && user && !from.value && user.value && user.value.indexOf('@') !== -1) {
                from.value = user.value;
            }
        });
    };

    window.auftragoEnableEmailSettings = function () {
        var shell = document.querySelector('.email-settings-shell');
        if (!shell) {
            return;
        }

        var tabs = Array.prototype.slice.call(shell.querySelectorAll('[data-email-settings-tab]'));
        var panels = Array.prototype.slice.call(shell.querySelectorAll('[data-email-settings-panel]'));
        var enabledToggle = shell.querySelector('[data-mail-enabled-toggle]');
        var stateLabel = shell.querySelector('[data-mail-state-label]');
        var stateInfo = shell.querySelector('[data-mail-state-info]');

        function panelFromHash() {
            var hash = (window.location.hash || '').replace('#', '');
            var target = hash ? document.getElementById(hash) : null;
            return target && target.hasAttribute('data-email-settings-panel')
                ? target.getAttribute('data-email-settings-panel')
                : 'smtp';
        }

        function showPanel(panelKey) {
            panels.forEach(function (panel) {
                var active = (panel.getAttribute('data-email-settings-panel') || '') === panelKey;
                panel.classList.toggle('is-active', active);
            });
            tabs.forEach(function (tab) {
                var active = (tab.getAttribute('data-email-settings-tab') || '') === panelKey;
                tab.classList.toggle('is-active', active);
                tab.setAttribute('aria-current', active ? 'page' : 'false');
            });
        }

        function syncEnabledState() {
            if (!enabledToggle) {
                return;
            }
            shell.classList.toggle('is-mail-enabled', enabledToggle.checked);
            shell.classList.toggle('is-mail-disabled', !enabledToggle.checked);
            if (stateLabel) {
                stateLabel.textContent = enabledToggle.checked ? 'Aktiviert' : 'Deaktiviert';
            }
            if (stateInfo) {
                stateInfo.textContent = enabledToggle.checked
                    ? 'A4W darf Belege, Testmails und Login-Mails über die hinterlegten SMTP-Daten senden.'
                    : 'Die Detailwerte bleiben gespeichert, werden aber für Versand, Passwort-Reset und E-Mail-Codes nicht verwendet.';
            }
        }

        tabs.forEach(function (tab) {
            tab.addEventListener('click', function () {
                window.setTimeout(function () {
                    showPanel(tab.getAttribute('data-email-settings-tab') || 'smtp');
                }, 0);
            });
        });

        if (enabledToggle) {
            enabledToggle.addEventListener('change', syncEnabledState);
        }
        window.addEventListener('hashchange', function () {
            showPanel(panelFromHash());
        });
        syncEnabledState();
        showPanel(panelFromHash());
    };

    window.auftragoEnableAllDayToggle = function () {
        document.querySelectorAll('[data-all-day-form]').forEach(function (form) {
            var checkbox = form.querySelector('[data-all-day-checkbox]');
            var timeFields = form.querySelectorAll('[data-all-day-time]');
            if (!checkbox) {
                return;
            }
            var sync = function () {
                form.querySelectorAll('.all-day-time-cell').forEach(function (cell) {
                    cell.classList.toggle('d-none', checkbox.checked);
                });
                timeFields.forEach(function (field) {
                    field.disabled = checkbox.checked;
                    if (checkbox.checked) {
                        field.value = '';
                    }
                });
            };
            checkbox.addEventListener('change', sync);
            sync();
        });
    };

    window.auftragoEnableWorkEntryBatch = function () {
        document.querySelectorAll('[data-work-entry-batch]').forEach(function (form) {
            var boxes = Array.prototype.slice.call(form.querySelectorAll('[data-work-entry-checkbox]'));
            var bar = form.querySelector('[data-work-entry-batch-bar]');
            var label = form.querySelector('[data-work-entry-batch-label]');
            var invoiceButton = form.querySelector('[data-work-entry-invoice-button]');
            var proofButton = form.querySelector('[data-work-entry-proof-button]');
            var selectedBoxes = function () {
                return boxes.filter(function (box) { return box.checked; });
            };
            var sync = function () {
                var selected = selectedBoxes().length;
                if (bar) {
                    bar.classList.toggle('has-selection', selected > 0);
                    bar.classList.toggle('has-multi-selection', selected >= 2);
                }
                if (label) {
                    label.textContent = selected === 0
                        ? 'Einsätze per Checkbox markieren für Sammelaktionen'
                        : selected === 1
                            ? '1 Einsatz markiert'
                            : selected + ' Einsätze markiert';
                }
                if (invoiceButton) {
                    invoiceButton.disabled = selected < 1;
                }
                if (proofButton) {
                    proofButton.disabled = selected < 2;
                }
            };
            boxes.forEach(function (box) { box.addEventListener('change', sync); });
            sync();
        });
    };

    window.auftragoEnableTemplateVisualEditor = function () {
        var textarea = document.getElementById('templateHtml');
        var editor = document.getElementById('templateVisualEditor');
        var preview = document.getElementById('templateLivePreview');
        if (!textarea || !editor) {
            return;
        }

        var syncing = false;
        var lastEditedSource = 'html';
        var extractEmbeddedBlocks = function (html) {
            return ((html || '').match(/<(style|script)\b[\s\S]*?<\/\1>/gi) || []).join('\n');
        };
        var stripEditorOnlyBlocks = function (html) {
            return (html || '')
                .replace(/<style\b[\s\S]*?<\/style>/gi, '')
                .replace(/<script\b[\s\S]*?<\/script>/gi, '');
        };
        var mergeEditorHtml = function () {
            var embedded = extractEmbeddedBlocks(textarea.value);
            return (embedded ? embedded + '\n' : '') + editor.innerHTML;
        };
        var updatePreview = function () {
            if (preview) {
                preview.srcdoc = textarea.value || '<!doctype html><html><body></body></html>';
            }
        };

        editor.innerHTML = stripEditorOnlyBlocks(textarea.value);
        updatePreview();
        textarea.addEventListener('input', function () {
            if (!syncing) {
                lastEditedSource = 'html';
            }
            if (!syncing) {
                syncing = true;
                editor.innerHTML = stripEditorOnlyBlocks(textarea.value);
                syncing = false;
            }
            updatePreview();
        });
        editor.addEventListener('input', function () {
            if (syncing) {
                return;
            }
            lastEditedSource = 'visual';
            syncing = true;
            textarea.value = mergeEditorHtml();
            syncing = false;
            updatePreview();
        });
        var form = textarea.closest('form');
        if (form) {
            form.addEventListener('submit', function () {
                if (lastEditedSource === 'visual') {
                    textarea.value = mergeEditorHtml();
                }
            });
        }
    };
})();
