Navada25 commited on
Commit
0abe894
·
verified ·
1 Parent(s): e37b775

Update public/document_editor.js for document viewer

Browse files
Files changed (1) hide show
  1. public/document_editor.js +601 -0
public/document_editor.js ADDED
@@ -0,0 +1,601 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Enhanced Document Viewer with Editing and Commenting
2
+ (function() {
3
+ 'use strict';
4
+
5
+ class DocumentEditor {
6
+ constructor() {
7
+ this.comments = {};
8
+ this.editMode = false;
9
+ this.currentDocId = null;
10
+ this.annotations = {};
11
+ }
12
+
13
+ enableEditMode(docId) {
14
+ this.editMode = true;
15
+ this.currentDocId = docId;
16
+
17
+ const contentArea = document.getElementById('document-content');
18
+ const content = contentArea.querySelector('.document-content');
19
+
20
+ if (content) {
21
+ content.contentEditable = true;
22
+ content.style.border = '2px dashed #ff4757';
23
+ content.style.padding = '20px';
24
+
25
+ // Add edit toolbar
26
+ this.createEditToolbar(contentArea);
27
+
28
+ // Enable text selection for commenting
29
+ this.enableTextSelection(content);
30
+ }
31
+ }
32
+
33
+ createEditToolbar(container) {
34
+ const toolbar = document.createElement('div');
35
+ toolbar.className = 'document-edit-toolbar';
36
+ toolbar.style.cssText = `
37
+ position: sticky;
38
+ top: 0;
39
+ background: #2c3e50;
40
+ padding: 10px;
41
+ display: flex;
42
+ gap: 10px;
43
+ z-index: 150;
44
+ border-radius: 4px;
45
+ margin-bottom: 10px;
46
+ `;
47
+
48
+ toolbar.innerHTML = `
49
+ <button class="edit-btn" data-action="bold" title="Bold">
50
+ <b>B</b>
51
+ </button>
52
+ <button class="edit-btn" data-action="italic" title="Italic">
53
+ <i>I</i>
54
+ </button>
55
+ <button class="edit-btn" data-action="underline" title="Underline">
56
+ <u>U</u>
57
+ </button>
58
+ <button class="edit-btn" data-action="highlight" title="Highlight">
59
+ 🖍️
60
+ </button>
61
+ <div style="border-left: 1px solid #666; margin: 0 5px;"></div>
62
+ <button class="edit-btn" data-action="comment" title="Add Comment">
63
+ 💬
64
+ </button>
65
+ <button class="edit-btn" data-action="save" title="Save Changes">
66
+ 💾
67
+ </button>
68
+ <button class="edit-btn" data-action="export" title="Export PDF">
69
+ 📄
70
+ </button>
71
+ <div style="margin-left: auto;">
72
+ <button class="edit-btn" data-action="exit" title="Exit Edit Mode">
73
+ ✖️
74
+ </button>
75
+ </div>
76
+ `;
77
+
78
+ // Style buttons
79
+ toolbar.querySelectorAll('.edit-btn').forEach(btn => {
80
+ btn.style.cssText = `
81
+ padding: 6px 12px;
82
+ background: #34495e;
83
+ color: white;
84
+ border: none;
85
+ border-radius: 4px;
86
+ cursor: pointer;
87
+ font-size: 14px;
88
+ transition: background 0.3s;
89
+ `;
90
+
91
+ btn.addEventListener('mouseover', () => {
92
+ btn.style.background = '#ff4757';
93
+ });
94
+
95
+ btn.addEventListener('mouseout', () => {
96
+ btn.style.background = '#34495e';
97
+ });
98
+
99
+ btn.addEventListener('click', (e) => {
100
+ this.handleToolbarAction(e.currentTarget.dataset.action);
101
+ });
102
+ });
103
+
104
+ container.insertBefore(toolbar, container.firstChild);
105
+ }
106
+
107
+ handleToolbarAction(action) {
108
+ const content = document.querySelector('.document-content[contenteditable="true"]');
109
+
110
+ switch(action) {
111
+ case 'bold':
112
+ document.execCommand('bold', false, null);
113
+ break;
114
+ case 'italic':
115
+ document.execCommand('italic', false, null);
116
+ break;
117
+ case 'underline':
118
+ document.execCommand('underline', false, null);
119
+ break;
120
+ case 'highlight':
121
+ this.highlightSelection();
122
+ break;
123
+ case 'comment':
124
+ this.addCommentToSelection();
125
+ break;
126
+ case 'save':
127
+ this.saveDocument();
128
+ break;
129
+ case 'export':
130
+ this.exportToPDF();
131
+ break;
132
+ case 'exit':
133
+ this.exitEditMode();
134
+ break;
135
+ }
136
+ }
137
+
138
+ highlightSelection() {
139
+ const selection = window.getSelection();
140
+ if (selection.toString()) {
141
+ const range = selection.getRangeAt(0);
142
+ const span = document.createElement('span');
143
+ span.style.backgroundColor = '#fff59d';
144
+ span.className = 'document-highlight';
145
+
146
+ try {
147
+ range.surroundContents(span);
148
+ } catch (e) {
149
+ // Handle complex selections
150
+ const contents = range.extractContents();
151
+ span.appendChild(contents);
152
+ range.insertNode(span);
153
+ }
154
+
155
+ selection.removeAllRanges();
156
+ }
157
+ }
158
+
159
+ addCommentToSelection() {
160
+ const selection = window.getSelection();
161
+ if (selection.toString()) {
162
+ const commentText = prompt('Add your comment:');
163
+ if (commentText) {
164
+ const commentId = 'comment_' + Date.now();
165
+ const range = selection.getRangeAt(0);
166
+
167
+ // Create comment marker
168
+ const marker = document.createElement('span');
169
+ marker.className = 'comment-marker';
170
+ marker.dataset.commentId = commentId;
171
+ marker.style.cssText = `
172
+ background: #ffe082;
173
+ border-bottom: 2px solid #ff9800;
174
+ cursor: pointer;
175
+ position: relative;
176
+ `;
177
+
178
+ try {
179
+ range.surroundContents(marker);
180
+ } catch (e) {
181
+ const contents = range.extractContents();
182
+ marker.appendChild(contents);
183
+ range.insertNode(marker);
184
+ }
185
+
186
+ // Store comment
187
+ this.comments[commentId] = {
188
+ text: commentText,
189
+ author: 'Current User',
190
+ timestamp: new Date().toISOString(),
191
+ position: marker.getBoundingClientRect()
192
+ };
193
+
194
+ // Add click handler to show comment
195
+ marker.addEventListener('click', () => {
196
+ this.showComment(commentId);
197
+ });
198
+
199
+ // Show comment count badge
200
+ this.updateCommentCount();
201
+
202
+ selection.removeAllRanges();
203
+ }
204
+ } else {
205
+ alert('Please select text to comment on');
206
+ }
207
+ }
208
+
209
+ showComment(commentId) {
210
+ const comment = this.comments[commentId];
211
+ if (comment) {
212
+ // Remove existing comment popup
213
+ const existingPopup = document.querySelector('.comment-popup');
214
+ if (existingPopup) {
215
+ existingPopup.remove();
216
+ }
217
+
218
+ // Create comment popup
219
+ const popup = document.createElement('div');
220
+ popup.className = 'comment-popup';
221
+ popup.style.cssText = `
222
+ position: absolute;
223
+ background: white;
224
+ border: 1px solid #ccc;
225
+ border-radius: 8px;
226
+ padding: 15px;
227
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
228
+ z-index: 200;
229
+ max-width: 300px;
230
+ right: 20px;
231
+ top: ${comment.position.top}px;
232
+ `;
233
+
234
+ popup.innerHTML = `
235
+ <div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
236
+ <strong style="color: #2c3e50;">${comment.author}</strong>
237
+ <button onclick="this.parentElement.parentElement.remove()"
238
+ style="border: none; background: none; cursor: pointer; color: #999;">✖</button>
239
+ </div>
240
+ <p style="color: #333; margin: 10px 0;">${comment.text}</p>
241
+ <small style="color: #999;">${new Date(comment.timestamp).toLocaleString()}</small>
242
+ <div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #eee;">
243
+ <input type="text" placeholder="Reply..."
244
+ style="width: 100%; padding: 5px; border: 1px solid #ddd; border-radius: 4px;">
245
+ </div>
246
+ `;
247
+
248
+ document.getElementById('document-viewer').appendChild(popup);
249
+ }
250
+ }
251
+
252
+ updateCommentCount() {
253
+ const count = Object.keys(this.comments).length;
254
+ let badge = document.querySelector('.comment-count-badge');
255
+
256
+ if (!badge) {
257
+ badge = document.createElement('div');
258
+ badge.className = 'comment-count-badge';
259
+ badge.style.cssText = `
260
+ position: fixed;
261
+ top: 80px;
262
+ right: 20px;
263
+ background: #ff9800;
264
+ color: white;
265
+ padding: 8px 12px;
266
+ border-radius: 20px;
267
+ font-weight: bold;
268
+ z-index: 150;
269
+ `;
270
+ document.getElementById('document-viewer').appendChild(badge);
271
+ }
272
+
273
+ badge.textContent = `💬 ${count} Comments`;
274
+ }
275
+
276
+ saveDocument() {
277
+ const content = document.querySelector('.document-content[contenteditable="true"]');
278
+ if (content) {
279
+ const savedContent = content.innerHTML;
280
+ const docId = this.currentDocId || 'doc_' + Date.now();
281
+
282
+ // Save to localStorage (in real app, would save to backend)
283
+ const savedDoc = {
284
+ id: docId,
285
+ content: savedContent,
286
+ comments: this.comments,
287
+ annotations: this.annotations,
288
+ lastModified: new Date().toISOString(),
289
+ version: 1
290
+ };
291
+
292
+ localStorage.setItem(`navada_doc_${docId}`, JSON.stringify(savedDoc));
293
+
294
+ // Show success message
295
+ this.showNotification('Document saved successfully!', 'success');
296
+
297
+ return savedDoc;
298
+ }
299
+ }
300
+
301
+ exportToPDF() {
302
+ const content = document.querySelector('.document-content[contenteditable="true"]');
303
+ if (content) {
304
+ // In a real implementation, this would generate a proper PDF
305
+ // For now, we'll trigger a print dialog which can save as PDF
306
+ const printWindow = window.open('', '_blank');
307
+ printWindow.document.write(`
308
+ <html>
309
+ <head>
310
+ <title>Document Export</title>
311
+ <style>
312
+ body { font-family: Arial, sans-serif; padding: 20px; }
313
+ .comment-marker { background: #ffe082; }
314
+ .document-highlight { background: #fff59d; }
315
+ </style>
316
+ </head>
317
+ <body>
318
+ ${content.innerHTML}
319
+ </body>
320
+ </html>
321
+ `);
322
+ printWindow.document.close();
323
+ printWindow.print();
324
+ }
325
+ }
326
+
327
+ exitEditMode() {
328
+ const content = document.querySelector('.document-content[contenteditable="true"]');
329
+ if (content) {
330
+ content.contentEditable = false;
331
+ content.style.border = 'none';
332
+ }
333
+
334
+ // Remove toolbar
335
+ const toolbar = document.querySelector('.document-edit-toolbar');
336
+ if (toolbar) {
337
+ toolbar.remove();
338
+ }
339
+
340
+ this.editMode = false;
341
+ this.showNotification('Edit mode disabled', 'info');
342
+ }
343
+
344
+ enableTextSelection(element) {
345
+ element.addEventListener('mouseup', () => {
346
+ const selection = window.getSelection();
347
+ if (selection.toString()) {
348
+ this.showSelectionMenu(selection);
349
+ }
350
+ });
351
+ }
352
+
353
+ showSelectionMenu(selection) {
354
+ // Remove existing menu
355
+ const existingMenu = document.querySelector('.selection-menu');
356
+ if (existingMenu) {
357
+ existingMenu.remove();
358
+ }
359
+
360
+ const range = selection.getRangeAt(0);
361
+ const rect = range.getBoundingClientRect();
362
+
363
+ const menu = document.createElement('div');
364
+ menu.className = 'selection-menu';
365
+ menu.style.cssText = `
366
+ position: fixed;
367
+ top: ${rect.top - 40}px;
368
+ left: ${rect.left + (rect.width / 2) - 100}px;
369
+ background: #2c3e50;
370
+ border-radius: 4px;
371
+ padding: 5px;
372
+ display: flex;
373
+ gap: 5px;
374
+ z-index: 300;
375
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
376
+ `;
377
+
378
+ const actions = [
379
+ { icon: '💬', action: 'comment', title: 'Comment' },
380
+ { icon: '🖍️', action: 'highlight', title: 'Highlight' },
381
+ { icon: '📝', action: 'note', title: 'Add Note' }
382
+ ];
383
+
384
+ actions.forEach(item => {
385
+ const btn = document.createElement('button');
386
+ btn.textContent = item.icon;
387
+ btn.title = item.title;
388
+ btn.style.cssText = `
389
+ background: transparent;
390
+ border: none;
391
+ color: white;
392
+ cursor: pointer;
393
+ padding: 5px 10px;
394
+ font-size: 16px;
395
+ `;
396
+
397
+ btn.addEventListener('click', () => {
398
+ if (item.action === 'comment') {
399
+ this.addCommentToSelection();
400
+ } else if (item.action === 'highlight') {
401
+ this.highlightSelection();
402
+ } else if (item.action === 'note') {
403
+ this.addNoteToSelection();
404
+ }
405
+ menu.remove();
406
+ });
407
+
408
+ menu.appendChild(btn);
409
+ });
410
+
411
+ document.body.appendChild(menu);
412
+
413
+ // Remove menu when clicking elsewhere
414
+ setTimeout(() => {
415
+ document.addEventListener('click', function removeMenu(e) {
416
+ if (!menu.contains(e.target)) {
417
+ menu.remove();
418
+ document.removeEventListener('click', removeMenu);
419
+ }
420
+ });
421
+ }, 100);
422
+ }
423
+
424
+ addNoteToSelection() {
425
+ const selection = window.getSelection();
426
+ if (selection.toString()) {
427
+ const noteText = prompt('Add your note:');
428
+ if (noteText) {
429
+ const noteId = 'note_' + Date.now();
430
+ const range = selection.getRangeAt(0);
431
+
432
+ // Create note marker
433
+ const marker = document.createElement('span');
434
+ marker.className = 'note-marker';
435
+ marker.dataset.noteId = noteId;
436
+ marker.style.cssText = `
437
+ background: #e1f5fe;
438
+ border-bottom: 2px solid #03a9f4;
439
+ cursor: help;
440
+ position: relative;
441
+ `;
442
+ marker.title = noteText;
443
+
444
+ try {
445
+ range.surroundContents(marker);
446
+ } catch (e) {
447
+ const contents = range.extractContents();
448
+ marker.appendChild(contents);
449
+ range.insertNode(marker);
450
+ }
451
+
452
+ // Store note
453
+ this.annotations[noteId] = {
454
+ type: 'note',
455
+ text: noteText,
456
+ timestamp: new Date().toISOString()
457
+ };
458
+
459
+ selection.removeAllRanges();
460
+ }
461
+ }
462
+ }
463
+
464
+ showNotification(message, type = 'info') {
465
+ const notification = document.createElement('div');
466
+ notification.className = `document-notification ${type}`;
467
+ notification.style.cssText = `
468
+ position: fixed;
469
+ top: 20px;
470
+ right: 20px;
471
+ padding: 15px 20px;
472
+ border-radius: 8px;
473
+ color: white;
474
+ font-weight: 500;
475
+ z-index: 1000;
476
+ animation: slideIn 0.3s ease;
477
+ ${type === 'success' ? 'background: #27ae60;' : ''}
478
+ ${type === 'error' ? 'background: #e74c3c;' : ''}
479
+ ${type === 'info' ? 'background: #3498db;' : ''}
480
+ `;
481
+
482
+ notification.textContent = message;
483
+ document.body.appendChild(notification);
484
+
485
+ setTimeout(() => {
486
+ notification.style.animation = 'slideOut 0.3s ease';
487
+ setTimeout(() => notification.remove(), 300);
488
+ }, 3000);
489
+ }
490
+
491
+ // Collaborative features (simulation)
492
+ simulateCollaboration() {
493
+ // In a real app, this would use WebSockets
494
+ setInterval(() => {
495
+ const collaborators = ['Alice', 'Bob', 'Charlie'];
496
+ const randomUser = collaborators[Math.floor(Math.random() * collaborators.length)];
497
+
498
+ const cursor = document.createElement('div');
499
+ cursor.className = 'collaborator-cursor';
500
+ cursor.style.cssText = `
501
+ position: absolute;
502
+ width: 2px;
503
+ height: 20px;
504
+ background: #ff4757;
505
+ top: ${Math.random() * 400}px;
506
+ left: ${Math.random() * 600}px;
507
+ z-index: 100;
508
+ `;
509
+
510
+ const label = document.createElement('div');
511
+ label.textContent = randomUser;
512
+ label.style.cssText = `
513
+ position: absolute;
514
+ top: -20px;
515
+ left: 0;
516
+ background: #ff4757;
517
+ color: white;
518
+ padding: 2px 6px;
519
+ border-radius: 3px;
520
+ font-size: 12px;
521
+ white-space: nowrap;
522
+ `;
523
+
524
+ cursor.appendChild(label);
525
+
526
+ const viewer = document.getElementById('document-viewer');
527
+ if (viewer) {
528
+ viewer.appendChild(cursor);
529
+
530
+ // Remove after animation
531
+ setTimeout(() => cursor.remove(), 5000);
532
+ }
533
+ }, 10000);
534
+ }
535
+ }
536
+
537
+ // Initialize editor
538
+ window.DocumentEditor = new DocumentEditor();
539
+
540
+ // Add edit button to document viewer header
541
+ function addEditButton() {
542
+ const interval = setInterval(() => {
543
+ const header = document.querySelector('.document-header');
544
+ if (header && !header.querySelector('.edit-document-btn')) {
545
+ const editBtn = document.createElement('button');
546
+ editBtn.className = 'edit-document-btn';
547
+ editBtn.textContent = '✏️ Edit';
548
+ editBtn.style.cssText = `
549
+ padding: 6px 16px;
550
+ background: #27ae60;
551
+ color: white;
552
+ border: none;
553
+ border-radius: 4px;
554
+ cursor: pointer;
555
+ margin-right: 10px;
556
+ `;
557
+
558
+ editBtn.addEventListener('click', () => {
559
+ window.DocumentEditor.enableEditMode('current');
560
+ });
561
+
562
+ const btnContainer = header.querySelector('div');
563
+ if (btnContainer) {
564
+ btnContainer.insertBefore(editBtn, btnContainer.firstChild);
565
+ }
566
+
567
+ clearInterval(interval);
568
+ }
569
+ }, 1000);
570
+ }
571
+
572
+ // Initialize when DOM is ready
573
+ if (document.readyState === 'loading') {
574
+ document.addEventListener('DOMContentLoaded', addEditButton);
575
+ } else {
576
+ setTimeout(addEditButton, 1000);
577
+ }
578
+
579
+ // Add styles for animations
580
+ const style = document.createElement('style');
581
+ style.textContent = `
582
+ @keyframes slideIn {
583
+ from { transform: translateX(100%); opacity: 0; }
584
+ to { transform: translateX(0); opacity: 1; }
585
+ }
586
+ @keyframes slideOut {
587
+ from { transform: translateX(0); opacity: 1; }
588
+ to { transform: translateX(100%); opacity: 0; }
589
+ }
590
+ .collaborator-cursor {
591
+ animation: pulse 2s infinite;
592
+ }
593
+ @keyframes pulse {
594
+ 0% { opacity: 1; }
595
+ 50% { opacity: 0.5; }
596
+ 100% { opacity: 1; }
597
+ }
598
+ `;
599
+ document.head.appendChild(style);
600
+
601
+ })();