1/** About Us â scroll reveal + timeline progress line + active markers. */ 2(function () { 3 function initReveal() { 4 document.documentElement.classList.add('jbw-anim'); 5 6 var targets = document.querySelectorAll('[data-reveal]'); 7 if (!targets.length) return; 8 9 function markInView(el) { 10 var rect = el.getBoundingClientRect(); 11 return rect.top < window.innerHeight * 0.92 && rect.bottom > 0; 12 } 13 14 if (!('IntersectionObserver' in window)) { 15 targets.forEach(function (el) { 16 el.classList.add('is-inview'); 17 }); 18 return; 19 } 20 21 var observer = new IntersectionObserver( 22 function (entries) { 23 entries.forEach(function (entry) { 24 if (entry.isIntersecting) { 25 entry.target.classList.add('is-inview'); 26 observer.unobserve(entry.target); 27 } 28 }); 29 }, 30 { rootMargin: '0px 0px -8% 0px', threshold: 0.08 } 31 ); 32 33 targets.forEach(function (el) { 34 if (markInView(el)) { 35 el.classList.add('is-inview'); 36 } else { 37 observer.observe(el); 38 } 39 }); 40 } 41 42 function initTimeline() { 43 var timeline = document.querySelector('[data-jbw-timeline]'); 44 var progress = document.querySelector('[data-jbw-timeline-progress]'); 45 if (!timeline || !progress) return; 46 47 var items = Array.prototype.slice.call( 48 timeline.querySelectorAll('.jbw-about-timeline__item') 49 ); 50 var ticking = false; 51 var lastActive = -1; 52 var lastRatio = -1; 53 54 function update() { 55 ticking = false; 56 57 var rect = timeline.getBoundingClientRect(); 58 var top = window.scrollY + rect.top; 59 var height = timeline.offsetHeight; 60 var viewportMid = window.scrollY + window.innerHeight * 0.45; 61 var start = top; 62 var end = top + height; 63 var ratio = (viewportMid - start) / Math.max(end - start, 1); 64 ratio = Math.min(1, Math.max(0, ratio)); 65 66 if (Math.abs(ratio - lastRatio) > 0.001) { 67 lastRatio = ratio; 68 progress.style.transform = 'scaleY(' + ratio + ')'; 69 } 70 71 var focusY = window.innerHeight * 0.42; 72 var best = -1; 73 var bestDist = Infinity; 74 75 for (var i = 0; i < items.length; i++) { 76 var itemRect = items[i].getBoundingClientRect(); 77 if (itemRect.bottom < 0 || itemRect.top > window.innerHeight) continue; 78 var mid = itemRect.top + itemRect.height * 0.35; 79 var dist = Math.abs(mid - focusY); 80 if (dist < bestDist) { 81 bestDist = dist; 82 best = i; 83 } 84 } 85 86 if (best !== lastActive) { 87 if (lastActive >= 0) items[lastActive].classList.remove('is-active'); 88 if (best >= 0) items[best].classList.add('is-active'); 89 lastActive = best; 90 } 91 } 92 93 function onScroll() { 94 if (!ticking) { 95 ticking = true; 96 requestAnimationFrame(update); 97 } 98 } 99 100 window.addEventListener('scroll', onScroll, { passive: true }); 101 window.addEventListener('resize', onScroll); 102 update(); 103 } 104 105 if (document.readyState === 'loading') { 106 document.addEventListener('DOMContentLoaded', function () { 107 initReveal(); 108 initTimeline(); 109 }); 110 } else { 111 initReveal(); 112 initTimeline(); 113 } 114})();
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.