// ==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://gf.qytechs.cn/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();
})();