(function(){ const defaultDebounceTimeInMs = 500; let isReady = false document.addEventListener('DOMContentLoaded', function() { isReady = true }) function onReady(fn) { if (isReady || document.readyState === 'complete') { fn() } else { document.addEventListener('DOMContentLoaded', fn) } } htmx.defineExtension("resize", { init: (api) => { function hasResizeTrigger(elt) { for (spec of api.getTriggerSpecs(elt)) { if (spec.trigger === 'resize') { return true; } } return false; } function getDebounceTimeInMs(elt) { const amountAttr = elt.attributes['hx-resize-debounce']; if (!amountAttr) { return defaultDebounceTimeInMs } const amountStr = amountAttr.value if (!amountStr) { return defaultDebounceTimeInMs } try { return parseInt(amountStr); } catch (err) { console.warn('invalid debounce time on trigger:', trigger); console.error(err); return defaultDebounceTimeInMs; } } let nextID = 1; let timeoutIDs = {}; let observers = {}; function processNode(elt) { if (!hasResizeTrigger(elt)) { return; } let data = elt['hx-resize-internal-data']; if (!data) { data = { id: nextID, }; nextID += 1; elt['hx-resize-internal-data'] = data; } const obs = new ResizeObserver(() => { if (timeoutIDs[data.id]) { return; } const debounceTime = getDebounceTimeInMs(elt); timeoutIDs[data.id] = setTimeout(() => { elt.dispatchEvent(new Event('resize')); delete timeoutIDs[data.id]; }, debounceTime); }); obs.observe(elt); observers[data.id] = obs; } function cleanupNode(elt) { if (!hasResizeTrigger(elt)) { return; } let data = elt['hx-resize-internal-data']; if (!data) { return; } const obs = observers[data.id]; if (obs) { obs.disconnect(); } delete observers[data.id]; delete timeoutIDs[data.id]; } // process nodes htmx.on('htmx:beforeProcessNode', evt => processNode(evt.target)); htmx.on('htmx:beforeCleanupElement', evt => cleanupNode(evt.target)); onReady(function() { document.querySelectorAll('[hx-trigger]').forEach(processNode); }) } }); })()