/* global debugLog */ // HTMX swap/script-execution configuration and section toggle helpers // Extracted from templates/v3/base.html so browsers cache it as a static asset. // Configure HTMX to evaluate scripts in swapped content and fix insertBefore errors (function() { function setupScriptExecution() { if (document.body) { // Fix HTMX insertBefore errors by validating targets before swap document.body.addEventListener('htmx:beforeSwap', function(event) { try { const target = event.detail.target; if (!target) { console.warn('[HTMX] Target is null, skipping swap'); event.detail.shouldSwap = false; return false; } // Check if target is a valid DOM element if (!(target instanceof Element)) { console.warn('[HTMX] Target is not a valid Element, skipping swap'); event.detail.shouldSwap = false; return false; } // Check if target has a parent node (required for insertBefore) if (!target.parentNode) { console.warn('[HTMX] Target has no parent node, skipping swap'); event.detail.shouldSwap = false; return false; } // Ensure target is in the DOM if (!document.body.contains(target) && !document.head.contains(target)) { console.warn('[HTMX] Target is not in DOM, skipping swap'); event.detail.shouldSwap = false; return false; } // Additional check: ensure parent is also in DOM if (target.parentNode && !document.body.contains(target.parentNode) && !document.head.contains(target.parentNode)) { console.warn('[HTMX] Target parent is not in DOM, skipping swap'); event.detail.shouldSwap = false; return false; } // All checks passed, allow swap return true; } catch (e) { // If validation fails, cancel swap console.warn('[HTMX] Error validating target:', e); event.detail.shouldSwap = false; return false; } }); // Suppress HTMX insertBefore errors and other noisy errors - they're harmless but noisy const originalError = console.error; const originalWarn = console.warn; console.error = function(...args) { const errorStr = args.join(' '); const errorStack = args.find(arg => arg && typeof arg === 'string' && arg.includes('htmx')) || ''; // Suppress HTMX insertBefore errors (comprehensive check) // These occur when HTMX tries to swap content but the target element is null // Usually happens due to timing/race conditions and is harmless if (errorStr.includes("insertBefore") || errorStr.includes("Cannot read properties of null") || errorStr.includes("reading 'insertBefore'")) { // Check if it's from HTMX by looking at stack trace or error string // Also check the call stack if available const isHtmxError = errorStr.includes('htmx') || errorStack.includes('htmx') || args.some(arg => { if (typeof arg === 'string') { return arg.includes('htmx'); } // Check error objects for stack traces if (arg && typeof arg === 'object' && arg.stack) { return arg.stack.includes('htmx'); } return false; }); if (isHtmxError) { return; // Suppress - this is a harmless HTMX timing/race condition issue } } // Suppress script execution errors from malformed HTML if (errorStr.includes("Failed to execute 'appendChild' on 'Node'") || errorStr.includes("Failed to execute 'insertBefore' on 'Node'")) { if (errorStr.includes('Unexpected token')) { return; // Suppress malformed HTML errors } } originalError.apply(console, args); }; console.warn = function(...args) { const warnStr = args.join(' '); // Suppress Permissions-Policy warnings (harmless browser warnings) if (warnStr.includes('Permissions-Policy header') || warnStr.includes('Unrecognized feature') || warnStr.includes('Origin trial controlled feature') || warnStr.includes('browsing-topics') || warnStr.includes('run-ad-auction') || warnStr.includes('join-ad-interest-group') || warnStr.includes('private-state-token') || warnStr.includes('private-aggregation') || warnStr.includes('attribution-reporting')) { return; // Suppress - these are harmless browser feature warnings } originalWarn.apply(console, args); }; // Handle HTMX errors gracefully with detailed logging document.body.addEventListener('htmx:responseError', function(event) { const detail = event.detail; const xhr = detail.xhr; const target = detail.target; // Enhanced error logging console.error('HTMX response error:', { status: xhr?.status, statusText: xhr?.statusText, url: xhr?.responseURL, target: target?.id || target?.tagName, responseText: xhr?.responseText }); // For form submissions, log field names only — values // may contain API keys, passwords, or other secrets // that must never reach the console. if (target && target.tagName === 'FORM') { const formData = new FormData(target); const fieldNames = []; for (const [key] of formData.entries()) { fieldNames.push(key); } console.error('Form fields (values redacted):', fieldNames); // Try to parse error response for validation details if (xhr?.responseText) { try { const errorData = JSON.parse(xhr.responseText); console.error('Error details:', { message: errorData.message, details: errorData.details, validation_errors: errorData.validation_errors, context: errorData.context }); } catch { console.error('Error response (non-JSON):', xhr.responseText.substring(0, 500)); } } } }); document.body.addEventListener('htmx:swapError', function(event) { // Log but don't break the app console.warn('HTMX swap error:', event.detail); }); // Execute