Green Tools - Script Manager

Green Tools: Script Manager with Speed & Jump Boost for Narrow One

// ==UserScript==
// @name         Green Tools - Script Manager
// @namespace    http://tampermonkey.net/
// @version      3.3
// @description  Green Tools: Script Manager with Speed & Jump Boost for Narrow One
// @author       Green Tools
// @match        https://narrow.one/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    function getPassword() {
        const part1 = [71, 114, 101, 101, 110]; 
        const part2 = [80, 64]; 
        const part3 = [115, 115, 119, 111, 114, 100]; 

        return part1.map(c => String.fromCharCode(c)).join('') +
               part2.map(c => String.fromCharCode(c)).join('') +
               part3.map(c => String.fromCharCode(c)).join('');
    }

    const validPassword = getPassword();
    let isSpeedBoostActive = false;
    let isJumpBoostActive = false;
    let savedScripts = GM_getValue('savedScripts', {});
    
    function getRandomColor() {
        const colors = [
            'rgba(255, 99, 132, 0.9)', 'rgba(54, 162, 235, 0.9)', 'rgba(255, 206, 86, 0.9)',
            'rgba(75, 192, 192, 0.9)', 'rgba(153, 102, 255, 0.9)', 'rgba(255, 159, 64, 0.9)',
            'rgba(199, 199, 199, 0.9)', 'rgba(83, 102, 255, 0.9)', 'rgba(40, 159, 64, 0.9)',
            'rgba(210, 105, 30, 0.9)', 'rgba(139, 69, 19, 0.9)', 'rgba(0, 128, 128, 0.9)'
        ];
        return colors[Math.floor(Math.random() * colors.length)];
    }
    
    function createKeyOverlay() {
        const overlay = document.createElement('div');
        overlay.id = 'keyOverlay';
        overlay.style.position = 'fixed';
        overlay.style.top = '0';
        overlay.style.left = '0';
        overlay.style.width = '100%';
        overlay.style.height = '100%';
        overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
        overlay.style.zIndex = '99999';
        overlay.style.display = 'flex';
        overlay.style.alignItems = 'center';
        overlay.style.justifyContent = 'center';
        overlay.style.color = 'white';
        overlay.style.fontSize = 'clamp(20px, 6vw, 30px)'; // 响应式字体
        overlay.style.fontWeight = 'bold';
        overlay.style.touchAction = 'manipulation'; // 改善触摸响应

        const randomColor = getRandomColor();

        overlay.innerHTML = `
            <div style="text-align: center; background: ${randomColor}; padding: 20px; border-radius: 10px; box-shadow: 0 0 20px rgba(255,255,255,0.3); max-width: 90vw; width: 400px;">
                <h1 style="margin-bottom: 20px; font-size: clamp(18px, 5vw, 24px);">Enter the Password:</h1>
                <input id="keyInput" type="password" placeholder="Enter Password" style="font-size: clamp(16px, 4vw, 20px); padding: 12px; margin: 10px; border: 2px solid white; border-radius: 5px; background: rgba(255,255,255,0.1); color: white; width: 100%; box-sizing: border-box;"/>
                <br>
                <button id="submitKey" style="font-size: clamp(16px, 4vw, 20px); padding: 12px 24px; margin: 10px; border: none; border-radius: 5px; background: white; color: black; cursor: pointer; min-height: 44px;">Submit</button>
                <div id="errorMessage" style="color: yellow; font-size: clamp(16px, 4vw, 20px); margin-top: 10px;"></div>
            </div>
        `;
        document.body.appendChild(overlay);
        
        // 添加触摸事件支持
        const keyInput = document.getElementById('keyInput');
        keyInput.addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                verifyPassword();
            }
        });
        
        // 确保输入框在iPad上能正常获得焦点
        setTimeout(() => {
            keyInput.focus();
        }, 500);
    }
    
    function verifyPassword() {
        const userInput = document.getElementById('keyInput').value;
        if (userInput === validPassword) {
            document.getElementById('keyOverlay').style.display = 'none';
            console.log('Password valid. Green Tools activated.');
            createFloatingWindow();
        } else {
            document.getElementById('errorMessage').innerText = 'Invalid password, please try again.';
            // 重新聚焦输入框
            setTimeout(() => {
                document.getElementById('keyInput').focus();
            }, 100);
        }
    }

    function initPasswordSystem() {
        const submitKey = document.getElementById('submitKey');
        if (submitKey) {
            submitKey.addEventListener('click', verifyPassword);
            // 添加触摸事件
            submitKey.addEventListener('touchend', function(e) {
                e.preventDefault();
                verifyPassword();
            });
        }
    }

    function createFloatingWindow() {
        const floatingWindow = document.createElement('div');
        floatingWindow.id = 'greenToolsWindow';
        floatingWindow.style.position = 'fixed';
        floatingWindow.style.top = '20px';
        floatingWindow.style.right = '20px';
        floatingWindow.style.width = 'min(350px, 90vw)'; // 响应式宽度
        floatingWindow.style.backgroundColor = 'rgba(0, 100, 0, 0.9)';
        floatingWindow.style.border = '2px solid #00ff00';
        floatingWindow.style.borderRadius = '10px';
        floatingWindow.style.boxShadow = '0 0 20px rgba(0, 255, 0, 0.5)';
        floatingWindow.style.zIndex = '10000';
        floatingWindow.style.fontFamily = 'Arial, sans-serif';
        floatingWindow.style.color = 'white';
        floatingWindow.style.overflow = 'hidden';
        floatingWindow.style.touchAction = 'none'; // 防止浏览器处理触摸事件

        floatingWindow.innerHTML = `
            <div style="background: rgba(0, 80, 0, 0.9); padding: 15px; display: flex; justify-content: space-between; align-items: center; cursor: move; border-bottom: 1px solid #00ff00; min-height: 44px;">
                <div style="font-weight: bold; font-size: clamp(14px, 4vw, 16px);">Green Tools</div>
                <div>
                    <button id="minimizeBtn" style="background: transparent; border: none; color: white; cursor: pointer; margin-right: 10px; font-size: 18px; min-width: 44px; min-height: 44px;">−</button>
                    <button id="closeBtn" style="background: transparent; border: none; color: white; cursor: pointer; font-size: 18px; min-width: 44px; min-height: 44px;">×</button>
                </div>
            </div>
            <div id="windowContent" style="padding: 15px; max-height: 60vh; overflow-y: auto;">
                <div style="margin-bottom: 15px;">
                    <h3 style="margin-top: 0; border-bottom: 1px solid #00ff00; padding-bottom: 5px; font-size: clamp(14px, 4vw, 16px);">Built-in Features</h3>
                    <div style="display: flex; justify-content: space-between; margin-bottom: 15px; align-items: center;">
                        <span style="font-size: clamp(12px, 3.5vw, 14px);">Speed Boost</span>
                        <button id="speedToggle" style="background: #ff4444; border: none; border-radius: 15px; width: 60px; height: 30px; position: relative; cursor: pointer; min-height: 30px;">
                            <div style="position: absolute; top: 2px; left: 2px; width: 26px; height: 26px; background: white; border-radius: 50%; transition: left 0.3s;"></div>
                        </button>
                    </div>
                    <div style="display: flex; justify-content: space-between; margin-bottom: 15px; align-items: center;">
                        <span style="font-size: clamp(12px, 3.5vw, 14px);">Jump Boost</span>
                        <button id="jumpToggle" style="background: #ff4444; border: none; border-radius: 15px; width: 60px; height: 30px; position: relative; cursor: pointer; min-height: 30px;">
                            <div style="position: absolute; top: 2px; left: 2px; width: 26px; height: 26px; background: white; border-radius: 50%; transition: left 0.3s;"></div>
                        </button>
                    </div>
                </div>

                <div style="margin-bottom: 15px;">
                    <h3 style="border-bottom: 1px solid #00ff00; padding-bottom: 5px; font-size: clamp(14px, 4vw, 16px);">Custom Scripts</h3>
                    <textarea id="scriptInput" placeholder="Paste your script here..." style="width: 100%; height: 100px; background: rgba(255,255,255,0.1); color: white; border: 1px solid #00ff00; border-radius: 5px; padding: 10px; margin-bottom: 10px; resize: vertical; font-size: clamp(12px, 3.5vw, 14px); box-sizing: border-box;"></textarea>
                    <div style="display: flex; justify-content: space-between; gap: 5px; flex-wrap: wrap;">
                        <button id="runScript" style="background: #00aa00; border: none; border-radius: 5px; color: white; padding: 10px; cursor: pointer; flex: 1; min-height: 44px; font-size: clamp(12px, 3.5vw, 14px);">Run</button>
                        <button id="saveScript" style="background: #0088cc; border: none; border-radius: 5px; color: white; padding: 10px; cursor: pointer; flex: 1; min-height: 44px; font-size: clamp(12px, 3.5vw, 14px);">Save</button>
                        <input type="text" id="scriptName" placeholder="Script name" style="background: rgba(255,255,255,0.1); color: white; border: 1px solid #00ff00; border-radius: 5px; padding: 10px; flex: 1; min-width: 100px; font-size: clamp(12px, 3.5vw, 14px); box-sizing: border-box;">
                    </div>
                </div>

                <div>
                    <h3 style="border-bottom: 1px solid #00ff00; padding-bottom: 5px; font-size: clamp(14px, 4vw, 16px);">Saved Scripts</h3>
                    <div id="savedScriptsList" style="max-height: 200px; overflow-y: auto;">
                        <!-- Saved scripts will be listed here -->
                    </div>
                </div>
            </div>
        `;

        document.body.appendChild(floatingWindow);
        makeDraggable(floatingWindow);

        // 为所有按钮添加触摸事件支持
        const addTouchSupport = (element, handler) => {
            element.addEventListener('click', handler);
            element.addEventListener('touchend', function(e) {
                e.preventDefault();
                handler();
            });
        };

        addTouchSupport(document.getElementById('speedToggle'), toggleSpeedBoost);
        addTouchSupport(document.getElementById('jumpToggle'), toggleJumpBoost);
        addTouchSupport(document.getElementById('runScript'), runCustomScript);
        addTouchSupport(document.getElementById('saveScript'), saveCustomScript);
        addTouchSupport(document.getElementById('minimizeBtn'), toggleMinimize);
        addTouchSupport(document.getElementById('closeBtn'), closeWindow);

        loadSavedScripts();
    }

    function makeDraggable(element) {
        const header = element.querySelector('div');
        let isDragging = false;
        let currentX, currentY, initialX, initialY, xOffset = 0, yOffset = 0;

        const dragStart = (clientX, clientY) => {
            initialX = clientX - xOffset;
            initialY = clientY - yOffset;
            isDragging = true;
        };

        const dragEnd = () => {
            initialX = currentX;
            initialY = currentY;
            isDragging = false;
        };

        const drag = (clientX, clientY) => {
            if (isDragging) {
                currentX = clientX - initialX;
                currentY = clientY - initialY;
                xOffset = currentX;
                yOffset = currentY;
                setTranslate(currentX, currentY, element);
            }
        };

        const setTranslate = (xPos, yPos, el) => {
            el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
        };

        // 鼠标事件
        header.addEventListener("mousedown", (e) => {
            dragStart(e.clientX, e.clientY);
        });

        document.addEventListener("mouseup", dragEnd);
        document.addEventListener("mousemove", (e) => {
            drag(e.clientX, e.clientY);
        });

        // 触摸事件
        header.addEventListener("touchstart", (e) => {
            e.preventDefault();
            const touch = e.touches[0];
            dragStart(touch.clientX, touch.clientY);
        }, { passive: false });

        document.addEventListener("touchend", dragEnd);
        document.addEventListener("touchmove", (e) => {
            if (isDragging) {
                e.preventDefault();
                const touch = e.touches[0];
                drag(touch.clientX, touch.clientY);
            }
        }, { passive: false });
    }

    // 其余函数保持不变(toggleSpeedBoost, applySpeedBoost, removeSpeedBoost, toggleJumpBoost, applyJumpBoost, removeJumpBoost, runCustomScript, saveCustomScript, loadSavedScripts, toggleMinimize, closeWindow, showNotification)
    function toggleSpeedBoost() {
        const toggle = document.getElementById('speedToggle');
        const toggleCircle = toggle.querySelector('div');

        if (!isSpeedBoostActive) {
            toggle.style.background = '#44ff44';
            toggleCircle.style.left = '32px';
            applySpeedBoost();
            isSpeedBoostActive = true;
            showNotification('Speed Boost Activated');
        } else {
            toggle.style.background = '#ff4444';
            toggleCircle.style.left = '2px';
            removeSpeedBoost();
            isSpeedBoostActive = false;
            showNotification('Speed Boost Deactivated');
        }
    }

    function applySpeedBoost() {
        const targetWalkSpeed = 115;

        Object.defineProperty(Object.prototype, 'walkSpeed', {
            get() {
                return this._walkSpeed || targetWalkSpeed;
            },
            set(value) {
                this._walkSpeed = targetWalkSpeed;
                console.log('walkSpeed set to ' + this._walkSpeed);
            },
            configurable: true
        });

        Object.defineProperty(Object.prototype, 'flagWalkSpeed', {
            get() {
                return this._flagWalkSpeed || targetWalkSpeed;
            },
            set(value) {
                this._flagWalkSpeed = targetWalkSpeed;
                console.log('flagWalkSpeed set to ' + this._flagWalkSpeed);
            },
            configurable: true
        });
    }

    function removeSpeedBoost() {
        delete Object.prototype.walkSpeed;
        delete Object.prototype.flagWalkSpeed;
    }

    function toggleJumpBoost() {
        const toggle = document.getElementById('jumpToggle');
        const toggleCircle = toggle.querySelector('div');

        if (!isJumpBoostActive) {
            toggle.style.background = '#44ff44';
            toggleCircle.style.left = '32px';
            applyJumpBoost();
            isJumpBoostActive = true;
            showNotification('Jump Boost Activated');
        } else {
            toggle.style.background = '#ff4444';
            toggleCircle.style.left = '2px';
            removeJumpBoost();
            isJumpBoostActive = false;
            showNotification('Jump Boost Deactivated');
        }
    }

    function applyJumpBoost() {
        const targetJumpForce = 20;

        Object.defineProperty(Object.prototype, 'jumpForce', {
            get() {
                return this._jumpForce || targetJumpForce;
            },
            set(value) {
                this._jumpForce = targetJumpForce;
                console.log('jumpForce set to ' + this._jumpForce);
            },
            configurable: true
        });
    }

    function removeJumpBoost() {
        delete Object.prototype.jumpForce;
    }

    function runCustomScript() {
        const scriptCode = document.getElementById('scriptInput').value;
        if (scriptCode.trim()) {
            try {
                eval(scriptCode);
                showNotification('Custom script executed');
            } catch (error) {
                showNotification('Error in script: ' + error.message, true);
            }
        } else {
            showNotification('Please enter a script', true);
        }
    }

    function saveCustomScript() {
        const scriptCode = document.getElementById('scriptInput').value;
        const scriptName = document.getElementById('scriptName').value;

        if (scriptCode.trim() && scriptName.trim()) {
            savedScripts[scriptName] = scriptCode;
            GM_setValue('savedScripts', savedScripts);
            loadSavedScripts();
            showNotification('Script saved: ' + scriptName);
            document.getElementById('scriptName').value = '';
        } else {
            showNotification('Please enter both script and name', true);
        }
    }

    function loadSavedScripts() {
        const savedScriptsList = document.getElementById('savedScriptsList');
        savedScriptsList.innerHTML = '';

        for (const name in savedScripts) {
            const scriptItem = document.createElement('div');
            scriptItem.style.display = 'flex';
            scriptItem.style.justifyContent = 'space-between';
            scriptItem.style.alignItems = 'center';
            scriptItem.style.marginBottom = '8px';
            scriptItem.style.padding = '8px';
            scriptItem.style.backgroundColor = 'rgba(255,255,255,0.1)';
            scriptItem.style.borderRadius = '5px';

            scriptItem.innerHTML = `
                <span style="font-size: clamp(12px, 3.5vw, 14px);">${name}</span>
                <div>
                    <button class="run-saved-script" data-name="${name}" style="background: #00aa00; border: none; border-radius: 5px; color: white; padding: 8px 12px; margin-right: 5px; cursor: pointer; font-size: clamp(11px, 3vw, 12px); min-height: 36px;">Run</button>
                    <button class="delete-script" data-name="${name}" style="background: #ff4444; border: none; border-radius: 5px; color: white; padding: 8px 12px; cursor: pointer; font-size: clamp(11px, 3vw, 12px); min-height: 36px;">Delete</button>
                </div>
            `;

            savedScriptsList.appendChild(scriptItem);
        }

        // 为保存的脚本按钮添加触摸支持
        document.querySelectorAll('.run-saved-script').forEach(button => {
            button.addEventListener('click', function() {
                const scriptName = this.getAttribute('data-name');
                try {
                    eval(savedScripts[scriptName]);
                    showNotification('Script executed: ' + scriptName);
                } catch (error) {
                    showNotification('Error in script: ' + error.message, true);
                }
            });
            button.addEventListener('touchend', function(e) {
                e.preventDefault();
                const scriptName = this.getAttribute('data-name');
                try {
                    eval(savedScripts[scriptName]);
                    showNotification('Script executed: ' + scriptName);
                } catch (error) {
                    showNotification('Error in script: ' + error.message, true);
                }
            });
        });

        document.querySelectorAll('.delete-script').forEach(button => {
            button.addEventListener('click', function() {
                const scriptName = this.getAttribute('data-name');
                delete savedScripts[scriptName];
                GM_setValue('savedScripts', savedScripts);
                loadSavedScripts();
                showNotification('Script deleted: ' + scriptName);
            });
            button.addEventListener('touchend', function(e) {
                e.preventDefault();
                const scriptName = this.getAttribute('data-name');
                delete savedScripts[scriptName];
                GM_setValue('savedScripts', savedScripts);
                loadSavedScripts();
                showNotification('Script deleted: ' + scriptName);
            });
        });
    }

    function toggleMinimize() {
        const content = document.getElementById('windowContent');
        if (content.style.display === 'none') {
            content.style.display = 'block';
        } else {
            content.style.display = 'none';
        }
    }

    function closeWindow() {
        document.getElementById('greenToolsWindow').style.display = 'none';
        showNotification('Green Tools minimized to system tray');
        
        const reopenBtn = document.createElement('div');
        reopenBtn.innerHTML = 'GT';
        reopenBtn.style.position = 'fixed';
        reopenBtn.style.bottom = '20px';
        reopenBtn.style.right = '20px';
        reopenBtn.style.width = '50px';
        reopenBtn.style.height = '50px';
        reopenBtn.style.backgroundColor = 'rgba(0, 100, 0, 0.9)';
        reopenBtn.style.border = '2px solid #00ff00';
        reopenBtn.style.borderRadius = '50%';
        reopenBtn.style.display = 'flex';
        reopenBtn.style.alignItems = 'center';
        reopenBtn.style.justifyContent = 'center';
        reopenBtn.style.color = 'white';
        reopenBtn.style.fontSize = '16px';
        reopenBtn.style.fontWeight = 'bold';
        reopenBtn.style.cursor = 'pointer';
        reopenBtn.style.zIndex = '10000';
        reopenBtn.style.touchAction = 'manipulation';
        reopenBtn.id = 'greenToolsReopen';

        const reopenHandler = function() {
            document.getElementById('greenToolsWindow').style.display = 'block';
            this.remove();
        };

        reopenBtn.addEventListener('click', reopenHandler);
        reopenBtn.addEventListener('touchend', function(e) {
            e.preventDefault();
            reopenHandler.call(this);
        });

        document.body.appendChild(reopenBtn);
    }

    function showNotification(message, isError = false) {
        const notification = document.createElement('div');
        notification.textContent = message;
        notification.style.position = 'fixed';
        notification.style.top = '20px';
        notification.style.left = '50%';
        notification.style.transform = 'translateX(-50%)';
        notification.style.backgroundColor = isError ? 'rgba(255, 0, 0, 0.8)' : 'rgba(0, 255, 0, 0.8)';
        notification.style.color = 'white';
        notification.style.padding = '15px 25px';
        notification.style.borderRadius = '8px';
        notification.style.zIndex = '10001';
        notification.style.fontSize = 'clamp(14px, 4vw, 16px)';
        notification.style.fontWeight = 'bold';
        notification.style.boxShadow = '0 0 15px rgba(0,0,0,0.5)';
        notification.style.textAlign = 'center';
        notification.style.maxWidth = '80vw';

        document.body.appendChild(notification);

        setTimeout(() => {
            if (document.body.contains(notification)) {
                document.body.removeChild(notification);
            }
        }, 3000);
    }

    // 改进的初始化函数
    function init() {
        console.log(`
  ____                                  _____                   _
 / ___|  _ __    ___    ___   _ __     |_   _|   ___     ___   | |  ___
| |  _  | '__|  / _ \\  / _ \\ | '_ \\      | |    / _ \\   / _ \\  | | / __|
| |_| | | |    |  __/ |  __/ | | | |     | |   | (_) | | (_) | | | \\__ \\
 \\____| |_|     \\___|  \\___| |_| |_|     |_|    \\___/   \\___/  |_| |___/

Green Tools - Script Manager for Narrow One
    `);
        
        // 使用更可靠的初始化方法
        const initInterval = setInterval(() => {
            if (document.body) {
                clearInterval(initInterval);
                createKeyOverlay();
                initPasswordSystem();
                
                // 确保密码输入框获得焦点
                setTimeout(() => {
                    const keyInput = document.getElementById('keyInput');
                    if (keyInput) {
                        keyInput.focus();
                    }
                }, 1000);
            }
        }, 100);
        
        // 10秒后停止检查
        setTimeout(() => {
            clearInterval(initInterval);
        }, 10000);
    }

    // 启动脚本
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        setTimeout(init, 100);
    }
})();