PageSourceSearch

https://haiphen.io/components/featured/context-supply.js?v=1

js haiphen.io collected 2026-10-01 12:02:19 UTC 4,935 bytes, 116 lines download raw bytes

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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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      + ' &middot; ' + 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.