Drawaria Word Office - Text Editor

Word-style text editor: format text, insert images/tables, auto-save, all from the userscript menu.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name		Drawaria Word Office - Text Editor
// @description Word-style text editor: format text, insert images/tables, auto-save, all from the userscript menu.
// @version		1.0
// @match		https://drawaria.online/*
// @match		https://*.drawaria.online/*
// @icon		https://drawaria.online/favicon-32x32.png
// @grant       GM.setValue
// @grant       GM.getValue
// @grant       GM_registerMenuCommand
// @license MIT
// @namespace https://greasyfork.org/users/1088100
// ==/UserScript==
(function() {
    'use strict';

    console.log('Drawaria Word Processor initializing...');

    // Debounce function for performance optimization
    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    // Function to create the Word-like text editor
    function createWordEditor() {
        console.log('Creating Word editor interface...');

        // Create main editor container
        const editorContainer = document.createElement('div');
        editorContainer.id = 'word-editor-container';
        editorContainer.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            width: 900px;
            height: 700px;
            background: #ffffff;
            border: 2px solid #d1d5db;
            border-radius: 12px;
            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
            z-index: 10000;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            display: flex;
            flex-direction: column;
            resize: both;
            overflow: hidden;
            min-width: 600px;
            min-height: 400px;
        `;

        // Create title bar
        const titleBar = document.createElement('div');
        titleBar.id = 'word-editor-titlebar';
        titleBar.style.cssText = `
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 12px 20px;
            font-weight: 600;
            font-size: 16px;
            border-radius: 10px 10px 0 0;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: move;
        `;
        titleBar.innerHTML = `
            <span>📝 Drawaria Word Office</span>
            <div>
                <button id="minimize-editor" style="background: rgba(255,255,255,0.2); border: none; color: white; padding: 4px 8px; border-radius: 4px; margin-right: 8px; cursor: pointer;">−</button>
                <button id="close-editor" style="background: rgba(255,255,255,0.2); border: none; color: white; padding: 4px 8px; border-radius: 4px; cursor: pointer;">×</button>
            </div>
        `;

        // Create toolbar
        const toolbar = document.createElement('div');
        toolbar.id = 'word-editor-toolbar';
        toolbar.style.cssText = `
            background: #f8fafc;
            border-bottom: 1px solid #e2e8f0;
            padding: 12px;
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            align-items: center;
        `;

        // Toolbar HTML with comprehensive formatting options
        toolbar.innerHTML = `
            <div style="display: flex; gap: 4px; align-items: center; margin-right: 12px;">
                <button class="toolbar-btn" data-command="undo" title="Undo">↶</button>
                <button class="toolbar-btn" data-command="redo" title="Redo">↷</button>
            </div>

            <div style="width: 1px; height: 24px; background: #cbd5e0; margin: 0 8px;"></div>

            <div style="display: flex; gap: 4px; align-items: center; margin-right: 12px;">
                <select id="font-family" style="padding: 4px 8px; border: 1px solid #cbd5e0; border-radius: 4px; font-size: 12px;">
                    <option value="Arial">Arial</option>
                    <option value="Times New Roman">Times New Roman</option>
                    <option value="Helvetica">Helvetica</option>
                    <option value="Georgia">Georgia</option>
                    <option value="Verdana">Verdana</option>
                    <option value="Courier New">Courier New</option>
                    <option value="Comic Sans MS">Comic Sans MS</option>
                </select>

                <select id="font-size" style="padding: 4px 8px; border: 1px solid #cbd5e0; border-radius: 4px; font-size: 12px; width: 60px;">
                    <option value="1">8</option>
                    <option value="2">10</option>
                    <option value="3" selected>12</option>
                    <option value="4">14</option>
                    <option value="5">18</option>
                    <option value="6">24</option>
                    <option value="7">36</option>
                </select>
            </div>

            <div style="width: 1px; height: 24px; background: #cbd5e0; margin: 0 8px;"></div>

            <div style="display: flex; gap: 4px; align-items: center; margin-right: 12px;">
                <button class="toolbar-btn format-btn" data-command="bold" title="Bold"><b>B</b></button>
                <button class="toolbar-btn format-btn" data-command="italic" title="Italic"><i>I</i></button>
                <button class="toolbar-btn format-btn" data-command="underline" title="Underline"><u>U</u></button>
                <button class="toolbar-btn format-btn" data-command="strikeThrough" title="Strikethrough"><s>S</s></button>
            </div>

            <div style="width: 1px; height: 24px; background: #cbd5e0; margin: 0 8px;"></div>

            <div style="display: flex; gap: 4px; align-items: center; margin-right: 12px;">
                <input type="color" id="text-color" value="#000000" title="Text Color" style="width: 32px; height: 28px; border: 1px solid #cbd5e0; border-radius: 4px; cursor: pointer;">
                <input type="color" id="bg-color" value="#ffffff" title="Highlight Color" style="width: 32px; height: 28px; border: 1px solid #cbd5e0; border-radius: 4px; cursor: pointer;">
            </div>

            <div style="width: 1px; height: 24px; background: #cbd5e0; margin: 0 8px;"></div>

            <div style="display: flex; gap: 4px; align-items: center; margin-right: 12px;">
                <button class="toolbar-btn format-btn" data-command="justifyLeft" title="Align Left">⬅</button>
                <button class="toolbar-btn format-btn" data-command="justifyCenter" title="Center">⬌</button>
                <button class="toolbar-btn format-btn" data-command="justifyRight" title="Align Right">➡</button>
                <button class="toolbar-btn format-btn" data-command="justifyFull" title="Justify">⬍</button>
            </div>

            <div style="width: 1px; height: 24px; background: #cbd5e0; margin: 0 8px;"></div>

            <div style="display: flex; gap: 4px; align-items: center; margin-right: 12px;">
                <button class="toolbar-btn" data-command="insertUnorderedList" title="Bullet List">• List</button>
                <button class="toolbar-btn" data-command="insertOrderedList" title="Numbered List">1. List</button>
                <button class="toolbar-btn" data-command="outdent" title="Decrease Indent">⬅ Indent</button>
                <button class="toolbar-btn" data-command="indent" title="Increase Indent">Indent ➡</button>
            </div>

            <div style="width: 1px; height: 24px; background: #cbd5e0; margin: 0 8px;"></div>

            <div style="display: flex; gap: 4px; align-items: center;">
                <button class="toolbar-btn" id="insert-table" title="Insert Table">📊 Table</button>
                <button class="toolbar-btn" id="insert-image" title="Insert Image">🖼️ Image</button>
                <button class="toolbar-btn" id="insert-link" title="Insert Link">🔗 Link</button>
            </div>

            <div style="width: 1px; height: 24px; background: #cbd5e0; margin: 0 8px;"></div>

            <div style="display: flex; gap: 4px; align-items: center;">
                <button class="toolbar-btn" id="save-file" title="Save Document">💾 Save</button>
                <button class="toolbar-btn" id="load-file" title="Load Document">📂 Load</button>
            </div>
        `;

        // Create editor content area
        const editorContent = document.createElement('div');
        editorContent.style.cssText = `
            flex: 1;
            display: flex;
            background: #f1f5f9;
            overflow: hidden;
        `;

        // Create document area (like Word's page view)
        const documentArea = document.createElement('div');
        documentArea.id = 'document-area';
        documentArea.style.cssText = `
            flex: 1;
            padding: 20px;
            overflow-y: auto;
            background: #f1f5f9;
            display: flex;
            flex-direction: column;
            align-items: center;
        `;

        // Create the actual document (paper-like)
        const documentElement = document.createElement('div');
        documentElement.contentEditable = true;
        documentElement.id = 'word-document';
        documentElement.style.cssText = `
            background: white;
            min-height: 800px;
            padding: 60px 80px;
            margin: 0 auto;
            width: 100%;
            max-width: 700px;
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
            border-radius: 8px;
            font-family: 'Times New Roman', serif;
            font-size: 12pt;
            line-height: 1.6;
            color: #1f2937;
            outline: none;
            position: relative;
        `;
        documentElement.innerHTML = `
            <h1 style="text-align: center; margin-bottom: 30px; color: #1f2937;">Document Title</h1>
            <p>Start typing your document here. This is a fully functional Microsoft Word-like editor with support for:</p>
            <ul>
                <li>Rich text formatting (bold, italic, underline)</li>
                <li>Font selection and sizing</li>
                <li>Text alignment and indentation</li>
                <li>Bullet points and numbered lists</li>
                <li>Tables and images</li>
                <li>Real-time editing</li>
            </ul>
            <p>Click anywhere to start editing!</p>
        `;

        // Create status bar
        const statusBar = document.createElement('div');
        statusBar.id = 'word-editor-status-bar';
        statusBar.style.cssText = `
            background: #f8fafc;
            border-top: 1px solid #e2e8f0;
            padding: 8px 20px;
            font-size: 12px;
            color: #64748b;
            display: flex;
            justify-content: space-between;
            align-items: center;
        `;
        statusBar.innerHTML = `
            <div id="status-info">Ready</div>
            <div id="document-stats">Words: 0 | Characters: 0</div>
        `;

        // Assemble the editor
        documentArea.appendChild(documentElement);
        editorContent.appendChild(documentArea);
        editorContainer.appendChild(titleBar);
        editorContainer.appendChild(toolbar);
        editorContainer.appendChild(editorContent);
        editorContainer.appendChild(statusBar);

        // Add CSS styles for toolbar buttons
        const style = document.createElement('style');
        style.textContent = `
            .toolbar-btn {
                background: white;
                border: 1px solid #cbd5e0;
                padding: 6px 12px;
                border-radius: 4px;
                cursor: pointer;
                font-size: 12px;
                font-weight: 500;
                color: #374151;
                transition: all 0.2s;
            }

            .toolbar-btn:hover {
                background: #f3f4f6;
                border-color: #9ca3af;
            }

            .toolbar-btn:active, .toolbar-btn.active {
                background: #e5e7eb;
                border-color: #6b7280;
            }

            .format-btn.active {
                background: #3b82f6;
                color: white;
                border-color: #2563eb;
            }

            #word-document:focus {
                box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
            }

            #word-document table {
                border-collapse: collapse;
                width: 100%;
                margin: 10px 0;
            }

            #word-document table td, #word-document table th {
                border: 1px solid #cbd5e0;
                padding: 8px;
                text-align: left;
            }

            #word-document table th {
                background-color: #f8fafc;
                font-weight: bold;
            }

            #word-document img {
                max-width: 100%;
                height: auto;
                border-radius: 4px;
                margin: 10px 0;
            }

            #word-document a {
                color: #3b82f6;
                text-decoration: underline;
            }

            #word-document blockquote {
                border-left: 4px solid #e5e7eb;
                margin: 16px 0;
                padding-left: 16px;
                color: #6b7280;
                font-style: italic;
            }
        `;
        document.head.appendChild(style);

        return { editorContainer, documentElement, statusBar };
    }

    // Function to initialize the editor functionality
    function initializeEditor(editorContainer, documentElement, statusBar) {
        console.log('Initializing editor functionality...');

        // Make title bar draggable
        let isDragging = false;
        let dragOffset = { x: 0, y: 0 };
        const titleBar = editorContainer.querySelector('#word-editor-titlebar');
        titleBar.addEventListener('mousedown', (e) => {
            if (e.target.tagName === 'BUTTON') return;
            isDragging = true;
            const rect = editorContainer.getBoundingClientRect();
            dragOffset.x = e.clientX - rect.left;
            dragOffset.y = e.clientY - rect.top;
            document.addEventListener('mousemove', handleDrag);
            document.addEventListener('mouseup', stopDrag);
        });

        function handleDrag(e) {
            if (!isDragging) return;
            editorContainer.style.left = (e.clientX - dragOffset.x) + 'px';
            editorContainer.style.top = (e.clientY - dragOffset.y) + 'px';
            editorContainer.style.right = 'auto';
            editorContainer.style.bottom = 'auto';
        }

        function stopDrag() {
            isDragging = false;
            document.removeEventListener('mousemove', handleDrag);
            document.removeEventListener('mouseup', stopDrag);
        }

        // Close and minimize functionality
        document.getElementById('close-editor').addEventListener('click', () => {
            editorContainer.remove();
        });

        document.getElementById('minimize-editor').addEventListener('click', () => {
            const editorContent = editorContainer.querySelector('div:nth-child(3)');
            const toolbar = editorContainer.querySelector('#word-editor-toolbar');
            const statusBarElement = editorContainer.querySelector('#word-editor-status-bar');
            if (editorContent.style.display === 'none') {
                editorContent.style.display = 'flex';
                toolbar.style.display = 'flex';
                statusBarElement.style.display = 'flex';
                editorContainer.style.height = '700px';
            } else {
                editorContent.style.display = 'none';
                toolbar.style.display = 'none';
                statusBarElement.style.display = 'none';
                editorContainer.style.height = '50px';
            }
        });

        // Toolbar functionality
        const toolbar = editorContainer.querySelector('#word-editor-toolbar');

        // Format buttons
        toolbar.addEventListener('click', (e) => {
            const target = e.target.closest('.toolbar-btn');
            if (target) {
                const command = target.dataset.command;
                if (command) {
                    document.execCommand(command, false, null);
                    updateFormatButtons();
                    documentElement.focus();
                }
            }
        });

        // Font family and size
        const fontFamily = document.getElementById('font-family');
        const fontSize = document.getElementById('font-size');

        fontFamily.addEventListener('change', () => {
            document.execCommand('fontName', false, fontFamily.value);
            documentElement.focus();
        });

        fontSize.addEventListener('change', () => {
            document.execCommand('fontSize', false, fontSize.value);
            documentElement.focus();
        });

        // Color pickers
        const textColor = document.getElementById('text-color');
        const bgColor = document.getElementById('bg-color');

        textColor.addEventListener('change', () => {
            document.execCommand('foreColor', false, textColor.value);
            documentElement.focus();
        });

        bgColor.addEventListener('change', () => {
            document.execCommand('backColor', false, bgColor.value);
            documentElement.focus();
        });

        // Special buttons
        document.getElementById('insert-table').addEventListener('click', () => {
            const rows = prompt('Number of rows:', '3');
            const cols = prompt('Number of columns:', '3');
            if (rows && cols) {
                insertTable(parseInt(rows), parseInt(cols));
            }
        });

        document.getElementById('insert-image').addEventListener('click', () => {
            const url = prompt('Image URL:', 'https://');
            if (url && url !== 'https://') {
                insertImage(url);
            }
        });

        document.getElementById('insert-link').addEventListener('click', () => {
            const url = prompt('Link URL:', 'https://');
            const text = prompt('Link text:', 'Click here');
            if (url && url !== 'https://' && text) {
                insertLink(url, text);
            }
        });

        // Save and Load buttons
        document.getElementById('save-file').addEventListener('click', () => {
            saveFile();
        });

        document.getElementById('load-file').addEventListener('click', () => {
            loadFile();
        });

        // Update format buttons based on current selection
        function updateFormatButtons() {
            const formatButtons = toolbar.querySelectorAll('.format-btn');
            formatButtons.forEach(btn => {
                const command = btn.dataset.command;
                try {
                    if (document.queryCommandState(command)) {
                        btn.classList.add('active');
                    } else {
                        btn.classList.remove('active');
                    }
                } catch (e) {
                    console.error('Failed to query command state for:', command, e);
                }
            });
        }

        // Insert table function
        function insertTable(rows, cols) {
            let tableHTML = '<table><tbody>';
            for (let i = 0; i < rows; i++) {
                tableHTML += '<tr>';
                for (let j = 0; j < cols; j++) {
                    tableHTML += `<td>Cell</td>`;
                }
                tableHTML += '</tr>';
            }
            tableHTML += '</tbody></table><p></p>';
            document.execCommand('insertHTML', false, tableHTML);
            documentElement.focus();
        }

        // Insert image function
        function insertImage(url) {
            const imgHTML = `<img src="${url}" alt="Inserted image" style="max-width: 100%; height: auto;"><p></p>`;
            document.execCommand('insertHTML', false, imgHTML);
            documentElement.focus();
        }

        // Insert link function
        function insertLink(url, text) {
            const linkHTML = `<a href="${url}" target="_blank">${text}</a>`;
            document.execCommand('insertHTML', false, linkHTML);
            documentElement.focus();
        }

        // Save file to PC
        function saveFile() {
            const content = documentElement.innerHTML;
            const fileName = 'DrawariaWordOfficeDoc.html';
            const blob = new Blob([content], { type: 'text/html' });
            const link = document.createElement('a');
            link.href = URL.createObjectURL(blob);
            link.download = fileName;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            updateStatusInfo('Document saved to PC!');
        }

        // Load file from PC
        function loadFile() {
            const input = document.createElement('input');
            input.type = 'file';
            input.accept = '.html, .txt';
            input.addEventListener('change', (e) => {
                const file = e.target.files[0];
                if (file) {
                    const reader = new FileReader();
                    reader.onload = function(event) {
                        documentElement.innerHTML = event.target.result;
                        updateStats();
                        updateStatusInfo('Document loaded from PC!');
                    };
                    reader.readAsText(file);
                }
            });
            input.click();
        }

        // Update status bar
        const updateStats = debounce(() => {
            const text = documentElement.textContent || documentElement.innerText || '';
            const words = text.trim().split(/\s+/).filter(word => word.length > 0).length;
            const characters = text.length;

            const statsElement = document.getElementById('document-stats');
            if (statsElement) {
                statsElement.textContent = `Words: ${words} | Characters: ${characters}`;
            }
        }, 300);

        const updateStatusInfo = (message) => {
            const statusInfo = document.getElementById('status-info');
            if (statusInfo) {
                statusInfo.textContent = message;
            }
        };

        // Document event listeners
        documentElement.addEventListener('input', () => {
            updateStats();
            updateStatusInfo('Editing...');
            saveDocument();
        });
        documentElement.addEventListener('keyup', updateFormatButtons);
        documentElement.addEventListener('mouseup', updateFormatButtons);
        documentElement.addEventListener('paste', () => {
            setTimeout(() => {
                updateStats();
            }, 0);
        });

        // Keyboard shortcuts
        documentElement.addEventListener('keydown', (e) => {
            if (e.ctrlKey || e.metaKey) {
                switch (e.key.toLowerCase()) {
                    case 'b':
                        e.preventDefault();
                        document.execCommand('bold');
                        updateFormatButtons();
                        break;
                    case 'i':
                        e.preventDefault();
                        document.execCommand('italic');
                        updateFormatButtons();
                        break;
                    case 'u':
                        e.preventDefault();
                        document.execCommand('underline');
                        updateFormatButtons();
                        break;
                    case 's':
                        e.preventDefault();
                        saveDocument();
                        break;
                }
            }
        });

        // Save document function (using GM storage)
        const saveDocument = debounce(async() => {
            try {
                if (typeof GM !== 'undefined' && GM.setValue) {
                    const content = documentElement.innerHTML;
                    await GM.setValue('drawaria_word_document', content);
                    updateStatusInfo('Document saved');
                } else {
                    updateStatusInfo('Save function not available');
                }
                console.log('Document auto-saved successfully');
                setTimeout(() => updateStatusInfo('Ready'), 2000);
            } catch (error) {
                console.error('Failed to save document:', error);
                updateStatusInfo('Save failed!');
            }
        }, 3000); // Auto-save after 3 seconds of inactivity

        // Load saved document (using GM storage)
        async function loadDocument() {
            try {
                if (typeof GM !== 'undefined' && GM.getValue) {
                    const savedContent = await GM.getValue('drawaria_word_document', null);
                    if (savedContent) {
                        documentElement.innerHTML = savedContent;
                        updateStats();
                        updateStatusInfo('Saved document loaded');
                        console.log('Saved document loaded successfully');
                    }
                }
                updateStatusInfo('Ready');
            } catch (error) {
                console.error('Failed to load saved document:', error);
                updateStatusInfo('Load failed!');
            }
        }

        // Load saved document on initialization
        loadDocument();

        // Initial stats update
        updateStats();
        updateFormatButtons();

        console.log('Word editor fully initialized and ready to use');
    }

    // Function to open the editor
    function openEditor() {
        const existingEditor = document.getElementById('word-editor-container');
        if (!existingEditor) {
            const { editorContainer, documentElement, statusBar } = createWordEditor();
            document.body.appendChild(editorContainer);
            initializeEditor(editorContainer, documentElement, statusBar);
        } else {
            console.log('Editor is already open.');
        }
    }

    // Function to close the editor
    function closeEditor() {
        const existingEditor = document.getElementById('word-editor-container');
        if (existingEditor) {
            existingEditor.remove();
        } else {
            console.log('Editor is not open.');
        }
    }

    // Main initialization function
    function init() {
        console.log('Drawaria Word Processor starting...');

        if (typeof GM_registerMenuCommand === 'undefined') {
            console.error('GM_registerMenuCommand is not available. Please ensure you are using a compatible userscript manager.');
            return;
        }

        // Register menu commands
        GM_registerMenuCommand('📂 Open Word Office', openEditor);
        GM_registerMenuCommand('❌ Close Word Office', closeEditor);

        console.log('Drawaria Word Processor initialized successfully! Use the userscript menu to open the editor.');
    }

    // Start the extension
    init();

})();