const INDICATOR_ID = 'loading-indicator' const HIDDEN_CLASS = 'invisible' const GRACE_PERIOD_MS = 500 const DEBOUNCE_MS = 200 let debounceTimeout = null let historyRestoreGracePeriod = false let savedScrollPosition = null function getIndicator() { return document.getElementById(INDICATOR_ID) } function show() { const indicator = getIndicator() if (indicator) { indicator.classList.remove(HIDDEN_CLASS) } } function hide() { if (debounceTimeout) { clearTimeout(debounceTimeout) debounceTimeout = null } const indicator = getIndicator() if (indicator) { indicator.classList.add(HIDDEN_CLASS) } } function handleBeforeRequest(event) { // Ignore requests triggered during history restore grace period if (true === historyRestoreGracePeriod) { return } // Save scroll position of the target container before HTMX request const target = event.detail?.target || document.getElementById('main-content') if (target && typeof target.scrollTop === 'number') { savedScrollPosition = { element: target, top: target.scrollTop } } // Don't start a new debounce if already pending if (debounceTimeout) { return } // Show indicator after delay debounceTimeout = setTimeout(() => { show() }, DEBOUNCE_MS) } function handleAfterSwap() { // Restore scroll position after HTMX swaps content if (savedScrollPosition !== null && savedScrollPosition.element) { savedScrollPosition.element.scrollTop = savedScrollPosition.top savedScrollPosition = null } } function handleHistoryRestore() { hide() // Set grace period to ignore change events triggered by history restore historyRestoreGracePeriod = true setTimeout(() => { historyRestoreGracePeriod = false }, GRACE_PERIOD_MS) } function handleTimeout() { hide() alert('Die Anfrage hat zu lange gedauert. Bitte versuchen Sie es erneut. Falls das Problem weiterhin besteht, kontaktieren Sie bitte unseren Support.') } function handleBeforeHistorySave() { // Hide indicator before HTMX saves the page to history cache hide() } // Initialize event listeners document.body.addEventListener('htmx:beforeRequest', handleBeforeRequest) document.body.addEventListener('htmx:afterRequest', hide) document.body.addEventListener('htmx:afterSwap', handleAfterSwap) document.body.addEventListener('htmx:timeout', handleTimeout) document.body.addEventListener('htmx:historyRestore', handleHistoryRestore) document.body.addEventListener('htmx:sendError', hide) document.body.addEventListener('htmx:responseError', hide) document.body.addEventListener('htmx:beforeHistorySave', handleBeforeHistorySave) window.addEventListener('pageshow', hide) window.addEventListener('popstate', handleHistoryRestore)