1/** 2 * Context Panel â Supply Tab 3 * 4 * Renders supply chain tier diagram + HHI concentration gauge. 5 * Data sourced from entity's services_json (supply service results). 6 */ 7(function () { 8 "use strict"; 9 10 function esc(s) { 11 return String(s ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); 12 } 13 14 function hhiGaugeSvg(hhi) { 15 // Arc gauge: green (<0.4), amber (0.4-0.7), red (>0.7) 16 var color = hhi > 0.7 ? "#EF4444" : hhi > 0.4 ? "#F59E0B" : "#10B981"; 17 var cls = hhi > 0.7 ? "high" : hhi > 0.4 ? "med" : "low"; 18 19 // SVG arc: 180° semicircle, filled proportionally to HHI (0-1) 20 var angle = Math.min(hhi, 1) * 180; 21 var rad = angle * Math.PI / 180; 22 var cx = 80, cy = 70, r = 55; 23 var x = cx + r * Math.cos(Math.PI - rad); 24 var y = cy - r * Math.sin(Math.PI - rad); 25 var largeArc = angle > 90 ? 1 : 0; 26 27 var svg = '<svg viewBox="0 0 160 90" style="max-width:160px">' 28 + '<path d="M ' + (cx - r) + ' ' + cy + ' A ' + r + ' ' + r + ' 0 0 1 ' + (cx + r) + ' ' + cy + '" fill="none" stroke="#e2e8f0" stroke-width="10" stroke-linecap="round"/>' 29 + '<path d="M ' + (cx - r) + ' ' + cy + ' A ' + r + ' ' + r + ' 0 ' + largeArc + ' 1 ' + x.toFixed(1) + ' ' + y.toFixed(1) + '" fill="none" stroke="' + color + '" stroke-width="10" stroke-linecap="round"/>' 30 + '<text x="' + cx + '" y="' + (cy - 10) + '" text-anchor="middle" font-size="18" font-weight="900" fill="' + color + '">' + hhi.toFixed(2) + '</text>' 31 + '<text x="' + cx + '" y="' + (cy + 6) + '" text-anchor="middle" font-size="10" fill="#64748b">HHI</text>' 32 + '</svg>'; 33 34 return '<div class="hp-obs__hhi-gauge">' + svg 35 + '<div class="hp-obs__hhi-label hp-obs__hhi--' + cls + '">' 36 + (hhi > 0.7 ? "High" : hhi > 0.4 ? "Medium" : "Low") + ' Concentration</div></div>'; 37 } 38 39 function render(pane, entity) { 40 if (!pane || !entity) return; 41 42 var services = entity.services || {}; 43 var supplyData = services.Supply || services.supply; 44 var supplyScore = supplyData ? supplyData.score : 0; 45 var supplyFindings = supplyData ? (supplyData.findings || 0) : 0; 46 47 var html = ''; 48 49 // If no supply data, show placeholder 50 if (!supplyData || supplyScore === 0) { 51 html = '<div style="padding:24px;color:#94a3b8;font-size:.82rem;text-align:center">' 52 + 'No supply chain data available for ' + esc(entity.company_name || "this entity") + '.' 53 + '<br><span style="font-size:.72rem">The supply service found ' + supplyFindings + ' findings.</span></div>'; 54 pane.innerHTML = html; 55 return; 56 } 57 58 // Supply score header 59 html += '<div style="margin-bottom:14px;font-size:.82rem">' 60 + '<strong>Supply Chain Score:</strong> ' + supplyScore + '/100' 61 + ' · ' + supplyFindings + ' findings</div>'; 62 63 // Tier diagram (simulated from available data) 64 html += '<div class="hp-obs__supply-tiers">'; 65 66 // Tier 1 â primary entity 67 html += '<div class="hp-obs__supply-tier">' 68 + '<div class="hp-obs__supply-tier-label">Entity</div>' 69 + '<div class="hp-obs__supply-item">' 70 + '<div class="hp-obs__supply-item-name">' + esc(entity.company_name) + '</div>' 71 + '<div class="hp-obs__supply-item-cat">' + esc(entity.sector || "Unknown sector") + '</div>' 72 + '</div></div>'; 73 74 // Tier 2 â derived from tags/signal data 75 var tags = Array.isArray(entity.tags) ? entity.tags : []; 76 if (tags.length > 0) { 77 html += '<div class="hp-obs__supply-tier">' 78 + '<div class="hp-obs__supply-tier-label">Dependencies</div>'; 79 for (var i = 0; i < Math.min(tags.length, 4); i++) { 80 html += '<div class="hp-obs__supply-item">' 81 + '<div class="hp-obs__supply-item-name">' + esc(tags[i]) + '</div>' 82 + '</div>'; 83 } 84 html += '</div>'; 85 } 86 87 // Tier 3 â signal sources 88 if (entity.signal_type) { 89 html += '<div class="hp-obs__supply-tier">' 90 + '<div class="hp-obs__supply-tier-label">Signal Sources</div>' 91 + '<div class="hp-obs__supply-item">' 92 + '<div class="hp-obs__supply-item-name">' + esc(entity.signal_type) + '</div>' 93 + '<div class="hp-obs__supply-item-cat">Primary signal</div>' 94 + '</div>'; 95 if (entity.vulnerability_id) { 96 html += '<div class="hp-obs__supply-item">' 97 + '<div class="hp-obs__supply-item-name">' + esc(entity.vulnerability_id) + '</div>' 98 + '<div class="hp-obs__supply-item-cat">Vulnerability ID</div>' 99 + '</div>'; 100 } 101 html += '</div>'; 102 } 103 104 html += '</div>'; // end tiers 105 106 // HHI gauge â use supply score as proxy (normalized to 0-1) 107 var hhi = Math.min(supplyScore / 100, 1); 108 html += hhiGaugeSvg(hhi); 109 110 pane.innerHTML = html; 111 } 112 113 window.HAIPHEN = window.HAIPHEN || {}; 114 window.HAIPHEN.Featured = window.HAIPHEN.Featured || {}; 115 window.HAIPHEN.Featured.ContextSupply = { render: render }; 116})();
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.