1/** 2 * Block: ritzau/via-ritzau-latest-news-feed â Frontend view script 3 * 4 * - Padding is calculated from the container's bounding rect so the first card 5 * aligns exactly with the section heading/text above it. 6 * - Hard stop at both ends: can't drag past the first or last card's container edge. 7 * - Momentum / inertia after release. 8 * - Native browser drag (link drag) is suppressed; clicks still work â and a click 9 * is suppressed when a drag occurred (cards are links). 10 */ 11(function () { 12 'use strict'; 13 14 document.querySelectorAll('.ritzau-block-via-ritzau-latest-news-feed__track').forEach(function (track) { 15 var block = track.closest('.ritzau-block-via-ritzau-latest-news-feed'); 16 var container = block ? block.querySelector('.ritzau-block-via-ritzau-latest-news-feed__container') : null; 17 18 var isDown = false; 19 var startX = 0; 20 var startScroll = 0; 21 var hasDragged = false; 22 var velocity = 0; // px/ms 23 var lastX = 0; 24 var lastTime = 0; 25 var rafId = null; 26 var maxScroll = 0; 27 28 // ââ Align padding with the container's edges ââââââââââââââââââââââ 29 // Runs on load and on every resize so it's always in sync. 30 function updatePadding() { 31 if (!container) { return; } 32 33 var containerRect = container.getBoundingClientRect(); 34 var containerStyle = window.getComputedStyle(container); 35 var trackWidth = track.clientWidth; // visible width, excludes scrollbar 36 37 var innerLeft = containerRect.left + parseFloat(containerStyle.paddingLeft || 0); 38 var innerRight = containerRect.right - parseFloat(containerStyle.paddingRight || 0); 39 40 var leftPad = Math.round(Math.max(16, innerLeft)); 41 var rightPad = Math.round(Math.max(16, trackWidth - innerRight)); 42 43 track.style.paddingLeft = leftPad + 'px'; 44 track.style.paddingRight = rightPad + 'px'; 45 46 // Let the browser reflow before reading scrollWidth 47 requestAnimationFrame(function () { 48 maxScroll = track.scrollWidth - track.clientWidth; 49 }); 50 } 51 52 updatePadding(); 53 window.addEventListener('resize', updatePadding); 54 55 // ââ Prevent native browser drag on links/images inside the track ââ 56 track.addEventListener('dragstart', function (e) { 57 e.preventDefault(); 58 }); 59 60 // ââ Drag start ââââââââââââââââââââââââââââââââââââââââââââââââââââ 61 track.addEventListener('mousedown', function (e) { 62 if (e.button !== 0) { return; } 63 if (rafId) { cancelAnimationFrame(rafId); rafId = null; } 64 65 isDown = true; 66 hasDragged = false; 67 velocity = 0; 68 startX = e.clientX; 69 startScroll = track.scrollLeft; 70 lastX = e.clientX; 71 lastTime = Date.now(); 72 73 track.classList.add('is-dragging'); 74 }); 75 76 // ââ Drag move âââââââââââââââââââââââââââââââââââââââââââââââââââââ 77 document.addEventListener('mousemove', function (e) { 78 if (!isDown) { return; } 79 e.preventDefault(); 80 81 var x = e.clientX; 82 var now = Date.now(); 83 var dt = now - lastTime; 84 85 if (dt > 0) { 86 velocity = (lastX - x) / dt; // px/ms, positive = dragging forward (left) 87 } 88 lastX = x; 89 lastTime = now; 90 91 if (Math.abs(x - startX) > 4) { hasDragged = true; } 92 93 // Hard stop: clamp to [0, maxScroll] 94 var desired = startScroll - (x - startX); 95 track.scrollLeft = Math.max(0, Math.min(maxScroll, desired)); 96 }); 97 98 // ââ Drag end ââââââââââââââââââââââââââââââââââââââââââââââââââââââ 99 document.addEventListener('mouseup', function () { 100 if (!isDown) { return; } 101 isDown = false;
102 track.classList.remove('is-dragging'); 103 104 if (hasDragged && Math.abs(velocity) > 0.08) { 105 applyInertia(velocity * 16); // px/ms â approx px/frame @60fps 106 } else { 107 snapToStartIfNear(); 108 } 109 }); 110 111 // ââ Inertia âââââââââââââââââââââââââââââââââââââââââââââââââââââââ 112 function applyInertia(v) { 113 var FRICTION = 0.93; 114 rafId = requestAnimationFrame(function step() { 115 var next = track.scrollLeft + v; 116 117 // Hard stop at boundaries 118 if (next <= 0) { 119 track.scrollLeft = 0; 120 rafId = null; 121 return; 122 } 123 if (next >= maxScroll) { 124 track.scrollLeft = maxScroll; 125 rafId = null; 126 return; 127 } 128 129 if (Math.abs(v) < 0.4) { 130 rafId = null; 131 snapToStartIfNear(); 132 return; 133 } 134 135 track.scrollLeft = next; 136 v *= FRICTION; 137 rafId = requestAnimationFrame(step); 138 }); 139 } 140 141 // ââ Snap to start if close to the beginning ââââââââââââââââââââââââ 142 function snapToStartIfNear() { 143 if (track.scrollLeft < 40) { 144 track.scrollTo({ left: 0, behavior: 'smooth' }); 145 } 146 } 147 148 // ââ Block link clicks when a drag occurred âââââââââââââââââââââââââ 149 track.addEventListener('click', function (e) { 150 if (hasDragged) { 151 e.preventDefault(); 152 e.stopPropagation(); 153 hasDragged = false; 154 } 155 }, true); 156 }); 157 158}());
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.