1// static/js/search-auth.js â lock badges and click gating, fetchless using pre-attached flags 2(function () { 3 const ICONS = (window.constants && window.constants.ICONS) || {}; 4 5 function isLoggedIn() { 6 return document.documentElement.classList.contains('user-logged-in'); 7 } 8 9 function readAuthRequiredFromEl(el) { 10 const raw = el?.getAttribute('data-auth-required'); 11 return raw === '1' ? true : raw === '0' ? false : null; 12 } 13 14 function decorateLocks(instance) { 15 const container = instance?.elements?.searchResults; 16 if (!container) return; 17 18 const loggedIn = isLoggedIn(); 19 instance._authLockMap = instance._authLockMap || Object.create(null); 20 21 for (const el of container.children) { 22 const sym = String(el.dataset?.symbol || '').toUpperCase(); 23 const badge = el.querySelector('.auth-lock-badge'); 24 25 // Prefer DOM attribute, fallback to any cached map entry, else default to false 26 let needsAuth = readAuthRequiredFromEl(el); 27 if (needsAuth === null) needsAuth = !!instance._authLockMap[sym]; 28 29 const shouldShow = !loggedIn && !!needsAuth; 30 31 if (shouldShow && !badge && ICONS.LOCK) { 32 el.insertAdjacentHTML('beforeend', `<span class="auth-lock-badge" aria-label="Login required">${ICONS.LOCK}</span>`); 33 } else if (!shouldShow && badge) { 34 badge.remove(); 35 } 36 37 // Keep cache updated to support click gating 38 instance._authLockMap[sym] = !!needsAuth; 39 } 40 } 41 42 function patchPrototype() { 43 if (typeof StockSearch === 'undefined') return; 44 if (StockSearch.prototype.__authPatched) return; 45 46 // Decorate after rendering, no network 47 const prevShowResults = StockSearch.prototype.showResults; 48 StockSearch.prototype.showResults = function (matches, q) { 49 prevShowResults.call(this, matches, q); 50 decorateLocks(this); 51 }; 52 53 // Gate clicks purely from stock.auth_required or DOM attributes 54 const prevSelect = StockSearch.prototype.selectStock; 55 StockSearch.prototype.selectStock = function (stock) { 56 try { 57 const sym = String(stock?.symbol || '').toUpperCase(); 58 let lockedFlag = !!stock?.auth_required; 59 60 if (!('auth_required' in (stock || {}))) { 61 const el = this.elements?.searchResults?.querySelector(`[data-symbol="${sym}"]`); 62 const v = readAuthRequiredFromEl(el); 63 // fallback to cache if attribute missing 64 lockedFlag = v === null ? !!(this._authLockMap && this._authLockMap[sym]) : v; 65 } 66 67 if (!isLoggedIn() && lockedFlag) { 68 window.openModal?.('authModal', 'signup'); 69 return; 70 } 71 } catch {} 72 prevSelect.call(this, stock); 73 }; 74 75 StockSearch.prototype.__authPatched = true; 76 } 77 78 function installResultsObserver() { 79 const inst = window.stockSearchInstance; 80 const container = inst?.elements?.searchResults;
81 if (!inst || !container || inst.__authObs) return; 82 83 const obs = new MutationObserver(() => decorateLocks(inst)); 84 obs.observe(container, { childList: true }); 85 inst.__authObs = obs; 86 87 // First paint 88 decorateLocks(inst); 89 } 90 91 function installLoginObserver() { 92 try { 93 const root = document.documentElement; 94 const obs = new MutationObserver(() => { 95 const inst = window.stockSearchInstance; 96 if (inst) decorateLocks(inst); 97 }); 98 obs.observe(root, { attributes: true, attributeFilter: ['class'] }); 99 } catch {} 100 } 101 102 // Boot 103 document.addEventListener('DOMContentLoaded', () => { 104 patchPrototype(); 105 106 // Wait until StockSearch instance exists, then attach observer 107 const t = setInterval(() => { 108 if (window.stockSearchInstance) { 109 clearInterval(t); 110 installResultsObserver(); 111 } 112 }, 30); 113 114 installLoginObserver(); 115 }); 116 117 // public hook for any future explicit refresh 118 window.refreshAuthLocks = function () { 119 const inst = window.stockSearchInstance; 120 if (inst) decorateLocks(inst); 121 }; 122})();
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.