// Global variables let quill; let currentMode = 'richtext'; let currentContent = ''; let pages = []; // Initialize Quill editor function initQuill() { quill = new Quill('#quillEditor', { theme: 'snow', modules: { toolbar: '#quillToolbar' }, placeholder: 'Start writing your letter content here...' }); // Update preview on content change quill.on('text-change', () => { updatePreview(); }); } // Mode switching document.getElementById('richTextMode').addEventListener('click', () => { switchMode('richtext'); }); document.getElementById('markdownMode').addEventListener('click', () => { switchMode('markdown'); }); document.getElementById('previewMode').addEventListener('click', () => { switchMode('preview'); }); function switchMode(mode) { // Update button states document.querySelectorAll('.toolbar-left .btn').forEach(btn => { btn.classList.remove('active', 'btn-primary'); }); // Hide all editors document.querySelectorAll('.editor-mode').forEach(editor => { editor.classList.remove('active'); }); currentMode = mode; switch(mode) { case 'richtext': document.getElementById('richTextMode').classList.add('active', 'btn-primary'); document.getElementById('richTextEditor').classList.add('active'); // Convert markdown to rich text if switching from markdown if (currentMode === 'markdown') { const markdownContent = document.getElementById('markdownTextarea').value; const html = marked.parse(markdownContent); quill.root.innerHTML = DOMPurify.sanitize(html); } break; case 'markdown': document.getElementById('markdownMode').classList.add('active', 'btn-primary'); document.getElementById('markdownEditor').classList.add('active'); // Convert rich text to markdown if switching from rich text if (currentMode === 'richtext') { const html = quill.root.innerHTML; document.getElementById('markdownTextarea').value = htmlToMarkdown(html); } break; case 'preview': document.getElementById('previewMode').classList.add('active', 'btn-primary'); // Hide editor section document.querySelector('.editor-section').style.display = 'none'; document.querySelector('.preview-section').style.flex = '1'; break; } if (mode !== 'preview') { document.querySelector('.editor-section').style.display = 'flex'; document.querySelector('.preview-section').style.flex = '1'; } updatePreview(); } // Markdown editor functions function insertMarkdown(before, after) { const textarea = document.getElementById('markdownTextarea'); const start = textarea.selectionStart; const end = textarea.selectionEnd; const selectedText = textarea.value.substring(start, end); const replacement = before + selectedText + after; textarea.value = textarea.value.substring(0, start) + replacement + textarea.value.substring(end); textarea.focus(); textarea.setSelectionRange(start + before.length, start + before.length + selectedText.length); updatePreview(); } function insertTable() { const table = '\n| Header 1 | Header 2 | Header 3 |\n|----------|----------|----------|\n| Cell 1 | Cell 2 | Cell 3 |\n| Cell 4 | Cell 5 | Cell 6 |\n'; const textarea = document.getElementById('markdownTextarea'); const start = textarea.selectionStart; textarea.value = textarea.value.substring(0, start) + table + textarea.value.substring(start); textarea.focus(); textarea.setSelectionRange(start + table.length, start + table.length); updatePreview(); } // Update preview on markdown change document.getElementById('markdownTextarea').addEventListener('input', updatePreview); // Convert HTML to Markdown (basic conversion) function htmlToMarkdown(html) { let markdown = html; // Headers markdown = markdown.replace(/]*>(.*?)<\/h1>/gi, '# $1\n'); markdown = markdown.replace(/]*>(.*?)<\/h2>/gi, '## $1\n'); markdown = markdown.replace(/]*>(.*?)<\/h3>/gi, '### $1\n'); // Bold and italic markdown = markdown.replace(/]*>(.*?)<\/strong>/gi, '**$1**'); markdown = markdown.replace(/]*>(.*?)<\/b>/gi, '**$1**'); markdown = markdown.replace(/]*>(.*?)<\/em>/gi, '*$1*'); markdown = markdown.replace(/]*>(.*?)<\/i>/gi, '*$1*'); // Links markdown = markdown.replace(/]+href="([^"]*)"[^>]*>(.*?)<\/a>/gi, '[$2]($1)'); // Images markdown = markdown.replace(/]+src="([^"]*)"[^>]*alt="([^"]*)"[^>]*>/gi, '![$2]($1)'); // Lists markdown = markdown.replace(/]*>(.*?)<\/ul>/gis, (match, content) => { return content.replace(/]*>(.*?)<\/li>/gi, '- $1\n'); }); markdown = markdown.replace(/]*>(.*?)<\/ol>/gis, (match, content) => { let index = 1; return content.replace(/]*>(.*?)<\/li>/gi, () => { return `${index++}. $1\n`; }); }); // Paragraphs markdown = markdown.replace(/]*>(.*?)<\/p>/gi, '$1\n\n'); // Line breaks markdown = markdown.replace(/]*>/gi, '\n'); // Remove remaining HTML tags markdown = markdown.replace(/<[^>]*>/g, ''); // Clean up extra whitespace markdown = markdown.replace(/\n\n+/g, '\n\n').trim(); return markdown; } // Update preview function function updatePreview() { let content = ''; if (currentMode === 'richtext') { content = quill.root.innerHTML; } else if (currentMode === 'markdown') { const markdownContent = document.getElementById('markdownTextarea').value; content = marked.parse(markdownContent); } currentContent = DOMPurify.sanitize(content); paginateContent(); } // Paginate content function paginateContent() { const container = document.getElementById('previewContainer'); container.innerHTML = ''; // Create a temporary element to measure content const tempDiv = document.createElement('div'); tempDiv.style.cssText = 'position: absolute; visibility: hidden; width: 130mm; font-size: 16px; line-height: 1.6;'; tempDiv.innerHTML = currentContent; document.body.appendChild(tempDiv); // Split content into pages const maxHeight = 200; // mm - approximate content area height const elements = Array.from(tempDiv.children); pages = []; let currentPage = []; let currentHeight = 0; elements.forEach(element => { const elementHeight = element.offsetHeight / 3.78; // Convert px to mm (approximate) if (currentHeight + elementHeight > maxHeight && currentPage.length > 0) { pages.push(currentPage); currentPage = []; currentHeight = 0; } currentPage.push(element.outerHTML); currentHeight += elementHeight; }); if (currentPage.length > 0) { pages.push(currentPage); } // If no pages created, create one with all content if (pages.length === 0) { pages.push([currentContent]); } document.body.removeChild(tempDiv); // Create preview pages pages.forEach((pageContent, index) => { const pageDiv = createPreviewPage(pageContent.join(''), index + 1); container.appendChild(pageDiv); }); // Update page info document.getElementById('currentPage').textContent = '1'; document.getElementById('totalPages').textContent = pages.length; } // Create preview page function createPreviewPage(content, pageNumber) { const pageDiv = document.createElement('div'); pageDiv.className = 'preview-page'; pageDiv.innerHTML = ` OmegaTech Solution Logo

