1/* ADRIAGEL Homepage Experience â Cinematic-Fassung (2026-07) 2 Ersetzt die Slider-Fassung: Hero-/Bundle-Kino-Rotation + Slider-Preloader sind 3 ausgebaut (Sektionen existieren nicht mehr). Neu: VideoDirector (max 2 parallel, 4 Play/Pause-Hysterese gegen Flackern), AV1-Formatwahl, Kachel-Hover-Warmup, 5 Farbwelt-Video-Buehne, Masken-Cleanup, Sichtbarkeits-Gating der Dauer-Animationen. 6 Erhalten aus Bestand: USP-Mobile-Autoslider, Farbwelt-Gruppen, Parallax, Count-up, 7 3D-Tilt, Sticky-Nav, Reveal-Observer, Adria-Licht-Layer-Loader. 8 Nur /* *\/-Kommentare (FroshHtmlMinify-Regel gilt zwar fuer Inline-JS, wir bleiben konsistent). */ 9 10document.querySelector('.ag-homepage') && (function () { 11 'use strict'; 12 var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; 13 var canHover = window.matchMedia('(hover: hover)').matches; 14 var isMobile = window.matchMedia('(max-width: 768px)').matches; 15 /* Save-Data / 2G respektieren: dann KEIN Video-Autoplay, Poster bleiben stehen */ 16 var conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection; 17 var dataSaver = !!(conn && (conn.saveData || /(^|-)2g$/.test(conn.effectiveType || ''))); 18 19 /* ---------- AV1 bevorzugen (10-bit, bessere Qualitaet pro MB); H.264 bleibt Fallback ---------- */ 20 var AV1_OK = document.createElement('video').canPlayType('video/mp4; codecs="av01.0.08M.10"') === 'probably'; 21 /* Shopware asset() haengt einen Versions-Query an (â¦mp4?<ts>) â Regex muss das mitnehmen. 22 Mobil: 1280er-H.264-Rendition (<base>.m.mp4, universell dekodierbar, ~1/4 der Bytes) â 23 bei 390px-Viewport und 3x-DPR ist 1280 Quellbreite verlustfrei fuers Auge. */ 24 function pickSrc(url) { 25 if (isMobile) { return url.replace(/\.mp4(\?[^#]*)?$/, '.m.mp4$1'); } 26 return AV1_OK ? url.replace(/\.mp4(\?[^#]*)?$/, '.av1.mp4$1') : url; 27 } 28 29 /* ---------- VideoDirector: hartes Budget von max 2 gleichzeitig spielenden Videos ---------- */ 30 var Director = (function () { 31 var playing = new Set(); 32 var CAP = 2; 33 function prioOf(v) { return parseInt(v.dataset.prio || '3', 10); } 34 function ensureSrc(v) { if (!v.src && v.dataset.src) { v.src = pickSrc(v.dataset.src); } } 35 function stop(v) { v.pause(); playing.delete(v); } 36 function request(v) { 37 if (reduced || dataSaver) { return false; } 38 ensureSrc(v); 39 if (playing.has(v)) { return true; } 40 if (playing.size >= CAP) { 41 var victim = Array.from(playing).sort(function (a, b) { return prioOf(b) - prioOf(a); })[0]; 42 if (victim && prioOf(victim) >= prioOf(v)) { stop(victim); } else { return false; } 43 } 44 playing.add(v); 45 v.play().catch(function () { playing.delete(v); }); 46 return true; 47 } 48 return { request: request, stop: stop, ensureSrc: ensureSrc }; 49 })(); 50 51 /* Hysterese gegen Play/Pause-Flackern: Start ab 35% Sichtbarkeit, Stop erst bei 0% */ 52 var vio = new IntersectionObserver(function (entries) { 53 entries.forEach(function (e) { 54 var v = e.target; 55 if (e.intersectionRatio >= 0.35) { 56 if (v.dataset.prio !== '3' && !v.closest('.ag-colorworlds__stage')) { Director.request(v); } 57 if (v.closest('.ag-colorworlds__stage') && v.classList.contains('is-on')) { Director.request(v); } 58 } else if (!e.isIntersecting) { 59 Director.stop(v); 60 } 61 }); 62 }, { threshold: [0, 0.35] }); 63 document.querySelectorAll('video[data-director]').forEach(function (v) { vio.observe(v); }); 64 document.querySelectorAll('video[data-eager]').forEach(function (v) { Director.request(v); }); 65 66 /* ---------- Hero: Masken-Overlay nach dem Reveal aus dem DOM nehmen ---------- */ 67 var heroMask = document.querySelector('.ag-hero__mask'); 68 if (heroMask) { 69 if (reduced) { heroMask.remove(); } 70 else { heroMask.addEventListener('animationend', function () { heroMask.remove(); }, { once: true }); } 71 } 72 73 /* ---------- Sichtbarkeits-Gate fuer Dauer-Animationen (Gradient, Scroll-Cue) ---------- */ 74 var aio = new IntersectionObserver(function (entries) { 75 entries.forEach(function (e) { e.target.classList.toggle('is-anim', e.isIntersecting); }); 76 }, { threshold: 0.05 }); 77 document.querySelectorAll('.ag-newsletter, .ag-hero').forEach(function (el) { aio.observe(el); }); 78 79 /* ---------- Kategorien-Mosaik: Hover-Video mit Warmup (fluessig ab 1. Hover) ---------- */ 80 (function () { 81 var cards = Array.from(document.querySelectorAll('.ag-category-card')); 82 if (!cards.length || reduced) { return; } 83 84 /* Touch-Geraete (kein Hover): die Kachel im mittleren Viewport-B
84and spielt 85 automatisch â das Kino kommt zum Daumen. Kein Warmup (Datenbudget: Videos 86 laden erst beim Eintritt ins Band, mobil als kleine .m.mp4-Rendition). 87 Director-Cap 2 + Hysterese des Bandes verhindern Flackern und Ueberlast. */ 88 if (!canHover) { 89 if (dataSaver) { return; } 90 cards.forEach(function (card) { 91 var v = card.querySelector('video'); 92 if (v) { v.addEventListener('playing', function () { v.classList.add('is-on'); }); } 93 }); 94 var tio = new IntersectionObserver(function (entries) { 95 entries.forEach(function (e) { 96 var v = e.target.querySelector('video'); 97 if (!v) { return; } 98 if (e.isIntersecting) { 99 Director.request(v); 100 } else { 101 Director.stop(v); 102 v.classList.remove('is-on'); 103 } 104 }); 105 }, { rootMargin: '-32% 0px -32% 0px', threshold: 0 }); 106 cards.forEach(function (card) { if (card.querySelector('video')) { tio.observe(card); } }); 107 return; 108 } 109 var activeCard = null; 110 var grid = document.querySelector('.ag-category-grid'); 111 if (grid) { 112 var gio = new IntersectionObserver(function (entries) { 113 if (!entries.some(function (e) { return e.isIntersecting; })) { return; } 114 gio.disconnect(); 115 var i = 0; 116 var warm = function () { 117 if (i >= cards.length) { return; } 118 var v = cards[i++].querySelector('video'); 119 if (v && !v.src && v.dataset.src) { v.src = pickSrc(v.dataset.src); v.preload = 'auto'; } 120 if (v) { v.addEventListener('canplaythrough', warm, { once: true }); } 121 setTimeout(warm, 4000); 122 }; 123 warm(); 124 }, { threshold: 0.1 }); 125 gio.observe(grid); 126 } 127 cards.forEach(function (card) { 128 var video = card.querySelector('video'); 129 if (!video) { return; } 130 video.addEventListener('playing', function () { 131 if (activeCard === card) { video.classList.add('is-on'); } 132 }); 133 function hideCard(c) { 134 var v = c.querySelector('video'); 135 if (!v) { return; } 136 v.classList.remove('is-on'); 137 Director.stop(v); 138 } 139 function show() { 140 if (activeCard && activeCard !== card) { hideCard(activeCard); } 141 activeCard = card; 142 Director.ensureSrc(video); 143 if (video.readyState >= 3) { 144 if (Director.request(video)) { video.classList.add('is-on'); } 145 } else { 146 Director.request(video); 147 } 148 } 149 card.addEventListener('mouseenter', show); 150 card.addEventListener('focusin', show); 151 card.addEventListener('mouseleave', function () { 152 hideCard(card); 153 if (activeCard === card) { activeCard = null; } 154 }); 155 }); 156 })(); 157 158 /* ---------- Neuheiten: cinematischer Auto-Slider (Tolga 2026-07-13) ---------- 159 Native scroll-snap-Rail (Compositor, kein Layout-Thrash) + sanftes Auto-Advance. 160 Pausiert bei Interaktion (9s), ausserhalb des Viewports, bei verstecktem Tab 161 und unter prefers-reduced-motion. Dots nur mobil (CSS), Tap = direkt hin. */ 162 (function () { 163 var rail = document.querySelector('.ag-neuheiten__rail[data-ag-autoslider]'); 164 if (!rail) { return; } 165 var cards = Array.from(rail.querySelectorAll('.ag-neuheiten__card')); 166 if (cards.length < 2) { return; } 167 var dotsWrap = document.querySelector('.ag-neuheiten__dots'); 168 var inView = false; 169 var holdUntil = 0; 170 171 /* live abfragen statt Load-Snapshot: Tablet-Rotation um die 768px-Grenze */ 172 function alignCenter() { return window.matchMedia('(max-width: 768px)').matches; } 173 function targetLeft(card) { 174 var left = alignCenter() 175 ? card.offsetLeft - (rail.clientWidth - card.offsetWidth) / 2 176 : card.offsetLeft - rail.offsetLeft; 177 return Math.max(0, Math.min(left, rail.scrollWidth - rail.clientWidth)); 178 } 179 function nearestIndex() { 180 var best = 0; 181 var bestDist = Infinity; 182 cards.forEach(function (c, i) { 183 var d = Math.abs(targetLeft(c) - rail.scrollLeft); 184 if (d < bestDist) { bestDist = d; best = i; } 185 }); 186 return best; 187 } 188 function goTo(i) { 189 rail.scrollTo({ left: targetLeft(cards[i]), behavior: 'smooth' }); 190 markDot(i); 191 } 192 193 /* Dots (Anker kommt aus Twig, Buttons baut JS) */ 194 var dots = []; 195 if (dotsWrap) { 196 cards.forEach(function (c, i) { 197 var b = document.createElement('button'); 198 b.type = 'button'; 199 b.setAttribute('aria-label', String(i + 1) + ' / ' + cards.length); 200 b.addEventListener('click', function () { holdUntil = Date.now() + 9000; goTo(i); });
201 dotsWrap.appendChild(b); 202 dots.push(b); 203 }); 204 } 205 function markDot(i) { 206 dots.forEach(function (d, k) { d.classList.toggle('is-active', k === i); }); 207 } 208 markDot(0); 209 var scrollSync = null; 210 rail.addEventListener('scroll', function () { 211 if (scrollSync) { return; } 212 scrollSync = requestAnimationFrame(function () { scrollSync = null; markDot(nearestIndex()); }); 213 }, { passive: true }); 214 215 /* Interaktion pausiert das Auto-Advance */ 216 ['pointerdown', 'wheel', 'touchstart'].forEach(function (ev) { 217 rail.addEventListener(ev, function () { holdUntil = Date.now() + 9000; }, { passive: true }); 218 }); 219 rail.addEventListener('pointerenter', function () { holdUntil = Date.now() + 9000; }); 220 221 var sio = new IntersectionObserver(function (entries) { 222 inView = entries.some(function (e) { return e.isIntersecting; }); 223 }, { threshold: 0.25 }); 224 sio.observe(rail); 225 226 if (!reduced) { 227 /* Tolga 2026-07-13: alle 3 Sekunden nach links weiter */ 228 setInterval(function () { 229 if (!inView || document.hidden || Date.now() < holdUntil) { return; } 230 var atEnd = rail.scrollLeft + rail.clientWidth >= rail.scrollWidth - 8; 231 var i = nearestIndex(); 232 goTo(atEnd || i >= cards.length - 1 ? 0 : i + 1); 233 }, 3000); 234 } 235 })(); 236 237 /* ---------- USP-Bar: Mobile-Autoslider (Bestand) ---------- */ 238 (function () { 239 var mq = window.matchMedia('(max-width: 768px)'); 240 var timer = null; 241 function start() { 242 var items = document.querySelectorAll('.ag-usp__item'); 243 if (!items.length) { return; } 244 if (!mq.matches) { return stopAll(items); } 245 items.forEach(function (el, i) { el.classList.toggle('is-active', i === 0); }); 246 var idx = 0; 247 timer = setInterval(function () { 248 items[idx].classList.remove('is-active'); 249 idx = (idx + 1) % items.length; 250 items[idx].classList.add('is-active'); 251 }, 3000); 252 } 253 function stopAll(items) { 254 clearInterval(timer); 255 timer = null; 256 if (items) { items.forEach(function (el) { el.classList.remove('is-active'); }); } 257 } 258 mq.addEventListener('change', function () { 259 stopAll(document.querySelectorAll('.ag-usp__item')); 260 start(); 261 }); 262 start(); 263 })(); 264 265 /* ---------- Farbwelten: Buttons schalten Buehnen-Video + Caption + Produktgruppe ---------- */ 266 (function () { 267 var section = document.querySelector('.ag-colorworlds'); 268 if (!section) { return; } 269 var buttons = section.querySelectorAll('.ag-color-btn'); 270 var groups = section.querySelectorAll('.ag-colorworlds__group'); 271 var stage = section.querySelector('.ag-colorworlds__stage'); 272 var caption = document.getElementById('cwCaption'); 273 buttons.forEach(function (btn) { 274 btn.addEventListener('click', function () { 275 var color = btn.dataset.color; 276 buttons.forEach(function (b) { b.classList.remove('is-active'); }); 277 btn.classList.add('is-active'); 278 section.setAttribute('data-color', color); 279 groups.forEach(function (g) { 280 if (g.dataset.color === color) { 281 g.style.display = 'grid'; 282 g.style.animation = 'ag-fade-in 0.5s ease forwards'; 283 } else { 284 g.style.display = 'none'; 285 } 286 }); 287 if (stage) { 288 stage.querySelectorAll('video').forEach(function (v) { 289 var on = v.dataset.world === color; 290 v.classList.toggle('is-on', on); 291 if (on) { Director.request(v); } else { Director.stop(v); } 292 }); 293 } 294 if (caption) { caption.textContent = btn.textContent.trim(); } 295 }); 296 }); 297 var style = document.createElement('style'); 298 style.textContent = '@keyframes ag-fade-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }'; 299 document.head.appendChild(style); 300 })(); 301 302 /* ---------- Parallax (Bestand, .ag-parallax + data-speed) ---------- */ 303 (function () { 304 var els = document.querySelectorAll('.ag-parallax'); 305 if (!els.length || reduced) { return; } 306 var ticking = false;
307 function update() { 308 els.forEach(function (el) { 309 var speed = parseFloat(el.dataset.speed) || 0.3; 310 var r = el.getBoundingClientRect(); 311 if (r.top < window.innerHeight && r.bottom > 0) { 312 el.style.transform = 'translateY(' + (r.top - window.innerHeight / 2) * speed + 'px)'; 313 } 314 }); 315 ticking = false; 316 } 317 window.addEventListener('scroll', function () { 318 if (!ticking) { requestAnimationFrame(update); ticking = true; } 319 }, { passive: true }); 320 })(); 321 322 /* ---------- Count-up (Bestand) ---------- */ 323 (function () { 324 var els = document.querySelectorAll('[data-count]'); 325 if (!els.length) { return; } 326 var cio = new IntersectionObserver(function (entries) { 327 entries.forEach(function (e) { 328 if (!e.isIntersecting) { return; } 329 var el = e.target; 330 cio.unobserve(el); 331 var target = parseInt(el.dataset.count, 10); 332 var suffix = el.dataset.suffix || ''; 333 if (reduced) { el.textContent = target + suffix; return; } 334 var t0 = performance.now(); 335 requestAnimationFrame(function tick(now) { 336 var p = Math.min((now - t0) / 2000, 1); 337 el.textContent = Math.round(target * (1 - Math.pow(1 - p, 4))) + suffix; 338 if (p < 1) { requestAnimationFrame(tick); } 339 }); 340 }); 341 }, { threshold: 0.5 }); 342 els.forEach(function (el) { cio.observe(el); }); 343 })(); 344 345 /* ---------- 3D-Tilt auf Produktkarten (Bestand, nur pointer:fine) ---------- */ 346 (function () { 347 var cards = document.querySelectorAll('.ag-neuheiten__card, .ag-product-card'); 348 if (!cards.length || window.matchMedia('(pointer: coarse)').matches || reduced) { return; } 349 cards.forEach(function (card) { 350 card.addEventListener('mousemove', function (ev) { 351 var r = card.getBoundingClientRect(); 352 var x = (ev.clientX - r.left) / r.width - 0.5; 353 var y = (ev.clientY - r.top) / r.height - 0.5; 354 card.style.transform = 'perspective(800px) rotateY(' + 8 * x + 'deg) rotateX(' + (-8 * y) + 'deg) translateY(-5px)'; 355 }); 356 card.addEventListener('mouseleave', function () { 357 card.style.transform = ''; 358 card.style.transition = 'transform 0.5s cubic-bezier(0.4, 0, 0.2, 1)'; 359 setTimeout(function () { card.style.transition = ''; }, 500); 360 }); 361 card.addEventListener('mouseenter', function () { card.style.transition = 'none'; }); 362 }); 363 })(); 364 365 /* ---------- Sticky-Nav ab 992px (Bestand) ---------- */ 366 (function () { 367 if (window.innerWidth < 992) { return; } 368 var menu = document.querySelector('.main-navigation-menu-list'); 369 if (!menu) { return; } 370 var links = Array.from(menu.querySelectorAll(':scope > .nav-item > .nav-link')); 371 if (!links.length) { return; } 372 var nav = document.createElement('div'); 373 nav.className = 'ag-sticky-nav'; 374 var html = '<div style="display:flex;justify-content:center;align-items:center;gap:clamp(0.8rem,2vw,2rem);padding:0.65rem 2rem;flex-wrap:nowrap;font-family:Jost,system-ui,sans-serif">'; 375 links.forEach(function (link) { 376 var text = (link.textContent || '').trim(); 377 if (!text) { return; } 378 var isAktion = text.toLowerCase().indexOf('finalaktion') !== -1 || text.toLowerCase().indexOf('aktionsbundle') !== -1; 379 var color = isAktion ? '#b5201e' : 'rgba(255,255,255,0.85)'; 380 var hover = isAktion ? '#8e1518' : '#C9A26D'; 381 html += '<a href="' + link.getAttribute('href') + '" style="color:' + color + ';text-decoration:none;font-size:1.18rem;font-weight:' + (isAktion ? '700' : '500') + ';letter-spacing:0.08em;padding:0.3rem 0;transition:all 0.25s ease;white-space:nowrap;border-bottom:1px solid transparent;text-transform:uppercase" onmouseenter="this.style.color=\'' + hover + '\';this.style.borderBottomColor=\'' + hover + '\'" onmouseleave="this.style.color=\'' + color + '\';this.style.borderBottomColor=\'transparent\'">
381' + text + '</a>'; 382 }); 383 html += '</div>'; 384 nav.innerHTML = html; 385 document.body.appendChild(nav); 386 var ticking = false; 387 var visible = false; 388 window.addEventListener('scroll', function () { 389 if (ticking) { return; } 390 requestAnimationFrame(function () { 391 var show = window.scrollY > 300; 392 if (show !== visible) { nav.classList.toggle('is-visible', show); visible = show; } 393 ticking = false; 394 }); 395 ticking = true; 396 }, { passive: true }); 397 })(); 398 399 /* ---------- Reveal-Observer (Bestand: ag-reveal -> ag-revealed) ---------- */ 400 (function () { 401 var els = document.querySelectorAll('.ag-reveal, .ag-reveal--left, .ag-reveal--right, .ag-reveal--scale'); 402 if (!els.length) { return; } 403 var rio = new IntersectionObserver(function (entries) { 404 entries.forEach(function (e) { 405 if (e.isIntersecting) { 406 e.target.classList.add('ag-revealed'); 407 rio.unobserve(e.target); 408 } 409 }); 410 }, { threshold: 0.12, rootMargin: '0px 0px 120px 0px' }); 411 els.forEach(function (el) { rio.observe(el); }); 412 })(); 413})(); 414 415/* Adria-Licht-Layer-Loader (Bestand, Phase 3): laedt den WebGL-Kaustik-Shader nur, 416 wenn ALLE Guards bestehen. Mobile, reduced-motion, Save-Data, schwache 417 Hardware und fehlendes WebGL2 bekommen exakt die heutige Seite. */ 418document.querySelector('.ag-homepage') && (function () { 419 if (window.innerWidth < 992) { return; } 420 if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { return; } 421 if (window.matchMedia('(pointer: coarse)').matches) { return; } 422 var c = navigator.connection; 423 if (c && (c.saveData || /2g/.test(c.effectiveType || ''))) { return; } 424 if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4) { return; } 425 if (navigator.deviceMemory && navigator.deviceMemory < 4) { return; } 426 try { var t = document.createElement('canvas'); if (!t.getContext('webgl2')) { return; } } catch (e) { return; } 427 function load() { 428 var s = document.createElement('script'); 429 /* Cache-Buster: erbt den Versions-Stempel des Haupt-Scripts */ 430 var me = document.querySelector('script[src*="adriagel-homepage-experience"]'); 431 var v = me && me.src.indexOf('?') > -1 ? me.src.split('?')[1] : '1'; 432 s.src = '/bundles/adriagelhomepage/js/adriagel-adria-layer.js?' + v; 433 s.defer = true; 434 document.head.appendChild(s); 435 } 436 'requestIdleCallback' in window ? requestIdleCallback(load, { timeout: 4000 }) : setTimeout(load, 3000); 437})();
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.