Pause Mouse Movement for Website

Allows toggling pause on website's mousemove listeners, while local cursor still moves.

// ==UserScript==
// @name         Pause Mouse Movement for Website
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Allows toggling pause on website's mousemove listeners, while local cursor still moves.
// @author       Your Name Here
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration ---
    const TOGGLE_KEY = 'm'; // Key to press for toggling
    const REQUIRE_CTRL = true; // Require Ctrl key?
    const REQUIRE_ALT = true; // Require Alt key?
    const REQUIRE_SHIFT = false; // Require Shift key?
    // --- End Configuration ---

    let isPaused = false; // Initial state: website mouse movement is NOT paused
    const listenerMap = new Map(); // Stores mapping: originalListener -> wrappedListener
    let statusIndicator = null; // Reference to the status indicator element

    console.log("Pause Mouse Movement Script: Initializing.");

    // --- Status Indicator ---
    function createStatusIndicator() {
        const indicator = document.createElement('div');
        indicator.style.position = 'fixed';
        indicator.style.bottom = '10px';
        indicator.style.right = '10px';
        indicator.style.padding = '5px 10px';
        indicator.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        indicator.style.color = 'white';
        indicator.style.fontSize = '12px';
        indicator.style.borderRadius = '3px';
        indicator.style.zIndex = '99999'; // Ensure it's on top
        indicator.style.fontFamily = 'sans-serif';
        indicator.style.pointerEvents = 'none'; // Don't let it interfere with mouse events
        document.body.appendChild(indicator);
        return indicator;
    }

    function updateStatusIndicator() {
        if (!statusIndicator && document.body) {
            statusIndicator = createStatusIndicator();
        }
        if (statusIndicator) {
            statusIndicator.textContent = `Website Mouse: ${isPaused ? 'Paused' : 'Active'} (Toggle: ${REQUIRE_CTRL ? 'Ctrl+' : ''}${REQUIRE_ALT ? 'Alt+' : ''}${REQUIRE_SHIFT ? 'Shift+' : ''}${TOGGLE_KEY.toUpperCase()})`;
            statusIndicator.style.backgroundColor = isPaused ? 'rgba(255, 0, 0, 0.7)' : 'rgba(0, 128, 0, 0.7)'; // Red when paused, Green when active
        }
    }

    // --- Toggle Function ---
    function togglePause() {
        isPaused = !isPaused;
        console.log(`Pause Mouse Movement Script: Website mousemove listeners ${isPaused ? 'paused' : 'activated'}.`);
        updateStatusIndicator();
    }

    // --- Keyboard Shortcut Listener ---
    document.addEventListener('keydown', (event) => {
        // Check if the pressed key and modifiers match the configuration
        if (event.key.toLowerCase() === TOGGLE_KEY.toLowerCase() &&
            event.ctrlKey === REQUIRE_CTRL &&
            event.altKey === REQUIRE_ALT &&
            event.shiftKey === REQUIRE_SHIFT)
        {
            // Prevent default action if the key combination might have one
            event.preventDefault();
            event.stopPropagation();
            togglePause();
        }
    }, true); // Use capture phase to catch before page scripts

    // --- Intercept addEventListener ---
    const originalAddEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(type, listener, options) {
        if (type === 'mousemove' && typeof listener === 'function') {
            // Only wrap mousemove listeners
            if (!listenerMap.has(listener)) {
                const wrappedListener = function(...args) {
                    if (!isPaused) {
                        // If not paused, call the original listener
                        // Use Reflect.apply to correctly handle 'this' context and arguments
                        try {
                            Reflect.apply(listener, this, args);
                        } catch (e) {
                            console.error("Error executing original mousemove listener:", e);
                        }
                    }
                    // If paused, do nothing, effectively blocking the listener
                };
                listenerMap.set(listener, wrappedListener);
                // Add the wrapped listener instead of the original
                originalAddEventListener.call(this, type, wrappedListener, options);
                // console.log("Wrapped mousemove listener added for:", this);
            } else {
                // Listener already wrapped, potentially re-adding? Call original to ensure browser handles it correctly.
                originalAddEventListener.call(this, type, listenerMap.get(listener), options);
            }
        } else {
            // For all other event types, call the original function directly
            originalAddEventListener.call(this, type, listener, options);
        }
    };

    // --- Intercept removeEventListener ---
    const originalRemoveEventListener = EventTarget.prototype.removeEventListener;
    EventTarget.prototype.removeEventListener = function(type, listener, options) {
        if (type === 'mousemove' && typeof listener === 'function') {
            // If it's a mousemove listener, try to remove the wrapped version
            const wrappedListener = listenerMap.get(listener);
            if (wrappedListener) {
                // Remove the wrapped listener
                originalRemoveEventListener.call(this, type, wrappedListener, options);
                listenerMap.delete(listener); // Clean up the map
                // console.log("Wrapped mousemove listener removed for:", this);
            } else {
                // If no wrapper found (maybe added before script ran?), try removing original anyway
                originalRemoveEventListener.call(this, type, listener, options);
            }
        } else {
            // For all other event types, call the original function directly
            originalRemoveEventListener.call(this, type, listener, options);
        }
    };

    console.log("Pause Mouse Movement Script: Event listener interception active.");

    // Update status indicator once the body is available
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', updateStatusIndicator);
    } else {
        // DOMContentLoaded has already fired
        updateStatusIndicator();
    }

})();