1(function () { 2 if (window.__tskScrollGuard) return; 3 window.__tskScrollGuard = true; 4 5 var NEAR_BOTTOM_PX = 120; // within this of the bottom => reader counts as "at the bottom" 6 var ANCHOR_SLACK_PX = 40; // element within this of content-bottom => it is a bottom anchor 7 8 var stick = new WeakMap(); // scroll container -> boolean "stuck to bottom" 9 var lastTop = new WeakMap(); // scroll container -> last observed scrollTop 10 11 function nearestScrollable(el) { 12 var n = el; 13 while (n && n !== document.body && n !== document.documentElement) { 14 var s = window.getComputedStyle(n); 15 if (/(auto|scroll)/.test(s.overflowY) && n.scrollHeight > n.clientHeight + 1) return n; 16 n = n.parentElement; 17 } 18 return null; 19 } 20 21 function isNearBottom(sc) { 22 return sc.scrollHeight - sc.scrollTop - sc.clientHeight <= NEAR_BOTTOM_PX; 23 } 24 25 // Track sticky state from scroll direction. Capture phase catches scroll on 26 // inner containers (scroll does not bubble). The guard only scrolls down, so 27 // an upward move is the reader escaping; returning near the bottom re-sticks. 28 document.addEventListener( 29 'scroll', 30 function (e) { 31 var sc = e.target; 32 if (!(sc instanceof Element)) return; 33 var prev = lastTop.has(sc) ? lastTop.get(sc) : sc.scrollTop; 34 var now = sc.scrollTop; 35 lastTop.set(sc, now); 36 if (now < prev - 2) stick.set(sc, false); 37 else if (isNearBottom(sc)) stick.set(sc, true); 38 }, 39 true, 40 ); 41 42 var orig = Element.prototype.scrollIntoView; 43 Element.prototype.scrollIntoView = function () { 44 try { 45 var sc = nearestScrollable(this); 46 if (sc) { 47 var er = this.getBoundingClientRect(); 48 var cr = sc.getBoundingClientRect(); 49 var elBottomInContent = er.bottom - cr.top + sc.scrollTop; 50 var targetsBottom = sc.scrollHeight - elBottomInContent < ANCHOR_SLACK_PX; 51 if (targetsBottom) { 52 // Default to stuck until the reader proves otherwise by scrolling up. 53 var stuck = stick.has(sc) ? stick.get(sc) : true; 54 if (!stuck) return; // reader scrolled up: keep their place 55 // At the bottom: allow, but instant (no smooth) to kill per-token shake. 56 return orig.call(this, { block: 'end' }); 57 } 58 } 59 } catch (e) { 60 /* fall through to native on any measurement error */ 61 } 62 return orig.apply(this, arguments); 63 }; 64})();
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.