// Enhanced Document Viewer with Editing and Commenting (function() { 'use strict'; class DocumentEditor { constructor() { this.comments = {}; this.editMode = false; this.currentDocId = null; this.annotations = {}; } enableEditMode(docId) { this.editMode = true; this.currentDocId = docId; const contentArea = document.getElementById('document-content'); const content = contentArea.querySelector('.document-content'); if (content) { content.contentEditable = true; content.style.border = '2px dashed #ff4757'; content.style.padding = '20px'; // Add edit toolbar this.createEditToolbar(contentArea); // Enable text selection for commenting this.enableTextSelection(content); } } createEditToolbar(container) { const toolbar = document.createElement('div'); toolbar.className = 'document-edit-toolbar'; toolbar.style.cssText = ` position: sticky; top: 0; background: #2c3e50; padding: 10px; display: flex; gap: 10px; z-index: 150; border-radius: 4px; margin-bottom: 10px; `; toolbar.innerHTML = `
`; // Style buttons toolbar.querySelectorAll('.edit-btn').forEach(btn => { btn.style.cssText = ` padding: 6px 12px; background: #34495e; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background 0.3s; `; btn.addEventListener('mouseover', () => { btn.style.background = '#ff4757'; }); btn.addEventListener('mouseout', () => { btn.style.background = '#34495e'; }); btn.addEventListener('click', (e) => { this.handleToolbarAction(e.currentTarget.dataset.action); }); }); container.insertBefore(toolbar, container.firstChild); } handleToolbarAction(action) { const content = document.querySelector('.document-content[contenteditable="true"]'); switch(action) { case 'bold': document.execCommand('bold', false, null); break; case 'italic': document.execCommand('italic', false, null); break; case 'underline': document.execCommand('underline', false, null); break; case 'highlight': this.highlightSelection(); break; case 'comment': this.addCommentToSelection(); break; case 'save': this.saveDocument(); break; case 'export': this.exportToPDF(); break; case 'exit': this.exitEditMode(); break; } } highlightSelection() { const selection = window.getSelection(); if (selection.toString()) { const range = selection.getRangeAt(0); const span = document.createElement('span'); span.style.backgroundColor = '#fff59d'; span.className = 'document-highlight'; try { range.surroundContents(span); } catch (e) { // Handle complex selections const contents = range.extractContents(); span.appendChild(contents); range.insertNode(span); } selection.removeAllRanges(); } } addCommentToSelection() { const selection = window.getSelection(); if (selection.toString()) { const commentText = prompt('Add your comment:'); if (commentText) { const commentId = 'comment_' + Date.now(); const range = selection.getRangeAt(0); // Create comment marker const marker = document.createElement('span'); marker.className = 'comment-marker'; marker.dataset.commentId = commentId; marker.style.cssText = ` background: #ffe082; border-bottom: 2px solid #ff9800; cursor: pointer; position: relative; `; try { range.surroundContents(marker); } catch (e) { const contents = range.extractContents(); marker.appendChild(contents); range.insertNode(marker); } // Store comment this.comments[commentId] = { text: commentText, author: 'Current User', timestamp: new Date().toISOString(), position: marker.getBoundingClientRect() }; // Add click handler to show comment marker.addEventListener('click', () => { this.showComment(commentId); }); // Show comment count badge this.updateCommentCount(); selection.removeAllRanges(); } } else { alert('Please select text to comment on'); } } showComment(commentId) { const comment = this.comments[commentId]; if (comment) { // Remove existing comment popup const existingPopup = document.querySelector('.comment-popup'); if (existingPopup) { existingPopup.remove(); } // Create comment popup const popup = document.createElement('div'); popup.className = 'comment-popup'; popup.style.cssText = ` position: absolute; background: white; border: 1px solid #ccc; border-radius: 8px; padding: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 200; max-width: 300px; right: 20px; top: ${comment.position.top}px; `; popup.innerHTML = `
${comment.author}

${comment.text}

