Translsis commited on
Commit
39ab88f
·
verified ·
1 Parent(s): e818e7d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +145 -65
index.html CHANGED
@@ -4,6 +4,7 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>PDF Tab Viewer</title>
 
7
  <style>
8
  * {
9
  margin: 0;
@@ -199,13 +200,56 @@
199
  }
200
  }
201
 
202
- .pdf-viewer {
203
- width: 100%;
204
- height: 700px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  border: 2px solid #e9ecef;
206
  border-radius: 10px;
207
  background: #f8f9fa;
208
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
 
 
 
 
 
 
 
 
209
  }
210
 
211
  .empty-state {
@@ -229,13 +273,21 @@
229
  .empty-state p {
230
  font-size: 1.1em;
231
  }
 
 
 
 
 
 
 
 
232
  </style>
233
  </head>
234
  <body>
235
  <div class="container">
236
  <div class="header">
237
  <h1>📄 PDF Tab Viewer</h1>
238
- <p>Chọn hoặc kéo thả nhiều file PDF để xem</p>
239
  </div>
240
 
241
  <div class="upload-section">
@@ -265,6 +317,9 @@
265
  </div>
266
 
267
  <script>
 
 
 
268
  const uploadArea = document.getElementById('uploadArea');
269
  const fileInput = document.getElementById('fileInput');
270
  const tabsContainer = document.getElementById('tabs');
@@ -272,19 +327,16 @@
272
  const fileCountEl = document.getElementById('fileCount');
273
 
274
  let pdfFiles = [];
 
 
275
  let activeIndex = 0;
276
 
277
- // Click to select files
278
- uploadArea.addEventListener('click', () => {
279
- fileInput.click();
280
- });
281
 
282
- // File input change
283
  fileInput.addEventListener('change', (e) => {
284
  handleFiles(e.target.files);
285
  });
286
 
287
- // Drag and drop
288
  uploadArea.addEventListener('dragover', (e) => {
289
  e.preventDefault();
290
  uploadArea.classList.add('dragover');
@@ -305,28 +357,25 @@
305
  if (files.length === 0) return;
306
 
307
  const newFiles = Array.from(files);
 
308
  pdfFiles = [...pdfFiles, ...newFiles];
309
 
310
- // Update file count
311
  fileCountEl.style.display = 'inline-block';
312
  fileCountEl.textContent = `${pdfFiles.length} file đã chọn`;
313
 
314
- // Add new tabs and content
315
  newFiles.forEach((file, idx) => {
316
- const fileIndex = pdfFiles.length - newFiles.length + idx;
317
  addTab(file, fileIndex);
318
  });
319
 
320
- // Activate first new file
321
  if (newFiles.length > 0) {
322
- switchTab(pdfFiles.length - newFiles.length);
323
  }
324
  }
325
 
326
  function addTab(file, index) {
327
- // Create tab
328
  const tab = document.createElement('button');
329
- tab.className = 'tab' + (pdfFiles.length === 1 ? ' active' : '');
330
  tab.innerHTML = `
331
  <span>📄</span>
332
  <span>${file.name}</span>
@@ -336,90 +385,122 @@
336
  tab.dataset.index = index;
337
  tabsContainer.appendChild(tab);
338
 
339
- // Create content
340
  const tabContent = document.createElement('div');
341
- tabContent.className = 'tab-content' + (pdfFiles.length === 1 ? ' active' : '');
342
  tabContent.dataset.index = index;
343
-
344
- // Create PDF viewer - dùng object thay vì embed
345
- const reader = new FileReader();
346
- reader.onload = function(e) {
347
- const objectEl = document.createElement('object');
348
- objectEl.data = e.target.result;
349
- objectEl.type = 'application/pdf';
350
- objectEl.className = 'pdf-viewer';
351
-
352
- // Fallback nếu không load được
353
- objectEl.innerHTML = `
354
- <div style="padding: 50px; text-align: center;">
355
- <h2>📄 ${file.name}</h2>
356
- <p style="margin: 20px 0; color: #6c757d;">Trình duyệt không hỗ trợ xem PDF trực tiếp</p>
357
- <a href="${e.target.result}" download="${file.name}"
358
- style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
359
- color: white; padding: 15px 30px; border-radius: 50px;
360
- text-decoration: none; display: inline-block; font-weight: bold;">
361
- ⬇️ Tải xuống PDF
362
- </a>
363
- <p style="margin-top: 20px; color: #6c757d; font-size: 0.9em;">
364
- Hoặc mở bằng: Chrome, Edge, Firefox (phiên bản mới nhất)
365
- </p>
366
- </div>
367
- `;
368
-
369
- tabContent.appendChild(objectEl);
370
- };
371
- reader.readAsDataURL(file);
372
 
373
  contentContainer.innerHTML = '';
374
  const allContents = document.querySelectorAll('.tab-content');
375
  allContents.forEach(c => contentContainer.appendChild(c));
376
  contentContainer.appendChild(tabContent);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  }
378
 
379
  function switchTab(index) {
380
  activeIndex = index;
381
 
382
- // Update tabs
383
  const tabs = tabsContainer.querySelectorAll('.tab');
384
- tabs.forEach((tab, i) => {
385
  tab.classList.toggle('active', parseInt(tab.dataset.index) === index);
386
  });
387
 
388
- // Update content
389
  const contents = contentContainer.querySelectorAll('.tab-content');
390
- contents.forEach((content, i) => {
391
  content.classList.toggle('active', parseInt(content.dataset.index) === index);
392
  });
393
  }
394
 
395
  function closeTab(index) {
396
- // Remove from array
397
  pdfFiles.splice(index, 1);
 
 
398
 
399
- // Remove tab
400
  const tab = tabsContainer.querySelector(`[data-index="${index}"]`);
401
  if (tab) tab.remove();
402
 
403
- // Remove content
404
  const content = contentContainer.querySelector(`[data-index="${index}"]`);
405
  if (content) content.remove();
406
 
407
- // Update indices
408
  const tabs = tabsContainer.querySelectorAll('.tab');
409
- tabs.forEach((tab, i) => {
410
- if (parseInt(tab.dataset.index) > index) {
411
- tab.dataset.index = parseInt(tab.dataset.index) - 1;
 
 
 
 
412
  }
413
  });
414
 
415
  const contents = contentContainer.querySelectorAll('.tab-content');
416
- contents.forEach((content, i) => {
417
- if (parseInt(content.dataset.index) > index) {
418
- content.dataset.index = parseInt(content.dataset.index) - 1;
 
419
  }
420
  });
421
 
422
- // Update file count
423
  if (pdfFiles.length === 0) {
424
  fileCountEl.style.display = 'none';
425
  contentContainer.innerHTML = `
@@ -436,7 +517,6 @@
436
  `;
437
  } else {
438
  fileCountEl.textContent = `${pdfFiles.length} file đã chọn`;
439
- // Switch to first tab if active was closed
440
  if (activeIndex >= pdfFiles.length) {
441
  switchTab(Math.max(0, pdfFiles.length - 1));
442
  }
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>PDF Tab Viewer</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
8
  <style>
9
  * {
10
  margin: 0;
 
200
  }
201
  }
202
 
203
+ .pdf-controls {
204
+ display: flex;
205
+ justify-content: center;
206
+ align-items: center;
207
+ gap: 15px;
208
+ margin-bottom: 20px;
209
+ padding: 15px;
210
+ background: #f8f9fa;
211
+ border-radius: 10px;
212
+ }
213
+
214
+ .pdf-controls button {
215
+ padding: 10px 20px;
216
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
217
+ color: white;
218
+ border: none;
219
+ border-radius: 8px;
220
+ cursor: pointer;
221
+ font-weight: bold;
222
+ transition: all 0.3s ease;
223
+ }
224
+
225
+ .pdf-controls button:hover:not(:disabled) {
226
+ transform: translateY(-2px);
227
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
228
+ }
229
+
230
+ .pdf-controls button:disabled {
231
+ opacity: 0.5;
232
+ cursor: not-allowed;
233
+ }
234
+
235
+ .pdf-controls span {
236
+ font-weight: bold;
237
+ color: #495057;
238
+ }
239
+
240
+ .pdf-viewer-container {
241
  border: 2px solid #e9ecef;
242
  border-radius: 10px;
243
  background: #f8f9fa;
244
+ padding: 20px;
245
+ overflow: auto;
246
+ max-height: 700px;
247
+ }
248
+
249
+ .pdf-canvas {
250
+ display: block;
251
+ margin: 0 auto;
252
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
253
  }
254
 
255
  .empty-state {
 
273
  .empty-state p {
274
  font-size: 1.1em;
275
  }
276
+
277
+ .loading {
278
+ text-align: center;
279
+ padding: 50px;
280
+ color: #667eea;
281
+ font-size: 1.2em;
282
+ font-weight: bold;
283
+ }
284
  </style>
285
  </head>
286
  <body>
287
  <div class="container">
288
  <div class="header">
289
  <h1>📄 PDF Tab Viewer</h1>
290
+ <p>Chọn hoặc kéo thả nhiều file PDF để xem (hỗ trợ mọi trình duyệt)</p>
291
  </div>
292
 
293
  <div class="upload-section">
 
317
  </div>
318
 
319
  <script>
320
+ // Setup PDF.js worker
321
+ pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
322
+
323
  const uploadArea = document.getElementById('uploadArea');
324
  const fileInput = document.getElementById('fileInput');
325
  const tabsContainer = document.getElementById('tabs');
 
327
  const fileCountEl = document.getElementById('fileCount');
328
 
329
  let pdfFiles = [];
330
+ let pdfDocs = [];
331
+ let currentPages = [];
332
  let activeIndex = 0;
333
 
334
+ uploadArea.addEventListener('click', () => fileInput.click());
 
 
 
335
 
 
336
  fileInput.addEventListener('change', (e) => {
337
  handleFiles(e.target.files);
338
  });
339
 
 
340
  uploadArea.addEventListener('dragover', (e) => {
341
  e.preventDefault();
342
  uploadArea.classList.add('dragover');
 
357
  if (files.length === 0) return;
358
 
359
  const newFiles = Array.from(files);
360
+ const startIndex = pdfFiles.length;
361
  pdfFiles = [...pdfFiles, ...newFiles];
362
 
 
363
  fileCountEl.style.display = 'inline-block';
364
  fileCountEl.textContent = `${pdfFiles.length} file đã chọn`;
365
 
 
366
  newFiles.forEach((file, idx) => {
367
+ const fileIndex = startIndex + idx;
368
  addTab(file, fileIndex);
369
  });
370
 
 
371
  if (newFiles.length > 0) {
372
+ switchTab(startIndex);
373
  }
374
  }
375
 
376
  function addTab(file, index) {
 
377
  const tab = document.createElement('button');
378
+ tab.className = 'tab' + (index === 0 ? ' active' : '');
379
  tab.innerHTML = `
380
  <span>📄</span>
381
  <span>${file.name}</span>
 
385
  tab.dataset.index = index;
386
  tabsContainer.appendChild(tab);
387
 
 
388
  const tabContent = document.createElement('div');
389
+ tabContent.className = 'tab-content' + (index === 0 ? ' active' : '');
390
  tabContent.dataset.index = index;
391
+ tabContent.innerHTML = '<div class="loading">⏳ Đang tải PDF...</div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
 
393
  contentContainer.innerHTML = '';
394
  const allContents = document.querySelectorAll('.tab-content');
395
  allContents.forEach(c => contentContainer.appendChild(c));
396
  contentContainer.appendChild(tabContent);
397
+
398
+ loadPDF(file, index, tabContent);
399
+ }
400
+
401
+ async function loadPDF(file, index, container) {
402
+ try {
403
+ const arrayBuffer = await file.arrayBuffer();
404
+ const pdf = await pdfjsLib.getDocument({data: arrayBuffer}).promise;
405
+
406
+ pdfDocs[index] = pdf;
407
+ currentPages[index] = 1;
408
+
409
+ container.innerHTML = `
410
+ <div class="pdf-controls">
411
+ <button onclick="changePage(${index}, -1)">◀ Trang trước</button>
412
+ <span>Trang <span id="pageNum${index}">1</span> / <span id="pageCount${index}">${pdf.numPages}</span></span>
413
+ <button onclick="changePage(${index}, 1)">Trang sau ▶</button>
414
+ </div>
415
+ <div class="pdf-viewer-container">
416
+ <canvas id="pdfCanvas${index}" class="pdf-canvas"></canvas>
417
+ </div>
418
+ `;
419
+
420
+ renderPage(index, 1);
421
+ } catch (error) {
422
+ container.innerHTML = `
423
+ <div style="text-align: center; padding: 50px; color: #dc3545;">
424
+ <h2>❌ Lỗi khi tải PDF</h2>
425
+ <p>${error.message}</p>
426
+ </div>
427
+ `;
428
+ }
429
+ }
430
+
431
+ async function renderPage(index, pageNum) {
432
+ const pdf = pdfDocs[index];
433
+ const page = await pdf.getPage(pageNum);
434
+
435
+ const canvas = document.getElementById(`pdfCanvas${index}`);
436
+ const context = canvas.getContext('2d');
437
+
438
+ const viewport = page.getViewport({scale: 1.5});
439
+ canvas.height = viewport.height;
440
+ canvas.width = viewport.width;
441
+
442
+ await page.render({
443
+ canvasContext: context,
444
+ viewport: viewport
445
+ }).promise;
446
+
447
+ currentPages[index] = pageNum;
448
+ document.getElementById(`pageNum${index}`).textContent = pageNum;
449
+ }
450
+
451
+ function changePage(index, delta) {
452
+ const newPage = currentPages[index] + delta;
453
+ const pdf = pdfDocs[index];
454
+
455
+ if (newPage >= 1 && newPage <= pdf.numPages) {
456
+ renderPage(index, newPage);
457
+ }
458
  }
459
 
460
  function switchTab(index) {
461
  activeIndex = index;
462
 
 
463
  const tabs = tabsContainer.querySelectorAll('.tab');
464
+ tabs.forEach((tab) => {
465
  tab.classList.toggle('active', parseInt(tab.dataset.index) === index);
466
  });
467
 
 
468
  const contents = contentContainer.querySelectorAll('.tab-content');
469
+ contents.forEach((content) => {
470
  content.classList.toggle('active', parseInt(content.dataset.index) === index);
471
  });
472
  }
473
 
474
  function closeTab(index) {
 
475
  pdfFiles.splice(index, 1);
476
+ pdfDocs.splice(index, 1);
477
+ currentPages.splice(index, 1);
478
 
 
479
  const tab = tabsContainer.querySelector(`[data-index="${index}"]`);
480
  if (tab) tab.remove();
481
 
 
482
  const content = contentContainer.querySelector(`[data-index="${index}"]`);
483
  if (content) content.remove();
484
 
 
485
  const tabs = tabsContainer.querySelectorAll('.tab');
486
+ tabs.forEach((tab) => {
487
+ const idx = parseInt(tab.dataset.index);
488
+ if (idx > index) {
489
+ tab.dataset.index = idx - 1;
490
+ const closeBtn = tab.querySelector('.close-tab');
491
+ closeBtn.setAttribute('onclick', `event.stopPropagation(); closeTab(${idx - 1})`);
492
+ tab.onclick = () => switchTab(idx - 1);
493
  }
494
  });
495
 
496
  const contents = contentContainer.querySelectorAll('.tab-content');
497
+ contents.forEach((content) => {
498
+ const idx = parseInt(content.dataset.index);
499
+ if (idx > index) {
500
+ content.dataset.index = idx - 1;
501
  }
502
  });
503
 
 
504
  if (pdfFiles.length === 0) {
505
  fileCountEl.style.display = 'none';
506
  contentContainer.innerHTML = `
 
517
  `;
518
  } else {
519
  fileCountEl.textContent = `${pdfFiles.length} file đã chọn`;
 
520
  if (activeIndex >= pdfFiles.length) {
521
  switchTab(Math.max(0, pdfFiles.length - 1));
522
  }