1(function () { 2 var ROOT_ID = 'sermon-library-root'; 3 var MESSAGE_TYPE = 'SERMON_LIBRARY_EMBED_HEIGHT'; 4 var DEFAULT_SRC = 'https://sermon-library-manager.web.app/embed'; 5 var DEFAULT_INITIAL_HEIGHT = 900; 6 var DEFAULT_MIN_HEIGHT = 700; 7 var MIN_HEIGHT = 600; 8 9 function clampHeight(height, minHeight) { 10 return Math.max(Math.max(minHeight, MIN_HEIGHT), height); 11 } 12 13 function parsePositiveInt(value, fallback) { 14 var parsed = parseInt(value, 10); 15 return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; 16 } 17 18 function createOrigin(url) { 19 try { 20 return new URL(url, window.location.href).origin; 21 } catch (_error) { 22 return window.location.origin; 23 } 24 } 25 26 // Squarespace Fluid Engine uses display:grid + overflow:hidden on its section 27 // containers, which clips the negative-margin full-bleed technique. Instead we 28 // walk up the DOM, strip padding from Squarespace block wrappers, and unlock 29 // overflow so the iframe can genuinely fill the viewport width. 30 function applyFullBleed(root) { 31 if (!root.classList.contains('sermon-library-embed--fullbleed')) { 32 return; 33 } 34 35 var SQS_PADDING_SELECTORS = [ 36 'sqs-block-content', 37 'sqs-block', 38 'sqs-block-code', 39 'sqs-layout', 40 'sqs-col-wrappr', 41 ]; 42 43 var el = root.parentElement; 44 var depth = 0; 45 46 while (el && el !== document.body && depth < 10) { 47 var classes = el.className || ''; 48 49 // Remove padding from known Squarespace wrapper elements 50 var isSqsWrapper = SQS_PADDING_SELECTORS.some(function (cls) { 51 return classes.indexOf(cls) !== -1; 52 }); 53 54 if (isSqsWrapper) { 55 el.style.padding = '0'; 56 el.style.maxWidth = 'none'; 57 } 58 59 // Unlock overflow on any ancestor that would clip us 60 var computed = window.getComputedStyle(el); 61 if (computed.overflow === 'hidden' || computed.overflowX === 'hidden') { 62 el.style.overflow = 'visible'; 63 el.style.overflowX = 'visible'; 64 } 65 66 el = el.parentElement; 67 depth++; 68 } 69 70 // Now that containers are unlocked, stretch to viewport width 71 root.style.width = '100vw'; 72 root.style.position = 'relative'; 73 root.style.left = '50%'; 74 root.style.transform = 'translateX(-50%)'; 75 } 76 77 function init() { 78 var root = document.getElementById(ROOT_ID); 79 80 if (!root) { 81 return; 82 } 83 84 if (root.dataset.sermonLibraryMounted === 'true') { 85 return; 86 } 87 88 applyFullBleed(root); 89 90 var src = root.getAttribute('data-src') || DEFAULT_SRC; 91 var minHeight = parsePositiveInt(root.getAttribute('data-min-height'), DEFAULT_MIN_HEIGHT); 92 var initialHeight = parsePositiveInt( 93 root.getAttribute('data-initial-height'), 94 DEFAULT_INITIAL_HEIGHT, 95 ); 96 var expectedOrigin = createOrigin(src); 97 98 // Append a timestamp so every page load bypasses CDN and browser cache 99 // for the iframe shell. JS/CSS bundles inside are still content-hash cached. 100 var sep = src.indexOf('?') !== -1 ? '&' : '?'; 101 var iframeSrc = src + sep + '_cb=' + Date.now(); 102 103 var iframe = document.createElement('iframe'); 104 iframe.src = iframeSrc; 105 iframe.title = 'Restored Church Sermon Library'; 106 iframe.loading = 'lazy'; 107 iframe.allow = 'autoplay; encrypted-media; picture-in-picture'; 108 iframe.allowFullscreen = true; 109 // Disable iframe's own scroll context so the parent page is the sole 110 // scroll container. Without this, mobile browsers create a nested 111 // scroll region inside the iframe. 112 iframe.scrolling = 'no'; 113 iframe.style.width = '100%'; 114 iframe.style.display = 'block'; 115 iframe.style.border = '0'; 116 iframe.style.overflow = 'hidden'; 117 iframe.style.background = '#0d0d0d'; 118 iframe.style.minHeight = minHeight + 'px'; 119 iframe.style.height = clampHeight(initialHeight, minHeight) + 'px'; 120 121 // Prevent white flash from root element before iframe paints 122 root.style.background = '#0d0d0d'; 123 root.style.display = 'block'; 124 125 root.innerHTML = ''; 126 root.appendChild(iframe); 127 root.dataset.sermonLibraryMounted = 'true'; 128 129 function handleMessage(event) { 130 if (event.origin !== expectedOrigin) { 131 return; 132 } 133 134 if (event.source !== iframe.contentWindow) { 135 return; 136 } 137 138 var data = event.data; 139 140 if (!data) { 141 return; 142 } 143 144 if (data.type === MESSAGE_TYPE && typeof data.height === 'number') { 145 iframe.style.height = clampHeight(Math.round(data.height), minHeight) + 'px'; 146 } 147 148 // Child app requests scroll-to-player on mobile sermon selection. 149 // Since the parent page owns all scrolling (iframe is auto-height with 150 // no internal scroll context), we scroll the iframe element into view. 151 if (data.type === 'SERMON_LIBRARY_SCROLL_TO_PLAYER') { 152 iframe.scrollIntoView({ behavior: 'smooth', block: 'start' }); 153 } 154 } 155 156 window.addEventListener('message', handleMessage); 157 158 // ââ Synthetic sticky: send scroll geometry to child ââââââââââââââââââââââ 159 var scrollTicking = false;
160 161 function sendScrollState() { 162 var rect = iframe.getBoundingClientRect(); 163 iframe.contentWindow.postMessage( 164 { 165 type: 'SERMON_LIBRARY_SCROLL_UPDATE', 166 iframeTop: rect.top, 167 iframeBottom: rect.bottom, 168 iframeHeight: rect.height, 169 viewportHeight: window.innerHeight, 170 }, 171 expectedOrigin, 172 ); 173 scrollTicking = false; 174 } 175 176 function scheduleScrollUpdate() { 177 if (!scrollTicking) { 178 scrollTicking = true; 179 window.requestAnimationFrame(sendScrollState); 180 } 181 } 182 183 window.addEventListener('scroll', scheduleScrollUpdate, { passive: true }); 184 window.addEventListener('resize', scheduleScrollUpdate, { passive: true }); 185 186 iframe.addEventListener( 187 'load', 188 function () { 189 window.requestAnimationFrame(sendScrollState); 190 setTimeout(sendScrollState, 600); 191 setTimeout(sendScrollState, 1800); 192 }, 193 { once: true }, 194 ); 195 196 } 197 198 if (document.readyState === 'loading') { 199 document.addEventListener('DOMContentLoaded', init, { once: true }); 200 } else { 201 init(); 202 } 203})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.