Human Typer (Ultimate Fix)

Activates via Alt+V. Pause: Ctrl+Shift+Alt. Kill: Ctrl+Shift+Alt+Space. Console: exit

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Human Typer (Ultimate Fix)
// @namespace    http://tampermonkey.net/
// @version      8.0
// @description  Activates via Alt+V. Pause: Ctrl+Shift+Alt. Kill: Ctrl+Shift+Alt+Space. Console: exit
// @author       You
// @match        *://*/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // --- 1. DEFINE CONSOLE COMMAND AT THE VERY TOP ---
    // This ensures "exit" is ALWAYS defined in the console, even if the rest of the script breaks.
    window.exit = function() {
        clearTimeout(window.__ht_timeout);
        window.__ht_typing = false;
        window.__ht_paused = false;
        window.__ht_target = null;
        console.log('[HumanTyper] 🛑 SCRIPT EXITED.');
    };
    
    console.log('[HumanTyper] Script loaded. Type "exit" in the console to kill it.');

    // --- 2. ANTI-DOUBLE-SCRIPT LOCK ---
    if (window.__humanTyperMutex) {
        console.log('[HumanTyper] Duplicate script detected. Shutting down duplicate.');
        return; 
    }
    window.__humanTyperMutex = true;

    const LOG_PREFIX = '[HumanTyper]';
    const LOG = console.log.bind(console, LOG_PREFIX);

    // Using window.__ht_ variables so the window.exit() function can see and kill them
    window.__ht_typing = false;
    window.__ht_paused = false;
    let textToType = "";
    let currentIndex = 0;
    window.__ht_timeout = null;
    window.__ht_target = null; 

    // --- 3. Intercept Alt+V (New Activation) ---
    document.addEventListener('keydown', async function(e) {
        // Check for Alt+V (and ensure Ctrl/Shift are NOT held)
        if (e.altKey && !e.ctrlKey && !e.shiftKey && e.code === 'KeyV') {
            
            // CRITICAL FIX: stopImmediatePropagation stops Chrome from stealing focus to open the "View" menu
            e.preventDefault(); 
            e.stopPropagation();
            e.stopImmediatePropagation(); 

            if (window.__ht_typing) return;

            LOG('Alt+V detected. Reading clipboard...');

            let txt = '';
            try {
                if (navigator.clipboard && navigator.clipboard.readText) {
                    txt = await navigator.clipboard.readText();
                }
            } catch (err) {
                console.error(LOG_PREFIX, 'Failed to read clipboard.', err);
                console.error(LOG_PREFIX, 'NOTE: If you are testing on a local HTTP:// website, Chrome blocks clipboard access. It MUST be on HTTPS://');
                return;
            }

            if (!txt) {
                LOG('Clipboard is empty.');
                return;
            }

            window.__ht_target = document.activeElement;
            
            if (!window.__ht_target || !(window.__ht_target.isContentEditable || window.__ht_target.tagName === 'INPUT' || window.__ht_target.tagName === 'TEXTAREA')) {
                LOG('Please focus on a text box before pressing Alt+V.');
                return;
            }

            textToType = txt;
            currentIndex = 0;
            window.__ht_typing = true;
            window.__ht_paused = false;
            
            LOG(`Starting typing (${textToType.length} chars)...`);
            typeNextChar();
        }
    }, true); // 'true' means capture phase (runs before the website)

    // --- 4. Intercept Ctrl+Shift+Alt (Pause) & Ctrl+Shift+Alt+Space (Kill Switch) ---
    document.addEventListener('keydown', function(e) {
        if (e.ctrlKey && e.shiftKey && e.altKey) {
            if (e.repeat) return; 
            e.preventDefault();

            // --- KILL SWITCH LOGIC ---
            if (e.code === 'Space') {
                clearTimeout(window.__ht_timeout);
                window.__ht_typing = false;
                window.__ht_paused = false;
                currentIndex = 0;
                textToType = "";
                window.__ht_target = null;
                LOG('🛑 SCRIPT COMPLETELY STOPPED & RESET.');
                return; 
            }

            // --- PAUSE/RESUME LOGIC ---
            if (!window.__ht_typing) return;

            window.__ht_paused = !window.__ht_paused;

            if (window.__ht_paused) {
                clearTimeout(window.__ht_timeout);
                LOG('⏸️ PAUSED');
            } else {
                LOG('▶️ RESUMED');
                typeNextChar(); 
            }
        }
    }, true);

    // --- 5. Foolproof Typing Logic ---
    function typeNextChar() {
        if (!window.__ht_typing || window.__ht_paused) return;
        
        if (!window.__ht_target || !document.body.contains(window.__ht_target)) {
            LOG('Target text box was removed from the page. Stopping.');
            window.__ht_typing = false;
            return;
        }

        if (currentIndex >= textToType.length) {
            window.__ht_typing = false;
            LOG('Typing loop finished. Running verification...');
            verifyOutput(); 
            return;
        }

        // FORCE FOCUS: Keep typing even if invisible or scrolled away
        if (document.activeElement !== window.__ht_target) {
            window.__ht_target.focus();
        }

        const charToType = textToType[currentIndex];

        if (window.__ht_target.tagName === 'INPUT' || window.__ht_target.tagName === 'TEXTAREA') {
            const prototype = window.__ht_target.tagName === 'TEXTAREA' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
            const nativeSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
            
            const start = window.__ht_target.selectionStart;
            const end = window.__ht_target.selectionEnd;
            const newVal = window.__ht_target.value.substring(0, start) + charToType + window.__ht_target.value.substring(end);
            
            nativeSetter.call(window.__ht_target, newVal);
            window.__ht_target.selectionStart = window.__ht_target.selectionEnd = start + 1;
            window.__ht_target.dispatchEvent(new Event('input', { bubbles: true }));
            
        } else if (window.__ht_target.isContentEditable) {
            document.execCommand('insertHTML', false, charToType);
        }

        currentIndex++;
        
        const humanDelay = Math.floor(Math.random() * 30) + 15; 
        window.__ht_timeout = setTimeout(typeNextChar, humanDelay);
    }

    // --- 6. Verification Subroutine ---
    function verifyOutput() {
        if (!window.__ht_target) return;

        let currentText = "";

        if (window.__ht_target.tagName === 'INPUT' || window.__ht_target.tagName === 'TEXTAREA') {
            currentText = window.__ht_target.value;
        } else if (window.__ht_target.isContentEditable) {
            currentText = window.__ht_target.innerText;
        }

        if (currentText === textToType) {
            LOG('✅ VERIFICATION PASSED: Output is 100% identical to clipboard.');
            return;
        }

        LOG('⚠️ MISMATCH DETECTED: Force-correcting text to match clipboard...');
        forceExactText(window.__ht_target, textToType);
    }

    function forceExactText(el, text) {
        el.focus();
        
        if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
            const prototype = el.tagName === 'TEXTAREA' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
            const nativeSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
            
            nativeSetter.call(el, text);
            el.selectionStart = el.selectionEnd = text.length;
            el.dispatchEvent(new Event('input', { bubbles: true }));
            el.dispatchEvent(new Event('change', { bubbles: true }));
        } else if (el.isContentEditable) {
            const selection = window.getSelection();
            const range = document.createRange();
            range.selectNodeContents(el);
            selection.removeAllRanges();
            selection.addRange(range);
            
            document.execCommand('insertText', false, text);
        }
        
        LOG('✅ FIX APPLIED: Text force-corrected to match clipboard.');
    }

})();