BJ's Torn PDA - Notes & To-Do List

A simple, persistent notepad and to-do list in a slide-out panel for Torn PDA.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         BJ's Torn PDA - Notes & To-Do List
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  A simple, persistent notepad and to-do list in a slide-out panel for Torn PDA.
// @author       BazookaJoe
// @match        https://www.torn.com/*
// @require      https://code.jquery.com/jquery-3.7.1.min.js
// ==/UserScript==

/* globals jQuery, $ */

(function() {
    'use-strict';

    // --- 1. CONFIGURATION & STORAGE KEYS ---
    const STORAGE_KEYS = {
        PANEL_OPEN: 'pda_notes_panel_isOpen',
        NOTES_CONTENT: 'pda_notes_content_v1',
        TODO_LIST: 'pda_todo_list_v1'
    };

    let todoItems = []; // This will hold our list of to-do objects

    // --- 2. STYLING ---
    function addStyles() {
        const styles = `
            #pda-notes-container {
                position: fixed;
                top: 70px; /* Position it high on the page */
                right: -320px; /* Start off-screen */
                width: 320px;
                z-index: 10005;
                transition: right 0.3s ease-in-out;
                font-family: Arial, sans-serif;
                color: #333;
            }
            #pda-notes-container.expanded {
                right: 0;
            }
            #pda-notes-panel {
                background: #f7f7f7;
                border: 2px solid #6c757d; /* Grey theme */
                border-right: none;
                padding: 0;
                border-top-left-radius: 8px;
                border-bottom-left-radius: 8px;
                box-shadow: -2px 2px 10px rgba(0,0,0,0.4);
                height: calc(100vh - 90px);
                display: flex;
                flex-direction: column;
            }
            #pda-notes-panel h4 {
                margin: 0; padding: 10px; font-size: 16px;
                background-color: #e9ecef; border-bottom: 1px solid #dee2e6;
                flex-shrink: 0;
            }
            .pda-notes-section {
                padding: 10px;
                border-bottom: 1px solid #dee2e6;
            }
            #pda-notes-textarea {
                width: 100%;
                height: 150px;
                box-sizing: border-box;
                border: 1px solid #ccc;
                border-radius: 4px;
                padding: 8px;
                font-size: 14px;
                resize: vertical;
            }
            #pda-todo-list {
                flex-grow: 1;
                overflow-y: auto;
                min-height: 100px;
                background: #fff;
                border: 1px solid #ccc;
                padding: 5px;
                margin-bottom: 10px;
            }
            .pda-todo-item {
                display: flex;
                align-items: center;
                padding: 6px;
                border-bottom: 1px solid #eee;
            }
            .pda-todo-item:last-child {
                border-bottom: none;
            }
            .pda-todo-item input[type="checkbox"] {
                margin-right: 10px;
            }
            .pda-todo-item.completed span {
                text-decoration: line-through;
                color: #888;
            }
            #pda-todo-input-area { display: flex; gap: 5px; margin-bottom: 10px; }
            #pda-todo-new-item { flex-grow: 1; padding: 5px; border: 1px solid #ccc; border-radius: 3px; }
            .pda-notes-button {
                padding: 5px 10px; border: 1px solid #999;
                background-color: #ddd; border-radius: 3px; cursor: pointer;
            }

            #pda-notes-toggle {
                position: fixed;
                top: 70px; /* Match panel top */
                right: 0;
                width: 35px;
                height: 50px;
                background-color: #6c757d;
                color: white;
                border: 2px solid #6c757d;
                border-right: none;
                border-top-left-radius: 8px;
                border-bottom-left-radius: 8px;
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                font-weight: bold;
                font-size: 16px;
                z-index: 10006;
            }
        `;
        const styleEl = document.createElement('style');
        styleEl.textContent = styles;
        document.head.appendChild(styleEl);
    }

    // --- 3. CORE LOGIC ---

    function loadData() {
        // Load panel state
        if (localStorage.getItem(STORAGE_KEYS.PANEL_OPEN) === 'true') {
            $('#pda-notes-container').addClass('expanded');
            $('#pda-notes-toggle').text('»');
        }

        // Load notes
        const savedNotes = localStorage.getItem(STORAGE_KEYS.NOTES_CONTENT);
        if (savedNotes) {
            $('#pda-notes-textarea').val(savedNotes);
        }

        // Load To-Do list
        const savedTodos = localStorage.getItem(STORAGE_KEYS.TODO_LIST);
        todoItems = savedTodos ? JSON.parse(savedTodos) : [];
        renderTodoList();
    }

    function renderTodoList() {
        const listContainer = $('#pda-todo-list');
        listContainer.empty(); // Clear existing items before re-rendering

        if (todoItems.length === 0) {
            listContainer.html('<div style="color: #888; text-align: center; padding: 20px;">No to-do items yet.</div>');
            return;
        }

        todoItems.forEach((item, index) => {
            const itemEl = $(`
                <div class="pda-todo-item ${item.completed ? 'completed' : ''}" data-index="${index}">
                    <input type="checkbox" ${item.completed ? 'checked' : ''}>
                    <span></span>
                </div>
            `);
            itemEl.find('span').text(item.text); // Use .text() to prevent HTML injection
            listContainer.append(itemEl);
        });
    }

    function saveTodoList() {
        localStorage.setItem(STORAGE_KEYS.TODO_LIST, JSON.stringify(todoItems));
    }

    // --- 4. UI CREATION & EVENT HANDLERS ---
    function createPanel() {
        if (document.getElementById('pda-notes-container')) return;

        const panelHTML = `
            <div id="pda-notes-container">
                <div id="pda-notes-panel">
                    <div class="pda-notes-section">
                        <h4>Notes</h4>
                        <textarea id="pda-notes-textarea" placeholder="Your notes here..."></textarea>
                    </div>
                    <div class="pda-notes-section" style="display:flex; flex-direction:column; flex-grow: 1;">
                        <h4>To-Do List</h4>
                        <div id="pda-todo-list"></div>
                        <div id="pda-todo-input-area">
                            <input type="text" id="pda-todo-new-item" placeholder="Add a new task...">
                            <button id="pda-todo-add-btn" class="pda-notes-button">Add</button>
                        </div>
                        <button id="pda-todo-clear-btn" class="pda-notes-button">Clear Checked Items</button>
                    </div>
                </div>
            </div>
        `;
        const toggleButtonHTML = `<div id="pda-notes-toggle">N</div>`;

        $('body').append(panelHTML).append(toggleButtonHTML);

        // --- Event Handlers ---
        // Toggle panel visibility
        $('#pda-notes-toggle').on('click', function() {
            const container = $('#pda-notes-container');
            const isOpen = container.toggleClass('expanded').hasClass('expanded');
            $(this).text(isOpen ? '»' : 'N');
            localStorage.setItem(STORAGE_KEYS.PANEL_OPEN, isOpen);
        });

        // Save notes on input
        $('#pda-notes-textarea').on('input', function() {
            localStorage.setItem(STORAGE_KEYS.NOTES_CONTENT, $(this).val());
        });

        // Add a to-do item
        $('#pda-todo-add-btn').on('click', function() {
            const inputEl = $('#pda-todo-new-item');
            const newText = inputEl.val().trim();
            if (newText) {
                todoItems.push({ text: newText, completed: false });
                saveTodoList();
                renderTodoList();
                inputEl.val('').focus();
            }
        });
        // Allow adding with Enter key
        $('#pda-todo-new-item').on('keypress', function(e) {
            if (e.which === 13) { // Enter key
                $('#pda-todo-add-btn').click();
            }
        });


        // Toggle completion status of a to-do item
        $('#pda-todo-list').on('change', 'input[type="checkbox"]', function() {
            const itemDiv = $(this).closest('.pda-todo-item');
            const itemIndex = itemDiv.data('index');
            if (typeof itemIndex !== 'undefined' && todoItems[itemIndex]) {
                todoItems[itemIndex].completed = this.checked;
                itemDiv.toggleClass('completed', this.checked);
                saveTodoList();
            }
        });

        // Clear checked items
        $('#pda-todo-clear-btn').on('click', function() {
            todoItems = todoItems.filter(item => !item.completed);
            saveTodoList();
            renderTodoList();
        });
    }

    // --- 5. INITIALIZATION ---
    function main() {
        addStyles();
        createPanel();
        loadData();
    }

    // Use a timeout to ensure the Torn page is fully loaded before the script runs
    setTimeout(main, 500);

})();