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

})();