Greasy Fork is available in English.

Torn Framework

Framework with proper script communication

// ==UserScript==
// @name         Torn Framework
// @version      2.1
// @namespace    http://tampermonkey.net/
// @description  Framework with proper script communication
// @author
// @match        https://www.torn.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        unsafeWindow
// @run-at document-body
// ==/UserScript==

(function () {
    'use strict';

    console.log('=== TORN FRAMEWORK VERSION STARTING ===');

    // Use unsafeWindow to ensure global access
    const globalWindow = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;

    // Prevent multiple instances
    if (globalWindow.TornFramework) {
        console.log('WARNING: TornFramework already exists!', globalWindow.TornFramework);
        return;
    }

    // Global framework object
    globalWindow.TornFramework = {
        version: '2.1-FIXED',
        modules: new Map(),
        initialized: false,
        debug: true
    };

    console.log('Framework object created on globalWindow:', globalWindow.TornFramework);

    // Settings
    let settings = {
        consoleEnabled: GM_getValue('consoleEnabled', true),
        menuVisible: GM_getValue('menuVisible', false)
    };

    function saveSettings() {
        Object.keys(settings).forEach(key => {
            GM_setValue(key, settings[key]);
        });
    }

    // =========================
    // LOGGING SYSTEM
    // =========================
    const logDiv = document.createElement("div");
    logDiv.id = 'torn-framework-console';
    logDiv.style.cssText = `
        position: fixed;
        bottom: 0;
        left: 0;
        width: 450px;
        max-height: 300px;
        overflow-y: auto;
        background: linear-gradient(135deg, rgba(0,0,0,0.95), rgba(20,20,20,0.95));
        color: white;
        font-size: 11px;
        z-index: 999999;
        padding: 12px;
        border-radius: 0 12px 0 0;
        font-family: 'Consolas', 'Monaco', monospace;
        border: 2px solid #37b24d;
        box-shadow: 0 4px 20px rgba(55,178,77,0.3);
        backdrop-filter: blur(10px);
        display: ${settings.consoleEnabled ? 'block' : 'none'};
    `;
    document.body.appendChild(logDiv);

    function log(msg, type = 'info', module = 'FRAMEWORK') {
        try {
            const timestamp = new Date().toLocaleTimeString();
            const p = document.createElement("div");

            const colors = {
                'error': '#ff6b6b',
                'success': '#51cf66',
                'warning': '#ffd43b',
                'info': '#74c0fc',
                'debug': '#9775fa'
            };

            p.style.cssText = `
                color: ${colors[type] || '#ccc'};
                padding: 3px 0;
                border-bottom: 1px solid rgba(255,255,255,0.1);
                font-size: 10px;
                line-height: 1.3;
            `;

            const moduleColor = module === 'FRAMEWORK' ? '#37b24d' : '#f59f00';
            p.innerHTML = `<span style="color: #666;">[${timestamp}]</span> <span style="color: ${moduleColor}; font-weight: bold;">[${module}]</span> ${msg}`;

            logDiv.appendChild(p);
            logDiv.scrollTop = logDiv.scrollHeight;

            // Limit to 100 logs
            while (logDiv.children.length > 100) {
                logDiv.removeChild(logDiv.firstChild);
            }

            console.log(`[TornFramework:${module}] ${msg}`);
        } catch (error) {
            console.error('Framework logging failed:', error);
        }
    }

    globalWindow.TornFramework.log = log;
    log('Framework logging system initialized', 'success');

    // =========================
    // MAIN MENU SYSTEM
    // =========================
    const modMenu = document.createElement("div");
    modMenu.id = 'torn-framework-menu';
    modMenu.style.cssText = `
        position: fixed;
        top: 10px;
        right: 10px;
        width: 420px;
        max-height: 85vh;
        overflow-y: auto;
        background: linear-gradient(135deg, rgba(0,0,0,0.95), rgba(20,20,20,0.95));
        color: white;
        font-size: 12px;
        z-index: 1000000;
        padding: 20px;
        border-radius: 12px;
        font-family: 'Segoe UI', Arial, sans-serif;
        border: 2px solid #37b24d;
        box-shadow: 0 8px 32px rgba(55,178,77,0.4);
        backdrop-filter: blur(10px);
        display: none;
    `;

    modMenu.innerHTML = `
        <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; border-bottom: 2px solid #37b24d; padding-bottom: 15px;">
            <h3 style="margin: 0; color: #37b24d; text-shadow: 0 2px 4px rgba(0,0,0,0.5);">Torn Framework v${globalWindow.TornFramework.version}</h3>
            <button id="closeMenu" style="background: linear-gradient(45deg, #f03e3e, #c92a2a); border: none; color: white; padding: 6px 10px; border-radius: 6px; cursor: pointer; font-size: 12px;">✕</button>
        </div>

        <!-- FRAMEWORK STATUS -->
        <div style="margin-bottom: 15px; padding: 12px; background: linear-gradient(45deg, rgba(55,178,77,0.1), rgba(116,184,22,0.1)); border-radius: 8px; border: 1px solid #37b24d;">
            <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; font-size: 10px; margin-bottom: 8px;">
                <div>Modules: <span id="moduleCount">0</span></div>
                <div>Active: <span id="activeCount">0</span></div>
                <div>Uptime: <span id="uptimeDisplay">0s</span></div>
            </div>
            <div id="moduleList" style="max-height: 80px; overflow-y: auto; font-size: 9px; color: #ccc;"></div>
        </div>

        <!-- FRAMEWORK SETTINGS -->
        <div style="margin-bottom: 15px; border: 1px solid #37b24d; padding: 12px; border-radius: 8px;">
            <h4 style="margin: 0 0 12px 0; color: #37b24d;">Framework Settings</h4>
            <label style="display: flex; align-items: center; cursor: pointer; margin-bottom: 8px;">
                <input type="checkbox" id="consoleEnabled" ${settings.consoleEnabled ? 'checked' : ''}>
                <span style="margin-left: 8px;">Debug Console</span>
            </label>
        </div>

        <!-- MODULE SECTIONS WILL BE INJECTED HERE -->
        <div id="moduleMenuSections"></div>
    `;
    document.body.appendChild(modMenu);

    // Menu toggle button
    const menuBtn = document.createElement("button");
    menuBtn.innerHTML = "⚙️";
    menuBtn.style.cssText = `
        position: fixed;
        top: 10px;
        right: 10px;
        z-index: 999999;
        background: linear-gradient(135deg, #37b24d, #51cf66);
        color: white;
        border: none;
        padding: 12px 14px;
        border-radius: 50%;
        cursor: pointer;
        font-size: 16px;
        box-shadow: 0 4px 16px rgba(55,178,77,0.4);
        transition: all 0.3s ease;
    `;
    menuBtn.onclick = () => {
        const isVisible = modMenu.style.display !== 'none';
        modMenu.style.display = isVisible ? 'none' : 'block';
        menuBtn.style.right = isVisible ? '10px' : '440px';
        if (!isVisible) updateFrameworkStatus();
        settings.menuVisible = !isVisible;
        saveSettings();
    };
    document.body.appendChild(menuBtn);

    // =========================
    // MODULE MANAGEMENT
    // =========================
    globalWindow.TornFramework.registerModule = function (moduleConfig) {
        const {
            name,
            version = '1.0',
            description = '',
            menuSection = null,
            initialize = null,
            cleanup = null,
            isActive = () => false
        } = moduleConfig;

        log(`Module registration attempt: ${name}`, 'info');

        if (!name) {
            log('Module registration failed: name is required', 'error');
            return false;
        }

        if (globalWindow.TornFramework.modules.has(name)) {
            log(`Module ${name} already registered, updating...`, 'warning');
        }

        const module = { name, version, description, menuSection, initialize, cleanup, isActive, registered: Date.now() };
        globalWindow.TornFramework.modules.set(name, module);
        log(`Module registered: ${name} v${version}`, 'success');

        if (menuSection) addModuleMenuSection(name, menuSection);

        if (initialize && typeof initialize === 'function') {
            try {
                initialize();
                log(`Module ${name} initialized`, 'success');
            } catch (error) {
                log(`Module ${name} initialization failed: ${error.message}`, 'error');
            }
        }

        updateFrameworkStatus();
        return true;
    };

    function addModuleMenuSection(moduleName, sectionHTML) {
        const container = document.getElementById('moduleMenuSections');
        if (!container) return;

        const existing = document.getElementById(`module-${moduleName}`);
        if (existing) existing.remove();

        const section = document.createElement('div');
        section.id = `module-${moduleName}`;
        section.style.cssText = 'margin-bottom: 15px; border: 1px solid #f59f00; padding: 12px; border-radius: 8px;';
        section.innerHTML = sectionHTML;

        container.appendChild(section);
        log(`Menu section added for module: ${moduleName}`, 'debug');
    }

    function updateFrameworkStatus() {
        try {
            const moduleCount = globalWindow.TornFramework.modules.size;
            const activeCount = Array.from(globalWindow.TornFramework.modules.values())
                .filter(module => module.isActive()).length;
            const uptime = Math.floor((Date.now() - globalWindow.TornFramework.startTime) / 1000);

            document.getElementById('moduleCount').textContent = moduleCount;
            document.getElementById('activeCount').textContent = activeCount;
            document.getElementById('uptimeDisplay').textContent = `${uptime}s`;

            const moduleList = document.getElementById('moduleList');
            if (moduleList) {
                const moduleInfo = Array.from(globalWindow.TornFramework.modules.values())
                    .map(module => {
                        const status = module.isActive() ? '🟢' : '🔴';
                        return `${status} ${module.name} v${module.version}`;
                    }).join('<br>');
                moduleList.innerHTML = moduleInfo || 'No modules loaded';
            }
        } catch (error) {
            log(`Status update failed: ${error.message}`, 'error');
        }
    }

    // =========================
    // SETTINGS EVENTS
    // =========================
    function setupFrameworkEvents() {
        document.getElementById('closeMenu').onclick = () => {
            modMenu.style.display = 'none';
            menuBtn.style.right = '10px';
            settings.menuVisible = false;
            saveSettings();
        };

        document.getElementById('consoleEnabled').onchange = function () {
            settings.consoleEnabled = this.checked;
            logDiv.style.display = settings.consoleEnabled ? 'block' : 'none';
            saveSettings();
        };
    }

    // =========================
    // INITIALIZATION
    // =========================
    function initFramework() {
        if (globalWindow.TornFramework.initialized) return;

        log('Initializing Torn Framework...', 'success');

        globalWindow.TornFramework.startTime = Date.now();
        globalWindow.TornFramework.initialized = true;

        setupFrameworkEvents();

        if (settings.menuVisible) {
            modMenu.style.display = 'block';
            menuBtn.style.right = '440px';
        }

        log('Framework ready! Modules can now register.', 'success');
        updateFrameworkStatus();

        // Periodically update status
        setInterval(updateFrameworkStatus, 5000);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            log('DOM loaded, initializing framework');
            setTimeout(initFramework, 500);
        });
    } else {
        log('DOM already loaded, initializing framework');
        setTimeout(initFramework, 500);
    }

    log('Framework script completed loading');
})();