Advertisement
NashrifZMgs

MouseCursorforVia

Jun 13th, 2025
466
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Universal On-Screen Pointer (Standalone)
  3. // @namespace    https://viayoo.com
  4. // @version      2025-06-28.1
  5. // @description  A standalone, smooth, fully-featured on-screen pointer for TV browsers, with scroll mode, drag-and-drop, and state persistence.
  6. // @author       Gemini & Vyacheslav
  7. // @match        *://*/*
  8. // @grant        none
  9. // @run-at       document-start
  10. // ==/UserScript==
  11.  
  12. (function() {
  13.     'use strict';
  14.  
  15.     function initializeScript() {
  16.         // --- 1. CORE STATE VARIABLES ---
  17.         let pointer, tunerUI, tunerValueElement;
  18.         let scriptActive, currentMode, isDragging, enterHoldStartTime, dragTarget, textInputMode, scrollTarget, escapeKeyPressed, tunerIsVisible, currentPointerStyleIndex;
  19.         const cursor = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
  20.         const keysDown = {};
  21.         let lastFrameTime = performance.now();
  22.         const SETTINGS = { POINTER_INITIAL_SPEED: 150, POINTER_TOP_SPEED: 900, POINTER_ACCELERATION_TIME: 800, CURSOR_SIZE: 24, ENTER_HOLD_THRESHOLD: 220, SCROLL_SPEED_INITIAL: 8, SCROLL_SPEED_MAX: 40, SCROLL_ACCELERATION_TIME: 1200, };
  23.         const COLORS = { NORMAL: 'black', SCROLL: '#0EA5E9' };
  24.         const POINTER_STYLES = [ { width: `${SETTINGS.CURSOR_SIZE}px`, height: `${SETTINGS.CURSOR_SIZE}px`, backgroundColor: 'rgba(255, 255, 255, 0.5)', border: '3px solid', baseBorderColor: COLORS.NORMAL, borderRadius: '50%', boxShadow: '0 2px 5px rgba(0,0,0,0.5)' }, { width: '12px', height: '12px', backgroundColor: 'rgba(255, 20, 20, 0.9)', border: '1px solid', baseBorderColor: 'rgba(255, 255, 255, 0.8)', borderRadius: '50%', boxShadow: '0 0 5px red' }, ];
  25.  
  26.         // --- 2. ALL FUNCTION DEFINITIONS ---
  27.  
  28.         function createTunerUI() {
  29.             if (document.getElementById('gm-pointer-tuner-ui')) return;
  30.             tunerUI = document.createElement('div');
  31.             tunerUI.id = 'gm-pointer-tuner-ui';
  32.             Object.assign(tunerUI.style, { position: 'fixed', top: '20px', left: '50%', transform: 'translateX(-50%)', display: 'none', alignItems: 'center', gap: '20px', backgroundColor: 'rgba(0,0,0,0.7)', color: 'white', fontFamily: 'sans-serif', padding: '10px 20px', borderRadius: '8px', zIndex: '2147483647', border: '1px solid #555' });
  33.             const l = document.createElement('div'); l.textContent = '<';
  34.             const r = document.createElement('div'); r.textContent = '>';
  35.             tunerValueElement = document.createElement('div');
  36.             [l, r].forEach(a => Object.assign(a.style, { fontSize: '24px', fontWeight: 'bold', cursor: 'pointer' }));
  37.             Object.assign(tunerValueElement.style, { fontSize: '18px', minWidth: '80px', textAlign: 'center' });
  38.             tunerUI.appendChild(l);
  39.             tunerUI.appendChild(tunerValueElement);
  40.             tunerUI.appendChild(r);
  41.             document.body.appendChild(tunerUI);
  42.         }
  43.  
  44.         const toggleTuner = () => {
  45.             tunerIsVisible = !tunerIsVisible;
  46.             if (tunerUI) tunerUI.style.display = tunerIsVisible ? 'flex' : 'none';
  47.         };
  48.  
  49.         function saveState() {
  50.             sessionStorage.setItem('gm-pointer-active', JSON.stringify(scriptActive));
  51.         }
  52.  
  53.         function loadState() {
  54.             const wasPointerActive = sessionStorage.getItem('gm-pointer-active');
  55.             scriptActive = wasPointerActive === null ? true : JSON.parse(wasPointerActive);
  56.         }
  57.  
  58.         function createPointer() {
  59.             pointer = document.createElement('div');
  60.             pointer.id = 'gm-pointer';
  61.             Object.assign(pointer.style, { position: 'fixed', top: '0px', left: '0px', zIndex: '2147483649', transform: 'translate(-50%, -50%)', transition: 'transform 0.1s ease-out, border-color 0.2s, background-color 0.2s, border-radius 0.2s, width 0.2s, height 0.2s, box-shadow 0.2s', pointerEvents: 'none' });
  62.             document.body.appendChild(pointer);
  63.         }
  64.  
  65.         const updateUIVisibility = () => {
  66.             if (!pointer) return;
  67.             pointer.style.display = scriptActive ? 'block' : 'none';
  68.             const currentStyle = POINTER_STYLES[currentPointerStyleIndex];
  69.             let borderColor = currentStyle.baseBorderColor || COLORS.NORMAL;
  70.             if (currentMode === 'scroll') { borderColor = COLORS.SCROLL; }
  71.             pointer.style.borderColor = borderColor;
  72.         };
  73.  
  74.         function applyPointerStyle() {
  75.             if (!pointer) return;
  76.             const style = POINTER_STYLES[currentPointerStyleIndex];
  77.             Object.assign(pointer.style, { width: style.width, height: style.height, backgroundColor: style.backgroundColor, border: style.border, borderRadius: style.borderRadius, boxShadow: style.boxShadow, });
  78.             updateUIVisibility();
  79.         }
  80.  
  81.         const dispatchMouseEvent = (type, target, options) => {
  82.             if (!target) return;
  83.             const eventOptions = { bubbles: true, cancelable: true, view: window, clientX: options.x, clientY: options.y, pointerType: 'mouse', isPrimary: true, button: options.button || 0, ...options };
  84.             target.dispatchEvent(new (typeof PointerEvent === 'function' ? PointerEvent : MouseEvent)(type, eventOptions));
  85.         };
  86.  
  87.         const clickAtPoint = (x, y) => {
  88.             pointer.style.display = 'none';
  89.             let el = document.elementFromPoint(x, y);
  90.             pointer.style.display = 'block';
  91.             if (!el) return;
  92.             if (el.id.startsWith('gm-')) return;
  93.             if (['INPUT', 'TEXTAREA'].includes(el.tagName) || el.isContentEditable) {
  94.                 textInputMode = true;
  95.                 el.focus();
  96.                 return;
  97.             }
  98.             dispatchMouseEvent('mousedown', el, { x, y });
  99.             dispatchMouseEvent('mouseup', el, { x, y });
  100.             dispatchMouseEvent('click', el, { x, y });
  101.         };
  102.  
  103.         const findScrollableParent = (element) => {
  104.             if (!element) return null;
  105.             let el = element;
  106.             while (el && el !== document.body && el !== document.documentElement) {
  107.                 const style = window.getComputedStyle(el);
  108.                 if ((style.overflowY === 'scroll' || style.overflowY === 'auto' || style.overflowX === 'scroll' || style.overflowX === 'auto') && (el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth)) {
  109.                     return el;
  110.                 }
  111.                 el = el.parentElement;
  112.             }
  113.             return null;
  114.         };
  115.  
  116.         const exitTextInputMode = () => {
  117.             textInputMode = false;
  118.             if (document.activeElement) document.activeElement.blur();
  119.         };
  120.  
  121.         const updateTunerText = () => {
  122.             if (tunerValueElement) tunerValueElement.textContent = `${SETTINGS.POINTER_ACCELERATION_TIME} ms`;
  123.         };
  124.  
  125.         const gameLoop = () => {
  126.             const now = performance.now();
  127.             const deltaTime = (now - lastFrameTime) / 1000.0;
  128.             lastFrameTime = now;
  129.  
  130.             if (scriptActive && !textInputMode && !tunerIsVisible) {
  131.                 if (currentMode === 'pointer') {
  132.                     if (keysDown.Enter && !isDragging && (now - enterHoldStartTime > SETTINGS.ENTER_HOLD_THRESHOLD)) {
  133.                         isDragging = true;
  134.                         pointer.style.display = 'none';
  135.                         dragTarget = document.elementFromPoint(cursor.x, cursor.y) || window;
  136.                         pointer.style.display = 'block';
  137.                         dispatchMouseEvent('mousedown', dragTarget, { x: cursor.x, y: cursor.y });
  138.                     }
  139.                     let moveX = 0, moveY = 0;
  140.                     for (const key in keysDown) {
  141.                         if (key === 'Enter') continue;
  142.                         const rampFactor = Math.min(1, (now - (keysDown[key] || now)) / SETTINGS.POINTER_ACCELERATION_TIME);
  143.                         const currentSpeed = SETTINGS.POINTER_INITIAL_SPEED + (SETTINGS.POINTER_TOP_SPEED - SETTINGS.POINTER_INITIAL_SPEED) * rampFactor;
  144.                         if (key === 'ArrowUp') moveY -= currentSpeed;
  145.                         if (key === 'ArrowDown') moveY += currentSpeed;
  146.                         if (key === 'ArrowLeft') moveX -= currentSpeed;
  147.                         if (key === 'ArrowRight') moveX += currentSpeed;
  148.                     }
  149.                     cursor.x += moveX * deltaTime;
  150.                     cursor.y += moveY * deltaTime;
  151.                     if (isDragging) {
  152.                         dispatchMouseEvent('mousemove', dragTarget, { x: cursor.x, y: cursor.y });
  153.                     }
  154.                     cursor.x = Math.max(0, Math.min(window.innerWidth, cursor.x));
  155.                     cursor.y = Math.max(0, Math.min(window.innerHeight, cursor.y));
  156.                 } else if (currentMode === 'scroll') {
  157.                     const target = scrollTarget || window;
  158.                     for (const key in keysDown) {
  159.                         if (!['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(key)) continue;
  160.                         const holdDuration = now - (keysDown[key] || now);
  161.                         const rampFactor = Math.min(1, holdDuration / SETTINGS.SCROLL_ACCELERATION_TIME);
  162.                         const currentSpeed = SETTINGS.SCROLL_SPEED_INITIAL + (SETTINGS.SCROLL_SPEED_MAX - SETTINGS.SCROLL_SPEED_INITIAL) * rampFactor;
  163.                         if (key === 'ArrowUp') target.scrollBy(0, -currentSpeed);
  164.                         if (key === 'ArrowDown') target.scrollBy(0, currentSpeed);
  165.                         if (key === 'ArrowLeft') target.scrollBy(-currentSpeed, 0);
  166.                         if (key === 'ArrowRight') target.scrollBy(currentSpeed, 0);
  167.                     }
  168.                 }
  169.             }
  170.             if (pointer) pointer.style.transform = `translate(${cursor.x}px, ${cursor.y}px) translate(-50%, -50%) scale(${isDragging ? '0.8' : '1'})`;
  171.             requestAnimationFrame(gameLoop);
  172.         };
  173.  
  174.         const handleKeyDown = (e) => {
  175.             if (e.key === '6') { escapeKeyPressed = true; e.preventDefault(); e.stopImmediatePropagation(); return; }
  176.             if (tunerIsVisible) {
  177.                 if (e.key === 'ArrowLeft') { SETTINGS.POINTER_ACCELERATION_TIME = Math.max(100, SETTINGS.POINTER_ACCELERATION_TIME - 50); updateTunerText(); }
  178.                 else if (e.key === 'ArrowRight') { SETTINGS.POINTER_ACCELERATION_TIME = Math.min(2000, SETTINGS.POINTER_ACCELERATION_TIME + 50); updateTunerText(); }
  179.                 e.preventDefault(); e.stopPropagation();
  180.                 return;
  181.             }
  182.             if (textInputMode) {
  183.                 if (['3', 'Escape'].includes(e.key)) { e.preventDefault(); e.stopPropagation(); exitTextInputMode(); }
  184.                 return;
  185.             }
  186.             if (e.key === '1') { e.preventDefault(); e.stopPropagation(); scriptActive = !scriptActive; updateUIVisibility(); return; }
  187.             if (!scriptActive) return;
  188.  
  189.             if (e.key === '5') { e.preventDefault(); e.stopPropagation(); toggleTuner(); return; }
  190.             if (e.key === '8') { e.preventDefault(); e.stopPropagation(); currentPointerStyleIndex = (currentPointerStyleIndex + 1) % POINTER_STYLES.length; applyPointerStyle(); return; }
  191.             if (e.key === '2') { e.preventDefault(); e.stopPropagation(); currentMode = (currentMode === 'pointer') ? 'scroll' : 'pointer'; scrollTarget = findScrollableParent(document.elementFromPoint(cursor.x, cursor.y)); updateUIVisibility(); return; }
  192.            
  193.             if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter'].includes(e.key)) {
  194.                 e.preventDefault(); e.stopPropagation();
  195.                 if (!keysDown[e.key]) {
  196.                     keysDown[e.key] = performance.now();
  197.                     if (e.key === 'Enter') enterHoldStartTime = keysDown.Enter;
  198.                 }
  199.             }
  200.         };
  201.  
  202.         const handleKeyUp = (e) => {
  203.             if (e.key === '6') { escapeKeyPressed = false; }
  204.             if (!scriptActive || textInputMode || tunerIsVisible) {
  205.                 delete keysDown[e.key];
  206.                 return;
  207.             }
  208.             if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter'].includes(e.key)) {
  209.                 e.preventDefault(); e.stopPropagation();
  210.                 if (e.key === 'Enter') {
  211.                     if (isDragging) {
  212.                         dispatchMouseEvent('mouseup', dragTarget, { x: cursor.x, y: cursor.y });
  213.                     } else {
  214.                         clickAtPoint(cursor.x, cursor.y);
  215.                     }
  216.                     isDragging = false;
  217.                     dragTarget = null;
  218.                 }
  219.                 delete keysDown[e.key];
  220.             }
  221.         };
  222.  
  223.         // --- 3. FINAL INITIALIZATION & EXECUTION ---
  224.         currentMode = 'pointer'; isDragging = false; enterHoldStartTime = 0; dragTarget = null; textInputMode = false; scrollTarget = null; escapeKeyPressed = false; currentPointerStyleIndex = 0; tunerIsVisible = false;
  225.         createTunerUI();
  226.         createPointer();
  227.         loadState();
  228.         updateTunerText();
  229.         applyPointerStyle();
  230.        
  231.         setInterval(() => { if (document.activeElement && document.activeElement.tagName === 'IFRAME' && escapeKeyPressed) { window.focus(); escapeKeyPressed = false; } }, 100);
  232.         document.addEventListener('keydown', handleKeyDown, true);
  233.         document.addEventListener('keyup', handleKeyUp, true);
  234.         window.addEventListener('beforeunload', saveState);
  235.         requestAnimationFrame(gameLoop);
  236.     }
  237.  
  238.     document.addEventListener('DOMContentLoaded', initializeScript, { once: true });
  239. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement