WordsPerWinIt

Logixx

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         WordsPerWinIt
// @description  Logixx
// @icon         https://www.google.com/s2/favicons?sz=64&domain=monkeytype.com
// @version      0.5
// @match        *://monkeytype.com/*
// @run-at       document-start
// @grant        none
// @license      Logixx
// @namespace https://greasyfork.org/users/1222651
// ==/UserScript==

(function () {
    "use strict";

    // Configuration
    let WORDS_PER_MINUTE = 100;
    let ACCURACY = 95; // Probability of committing an error as a percentage (0-100)
    let TOGGLE_KEY = "ArrowRight";
    let toggle = false;
    let IndexCounter = 0;
    let typingInterval = undefined;
    const log = console.log;

    // UI Container for settings
    const uiContainer = createUIContainer();
    setupUI();
    document.body.appendChild(uiContainer);

    // Key Events
    window.addEventListener("keydown", handleKeyPress);
    window.addEventListener("keydown", toggleUI);

    // Hooking into Input Events
    hookInputEvents(HTMLInputElement.prototype);

    // Functions
    function toggleUI(event) {
        if (event.key === "Tab" || event.key === ".") {
            event.preventDefault();
            uiContainer.style.display = uiContainer.style.display === 'none' ? 'block' : 'none';
        }
    }

    function createUIContainer() {
        const container = document.createElement("div");
        container.style.cssText = `
            position: fixed; top: 20px; right: 20px; width: 250px;
            padding: 15px; background: rgba(34, 34, 34, 0.9); color: white;
            border-radius: 10px; font-family: 'Arial', sans-serif; z-index: 1000;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); transition: all 0.3s;
        `;
        container.style.display = 'block'; // Start visible
        container.addEventListener('click', (event) => {
            event.stopPropagation();
        });
        return container;
    }

    function setupUI() {
        uiContainer.appendChild(createHeader());
        uiContainer.appendChild(createDelaySetting("Words Per Minute:", 1000, WORDS_PER_MINUTE, (value) => { WORDS_PER_MINUTE = value; }, "WPM"));
        uiContainer.appendChild(createDelaySetting("Accuracy Percentage:", 100, ACCURACY, (value) => { ACCURACY = value; }, "%"));
        uiContainer.appendChild(createKeybindButton());
    }

    function createHeader() {
        const header = document.createElement("h2");
        header.textContent = "Words Per WinIt Settings:";
        header.style.cssText = `
            font-size: 20px; color: #f2a900; text-align: center; margin-bottom: 15px;
            text-shadow: 1px 1px 5px rgba(0, 0, 0, 0.8);
        `;
        return header;
    }

    function createDelaySetting(labelText, max, initialValue, onChange, unit) {
        const container = document.createElement("div");
        container.style.marginBottom = "10px";

        const label = document.createElement("label");
        label.textContent = labelText;
        label.style.cssText = "display: inline-block; margin-bottom: 5px;";

        const valueDisplay = document.createElement("span");
        valueDisplay.textContent = `${initialValue} ${unit}`;
        valueDisplay.style.cssText = "color: #00e676; font-weight: bold; display: block;";

        const input = document.createElement("input");
        input.type = "range";
        input.min = "0";
        input.max = `${max}`; // Adjust max as necessary
        input.value = initialValue;
        input.style.cssText = "width: 100%; margin-top: 5px;";

        input.addEventListener("input", () => {
            const value = parseInt(input.value, 10);
            valueDisplay.textContent = `${value} ${unit}`;
            onChange(value);
        });

        container.appendChild(label);
        container.appendChild(valueDisplay);
        container.appendChild(input);
        return container;
    }

    const BLACKLISTED_KEYS = ["Escape", "Backspace", "Enter", "Print"];
    function createKeybindButton() {
        const keybindLabel = document.createElement("label");
        keybindLabel.textContent = "Toggle Keybind:";
        keybindLabel.style.cssText = "display: block; margin-top: 10px;";

        const keybindButton = document.createElement("button");
        keybindButton.textContent = `${TOGGLE_KEY}`;
        keybindButton.style.cssText = `
            width: 100%; padding: 8px; margin-top: 4px; font-size: 14px;
            text-align: center; border: none; border-radius: 5px; background: #333; color: #fff;
            cursor: pointer; transition: background 0.3s;
        `;
        keybindButton.onmouseover = () => { keybindButton.style.background = "#444"; };
        keybindButton.onmouseout = () => { keybindButton.style.background = "#333"; };

        let listeningForKey = false;
        keybindButton.addEventListener("click", () => {
            keybindButton.textContent = "set key...";
            listeningForKey = true;
        });

        document.addEventListener("keydown", (event) => {
            if (listeningForKey) {
                if (BLACKLISTED_KEYS.includes(event.key)) {
                    keybindButton.textContent = "choose another key...";
                    return;
                }
                TOGGLE_KEY = event.code;
                keybindButton.textContent = `${event.key}`;
                listeningForKey = false;
                event.stopPropagation();
            }
        });

        return keybindButton;
    }

    function handleKeyPress(event) {
        if (event.code === TOGGLE_KEY) {
            event.preventDefault();
            if (event.repeat) return; // Prevent triggering multiple times
            toggle = !toggle;

            if (toggle) {
                IndexCounter = 0;
                log("STARTED TYPING TEST");
                startTyping();
            } else {
                log("STOPPED TYPING TEST");
            }
        }
    }

    function startTyping() {
        let CHARACTER_INTERVAL;
        let LAST_DATE = Date.now();

        typingInterval = setInterval(() => {
            if (!toggle) {
                clearInterval(typingInterval);
                clearInterval(CHARACTER_INTERVAL);
                return;
            }

            const CHARACTERS_PER_MINUTE = ((60000 / WORDS_PER_MINUTE) / 5) / (1 + ((WORDS_PER_MINUTE / 250) * .15));

            if (Date.now() - LAST_DATE >= CHARACTERS_PER_MINUTE) {

                typeCharacter();
                LAST_DATE = Date.now();
            };
        }, 1);
    }

    function canType() {
        const typingTest = document.getElementById("typingTest");
        const isHidden = typingTest && typingTest.classList.contains("hidden");
        if (isHidden) toggle = false;
        return toggle && !isHidden;
    }


    function getNextCharacter() {
        // Logixx errors
        IndexCounter++;
        if (IndexCounter === Math.round(100 / ((100 - ACCURACY) / 2))) {
            IndexCounter = 0;
            return "‍";
        };

        const currentWord = document.querySelector(".word.active");
        for (const letter of currentWord.children) {
            if (letter.className === "") return letter.textContent;
        }
        return " "; // Return space if all letters are typed
    }

    function typeCharacter() {
        if (!canType()) return clearInterval(typingInterval); // Check if we can type
        const nextChar = getNextCharacter();

        if (nextChar) {
            pressKey(nextChar);
        } else {
            log("Finished typing.");
            toggle = false; // Stop typing if finished
        }
    }

    // Intercept when JQuery attached an addEventListener to the Input element
    function HookKeyboard(element) {
        element.addEventListener = new Proxy(element.addEventListener, {
            apply(target, _this, args) {
                const [type, listener, ...options] = args;
                if (_this.id === "wordsInput") {
                    InputEvents[type] = listener;
                }
                return target.apply(_this, args);
            }
        })
    }

    HookKeyboard(HTMLInputElement.prototype);

    const InputEvents = {};
    function pressKey(key) {
        const wordsInput = document.getElementById("wordsInput");
        const KeyboardEvent = Object.assign({}, DEFAULT_INPUT_OPTIONS, { target: wordsInput, data: key });
        const InputEvent = Object.assign({}, DEFAULT_KEY_OPTIONS, { target: wordsInput, key: key });
        wordsInput.value += key;
        InputEvents.beforeinput(InputEvent);
        InputEvents.input(InputEvent);
        InputEvents.keyup(KeyboardEvent);
    }

    function random(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function hookInputEvents(element) {
        const originalAddEventListener = element.addEventListener;

        element.addEventListener = function (type, listener, options) {
            if (type === 'input') {
                // Optionally handle input events
            }
            return originalAddEventListener.call(this, type, listener, options);
        };
    }

    const DEFAULT_KEY_OPTIONS = {
        key: "", code: "", keyCode: 0, which: 0, isTrusted: true, altKey: false,
        bubbles: true, cancelBubble: false, cancelable: true, charCode: 0,
        composed: true, ctrlKey: false, currentTarget: null, defaultPrevented: false,
        detail: 0, eventPhase: 0, isComposing: false, location: 0, metaKey: false,
        path: null, repeat: false, returnValue: true, shiftKey: false, srcElement: null,
        target: null, timeStamp: 6338.5, type: "", view: window,
    };

    const DEFAULT_INPUT_OPTIONS = {
        isTrusted: true, bubbles: true, cancelBubble: false, cancelable: false,
        composed: true, data: "", dataTransfer: null, defaultPrevented: false,
        detail: 0, eventPhase: 0, inputType: "insertText", isComposing: false,
        path: null, returnValue: true, sourceCapabilities: null, srcElement: null,
        target: null, currentTarget: null, timeStamp: 11543, type: "input",
        view: null, which: 0
    };
})();