(() => { 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; 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() { bindGlobalSliderWheel(); document.querySelectorAll('[data-module="slider"]').forEach(bindSliderWheel); document.querySelectorAll('[data-module="mail-list"]').forEach(bindMailListWheel); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", bindInteractiveScroll, { once: true }); } else { bindInteractiveScroll(); } window.addEventListener("load", () => { bindInteractiveScroll(); window.setTimeout(bindInteractiveScroll, 500); }); })();