PageSourceSearch

https://secure.namb.org/nx/portal-static/js/clp-embed-content.js

js namb.org collected 2026-09-24 21:08:27 UTC 4,548 bytes, 95 lines download raw bytes

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.