OMEGATECH SOLUTION

${content}
`; return pageDiv; } // PDF Quality Settings const pdfQualitySettings = { low: { scale: 0.8, quality: 0.7, compression: 'FAST' }, medium: { scale: 1.2, quality: 0.85, compression: 'FAST' }, high: { scale: 1.5, quality: 0.95, compression: 'SLOW' } }; // Dropdown menu functionality document.getElementById('exportPdfBtn').addEventListener('click', (e) => { e.stopPropagation(); const menu = document.getElementById('pdfQualityMenu'); menu.classList.toggle('show'); }); // Close dropdown when clicking outside document.addEventListener('click', () => { document.getElementById('pdfQualityMenu').classList.remove('show'); }); // Handle PDF export with quality selection document.querySelectorAll('.dropdown-item').forEach(item => { item.addEventListener('click', async (e) => { const quality = e.target.getAttribute('data-quality'); document.getElementById('pdfQualityMenu').classList.remove('show'); await exportPDF(quality); }); }); // Export to PDF function async function exportPDF(quality = 'medium') { const { jsPDF } = window.jspdf; const settings = pdfQualitySettings[quality]; // Show export modal const modal = createExportModal(`Generating ${quality} quality PDF...`); document.body.appendChild(modal); try { const pdf = new jsPDF('p', 'mm', 'a4', true); // Enable compression const pageWidth = pdf.internal.pageSize.getWidth(); const pageHeight = pdf.internal.pageSize.getHeight(); for (let i = 0; i < pages.length; i++) { // Update progress updateExportProgress(modal, (i / pages.length) * 100); // Create a temporary page for rendering const tempPage = createPreviewPage(pages[i].join(''), i + 1); tempPage.style.cssText = 'position: absolute; left: -9999px; width: 210mm; height: 297mm; background: white;'; document.body.appendChild(tempPage); // Wait for images to load await waitForImages(tempPage); // Render to canvas with quality-based settings const canvas = await html2canvas(tempPage, { scale: settings.scale, useCORS: true, backgroundColor: '#ffffff', logging: false, imageTimeout: 15000, removeContainer: true, allowTaint: false, foreignObjectRendering: false }); // Convert to JPEG with quality-based compression const imgData = canvas.toDataURL('image/jpeg', settings.quality); if (i > 0) { pdf.addPage(); } // Add image with compression pdf.addImage(imgData, 'JPEG', 0, 0, pageWidth, pageHeight, undefined, settings.compression); // Clean up document.body.removeChild(tempPage); // Clear canvas to free memory canvas.width = 0; canvas.height = 0; } // Update progress to 100% updateExportProgress(modal, 100); // Save PDF with compression const pdfOutput = pdf.output('blob'); const url = URL.createObjectURL(pdfOutput); const a = document.createElement('a'); a.href = url; a.download = `letter-omegatech-${quality}.pdf`; a.click(); URL.revokeObjectURL(url); // Close modal after a short delay setTimeout(() => { document.body.removeChild(modal); }, 500); } catch (error) { console.error('Error generating PDF:', error); alert('Error generating PDF. Please try again.'); document.body.removeChild(modal); } } // Export to HTML document.getElementById('exportHtml').addEventListener('click', () => { // Create HTML document let htmlContent = ` OmegaTech Solution - Letter
`; // Add each page pages.forEach((pageContent, index) => { htmlContent += `
OmegaTech Solution Logo

