Convert.ist: NEW
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ultimate DOCX Generator (1000% Perfection)</title>
<!-- Dependencies (loaded from CDN) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.5/purify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css">
<style>
:root {
--primary: #2563eb;
--primary-dark: #1e40af;
--bg: #f8fafc;
--text: #1e293b;
--success: #10b981;
--error: #ef4444;
--warning: #f59e0b;
--sidebar: #e2e8f0;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f172a;
--text: #f8fafc;
--sidebar: #1e293b;
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
line-height: 1.6;
background: var(--bg);
color: var(--text);
min-height: 100vh;
}
.container {
display: grid;
grid-template-columns: 250px 1fr;
min-height: 100vh;
}
.sidebar {
background: var(--sidebar);
padding: 1.5rem;
border-right: 1px solid rgba(0,0,0,0.1);
}
.main-content {
padding: 2rem;
overflow-x: hidden;
}
h1 {
color: var(--primary);
margin-bottom: 1.5rem;
text-align: center;
}
.editor-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
}
@media (max-width: 1024px) {
.editor-container {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
}
.sidebar {
display: none;
}
}
.toolbar {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-bottom: 1rem;
padding: 0.5rem;
background: var(--sidebar);
border-radius: 8px;
}
button {
background: var(--primary);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 0.3rem;
}
button:hover {
background: var(--primary-dark);
transform: translateY(-1px);
}
button:active {
transform: translateY(0);
}
button.secondary {
background: var(--sidebar);
color: var(--text);
border: 1px solid var(--primary);
}
button.secondary:hover {
background: var(--primary);
color: white;
}
#editor {
min-height: 500px;
border: 2px solid var(--primary);
border-radius: 8px;
padding: 1rem;
background: white;
color: black;
outline: none;
overflow-y: auto;
}
#preview {
min-height: 500px;
border: 2px solid var(--primary);
border-radius: 8px;
padding: 1rem;
background: var(--bg);
overflow-y: auto;
}
.convert-btn {
width: 100%;
padding: 1rem;
margin-top: 1.5rem;
font-weight: bold;
background: var(--success);
}
.convert-btn:hover {
background: #059669;
}
.loading {
display: none;
text-align: center;
margin: 1rem 0;
}
.toast {
position: fixed;
bottom: 1rem;
right: 1rem;
padding: 1rem;
background: var(--success);
color: white;
border-radius: 8px;
display: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 1000;
}
.error {
background: var(--error) !important;
}
.warning {
background: var(--warning) !important;
}
.sidebar-section {
margin-bottom: 1.5rem;
}
.sidebar-title {
font-weight: bold;
margin-bottom: 0.5rem;
color: var(--primary);
display: flex;
align-items: center;
gap: 0.5rem;
}
.sidebar-menu {
list-style: none;
}
.sidebar-menu li {
margin-bottom: 0.3rem;
}
.sidebar-menu button {
width: 100%;
text-align: left;
justify-content: flex-start;
}
.feature-badge {
background: var(--primary-dark);
color: white;
padding: 0.2rem 0.5rem;
border-radius: 12px;
font-size: 0.7rem;
margin-left: 0.5rem;
}
.template-preview {
width: 100%;
height: 100px;
background: white;
border: 1px solid #ddd;
margin-bottom: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
}
.template-preview:hover {
transform: scale(1.02);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.tab-buttons {
display: flex;
margin-bottom: 1rem;
}
.tab-button {
border-radius: 0;
border-bottom: 3px solid transparent;
}
.tab-button.active {
border-bottom: 3px solid var(--primary-dark);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.settings-option {
margin-bottom: 1rem;
}
.settings-option label {
display: block;
margin-bottom: 0.3rem;
font-weight: 500;
}
.settings-option select, .settings-option input {
width: 100%;
padding: 0.5rem;
border-radius: 4px;
border: 1px solid #ddd;
}
.progress-bar {
width: 100%;
height: 10px;
background: #e2e8f0;
border-radius: 5px;
margin-top: 0.5rem;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: var(--primary);
width: 0%;
transition: width 0.3s;
}
.ai-prompt {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
}
.ai-prompt input {
flex-grow: 1;
padding: 0.5rem;
border-radius: 4px;
border: 1px solid #ddd;
}
.ai-prompt button {
white-space: nowrap;
}
.watermark-toggle {
display: flex;
align-items: center;
gap: 0.5rem;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: var(--success);
}
input:checked + .slider:before {
transform: translateX(26px);
}
.autocomplete-suggestion {
position: absolute;
background: #f1f5f9;
border: 1px solid var(--primary);
border-radius: 4px;
padding: 0.5rem;
z-index: 10;
color: var(--text);
font-size: 0.9rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="sidebar-section">
<h2 class="sidebar-title">Document Tools</h2>
<ul class="sidebar-menu">
<li><button onclick="newDocument()"><span>🆕</span> New Document</button></li>
<li><button onclick="loadDocument()"><span>📂</span> Load DOCX</button></li>
<li><button onclick="generateDOCX()" class="convert-btn"><span>💾</span> Save as DOCX</button></li>
<li><button onclick="printDocument()"><span>🖨️</span> Print</button></li>
</ul>
</div>
<div class="sidebar-section">
<h2 class="sidebar-title">Templates <span class="feature-badge">PRO</span></h2>
<div class="template-preview" onclick="applyTemplate('report')">
Business Report
</div>
<div class="template-preview" onclick="applyTemplate('letter')">
Formal Letter
</div>
<div class="template-preview" onclick="applyTemplate('resume')">
Resume/CV
</div>
</div>
<div class="sidebar-section">
<h2 class="sidebar-title">AI Assistant <span class="feature-badge">BETA</span></h2>
<div class="ai-prompt">
<input type="text" id="aiPrompt" placeholder="Improve this text...">
<button onclick="generateWithAI()">Go</button>
</div>
</div>
<div class="sidebar-section">
<h2 class="sidebar-title">Document Stats</h2>
<div id="documentStats">
<p>Words: <span id="wordCount">0</span></p>
<p>Characters: <span id="charCount">0</span></p>
<p>Pages: <span id="pageCount">0</span></p>
</div>
</div>
</div>
<div class="main-content">
<h1>Ultimate DOCX Generator <span class="feature-badge">1000% PERFECTION</span></h1>
<div class="tab-buttons">
<button class="tab-button active" onclick="openTab('editTab')">Editor</button>
<button class="tab-button" onclick="openTab('previewTab')">Preview</button>
<button class="tab-button" onclick="openTab('settingsTab')">Settings</button>
</div>
<div id="editTab" class="tab-content active">
<div class="toolbar">
<button onclick="formatText('bold')"><span>🔊</span> Bold</button>
<button onclick="formatText('italic')"><span>🔊</span> Italic</button>
<button onclick="formatText('underline')"><span>🔊</span> Underline</button>
<button onclick="formatText('heading1')"><span>🔊</span> H1</button>
<button onclick="formatText('heading2')"><span>🔊</span> H2</button>
<button onclick="formatText('bullet')"><span>🔊</span> Bullets</button>
<button onclick="formatText('numbered')"><span>🔊</span> Numbers</button>
<button onclick="formatText('quote')"><span>🔊</span> Quote</button>
<button onclick="insertImage()"><span>🖼️</span> Image</button>
<button onclick="insertChart()"><span>📊</span> Chart</button>
<button onclick="insertTable()"><span>🔊</span> Table</button>
<button onclick="insertEquation()"><span>🔊</span> Equation</button>
<button onclick="insertLink()"><span>🔗</span> Link</button>
</div>
<div id="editor" contenteditable="true">
<h1>My Perfect Document</h1>
<p>Start typing here to create your professional document...</p>
</div>
</div>
<div id="previewTab" class="tab-content">
<div id="preview"></div>
</div>
<div id="settingsTab" class="tab-content">
<div class="settings-option">
<label for="pageSize">Page Size:</label>
<select id="pageSize">
<option value="A4">A4 (210 × 297 mm)</option>
<option value="Letter">Letter (8.5 × 11 in)</option>
<option value="Legal">Legal (8.5 × 14 in)</option>
</select>
</div>
<div class="settings-option">
<label for="pageOrientation">Page Orientation:</label>
<select id="pageOrientation">
<option value="portrait">Portrait</option>
<option value="landscape">Landscape</option>
</select>
</div>
<div class="settings-option">
<label for="marginSize">Margin Size:</label>
<select id="marginSize">
<option value="normal">Normal (1 in)</option>
<option value="narrow">Narrow (0.5 in)</option>
<option value="wide">Wide (1.5 in)</option>
</select>
</div>
<div class="settings-option">
<div class="watermark-toggle">
<label>Add Watermark:</label>
<label class="toggle-switch">
<input type="checkbox" id="watermarkToggle">
<span class="slider"></span>
</label>
</div>
<input type="text" id="watermarkText" placeholder="Watermark text" style="margin-top: 0.5rem;" disabled>
</div>
<div class="settings-option">
<div class="watermark-toggle">
<label>Enable AI Autocomplete:</label>
<label class="toggle-switch">
<input type="checkbox" id="autocompleteToggle">
<span class="slider"></span>
</label>
</div>
</div>
<div class="settings-option">
<label for="documentTheme">Document Theme:</label>
<select id="documentTheme">
<option value="default">Default</option>
<option value="professional">Professional</option>
<option value="modern">Modern</option>
<option value="elegant">Elegant</option>
</select>
</div>
<button onclick="applySettings()" style="margin-top: 1rem;">Apply Settings</button>
</div>
<div class="loading" id="loading">
<div>Generating your perfect document...</div>
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<div id="progress">0%</div>
</div>
<div class="toast" id="toast">DOCX Generated Successfully!</div>
</div>
</div>
<script>
// ===== GLOBAL VARIABLES ===== //
const editor = document.getElementById('editor');
const preview = document.getElementById('preview');
let images = [];
let charts = [];
let tables = [];
let currentTemplate = null;
let isAutocompleteEnabled = false;
// Document settings
const documentSettings = {
pageSize: 'A4',
orientation: 'portrait',
margins: 'normal',
watermark: false,
watermarkText: '',
theme: 'default'
};
// ===== INITIALIZATION ===== //
document.addEventListener('DOMContentLoaded', function() {
updatePreview();
updateStats();
// Set up event listeners
editor.addEventListener('input', function() {
updatePreview();
updateStats();
});
// Watermark toggle
document.getElementById('watermarkToggle').addEventListener('change', function() {
const watermarkText = document.getElementById('watermarkText');
watermarkText.disabled = !this.checked;
documentSettings.watermark = this.checked;
});
// AI Autocomplete toggle
document.getElementById('autocompleteToggle').addEventListener('change', function() {
isAutocompleteEnabled = this.checked;
showToast(`AI Autocomplete ${isAutocompleteEnabled ? 'enabled' : 'disabled'}`);
});
// Load any saved settings from localStorage
loadSettings();
});
// ===== DOCUMENT FUNCTIONS ===== //
function newDocument() {
if (confirm('Are you sure you want to create a new document? Any unsaved changes will be lost.')) {
editor.innerHTML = '<h1>New Document</h1><p>Start typing here...</p>';
images = [];
charts = [];
tables = [];
currentTemplate = null;
updatePreview();
showToast('New document created');
}
}
function loadDocument() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.docx';
input.onchange = e => {
const file = e.target.files[0];
if (!file) return;
showLoading('Loading document...');
// Placeholder for DOCX parsing
setTimeout(() => {
editor.innerHTML = '<h1>Loaded Document</h1><p>DOCX loading would be implemented here with proper parsing...</p>';
hideLoading();
showToast('Document loaded (simulated)');
}, 1500);
};
input.click();
}
function printDocument() {
const printWindow = window.open('', '', 'width=800,height=600');
printWindow.document.write(`
<html>
<head>
<title>Print Document</title>
<style>
body { font-family: Arial; line-height: 1.6; padding: 20px; }
img { max-width: 100%; }
</style>
</head>
<body>
${preview.innerHTML}
<script>
setTimeout(() => {
window.print();
window.close();
}, 200);
</script>
</body>
</html>
`);
printWindow.document.close();
}
function applyTemplate(template) {
currentTemplate = template;
let content = '';
switch(template) {
case 'report':
content = `
<h1>Business Report</h1>
<h2>Executive Summary</h2>
<p>This report summarizes the key findings of our analysis...</p>
<h2>Findings</h2>
<ul>
<li>Key finding 1</li>
<li>Key finding 2</li>
<li>Key finding 3</li>
</ul>
<h2>Recommendations</h2>
<ol>
<li>Recommended action 1</li>
<li>Recommended action 2</li>
</ol>
`;
break;
case 'letter':
content = `
<p style="text-align:right;">[Your Address]<br>[Date]</p>
<p>[Recipient Name]<br>[Recipient Address]</p>
<p>Dear [Name],</p>
<p>I am writing to you regarding...</p>
<p>Sincerely,<br>[Your Name]</p>
`;
break;
case 'resume':
content = `
<h1 style="text-align:center;">[Your Name]</h1>
<p style="text-align:center;">[Your Contact Information]</p>
<h2>Professional Summary</h2>
<p>Experienced professional with skills in...</p>
<h2>Work Experience</h2>
<p><strong>[Job Title]</strong> at [Company]<br>
[Dates]<br>
[Description]</p>
<h2>Education</h2>
<p><strong>[Degree]</strong> from [University]<br>
[Year]</p>
`;
break;
}
editor.innerHTML = content;
showToast(`"${template}" template applied`);
}
// ===== EDITOR FUNCTIONS ===== //
function formatText(command) {
if (command === 'heading1') document.execCommand('formatBlock', false, 'h1');
else if (command === 'heading2') document.execCommand('formatBlock', false, 'h2');
else if (command === 'bullet') document.execCommand('insertUnorderedList');
else if (command === 'numbered') document.execCommand('insertOrderedList');
else if (command === 'quote') document.execCommand('formatBlock', false, 'blockquote');
else document.execCommand(command, false, null);
editor.focus();
}
function insertImage() {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
const img = await createImageBitmap(file);
images.push({
file,
width: img.width,
height: img.height,
alt: file.name
});
const imgElem = document.createElement('img');
imgElem.src = URL.createObjectURL(file);
imgElem.style.maxWidth = '100%';
imgElem.style.height = 'auto';
imgElem.alt = file.name;
if (window.getSelection) {
const selection = window.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(imgElem);
if (!imgElem.nextElementSibling || imgElem.nextElementSibling.tagName !== 'P') {
const p = document.createElement('p');
p.innerHTML = '<br>';
imgElem.parentNode.insertBefore(p, imgElem.nextSibling);
}
}
} else {
editor.appendChild(imgElem);
}
showToast('Image inserted');
} catch (error) {
showToast('Error loading image: ' + error.message, true);
}
};
input.click();
}
function insertChart() {
const chartId = 'chart-' + Date.now();
const chartContainer = document.createElement('div');
chartContainer.className = 'chart-container';
chartContainer.style.margin = '20px 0';
const canvas = document.createElement('canvas');
canvas.id = chartId;
canvas.width = 500;
canvas.height = 300;
chartContainer.appendChild(canvas);
if (window.getSelection) {
const selection = window.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(chartContainer);
}
} else {
editor.appendChild(chartContainer);
}
const ctx = canvas.getContext('2d');
const chart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [{
label: 'Sales',
data: [120, 190, 300, 250],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: false,
plugins: {
title: {
display: true,
text: 'Sales by Quarter'
}
}
}
});
charts.push({
id: chartId,
chart: chart
});
showToast('Chart inserted. Right-click to edit data.');
}
function insertTable() {
const rows = prompt('Number of rows:', '3');
const cols = prompt('Number of columns:', '3');
if (rows && cols) {
const table = document.createElement('table');
table.style.width = '100%';
table.style.borderCollapse = 'collapse';
table.style.margin = '10px 0';
for (let i = 0; i < parseInt(rows); i++) {
const tr = document.createElement('tr');
for (let j = 0; j < parseInt(cols); j++) {
const td = document.createElement('td');
td.style.border = '1px solid #ddd';
td.style.padding = '8px';
td.innerHTML = i === 0 ? '<strong>Header</strong>' : 'Cell';
tr.appendChild(td);
}
table.appendChild(tr);
}
if (window.getSelection) {
const selection = window.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(table);
}
} else {
editor.appendChild(table);
}
tables.push(table);
showToast('Table inserted');
}
}
function insertEquation() {
const equation = prompt('Enter LaTeX equation (e.g., E=mc^2):', '\\frac{a}{b}');
if (equation) {
const eqElem = document.createElement('div');
eqElem.style.textAlign = 'center';
eqElem.style.margin = '15px 0';
eqElem.innerHTML = `$$${equation}$$`;
if (window.getSelection) {
const selection = window.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(eqElem);
}
} else {
editor.appendChild(eqElem);
}
renderMathInPreview();
showToast('Equation inserted');
}
}
function insertLink() {
const url = prompt('Enter URL:', 'https://');
if (url) {
const text = prompt('Enter link text:', url);
document.execCommand('createLink', false, url);
if (text && text !== url) {
const selection = window.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(document.createTextNode(text));
document.execCommand('createLink', false, url);
}
}
showToast('Link inserted');
}
}
function generateWithAI() {
const prompt = document.getElementById('aiPrompt').value;
if (!prompt.trim()) {
showToast('Please enter an AI prompt', true);
return;
}
showLoading('AI is generating content...');
setTimeout(() => {
const aiResponses = [
"Based on your request, here's an improved version of your content...",
"The AI suggests the following enhancements to your document...",
"After analyzing your text, the AI recommends these changes...",
"Here's a professionally rewritten version of your content...",
"The AI has generated this content based on your prompt..."
];
const randomResponse = aiResponses[Math.floor(Math.random() * aiResponses.length)];
const p = document.createElement('p');
p.innerHTML = `<strong>AI Suggestion:</strong> ${randomResponse}`;
p.style.color = 'var(--primary)';
p.style.margin = '15px 0';
editor.appendChild(p);
hideLoading();
showToast('AI content generated (simulated)');
}, 2000);
}
// ===== AI AUTOCOMPLETE ===== //
editor.addEventListener('input', function(e) {
if (!isAutocompleteEnabled) return;
const selection = window.getSelection();
if (!selection.rangeCount) return;
const range = selection.getRangeAt(0);
const text = editor.innerText;
const words = text.trim().split(/\s+/);
const lastWord = words[words.length - 1];
const existingSuggestion = document.querySelector('.autocomplete-suggestion');
if (existingSuggestion) existingSuggestion.remove();
let suggestion = '';
if (lastWord.toLowerCase() === 'dear') {
suggestion = 'Dear [Name], I hope this message finds you well.';
} else if (lastWord.toLowerCase() === 'summary') {
suggestion = 'Summary: This document outlines key findings and recommendations.';
}
if (suggestion) {
const suggestionBox = document.createElement('div');
suggestionBox.className = 'autocomplete-suggestion';
suggestionBox.textContent = suggestion;
const rect = range.getBoundingClientRect();
suggestionBox.style.left = `${rect.left}px`;
suggestionBox.style.top = `${rect.bottom + window.scrollY}px`;
suggestionBox.tabIndex = 0;
suggestionBox.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
e.preventDefault();
insertTextAtCursor(suggestion);
suggestionBox.remove();
}
});
document.body.appendChild(suggestionBox);
suggestionBox.focus();
}
});
function insertTextAtCursor(text) {
const selection = window.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(document.createTextNode(text));
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
}
}
// ===== PREVIEW & STATS FUNCTIONS ===== //
function updatePreview() {
preview.innerHTML = DOMPurify.sanitize(editor.innerHTML, {
ALLOWED_TAGS: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'ul', 'ol', 'li', 'strong', 'em', 'u', 'blockquote', 'img', 'table', 'tr', 'td', 'th', 'a', 'div', 'span'],
ALLOWED_ATTR: ['style', 'src', 'href', 'alt', 'width', 'height']
});
renderMathInPreview();
preview.querySelectorAll('img').forEach(img => {
img.style.maxWidth = '100%';
img.style.height = 'auto';
});
preview.querySelectorAll('table').forEach(table => {
table.style.width = '100%';
table.style.borderCollapse = 'collapse';
table.style.margin = '10px 0';
table.querySelectorAll('td, th').forEach(cell => {
cell.style.border = '1px solid #ddd';
cell.style.padding = '8px';
});
});
}
function renderMathInPreview() {
preview.querySelectorAll('.katex').forEach(el => el.remove());
const elements = preview.querySelectorAll('div');
elements.forEach(el => {
if (el.textContent.startsWith('$$') && el.textContent.endsWith('$$')) {
try {
katex.render(el.textContent.slice(2, -2), el, {
displayMode: true,
throwOnError: false
});
} catch (e) {
console.warn('KaTeX error:', e);
}
}
});
}
function updateStats() {
const text = editor.innerText || editor.textContent;
const wordCount = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
document.getElementById('wordCount').textContent = wordCount;
document.getElementById('charCount').textContent = text.length;
const pageCount = Math.ceil(wordCount / 500);
document.getElementById('pageCount').textContent = pageCount;
}
// ===== DOCX GENERATION ===== //
async function generateDOCX() {
showLoading('Generating DOCX...');
try {
const zip = new JSZip();
updateSettingsFromForm();
zip.file('[Content_Types].xml', generateContentTypes());
zip.file('_rels/.rels', generateRels());
zip.file('word/document.xml', generateDocumentXML());
zip.file('word/styles.xml', generateStylesXML());
zip.file('word/_rels/document.xml.rels', generateDocumentRels());
zip.file('word/settings.xml', generateSettingsXML());
images.forEach((img, i) => {
const ext = img.file.name.split('.').pop();
zip.file(`word/media/image${i+1}.${ext}`, img.file);
});
let progress = 0;
const progressInterval = setInterval(() => {
progress += Math.random() * 10;
if (progress > 90) progress = 90;
updateProgress(progress);
}, 200);
const blob = await zip.generateAsync({
type: 'blob',
compression: 'DEFLATE',
compressionOptions: { level: 9 }
}, metadata => {
updateProgress(90 + (metadata.percent / 10));
});
clearInterval(progressInterval);
updateProgress(100);
saveAs(blob, generateFilename());
showToast('DOCX generated successfully!');
saveSettings();
} catch (error) {
showToast('Error generating DOCX: ' + error.message, true);
console.error(error);
} finally {
hideLoading();
}
}
function generateFilename() {
let filename = 'document';
const h1s = editor.getElementsByTagName('h1');
if (h1s.length > 0) {
filename = h1s[0].textContent.trim().toLowerCase().replace(/\s+/g, '-');
filename = filename.replace(/[^a-z0-9-]/g, '');
}
if (currentTemplate) {
filename += `-${currentTemplate}`;
}
const today = new Date();
filename += `-${today.getFullYear()}-${today.getMonth()+1}-${today.getDate()}`;
return `${filename}.docx`;
}
function generateContentTypes() {
let content = `<?xml version="1.0" encoding="UTF-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="xml" ContentType="application/xml"/>
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="png" ContentType="image/png"/>
<Default Extension="jpeg" ContentType="image/jpeg"/>
<Default Extension="jpg" ContentType="image/jpeg"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
<Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/>`;
if (charts.length > 0) {
content += `
<Override PartName="/word/charts/chart1.xml" ContentType="application/vnd.openxmlformats-officedocument.drawingml.chart+xml"/>`;
}
content += `
</Types>`;
return content;
}
function generateRels() {
return `<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`;
}
function generateDocumentXML() {
let content = DOMPurify.sanitize(editor.innerHTML, {
ALLOWED_TAGS: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'ul', 'ol', 'li', 'strong', 'em', 'u', 'blockquote', 'img', 'table', 'tr', 'td', 'th', 'a', 'div', 'span'],
ALLOWED_ATTR: ['style', 'src', 'href', 'alt', 'width', 'height']
});
content = content
.replace(/<h1>/g, '<w:p><w:pPr><w:pStyle w:val="Heading1"/></w:pPr><w:r><w:t>')
.replace(/<\/h1>/g, '</w:t></w:r></w:p>')
.replace(/<h2>/g, '<w:p><w:pPr><w:pStyle w:val="Heading2"/></w:pPr><w:r><w:t>')
.replace(/<\/h2>/g, '</w:t></w:r></w:p>')
.replace(/<strong>/g, '<w:r><w:rPr><w:b/></w:rPr><w:t>')
.replace(/<\/strong>/g, '</w:t></w:r>')
.replace(/<em>/g, '<w:r><w:rPr><w:i/></w:rPr><w:t>')
.replace(/<\/em>/g, '</w:t></w:r>')
.replace(/<u>/g, '<w:r><w:rPr><w:u w:val="single"/></w:rPr><w:t>')
.replace(/<\/u>/g, '</w:t></w:r>')
.replace(/<p>/g, '<w:p><w:r><w:t>')
.replace(/<\/p>/g, '</w:t></w:r></w:p>')
.replace(/<li>/g, '<w:p><w:pPr><w:numPr><w:ilvl w:val="0"/><w:numId w:val="1"/></w:numPr></w:pPr><w:r><w:t>')
.replace(/<\/li>/g, '</w:t></w:r></w:p>');
images.forEach((img, i) => {
const ext = img.file.name.split('.').pop();
content = content.replace(
new RegExp(`<img[^>]*src=["'][^"']*${img.file.name}[^"']*["'][^>]*>`, 'g'),
`<w:p><w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="${img.width * 9525}" cy="${img.height * 9525}"/>
<wp:docPr id="1" name="Picture ${i+1}"/>
<wp:cNvGraphicFramePr><a:graphicFrameLocks noChangeAspect="1"/></wp:cNvGraphicFramePr>
<a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr><pic:cNvPr id="0" name="Picture ${i+1}"/><pic:cNvPicPr/></pic:nvPicPr>
<pic:blipFill><a:blip r:embed="rIdImage${i+1}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill>
<pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${img.width * 9525}" cy="${img.height * 9525}"/></a:xfrm>
<a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr>
</pic:pic>
</a:graphicData></a:graphic>
</wp:inline></w:drawing></w:r></w:p>`
);
});
let watermarkContent = '';
if (documentSettings.watermark && documentSettings.watermarkText) {
watermarkContent = `
<w:p>
<w:pPr>
<w:pStyle w:val="Watermark"/>
</w:pPr>
</w:p>`;
}
return `<?xml version="1.0" encoding="UTF-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<w:body>
${watermarkContent}
${content}
<w:sectPr>
<w:pgSz w:w="${getPageWidth()}" w:h="${getPageHeight()}"/>
<w:pgMar w:top="${getMarginSize()}" w:right="${getMarginSize()}" w:bottom="${getMarginSize()}" w:left="${getMarginSize()}"/>
<w:cols w:space="720"/>
</w:sectPr>
</w:body>
</w:document>`;
}
function getPageWidth() {
if (documentSettings.pageSize === 'Letter') {
return documentSettings.orientation === 'portrait' ? 12240 : 15840;
} else if (documentSettings.pageSize === 'Legal') {
return documentSettings.orientation === 'portrait' ? 12240 : 20160;
}
return documentSettings.orientation === 'portrait' ? 11906 : 16838;
}
function getPageHeight() {
if (documentSettings.pageSize === 'Letter') {
return documentSettings.orientation === 'portrait' ? 15840 : 12240;
} else if (documentSettings.pageSize === 'Legal') {
return documentSettings.orientation === 'portrait' ? 20160 : 12240;
}
return documentSettings.orientation === 'portrait' ? 16838 : 11906;
}
function getMarginSize() {
switch(documentSettings.margins) {
case 'narrow': return 720;
case 'wide': return 2160;
default: return 1440;
}
}
function generateStylesXML() {
let watermarkStyle = '';
if (documentSettings.watermark && documentSettings.watermarkText) {
watermarkStyle = `
<w:style w:type="paragraph" w:customStyle="1" w:styleId="Watermark">
<w:name w:val="Watermark"/>
<w:basedOn w:val="Normal"/>
<w:rsid w:val="00FFFFFF"/>
<w:pPr>
<w:spacing w:after="0" w:line="1" w:lineRule="auto"/>
</w:pPr>
<w:rPr>
<w:color w:val="CCCCCC"/>
<w:sz w:val="72"/>
<w:szCs w:val="72"/>
</w:rPr>
</w:style>`;
}
return `<?xml version="1.0" encoding="UTF-8"?>
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:docDefaults>
<w:rPrDefault>
<w:rPr>
<w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorHAnsi" w:hAnsiTheme="minorHAnsi" w:cstheme="minorBidi"/>
<w:sz w:val="22"/>
<w:szCs w:val="22"/>
<w:lang w:val="en-US" w:eastAsia="en-US" w:bidi="ar-SA"/>
</w:rPr>
</w:rPrDefault>
<w:pPrDefault>
<w:pPr>
<w:spacing w:after="200" w:line="276" w:lineRule="auto"/>
</w:pPr>
</w:pPrDefault>
</w:docDefaults>
<w:style w:type="paragraph" w:styleId="Normal">
<w:name w:val="Normal"/>
<w:qFormat/>
</w:style>
<w:style w:type="paragraph" w:styleId="Heading1">
<w:name w:val="heading 1"/>
<w:basedOn w:val="Normal"/>
<w:next w:val="Normal"/>
<w:qFormat/>
<w:pPr>
<w:keepNext/>
<w:spacing w:before="480" w:after="120"/>
<w:outlineLvl w:val="0"/>
</w:pPr>
<w:rPr>
<w:b/>
<w:bCs/>
<w:sz w:val="32"/>
<w:szCs w:val="32"/>
</w:rPr>
</w:style>
<w:style w:type="paragraph" w:styleId="Heading2">
<w:name w:val="heading 2"/>
<w:basedOn w:val="Normal"/>
<w:next w:val="Normal"/>
<w:qFormat/>
<w:pPr>
<w:keepNext/>
<w:spacing w:before="360" w:after="60"/>
<w:outlineLvl w:val="1"/>
</w:pPr>
<w:rPr>
<w:b/>
<w:bCs/>
<w:sz w:val="26"/>
<w:szCs w:val="26"/>
</w:rPr>
</w:style>
${watermarkStyle}
</w:styles>`;
}
function generateDocumentRels() {
let rels = `<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">`;
images.forEach((img, i) => {
const ext = img.file.name.split('.').pop();
rels += `
<Relationship Id="rIdImage${i+1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image${i+1}.${ext}"/>`;
});
rels += `
</Relationships>`;
return rels;
}
function generateSettingsXML() {
return `<?xml version="1.0" encoding="UTF-8"?>
<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:zoom w:percent="100"/>
<w:proofState w:grammar="clean" w:spelling="clean"/>
<w:defaultTabStop w:val="720"/>
<w:characterSpacingControl w:val="doNotCompress"/>
<w:compat>
<w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="15"/>
</w:compat>
</w:settings>`;
}
// ===== SETTINGS FUNCTIONS ===== //
function updateSettingsFromForm() {
documentSettings.pageSize = document.getElementById('pageSize').value;
documentSettings.orientation = document.getElementById('pageOrientation').value;
documentSettings.margins = document.getElementById('marginSize').value;
documentSettings.watermark = document.getElementById('watermarkToggle').checked;
documentSettings.watermarkText = document.getElementById('watermarkText').value;
documentSettings.theme = document.getElementById('documentTheme').value;
}
function applySettings() {
updateSettingsFromForm();
showToast('Document settings applied');
}
function saveSettings() {
localStorage.setItem('docxGeneratorSettings', JSON.stringify(documentSettings));
}
function loadSettings() {
const savedSettings = localStorage.getItem('docxGeneratorSettings');
if (savedSettings) {
Object.assign(documentSettings, JSON.parse(savedSettings));
document.getElementById('pageSize').value = documentSettings.pageSize;
document.getElementById('pageOrientation').value = documentSettings.orientation;
document.getElementById('marginSize').value = documentSettings.margins;
document.getElementById('watermarkToggle').checked = documentSettings.watermark;
document.getElementById('watermarkText').value = documentSettings.watermarkText || '';
document.getElementById('watermarkText').disabled = !documentSettings.watermark;
document.getElementById('documentTheme').value = documentSettings.theme;
}
}
// ===== UI HELPER FUNCTIONS ===== //
function openTab(tabId) {
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.tab-button').forEach(button => {
button.classList.remove('active');
});
document.getElementById(tabId).classList.add('active');
event.currentTarget.classList.add('active');
if (tabId === 'previewTab') {
updatePreview();
}
}
function showLoading(message = 'Processing...') {
const loading = document.getElementById('loading');
loading.firstElementChild.textContent = message;
loading.style.display = 'block';
updateProgress(0);
}
function hideLoading() {
document.getElementById('loading').style.display = 'none';
}
function updateProgress(percent) {
document.getElementById('progressFill').style.width = `${percent}%`;
document.getElementById('progress').textContent = `${Math.round(percent)}%`;
}
function showToast(message, isError = false, isWarning = false) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.className = 'toast';
if (isError) {
toast.classList.add('error');
} else if (isWarning) {
toast.classList.add('warning');
}
toast.style.display = 'block';
setTimeout(() => {
toast.style.display = 'none';
}, 3000);
}
</script>
</body>
</html>
Comments
Post a Comment