(() => { const BOUND_FLAG = "__nodedcWheelOverrideBound"; const GLOBAL_SLIDER_BOUND_FLAG = "__nodedcGlobalSliderWheelOverrideBound"; const SYNTHETIC_WHEEL_FLAG = "__nodedcSyntheticWheel"; const SLIDER_WHEEL_MULTIPLIER = 3; const SLIDER_BASE_COOLDOWN = 220; const MAIL_BASE_COOLDOWN = 280; const MIN_SCROLL_SPEED = 0.2; const MAX_SCROLL_SPEED = 2; const DEFAULT_SCROLL_SPEED = 0.55; const ADAPTIVE_VIDEO_BOUND_FLAG = "__nodedcAdaptiveVideoBound"; const MEDIA_HANDLE_BOUND_FLAG = "__nodedcMediaHandleBound"; const DOCK_LINK_BOUND_FLAG = "__nodedcDockLinkBound"; const ORDERED_SLIDER_BOUND_FLAG = "__nodedcOrderedSliderBound"; const IMAGE_EXTENSIONS = new Set(["avif", "gif", "jpeg", "jpg", "png", "svg", "webp"]); const VIDEO_EXTENSIONS = new Set(["m4v", "mov", "mp4", "webm"]); function isDesktopPointer() { return window.matchMedia?.("(pointer: fine)").matches ?? true; } function shouldIgnoreWheel(event) { return event.ctrlKey || !isDesktopPointer(); } function createSyntheticWheel(event, deltaX, deltaY) { if (typeof WheelEvent !== "function") return null; const syntheticEvent = new WheelEvent("wheel", { bubbles: true, cancelable: true, view: window, deltaX, deltaY, deltaZ: event.deltaZ || 0, deltaMode: event.deltaMode, altKey: event.altKey, metaKey: event.metaKey, shiftKey: event.shiftKey, }); Object.defineProperty(syntheticEvent, SYNTHETIC_WHEEL_FLAG, { value: true }); return syntheticEvent; } function createSyntheticHorizontalWheel(event) { return createSyntheticWheel(event, event.deltaY * SLIDER_WHEEL_MULTIPLIER, 0); } function getPrimaryWheelDelta(event) { return Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY; } function clamp(value, min, max) { return Math.min(max, Math.max(min, value)); } function getScrollSpeed(surface) { const rawValue = surface?.dataset?.scrollSpeed ?? surface?.closest?.("[data-scroll-speed]")?.dataset?.scrollSpeed; const value = Number.parseFloat(rawValue); if (!Number.isFinite(value)) return DEFAULT_SCROLL_SPEED; return clamp(value, MIN_SCROLL_SPEED, MAX_SCROLL_SPEED); } function getStepCooldown(baseCooldown, surface) { return Math.round(baseCooldown / getScrollSpeed(surface)); } function dispatchPointerStep(item) { if (typeof PointerEvent !== "function") return false; const rect = item.getBoundingClientRect(); const eventInit = { bubbles: true, cancelable: true, clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2, pointerId: 1, pointerType: "mouse", isPrimary: true, }; item.dispatchEvent(new PointerEvent("pointerdown", eventInit)); document.dispatchEvent(new PointerEvent("pointerup", eventInit)); return true; } function stepMailList(surface, event) { const now = Date.now(); const lastStep = Number(surface.__nodedcMailWheelLastStep || 0); if (now - lastStep < getStepCooldown(MAIL_BASE_COOLDOWN, surface)) return; const items = Array.from(surface.children); if (!items.length) return; const currentIndex = Math.max( 0, items.findIndex((item) => item.classList.contains("current")), ); const direction = event.deltaY > 0 ? 1 : -1; const nextIndex = Math.max(0, Math.min(items.length - 1, currentIndex + direction)); if (nextIndex === currentIndex) return; if (dispatchPointerStep(items[nextIndex])) { surface.__nodedcMailWheelLastStep = now; } } function stepSlider(surface, event) { const delta = getPrimaryWheelDelta(event); if (Math.abs(delta) < 1) return; const now = Date.now(); const lastStep = Number(surface.__nodedcSliderWheelLastStep || 0); if (now - lastStep < getStepCooldown(SLIDER_BASE_COOLDOWN, surface)) return; const slider = surface.__nodedcSlider; if (slider && typeof slider.goToNext === "function" && typeof slider.goToPrev === "function") { if (delta > 0) slider.goToNext(); else slider.goToPrev(); surface.__nodedcSliderWheelLastStep = now; return; } if (Math.abs(event.deltaY) >= Math.abs(event.deltaX)) { const syntheticEvent = createSyntheticHorizontalWheel(event); if (syntheticEvent) { surface.dispatchEvent(syntheticEvent); surface.__nodedcSliderWheelLastStep = now; } } } function findSliderAtPoint(event) { if (!Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) return null; return Array.from(document.querySelectorAll('[data-module="slider"]')).find((surface) => { const rect = surface.getBoundingClientRect(); const left = Math.max(rect.left, 0); const right = Math.min(rect.right, window.innerWidth); return ( event.clientX >= left && event.clientX <= right && event.clientY >= rect.top && event.clientY <= rect.bottom ); }); } function bindGlobalSliderWheel() { if (document[GLOBAL_SLIDER_BOUND_FLAG]) return; document[GLOBAL_SLIDER_BOUND_FLAG] = true; document.addEventListener( "wheel", (event) => { if (shouldIgnoreWheel(event) || event[SYNTHETIC_WHEEL_FLAG]) return; if (event.target?.closest?.('[data-module="mail-list"]')) return; const slider = findSliderAtPoint(event); if (!slider) return; event.preventDefault(); event.stopImmediatePropagation(); stepSlider(slider, event); }, { capture: true, passive: false }, ); } function bindSliderWheel(surface) { if (!surface || surface[BOUND_FLAG]) return; surface[BOUND_FLAG] = true; surface.addEventListener( "wheel", (event) => { if (shouldIgnoreWheel(event) || event[SYNTHETIC_WHEEL_FLAG]) return; event.preventDefault(); event.stopImmediatePropagation(); stepSlider(surface, event); }, { capture: true, passive: false }, ); surface.addEventListener( "wheel", (event) => { if (shouldIgnoreWheel(event)) return; event.stopPropagation(); }, { passive: false }, ); } function bindMailListWheel(surface) { if (!surface || surface[BOUND_FLAG]) return; surface[BOUND_FLAG] = true; surface.addEventListener( "wheel", (event) => { if (shouldIgnoreWheel(event)) return; event.preventDefault(); if (Math.abs(event.deltaY) >= Math.abs(event.deltaX)) { stepMailList(surface, event); } }, { capture: true, passive: false }, ); surface.addEventListener( "wheel", (event) => { if (shouldIgnoreWheel(event)) return; event.stopPropagation(); }, { passive: false }, ); } function bindInteractiveScroll() { document.querySelectorAll('[data-module="mail-list"]').forEach(bindMailListWheel); } function bindOrderedSlider(surface) { if (!surface || surface[ORDERED_SLIDER_BOUND_FLAG]) return; const slider = surface.__nodedcSlider; if (!slider) return; surface[ORDERED_SLIDER_BOUND_FLAG] = true; const items = Array.from(surface.children); const startOffset = items.length > 1 ? -1 : 0; slider.config.infinite = true; slider.target = startOffset; slider.current = startOffset; slider.speed = 0; items.forEach((item) => { item.classList.remove("current"); }); slider.resize?.(); slider.update?.(); } function bindOrderedSliders() { document.querySelectorAll('[data-module="slider"][data-admin-order="true"]').forEach(bindOrderedSlider); } function bindDockLink(link) { if (!link || link[DOCK_LINK_BOUND_FLAG]) return; link[DOCK_LINK_BOUND_FLAG] = true; link.addEventListener( "click", (event) => { const href = String(link.getAttribute("href") || "").trim(); const targetId = String(link.dataset.target || "").trim(); const hasAppTarget = Boolean(targetId && document.getElementById(targetId)); const isInertHref = !href || href === "#" || href === "#hero" || href.toLowerCase().startsWith("javascript:"); if (!hasAppTarget && !isInertHref) return; event.preventDefault(); window.requestAnimationFrame(() => link.blur()); if (!hasAppTarget) event.stopPropagation(); }, { capture: true }, ); } function bindDockLinks() { document.querySelectorAll('[data-module="dock"] a[data-app]').forEach(bindDockLink); } function mediaExtension(src) { const cleanSrc = String(src || "").split(/[?#]/)[0]; const match = /\.([a-z0-9]+)$/i.exec(cleanSrc); return match ? match[1].toLowerCase() : ""; } function createMediaElement(src) { const extension = mediaExtension(src); if (IMAGE_EXTENSIONS.has(extension)) { const image = document.createElement("img"); image.className = "video-el"; image.src = src; image.alt = ""; image.loading = "lazy"; image.decoding = "async"; return image; } if (!VIDEO_EXTENSIONS.has(extension)) return null; const video = document.createElement("video"); video.className = "video-el"; video.dataset.module = "video-handle"; video.src = src; video.muted = true; video.loop = true; video.playsInline = true; video.preload = "none"; video.setAttribute("playsinline", "true"); return video; } function hydrateMediaHandle(handle) { if (!handle || handle[MEDIA_HANDLE_BOUND_FLAG]) return; const src = handle.dataset.mediaSrc; const media = createMediaElement(src); if (!media) return; handle[MEDIA_HANDLE_BOUND_FLAG] = true; handle.textContent = ""; handle.append(media); } function hydrateMediaHandles() { document.querySelectorAll("[data-media-handle]").forEach(hydrateMediaHandle); } function applyAdaptiveVideoAspect(video) { const surface = video.closest("[data-adaptive-video]"); if (!surface || !video.videoWidth || !video.videoHeight) return; const windowEl = video.closest("[data-adaptive-video-window]"); const sourceAspect = video.videoWidth / video.videoHeight; const targets = windowEl ? [surface, windowEl] : [surface]; targets.forEach((target) => { target.style.setProperty("--nodedc-video-source-aspect", sourceAspect.toFixed(6)); }); } function bindAdaptiveVideo(video) { if (!video || video[ADAPTIVE_VIDEO_BOUND_FLAG]) return; video[ADAPTIVE_VIDEO_BOUND_FLAG] = true; video.addEventListener("loadedmetadata", () => applyAdaptiveVideoAspect(video)); applyAdaptiveVideoAspect(video); } function bindAdaptiveVideos() { document.querySelectorAll("[data-adaptive-video] video").forEach(bindAdaptiveVideo); } hydrateMediaHandles(); if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", () => { hydrateMediaHandles(); bindInteractiveScroll(); bindOrderedSliders(); bindDockLinks(); bindAdaptiveVideos(); }, { once: true }, ); } else { hydrateMediaHandles(); bindInteractiveScroll(); bindOrderedSliders(); bindDockLinks(); bindAdaptiveVideos(); } window.addEventListener("load", () => { hydrateMediaHandles(); bindInteractiveScroll(); bindOrderedSliders(); bindDockLinks(); bindAdaptiveVideos(); window.setTimeout(() => { hydrateMediaHandles(); bindInteractiveScroll(); bindOrderedSliders(); bindDockLinks(); bindAdaptiveVideos(); }, 500); }); })();