(function () { const nativeValueSetter = Object.getOwnPropertyDescriptor( window.HTMLInputElement.prototype, 'value' ).set; function setNativeValue(el, value) { nativeValueSetter.call(el, value); el.dispatchEvent(new Event('input', { bubbles: true })); el.dispatchEvent(new Event('change', { bubbles: true })); } function buildOverlay(input) { if (input.dataset.ccOverlay) return; input.dataset.ccOverlay = 'true'; const min = parseFloat(input.min) || 1; const max = parseFloat(input.max) || 3; const step = parseFloat(input.step) || 1; const shell = document.createElement('div'); shell.className = 'cc-slider-shell'; input.parentNode.insertBefore(shell, input); shell.appendChild(input); const track = document.createElement('div'); track.className = 'cc-slider-track'; shell.appendChild(track); const handle = document.createElement('div'); handle.className = 'cc-slider-handle'; shell.appendChild(handle); function stepToPercent(value) { return ((value - min) / (max - min)) * 100; } function percentToStep(percent) { const raw = min + (percent / 100) * (max - min); const snapped = Math.round((raw - min) / step) * step + min; return Math.min(max, Math.max(min, snapped)); } function setHandlePercent(percent) { handle.style.left = percent + '%'; } function syncFromValue() { setHandlePercent(stepToPercent(parseFloat(input.value))); } syncFromValue(); let dragging = false; function trackPercentFromClientX(clientX) { const rect = track.getBoundingClientRect(); const percent = ((clientX - rect.left) / rect.width) * 100; return Math.min(100, Math.max(0, percent)); } function animateHandleTo(targetPercent, onDone) { const startPercent = parseFloat(handle.style.left) || 0; const duration = 150; const startTime = performance.now(); function step(now) { const t = Math.min((now - startTime) / duration, 1); const eased = 1 - Math.pow(1 - t, 3); setHandlePercent(startPercent + (targetPercent - startPercent) * eased); if (t < 1) { requestAnimationFrame(step); } else if (onDone) { onDone(); } } requestAnimationFrame(step); } function finishDrag(clientX) { const rawPercent = trackPercentFromClientX(clientX); const snappedValue = percentToStep(rawPercent); const snappedPercent = stepToPercent(snappedValue); animateHandleTo(snappedPercent, function () { if (parseFloat(input.value) !== snappedValue) { setNativeValue(input, snappedValue); } }); } function onPointerDown(e) { dragging = true; handle.setPointerCapture(e.pointerId); setHandlePercent(trackPercentFromClientX(e.clientX)); } function onPointerMove(e) { if (!dragging) return; setHandlePercent(trackPercentFromClientX(e.clientX)); } function onPointerUp(e) { if (!dragging) return; dragging = false; finishDrag(e.clientX); } handle.addEventListener('pointerdown', onPointerDown); handle.addEventListener('pointermove', onPointerMove); handle.addEventListener('pointerup', onPointerUp); track.addEventListener('pointerdown', function (e) { if (e.target === handle) return; dragging = true; track.setPointerCapture(e.pointerId); setHandlePercent(trackPercentFromClientX(e.clientX)); }); track.addEventListener('pointermove', onPointerMove); track.addEventListener('pointerup', onPointerUp); input.addEventListener('change', function () { if (!dragging) syncFromValue(); }); } function setupSliders() { document.querySelectorAll( '.yop-poll-container input[type="range"]:not([data-cc-overlay])' ).forEach(buildOverlay); } setupSliders(); const observer = new MutationObserver(function () { setupSliders(); }); observer.observe(document.body, { childList: true, subtree: true }); })();