${new Date(comment.timestamp).toLocaleString()}
`; document.getElementById('document-viewer').appendChild(popup); } } updateCommentCount() { const count = Object.keys(this.comments).length; let badge = document.querySelector('.comment-count-badge'); if (!badge) { badge = document.createElement('div'); badge.className = 'comment-count-badge'; badge.style.cssText = ` position: fixed; top: 80px; right: 20px; background: #ff9800; color: white; padding: 8px 12px; border-radius: 20px; font-weight: bold; z-index: 150; `; document.getElementById('document-viewer').appendChild(badge); } badge.textContent = `💬 ${count} Comments`; } saveDocument() { const content = document.querySelector('.document-content[contenteditable="true"]'); if (content) { const savedContent = content.innerHTML; const docId = this.currentDocId || 'doc_' + Date.now(); // Save to localStorage (in real app, would save to backend) const savedDoc = { id: docId, content: savedContent, comments: this.comments, annotations: this.annotations, lastModified: new Date().toISOString(), version: 1 }; localStorage.setItem(`navada_doc_${docId}`, JSON.stringify(savedDoc)); // Show success message this.showNotification('Document saved successfully!', 'success'); return savedDoc; } } exportToPDF() { const content = document.querySelector('.document-content[contenteditable="true"]'); if (content) { // In a real implementation, this would generate a proper PDF // For now, we'll trigger a print dialog which can save as PDF const printWindow = window.open('', '_blank'); printWindow.document.write(` Document Export ${content.innerHTML} `); printWindow.document.close(); printWindow.print(); } } exitEditMode() { const content = document.querySelector('.document-content[contenteditable="true"]'); if (content) { content.contentEditable = false; content.style.border = 'none'; } // Remove toolbar const toolbar = document.querySelector('.document-edit-toolbar'); if (toolbar) { toolbar.remove(); } this.editMode = false; this.showNotification('Edit mode disabled', 'info'); } enableTextSelection(element) { element.addEventListener('mouseup', () => { const selection = window.getSelection(); if (selection.toString()) { this.showSelectionMenu(selection); } }); } showSelectionMenu(selection) { // Remove existing menu const existingMenu = document.querySelector('.selection-menu'); if (existingMenu) { existingMenu.remove(); } const range = selection.getRangeAt(0); const rect = range.getBoundingClientRect(); const menu = document.createElement('div'); menu.className = 'selection-menu'; menu.style.cssText = ` position: fixed; top: ${rect.top - 40}px; left: ${rect.left + (rect.width / 2) - 100}px; background: #2c3e50; border-radius: 4px; padding: 5px; display: flex; gap: 5px; z-index: 300; box-shadow: 0 2px 8px rgba(0,0,0,0.2); `; const actions = [ { icon: '💬', action: 'comment', title: 'Comment' }, { icon: '🖍️', action: 'highlight', title: 'Highlight' }, { icon: '📝', action: 'note', title: 'Add Note' } ]; actions.forEach(item => { const btn = document.createElement('button'); btn.textContent = item.icon; btn.title = item.title; btn.style.cssText = ` background: transparent; border: none; color: white; cursor: pointer; padding: 5px 10px; font-size: 16px; `; btn.addEventListener('click', () => { if (item.action === 'comment') { this.addCommentToSelection(); } else if (item.action === 'highlight') { this.highlightSelection(); } else if (item.action === 'note') { this.addNoteToSelection(); } menu.remove(); }); menu.appendChild(btn); }); document.body.appendChild(menu); // Remove menu when clicking elsewhere setTimeout(() => { document.addEventListener('click', function removeMenu(e) { if (!menu.contains(e.target)) { menu.remove(); document.removeEventListener('click', removeMenu); } }); }, 100); } addNoteToSelection() { const selection = window.getSelection(); if (selection.toString()) { const noteText = prompt('Add your note:'); if (noteText) { const noteId = 'note_' + Date.now(); const range = selection.getRangeAt(0); // Create note marker const marker = document.createElement('span'); marker.className = 'note-marker'; marker.dataset.noteId = noteId; marker.style.cssText = ` background: #e1f5fe; border-bottom: 2px solid #03a9f4; cursor: help; position: relative; `; marker.title = noteText; try { range.surroundContents(marker); } catch (e) { const contents = range.extractContents(); marker.appendChild(contents); range.insertNode(marker); } // Store note this.annotations[noteId] = { type: 'note', text: noteText, timestamp: new Date().toISOString() }; selection.removeAllRanges(); } } } showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.className = `document-notification ${type}`; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 15px 20px; border-radius: 8px; color: white; font-weight: 500; z-index: 1000; animation: slideIn 0.3s ease; ${type === 'success' ? 'background: #27ae60;' : ''} ${type === 'error' ? 'background: #e74c3c;' : ''} ${type === 'info' ? 'background: #3498db;' : ''} `; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.animation = 'slideOut 0.3s ease'; setTimeout(() => notification.remove(), 300); }, 3000); } // Collaborative features (simulation) simulateCollaboration() { // In a real app, this would use WebSockets setInterval(() => { const collaborators = ['Alice', 'Bob', 'Charlie']; const randomUser = collaborators[Math.floor(Math.random() * collaborators.length)]; const cursor = document.createElement('div'); cursor.className = 'collaborator-cursor'; cursor.style.cssText = ` position: absolute; width: 2px; height: 20px; background: #ff4757; top: ${Math.random() * 400}px; left: ${Math.random() * 600}px; z-index: 100; `; const label = document.createElement('div'); label.textContent = randomUser; label.style.cssText = ` position: absolute; top: -20px; left: 0; background: #ff4757; color: white; padding: 2px 6px; border-radius: 3px; font-size: 12px; white-space: nowrap; `; cursor.appendChild(label); const viewer = document.getElementById('document-viewer'); if (viewer) { viewer.appendChild(cursor); // Remove after animation setTimeout(() => cursor.remove(), 5000); } }, 10000); } } // Initialize editor window.DocumentEditor = new DocumentEditor(); // Add edit button to document viewer header function addEditButton() { const interval = setInterval(() => { const header = document.querySelector('.document-header'); if (header && !header.querySelector('.edit-document-btn')) { const editBtn = document.createElement('button'); editBtn.className = 'edit-document-btn'; editBtn.textContent = '✏️ Edit'; editBtn.style.cssText = ` padding: 6px 16px; background: #27ae60; color: white; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px; `; editBtn.addEventListener('click', () => { window.DocumentEditor.enableEditMode('current'); }); const btnContainer = header.querySelector('div'); if (btnContainer) { btnContainer.insertBefore(editBtn, btnContainer.firstChild); } clearInterval(interval); } }, 1000); } // Initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', addEditButton); } else { setTimeout(addEditButton, 1000); } // Add styles for animations const style = document.createElement('style'); style.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } .collaborator-cursor { animation: pulse 2s infinite; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } } `; document.head.appendChild(style); })();