MachineLearning / script.js
saifisvibin's picture
Upload 5 files
9218ec0 verified
// PDF.js Configuration
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
// Theme Management
const themeToggle = document.getElementById('themeToggle');
const sunIcon = document.getElementById('sunIcon');
const moonIcon = document.getElementById('moonIcon');
// Initialize theme from localStorage or default to dark
const currentTheme = localStorage.getItem('theme') || 'dark';
if (currentTheme === 'light') {
document.documentElement.setAttribute('data-theme', 'light');
sunIcon.style.display = 'none';
moonIcon.style.display = 'block';
}
// Toggle theme function
function toggleTheme() {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
// Toggle icons
if (newTheme === 'light') {
sunIcon.style.display = 'none';
moonIcon.style.display = 'block';
} else {
sunIcon.style.display = 'block';
moonIcon.style.display = 'none';
}
}
// State Management
const state = {
pdfDoc: null,
pageNum: 1,
pageRendering: false,
pageNumPending: null,
scale: 1.5,
canvas: document.getElementById('pdfCanvas'),
ctx: null
};
// Initialize
state.ctx = state.canvas.getContext('2d');
// DOM Elements
const elements = {
loadingOverlay: document.getElementById('loadingOverlay'),
pageNum: document.getElementById('pageNum'),
pageCount: document.getElementById('pageCount'),
currentPageDisplay: document.getElementById('currentPageDisplay'),
totalPagesDisplay: document.getElementById('totalPagesDisplay'),
zoomLevel: document.getElementById('zoomLevel'),
prevPageBtn: document.getElementById('prevPageBtn'),
nextPageBtn: document.getElementById('nextPageBtn'),
zoomInBtn: document.getElementById('zoomInBtn'),
zoomOutBtn: document.getElementById('zoomOutBtn'),
fitWidthBtn: document.getElementById('fitWidthBtn'),
pageInput: document.getElementById('pageInput'),
goToPageBtn: document.getElementById('goToPageBtn'),
sidebar: document.getElementById('sidebar'),
sidebarToggle: document.getElementById('sidebarToggle'),
closeSidebarBtn: document.getElementById('closeSidebarBtn'),
searchBtn: document.getElementById('searchBtn'),
searchPanel: document.getElementById('searchPanel'),
closeSearchBtn: document.getElementById('closeSearchBtn'),
fullscreenBtn: document.getElementById('fullscreenBtn'),
canvasContainer: document.getElementById('canvasContainer')
};
// Render Page
function renderPage(num) {
state.pageRendering = true;
state.pdfDoc.getPage(num).then(page => {
const viewport = page.getViewport({ scale: state.scale });
state.canvas.height = viewport.height;
state.canvas.width = viewport.width;
const renderContext = {
canvasContext: state.ctx,
viewport: viewport
};
const renderTask = page.render(renderContext);
renderTask.promise.then(() => {
state.pageRendering = false;
if (state.pageNumPending !== null) {
renderPage(state.pageNumPending);
state.pageNumPending = null;
}
// Update UI
updatePageDisplay();
// Hide loading overlay on first render
if (num === 1) {
elements.loadingOverlay.classList.add('hidden');
}
});
});
// Update page display immediately
elements.pageNum.textContent = num;
elements.currentPageDisplay.textContent = num;
}
// Queue Page Render
function queueRenderPage(num) {
if (state.pageRendering) {
state.pageNumPending = num;
} else {
renderPage(num);
}
}
// Update Page Display
function updatePageDisplay() {
elements.zoomLevel.textContent = Math.round(state.scale * 100) + '%';
updateThumbnailSelection();
}
// Navigate to Previous Page
function onPrevPage() {
if (state.pageNum <= 1) {
return;
}
state.pageNum--;
queueRenderPage(state.pageNum);
}
// Navigate to Next Page
function onNextPage() {
if (state.pageNum >= state.pdfDoc.numPages) {
return;
}
state.pageNum++;
queueRenderPage(state.pageNum);
}
// Zoom In
function zoomIn() {
if (state.scale < 3) {
state.scale += 0.25;
queueRenderPage(state.pageNum);
}
}
// Zoom Out
function zoomOut() {
if (state.scale > 0.5) {
state.scale -= 0.25;
queueRenderPage(state.pageNum);
}
}
// Fit to Width
function fitToWidth() {
const containerWidth = elements.canvasContainer.clientWidth - 40;
state.pdfDoc.getPage(state.pageNum).then(page => {
const viewport = page.getViewport({ scale: 1 });
state.scale = containerWidth / viewport.width;
queueRenderPage(state.pageNum);
});
}
// Go to Page
function goToPage() {
const pageNumber = parseInt(elements.pageInput.value);
if (pageNumber >= 1 && pageNumber <= state.pdfDoc.numPages) {
state.pageNum = pageNumber;
queueRenderPage(state.pageNum);
elements.pageInput.value = '';
}
}
// Toggle Sidebar
function toggleSidebar() {
elements.sidebar.classList.toggle('hidden');
}
// Toggle Search
function toggleSearch() {
elements.searchPanel.classList.toggle('active');
if (elements.searchPanel.classList.contains('active')) {
document.getElementById('searchInput').focus();
}
}
// Toggle Fullscreen
function toggleFullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
}
}
}
// Event Listeners
themeToggle.addEventListener('click', toggleTheme);
elements.prevPageBtn.addEventListener('click', onPrevPage);
elements.nextPageBtn.addEventListener('click', onNextPage);
elements.zoomInBtn.addEventListener('click', zoomIn);
elements.zoomOutBtn.addEventListener('click', zoomOut);
elements.fitWidthBtn.addEventListener('click', fitToWidth);
elements.goToPageBtn.addEventListener('click', goToPage);
elements.sidebarToggle.addEventListener('click', toggleSidebar);
elements.closeSidebarBtn.addEventListener('click', toggleSidebar);
elements.searchBtn.addEventListener('click', toggleSearch);
elements.closeSearchBtn.addEventListener('click', toggleSearch);
elements.fullscreenBtn.addEventListener('click', toggleFullscreen);
// Enter key for page input
elements.pageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
goToPage();
}
});
// Keyboard Shortcuts
document.addEventListener('keydown', (e) => {
// Arrow keys for navigation
if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
e.preventDefault();
onPrevPage();
} else if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
e.preventDefault();
onNextPage();
}
// Zoom shortcuts
if (e.key === '+' || e.key === '=') {
e.preventDefault();
zoomIn();
} else if (e.key === '-') {
e.preventDefault();
zoomOut();
}
// Search shortcut
if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
e.preventDefault();
toggleSearch();
}
// Fullscreen shortcut
if (e.key === 'f' && !e.ctrlKey && !e.metaKey) {
const activeElement = document.activeElement;
if (activeElement.tagName !== 'INPUT') {
toggleFullscreen();
}
}
});
// Search Functionality
let searchMatches = [];
let currentMatch = 0;
document.getElementById('searchInput').addEventListener('input', async (e) => {
const query = e.target.value.toLowerCase();
const results = document.getElementById('searchResults');
if (query.length < 2) {
results.textContent = '';
searchMatches = [];
return;
}
searchMatches = [];
// Search through all pages
for (let i = 1; i <= state.pdfDoc.numPages; i++) {
const page = await state.pdfDoc.getPage(i);
const textContent = await page.getTextContent();
const text = textContent.items.map(item => item.str).join(' ').toLowerCase();
if (text.includes(query)) {
searchMatches.push(i);
}
}
if (searchMatches.length > 0) {
results.textContent = `Found ${searchMatches.length} matches`;
currentMatch = 0;
} else {
results.textContent = 'No matches found';
}
});
document.getElementById('searchNextBtn').addEventListener('click', () => {
if (searchMatches.length > 0) {
currentMatch = (currentMatch + 1) % searchMatches.length;
state.pageNum = searchMatches[currentMatch];
queueRenderPage(state.pageNum);
document.getElementById('searchResults').textContent =
`Match ${currentMatch + 1} of ${searchMatches.length} (Page ${searchMatches[currentMatch]})`;
}
});
document.getElementById('searchPrevBtn').addEventListener('click', () => {
if (searchMatches.length > 0) {
currentMatch = (currentMatch - 1 + searchMatches.length) % searchMatches.length;
state.pageNum = searchMatches[currentMatch];
queueRenderPage(state.pageNum);
document.getElementById('searchResults').textContent =
`Match ${currentMatch + 1} of ${searchMatches.length} (Page ${searchMatches[currentMatch]})`;
}
});
// Mouse Wheel Zoom
elements.canvasContainer.addEventListener('wheel', (e) => {
if (e.ctrlKey) {
e.preventDefault();
if (e.deltaY < 0) {
zoomIn();
} else {
zoomOut();
}
}
});
// Load PDF
const url = 'Machine-Learning-Systems.pdf';
pdfjsLib.getDocument(url).promise.then(pdfDoc => {
state.pdfDoc = pdfDoc;
elements.pageCount.textContent = pdfDoc.numPages;
elements.totalPagesDisplay.textContent = pdfDoc.numPages;
elements.pageInput.max = pdfDoc.numPages;
// Render the first page
renderPage(state.pageNum);
// Generate thumbnails
generateThumbnails();
}).catch(error => {
console.error('Error loading PDF:', error);
elements.loadingOverlay.innerHTML = `
<div style="text-align: center;">
<h2 style="color: var(--color-accent-primary); margin-bottom: 1rem;">Error Loading PDF</h2>
<p style="color: var(--color-text-secondary);">Please make sure the PDF file is in the same directory as this HTML file.</p>
<p style="color: var(--color-text-muted); margin-top: 1rem; font-size: 0.875rem;">Looking for: ${url}</p>
</div>
`;
});
// Generate Thumbnails
let currentThumbnailCount = 20;
const thumbnailsPerLoad = 20;
async function generateThumbnails(startFrom = 1, count = 20) {
const thumbnailsContainer = document.getElementById('thumbnails');
if (startFrom === 1) {
thumbnailsContainer.innerHTML = '<h3 style="font-size: 0.875rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--color-text-secondary);">Pages</h3>';
}
const endAt = Math.min(startFrom + count - 1, state.pdfDoc.numPages);
for (let i = startFrom; i <= endAt; i++) {
const page = await state.pdfDoc.getPage(i);
const viewport = page.getViewport({ scale: 0.2 });
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
await page.render({
canvasContext: ctx,
viewport: viewport
}).promise;
const thumbnailDiv = document.createElement('div');
thumbnailDiv.className = 'thumbnail-item';
thumbnailDiv.dataset.page = i;
// Create wrapper and label
const wrapper = document.createElement('div');
wrapper.className = 'thumbnail-canvas-wrapper';
wrapper.appendChild(canvas); // Append the actual canvas object with drawing
const label = document.createElement('div');
label.className = 'thumbnail-label';
label.textContent = `Page ${i}`;
thumbnailDiv.appendChild(wrapper);
thumbnailDiv.appendChild(label);
thumbnailDiv.addEventListener('click', () => {
state.pageNum = i;
queueRenderPage(i);
// Highlight selected thumbnail
updateThumbnailSelection(); // Modified this line
});
thumbnailsContainer.appendChild(thumbnailDiv);
}
// Remove existing "Show More" button if present
const existingShowMore = thumbnailsContainer.querySelector('.show-more-btn');
if (existingShowMore) {
existingShowMore.remove();
}
// Add "Show More" button if there are more pages
if (endAt < state.pdfDoc.numPages) {
const showMoreDiv = document.createElement('div');
showMoreDiv.className = 'show-more-btn';
showMoreDiv.innerHTML = `
<button onclick="loadMoreThumbnails()">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 5V19M5 12L12 19L19 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Show More (${state.pdfDoc.numPages - endAt} remaining)
</button>
`;
thumbnailsContainer.appendChild(showMoreDiv);
}
// Highlight the current page
updateThumbnailSelection();
}
function loadMoreThumbnails() {
const nextStart = currentThumbnailCount + 1;
currentThumbnailCount += thumbnailsPerLoad;
generateThumbnails(nextStart, thumbnailsPerLoad);
}
function updateThumbnailSelection() {
document.querySelectorAll('.thumbnail-item').forEach(t => {
if (parseInt(t.dataset.page) === state.pageNum) {
t.classList.add('active');
// Scroll to the active thumbnail if it's not in view
t.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} else {
t.classList.remove('active');
}
});
}
// Responsive adjustments
window.addEventListener('resize', () => {
if (window.innerWidth <= 768 && !elements.sidebar.classList.contains('hidden')) {
elements.sidebar.classList.add('hidden');
}
});
console.log('PDF Viewer initialized successfully!');