1(function () { 2 'use strict'; 3 4 function toPositiveInt(value, fallback) { 5 const parsed = parseInt(value, 10); 6 return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; 7 } 8 9 function toPositiveFloat(value, fallback) { 10 const parsed = parseFloat(value); 11 return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; 12 } 13 14 function clamp(value, min, max) { 15 return Math.max(min, Math.min(max, value)); 16 } 17 18 function mod(value, size) { 19 return ((value % size) + size) % size; 20 } 21 22 function getSlidesPerView(carousel, totalSlides) { 23 const computed = window.getComputedStyle(carousel); 24 let slides = toPositiveInt(computed.getPropertyValue('--amm-grid-cols-mobile'), 1); 25 26 if (window.innerWidth >= 960) { 27 slides = toPositiveInt(computed.getPropertyValue('--amm-grid-cols-desktop'), slides); 28 } else if (window.innerWidth >= 600) { 29 slides = toPositiveInt(computed.getPropertyValue('--amm-grid-cols-tablet'), slides); 30 } 31 32 return clamp(slides, 1, totalSlides); 33 } 34 35 function createButton(className, label, iconName) { 36 const button = document.createElement('button'); 37 const icon = document.createElement('span'); 38 39 button.type = 'button'; 40 button.className = className; 41 button.setAttribute('aria-label', label); 42 icon.className = 'material-icons'; 43 icon.setAttribute('aria-hidden', 'true'); 44 icon.setAttribute('data-no-auto-translation', ''); 45 icon.textContent = iconName; 46 button.appendChild(icon); 47 48 return button; 49 } 50 51 document.addEventListener('DOMContentLoaded', function () { 52 const carousels = document.querySelectorAll('.wp-block-amm-grid.is-carousel-enabled'); 53 54 carousels.forEach(function (carousel) { 55 if (carousel.hasAttribute('data-carousel-initialized')) { 56 return; 57 } 58 let inner = carousel.querySelector('.amm-grid__inner'); 59 if (!inner) { 60 return; 61 } 62 carousel.setAttribute('data-carousel-initialized', 'true'); 63 64 let viewport = inner.parentElement && inner.parentElement.classList.contains('amm-grid__viewport') 65 ? inner.parentElement 66 : null; 67 68 // Older saved blocks won't have the viewport wrapper yet, so add it at runtime. 69 if (!viewport) { 70 viewport = document.createElement('div'); 71 viewport.className = 'amm-grid__viewport'; 72 carousel.insertBefore(viewport, inner); 73 viewport.appendChild(inner); 74 } 75 76 const realSlides = Array.prototype.slice.call(inner.children); 77 const totalSlides = realSlides.length; 78 if (totalSlides < 2) { 79 return; 80 } 81 82 realSlides.forEach(function (slide, index) { 83 slide.removeAttribute('data-carousel-clone'); 84 slide.removeAttribute('aria-hidden'); 85 slide.setAttribute('data-carousel-real-index', String(index)); 86 }); 87 88 const maxSelectors = clamp(toPositiveInt(carousel.dataset.carouselMaxSelectors, 5), 1, totalSlides); 89 const cycleTimeSeconds = toPositiveFloat(carousel.dataset.carouselCycleTime, 4); 90 const autoSlideEnabled = carousel.dataset.carouselAutoSlide === 'true'; 91 const featureFocusEnabled = carousel.dataset.carouselFeatureFocus === 'true'; 92 const showArrows = carousel.dataset.carouselShowArrows === 'true'; 93 const showDots = carousel.dataset.carouselShowDots !== 'false'; 94 const focusEdgeVisibility = 0.78; 95 const transitionStyle = 'transform 320ms ease'; 96 const baseTrackGap = parseFloat(window.getComputedStyle(inner).getPropertyValue('column-gap') || window.getComputedStyle(inner).getPropertyValue('gap')) || 0; 97 98 let currentSlide = 0; 99 let slidesPerView = 1; 100 let cloneCount = 0; 101 let currentPhysicalIndex = 0; 102 let renderedSlidesPerView = 0; 103 let trackSlides = realSlides.slice(); 104 let autoplayInterval = null; 105 let pendingRender = false;
106 let resizeRaf = null; 107 let transitionRestoreRaf = null; 108 let isTransitioning = false; 109 let isPointerInside = false; 110 let hasFocusWithin = false; 111 let mediaEngaged = false; 112 113 const nav = document.createElement('div'); 114 nav.className = 'amm-grid-carousel__nav'; 115 const prev = createButton('amm-grid-carousel__arrow amm-grid-carousel__arrow--prev', 'Previous slide', 'chevron_left'); 116 const next = createButton('amm-grid-carousel__arrow amm-grid-carousel__arrow--next', 'Next slide', 'chevron_right'); 117 nav.appendChild(prev); 118 nav.appendChild(next); 119 120 const indicators = document.createElement('div'); 121 indicators.className = 'amm-grid-carousel__indicators'; 122 indicators.setAttribute('role', 'tablist'); 123 124 const indicatorsParent = carousel.querySelector('.amm-grid__content') || carousel; 125 carousel.appendChild(nav); 126 indicatorsParent.appendChild(indicators); 127 128 function isMeasurable() { 129 return inner.clientWidth > 0 && viewport.clientWidth > 0 && carousel.getClientRects().length > 0; 130 } 131 132 function setTransitionEnabled(enabled) { 133 inner.style.transition = enabled ? transitionStyle : 'none'; 134 } 135 136 function clearTransitionRestore() { 137 if (!transitionRestoreRaf) { 138 return; 139 } 140 window.cancelAnimationFrame(transitionRestoreRaf); 141 transitionRestoreRaf = null; 142 } 143 144 function removeClones() { 145 const clones = inner.querySelectorAll('[data-carousel-clone="true"]'); 146 clones.forEach(function (clone) { 147 clone.remove(); 148 }); 149 } 150 151 function createClone(realIndex) { 152 const clone = realSlides[realIndex].cloneNode(true); 153 clone.setAttribute('data-carousel-clone', 'true'); 154 clone.setAttribute('aria-hidden', 'true'); 155 clone.setAttribute('data-carousel-real-index', String(realIndex)); 156 return clone; 157 } 158 159 function rebuildClones(nextSlidesPerView) { 160 removeClones(); 161 cloneCount = Math.min(totalSlides, nextSlidesPerView + 1); 162 163 if (cloneCount > 0) { 164 const prependFragment = document.createDocumentFragment(); 165 for (let index = totalSlides - cloneCount; index < totalSlides; index += 1) { 166 prependFragment.appendChild(createClone(index)); 167 } 168 inner.insertBefore(prependFragment, inner.firstChild); 169 170 const appendFragment = document.createDocumentFragment(); 171 for (let index = 0; index < cloneCount; index += 1) { 172 appendFragment.appendChild(createClone(index)); 173 } 174 inner.appendChild(appendFragment); 175 } 176 177 trackSlides = Array.prototype.slice.call(inner.children); 178 renderedSlidesPerView = nextSlidesPerView; 179 currentPhysicalIndex = cloneCount + currentSlide; 180 } 181 182 function scheduleRender() { 183 if (resizeRaf) { 184 window.cancelAnimationFrame(resizeRaf); 185 } 186 resizeRaf = window.requestAnimationFrame(function () { 187 render(); 188 }); 189 } 190 191 function ensureVisibleRender() { 192 if (!pendingRender) { 193 return; 194 } 195 if (isMeasurable()) { 196 scheduleRender(); 197 return; 198 } 199 window.setTimeout(ensureVisibleRender, 200); 200 } 201 202 function getIndicatorWindowStart() { 203 const visibleCount = Math.min(totalSlides, maxSelectors); 204 const half = Math.floor(visibleCount / 2); 205 const maxWindowStart = Math.max(0, totalSlides - visibleCount); 206 return clamp(currentSlide - half, 0, maxWindowStart); 207 } 208 209 function getFocusScale() { 210 if (!featureFocusEnabled || slidesPerView < 2) { 211 return 1; 212 } 213 return slidesPerView % 2 === 0 ? 1.06 : 1.08; 214 } 215 216 function updateSlideWidths() { 217 const baseGap = baseTrackGap; 218 const viewportWidth = viewport.clientWidth; 219 let gap = baseGap; 220 let width = (viewportWidth - gap * (slidesPerView - 1)) / slidesPerView; 221 222 if (featureFocusEnabled && slidesPerView >= 3) { 223 const centerCount = slidesPerView % 2 === 0 ? 2 : 1; 224 const edgeCount = Math.max(0, slidesPerView - centerCount); 225 const weightedSlots = centerCount + edgeCount * focusEdgeVisibility; 226 const gapCompensationFactor = (getFocusScale() - 1) / 2; 227 const denominator = weightedSlots + gapCompensationFactor * Math.max(0, slidesPerView - 1);
228 width = (viewportWidth - baseGap * (slidesPerView - 1)) / denominator; 229 gap = baseGap + gapCompensationFactor * width; 230 } 231 inner.style.gap = gap + 'px'; 232 233 trackSlides.forEach(function (slide) { 234 slide.style.width = width + 'px'; 235 }); 236 237 return { 238 width: width, 239 gap: gap, 240 viewportWidth: viewportWidth, 241 }; 242 } 243 244 function updateViewportHeight() { 245 let maxHeight = 0; 246 viewport.style.minHeight = ''; 247 248 realSlides.forEach(function (slide) { 249 const height = slide.offsetHeight; 250 if (height > maxHeight) { 251 maxHeight = height; 252 } 253 }); 254 255 viewport.style.minHeight = maxHeight > 0 ? maxHeight + 'px' : ''; 256 } 257 258 function getOffsetForPhysicalIndex(physicalIndex, metrics) { 259 const step = metrics.width + metrics.gap; 260 const baseOffset = physicalIndex * step; 261 262 if (!featureFocusEnabled) { 263 return baseOffset; 264 } 265 266 const centerCount = slidesPerView % 2 === 0 ? 2 : 1; 267 const focusedWidth = centerCount * metrics.width * getFocusScale() + Math.max(0, centerCount - 1) * metrics.gap; 268 const centeringOffset = (metrics.viewportWidth - focusedWidth) / 2; 269 return baseOffset - centeringOffset; 270 } 271 272 function updateFocusClasses() { 273 trackSlides.forEach(function (slide) { 274 slide.classList.remove('is-feature-focus-single'); 275 slide.classList.remove('is-feature-focus-left'); 276 slide.classList.remove('is-feature-focus-right'); 277 }); 278 279 if (!featureFocusEnabled) { 280 return; 281 } 282 283 if (slidesPerView % 2 === 1) { 284 const middleRealIndex = mod(currentSlide, totalSlides);
285 trackSlides.forEach(function (slide) { 286 const realIndex = toPositiveInt(slide.getAttribute('data-carousel-real-index'), 0); 287 if (realIndex === middleRealIndex) { 288 slide.classList.add('is-feature-focus-single'); 289 } 290 }); 291 return; 292 } 293 294 const leftRealIndex = mod(currentSlide, totalSlides); 295 const rightRealIndex = mod(leftRealIndex + 1, totalSlides); 296 297 trackSlides.forEach(function (slide) { 298 const realIndex = toPositiveInt(slide.getAttribute('data-carousel-real-index'), 0); 299 if (realIndex === leftRealIndex) { 300 slide.classList.add('is-feature-focus-left'); 301 } else if (realIndex === rightRealIndex) { 302 slide.classList.add('is-feature-focus-right'); 303 } 304 }); 305 } 306 307 function updateIndicators() { 308 const visibleCount = Math.min(totalSlides, maxSelectors); 309 const windowStart = getIndicatorWindowStart(); 310 const activePosition = clamp(currentSlide - windowStart, 0, visibleCount - 1); 311 312 indicators.innerHTML = ''; 313 314 for (let index = 0; index < visibleCount; index += 1) { 315 const slideIndex = windowStart + index; 316 const indicator = document.createElement('button'); 317 indicator.type = 'button'; 318 indicator.className = 'amm-grid-carousel__indicator'; 319 indicator.setAttribute('role', 'tab'); 320 indicator.setAttribute('aria-label', 'Go to slide ' + (slideIndex + 1)); 321 indicator.setAttribute('aria-selected', index === activePosition ? 'true' : 'false'); 322 if (index === activePosition) { 323 indicator.classList.add('is-active'); 324 } 325 326 indicator.addEventListener('click', function () { 327 const delta = index - activePosition; 328 if (delta === 0) { 329 return; 330 } 331 moveBy(delta); 332 resetAutoplay(); 333 }); 334 335 indicators.appendChild(indicator); 336 } 337 } 338 339 function updateControlsState() { 340 prev.disabled = false; 341 next.disabled = false; 342 } 343 344 function setTrackTransform(offset, animate) { 345 clearTransitionRestore(); 346 setTransitionEnabled(animate); 347 inner.style.transform = 'translate3d(' + -offset + 'px, 0, 0)'; 348 349 if (!animate) { 350 transitionRestoreRaf = window.requestAnimationFrame(function () { 351 setTransitionEnabled(true); 352 transitionRestoreRaf = null; 353 }); 354 } 355 } 356 357 function normalizePhysicalIndex(index) { 358 const minPhysical = cloneCount; 359 const maxPhysical = cloneCount + totalSlides - 1; 360 let normalized = index; 361 362 while (normalized < minPhysical) { 363 normalized += totalSlides; 364 } 365 while (normalized > maxPhysical) { 366 normalized -= totalSlides; 367 } 368 369 return normalized; 370 } 371 372 function snapToRealRange() { 373 const normalized = normalizePhysicalIndex(currentPhysicalIndex); 374 if (normalized === currentPhysicalIndex) { 375 return; 376 } 377 378 currentPhysicalIndex = normalized; 379 const metrics = updateSlideWidths(); 380 const offset = getOffsetForPhysicalIndex(currentPhysicalIndex, metrics); 381 382 setTransitionEnabled(false); 383 inner.style.transform = 'translate3d(' + -offset + 'px, 0, 0)'; 384 void inner.offsetHeight; 385 setTransitionEnabled(true); 386 } 387 388 function render(options) { 389 const renderOptions = options || {}; 390 if (!isMeasurable()) { 391 pendingRender = true; 392 ensureVisibleRender(); 393 return; 394 } 395 pendingRender = false;
396 397 const nextSlidesPerView = getSlidesPerView(carousel, totalSlides); 398 slidesPerView = nextSlidesPerView; 399 currentSlide = mod(currentSlide, totalSlides); 400 401 let rebuilt = false; 402 if (renderedSlidesPerView !== slidesPerView || cloneCount === 0) { 403 rebuildClones(slidesPerView); 404 rebuilt = true; 405 } 406 407 if (rebuilt) { 408 currentPhysicalIndex = cloneCount + currentSlide; 409 } else if (typeof renderOptions.physicalIndex === 'number') { 410 currentPhysicalIndex = renderOptions.physicalIndex; 411 } else { 412 currentPhysicalIndex = cloneCount + currentSlide; 413 } 414 415 const metrics = updateSlideWidths(); 416 const offset = getOffsetForPhysicalIndex(currentPhysicalIndex, metrics); 417 setTrackTransform(offset, !!renderOptions.animate); 418 updateViewportHeight(); 419 isTransitioning = !!renderOptions.animate; 420 421 updateFocusClasses(); 422 updateIndicators(); 423 updateControlsState(); 424 425 const canSlide = totalSlides > 1; 426 const isMobileViewport = window.innerWidth <= 767; 427 // Arrows are hidden on phones unless the block opts in (data-carousel-arrows-mobile). 428 const arrowsAllowed = !isMobileViewport || carousel.dataset.carouselArrowsMobile === 'true'; 429 nav.style.display = canSlide && showArrows && arrowsAllowed ? 'flex' : 'none'; 430 indicators.style.display = canSlide && showDots ? 'flex' : 'none'; 431 } 432 433 function moveBy(step) { 434 if (!step) { 435 return; 436 } 437 currentSlide = mod(currentSlide + step, totalSlides); 438 currentPhysicalIndex += step; 439 render({ 440 animate: true, 441 physicalIndex: currentPhysicalIndex, 442 }); 443 } 444 445 function nextSlide() { 446 moveBy(1); 447 } 448 449 function previousSlide() { 450 moveBy(-1); 451 } 452 453 function startAutoplay() { 454 if ( 455 !autoSlideEnabled || 456 autoplayInterval || 457 isPointerInside || 458 hasFocusWithin || 459 mediaEngaged 460 ) { 461 return; 462 } 463 autoplayInterval = window.setInterval(function () { 464 nextSlide(); 465 }, cycleTimeSeconds * 1000); 466 } 467 468 function stopAutoplay() { 469 if (!autoplayInterval) { 470 return; 471 } 472 window.clearInterval(autoplayInterval); 473 autoplayInterval = null; 474 } 475 476 function resetAutoplay() { 477 stopAutoplay(); 478 startAutoplay(); 479 } 480 481 function engageMedia() { 482 mediaEngaged = true; 483 stopAutoplay(); 484 scheduleRender(); 485 } 486 487 inner.addEventListener('transitionend', function (event) { 488 if (event.propertyName !== 'transform' || !isTransitioning) { 489 return; 490 } 491 isTransitioning = false; 492 snapToRealRange(); 493 }); 494 495 prev.addEventListener('click', function () { 496 previousSlide(); 497 resetAutoplay(); 498 }); 499 500 next.addEventListener('click', function () { 501 nextSlide(); 502 resetAutoplay(); 503 }); 504 505 if (typeof window.ammwebBindSwipe === 'function') { 506 window.ammwebBindSwipe(viewport, { 507 threshold: 40, 508 onStart: stopAutoplay, 509 onSwipeLeft: nextSlide, 510 onSwipeRight: previousSlide, 511 onEnd: resetAutoplay, 512 }); 513 } 514 515 carousel.addEventListener('mouseenter', function () { 516 isPointerInside = true; 517 stopAutoplay(); 518 }); 519 carousel.addEventListener('mouseleave', function () { 520 isPointerInside = false;
521 startAutoplay(); 522 }); 523 carousel.addEventListener('focusin', function () { 524 hasFocusWithin = true; 525 stopAutoplay(); 526 }); 527 carousel.addEventListener('focusout', function (event) { 528 if (!carousel.contains(event.relatedTarget)) { 529 hasFocusWithin = false; 530 startAutoplay(); 531 } 532 }); 533 carousel.addEventListener('click', function (event) { 534 if (event.target.closest('[data-amm-industry-expert-play-video], [data-amm-video-modal-trigger], video, audio, iframe')) { 535 engageMedia(); 536 } 537 }); 538 carousel.addEventListener('play', engageMedia, true); 539 carousel.addEventListener('loadedmetadata', scheduleRender, true); 540 carousel.addEventListener('load', scheduleRender, true); 541 542 carousel.addEventListener('keydown', function (event) { 543 if (event.key === 'ArrowLeft') { 544 previousSlide(); 545 resetAutoplay(); 546 } else if (event.key === 'ArrowRight') { 547 nextSlide(); 548 resetAutoplay(); 549 } 550 }); 551 552 window.addEventListener('resize', scheduleRender); 553 window.addEventListener('load', scheduleRender); 554 window.addEventListener('pageshow', scheduleRender); 555 document.addEventListener('visibilitychange', scheduleRender); 556 document.addEventListener('shown.bs.tab', scheduleRender); 557 document.addEventListener('tabsactivate', scheduleRender); 558 document.addEventListener('accordionactivate', scheduleRender); 559 560 if (typeof ResizeObserver !== 'undefined') { 561 const resizeObserver = new ResizeObserver(function () { 562 scheduleRender(); 563 }); 564 resizeObserver.observe(carousel); 565 resizeObserver.observe(viewport); 566 resizeObserver.observe(inner); 567 } 568 569 render(); 570 startAutoplay(); 571 }); 572 }); 573})();
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.