pdf-list / index.html
bep40's picture
Add 3 files
222671a verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PDF Viewer Hub</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>
<style>
.pdf-thumbnail:hover .pdf-overlay {
opacity: 1;
}
.pdf-overlay {
transition: opacity 0.3s ease;
}
.pdf-container {
height: 600px;
overflow: auto;
position: relative;
}
.pdf-fullscreen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
background: white;
overflow: auto;
}
.search-highlight {
background-color: rgba(255, 255, 0, 0.5);
}
</style>
</head>
<body class="bg-gray-100">
<div class="container mx-auto px-4 py-8">
<header class="mb-10 text-center">
<h1 class="text-4xl font-bold text-indigo-700 mb-2">PDF Viewer Hub</h1>
<p class="text-gray-600 text-lg">View, search and download PDF documents</p>
</header>
<div class="flex flex-wrap -mx-2">
<!-- PDF Thumbnails -->
<div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4">
<div class="pdf-thumbnail bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 relative">
<img src="https://via.placeholder.com/300x400/4F46E5/FFFFFF?text=Sample+PDF+1" alt="Sample PDF 1" class="w-full h-64 object-cover">
<div class="pdf-overlay absolute inset-0 bg-black bg-opacity-50 opacity-0 flex items-center justify-center">
<button onclick="openPdfModal('sample1.pdf', 'Sample Document 1')" class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition">
<i class="fas fa-eye mr-1"></i> View
</button>
<a href="sample1.pdf" download class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition">
<i class="fas fa-download mr-1"></i> Download
</a>
</div>
<div class="p-4">
<h3 class="font-semibold text-lg mb-1">Sample Document 1</h3>
<p class="text-gray-500 text-sm">2.4 MB • 12 pages</p>
</div>
</div>
</div>
<div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4">
<div class="pdf-thumbnail bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 relative">
<img src="https://via.placeholder.com/300x400/4F46E5/FFFFFF?text=Sample+PDF+2" alt="Sample PDF 2" class="w-full h-64 object-cover">
<div class="pdf-overlay absolute inset-0 bg-black bg-opacity-50 opacity-0 flex items-center justify-center">
<button onclick="openPdfModal('sample2.pdf', 'Sample Document 2')" class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition">
<i class="fas fa-eye mr-1"></i> View
</button>
<a href="sample2.pdf" download class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition">
<i class="fas fa-download mr-1"></i> Download
</a>
</div>
<div class="p-4">
<h3 class="font-semibold text-lg mb-1">Sample Document 2</h3>
<p class="text-gray-500 text-sm">3.1 MB • 18 pages</p>
</div>
</div>
</div>
<div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4">
<div class="pdf-thumbnail bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 relative">
<img src="https://via.placeholder.com/300x400/4F46E5/FFFFFF?text=Sample+PDF+3" alt="Sample PDF 3" class="w-full h-64 object-cover">
<div class="pdf-overlay absolute inset-0 bg-black bg-opacity-50 opacity-0 flex items-center justify-center">
<button onclick="openPdfModal('sample3.pdf', 'Sample Document 3')" class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition">
<i class="fas fa-eye mr-1"></i> View
</button>
<a href="sample3.pdf" download class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition">
<i class="fas fa-download mr-1"></i> Download
</a>
</div>
<div class="p-4">
<h3 class="font-semibold text-lg mb-1">Sample Document 3</h3>
<p class="text-gray-500 text-sm">1.8 MB • 8 pages</p>
</div>
</div>
</div>
<div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4">
<div class="pdf-thumbnail bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 relative">
<img src="https://via.placeholder.com/300x400/4F46E5/FFFFFF?text=Sample+PDF+4" alt="Sample PDF 4" class="w-full h-64 object-cover">
<div class="pdf-overlay absolute inset-0 bg-black bg-opacity-50 opacity-0 flex items-center justify-center">
<button onclick="openPdfModal('sample4.pdf', 'Sample Document 4')" class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition">
<i class="fas fa-eye mr-1"></i> View
</button>
<a href="sample4.pdf" download class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition">
<i class="fas fa-download mr-1"></i> Download
</a>
</div>
<div class="p-4">
<h3 class="font-semibold text-lg mb-1">Sample Document 4</h3>
<p class="text-gray-500 text-sm">5.2 MB • 24 pages</p>
</div>
</div>
</div>
</div>
</div>
<!-- PDF Viewer Modal -->
<div id="pdfModal" class="fixed inset-0 z-50 overflow-y-auto hidden">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start justify-between">
<h3 id="pdfTitle" class="text-lg leading-6 font-medium text-gray-900"></h3>
<button type="button" onclick="closePdfModal()" class="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none">
<span class="sr-only">Close</span>
<i class="fas fa-times"></i>
</button>
</div>
<div class="mt-4">
<div class="flex justify-between items-center mb-4">
<div class="flex space-x-2">
<button id="zoomIn" class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded">
<i class="fas fa-search-plus"></i>
</button>
<button id="zoomOut" class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded">
<i class="fas fa-search-minus"></i>
</button>
<button id="fullscreen" class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded">
<i class="fas fa-expand"></i>
</button>
<a id="downloadBtn" download class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded inline-flex items-center">
<i class="fas fa-download mr-1"></i> Download
</a>
</div>
<div class="relative">
<div class="flex">
<input type="text" id="searchText" placeholder="Search in document..." class="border rounded-l py-1 px-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500">
<button id="searchBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white py-1 px-3 rounded-r">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</div>
<div id="pdfContainer" class="pdf-container border">
<canvas id="pdfCanvas"></canvas>
</div>
<div class="flex justify-between items-center mt-4">
<div>
<button id="prevPage" class="bg-indigo-600 hover:bg-indigo-700 text-white py-1 px-4 rounded mr-2">
<i class="fas fa-chevron-left"></i> Prev
</button>
<button id="nextPage" class="bg-indigo-600 hover:bg-indigo-700 text-white py-1 px-4 rounded">
Next <i class="fas fa-chevron-right"></i>
</button>
</div>
<div class="text-gray-600 text-sm">
Page <span id="currentPage">1</span> of <span id="totalPages">1</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Initialize PDF.js
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
// Modal variables
let pdfModal = document.getElementById('pdfModal');
let currentPdf = null;
let currentPage = 1;
let scale = 1;
let fullscreen = false;
let searchText = '';
let searchResults = [];
let currentSearchResult = 0;
// Open PDF modal
function openPdfModal(pdfUrl, pdfTitle) {
document.getElementById('pdfTitle').textContent = pdfTitle;
document.getElementById('downloadBtn').href = pdfUrl;
// Show modal
pdfModal.classList.remove('hidden');
// Load PDF
loadPdf(pdfUrl);
// Reset search
document.getElementById('searchText').value = '';
searchText = '';
searchResults = [];
currentSearchResult = 0;
}
// Close PDF modal
function closePdfModal() {
if (fullscreen) {
toggleFullscreen();
}
pdfModal.classList.add('hidden');
currentPdf = null;
}
// Load PDF
function loadPdf(pdfUrl) {
// Reset variables
currentPage = 1;
scale = 1;
// Loading PDF document
pdfjsLib.getDocument(pdfUrl).promise.then(function(pdf) {
currentPdf = pdf;
// Update total pages
document.getElementById('totalPages').textContent = pdf.numPages;
// Render first page
renderPage(currentPage);
}).catch(function(error) {
alert('Error loading PDF: ' + error.message);
});
}
// Render PDF page
function renderPage(pageNum) {
if (!currentPdf) return;
currentPdf.getPage(pageNum).then(function(page) {
let viewport = page.getViewport({ scale: scale });
let canvas = document.getElementById('pdfCanvas');
let context = canvas.getContext('2d');
// Adjust canvas size
canvas.height = viewport.height;
canvas.width = viewport.width;
// Clear previous highlights
document.querySelectorAll('.search-highlight').forEach(el => el.remove());
// Update current page number
document.getElementById('currentPage').textContent = pageNum;
// Disable prev/next buttons on first/last page
document.getElementById('prevPage').disabled = (pageNum <= 1);
document.getElementById('nextPage').disabled = (pageNum >= currentPdf.numPages);
// Render PDF page
let renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
// Highlight search terms if any
if (searchText) {
searchInPage(page, viewport);
}
});
}
// Search in PDF page
function searchInPage(page, viewport) {
page.getTextContent().then(function(textContent) {
// Search for text
searchResults = [];
let text = textContent.items.map(item => item.str).join(' ');
let regex = new RegExp(searchText, 'gi');
let match;
while ((match = regex.exec(text)) !== null) {
searchResults.push(match.index);
}
// Highlight matches
if (searchResults.length > 0) {
highlightMatches(page, textContent, viewport);
document.getElementById('searchText').classList.remove('border-gray-300');
document.getElementById('searchText').classList.add('border-green-500');
} else {
document.getElementById('searchText').classList.remove('border-green-500');
document.getElementById('searchText').classList.add('border-gray-300');
}
});
}
// Highlight search matches
function highlightMatches(page, textContent, viewport) {
let textItems = textContent.items;
let canvas = document.getElementById('pdfCanvas');
let searchTermLength = searchText.length;
// Clear previous highlights
document.querySelectorAll('.search-highlight').forEach(el => el.remove());
let foundChars = 0;
let currentResult = 0;
for (let i = 0; i < textItems.length; i++) {
let item = textItems[i];
let itemText = item.str;
// Check if this item contains any part of our search results
while (currentResult < searchResults.length &&
foundChars + itemText.length >= searchResults[currentResult]) {
let position = searchResults[currentResult] - foundChars;
if (position < 0) position = 0;
// Get text up to the search term
let textBefore = itemText.substr(0, position);
let measureBefore = page.getTextWidth(textBefore);
// Get the search term text
let searchTermText = itemText.substr(position, searchTermLength);
let searchMeasure = page.getTextWidth(searchTermText);
// Create highlight div
let highlightDiv = document.createElement('div');
highlightDiv.className = 'search-highlight absolute';
highlightDiv.style.top = (item.transform[5] - (item.height * (scale * 0.9))) + 'px';
highlightDiv.style.left = (item.transform[4] + measureBefore) + 'px';
highlightDiv.style.width = searchMeasure + 'px';
highlightDiv.style.height = (item.height * scale) + 'px';
if (currentSearchResult === currentResult) {
highlightDiv.style.border = '2px solid red';
highlightDiv.style.boxSizing = 'border-box';
}
document.querySelector('#pdfContainer').appendChild(highlightDiv);
currentResult++;
// If we've reached the end of this item's text, break
if (position + searchTermLength >= itemText.length) {
break;
}
}
foundChars += itemText.length;
}
}
// Toggle fullscreen mode
function toggleFullscreen() {
let container = document.getElementById('pdfContainer');
if (!fullscreen) {
container.classList.add('pdf-fullscreen');
fullscreen = true;
document.getElementById('fullscreen').innerHTML = '<i class="fas fa-compress"></i>';
} else {
container.classList.remove('pdf-fullscreen');
fullscreen = false;
document.getElementById('fullscreen').innerHTML = '<i class="fas fa-expand"></i>';
}
renderPage(currentPage);
}
// Event listeners
document.getElementById('prevPage').addEventListener('click', function() {
if (currentPage > 1) {
currentPage--;
renderPage(currentPage);
}
});
document.getElementById('nextPage').addEventListener('click', function() {
if (currentPdf && currentPage < currentPdf.numPages) {
currentPage++;
renderPage(currentPage);
}
});
document.getElementById('zoomIn').addEventListener('click', function() {
scale += 0.25;
renderPage(currentPage);
});
document.getElementById('zoomOut').addEventListener('click', function() {
if (scale > 0.5) {
scale -= 0.25;
renderPage(currentPage);
}
});
document.getElementById('fullscreen').addEventListener('click', toggleFullscreen);
document.getElementById('searchBtn').addEventListener('click', function() {
searchText = document.getElementById('searchText').value.trim();
if (searchText && currentPdf) {
currentSearchResult = 0;
renderPage(currentPage);
}
});
// Handle search text input (trigger search on Enter)
document.getElementById('searchText').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
document.getElementById('searchBtn').click();
}
});
// Close modal when clicking outside
document.querySelector('#pdfModal > div > div').addEventListener('click', function(e) {
if (e.target === this) {
closePdfModal();
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=bep40/pdf-list" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>