/* desfoto.de - progressive enhancement only. */ (function () { 'use strict'; /* Mobile navigation */ var toggle = document.querySelector('[data-nav-toggle]'); var nav = document.getElementById('site-nav'); if (toggle && nav) { toggle.addEventListener('click', function () { var open = nav.classList.toggle('is-open'); toggle.setAttribute('aria-expanded', open ? 'true' : 'false'); }); nav.addEventListener('click', function (event) { if (event.target.closest('a') && window.innerWidth < 832) { nav.classList.remove('is-open'); toggle.setAttribute('aria-expanded', 'false'); } }); } /* Click-to-load YouTube (youtube-nocookie) */ document.querySelectorAll('[data-video-id]').forEach(function (facade) { facade.addEventListener('click', function () { var id = facade.getAttribute('data-video-id'); var iframe = document.createElement('iframe'); iframe.setAttribute('src', 'https://www.youtube-nocookie.com/embed/' + encodeURIComponent(id) + '?autoplay=1&rel=0&modestbranding=1&playsinline=1'); iframe.setAttribute('title', facade.getAttribute('aria-label') || 'Video'); iframe.setAttribute('loading', 'eager'); iframe.setAttribute('allow', 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture'); iframe.setAttribute('allowfullscreen', ''); iframe.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin'); var shell = facade.parentElement; shell.replaceChild(iframe, facade); iframe.focus(); }); }); /* Contact form -> local mail draft, nothing is transmitted by this page */ var form = document.querySelector('[data-contact-form]'); if (form) { var status = form.querySelector('[data-contact-status]'); form.addEventListener('submit', function (event) { event.preventDefault(); var data = new FormData(form); var name = (data.get('name') || '').toString().trim(); var email = (data.get('email') || '').toString().trim(); var topic = (data.get('topic') || '').toString().trim(); var date = (data.get('date') || '').toString().trim(); var message = (data.get('message') || '').toString().trim(); if (!name || !email || !message) { if (status) { status.hidden = false; status.textContent = 'Bitte Name, E-Mail und Nachricht ausfüllen.'; status.style.color = '#95330F'; } return; } var subject = 'Anfrage über desfoto.de: ' + topic; var body = 'Name: ' + name + '\nE-Mail: ' + email + '\nBereich: ' + topic + (date ? '\nWunschtermin: ' + date : '') + '\n\n' + message + '\n'; window.location.href = 'mailto:info@dennyschulz.de?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body); if (status) { status.hidden = false; status.textContent = 'Dein Mailprogramm sollte sich mit dem fertigen Entwurf öffnen.'; status.style.color = ''; } }); } /* Motion layer: reveals, scroll progress, parallax and pointer spotlights. Everything degrades to a static page and is disabled for reduced motion. */ var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; /* Anything inside [data-stagger] reveals one item after another. */ Array.prototype.forEach.call(document.querySelectorAll('[data-stagger]'), function (group) { var step = parseFloat(group.getAttribute('data-stagger')) || 0.08; Array.prototype.forEach.call(group.children, function (child, index) { if (!child.classList.contains('reveal')) { child.classList.add('reveal'); } child.style.setProperty('--d', (index * step).toFixed(2) + 's'); }); }); var items = document.querySelectorAll('.reveal'); var revealAll = function () { Array.prototype.forEach.call(items, function (item) { item.classList.add('is-in'); }); }; if (reduce || !('IntersectionObserver' in window)) { revealAll(); } else { try { var fired = 0; var observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { fired = 1; entry.target.classList.add('is-in'); observer.unobserve(entry.target); } }); }, { rootMargin: '0px 0px -8% 0px', threshold: 0.06 }); Array.prototype.forEach.call(items, function (item) { observer.observe(item); }); /* Safety net: if the browser never reports a box that is on screen, drop the effect entirely instead of leaving content hidden. */ window.setTimeout(function () { if (fired) { return; } var onScreen = false; Array.prototype.forEach.call(items, function (item) { var box = item.getBoundingClientRect(); if (box.top < window.innerHeight && box.bottom > 0) { onScreen = true; } }); if (onScreen) { observer.disconnect(); revealAll(); } }, 1600); } catch (error) { revealAll(); } } if (reduce) { return; } var progress = document.querySelector('[data-progress]'); var parallax = Array.prototype.slice.call(document.querySelectorAll('[data-parallax]')); var ticking = false; function onScroll() { var doc = document.documentElement; var max = doc.scrollHeight - window.innerHeight; var ratio = max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0; if (progress) { progress.style.setProperty('--scroll', ratio.toFixed(4)); } if (parallax.length) { var view = window.innerHeight; parallax.forEach(function (element) { var rect = element.getBoundingClientRect(); if (rect.bottom < -240 || rect.top > view + 240) { return; } var speed = parseFloat(element.getAttribute('data-parallax')) || 0.12; var offset = (rect.top + rect.height / 2 - view / 2) * speed; element.style.setProperty('--py', offset.toFixed(1) + 'px'); }); } ticking = false; } function requestScroll() { if (!ticking) { ticking = true; window.requestAnimationFrame(onScroll); } } window.addEventListener('scroll', requestScroll, { passive: true }); window.addEventListener('resize', requestScroll); onScroll(); /* Pointer-following highlight on the media cards. */ Array.prototype.forEach.call(document.querySelectorAll('.spot'), function (card) { card.addEventListener('pointermove', function (event) { var rect = card.getBoundingClientRect(); card.style.setProperty('--mx', ((event.clientX - rect.left) / rect.width * 100).toFixed(1) + '%'); card.style.setProperty('--my', ((event.clientY - rect.top) / rect.height * 100).toFixed(1) + '%'); }); }); }());