(() => { function initialize() { const table = document.getElementById('asset-table'); const openButton = document.getElementById('duplicate-search-button'); const dialog = document.getElementById('duplicate-search-dialog'); const select = document.getElementById('duplicate-field-select'); const applyButton = document.getElementById('duplicate-apply-button'); const clearButton = document.getElementById('duplicate-clear-button'); const ignoreEmpty = document.getElementById('duplicate-ignore-empty'); const caseInsensitive = document.getElementById('duplicate-case-insensitive'); const summary = document.getElementById('duplicate-search-summary'); if (!table || !openButton || !dialog || table.dataset.duplicateSearchReady === '1') return; table.dataset.duplicateSearchReady = '1'; const dataRows = () => [...table.tBodies[0].rows].filter(row => !row.querySelector('.empty-state')); const normallyVisible = row => { if (row.classList.contains('duplicate-hidden')) return false; if (row.hidden || row.classList.contains('hidden')) return false; const style = getComputedStyle(row); return style.display !== 'none' && style.visibility !== 'hidden'; }; const availableFields = () => [...table.querySelectorAll('thead tr:first-child th[data-field]')] .filter(th => th.style.display !== 'none') .map(th => ({ field: th.dataset.field, label: th.dataset.label || th.textContent.trim() })); function fillSelect() { const previous = select.value; select.innerHTML = ''; availableFields().forEach(item => { const option = document.createElement('option'); option.value = item.field; option.textContent = item.label; select.appendChild(option); }); if ([...select.options].some(option => option.value === previous)) { select.value = previous; } } function clearDuplicateFilter() { dataRows().forEach(row => row.classList.remove('duplicate-hidden', 'duplicate-match')); summary.hidden = true; window.dispatchEvent(new Event('asset-table-filter-changed')); } function normalize(value) { let result = value.trim().replace(/\s+/g, ' '); if (caseInsensitive.checked) result = result.toLocaleLowerCase(); return result; } function applyDuplicateFilter() { // Remove only our previous result first. Normal column filters stay intact. dataRows().forEach(row => row.classList.remove('duplicate-hidden', 'duplicate-match')); const field = select.value; if (!field) return; const candidates = dataRows().filter(normallyVisible); const groups = new Map(); candidates.forEach(row => { const cell = row.querySelector(`td[data-field="${CSS.escape(field)}"]`); const value = normalize(cell?.textContent || ''); if (ignoreEmpty.checked && !value) return; const rows = groups.get(value) || []; rows.push(row); groups.set(value, rows); }); const duplicateRows = new Set(); let duplicateGroups = 0; groups.forEach(rows => { if (rows.length > 1) { duplicateGroups += 1; rows.forEach(row => duplicateRows.add(row)); } }); candidates.forEach(row => { if (duplicateRows.has(row)) { row.classList.add('duplicate-match'); } else { row.classList.add('duplicate-hidden'); } }); summary.textContent = `${duplicateRows.size} Datensätze in ${duplicateGroups} Dublettengruppen gefunden.`; summary.hidden = false; window.dispatchEvent(new Event('asset-table-filter-changed')); dialog.close(); } openButton.addEventListener('click', () => { // Search always starts from the current normal filter result, not from // a previous duplicate result. clearDuplicateFilter(); fillSelect(); dialog.showModal(); }); applyButton.addEventListener('click', applyDuplicateFilter); clearButton.addEventListener('click', clearDuplicateFilter); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { initialize(); } window.addEventListener('asset-table-content-reloaded', initialize); })();