1/** 2 * CLP Embed Content Script 3 * Responds to clpEmbed postMessage from parent with current page height, 4 * and starts MutationObserver for ongoing height updates. 5 * Uses MutationObserver (not ResizeObserver) to avoid feedback loops: 6 * setting iframe.style.height on the parent side changes the iframe viewport, 7 * which causes vh-unit elements to resize â ResizeObserver would fire again 8 * creating an infinite loop. MutationObserver only fires on actual DOM changes. 9 */ 10(function () { 11 12 const stampClpEmbedUrl = function () { 13 try { 14 const url = new URL(window.location.href); 15 if (url.searchParams.get('clpEmbedEnv') !== '1') { 16 url.searchParams.set('clpEmbedEnv', '1'); 17 history.replaceState(history.state, '', url.toString()); 18 } 19 } catch (ignore) {} 20 }; 21 22 const CLP_EMBED_LOGOUT_URL = window.location.protocol + '//' + window.location.host + '/nx/logout'; 23 24 // Appends a "Log Out" item to the bottom of the sidebar menu while embedded. 25 // The "common/portal/left-nav" fragment is rendered twice on the page - once for 26 // the desktop sidebar and once for the mobile/tablet offcanvas mini sidebar - both 27 // sharing the "sidebar-box" list class, so targeting that class covers mobile, 28 // tablet and desktop in one pass. 29 const addClpEmbedLogoutButton = function () { 30 try { 31 document.querySelectorAll('ul.sidebar-box').forEach(function (ul) { 32 if (ul.querySelector('.clp-embed-logout')) return; // already added 33 const li = document.createElement('li'); 34 li.className = 'sidebar-option nav-item clp-embed-logout'; 35 const a = document.createElement('a'); 36 a.className = 'nav-link link-dark'; 37 a.href = CLP_EMBED_LOGOUT_URL; 38 a.innerHTML = '<i class="far fa-arrow-right-from-bracket"></i><span class="sidebar-menu">Log Out</span>'; 39 li.appendChild(a); 40 ul.appendChild(li); 41 }); 42 } catch (e) { 43 console.log('[Neon] add Login-Out btn error for embed clp.', e) 44 } 45 }; 46 47 window.addEventListener('message', function (e) { 48 if (!e.data || e.data.sharingType !== 'clpEmbed') return; 49 const targetOrigin = e.origin || '*'; 50 const embedIframeId = e.data.embedIframeId; 51 52 stampClpEmbedUrl(); 53 54 // Override viewport-relative sizing so content height is stable 55 if (!document.getElementById('__neon-clp-min-height-reset')) { 56 const ms = document.createElement('style'); 57 ms.id = '__neon-clp-min-height-reset'; 58 ms.textContent = '.neonfront, .p2p-right-content { min-height: 0 !important; } html, body { overflow-x: hidden !important; height: auto !important; } #header-content, #footer-content { display: none !important; height: 0 !important; overflow: hidden !important; }'; 59 document.head.appendChild(ms); 60 } 61 62 let _lastSentHeight = 0; 63 let _debounceTimer = null; 64 const sendHeight = function () { 65 66 stampClpEmbedUrl(); 67 // Re-run on every call (initial load + MutationObserver hits) so the 68 // button survives SPA route/nav re-renders; the dedupe check inside 69 // makes repeated calls a no-op once it's already present. 70 addClpEmbedLogoutButton(); 71 72 clearTimeout(_debounceTimer); 73 _debounceTimer = setTimeout(function () { 74 const h = document.querySelector('.neonfront')?.offsetHeight || document.body.offsetHeight; 75 if (h > 0 && Math.abs(h - _lastSentHeight) > 2) { 76 _lastSentHeight = h; 77 parent.postMessage({type: 'iframeHeight', height: h, embedIframeId: embedIframeId}, targetOrigin); 78 } 79 }, 100); 80 }; 81 82 sendHeight(); 83 setTimeout(sendHeight, 300); 84 85 // MutationObserver fires on actual DOM changes (Vue routing, dynamic content),
86 // but NOT when parent sets iframe.style.height â avoiding the feedback loop. 87 if (!window._neonClpMutationObserver) { 88 window._neonClpMutationObserver = new MutationObserver(sendHeight); 89 window._neonClpMutationObserver.observe(document.body, {childList: true, subtree: true, attributes: false, characterData: false}); 90 } 91 92 // Also re-measure when all resources finish loading 93 window.addEventListener('load', sendHeight); 94 }); 95})();
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.