1/* Landing page: home-update â accordions */ 2(function () { 3 function setupAccordion(itemSelector, arrowSelector, exclusive) { 4 var items = document.querySelectorAll(itemSelector); 5 6 function setArrow(item, isOpen) { 7 var arrow = item.querySelector(arrowSelector); 8 if (arrow) { 9 arrow.innerHTML = isOpen ? '▲' : '▼'; 10 } 11 } 12 13 items.forEach(function (item) { 14 item.addEventListener('click', function (e) { 15 // Let CTA links inside the panel work normally 16 if (e.target.closest('a')) { 17 return; 18 } 19 var isOpen = item.classList.toggle('is-open'); 20 setArrow(item, isOpen); 21 if (exclusive && isOpen) { 22 items.forEach(function (other) { 23 if (other !== item && other.classList.contains('is-open')) { 24 other.classList.remove('is-open'); 25 setArrow(other, false); 26 } 27 }); 28 } 29 }); 30 }); 31 } 32 33 setupAccordion('.sa-landing--home-update .lp-faq-item', '.lp-faq-arrow', false); 34 setupAccordion('.sa-landing--home-update .lp-offer', '.lp-offer-arrow', true); 35 36 /* Swap the side image when an offer card is opened. 37 Each .lp-offer may define data-image (URL) and data-image-alt. 38 Until images are provided, the placeholder label updates instead. */ 39 (function () { 40 var imageBox = document.getElementById('lp-offer-image'); 41 if (!imageBox) { 42 return; 43 } 44 45 function applyImage(src, alt) { 46 if (src) { 47 var img = imageBox.querySelector('img'); 48 if (!img) { 49 imageBox.textContent = ''; 50 img = document.createElement('img'); 51 img.className = 'lp-offer-img'; 52 imageBox.appendChild(img); 53 imageBox.classList.add('has-image'); 54 } 55 img.src = src; 56 img.alt = alt; 57 } else { 58 imageBox.classList.remove('has-image'); 59 imageBox.innerHTML = ''; 60 imageBox.textContent = '[ Image: ' + (alt || 'coming soon') + ' ]'; 61 } 62 } 63 64 function showImageFor(card) { 65 applyImage(card.getAttribute('data-image'), card.getAttribute('data-image-alt') || ''); 66 } 67 68 // Preload all accordion images up front so switches are instant 69 document.querySelectorAll('.sa-landing--home-update .lp-offer[data-image]').forEach(function (card) { 70 var src = card.getAttribute('data-image'); 71 if (src) { 72 (new Image()).src = src; 73 } 74 }); 75 76 var cards = document.querySelectorAll('.sa-landing--home-update .lp-offer'); 77 cards.forEach(function (card) { 78 card.addEventListener('click', function (e) { 79 if (e.target.closest('a')) { 80 return; 81 } 82 if (card.classList.contains('is-open')) { 83 showImageFor(card); 84 } 85 }); 86 }); 87 88 // Initialize with the card that starts open 89 var openCard = document.querySelector('.sa-landing--home-update .lp-offer.is-open'); 90 if (openCard && (openCard.getAttribute('data-image') || openCard.getAttribute('data-image-alt'))) { 91 showImageFor(openCard); 92 } 93 })(); 94 95 /* Countdown to first day of classes. Live when data-live="1"; otherwise 96 preview with ?show-countdown=1. Hides itself once the target date hits. */ 97 (function () { 98 var root = document.getElementById('lp-countdown'); 99 if (!root) { 100 return; 101 } 102 103 var live = root.getAttribute('data-live') === '1'; 104 var preview = false; 105 try { 106 preview = /(?:^|[?&])show-countdown=1(?:&|$)/.test(window.location.search); 107 } catch (e) {} 108 109 if (!live && !preview) { 110 return; 111 } 112 113 if (preview) { 114 root.removeAttribute('hidden'); 115 } 116 117 var targetMs = Date.parse(root.getAttribute('data-target') || ''); 118 if (!targetMs) { 119 return; 120 } 121 122 var values = { 123 days: root.querySelector('[data-unit="days"]'), 124 hours: root.querySelector('[data-unit="hours"]'), 125 minutes: root.querySelector('[data-unit="minutes"]'), 126 seconds: root.querySelector('[data-unit="seconds"]') 127 }; 128 129 function pad(n) { 130 return n < 10 ? '0' + n : String(n); 131 } 132 133 function render(diff) { 134 var total = Math.floor(diff / 1000); 135 var days = Math.floor(total / 86400); 136 total %= 86400; 137 var hours = Math.floor(total / 3600); 138 total %= 3600; 139 var minutes = Math.floor(total / 60); 140 var seconds = total % 60; 141 142 if (values.days) { 143 values.days.textContent = String(days); 144 } 145 if (values.hours) { 146 values.hours.textContent = pad(hours); 147 } 148 if (values.minutes) { 149 values.minutes.textContent = pad(minutes); 150 } 151 if (values.seconds) { 152 values.seconds.textContent = pad(seconds); 153 } 154 } 155 156 function tick() { 157 var diff = targetMs - Date.now(); 158 if (diff <= 0) { 159 window.clearInterval(intervalId); 160 root.setAttribute('hidden', ''); 161 return; 162 } 163 render(diff); 164 } 165 166 var intervalId = window.setInterval(tick, 1000); 167 tick(); 168 })(); 169 170 /* Awards popup: homepage only, once per browser. Append ?show-popup=1 171 to preview without writing the seen flag. */ 172 (function () { 173 var popup = document.getElementById('lp-awards-popup'); 174 if (!popup) { 175 return; 176 } 177 178 var STORAGE_KEY = 'sa-home-awards-popup-seen'; 179 var DELAY_MS = 3000; 180 var dialog = popup.querySelector('.lp-popup-dialog'); 181 var closeEls = popup.querySelectorAll('[data-lp-popup-close]'); 182 var lastFocus = null; 183 var preview = false;
184 185 var countdownPreview = false; 186 try { 187 preview = window.location.search.indexOf('show-popup=1') !== -1; 188 countdownPreview = /(?:^|[?&])show-countdown=1(?:&|$)/.test(window.location.search); 189 } catch (e) {} 190 191 // Keep the awards popup out of the way while reviewing the countdown. 192 if (countdownPreview) { 193 return; 194 } 195 196 function storageGet() { 197 try { return window.localStorage.getItem(STORAGE_KEY); } catch (e) { return '1'; } 198 } 199 200 function storageSet() { 201 if (preview) { 202 return; 203 } 204 try { window.localStorage.setItem(STORAGE_KEY, '1'); } catch (e) {} 205 } 206 207 function focusables() { 208 return dialog.querySelectorAll('a[href], button:not([disabled])'); 209 } 210 211 function openPopup() { 212 lastFocus = document.activeElement; 213 popup.removeAttribute('hidden'); 214 document.body.classList.add('lp-popup-open'); 215 window.requestAnimationFrame(function () { 216 popup.classList.add('is-open'); 217 var closeBtn = popup.querySelector('.lp-popup-close'); 218 if (closeBtn) { 219 closeBtn.focus(); 220 } 221 }); 222 } 223 224 function closePopup() { 225 popup.classList.remove('is-open'); 226 document.body.classList.remove('lp-popup-open'); 227 storageSet(); 228 window.setTimeout(function () { 229 popup.setAttribute('hidden', ''); 230 if (lastFocus && typeof lastFocus.focus === 'function') { 231 lastFocus.focus(); 232 } 233 }, 200); 234 } 235 236 if (!preview && storageGet()) { 237 return; 238 } 239 240 var reduceMotion = false; 241 try { 242 reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; 243 } catch (e) {} 244 245 window.setTimeout(openPopup, reduceMotion ? 0 : DELAY_MS); 246 247 closeEls.forEach(function (el) { 248 el.addEventListener('click', function (e) { 249 e.preventDefault(); 250 closePopup(); 251 }); 252 }); 253 254 popup.addEventListener('click', function (e) { 255 if (e.target.closest('.lp-btn')) { 256 storageSet(); 257 } 258 }); 259 260 document.addEventListener('keydown', function (e) { 261 if (popup.hasAttribute('hidden')) { 262 return; 263 } 264 if (e.key === 'Escape') { 265 e.preventDefault(); 266 closePopup(); 267 return; 268 } 269 if (e.key !== 'Tab') { 270 return; 271 } 272 var nodes = focusables(); 273 if (!nodes.length) { 274 return; 275 } 276 var first = nodes[0]; 277 var last = nodes[nodes.length - 1]; 278 if (e.shiftKey && document.activeElement === first) { 279 e.preventDefault(); 280 last.focus(); 281 } else if (!e.shiftKey && document.activeElement === last) { 282 e.preventDefault(); 283 first.focus(); 284 } 285 }); 286 })(); 287})();
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.