Touch Scroll Speed Multiplier

Increases the touchscreen scroll speed in most websites.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

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