Touch Scroll Speed Multiplier

Increases the touchscreen scroll speed in most websites.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Touch Scroll Speed Multiplier
// @namespace    http://tampermonkey.net/
// @version      1.4
// @license      MIT
// @description  Increases the touchscreen scroll speed in most websites.
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const MULTIPLIER = 2.5;
    const START_THRESHOLD = 6;

    const FAIL_RATIO = 0.35;      // poniżej tego uznajemy, że scroll nie działa sensownie
    const FAIL_STREAK_LIMIT = 2;  // po tylu porażkach wyłączamy skrypt na stronie
    const STORAGE_KEY = 'tssm_disabled:' + location.hostname;

    let lastY = null;
    let isHandling = false;
    let movedEnough = false;

    let pageDisabled = false;
    let failureStreak = 0;
    let probeDone = false;

    try {
        if (sessionStorage.getItem(STORAGE_KEY) === '1') {
            pageDisabled = true;
        }
    } catch (_) {}

    function disableForThisPage() {
        pageDisabled = true;
        try {
            sessionStorage.setItem(STORAGE_KEY, '1');
        } catch (_) {}
    }

    function getRoot() {
        return document.scrollingElement || document.documentElement;
    }

    function isScrollable(el) {
        if (!(el instanceof Element)) return false;

        const style = getComputedStyle(el);
        const overflowY = style.overflowY;

        return /(auto|scroll|overlay)/.test(overflowY) && el.scrollHeight > el.clientHeight + 1;
    }

    function canScroll(el, delta) {
        const maxScrollTop = el.scrollHeight - el.clientHeight;
        if (maxScrollTop <= 0) return false;

        if (delta > 0) return el.scrollTop < maxScrollTop - 1;
        if (delta < 0) return el.scrollTop > 1;

        return false;
    }

    function getParent(el) {
        if (!el) return null;
        return el.parentElement || (el.getRootNode && el.getRootNode().host) || null;
    }

    function findScrollTarget(x, y, delta) {
        let el = document.elementFromPoint(x, y);

        while (el && el !== document.body && el !== document.documentElement) {
            if (isScrollable(el) && canScroll(el, delta)) {
                return el;
            }
            el = getParent(el);
        }

        const root = getRoot();
        return canScroll(root, delta) ? root : null;
    }

    function getScrollPos(target) {
        const root = getRoot();
        if (target === root) {
            return window.scrollY || window.pageYOffset || 0;
        }
        return target.scrollTop;
    }

    function probeRootScrollOnce() {
        if (probeDone || pageDisabled) return;
        probeDone = true;

        const root = getRoot();
        const maxScrollTop = root.scrollHeight - root.clientHeight;

        if (root.scrollHeight <= root.clientHeight + 5) return;

        const before = getScrollPos(root);
        const step = before < maxScrollTop - 1 ? 1 : -1;
        const testValue = before + step;

        root.scrollTop = testValue;

        const after = getScrollPos(root);
        const works = Math.abs(after - testValue) < 0.5;

        root.scrollTop = before;

        if (!works) {
            disableForThisPage();
        }
    }

    function recordScrollResult(target, before, amount) {
        requestAnimationFrame(() => {
            if (pageDisabled) return;

            const after = getScrollPos(target);
            const moved = Math.abs(after - before);
            const expected = Math.max(1, Math.abs(amount) * FAIL_RATIO);

            if (moved < expected) {
                failureStreak++;
                if (failureStreak >= FAIL_STREAK_LIMIT) {
                    disableForThisPage();
                }
            } else {
                failureStreak = 0;
            }
        });
    }

    function scrollTarget(target, amount) {
        const before = getScrollPos(target);
        const root = getRoot();

        if (target === root) {
            window.scrollBy(0, amount);
        } else if (typeof target.scrollBy === 'function') {
            target.scrollBy(0, amount);
        } else {
            target.scrollTop = before + amount;
        }

        recordScrollResult(target, before, amount);
    }

    function onTouchStart(e) {
        if (pageDisabled) return;

        probeRootScrollOnce();

        if (e.touches && e.touches.length === 1) {
            isHandling = true;
            movedEnough = false;
            lastY = e.touches[0].clientY;
        } else {
            isHandling = false;
            lastY = null;
        }
    }

    function onTouchMove(e) {
        if (pageDisabled) return;

        if (!isHandling || !e.touches || e.touches.length !== 1) {
            isHandling = false;
            movedEnough = false;
            return;
        }

        const touch = e.touches[0];
        const y = touch.clientY;
        const delta = lastY - y;
        lastY = y;

        if (!movedEnough && Math.abs(delta) < START_THRESHOLD) {
            return;
        }

        movedEnough = true;

        const target = findScrollTarget(touch.clientX, touch.clientY, delta);
        if (!target) return;

        e.preventDefault();
        scrollTarget(target, delta * MULTIPLIER);
    }

    function onTouchEnd() {
        isHandling = false;
        movedEnough = false;
        lastY = null;
    }

    document.addEventListener('touchstart', onTouchStart, { passive: true });
    document.addEventListener('touchmove', onTouchMove, { passive: false });
    document.addEventListener('touchend', onTouchEnd, { passive: true });
    document.addEventListener('touchcancel', onTouchEnd, { passive: true });
})();