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