(() => { const perf = window.AssetPerf; perf?.start('browser-state:gesamtes Modul'); const persistent = document.body?.dataset.persistBrowserState === 'true'; const storage = persistent ? window.localStorage : window.sessionStorage; const prefix = 'assetmanager:'; const api = { persistent, get(key) { try { return storage.getItem(prefix + key); } catch (_) { return null; } }, set(key, value) { try { storage.setItem(prefix + key, value); } catch (_) {} }, remove(key) { try { storage.removeItem(prefix + key); } catch (_) {} } }; window.AssetBrowserState = api; function tableKey(table, index) { return table.dataset.storageKey || table.id || `table-${location.pathname}-${index}`; } function filterControls(table) { return [...table.querySelectorAll('thead input, thead select')].filter(control => { const type = (control.type || '').toLowerCase(); return !['checkbox', 'radio', 'button', 'submit'].includes(type); }); } function controlKey(control, index) { const cell = control.closest('th'); return control.name || control.dataset.field || cell?.dataset.field || `filter-${index}`; } function restoreTable(table, index) { if (table.dataset.browserStateReady === '1') return; if (table.dataset.serverFilters === '1') { table.dataset.browserStateReady = '1'; return; } const restoreStarted = performance.now(); const controls = filterControls(table); if (!controls.length) return; table.dataset.browserStateReady = '1'; const key = `${tableKey(table, index)}:filters`; let values = {}; try { values = JSON.parse(api.get(key) || '{}') || {}; } catch (_) {} controls.forEach((control, controlIndex) => { const field = controlKey(control, controlIndex); if (Object.prototype.hasOwnProperty.call(values, field)) { // Restore without dispatching one input/change pair per column. control.value = values[field]; } const save = () => { const current = {}; filterControls(table).forEach((item, itemIndex) => { current[controlKey(item, itemIndex)] = item.value; }); api.set(key, JSON.stringify(current)); }; control.addEventListener('input', save); control.addEventListener('change', save); }); // One filtering pass after every stored value is in place. if (typeof table.assetApplyFilters === 'function') { table.assetApplyFilters(); } table.dataset.browserStateRestored = '1'; window.dispatchEvent(new CustomEvent('asset-table-state-ready', { detail: { table } })); const duration = performance.now() - restoreStarted; const logger = duration >= 50 ? console.warn : console.debug; logger('[AssetManager][Performance]', 'browser-state:restoreTable', { dauer_ms: Math.round(duration * 10) / 10, filter: controls.length, gespeicherte_werte: Object.keys(values).length }); } function initializeTables() { document.querySelectorAll('table').forEach(restoreTable); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeTables, { once: true }); } else { initializeTables(); } // Filter controls are added by table-tools.js. Initialize only on its explicit // completion event instead of observing every DOM mutation in the document. window.addEventListener('asset-table-structure-ready', initializeTables); // A programmatic reset does not emit input/change events. Remove the // corresponding persisted filter state explicitly so it cannot return after // a page refresh. window.addEventListener('asset-table-filters-cleared', event => { const table = event.detail?.table; if (!(table instanceof HTMLTableElement)) return; const tables = [...document.querySelectorAll('table')]; const index = tables.indexOf(table); const key = `${tableKey(table, index >= 0 ? index : 0)}:filters`; api.remove(key); }); perf?.end('browser-state:gesamtes Modul'); })();