1(function (w, d) { 2 'use strict'; 3 function init(root) { 4 if (root.dataset.ready) { return; } 5 var viewport = root.querySelector('.bshhero__viewport'); 6 var slides = Array.prototype.slice.call(viewport.children); 7 var dots = Array.prototype.slice.call(root.querySelectorAll('[data-slide]')); 8 var desktop = w.matchMedia('(min-width: 1024px)'); 9 var phone = w.matchMedia('(max-width: 743px)'); 10 var slideWidth = 0, alignAfterLayout = false, sizeObserver = null, layoutNeedsMeasure = false; 11 var reduced = w.matchMedia('(prefers-reduced-motion: reduce)'); 12 var current = 0, copies = 0, step = 0, loop = false; 13 var frame = 0, settle = 0, timer = 0, hover = false, focused = false, touching = false, visible = true; 14 var scrollFrame = 0, scrolling = false, wanted = null, marked = -1, alignedTo = null; 15 var observedScrollLeft = 0; 16 var delay = Math.max(0, Number(root.dataset.delay) || 0); 17 var media = null; 18 root.dataset.ready = '1'; 19 function originalOnError(img) { 20 if (!img || img.tagName !== 'IMG' || img.hasAttribute('data-bshhero-fallback')) { return; } 21 var picture = img.parentNode; 22 var source = picture && picture.querySelector('source[data-bshhero-original]'); 23 if (!img.hasAttribute('srcset') && !source) { return; } 24 img.setAttribute('data-bshhero-fallback', '1'); 25 if (source) { 26 source.setAttribute('srcset', source.getAttribute('data-bshhero-original')); 27 source.removeAttribute('sizes'); 28 } 29 img.removeAttribute('srcset'); 30 img.removeAttribute('sizes'); 31 } 32 // Capture also covers loop clones. The original URL is already in src; 33 // if a native thumbnail is unavailable, retry the original once. 34 root.addEventListener('error', function (event) { originalOnError(event.target); }, true); 35 Array.prototype.forEach.call(root.querySelectorAll('img'), function (img) { 36 if (img.complete && !img.naturalWidth) { originalOnError(img); } 37 }); 38 function mark(commit) { 39 if (marked !== current) { 40 if (dots[marked]) { dots[marked].removeAttribute('aria-current'); } 41 if (dots[current]) { dots[current].setAttribute('aria-current', 'true'); } 42 root.dataset.active = String(current); 43 marked = current; 44 } 45 // Moving dots must not start every video crossed during a swipe. 46 if (commit && media) { media.setCurrent(current); } 47 } 48 function stop() { w.clearTimeout(timer); timer = 0; } 49 function canAdvance() { return slides.length > 1 && (!desktop.matches || loop); } 50 function arm() { 51 stop(); 52 if (!canAdvance() || !delay || scrolling || reduced.matches || (desktop.matches && hover) || focused || touching || !visible || d.hidden || !root.isConnected) { return; } 53 timer = w.setTimeout(function () { 54 if (!loop && current === slides.length - 1) { navigate(0, true); } 55 else { move(1); } 56 }, delay); 57 } 58 function jump(index, smooth) { 59 viewport.scrollTo({left: index * step, behavior: smooth && !reduced.matches ? 'smooth' : 'instant'}); 60 } 61 function logical(physical) { 62 var index = physical - copies; 63 return loop ? ((index % slides.length) + slides.length) % slides.length 64 : Math.max(0, Math.min(slides.length - 1, index)); 65 } 66 function beginScroll() { 67 stop(); 68 if (!scrolling) { 69 scrolling = true; 70 if (media && media.setMoving) { media.setMoving(true); } 71 } 72 } 73 function settleSoon() { 74 w.clearTimeout(settle); 75 settle = w.setTimeout(finishScroll, 150); 76 } 77 function navigate(physical, smooth) { 78 if (!step) { return; } 79 wanted = logical(physical); 80 if (!canAdvance() || Math.abs(viewport.scrollLeft - physical * step) < 1) { 81 // A new click can cancel an animation before its first frame. 82 if (scrolling && canAdvance()) { jump(physical, false); } 83 w.clearTimeout(settle); settle = 0; 84 if (scrollFrame) { w.cancelAnimationFrame(scrollFrame); scrollFrame = 0; } 85 current = wanted; 86 scrolling = false; wanted = null; 87 mark(true); 88 if (media && media.setMoving) { media.setMoving(false); } 89 arm(); return; 90 } 91 alignedTo = null; 92 beginScroll(); jump(physical, smooth); settleSoon(); 93 } 94 function move(delta) { 95 if (!step) { return; } 96 var target = (wanted === null ? current : wanted) + delta; 97 if (loop) { navigate(copies + target, true); } 98 else { navigate(Math.max(0, Math.min(slides.length - 1, target)), true); } 99 } 100 function clone(slide) { 101 var copy = slide.cloneNode(true); 102 copy.dataset.heroClone = '1'; 103 copy.setAttribute('aria-hidden', 'true'); 104 Array.prototype.forEach.call(copy.querySelectorAll('[id]'), function (el) { el.removeAttribute('id'); }); 105 Array.prototype.forEach.call(copy.querySelectorAll('a,button,[tabindex]'), function (el) { el.setAttribute('tabindex', '-1'); }); 106 Array.prototype.forEach.call(copy.querySelectorAll('img'), function (img) { img.removeAttribute('fetchpriority'); img.loading = 'lazy'; }); 107 copy.removeAttribute('data-video-state'); 108 Array.prototype.forEach.call(copy.querySelectorAll('video'), function (video) { video.removeAttribute('src'); video.preload = 'none'; }); 109 return copy; 110 } 111 function measure() { 112 frame = 0; 113 if (!root.isConnected) { stop(); return; } 114 // ResizeObserver delivers the already-computed slide width. Reading 115 // offsetLeft here forced a document-wide layout during startup. 116 if (w.ResizeObserver) { 117 if (!slideWidth) { return; } 118 step = slideWidth + (desktop.matches ? 24 : (phone.matches ? 10 : 22)); 119 } else { 120 var children = viewport.children; 121 step = children.length > 1 ? children[1].offsetLeft - children[0].offsetLeft : 0; 122 } 123 // Initial mobile position is already zero. Even scrollTo(0) 124 // can force layout, so only align after a real position change. 125 if (alignAfterLayout || current !== 0 || loop) { 126 alignedTo = (current + copies) * step; 127 jump(current + copies, false); 128 alignAfterLayout = false;
129 } 130 scrolling = false; wanted = null; 131 mark(true); 132 if (media && media.setMoving) { media.setMoving(false); } 133 arm(); 134 } 135 function layout() { 136 stop(); 137 w.clearTimeout(settle); 138 if (scrollFrame) { w.cancelAnimationFrame(scrollFrame); scrollFrame = 0; } 139 var nextLoop = desktop.matches && slides.length > 3; 140 if (nextLoop !== loop) { 141 alignAfterLayout = true; 142 Array.prototype.forEach.call(viewport.querySelectorAll('[data-hero-clone]'), function (el) { el.remove(); }); 143 loop = nextLoop; 144 copies = loop ? 3 : 0; 145 if (loop) { 146 var before = d.createDocumentFragment(), after = d.createDocumentFragment(); 147 slides.slice(-copies).forEach(function (slide) { before.appendChild(clone(slide)); }); 148 slides.slice(0, copies).forEach(function (slide) { after.appendChild(clone(slide)); }); 149 viewport.insertBefore(before, viewport.firstChild); viewport.appendChild(after); 150 } 151 } 152 root.dataset.loop = String(loop); 153 root.dataset.autoplay = String(canAdvance() && delay > 0 && !reduced.matches); 154 if (frame) { w.cancelAnimationFrame(frame); frame = 0; } 155 if (sizeObserver) { 156 // Clones and breakpoint styles must be laid out before scrollTo. 157 // A fresh observation also delivers when the slide width stayed 158 // the same, so reduced-motion and loop changes still settle. 159 layoutNeedsMeasure = true; 160 sizeObserver.unobserve(slides[0]); 161 sizeObserver.observe(slides[0]); 162 } else { 163 frame = w.requestAnimationFrame(measure); 164 } 165 } 166 function syncScroll() { 167 scrollFrame = 0; 168 if (!step || !root.isConnected) { return; } 169 // Native scroll events arrive before rAF. Reuse their position; 170 // reading it here after another addon's writes forced layout again. 171 current = logical(Math.round(Math.max(0, observedScrollLeft) / step)); 172 mark(false); 173 } 174 function finishScroll() { 175 w.clearTimeout(settle); settle = 0; 176 if (!step || !scrolling || touching) { return; } 177 if (scrollFrame) { w.cancelAnimationFrame(scrollFrame); scrollFrame = 0; } 178 var physical = Math.round(Math.max(0, viewport.scrollLeft) / step); 179 current = logical(physical); 180 if (loop && (physical < copies || physical >= copies + slides.length)) { 181 alignedTo = (copies + current) * step; 182 jump(copies + current, false); 183 } 184 wanted = null; scrolling = false; 185 mark(true); 186 if (media && media.setMoving) { media.setMoving(false); } 187 arm(); 188 } 189 viewport.addEventListener('scroll', function () { 190 // Read once before beginScroll/media/dot updates can change styles. 191 observedScrollLeft = viewport.scrollLeft; 192 if (alignedTo !== null && Math.abs(observedScrollLeft - alignedTo) < 1) { 193 alignedTo = null; return; 194 } 195 alignedTo = null; 196 beginScroll(); 197 if (!scrollFrame) { scrollFrame = w.requestAnimationFrame(syncScroll); } 198 settleSoon(); 199 }, {passive: true}); 200 // Native scrollend includes momentum and snap completion. The timer 201 // remains a fallback for older browsers and never delays the paginator. 202 viewport.addEventListener('scrollend', finishScroll, {passive: true}); 203 viewport.addEventListener('keydown', function (event) { 204 if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { 205 event.preventDefault(); move(event.key === 'ArrowRight' ? 1 : -1); 206 } else if (event.key === 'Home' || event.key === 'End') { 207 event.preventDefault(); navigate(copies + (event.key === 'Home' ? 0 : slides.length - 1), true); 208 } 209 }); 210 root.querySelector('.bshhero__prev').addEventListener('click', function () { move(-1); }); 211 root.querySelector('.bshhero__next').addEventListener('click', function () { move(1); });
212 dots.forEach(function (dot, i) { dot.addEventListener('click', function () { navigate(copies + i, true); }); }); 213 root.addEventListener('mouseenter', function () { if (desktop.matches) { hover = true; stop(); } }); 214 root.addEventListener('mouseleave', function () { hover = false; arm(); }); 215 root.addEventListener('focusin', function (event) { focused = desktop.matches || event.target.matches(':focus-visible'); if (focused) { stop(); } }); 216 root.addEventListener('focusout', function (event) { focused = root.contains(event.relatedTarget); arm(); }); 217 // Do not move the banner under a finger held on the carousel. 218 function hold(event) { 219 touching = true; 220 if (viewport.contains(event.target) && !event.target.closest('button')) { wanted = null; } 221 stop(); 222 } 223 function release() { if (!touching) { return; } touching = false; if (scrolling) { settleSoon(); } else { arm(); } } 224 root.addEventListener(w.PointerEvent ? 'pointerdown' : 'touchstart', hold, {passive: true}); 225 w.addEventListener(w.PointerEvent ? 'pointerup' : 'touchend', release, {passive: true}); 226 w.addEventListener(w.PointerEvent ? 'pointercancel' : 'touchcancel', release, {passive: true}); 227 viewport.addEventListener('wheel', function () { wanted = null; }, {passive: true}); 228 d.addEventListener('visibilitychange', arm); 229 if (w.IntersectionObserver) { 230 new w.IntersectionObserver(function (entries) { visible = entries[0].isIntersecting; arm(); }).observe(root); 231 } 232 if (w.ResizeObserver) { 233 sizeObserver = new w.ResizeObserver(function (entries) { 234 var width = entries[0].contentRect.width; 235 if (!(width > 0) || (width === slideWidth && !layoutNeedsMeasure)) { return; } 236 slideWidth = width; 237 layoutNeedsMeasure = false; 238 if (frame) { w.cancelAnimationFrame(frame); } 239 // ResizeObserver supplies a completed layout. Align before this 240 // callback writes state, rather than defer to a dirty next rAF. 241 measure(); 242 }); 243 } 244 else { w.addEventListener('resize', layout, {passive: true}); } 245 [desktop, phone, reduced].forEach(function (query) { 246 if (query.addEventListener) { query.addEventListener('change', layout); } 247 else if (query.addListener) { query.addListener(layout); } 248 }); 249 if (w.BishofitHeroMedia) { 250 media = w.BishofitHeroMedia(root, function (index) { 251 navigate(copies + index, false); 252 }); 253 } 254 layout(); 255 } 256 function scan() { Array.prototype.forEach.call(d.querySelectorAll('[data-bshhero]'), init); } 257 if (d.readyState === 'loading') { d.addEventListener('DOMContentLoaded', scan, {once: true}); } 258 else { scan(); } 259 if (w.Tygh && w.Tygh.$ && w.Tygh.$.ceEvent) { w.Tygh.$.ceEvent('on', 'ce.commoninit', scan); } 260}(window, document));
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.