OMEGATECH SOLUTION

${pageContent.join('')}
`; }); htmlContent += `
`; // Download HTML file const blob = new Blob([htmlContent], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'letter-omegatech.html'; a.click(); URL.revokeObjectURL(url); }); // Helper functions function createExportModal(message) { const modal = document.createElement('div'); modal.className = 'export-modal'; modal.innerHTML = `

${message}

`; return modal; } function updateExportProgress(modal, percent) { const progressBar = modal.querySelector('.export-progress-bar'); progressBar.style.width = percent + '%'; } function waitForImages(element) { const images = element.querySelectorAll('img'); const promises = Array.from(images).map(img => { return new Promise((resolve) => { if (img.complete) { resolve(); } else { img.onload = resolve; img.onerror = resolve; } }); }); return Promise.all(promises); } function getExportStyles() { return ` body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 0; background: #f8f9fa; color: #333; line-height: 1.6; } .letterhead-container { width: 210mm; margin: 0 auto; padding: 0; box-sizing: border-box; background: white; box-shadow: 0 0 20px rgba(0,0,0,0.1); position: relative; } .page { width: 210mm; height: 297mm; padding: 40px; box-sizing: border-box; page-break-after: always; position: relative; background: white; display: flex; flex-direction: column; } .page:last-child { page-break-after: auto; } .company-logo-bg { max-width: 600px; opacity: 0.08; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1; pointer-events: none; } .header { display: flex; align-items: center; margin-bottom: 40px; position: relative; z-index: 10; flex-shrink: 0; } .header-logo { width: 60px; height: 60px; margin-right: 20px; flex-shrink: 0; } .company-info { display: flex; flex-direction: column; } .company-name { font-size: 24px; font-weight: bold; color: #2c3e50; margin: 0; letter-spacing: 0.5px; } .content-area { flex: 1; position: relative; z-index: 10; overflow: hidden; } .content-area h1 { font-size: 28px; margin: 20px 0 15px 0; color: #2c3e50; } .content-area h2 { font-size: 22px; margin: 18px 0 12px 0; color: #34495e; } .content-area h3 { font-size: 18px; margin: 15px 0 10px 0; color: #34495e; } .content-area p { margin: 10px 0; line-height: 1.6; } .content-area ul, .content-area ol { margin: 10px 0; padding-left: 25px; } .content-area img { max-width: 100%; height: auto; margin: 10px 0; } .content-area table { width: 100%; border-collapse: collapse; margin: 15px 0; } .content-area th, .content-area td { border: 1px solid #ddd; padding: 8px; text-align: left; } .content-area th { background-color: #f8f8f8; font-weight: bold; } .footer { margin-top: auto; padding-top: 25px; border-top: 1px solid #ecf0f1; position: relative; z-index: 10; flex-shrink: 0; } .footer-content { display: flex; justify-content: space-between; align-items: flex-start; max-width: 100%; } .footer-left, .footer-right { display: flex; flex-direction: column; gap: 8px; } .footer-item { display: flex; align-items: center; font-size: 12px; color: #666; } .footer-icon { width: 14px; height: 14px; margin-right: 8px; fill: #666; flex-shrink: 0; } @media print { body { background: white; } .letterhead-container { width: 100%; margin: 0; box-shadow: none; } .page { page-break-after: always; margin: 0; padding: 20mm; } .page:last-child { page-break-after: avoid; } .company-logo-bg { max-width: 500px; opacity: 0.05; } } `; } // Initialize on page load document.addEventListener('DOMContentLoaded', () => { initQuill(); updatePreview(); });