cool thingy!

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

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         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 });
    });

})();