cool thingy!

Per-page X-Mouse-like controls: custom actions for extra buttons, wheel, and hover focus (where possible) in Safari.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         cool thingy!
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Per-page X-Mouse-like controls: custom actions for extra buttons, wheel, and hover focus (where possible) in Safari.
// @author       You
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    // =========================
    // CONFIG: TWEAK THIS PART
    // =========================

    // Example actions:
    //  - "back": window.history.back()
    //  - "forward": window.history.forward()
    //  - "scrollUp"/"scrollDown": window.scrollBy(...)
    //  - "top": scroll to top
    //  - "bottom": scroll to bottom
    //  - "none": do nothing / let default happen

    const CONFIG = {
        // Map mouse buttons to actions
        // 0 = left, 1 = middle, 2 = right, 3+ = extra (if browser exposes them)
        buttonMap: {
            1: 'autoScrollToggle', // middle click toggles auto-scroll
            3: 'back',             // XButton1 (if exposed) -> back
            4: 'forward'           // XButton2 (if exposed) -> forward
        },

        // Auto-scroll speed (pixels per frame)
        autoScrollSpeed: 8,

        // Enable "focus window on hover" style behavior (limited in browser)
        focusOnHover: true
    };

    // =========================
    // INTERNAL STATE
    // =========================

    let autoScrollActive = false;
    let autoScrollDirection = 1; // 1 = down, -1 = up
    let autoScrollRAF = null;

    // =========================
    // ACTION HANDLERS
    // =========================

    function doAction(action, event) {
        switch (action) {
            case 'back':
                event.preventDefault();
                window.history.back();
                break;
            case 'forward':
                event.preventDefault();
                window.history.forward();
                break;
            case 'scrollUp':
                event.preventDefault();
                window.scrollBy({ top: -200, left: 0, behavior: 'smooth' });
                break;
            case 'scrollDown':
                event.preventDefault();
                window.scrollBy({ top: 200, left: 0, behavior: 'smooth' });
                break;
            case 'top':
                event.preventDefault();
                window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
                break;
            case 'bottom':
                event.preventDefault();
                window.scrollTo({ top: document.body.scrollHeight, left: 0, behavior: 'smooth' });
                break;
            case 'autoScrollToggle':
                event.preventDefault();
                toggleAutoScroll(event);
                break;
            case 'none':
            default:
                // Let default behavior happen
                break;
        }
    }

    // =========================
    // AUTO-SCROLL
    // =========================

    function autoScrollLoop() {
        if (!autoScrollActive) return;
        window.scrollBy(0, CONFIG.autoScrollSpeed * autoScrollDirection);
        autoScrollRAF = window.requestAnimationFrame(autoScrollLoop);
    }

    function toggleAutoScroll(event) {
        // Direction based on where you clicked relative to center
        const centerY = window.innerHeight / 2;
        autoScrollDirection = (event.clientY < centerY) ? -1 : 1;

        if (autoScrollActive) {
            autoScrollActive = false;
            if (autoScrollRAF) {
                window.cancelAnimationFrame(autoScrollRAF);
                autoScrollRAF = null;
            }
        } else {
            autoScrollActive = true;
            autoScrollLoop();
        }
    }

    // =========================
    // EVENT LISTENERS
    // =========================

    // Mouse button remapping
    window.addEventListener('mousedown', function (e) {
        // e.button: 0=left,1=middle,2=right, 3+=extra (if supported)
        const action = CONFIG.buttonMap[e.button];
        if (!action) return;
        doAction(action, e);
    }, true);

    // Optional: focus-on-hover style (limited)
    if (CONFIG.focusOnHover) {
        window.addEventListener('mouseover', function (e) {
            // We can't change OS-level focus, but we can visually "focus" elements.
            const el = e.target;
            if (el && el.focus && typeof el.focus === 'function') {
                // Only auto-focus inputs, textareas, and contenteditable
                const tag = el.tagName.toLowerCase();
                const isEditable =
                    tag === 'input' ||
                    tag === 'textarea' ||
                    el.isContentEditable;

                if (isEditable) {
                    el.focus({ preventScroll: true });
                }
            }
        }, true);
    }

    // Safety: stop auto-scroll on user input
    ['wheel', 'keydown', 'mousedown'].forEach(type => {
        window.addEventListener(type, () => {
            if (!autoScrollActive) return;
            autoScrollActive = false;
            if (autoScrollRAF) {
                window.cancelAnimationFrame(autoScrollRAF);
                autoScrollRAF = null;
            }
        }, { passive: true });
    });

})();