1/** 2 * Hero V4 â energy flow lines (desktop only, width >= 1024px). 3 * Loaded with defer at end of page so it never blocks HTML parsing mid-<main>. 4 * Wrapped in try/catch so a SMIL/DOM edge case cannot hang the tab. 5 */ 6(function () { 7 'use strict'; 8 9 function clearFlow(svg) { 10 if (!svg) { 11 return; 12 } 13 var nodes = svg.querySelectorAll('[data-hv4-flow]'); 14 for (var i = nodes.length - 1; i >= 0; i--) { 15 try { 16 if (nodes[i].parentNode === svg) { 17 svg.removeChild(nodes[i]); 18 } 19 } catch (e) { 20 /* ignore stale nodes */ 21 } 22 } 23 } 24 25 function init() { 26 try { 27 if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) { 28 console.log('[hv4-hero-flow] prefers-reduced-motion: skip animation'); 29 return; 30 } 31 32 if (window.innerWidth < 1024) { 33 return; 34 } 35 36 var root = document.querySelector('section.hero-v4'); 37 if (!root) { 38 return; 39 } 40 41 var svg = root.querySelector('svg.hv4-lines'); 42 var outer = root.querySelector('.hv4-outer'); 43 var center = root.querySelector('.hv4-col-center'); 44 if (!svg || !outer || !center) { 45 return; 46 } 47 48 var leftIcons = root.querySelectorAll('.hv4-col-left .hv4-icon'); 49 var rightIcons = root.querySelectorAll('.hv4-col-right .hv4-icon'); 50 51 function pos(el) { 52 var r = el.getBoundingClientRect(); 53 var p = outer.getBoundingClientRect(); 54 return { x: r.left - p.left + r.width / 2, y: r.top - p.top + r.height / 2 }; 55 } 56 57 function draw() { 58 clearFlow(svg); 59 var cRect = center.getBoundingClientRect(); 60 var pRect = outer.getBoundingClientRect(); 61 var cx = cRect.left - pRect.left + cRect.width / 2; 62 var cy = cRect.top - pRect.top + cRect.height / 2; 63 64 if (!isFinite(cx) || !isFinite(cy)) { 65 return; 66 } 67 68 var allIcons = []; 69 leftIcons.forEach(function (ic) { 70 allIcons.push({ el: ic, side: 'left' }); 71 }); 72 rightIcons.forEach(function (ic) { 73 allIcons.push({ el: ic, side: 'right' }); 74 }); 75 76 allIcons.forEach(function (item, i) { 77 var p1 = pos(item.el); 78 var cp1x; 79 var cp2x; 80 var startX; 81 var startY; 82 var endX; 83 var endY; 84 85 if (item.side === 'left') { 86 startX = p1.x; 87 startY = p1.y; 88 endX = cx; 89 endY = cy; 90 cp1x = startX + (endX - startX) * 0.5; 91 cp2x = endX - (endX - startX) * 0.15; 92 } else { 93 startX = cx; 94 startY = cy; 95 endX = p1.x; 96 endY = p1.y; 97 cp1x = startX + (endX - startX) * 0.15; 98 cp2x = endX - (endX - startX) * 0.5; 99 } 100 101 if (!isFinite(startX) || !isFinite(endX) || !isFinite(startY) || !isFinite(endY)) { 102 return; 103 } 104 105 var pathD = 106 'M' + 107 startX + 108 ',' + 109 startY + 110 ' C' + 111 cp1x + 112 ',' + 113 startY + 114 ' ' + 115 cp2x + 116 ',' + 117 endY + 118 ' ' + 119 endX + 120 ',' + 121 endY; 122 123 var path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 124 path.setAttribute('data-hv4-flow', '1'); 125 path.setAttribute('d', pathD); 126 path.setAttribute('fill', 'none'); 127 path.setAttribute('stroke', 'url(#hv4FlowGrad)'); 128 path.setAttribute('stroke-width', '1.5'); 129 path.setAttribute('opacity', '0.5'); 130 svg.appendChild(path); 131 132 var dot = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 133 dot.setAttribute('data-hv4-flow', '1'); 134 dot.setAttribute('r', '3'); 135 dot.setAttribute('fill', '#FF4500'); 136 dot.setAttribute('opacity', '0.38'); 137 svg.appendChild(dot); 138 139 var anim = document.createElementNS('http://www.w3.org/2000/svg', 'animateMotion'); 140 anim.setAttribute('dur', 3 + i * 0.4 + 's'); 141 anim.setAttribute('repeatCount', 'indefinite'); 142 anim.setAttribute('path', pathD); 143 dot.appendChild(anim); 144 }); 145 } 146 147 function setup() { 148 if (window.innerWidth < 1024) { 149 clearFlow(svg); 150 return; 151 } 152 svg.setAttribute('width', String(outer.offsetWidth || 0)); 153 svg.setAttribute('height', String(outer.offsetHeight || 0
153)); 154 draw(); 155 } 156 157 function scheduleSetup() { 158 window.requestAnimationFrame(function () { 159 window.requestAnimationFrame(setup); 160 }); 161 } 162 163 if (document.readyState === 'complete') { 164 scheduleSetup(); 165 } else { 166 window.addEventListener('load', scheduleSetup); 167 } 168 169 var resizeTimer; 170 window.addEventListener( 171 'resize', 172 function () { 173 clearTimeout(resizeTimer); 174 resizeTimer = setTimeout(function () { 175 if (window.innerWidth >= 1024) { 176 scheduleSetup(); 177 } else { 178 clearFlow(svg); 179 } 180 }, 200); 181 }, 182 { passive: true } 183 ); 184 } catch (err) { 185 console.warn('[hv4-hero-flow] disabled due to error:', err); 186 } 187 } 188 189 if (document.readyState === 'loading') { 190 document.addEventListener('DOMContentLoaded', init); 191 } else { 192 init(); 193 } 194})();
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.