Touch Scroll Speed Multiplier

Increases the touchscreen scroll speed in most websites.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==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 });
})();