/**
* Automated Table of Contents Generator
* Scans document for headings and generates TOC with REAL page numbers from PDF
*/
class TOCGenerator {
constructor() {
this.tocContainer = null;
this.headings = [];
this.pageNumbers = {};
this.totalPages = 0;
}
/**
* Initialize the TOC generator
*/
async init() {
console.log('๐ TOC Generator initializing...');
// Find the TOC container
this.tocContainer = document.querySelector('.toc-auto');
if (!this.tocContainer) {
console.error('โ TOC container not found. Looking for .toc-auto class.');
return;
}
console.log('โ
TOC container found:', this.tocContainer);
// Load real page numbers from PDF analysis
await this.loadPageNumbers();
// Generate TOC
this.generateTOC();
// Add event listener for dynamic updates
this.setupDynamicUpdates();
console.log('โ
TOC Generator initialized successfully');
}
/**
* Load real page numbers from the JSON file generated by PDF analysis
*/
async loadPageNumbers() {
try {
// Try to load page numbers from the JSON file
const response = await fetch('../page_numbers.json');
if (response.ok) {
this.pageNumbers = await response.json();
console.log('โ
Loaded real page numbers from PDF analysis');
// Calculate total pages from the highest page number
this.totalPages = Math.max(...Object.values(this.pageNumbers));
console.log(`๐ Total pages from PDF analysis: ${this.totalPages}`);
// Update CSS variable immediately
if (this.totalPages) {
document.documentElement.style.setProperty('--total-pages', this.totalPages);
console.log(`๐จ CSS variable --total-pages set to: ${this.totalPages}`);
} else {
console.error('โ Cannot set CSS variable: totalPages is null');
}
} else {
console.error('โ Page numbers file not found or empty');
this.pageNumbers = {};
this.totalPages = null; // No hardcoded value!
}
} catch (error) {
console.error('โ ERROR: Cannot load page numbers from JSON file!');
console.error(' This usually means:');
console.error(' 1. The HTML file is opened directly (file://) instead of through a web server');
console.error(' 2. The page_numbers.json file is missing or corrupted');
console.error(' 3. CORS policy is blocking the request');
console.error(' Solution: Run the HTML through a local web server or fix the JSON file path');
console.error(' Original error:', error);
// Show error in TOC container instead of hardcoded values
this.tocContainer.innerHTML = `
โ Fehler beim Laden der Seitennummern
Problem: Die Datei page_numbers.json kann nicht geladen werden.
Mรถgliche Ursachen:
- HTML-Datei wird direkt geรถffnet (file://) statt รผber Webserver
- page_numbers.json fehlt oder ist beschรคdigt
- CORS-Richtlinie blockiert den Zugriff
Lรถsung: Fรผhren Sie main.py aus, um PDF zu generieren und zu analysieren.
`;
this.pageNumbers = {};
this.totalPages = null; // No hardcoded value!
// Still try to generate TOC with estimated numbers
console.log('๐ Attempting to generate TOC with estimated numbers...');
this.generateTOC();
}
}
/**
* Generate the complete TOC
*/
generateTOC() {
console.log('๐ Generating TOC...');
// Clear existing content
this.tocContainer.innerHTML = 'Inhaltsverzeichnis
';
// Find all headings with specific classes
this.headings = [];
// Try different selectors to find headings
const headingSelectors = [
'h1.content-heading.heading-level-1',
'h2.content-heading.heading-level-2',
'h3.content-heading.heading-level-3',
'h1.heading-level-1',
'h2.heading-level-2',
'h3.heading-level-3',
'h1.content-heading',
'h2.content-heading',
'h3.content-heading'
];
console.log('๐ Looking for headings with selectors:', headingSelectors);
let foundAny = false;
headingSelectors.forEach(selector => {
const elements = document.querySelectorAll(selector);
console.log(`๐ Found ${elements.length} elements for selector: ${selector}`);
if (elements.length > 0) {
foundAny = true;
}
elements.forEach(element => {
// Get the text content WITH the numbering
const text = element.textContent.trim();
const level = element.classList.contains('heading-level-1') ? 1 :
element.classList.contains('heading-level-2') ? 2 : 3;
console.log(`๐ Heading: "${text}" (Level ${level})`);
// Only add if we don't already have this text
if (!this.headings.some(h => h.text === text)) {
this.headings.push({
element: element,
text: text,
level: level,
page: this.estimatePageNumber(element)
});
}
});
});
if (!foundAny) {
console.log('โ ๏ธ No headings found with any selector. Trying generic h1, h2, h3...');
const genericHeadings = document.querySelectorAll('h1, h2, h3');
console.log(`๐ Found ${genericHeadings.length} generic headings`);
genericHeadings.forEach((element, index) => {
const text = element.textContent.trim();
const level = element.tagName === 'H1' ? 1 : element.tagName === 'H2' ? 2 : 3;
console.log(`๐ Generic Heading: "${text}" (Level ${level})`);
this.headings.push({
element: element,
text: text,
level: level,
page: 3 + Math.floor(index / 2) // Simple page estimation
});
});
}
console.log(`๐ Total headings found: ${this.headings.length}`);
// Sort headings by their position in the document
this.headings.sort((a, b) => {
const posA = a.element.getBoundingClientRect().top;
const posB = b.element.getBoundingClientRect().top;
return posA - posB;
});
// Generate TOC entries
this.headings.forEach(heading => {
const entry = this.createTOCEntry(heading);
this.tocContainer.appendChild(entry);
});
// Update total pages based on actual content
if (this.headings.length > 0) {
const maxPage = Math.max(...this.headings.map(h => h.page));
this.totalPages = maxPage;
console.log(`๐ Total pages calculated: ${this.totalPages}`);
// Update CSS variable for footers
document.documentElement.style.setProperty('--total-pages', this.totalPages);
// Update footer text to show total pages
this.updateFooterText();
}
console.log('โ
TOC generation complete');
}
/* Removed getRealPageNumber function - using direct estimation instead */
/**
* Estimate page number (fallback method)
*/
estimatePageNumber(element) {
try {
if (!element) return 3;
const rect = element.getBoundingClientRect();
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const absoluteTop = rect.top + scrollTop;
// A4 page height in pixels (assuming 96 DPI)
// A4 = 210mm x 297mm, 1 inch = 25.4mm, 1 inch = 96 pixels
const pageHeightPixels = (297 * 96) / 25.4; // ~1123 pixels per page
// Account for margins and spacing - use actual content height
const effectivePageHeight = pageHeightPixels - 150; // More realistic margin
// Calculate page number (start from page 3 since pages 1-2 are title and TOC)
let pageNumber = Math.floor(absoluteTop / effectivePageHeight) + 3;
// Don't clamp - let it go to actual page count
console.log(`๐ Element "${element.textContent.trim()}" at position ${absoluteTop}px = page ${pageNumber}`);
return pageNumber;
} catch (error) {
console.warn('Error estimating page number:', error);
return 3; // Default fallback
}
}
/**
* Create a TOC entry element
*/
createTOCEntry(heading) {
const entry = document.createElement('div');
entry.className = `toc-entry toc-level-${heading.level}`;
// Create the text content
const textSpan = document.createElement('span');
textSpan.className = 'toc-text';
textSpan.textContent = heading.text;
// Create the dots area (now just spacing)
const dotsSpan = document.createElement('span');
dotsSpan.className = 'dots';
// Create the page number
const pageSpan = document.createElement('span');
pageSpan.className = 'toc-page';
pageSpan.textContent = heading.page;
// Assemble the entry
entry.appendChild(textSpan);
entry.appendChild(dotsSpan);
entry.appendChild(pageSpan);
// Add click functionality to jump to heading
entry.addEventListener('click', () => {
heading.element.scrollIntoView({ behavior: 'smooth' });
});
return entry;
}
/**
* Setup dynamic updates
*/
setupDynamicUpdates() {
// Refresh TOC when window is resized
window.addEventListener('resize', () => {
setTimeout(() => this.refresh(), 100);
});
// Refresh TOC when content changes
const observer = new MutationObserver(() => {
this.refresh();
});
// Observe the main content area
const mainContent = document.querySelector('.main-content');
if (mainContent) {
observer.observe(mainContent, {
childList: true,
subtree: true,
characterData: true
});
}
}
/**
* Refresh the TOC
*/
async refresh() {
await this.loadPageNumbers();
this.generateTOC();
}
/**
* Estimate page number based on element position
*/
estimatePageNumber(element) {
const pageHeightPixels = 1123; // A4 height in pixels at 96 DPI
const absoluteTop = element.offsetTop;
const estimatedPage = Math.floor(absoluteTop / pageHeightPixels) + 1;
console.log(`๐ Element "${element.textContent.trim()}" at position ${absoluteTop}px -> estimated page ${estimatedPage}`);
return estimatedPage;
}
/**
* Get total page count for footer updates
*/
getTotalPages() {
return this.totalPages;
}
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
console.log('๐ TOC Generator: DOM loaded, starting...');
// Wait for numbering script to finish
setTimeout(() => {
console.log('๐ TOC Generator: Creating instance...');
window.tocGenerator = new TOCGenerator();
window.tocGenerator.init();
}, 100);
});
// Also try immediate execution as fallback
console.log('๐ TOC Generator script loaded');
// Simple test - if this shows up, JavaScript is working
document.addEventListener('DOMContentLoaded', function() {
console.log('๐งช SIMPLE TEST: JavaScript is working!');
// Test if we can find the TOC container
const tocContainer = document.querySelector('.toc-auto');
if (tocContainer) {
console.log('โ
Found TOC container:', tocContainer);
// Don't overwrite the TOC content - just log that we found it
} else {
console.log('โ TOC container NOT found');
}
});