1(function () { 2 'use strict'; 3 4 var root = document.querySelector('[data-hg-island-intro]'); 5 if (!root) return; 6 7 var inputs = Array.prototype.slice.call(root.querySelectorAll('[data-hg-island-input]')); 8 var cards = Array.prototype.slice.call(root.querySelectorAll('[data-hg-island-card]')); 9 var statuses = Array.prototype.slice.call(root.querySelectorAll('[data-island-status]')); 10 var videos = Array.prototype.slice.call(root.querySelectorAll('[data-hg-island-video]')); 11 var viewport = root.querySelector('[data-hg-island-viewport]'); 12 var previousButton = root.querySelector('[data-hg-island-prev]'); 13 var nextButton = root.querySelector('[data-hg-island-next]'); 14 var reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)'); 15 var scrollTimer = 0; 16 var resizeTimer = 0; 17 var suppressScrollSyncUntil = 0; 18 19 if (!inputs.length || !cards.length || !viewport || !previousButton || !nextButton) return; 20 21 function normalizeIndex(index) { 22 return (index + inputs.length) % inputs.length; 23 } 24 25 function currentIndex() { 26 var index = inputs.findIndex(function (input) { 27 return input.checked; 28 }); 29 return index === -1 ? 0 : index; 30 } 31 32 function islandName(index) { 33 return inputs[normalizeIndex(index)].getAttribute('data-island-name') || 'island'; 34 } 35 36 function pauseInactiveVideos(activeIsland) { 37 videos.forEach(function (video) { 38 var panel = video.closest('[data-hg-island-panel]'); 39 if (panel && panel.getAttribute('data-island') !== activeIsland && !video.paused) { 40 video.pause(); 41 } 42 }); 43 } 44 45 function centerCard(index, behavior) { 46 if (viewport.scrollWidth <= viewport.clientWidth + 2) return; 47 48 var card = cards[index]; 49 var viewportRect = viewport.getBoundingClientRect(); 50 var cardRect = card.getBoundingClientRect(); 51 var desiredLeft = viewport.scrollLeft 52 + cardRect.left 53 - viewportRect.left 54 - ((viewportRect.width - cardRect.width) / 2); 55 var maximumLeft = Math.max(0, viewport.scrollWidth - viewport.clientWidth); 56 57 suppressScrollSyncUntil = Date.now() + (behavior === 'smooth' ? 650 : 120); 58 viewport.scrollTo({ 59 left: Math.max(0, Math.min(maximumLeft, desiredLeft)), 60 behavior: behavior 61 }); 62 } 63 64 function updateInterface(index, options) { 65 var selectedInput = inputs[index]; 66 var selectedIsland = selectedInput.value; 67 var shouldScroll = !options || options.scroll !== false; 68 var scrollBehavior = reducedMotion.matches ? 'auto' : 'smooth'; 69 70 root.setAttribute('data-active-island', selectedIsland); 71 72 cards.forEach(function (card, cardIndex) { 73 if (cardIndex === index) { 74 card.setAttribute('aria-current', 'true'); 75 } else { 76 card.removeAttribute('aria-current'); 77 } 78 }); 79 80 statuses.forEach(function (status) { 81 status.hidden = status.getAttribute('data-island-status') !== selectedIsland; 82 }); 83 84 previousButton.setAttribute( 85 'aria-label', 86 'Show previous island: ' + islandName(index - 1) 87 ); 88 nextButton.setAttribute( 89 'aria-label', 90 'Show next island: ' + islandName(index + 1) 91 ); 92 93 pauseInactiveVideos(selectedIsland); 94 95 if (shouldScroll) { 96 centerCard(index, scrollBehavior); 97 } 98 } 99 100 function selectIsland(index, options) { 101 var normalizedIndex = normalizeIndex(index); 102 var selectedInput = inputs[normalizedIndex]; 103 104 if (!selectedInput.checked) { 105 selectedInput.checked = true; 106 } 107 108 updateInterface(normalizedIndex, options); 109 110 if (options && options.focus) { 111 selectedInput.focus({ preventScroll: true }); 112 } 113 } 114 115 function nearestCardIndex() { 116 var viewportRect = viewport.getBoundingClientRect(); 117 var viewportCenter = viewportRect.left + (viewportRect.width / 2); 118 var nearestIndex = 0; 119 var nearestDistance = Infinity; 120 121 cards.forEach(function (card, index) { 122 var cardRect = card.getBoundingClientRect(); 123 var cardCenter = cardRect.left + (cardRect.width / 2); 124 var distance = Math.abs(cardCenter - viewportCenter); 125 126 if (distance < nearestDistance) { 127 nearestDistance = distance; 128 nearestIndex = index; 129 } 130 }); 131 132 return nearestIndex; 133 } 134 135 function syncSelectionFromScroll() { 136 if (Date.now() < suppressScrollSyncUntil) return; 137 if (viewport.scrollWidth <= viewport.clientWidth + 2) return; 138 139 var nearestIndex = nearestCardIndex(); 140 if (nearestIndex !== currentIndex()) { 141 selectIsland(nearestIndex, { scroll: false }); 142 } 143 } 144 145 root.setAttribute('data-enhanced', 'true'); 146 previousButton.hidden = false;
147 previousButton.disabled = false; 148 nextButton.hidden = false; 149 nextButton.disabled = false; 150 151 inputs.forEach(function (input, index) { 152 input.addEventListener('change', function () { 153 if (input.checked) updateInterface(index); 154 }); 155 156 input.addEventListener('keydown', function (event) { 157 var targetIndex = null; 158 159 if (event.key === 'ArrowRight' || event.key === 'ArrowDown') { 160 targetIndex = index + 1; 161 } else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') { 162 targetIndex = index - 1; 163 } else if (event.key === 'Home') { 164 targetIndex = 0; 165 } else if (event.key === 'End') { 166 targetIndex = inputs.length - 1; 167 } 168 169 if (targetIndex === null) return; 170 171 event.preventDefault(); 172 selectIsland(targetIndex, { focus: true }); 173 }); 174 }); 175 176 previousButton.addEventListener('click', function () { 177 selectIsland(currentIndex() - 1); 178 }); 179 180 nextButton.addEventListener('click', function () { 181 selectIsland(currentIndex() + 1); 182 }); 183 184 viewport.addEventListener('pointerdown', function () { 185 suppressScrollSyncUntil = 0; 186 }, { passive: true }); 187 188 viewport.addEventListener('touchstart', function () { 189 suppressScrollSyncUntil = 0; 190 }, { passive: true }); 191 192 viewport.addEventListener('scroll', function () { 193 window.clearTimeout(scrollTimer); 194 scrollTimer = window.setTimeout(syncSelectionFromScroll, 120); 195 }, { passive: true }); 196 197 videos.forEach(function (video) { 198 video.addEventListener('play', function () { 199 videos.forEach(function (otherVideo) { 200 if (otherVideo !== video && !otherVideo.paused) otherVideo.pause(); 201 }); 202 }); 203 }); 204 205 window.addEventListener('resize', function () { 206 window.clearTimeout(resizeTimer); 207 resizeTimer = window.setTimeout(function () { 208 centerCard(currentIndex(), 'auto'); 209 }, 140); 210 }, { passive: true }); 211 212 updateInterface(currentIndex(), { scroll: false }); 213 214 window.requestAnimationFrame(function () { 215 centerCard(currentIndex(), 'auto'); 216 }); 217}());
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.